go-fuse
go-fuse copied to clipboard
Race in Inode.Forgotten()
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?
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?
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.
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.
"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.
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
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.