wasmer_wasix/syscalls/wasi/
path_remove_directory.rs

1use std::fs;
2
3use super::*;
4use crate::syscalls::*;
5
6/// Returns Errno::Notemtpy if directory is not empty
7#[instrument(level = "trace", skip_all, fields(%fd, path = field::Empty), ret)]
8pub fn path_remove_directory<M: MemorySize>(
9    mut ctx: FunctionEnvMut<'_, WasiEnv>,
10    fd: WasiFd,
11    path: WasmPtr<u8, M>,
12    path_len: M::Offset,
13) -> Result<Errno, WasiError> {
14    WasiEnv::do_pending_operations(&mut ctx)?;
15
16    // TODO check if fd is a dir, ensure it's within sandbox, etc.
17    let env = ctx.data();
18    let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
19
20    let base_dir = wasi_try_ok!(state.fs.get_fd(fd));
21    let path_str = unsafe { get_input_str_ok!(&memory, path, path_len) };
22    Span::current().record("path", path_str.as_str());
23
24    wasi_try_ok!(path_remove_directory_internal(&mut ctx, fd, &path_str));
25    let env = ctx.data();
26
27    #[cfg(feature = "journal")]
28    if env.enable_journal {
29        wasi_try_ok!(
30            JournalEffector::save_path_remove_directory(&mut ctx, fd, path_str).map_err(|err| {
31                tracing::error!("failed to save remove directory event - {}", err);
32                Errno::Fault
33            })
34        )
35    }
36
37    Ok(Errno::Success)
38}
39
40pub(crate) fn path_remove_directory_internal(
41    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
42    fd: WasiFd,
43    path: &str,
44) -> Result<(), Errno> {
45    let env = ctx.data();
46    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
47    let working_dir = state.fs.get_fd(fd)?;
48
49    let (parent_inode, dir_name) =
50        state
51            .fs
52            .get_parent_inode_at_path(inodes, fd, Path::new(path), true)?;
53
54    let mut guard = parent_inode.write();
55    match guard.deref_mut() {
56        Kind::Dir {
57            entries: parent_entries,
58            ..
59        } => {
60            let Some(child_inode) = parent_entries.get(&dir_name) else {
61                return Err(Errno::Noent);
62            };
63
64            {
65                let Kind::Dir {
66                    entries: ref child_entries,
67                    path: ref child_path,
68                    ..
69                } = *child_inode.read()
70                else {
71                    return Err(Errno::Notdir);
72                };
73
74                if !child_entries.is_empty() {
75                    return Err(Errno::Notempty);
76                }
77
78                if let Err(e) = state.fs_remove_dir(child_path) {
79                    tracing::warn!(path = ?child_path, error = ?e, "failed to remove directory");
80                    return Err(e);
81                }
82            }
83
84            parent_entries.remove(&dir_name).expect(
85                "Entry should exist since we checked before and have an exclusive write lock",
86            );
87
88            Ok(())
89        }
90        Kind::Root { .. } => {
91            trace!("directories directly in the root node can not be removed");
92            Err(Errno::Access)
93        }
94        _ => {
95            trace!("path is not a directory");
96            Err(Errno::Notdir)
97        }
98    }
99}