// Read all entries for the directory, fix up conflicting names, and fill in // offset fields. // // LOCKS_REQUIRED(in) func readAllEntries( ctx context.Context, in inode.DirInode) (entries []fuseutil.Dirent, err error) { // Read one batch at a time. var tok string for { // Read a batch. var batch []fuseutil.Dirent batch, tok, err = in.ReadEntries(ctx, tok) if err != nil { err = fmt.Errorf("ReadEntries: %v", err) return } // Accumulate. entries = append(entries, batch...) // Are we done? if tok == "" { break } } // Ensure that the entries are sorted, for use in fixConflictingNames // below. sort.Sort(sortedDirents(entries)) // Fix name conflicts. err = fixConflictingNames(entries) if err != nil { err = fmt.Errorf("fixConflictingNames: %v", err) return } // Fix up offset fields. for i := 0; i < len(entries); i++ { entries[i].Offset = fuseops.DirOffset(i) + 1 } // Return a bogus inode ID for each entry, but not the root inode ID. // // NOTE(jacobsa): As far as I can tell this is harmless. Minting and // returning a real inode ID is difficult because fuse does not count // readdir as an operation that increases the inode ID's lookup count and // we therefore don't get a forget for it later, but we would like to not // have to remember every inode ID that we've ever minted for readdir. // // If it turns out this is not harmless, we'll need to switch to something // like inode IDs based on (object name, generation) hashes. But then what // about the birthday problem? And more importantly, what about our // semantic of not minting a new inode ID when the generation changes due // to a local action? for i, _ := range entries { entries[i].Inode = fuseops.RootInodeID + 1 } return }
// Look up the child with the given name within the parent, then return an // existing inode for that child or create a new one if necessary. Return // ENOENT if the child doesn't exist. // // Return the child locked, incrementing its lookup count. // // LOCKS_EXCLUDED(fs.mu) // LOCKS_EXCLUDED(parent) // LOCK_FUNCTION(child) func (fs *fileSystem) lookUpOrCreateChildInode( ctx context.Context, parent inode.DirInode, childName string) (child inode.Inode, err error) { // Set up a function that will find a lookup result for the child with the // given name. Expects no locks to be held. getLookupResult := func() (r inode.LookUpResult, err error) { parent.Lock() defer parent.Unlock() r, err = parent.LookUpChild(ctx, childName) if err != nil { err = fmt.Errorf("LookUpChild: %v", err) return } return } // Run a retry loop around lookUpOrCreateInodeIfNotStale. const maxTries = 3 for n := 0; n < maxTries; n++ { // Create a record. var result inode.LookUpResult result, err = getLookupResult() if err != nil { return } if !result.Exists() { err = fuse.ENOENT return } // Attempt to create the inode. Return if successful. fs.mu.Lock() child = fs.lookUpOrCreateInodeIfNotStale(result.FullName, result.Object) if child != nil { return } } err = fmt.Errorf("Did not converge after %v tries", maxTries) return }