wasmer_wasix/syscalls/wasi/
path_link.rs

1use super::*;
2use crate::syscalls::*;
3
4/// ### `path_link()`
5/// Create a hard link
6/// Inputs:
7/// - `Fd old_fd`
8///     The directory relative to which the `old_path` is
9/// - `LookupFlags old_flags`
10///     Flags to control how `old_path` is understood
11/// - `const char *old_path`
12///     String containing the old file path
13/// - `u32 old_path_len`
14///     Length of the `old_path` string
15/// - `Fd new_fd`
16///     The directory relative to which the `new_path` is
17/// - `const char *new_path`
18///     String containing the new file path
19/// - `u32 old_path_len`
20///     Length of the `new_path` string
21#[instrument(level = "trace", skip_all, fields(%old_fd, %new_fd, old_path = field::Empty, new_path = field::Empty, follow_symlinks = false), ret)]
22pub fn path_link<M: MemorySize>(
23    mut ctx: FunctionEnvMut<'_, WasiEnv>,
24    old_fd: WasiFd,
25    old_flags: LookupFlags,
26    old_path: WasmPtr<u8, M>,
27    old_path_len: M::Offset,
28    new_fd: WasiFd,
29    new_path: WasmPtr<u8, M>,
30    new_path_len: M::Offset,
31) -> Result<Errno, WasiError> {
32    WasiEnv::do_pending_operations(&mut ctx)?;
33
34    if old_flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 {
35        Span::current().record("follow_symlinks", true);
36    }
37    let env = ctx.data();
38    let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
39    let mut old_path_str = unsafe { get_input_str_ok!(&memory, old_path, old_path_len) };
40    Span::current().record("old_path", old_path_str.as_str());
41    let mut new_path_str = unsafe { get_input_str_ok!(&memory, new_path, new_path_len) };
42    Span::current().record("new_path", new_path_str.as_str());
43
44    wasi_try_ok!(path_link_internal(
45        &mut ctx,
46        old_fd,
47        old_flags,
48        &old_path_str,
49        new_fd,
50        &new_path_str
51    ));
52    let env = ctx.data();
53
54    #[cfg(feature = "journal")]
55    if env.enable_journal {
56        JournalEffector::save_path_link(
57            &mut ctx,
58            old_fd,
59            old_flags,
60            old_path_str,
61            new_fd,
62            new_path_str,
63        )
64        .map_err(|err| {
65            tracing::error!("failed to save path hard link event - {}", err);
66            WasiError::Exit(ExitCode::from(Errno::Fault))
67        })?;
68    }
69
70    Ok(Errno::Success)
71}
72
73pub(crate) fn path_link_internal(
74    ctx: &mut FunctionEnvMut<'_, WasiEnv>,
75    old_fd: WasiFd,
76    old_flags: LookupFlags,
77    old_path: &str,
78    new_fd: WasiFd,
79    new_path: &str,
80) -> Result<(), Errno> {
81    let env = ctx.data();
82    let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
83    let source_fd = state.fs.get_fd(old_fd)?;
84    let target_fd = state.fs.get_fd(new_fd)?;
85
86    if !source_fd.inner.rights.contains(Rights::PATH_LINK_SOURCE)
87        || !target_fd.inner.rights.contains(Rights::PATH_LINK_TARGET)
88    {
89        return Err(Errno::Access);
90    }
91
92    Span::current().record("old_path", old_path);
93    Span::current().record("new_path", new_path);
94
95    let source_inode = state.fs.get_inode_at_path(
96        inodes,
97        old_fd,
98        old_path,
99        old_flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0,
100    )?;
101    let target_path_arg = std::path::PathBuf::from(new_path);
102    let (target_parent_inode, new_entry_name) =
103        state
104            .fs
105            .get_parent_inode_at_path(inodes, new_fd, &target_path_arg, false)?;
106
107    if source_inode.stat.write().unwrap().st_nlink == Linkcount::MAX {
108        return Err(Errno::Mlink);
109    }
110    {
111        let mut guard = target_parent_inode.write();
112        match guard.deref_mut() {
113            Kind::Dir { entries, .. } => {
114                if entries.contains_key(&new_entry_name) {
115                    return Err(Errno::Exist);
116                }
117                entries.insert(new_entry_name, source_inode.clone());
118            }
119            Kind::Root { .. } => return Err(Errno::Inval),
120            Kind::File { .. }
121            | Kind::Symlink { .. }
122            | Kind::Buffer { .. }
123            | Kind::Socket { .. }
124            | Kind::PipeTx { .. }
125            | Kind::PipeRx { .. }
126            | Kind::DuplexPipe { .. }
127            | Kind::EventNotifications { .. }
128            | Kind::Epoll { .. } => return Err(Errno::Notdir),
129        }
130    }
131    source_inode.stat.write().unwrap().st_nlink += 1;
132
133    Ok(())
134}