From d51ffe8d4e4ffb0742e7a69a90a1ca30b831aef2 Mon Sep 17 00:00:00 2001 From: Pat Hickey Date: Mon, 4 Jan 2021 17:22:46 -0800 Subject: [PATCH] add . and .. to the readdir iterator --- crates/wasi-c2/TEST_FAILURES | 2 - crates/wasi-c2/src/dir.rs | 74 ++++++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/crates/wasi-c2/TEST_FAILURES b/crates/wasi-c2/TEST_FAILURES index e0e0c014da..d620a4c894 100644 --- a/crates/wasi-c2/TEST_FAILURES +++ b/crates/wasi-c2/TEST_FAILURES @@ -6,8 +6,6 @@ wasi_tests::directory_seek impl of Caps to see about this wasi_tests::fd_flags_set - set_fdflags is not implemented. test wanted to clear O_APPEND mode -wasi_tests::fd_readdir - - test expects `..` and `.`, Dir::readdir doesnt provide them wasi_tests::nofollow_errors - nofollow not implemented for dirs wasi_tests::path_filestat diff --git a/crates/wasi-c2/src/dir.rs b/crates/wasi-c2/src/dir.rs index 671b072810..e85bd85e28 100644 --- a/crates/wasi-c2/src/dir.rs +++ b/crates/wasi-c2/src/dir.rs @@ -262,27 +262,59 @@ impl WasiDir for cap_std::fs::Dir { &self, cursor: ReaddirCursor, ) -> Result>>, Error> { - let rd = self - .read_dir(Path::new(".")) - .expect("always possible to readdir an open Dir") // XXX is this true? - .enumerate() - .skip(u64::from(cursor) as usize); - Ok(Box::new(rd.map(|(ix, entry)| { - use cap_fs_ext::MetadataExt; - let entry = entry?; - let meta = entry.metadata()?; - let inode = meta.ino(); - let filetype = FileType::from(&meta.file_type()); - let name = entry.file_name().into_string().map_err(|_| Error::Ilseq)?; - let namelen = name.as_bytes().len().try_into()?; - let entity = ReaddirEntity { - next: ReaddirCursor::from(ix as u64 + 1), - filetype, - inode, - namelen, - }; - Ok((entity, name)) - }))) + use cap_fs_ext::MetadataExt; + + // cap_std's read_dir does not include . and .., we should prepend these. + // Why closures? failure of any individual entry doesn't mean the whole method should + // fail. + // Why the tuple? We can't construct a cap_std::fs::DirEntry. + let rd = vec![ + (|| { + let meta = self.dir_metadata()?; + let name = ".".to_owned(); + let namelen = name.as_bytes().len().try_into()?; + Ok((FileType::Directory, meta.ino(), namelen, name)) + })(), + (|| { + // XXX if parent dir is mounted it *might* be possible to give its inode, but we + // don't know that in this context. + let name = "..".to_owned(); + let namelen = name.as_bytes().len().try_into()?; + Ok((FileType::Directory, 0, namelen, name)) + })(), + ] + .into_iter() + .chain( + // Now process the `DirEntry`s: + self.read_dir(Path::new(".")) + .expect("always possible to readdir an open Dir") // XXX is this true? + .map(|entry| { + let entry = entry?; + let meta = entry.metadata()?; + let inode = meta.ino(); + let filetype = FileType::from(&meta.file_type()); + let name = entry.file_name().into_string().map_err(|_| Error::Ilseq)?; + let namelen = name.as_bytes().len().try_into()?; + Ok((filetype, inode, namelen, name)) + }), + ) + // Enumeration of the iterator makes it possible to define the ReaddirCursor + .enumerate() + .map(|(ix, r)| match r { + Ok((filetype, inode, namelen, name)) => Ok(( + ReaddirEntity { + next: ReaddirCursor::from(ix as u64 + 1), + filetype, + inode, + namelen, + }, + name, + )), + Err(e) => Err(e), + }) + .skip(u64::from(cursor) as usize); + + Ok(Box::new(rd)) } fn symlink(&self, src_path: &str, dest_path: &str) -> Result<(), Error> {