wasmer_wasix/syscalls/wasix/
path_open2.rs1use super::*;
2use crate::VIRTUAL_ROOT_FD;
3use crate::syscalls::*;
4
5#[instrument(level = "trace", skip_all, fields(%dirfd, path = field::Empty, follow_symlinks = field::Empty, ret_fd = field::Empty), ret)]
30pub fn path_open2<M: MemorySize>(
31 mut ctx: FunctionEnvMut<'_, WasiEnv>,
32 dirfd: WasiFd,
33 dirflags: LookupFlags,
34 path: WasmPtr<u8, M>,
35 path_len: M::Offset,
36 o_flags: Oflags,
37 fs_rights_base: Rights,
38 fs_rights_inheriting: Rights,
39 fs_flags: Fdflags,
40 fd_flags: Fdflagsext,
41 fd: WasmPtr<WasiFd, M>,
42) -> Result<Errno, WasiError> {
43 WasiEnv::do_pending_operations(&mut ctx)?;
44
45 if dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 {
46 Span::current().record("follow_symlinks", true);
47 }
48 let env = ctx.data();
49 let (memory, mut state, mut inodes) =
50 unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
51 let path_len64: u64 = path_len.into();
53 if path_len64 > 1024u64 * 1024u64 {
54 return Ok(Errno::Nametoolong);
55 }
56
57 if path_len64 == 0 {
58 return Ok(Errno::Noent);
59 }
60
61 let path_string = unsafe { get_input_str_ok!(&memory, path, path_len) };
68 Span::current().record("path", path_string.as_str());
69
70 let out_fd = wasi_try_ok!(path_open_internal(
71 ctx.data(),
72 dirfd,
73 dirflags,
74 &path_string,
75 o_flags,
76 fs_rights_base,
77 fs_rights_inheriting,
78 fs_flags,
79 fd_flags,
80 None,
81 )?);
82 let env = ctx.data();
83
84 #[cfg(feature = "journal")]
85 if env.enable_journal {
86 JournalEffector::save_path_open(
87 &mut ctx,
88 out_fd,
89 dirfd,
90 dirflags,
91 path_string,
92 o_flags,
93 fs_rights_base,
94 fs_rights_inheriting,
95 fs_flags,
96 fd_flags,
97 )
98 .map_err(|err| {
99 tracing::error!("failed to save unlink event - {}", err);
100 WasiError::Exit(ExitCode::from(Errno::Fault))
101 })?;
102 }
103
104 let env = ctx.data();
105 let (memory, mut state, mut inodes) =
106 unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
107
108 Span::current().record("ret_fd", out_fd);
109
110 let fd_ref = fd.deref(&memory);
111 wasi_try_mem_ok!(fd_ref.write(out_fd));
112
113 Ok(Errno::Success)
114}
115
116pub(crate) fn path_open_internal(
117 env: &WasiEnv,
118 dirfd: WasiFd,
119 dirflags: LookupFlags,
120 path: &str,
121 o_flags: Oflags,
122 fs_rights_base: Rights,
123 fs_rights_inheriting: Rights,
124 fs_flags: Fdflags,
125 fd_flags: Fdflagsext,
126 with_fd: Option<WasiFd>,
127) -> Result<Result<WasiFd, Errno>, WasiError> {
128 fn implied_fd_rights(has_read_access: bool, has_write_access: bool) -> Rights {
129 let mut rights = Rights::FD_ADVISE | Rights::FD_TELL | Rights::FD_SEEK;
130
131 if has_read_access {
132 rights |= Rights::FD_READ | Rights::FD_FILESTAT_GET;
133 }
134
135 if has_write_access {
136 rights |= Rights::FD_DATASYNC
137 | Rights::FD_FDSTAT_SET_FLAGS
138 | Rights::FD_WRITE
139 | Rights::FD_SYNC
140 | Rights::FD_ALLOCATE
141 | Rights::FD_FILESTAT_GET
142 | Rights::FD_FILESTAT_SET_SIZE
143 | Rights::FD_FILESTAT_SET_TIMES;
144 }
145
146 rights
147 }
148
149 let state = env.state.deref();
150 let inodes = &state.inodes;
151 let follow_symlinks = dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0;
152 let effective_dirfd = if path.starts_with('/') {
153 VIRTUAL_ROOT_FD
154 } else {
155 dirfd
156 };
157 let path_arg = std::path::PathBuf::from(path);
158 let working_dir = match state.fs.get_fd(effective_dirfd) {
159 Ok(fd) => fd,
160 Err(err) => return Ok(Err(err)),
161 };
162 let maybe_inode = state
163 .fs
164 .get_inode_at_path(inodes, effective_dirfd, path, follow_symlinks);
165 let working_dir_rights_inheriting = working_dir.inner.rights_inheriting;
166
167 if !working_dir.inner.rights.contains(Rights::PATH_OPEN) {
169 return Ok(Err(Errno::Access));
170 }
171
172 let mut open_flags = 0;
173 let has_read_access = fs_rights_base.contains(Rights::FD_READ);
178 let has_write_access = fs_rights_base.contains(Rights::FD_WRITE)
179 || fs_flags.contains(Fdflags::APPEND)
180 || o_flags.contains(Oflags::TRUNC)
181 || o_flags.contains(Oflags::CREATE);
182 let requested_base_rights =
183 fs_rights_base | implied_fd_rights(has_read_access, has_write_access);
184
185 let adjusted_rights = requested_base_rights & working_dir_rights_inheriting;
188 let adjusted_rights_inheriting = fs_rights_inheriting & working_dir_rights_inheriting;
189 let mut open_options = state.fs_new_open_options();
190
191 let target_rights = match maybe_inode {
192 Ok(_) => {
193 let write_permission = adjusted_rights.contains(Rights::FD_WRITE);
194
195 let (append_permission, truncate_permission, create_permission) = if write_permission {
197 (
198 fs_flags.contains(Fdflags::APPEND),
199 o_flags.contains(Oflags::TRUNC),
200 o_flags.contains(Oflags::CREATE),
201 )
202 } else {
203 (false, false, false)
204 };
205
206 virtual_fs::OpenOptionsConfig {
207 read: adjusted_rights.contains(Rights::FD_READ),
208 write: write_permission,
209 create_new: create_permission && o_flags.contains(Oflags::EXCL),
210 create: create_permission,
211 append: append_permission,
212 truncate: truncate_permission,
213 }
214 }
215 Err(_) => virtual_fs::OpenOptionsConfig {
216 append: fs_flags.contains(Fdflags::APPEND),
217 write: adjusted_rights.contains(Rights::FD_WRITE),
218 read: adjusted_rights.contains(Rights::FD_READ),
219 create_new: o_flags.contains(Oflags::CREATE) && o_flags.contains(Oflags::EXCL),
220 create: o_flags.contains(Oflags::CREATE),
221 truncate: o_flags.contains(Oflags::TRUNC),
222 },
223 };
224
225 let parent_rights = virtual_fs::OpenOptionsConfig {
226 read: working_dir.inner.rights.contains(Rights::FD_READ),
227 write: working_dir.inner.rights.contains(Rights::FD_WRITE),
228 create_new: true,
231 create: true,
232 append: true,
233 truncate: true,
234 };
235
236 let minimum_rights = target_rights.minimum_rights(&parent_rights);
237
238 open_options.options(minimum_rights.clone());
239
240 let open_shared_file_handle =
246 |path: &std::path::Path,
247 requested_config: virtual_fs::OpenOptionsConfig,
248 shared_config: virtual_fs::OpenOptionsConfig|
249 -> Result<Box<dyn VirtualFile + Send + Sync + 'static>, Errno> {
250 let mut open_options = state.fs_new_open_options();
251 open_options.options(shared_config.clone());
252 match open_options.open(path) {
253 Ok(handle) => Ok(handle),
254 Err(FsError::PermissionDenied)
255 if shared_config.read != requested_config.read
256 || shared_config.write != requested_config.write =>
257 {
258 let mut open_options = state.fs_new_open_options();
259 open_options.options(requested_config);
260 open_options.open(path).map_err(fs_error_into_wasi_err)
261 }
262 Err(err) => Err(fs_error_into_wasi_err(err)),
263 }
264 };
265
266 let orig_path = path;
267
268 let inode = if let Ok(inode) = maybe_inode {
269 let processing_inode = inode.clone();
271 let mut guard = processing_inode.write();
272
273 let deref_mut = guard.deref_mut();
274
275 if o_flags.contains(Oflags::EXCL) && o_flags.contains(Oflags::CREATE) {
276 return Ok(Err(Errno::Exist));
277 }
278
279 match deref_mut {
280 Kind::File {
281 handle, path, fd, ..
282 } => {
283 if let Some(special_fd) = fd {
284 assert!(handle.is_some());
286 return Ok(Ok(*special_fd));
287 }
288 if o_flags.contains(Oflags::DIRECTORY) || orig_path.ends_with('/') {
289 return Ok(Err(Errno::Notdir));
290 }
291
292 let requested_config = open_options
293 .write(minimum_rights.write)
294 .create(minimum_rights.create)
295 .append(false)
296 .truncate(minimum_rights.truncate)
297 .get_config();
298 let shared_config = virtual_fs::OpenOptionsConfig {
299 read: true,
300 write: true,
301 ..requested_config.clone()
302 };
303
304 if minimum_rights.read {
305 open_flags |= Fd::READ;
306 }
307 if minimum_rights.write {
308 open_flags |= Fd::WRITE;
309 }
310 if minimum_rights.create {
311 open_flags |= Fd::CREATE;
312 }
313 if minimum_rights.truncate {
314 open_flags |= Fd::TRUNCATE;
315 }
316 let requires_stronger_handle =
319 minimum_rights.write || minimum_rights.truncate || minimum_rights.create;
320 if handle.is_none() {
321 *handle = Some(Arc::new(std::sync::RwLock::new(wasi_try_ok_ok!(
322 open_shared_file_handle(
323 path.as_path(),
324 requested_config.clone(),
325 shared_config.clone()
326 )
327 ))));
328 } else if requires_stronger_handle {
329 let mut file = handle.as_ref().unwrap().write().unwrap();
330 *file = wasi_try_ok_ok!(open_shared_file_handle(
331 path.as_path(),
332 requested_config.clone(),
333 shared_config.clone(),
334 ));
335 }
336
337 if let Some(handle) = handle {
338 let handle = handle.read().unwrap();
339 if let Some(fd) = handle.get_special_fd() {
340 let dup_fd = wasi_try_ok_ok!(state.fs.clone_fd(fd));
343 trace!(
344 %dup_fd
345 );
346
347 return Ok(Ok(dup_fd));
350 }
351 }
352 }
353 Kind::Buffer { .. } => unimplemented!("wasi::path_open for Buffer type files"),
354 Kind::Root { .. } => {
355 if !o_flags.contains(Oflags::DIRECTORY) {
356 return Ok(Err(Errno::Isdir));
357 }
358 }
359 Kind::Dir { .. } => {
360 if fs_rights_base.contains(Rights::FD_WRITE) {
361 return Ok(Err(Errno::Isdir));
362 }
363 }
364 Kind::Socket { .. }
365 | Kind::PipeTx { .. }
366 | Kind::PipeRx { .. }
367 | Kind::DuplexPipe { .. }
368 | Kind::EventNotifications { .. }
369 | Kind::Epoll { .. } => {}
370 Kind::Symlink {
371 base_po_dir,
372 path_to_symlink,
373 relative_path,
374 } => {
375 let (resolved_base_fd, resolved_path) = if relative_path.is_absolute() {
378 (VIRTUAL_ROOT_FD, relative_path.clone())
379 } else {
380 let mut resolved_path = path_to_symlink.clone();
381 resolved_path.pop();
382 resolved_path.push(relative_path);
383 (*base_po_dir, resolved_path)
384 };
385 return path_open_internal(
386 env,
387 resolved_base_fd,
388 __WASI_LOOKUP_SYMLINK_FOLLOW,
389 &resolved_path.to_string_lossy(),
390 o_flags,
391 fs_rights_base,
392 fs_rights_inheriting,
393 fs_flags,
394 fd_flags,
395 with_fd,
396 );
397 }
398 }
399 inode
400 } else {
401 if o_flags.contains(Oflags::CREATE) {
403 if o_flags.contains(Oflags::DIRECTORY) {
404 return Ok(Err(Errno::Notdir));
405 }
406
407 if path.ends_with('/') {
409 return Ok(Err(Errno::Isdir));
410 }
411
412 let (parent_inode, new_entity_name) =
415 wasi_try_ok_ok!(state.fs.get_parent_inode_at_path(
416 inodes,
417 effective_dirfd,
418 &path_arg,
419 follow_symlinks
420 ));
421 let new_file_host_path = {
422 let guard = parent_inode.read();
423 match guard.deref() {
424 Kind::Dir { path, .. } => {
425 let mut new_path = path.clone();
426 new_path.push(&new_entity_name);
427 new_path
428 }
429 Kind::Root { .. } => {
430 let mut new_path = std::path::PathBuf::new();
431 new_path.push(&new_entity_name);
432 new_path
433 }
434 _ => return Ok(Err(Errno::Notdir)),
435 }
436 };
437 let handle = {
440 let requested_config = open_options
443 .read(minimum_rights.read)
444 .append(minimum_rights.append)
445 .write(minimum_rights.write)
446 .create_new(true)
447 .get_config();
448 let shared_config = virtual_fs::OpenOptionsConfig {
449 read: true,
450 write: true,
451 ..requested_config.clone()
452 };
453
454 if minimum_rights.read {
455 open_flags |= Fd::READ;
456 }
457 if minimum_rights.write {
458 open_flags |= Fd::WRITE;
459 }
460 if minimum_rights.create_new {
461 open_flags |= Fd::CREATE;
462 }
463 if minimum_rights.truncate {
464 open_flags |= Fd::TRUNCATE;
465 }
466
467 match open_shared_file_handle(
468 new_file_host_path.as_path(),
469 requested_config,
470 shared_config,
471 ) {
472 Ok(handle) => Some(handle),
473 Err(err) => {
474 if err == Errno::Exist {
478 return Ok(Err(Errno::Perm));
479 }
480
481 return Ok(Err(err));
482 }
483 }
484 };
485
486 let new_inode = {
487 let kind = Kind::File {
488 handle: handle.map(|a| Arc::new(std::sync::RwLock::new(a))),
489 path: new_file_host_path,
490 fd: None,
491 };
492 wasi_try_ok_ok!(
493 state
494 .fs
495 .create_inode(inodes, kind, false, new_entity_name.clone())
496 )
497 };
498
499 {
500 let mut guard = parent_inode.write();
501 if let Kind::Dir { entries, .. } = guard.deref_mut() {
502 entries.insert(new_entity_name, new_inode.clone());
503 }
504 }
505
506 new_inode
507 } else {
508 return Ok(Err(maybe_inode.unwrap_err()));
509 }
510 };
511
512 let out_fd = wasi_try_ok_ok!(if let Some(fd) = with_fd {
515 state
516 .fs
517 .with_fd(
518 adjusted_rights,
519 adjusted_rights_inheriting,
520 fs_flags,
521 fd_flags,
522 open_flags,
523 inode,
524 fd,
525 )
526 .map(|_| fd)
527 } else {
528 state.fs.create_fd(
529 adjusted_rights,
530 adjusted_rights_inheriting,
531 fs_flags,
532 fd_flags,
533 open_flags,
534 inode,
535 )
536 });
537
538 Ok(Ok(out_fd))
539}