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    let host_adjusted_path = {
66        let guard = parent_inode.read();
67        match guard.deref() {
68            Kind::Dir { path, .. } => path.join(&childs_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        match guard.deref_mut() {
79            Kind::Dir { entries, .. } => {
80                let removed_inode = wasi_try_ok!(entries.remove(&childs_name).ok_or(Errno::Inval));
81                // TODO: make this a debug assert in the future
82                assert!(inode.ino() == removed_inode.ino());
83                debug_assert!(inode.stat.read().unwrap().st_nlink > 0);
84                removed_inode
85            }
86            Kind::Root { .. } => return Ok(Errno::Access),
87            _ => unreachable!(
88                "Internal logic error in wasi::path_unlink_file, parent is not a directory"
89            ),
90        }
91    };
92
93    let st_nlink = {
94        let mut guard = removed_inode.stat.write().unwrap();
95        guard.st_nlink -= 1;
96        guard.st_nlink
97    };
98    if st_nlink == 0 {
99        {
100            let mut guard = removed_inode.read();
101            match guard.deref() {
102                Kind::File { handle, path, .. } => {
103                    if let Some(h) = handle {
104                        let mut h = h.write().unwrap();
105                        wasi_try_ok!(h.unlink().map_err(fs_error_into_wasi_err));
106                    } else {
107                        // File is closed
108                        // problem with the abstraction, we can't call unlink because there's no handle
109                        // drop mutable borrow on `path`
110                        let path = path.clone();
111                        drop(guard);
112                        wasi_try_ok!(state.fs_remove_file(path));
113                    }
114                }
115                Kind::Dir { .. } | Kind::Root { .. } => return Ok(Errno::Isdir),
116                Kind::Symlink { .. } => {
117                    match state.fs_remove_file(host_adjusted_path.as_path()) {
118                        Ok(()) => {}
119                        Err(Errno::Noent)
120                            if state
121                                .fs
122                                .ephemeral_symlink_at(host_adjusted_path.as_path())
123                                .is_some() => {}
124                        Err(err) => return Ok(err),
125                    }
126                    state
127                        .fs
128                        .unregister_ephemeral_symlink(host_adjusted_path.as_path());
129                }
130                _ => unimplemented!("wasi::path_unlink_file for Buffer"),
131            }
132        }
133    }
134
135    Ok(Errno::Success)
136}