wasmer_wasix/syscalls/wasi/
path_create_directory.rs

1use std::{
2    path::{Component, PathBuf},
3    str::FromStr,
4};
5
6use super::*;
7use crate::syscalls::*;
8
9/// ### `path_create_directory()`
10/// Create directory at a path
11/// Inputs:
12/// - `Fd fd`
13///     The directory that the path is relative to
14/// - `const char *path`
15///     String containing path data
16/// - `u32 path_len`
17///     The length of `path`
18/// Errors:
19/// Required Rights:
20/// - Rights::PATH_CREATE_DIRECTORY
21///     This right must be set on the directory that the file is created in (TODO: verify that this is true)
22#[instrument(level = "trace", skip_all, fields(%fd, path = field::Empty), ret)]
23pub fn path_create_directory<M: MemorySize>(
24    mut ctx: FunctionEnvMut<'_, WasiEnv>,
25    fd: WasiFd,
26    path: WasmPtr<u8, M>,
27    path_len: M::Offset,
28) -> Result<Errno, WasiError> {
29    WasiEnv::do_pending_operations(&mut ctx)?;
30
31    let env = ctx.data();
32    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
33
34    let mut path_string = unsafe { get_input_str_ok!(&memory, path, path_len) };
35    Span::current().record("path", path_string.as_str());
36
37    wasi_try_ok!(path_create_directory_internal(&mut ctx, fd, &path_string));
38    let env = ctx.data();
39
40    #[cfg(feature = "journal")]
41    if env.enable_journal {
42        JournalEffector::save_path_create_directory(&mut ctx, fd, path_string).map_err(|err| {
43            tracing::error!("failed to save create directory event - {}", err);
44            WasiError::Exit(ExitCode::from(Errno::Fault))
45        })?;
46    }
47
48    Ok(Errno::Success)
49}
50
51pub(crate) fn path_create_directory_internal(
52    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
53    fd: WasiFd,
54    path: &str,
55) -> Result<(), Errno> {
56    let env = ctx.data();
57    let (memory, state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
58    let working_dir = state.fs.get_fd(fd)?;
59
60    if !working_dir
61        .inner
62        .rights
63        .contains(Rights::PATH_CREATE_DIRECTORY)
64    {
65        trace!("working directory (fd={fd}) has no rights to create a directory");
66        return Err(Errno::Access);
67    }
68
69    let (parent_inode, dir_name) =
70        state
71            .fs
72            .get_parent_inode_at_path(inodes, fd, Path::new(path), true)?;
73
74    let mut guard = parent_inode.write();
75    match guard.deref_mut() {
76        Kind::Dir { entries, path, .. } => {
77            if let Some(child) = entries.get(&dir_name) {
78                return Err(Errno::Exist);
79            }
80
81            let mut new_dir_path = path.clone();
82            new_dir_path.push(&dir_name);
83
84            drop(guard);
85
86            // TODO: This condition should already have been checked by the entries.get check
87            // above, but it was in the code before my refactor and I'm keeping it just in case.
88            if path_filestat_get_internal(
89                &memory,
90                state,
91                inodes,
92                fd,
93                0,
94                &new_dir_path.to_string_lossy(),
95            )
96            .is_ok()
97            {
98                return Err(Errno::Exist);
99            }
100
101            state.fs_create_dir(&new_dir_path)?;
102
103            let kind = Kind::Dir {
104                parent: parent_inode.downgrade(),
105                path: new_dir_path,
106                entries: Default::default(),
107            };
108            let new_inode = state
109                .fs
110                .create_inode(inodes, kind, false, dir_name.clone())?;
111
112            // reborrow to insert
113            {
114                let mut guard = parent_inode.write();
115                let Kind::Dir { entries, .. } = guard.deref_mut() else {
116                    unreachable!();
117                };
118
119                entries.insert(dir_name, new_inode.clone());
120            }
121        }
122        Kind::Root { .. } => {
123            trace!("the root node can only contain pre-opened directories");
124            return Err(Errno::Access);
125        }
126        _ => {
127            trace!("path is not a directory");
128            return Err(Errno::Notdir);
129        }
130    }
131
132    Ok(())
133}