wasmer_wasix/syscalls/wasi/
path_unlink_file.rs1use super::*;
2use crate::syscalls::*;
3
4#[instrument(level = "trace", skip_all, fields(%fd, path = field::Empty), ret)]
14pub fn path_unlink_file<M: MemorySize>(
15 mut ctx: FunctionEnvMut<'_, WasiEnv>,
16 fd: WasiFd,
17 path: WasmPtr<u8, M>,
18 path_len: M::Offset,
19) -> Result<Errno, WasiError> {
20 WasiEnv::do_pending_operations(&mut ctx)?;
21
22 let env = ctx.data();
23 let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
24
25 let base_dir = wasi_try_ok!(state.fs.get_fd(fd));
26 if !base_dir.inner.rights.contains(Rights::PATH_UNLINK_FILE) {
27 return Ok(Errno::Access);
28 }
29 let path_str = unsafe { get_input_str_ok!(&memory, path, path_len) };
30 Span::current().record("path", path_str.as_str());
31
32 let ret = path_unlink_file_internal(&mut ctx, fd, &path_str)?;
33 let env = ctx.data();
34
35 if ret == Errno::Success {
36 #[cfg(feature = "journal")]
37 if env.enable_journal {
38 wasi_try_ok!(
39 JournalEffector::save_path_unlink(&mut ctx, fd, path_str).map_err(|err| {
40 tracing::error!("failed to save unlink event - {}", err);
41 Errno::Fault
42 })
43 )
44 }
45 }
46
47 Ok(ret)
48}
49
50pub(crate) fn path_unlink_file_internal(
51 ctx: &mut FunctionEnvMut<'_, WasiEnv>,
52 fd: WasiFd,
53 path: &str,
54) -> Result<Errno, WasiError> {
55 let env = ctx.data();
56 let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
57
58 let inode = wasi_try_ok!(state.fs.get_inode_at_path(inodes, fd, path, false));
59 let (parent_inode, child_name) = wasi_try_ok!(state.fs.get_parent_inode_at_path(
60 inodes,
61 fd,
62 std::path::Path::new(path),
63 false
64 ));
65 let host_adjusted_path = {
66 let guard = parent_inode.read();
67 match guard.deref() {
68 Kind::Dir { path, .. } => path.join(&child_name),
69 Kind::Root { .. } => return Ok(Errno::Access),
70 _ => unreachable!(
71 "Internal logic error in wasi::path_unlink_file, parent is not a directory"
72 ),
73 }
74 };
75
76 let removed_inode = {
77 let mut guard = parent_inode.write();
78 let entry = match guard.deref_mut() {
79 Kind::Dir { entries, .. } => entries.remove(&child_name),
80 Kind::Root { .. } => return Ok(Errno::Access),
81 _ => unreachable!(
82 "Internal logic error in wasi::path_unlink_file, parent is not a directory"
83 ),
84 };
85 let Some(removed_inode) = entry else {
86 drop(guard);
87
88 let inode_is_symlink = matches!(inode.read().deref(), Kind::Symlink { .. });
89 if !inode_is_symlink {
90 tracing::warn!(
91 "wasi::path_unlink_file: path resolution returned inode {:?} for {:?}, but parent directory had no matching entry",
92 inode.ino(),
93 child_name
94 );
95 return Ok(Errno::Noent);
96 }
97 return Ok(state.fs.remove_symlink_file(host_adjusted_path.as_path()));
98 };
99 assert!(inode.ino() == removed_inode.ino());
101 debug_assert!(inode.stat.read().unwrap().st_nlink > 0);
102 removed_inode
103 };
104
105 let st_nlink = {
106 let mut guard = removed_inode.stat.write().unwrap();
107 guard.st_nlink -= 1;
108 guard.st_nlink
109 };
110 if st_nlink == 0 {
111 {
112 let mut guard = removed_inode.read();
113 match guard.deref() {
114 Kind::File { handle, path, .. } => {
115 if let Some(h) = handle {
116 let mut h = h.write().unwrap();
117 wasi_try_ok!(h.unlink().map_err(fs_error_into_wasi_err));
118 } else {
119 let path = path.clone();
123 drop(guard);
124 wasi_try_ok!(state.fs_remove_file(path));
125 }
126 }
127 Kind::Dir { .. } | Kind::Root { .. } => return Ok(Errno::Isdir),
128 Kind::Symlink { .. } => {
129 drop(guard);
130 let errno = state.fs.remove_symlink_file(host_adjusted_path.as_path());
131 if errno != Errno::Success {
132 return Ok(errno);
133 }
134 }
135 _ => unimplemented!("wasi::path_unlink_file for Buffer"),
136 }
137 }
138 }
139
140 Ok(Errno::Success)
141}