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(
25        &mut ctx, fd, base_dir, &path_str
26    ));
27    let env = ctx.data();
28
29    #[cfg(feature = "journal")]
30    if env.enable_journal {
31        wasi_try_ok!(
32            JournalEffector::save_path_remove_directory(&mut ctx, fd, path_str).map_err(|err| {
33                tracing::error!("failed to save remove directory event - {}", err);
34                Errno::Fault
35            })
36        )
37    }
38
39    Ok(Errno::Success)
40}
41
42pub(crate) fn path_remove_directory_internal(
43    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
44    fd: WasiFd,
45    _base_dir: Fd,
46    path: &str,
47) -> Result<(), Errno> {
48    let env = ctx.data();
49    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
50
51    let (parent_inode, dir_name) =
52        state
53            .fs
54            .get_parent_inode_at_path(inodes, fd, Path::new(path), true)?;
55
56    let mut guard = parent_inode.write();
57    match guard.deref_mut() {
58        Kind::Dir {
59            entries: parent_entries,
60            ..
61        } => {
62            let Some(child_inode) = parent_entries.get(&dir_name) else {
63                return Err(Errno::Noent);
64            };
65
66            {
67                let Kind::Dir {
68                    entries: ref child_entries,
69                    path: ref child_path,
70                    ..
71                } = *child_inode.read()
72                else {
73                    return Err(Errno::Notdir);
74                };
75
76                if !child_entries.is_empty() {
77                    return Err(Errno::Notempty);
78                }
79
80                if let Err(e) = state.fs_remove_dir(child_path) {
81                    tracing::warn!(path = ?child_path, error = ?e, "failed to remove directory");
82                    return Err(e);
83                }
84            }
85
86            parent_entries.remove(&dir_name).expect(
87                "Entry should exist since we checked before and have an exclusive write lock",
88            );
89
90            Ok(())
91        }
92        Kind::Root { .. } => {
93            trace!("directories directly in the root node can not be removed");
94            Err(Errno::Access)
95        }
96        _ => {
97            trace!("path is not a directory");
98            Err(Errno::Notdir)
99        }
100    }
101}