// A helper function for use after incrementing an inode's lookup count. // Ensures that the lookup count is decremented again if the caller is going to // return in error (in which case the kernel and gcsfuse would otherwise // disagree about the lookup count for the inode's ID), so that the inode isn't // leaked. // // Typical usage: // // func (fs *fileSystem) doFoo() (err error) { // in, err := fs.lookUpOrCreateInodeIfNotStale(...) // if err != nil { // return // } // // defer fs.unlockAndMaybeDisposeOfInode(in, &err) // // ... // } // // LOCKS_EXCLUDED(fs.mu) // UNLOCK_FUNCTION(in) func (fs *fileSystem) unlockAndMaybeDisposeOfInode( in inode.Inode, err *error) { // If there is no error, just unlock. if *err == nil { in.Unlock() return } // Otherwise, go through the decrement helper, which requires the file system // lock. fs.mu.Lock() fs.unlockAndDecrementLookupCount(in, 1) }
// Decrement the supplied inode's lookup count, destroying it if the inode says // that it has hit zero. // // We require the file system lock to exclude concurrent lookups, which might // otherwise find an inode whose lookup count has gone to zero. // // UNLOCK_FUNCTION(fs.mu) // UNLOCK_FUNCTION(in) func (fs *fileSystem) unlockAndDecrementLookupCount( in inode.Inode, N uint64) { name := in.Name() // Decrement the lookup count. shouldDestroy := in.DecrementLookupCount(N) // Update file system state, orphaning the inode if we're going to destroy it // below. if shouldDestroy { delete(fs.inodes, in.ID()) // Update indexes if necessary. if fs.generationBackedInodes[name] == in { delete(fs.generationBackedInodes, name) } if fs.implicitDirInodes[name] == in { delete(fs.implicitDirInodes, name) } } // We are done with the file system. fs.mu.Unlock() // Now we can destroy the inode if necessary. if shouldDestroy { destroyErr := in.Destroy() if destroyErr != nil { log.Printf("Error destroying inode %q: %v", name, destroyErr) } } in.Unlock() }