wasmer_wasix/syscalls/wasix/
path_open2.rs1use super::*;
2use crate::syscalls::*;
3
4#[instrument(level = "trace", skip_all, fields(%dirfd, path = field::Empty, follow_symlinks = field::Empty, ret_fd = field::Empty), ret)]
29pub fn path_open2<M: MemorySize>(
30 mut ctx: FunctionEnvMut<'_, WasiEnv>,
31 dirfd: WasiFd,
32 dirflags: LookupFlags,
33 path: WasmPtr<u8, M>,
34 path_len: M::Offset,
35 o_flags: Oflags,
36 fs_rights_base: Rights,
37 fs_rights_inheriting: Rights,
38 fs_flags: Fdflags,
39 fd_flags: Fdflagsext,
40 fd: WasmPtr<WasiFd, M>,
41) -> Result<Errno, WasiError> {
42 WasiEnv::do_pending_operations(&mut ctx)?;
43
44 if dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 {
45 Span::current().record("follow_symlinks", true);
46 }
47 let env = ctx.data();
48 let (memory, mut state, mut inodes) =
49 unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
50 let path_len64: u64 = path_len.into();
52 if path_len64 > 1024u64 * 1024u64 {
53 return Ok(Errno::Nametoolong);
54 }
55
56 if path_len64 == 0 {
57 return Ok(Errno::Noent);
58 }
59
60 let path_string = unsafe { get_input_str_ok!(&memory, path, path_len) };
67 Span::current().record("path", path_string.as_str());
68
69 let out_fd = wasi_try_ok!(path_open_internal(
70 ctx.data(),
71 dirfd,
72 dirflags,
73 &path_string,
74 o_flags,
75 fs_rights_base,
76 fs_rights_inheriting,
77 fs_flags,
78 fd_flags,
79 None,
80 )?);
81 let env = ctx.data();
82
83 #[cfg(feature = "journal")]
84 if env.enable_journal {
85 JournalEffector::save_path_open(
86 &mut ctx,
87 out_fd,
88 dirfd,
89 dirflags,
90 path_string,
91 o_flags,
92 fs_rights_base,
93 fs_rights_inheriting,
94 fs_flags,
95 fd_flags,
96 )
97 .map_err(|err| {
98 tracing::error!("failed to save unlink event - {}", err);
99 WasiError::Exit(ExitCode::from(Errno::Fault))
100 })?;
101 }
102
103 let env = ctx.data();
104 let (memory, mut state, mut inodes) =
105 unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
106
107 Span::current().record("ret_fd", out_fd);
108
109 let fd_ref = fd.deref(&memory);
110 wasi_try_mem_ok!(fd_ref.write(out_fd));
111
112 Ok(Errno::Success)
113}
114
115pub(crate) fn path_open_internal(
116 env: &WasiEnv,
117 dirfd: WasiFd,
118 dirflags: LookupFlags,
119 path: &str,
120 o_flags: Oflags,
121 fs_rights_base: Rights,
122 fs_rights_inheriting: Rights,
123 fs_flags: Fdflags,
124 fd_flags: Fdflagsext,
125 with_fd: Option<WasiFd>,
126) -> Result<Result<WasiFd, Errno>, WasiError> {
127 let state = env.state.deref();
128 let inodes = &state.inodes;
129
130 let path_arg = std::path::PathBuf::from(&path);
131 let maybe_inode = state.fs.get_inode_at_path(
132 inodes,
133 dirfd,
134 path,
135 dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0,
136 );
137
138 let working_dir = wasi_try_ok_ok!(state.fs.get_fd(dirfd));
139 let working_dir_rights_inheriting = working_dir.inner.rights_inheriting;
140
141 if !working_dir.inner.rights.contains(Rights::PATH_OPEN) {
143 return Ok(Err(Errno::Access));
144 }
145
146 let mut open_flags = 0;
147 let adjusted_rights = working_dir_rights_inheriting;
154 let mut open_options = state.fs_new_open_options();
155
156 let target_rights = match maybe_inode {
157 Ok(_) => {
158 let write_permission = adjusted_rights.contains(Rights::FD_WRITE);
159
160 let (append_permission, truncate_permission, create_permission) = if write_permission {
162 (
163 fs_flags.contains(Fdflags::APPEND),
164 o_flags.contains(Oflags::TRUNC),
165 o_flags.contains(Oflags::CREATE),
166 )
167 } else {
168 (false, false, false)
169 };
170
171 virtual_fs::OpenOptionsConfig {
172 read: fs_rights_base.contains(Rights::FD_READ),
173 write: write_permission,
174 create_new: create_permission && o_flags.contains(Oflags::EXCL),
175 create: create_permission,
176 append: append_permission,
177 truncate: truncate_permission,
178 }
179 }
180 Err(_) => virtual_fs::OpenOptionsConfig {
181 append: fs_flags.contains(Fdflags::APPEND),
182 write: fs_rights_base.contains(Rights::FD_WRITE),
183 read: fs_rights_base.contains(Rights::FD_READ),
184 create_new: o_flags.contains(Oflags::CREATE) && o_flags.contains(Oflags::EXCL),
185 create: o_flags.contains(Oflags::CREATE),
186 truncate: o_flags.contains(Oflags::TRUNC),
187 },
188 };
189
190 let parent_rights = virtual_fs::OpenOptionsConfig {
191 read: working_dir.inner.rights.contains(Rights::FD_READ),
192 write: working_dir.inner.rights.contains(Rights::FD_WRITE),
193 create_new: true,
196 create: true,
197 append: true,
198 truncate: true,
199 };
200
201 let minimum_rights = target_rights.minimum_rights(&parent_rights);
202
203 open_options.options(minimum_rights.clone());
204
205 let orig_path = path;
206
207 let inode = if let Ok(inode) = maybe_inode {
208 let processing_inode = inode.clone();
210 let mut guard = processing_inode.write();
211
212 let deref_mut = guard.deref_mut();
213
214 if o_flags.contains(Oflags::EXCL) && o_flags.contains(Oflags::CREATE) {
215 return Ok(Err(Errno::Exist));
216 }
217
218 match deref_mut {
219 Kind::File {
220 handle, path, fd, ..
221 } => {
222 if let Some(special_fd) = fd {
223 assert!(handle.is_some());
225 return Ok(Ok(*special_fd));
226 }
227 if o_flags.contains(Oflags::DIRECTORY) || orig_path.ends_with('/') {
228 return Ok(Err(Errno::Notdir));
229 }
230
231 let open_options = open_options
232 .write(minimum_rights.write)
233 .create(minimum_rights.create)
234 .append(false)
235 .truncate(minimum_rights.truncate);
236
237 if minimum_rights.read {
238 open_flags |= Fd::READ;
239 }
240 if minimum_rights.write {
241 open_flags |= Fd::WRITE;
242 }
243 if minimum_rights.create {
244 open_flags |= Fd::CREATE;
245 }
246 if minimum_rights.truncate {
247 open_flags |= Fd::TRUNCATE;
248 }
249 *handle = Some(Arc::new(std::sync::RwLock::new(wasi_try_ok_ok!(
252 open_options.open(&path).map_err(fs_error_into_wasi_err)
253 ))));
254
255 if let Some(handle) = handle {
256 let handle = handle.read().unwrap();
257 if let Some(fd) = handle.get_special_fd() {
258 let dup_fd = wasi_try_ok_ok!(state.fs.clone_fd(fd));
261 trace!(
262 %dup_fd
263 );
264
265 return Ok(Ok(dup_fd));
268 }
269 }
270 }
271 Kind::Buffer { .. } => unimplemented!("wasi::path_open for Buffer type files"),
272 Kind::Root { .. } => {
273 if !o_flags.contains(Oflags::DIRECTORY) {
274 return Ok(Err(Errno::Notcapable));
275 }
276 }
277 Kind::Dir { .. } => {
278 if fs_rights_base.contains(Rights::FD_WRITE) {
279 return Ok(Err(Errno::Isdir));
280 }
281 }
282 Kind::Socket { .. }
283 | Kind::PipeTx { .. }
284 | Kind::PipeRx { .. }
285 | Kind::DuplexPipe { .. }
286 | Kind::EventNotifications { .. }
287 | Kind::Epoll { .. } => {}
288 Kind::Symlink {
289 base_po_dir,
290 path_to_symlink,
291 relative_path,
292 } => {
293 unimplemented!("SYMLINKS IN PATH_OPEN");
296 }
297 }
298 inode
299 } else {
300 if o_flags.contains(Oflags::CREATE) {
302 if o_flags.contains(Oflags::DIRECTORY) {
303 return Ok(Err(Errno::Notdir));
304 }
305
306 if path.ends_with('/') {
308 return Ok(Err(Errno::Isdir));
309 }
310
311 let (parent_inode, new_entity_name) =
314 wasi_try_ok_ok!(state.fs.get_parent_inode_at_path(
315 inodes,
316 dirfd,
317 &path_arg,
318 dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0
319 ));
320 let new_file_host_path = {
321 let guard = parent_inode.read();
322 match guard.deref() {
323 Kind::Dir { path, .. } => {
324 let mut new_path = path.clone();
325 new_path.push(&new_entity_name);
326 new_path
327 }
328 Kind::Root { .. } => {
329 let mut new_path = std::path::PathBuf::new();
330 new_path.push(&new_entity_name);
331 new_path
332 }
333 _ => return Ok(Err(Errno::Inval)),
334 }
335 };
336 let handle = {
339 let open_options = open_options
342 .read(minimum_rights.read)
343 .append(minimum_rights.append)
344 .write(minimum_rights.write)
345 .create_new(true);
346
347 if minimum_rights.read {
348 open_flags |= Fd::READ;
349 }
350 if minimum_rights.write {
351 open_flags |= Fd::WRITE;
352 }
353 if minimum_rights.create_new {
354 open_flags |= Fd::CREATE;
355 }
356 if minimum_rights.truncate {
357 open_flags |= Fd::TRUNCATE;
358 }
359
360 match open_options.open(&new_file_host_path) {
361 Ok(handle) => Some(handle),
362 Err(err) => {
363 if err == FsError::AlreadyExists {
367 return Ok(Err(Errno::Perm));
368 }
369
370 return Ok(Err(fs_error_into_wasi_err(err)));
371 }
372 }
373 };
374
375 let new_inode = {
376 let kind = Kind::File {
377 handle: handle.map(|a| Arc::new(std::sync::RwLock::new(a))),
378 path: new_file_host_path,
379 fd: None,
380 };
381 wasi_try_ok_ok!(
382 state
383 .fs
384 .create_inode(inodes, kind, false, new_entity_name.clone())
385 )
386 };
387
388 {
389 let mut guard = parent_inode.write();
390 if let Kind::Dir { entries, .. } = guard.deref_mut() {
391 entries.insert(new_entity_name, new_inode.clone());
392 }
393 }
394
395 new_inode
396 } else {
397 return Ok(Err(maybe_inode.unwrap_err()));
398 }
399 };
400
401 let out_fd = wasi_try_ok_ok!(if let Some(fd) = with_fd {
404 state
405 .fs
406 .with_fd(
407 adjusted_rights,
408 fs_rights_inheriting,
409 fs_flags,
410 fd_flags,
411 open_flags,
412 inode,
413 fd,
414 )
415 .map(|_| fd)
416 } else {
417 state.fs.create_fd(
418 adjusted_rights,
419 fs_rights_inheriting,
420 fs_flags,
421 fd_flags,
422 open_flags,
423 inode,
424 )
425 });
426
427 Ok(Ok(out_fd))
428}