in internal/fs/fs.go [873:967]
func (fs *fileSystem) lookUpOrCreateInodeIfNotStale(ic inode.Core) (in inode.Inode) {
if err := ic.SanityCheck(); err != nil {
panic(err.Error())
}
// Ensure that no matter which inode we return, we increase its lookup count
// on the way out and then release the file system lock.
defer func() {
if in != nil {
in.IncrementLookupCount()
}
fs.mu.Unlock()
}()
fs.mu.Lock()
// Handle Folders in hierarchical bucket.
if ic.Folder != nil {
return fs.createDirInode(ic, fs.folderInodes)
}
// Handle implicit directories.
if ic.MinObject == nil {
return fs.createDirInode(ic, fs.implicitDirInodes)
}
oGen := inode.Generation{
Object: ic.MinObject.Generation,
Metadata: ic.MinObject.MetaGeneration,
Size: ic.MinObject.Size,
}
// Retry loop for the stale index entry case below. On entry, we hold fs.mu
// but no inode lock.
for {
// Look at the current index entry.
existingInode, ok := fs.generationBackedInodes[ic.FullName]
// If we have no existing record, mint an inode and return it.
if !ok {
in = fs.mintInode(ic)
fs.generationBackedInodes[in.Name()] = in.(inode.GenerationBackedInode)
in.Lock()
return
}
// Otherwise we need to read the inode's source generation below, which
// requires the inode's lock. We must not hold the inode lock while
// acquiring the file system lock, so drop it while acquiring the inode's
// lock, then reacquire.
fs.mu.Unlock()
existingInode.Lock()
fs.mu.Lock()
// Check that the index still points at this inode. If not, it's possible
// that the inode is in the process of being destroyed and is unsafe to
// use. Go around and try again.
if fs.generationBackedInodes[ic.FullName] != existingInode {
existingInode.Unlock()
continue
}
// Have we found the correct inode?
cmp := oGen.Compare(existingInode.SourceGeneration())
if cmp == 0 {
in = existingInode
return
}
// The existing inode is newer than the backing object. The caller
// should call again with a newer backing object.
if cmp == -1 {
existingInode.Unlock()
return
}
// The backing object is newer than the existing inode, while
// holding the inode lock, excluding concurrent actions by the inode (in
// particular concurrent calls to Sync, which changes generation numbers).
// This means we've proven that the record cannot have been caused by the
// inode's actions, and therefore this is not the inode we want.
//
// Replace it with a newly-mintend inode and then go around, acquiring its
// lock in accordance with our lock ordering rules.
existingInode.Unlock()
in = fs.mintInode(ic)
fs.generationBackedInodes[in.Name()] = in.(inode.GenerationBackedInode)
continue
}
}