fn readdirall()

in src/nodes/dir.rs [81:175]


    fn readdirall(&self, ids: &IdGenerator, cache: &dyn Cache) -> NodeResult<Vec<ReplyEntry>> {
        let mut reply = vec!();

        let mut state = self.state.lock().unwrap();

        reply.push(ReplyEntry {
            inode: self.inode,
            fs_type: fuse::FileType::Directory,
            name: OsString::from(".")
        });
        reply.push(ReplyEntry {
            inode: state.parent,
            fs_type: fuse::FileType::Directory,
            name: OsString::from("..")
        });

        // First, return the entries that correspond to explicit mappings performed by the user at
        // either mount time or during a reconfiguration.  Those should clobber any on-disk
        // contents that we discover later when we issue the readdir on the underlying directory,
        // if any.
        for (name, dirent) in &state.children {
            if dirent.explicit_mapping {
                reply.push(ReplyEntry {
                    inode: dirent.node.inode(),
                    fs_type: dirent.node.file_type_cached(),
                    name: name.clone()
                });
            }
        }

        let mut handle = self.handle.lock().unwrap();

        if handle.is_none() {
            debug_assert!(state.underlying_path.is_none());
            return Ok(reply);
        }
        debug_assert!(state.underlying_path.is_some());
        let handle = handle.as_mut().unwrap();
        for entry in handle.iter() {
            let entry = entry?;
            let name = entry.file_name();

            let name = OsStr::from_bytes(name.to_bytes()).to_os_string();

            if name == "." || name == ".." {
                continue;
            }

            if let Some(dirent) = state.children.get(&name) {
                // Found a previously-known on-disk entry.  Must return it "as is" (even if its
                // type might have changed) because, if we called into `cache.get_or_create` below,
                // we might recreate the node unintentionally.  Note that mappings were handled
                // earlier, so only handle the non-mapping case here.
                if !dirent.explicit_mapping {
                    reply.push(ReplyEntry {
                        inode: dirent.node.inode(),
                        fs_type: dirent.node.file_type_cached(),
                        name: name.clone(),
                    });
                }
                continue;
            }

            let path = state.underlying_path.as_ref().unwrap().join(&name);

            // TODO(jmmv): In theory we shouldn't need to issue a stat for every entry during a
            // readdir.  However, it's much easier to handle things this way because we currently
            // require a file's metadata in order to instantiate a node.  Note that the Go variant
            // of this code does the same and an attempt to "fix" this resulted in more complex
            // code and no visible performance gains.  That said, it'd be worth to investigate this
            // again.
            let fs_attr = fs::symlink_metadata(&path)?;

            let fs_type = conv::filetype_fs_to_fuse(&path, fs_attr.file_type());
            let child = cache.get_or_create(ids, &path, &fs_attr, self.writable);

            reply.push(ReplyEntry { inode: child.inode(), fs_type: fs_type, name: name.clone() });

            // Do the insertion into state.children after calling reply.add() to be able to move
            // the name into the key without having to copy it again.
            let dirent = Dirent {
                node: child.clone(),
                explicit_mapping: false,
            };
            // TODO(jmmv): We should remove stale entries at some point (possibly here), but the Go
            // variant does not do this so any implications of this are not tested.  The reason this
            // hasn't caused trouble yet is because: on readdir, we don't use any contents from
            // state.children that correspond to unmapped entries, and any stale entries visited
            // during lookup will result in an ENOENT.
            state.children.insert(name, dirent);
        }
        // No need to worry about rewinding handle.iter() for future reads on the same OpenDir:
        // the rawdir::Dir implementation does this for us.
        Ok(reply)
    }