wasmer_wasix/syscalls/wasi/
fd_readdir.rs1use super::*;
2use crate::syscalls::*;
3
4#[instrument(level = "trace", skip_all, fields(%fd), ret)]
20pub fn fd_readdir<M: MemorySize>(
21 mut ctx: FunctionEnvMut<'_, WasiEnv>,
22 fd: WasiFd,
23 buf: WasmPtr<u8, M>,
24 buf_len: M::Offset,
25 cookie: Dircookie,
26 bufused: WasmPtr<M::Offset, M>,
27) -> Result<Errno, WasiError> {
28 WasiEnv::do_pending_operations(&mut ctx)?;
29
30 let env = ctx.data();
31 let (memory, mut state) = unsafe { env.get_memory_and_wasi_state(&ctx, 0) };
32 let buf_arr = wasi_try_mem_ok!(buf.slice(&memory, buf_len));
36 let bufused_ref = bufused.deref(&memory);
37 let working_dir = wasi_try_ok!(state.fs.get_fd(fd));
38 let mut buf_idx = 0usize;
39
40 let entries: Vec<(String, Filetype, u64)> = {
41 let guard = working_dir.inode.read();
42 match guard.deref() {
43 Kind::Dir { path, entries, .. } => {
44 trace!("reading dir {:?}", path);
45 let fs_info = wasi_try_ok!(
50 wasi_try_ok!(state.fs_read_dir(path))
51 .collect::<Result<Vec<_>, _>>()
52 .map_err(fs_error_into_wasi_err)
53 );
54 let mut entry_vec = wasi_try_ok!(
55 fs_info
56 .into_iter()
57 .map(|entry| {
58 let filename = entry.file_name().to_string_lossy().to_string();
59 trace!("getting file: {:?}", filename);
60 let filetype = virtual_file_type_to_wasi_file_type(
61 entry.file_type().map_err(fs_error_into_wasi_err)?,
62 );
63 Ok((
64 filename, filetype, 0, ))
66 })
67 .collect::<Result<Vec<(String, Filetype, u64)>, _>>()
68 );
69 entry_vec.extend(entries.iter().filter(|(_, inode)| inode.is_preopened).map(
70 |(name, inode)| {
71 let stat = inode.stat.read().unwrap();
72 (
73 inode.name.read().unwrap().to_string(),
74 stat.st_filetype,
75 stat.st_ino,
76 )
77 },
78 ));
79 entry_vec.push((".".to_string(), Filetype::Directory, 0));
82 entry_vec.push(("..".to_string(), Filetype::Directory, 0));
83 entry_vec.sort_by(|a, b| a.0.cmp(&b.0));
84 entry_vec
85 }
86 Kind::Root { entries } => {
87 trace!("reading root");
88 let sorted_entries = {
89 let mut entry_vec: Vec<(String, InodeGuard)> = entries
90 .iter()
91 .map(|(a, b)| (a.clone(), b.clone()))
92 .collect();
93 entry_vec.sort_by(|a, b| a.0.cmp(&b.0));
94 entry_vec
95 };
96 sorted_entries
97 .into_iter()
98 .map(|(name, inode)| {
99 let stat = inode.stat.read().unwrap();
100 (
101 format!("/{}", inode.name.read().unwrap().as_ref()),
102 stat.st_filetype,
103 stat.st_ino,
104 )
105 })
106 .collect()
107 }
108 Kind::File { .. }
109 | Kind::Symlink { .. }
110 | Kind::Buffer { .. }
111 | Kind::Socket { .. }
112 | Kind::PipeRx { .. }
113 | Kind::PipeTx { .. }
114 | Kind::DuplexPipe { .. }
115 | Kind::EventNotifications { .. }
116 | Kind::Epoll { .. } => return Ok(Errno::Notdir),
117 }
118 };
119
120 for (cur_cookie, (entry_path_str, wasi_file_type, ino)) in
121 (cookie + 1..).zip(entries.iter().skip(cookie as usize))
122 {
123 let namlen = entry_path_str.len();
124 trace!("returning dirent for {}", entry_path_str);
125 let dirent = Dirent {
126 d_next: cur_cookie,
127 d_ino: *ino,
128 d_namlen: namlen as u32,
129 d_type: *wasi_file_type,
130 };
131 let dirent_bytes = dirent_to_le_bytes(&dirent);
132 let buf_len: u64 = buf_len.into();
133 let upper_limit = std::cmp::min(
134 (buf_len - buf_idx as u64) as usize,
135 std::mem::size_of::<Dirent>(),
136 );
137 for (i, b) in dirent_bytes.iter().enumerate().take(upper_limit) {
138 wasi_try_mem_ok!(buf_arr.index((i + buf_idx) as u64).write(*b));
139 }
140 buf_idx += upper_limit;
141 if upper_limit != std::mem::size_of::<Dirent>() {
142 break;
143 }
144 let upper_limit = std::cmp::min((buf_len - buf_idx as u64) as usize, namlen);
145 for (i, b) in entry_path_str.bytes().take(upper_limit).enumerate() {
146 wasi_try_mem_ok!(buf_arr.index((i + buf_idx) as u64).write(b));
147 }
148 buf_idx += upper_limit;
149 if upper_limit != namlen {
150 break;
151 }
152 }
153
154 let buf_idx: M::Offset = wasi_try_ok!(buf_idx.try_into().map_err(|_| Errno::Overflow));
155 wasi_try_mem_ok!(bufused_ref.write(buf_idx));
156 Ok(Errno::Success)
157}