implement fd_readdir

This commit is contained in:
Pat Hickey
2020-12-18 16:13:29 -08:00
parent af0aa14ee8
commit c1919259dc
2 changed files with 78 additions and 7 deletions

View File

@@ -177,10 +177,11 @@ impl TableDirExt for crate::table::Table {
pub struct ReaddirEntity { pub struct ReaddirEntity {
pub next: ReaddirCursor, pub next: ReaddirCursor,
pub inode: u64, pub inode: u64,
pub namelen: u64, pub namelen: u32,
pub filetype: FileType, pub filetype: FileType,
} }
#[derive(Debug, Copy, Clone)]
pub struct ReaddirCursor(u64); pub struct ReaddirCursor(u64);
impl From<u64> for ReaddirCursor { impl From<u64> for ReaddirCursor {
fn from(c: u64) -> ReaddirCursor { fn from(c: u64) -> ReaddirCursor {
@@ -254,7 +255,8 @@ impl WasiDir for cap_std::fs::Dir {
cursor: ReaddirCursor, cursor: ReaddirCursor,
) -> Result<Box<dyn Iterator<Item = Result<(ReaddirEntity, String), Error>>>, Error> { ) -> Result<Box<dyn Iterator<Item = Result<(ReaddirEntity, String), Error>>>, Error> {
let rd = self let rd = self
.read_dir(PathBuf::new())? .read_dir(Path::new("."))
.expect("always possible to readdir an open Dir") // XXX is this true?
.enumerate() .enumerate()
.skip(u64::from(cursor) as usize); .skip(u64::from(cursor) as usize);
Ok(Box::new(rd.map(|(ix, entry)| { Ok(Box::new(rd.map(|(ix, entry)| {
@@ -267,7 +269,7 @@ impl WasiDir for cap_std::fs::Dir {
.file_name() .file_name()
.into_string() .into_string()
.map_err(|_| Error::Utf8(todo!()))?; .map_err(|_| Error::Utf8(todo!()))?;
let namelen = name.as_bytes().len() as u64; let namelen = name.as_bytes().len().try_into()?;
let entity = ReaddirEntity { let entity = ReaddirEntity {
next: ReaddirCursor::from(ix as u64 + 1), next: ReaddirCursor::from(ix as u64 + 1),
filetype, filetype,

View File

@@ -545,19 +545,60 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
fn fd_readdir( fn fd_readdir(
&self, &self,
dirfd: types::Fd, fd: types::Fd,
buf: &GuestPtr<u8>, buf: &GuestPtr<u8>,
buf_len: types::Size, buf_len: types::Size,
cookie: types::Dircookie, cookie: types::Dircookie,
) -> Result<types::Size, Error> { ) -> Result<types::Size, Error> {
let table = self.table(); let table = self.table();
let dir_entry: Ref<DirEntry> = table.get(u32::from(dirfd))?; let fd = u32::from(fd);
debug!(
"fd_readdir {} is a DirEntry: {}",
fd,
table.is::<DirEntry>(fd)
);
let dir_entry: Ref<DirEntry> = table.get(fd)?;
let d = dir_entry.get_cap(DirCaps::READDIR)?; let d = dir_entry.get_cap(DirCaps::READDIR)?;
let mut bufused = 0;
let mut buf = buf.clone();
for pair in d.readdir(ReaddirCursor::from(cookie))? { for pair in d.readdir(ReaddirCursor::from(cookie))? {
let (entity, name) = pair?; let (entity, name) = pair?;
todo!() let dirent_raw = dirent_bytes(types::Dirent::from(&entity));
let dirent_len: types::Size = dirent_raw.len().try_into()?;
let name_raw = name.as_bytes();
let name_len: types::Size = name_raw.len().try_into()?;
let offset = dirent_len.checked_add(name_len).ok_or(Error::Overflow)?;
// Copy as many bytes of the dirent as we can, up to the end of the buffer
let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused);
buf.as_array(dirent_copy_len)
.copy_from_slice(&dirent_raw[..dirent_copy_len as usize])?;
// If the dirent struct wasnt compied entirely, return that we filled the buffer, which
// tells libc that we're not at EOF.
if dirent_copy_len < dirent_len {
return Ok(buf_len);
} }
todo!()
buf = buf.add(dirent_copy_len)?;
// Copy as many bytes of the name as we can, up to the end of the buffer
let name_copy_len = std::cmp::min(name_len, buf_len - bufused);
buf.as_array(name_copy_len)
.copy_from_slice(&name_raw[..name_copy_len as usize])?;
// If the dirent struct wasn't copied entirely, return that we filled the buffer, which
// tells libc that we're not at EOF
if name_copy_len < name_len {
return Ok(buf_len);
}
buf = buf.add(name_copy_len)?;
bufused += offset;
}
Ok(bufused)
} }
fn path_create_directory( fn path_create_directory(
@@ -1136,3 +1177,31 @@ impl From<Filestat> for types::Filestat {
} }
} }
} }
impl From<&ReaddirEntity> for types::Dirent {
fn from(e: &ReaddirEntity) -> types::Dirent {
types::Dirent {
d_ino: e.inode,
d_namlen: e.namelen,
d_type: types::Filetype::from(&e.filetype),
d_next: e.next.into(),
}
}
}
fn dirent_bytes(dirent: types::Dirent) -> Vec<u8> {
use wiggle::GuestType;
assert_eq!(
types::Dirent::guest_size(),
std::mem::size_of::<types::Dirent>() as _,
"Dirent guest repr and host repr should match"
);
let size = types::Dirent::guest_size()
.try_into()
.expect("Dirent is smaller than 2^32");
let mut bytes = Vec::with_capacity(size);
bytes.resize(size, 0);
let ptr = bytes.as_mut_ptr() as *mut types::Dirent;
unsafe { ptr.write_unaligned(dirent) };
bytes
}