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 cur_cookie = cookie;
39 let mut buf_idx = 0usize;
40
41 let entries: Vec<(String, Filetype, u64)> = {
42 let guard = working_dir.inode.read();
43 match guard.deref() {
44 Kind::Dir { path, entries, .. } => {
45 trace!("reading dir {:?}", path);
46 let fs_info = wasi_try_ok!(
51 wasi_try_ok!(state.fs_read_dir(path))
52 .collect::<Result<Vec<_>, _>>()
53 .map_err(fs_error_into_wasi_err)
54 );
55 let mut entry_vec = wasi_try_ok!(
56 fs_info
57 .into_iter()
58 .map(|entry| {
59 let filename = entry.file_name().to_string_lossy().to_string();
60 trace!("getting file: {:?}", filename);
61 let filetype = virtual_file_type_to_wasi_file_type(
62 entry.file_type().map_err(fs_error_into_wasi_err)?,
63 );
64 Ok((
65 filename, filetype, 0, ))
67 })
68 .collect::<Result<Vec<(String, Filetype, u64)>, _>>()
69 );
70 entry_vec.extend(entries.iter().filter(|(_, inode)| inode.is_preopened).map(
71 |(name, inode)| {
72 let stat = inode.stat.read().unwrap();
73 (
74 inode.name.read().unwrap().to_string(),
75 stat.st_filetype,
76 stat.st_ino,
77 )
78 },
79 ));
80 entry_vec.push((".".to_string(), Filetype::Directory, 0));
83 entry_vec.push(("..".to_string(), Filetype::Directory, 0));
84 entry_vec.sort_by(|a, b| a.0.cmp(&b.0));
85 entry_vec
86 }
87 Kind::Root { entries } => {
88 trace!("reading root");
89 let sorted_entries = {
90 let mut entry_vec: Vec<(String, InodeGuard)> = entries
91 .iter()
92 .map(|(a, b)| (a.clone(), b.clone()))
93 .collect();
94 entry_vec.sort_by(|a, b| a.0.cmp(&b.0));
95 entry_vec
96 };
97 sorted_entries
98 .into_iter()
99 .map(|(name, inode)| {
100 let stat = inode.stat.read().unwrap();
101 (
102 format!("/{}", inode.name.read().unwrap().as_ref()),
103 stat.st_filetype,
104 stat.st_ino,
105 )
106 })
107 .collect()
108 }
109 Kind::File { .. }
110 | Kind::Symlink { .. }
111 | Kind::Buffer { .. }
112 | Kind::Socket { .. }
113 | Kind::PipeRx { .. }
114 | Kind::PipeTx { .. }
115 | Kind::DuplexPipe { .. }
116 | Kind::EventNotifications { .. }
117 | Kind::Epoll { .. } => return Ok(Errno::Notdir),
118 }
119 };
120
121 for (entry_path_str, wasi_file_type, ino) in entries.iter().skip(cookie as usize) {
122 cur_cookie += 1;
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}