Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should Inode.Forgotten() return true if Inode.changeCounter == 0? #504

Open
iain-macdonald opened this issue Mar 11, 2024 · 6 comments
Open

Comments

@iain-macdonald
Copy link

I recently discovered a bug in a go-fuse client related to the Inode.Forgotten() function. Roughly speaking, the client has logic for creating and reusing Inode Inos, and relies on calling Inode.Forgotten() to detect when Inos can be reused. However, Forgotten() returns true briefly between Inode construction and Inode initialization, so races can occur and lead to Ino reuse. This is a bit awkward to fix in the client, so I thought I'd ask if Inode.Forgotten() should really return true if Inode.lookupCount == 0 as in that case the Inode has not actually been initialized yet, or alternatively, if an Initialized() function could be added allowing the caller to check if an Inode has been initialized or not?

@hanwen
Copy link
Owner

hanwen commented Mar 12, 2024

Inode.Forgotten() to detect when Inos can be reused.

I would have to think this through in depth to double-check, but Forgotten() is intended for enabling cleanup in a scenario when you are sure the inode cannot be revived. If you are (re)using the inodes, then clearly you are reviving the inode. In a reuse scenario, the value of Forgotten() (or any other call that does no locking) can always be out of date by the time you make a decision based on the value.

If you want to securely reuse inodes, I'd put the eligible inodes in a global queue protected by mutex. You can put them in the queue if they are Forgotten. For construction, you can take them out of the queue one by one.

Does that make sense?

@hanwen
Copy link
Owner

hanwen commented Mar 12, 2024

maybe I misunderstand the bugreport, though. Who or what is issuing the Forgotten calls? IIRC, the locking should ensure that the inode only becomes visible in the tree atomically with the Forgotten becoming false.

@iain-macdonald
Copy link
Author

The code I'm talking about is here and here. It's not reusing inodes, it's reusing the uint64 identifier Attr.Ino (is that okay?). It reuses them only after the previous Inode with that ID has been Forgotten(), but as I mentioned, there is a race between when NewInode() is called and when Forgotten() is called.

@iain-macdonald
Copy link
Author

"Forgotten() is intended for enabling cleanup in a scenario when you are sure the inode cannot be revived." leads me to believe the current behavior of Forgotten() is not quite correct, as it can be called immediately after NewInode() but before being added to the filesystem, so it can return true, then false, and then later true again. Though the code I linked may also be misbehaving in some way.

To be clear, I'm also fine with adding an Initialized() function to check for the case I describe.

@iain-macdonald
Copy link
Author

Hey Han-Wen, did you have time to think about this and figure out which proposal I mentioned is more desirable?

FWIW it looks like Forgotten() is not too widely used among open-source repos hosted in GitHub, if that's any signal about the broader impact of changing its behavior: https://sourcegraph.com/search?q=context:global+.Forgotten%28%29+lang:Go+&patternType=keyword&sm=0

@hanwen
Copy link
Owner

hanwen commented Mar 22, 2024

did you have time to think about this and figure out which proposal I mentioned is more desirable?

unfortunately, I haven't. Familial duties have been taking all my time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants