wasmer_wasix/syscalls/wasi/
path_unlink_file.rs

1use super::*;
2use crate::syscalls::*;
3
4/// ### `path_unlink_file()`
5/// Unlink a file, deleting if the number of hardlinks is 1
6/// Inputs:
7/// - `Fd fd`
8///     The base file descriptor from which the path is understood
9/// - `const char *path`
10///     Array of UTF-8 bytes representing the path
11/// - `u32 path_len`
12///     The number of bytes in the `path` array
13#[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, childs_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
66    let removed_inode = {
67        let mut guard = parent_inode.write();
68        match guard.deref_mut() {
69            Kind::Dir { entries, .. } => {
70                let removed_inode = wasi_try_ok!(entries.remove(&childs_name).ok_or(Errno::Inval));
71                // TODO: make this a debug assert in the future
72                assert!(inode.ino() == removed_inode.ino());
73                debug_assert!(inode.stat.read().unwrap().st_nlink > 0);
74                removed_inode
75            }
76            Kind::Root { .. } => return Ok(Errno::Access),
77            _ => unreachable!(
78                "Internal logic error in wasi::path_unlink_file, parent is not a directory"
79            ),
80        }
81    };
82
83    let st_nlink = {
84        let mut guard = removed_inode.stat.write().unwrap();
85        guard.st_nlink -= 1;
86        guard.st_nlink
87    };
88    if st_nlink == 0 {
89        {
90            let mut guard = removed_inode.read();
91            match guard.deref() {
92                Kind::File { handle, path, .. } => {
93                    if let Some(h) = handle {
94                        let mut h = h.write().unwrap();
95                        wasi_try_ok!(h.unlink().map_err(fs_error_into_wasi_err));
96                    } else {
97                        // File is closed
98                        // problem with the abstraction, we can't call unlink because there's no handle
99                        // drop mutable borrow on `path`
100                        let path = path.clone();
101                        drop(guard);
102                        wasi_try_ok!(state.fs_remove_file(path));
103                    }
104                }
105                Kind::Dir { .. } | Kind::Root { .. } => return Ok(Errno::Isdir),
106                Kind::Symlink { .. } => {
107                    // TODO: actually delete real symlinks and do nothing for virtual symlinks
108                }
109                _ => unimplemented!("wasi::path_unlink_file for Buffer"),
110            }
111        }
112    }
113
114    Ok(Errno::Success)
115}