wasmer_wasix/syscalls/wasi/
path_symlink.rs1use super::*;
2use crate::syscalls::*;
3
4#[instrument(level = "trace", skip_all, fields(%fd, old_path = field::Empty, new_path = field::Empty), ret)]
18pub fn path_symlink<M: MemorySize>(
19 mut ctx: FunctionEnvMut<'_, WasiEnv>,
20 old_path: WasmPtr<u8, M>,
21 old_path_len: M::Offset,
22 fd: WasiFd,
23 new_path: WasmPtr<u8, M>,
24 new_path_len: M::Offset,
25) -> Result<Errno, WasiError> {
26 WasiEnv::do_pending_operations(&mut ctx)?;
27
28 let env = ctx.data();
29 let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
30 let old_path_str = unsafe { get_input_str_ok!(&memory, old_path, old_path_len) };
31 Span::current().record("old_path", old_path_str.as_str());
32 let new_path_str = unsafe { get_input_str_ok!(&memory, new_path, new_path_len) };
33 Span::current().record("new_path", new_path_str.as_str());
34
35 wasi_try_ok!(path_symlink_internal(
36 &mut ctx,
37 &old_path_str,
38 fd,
39 &new_path_str
40 ));
41 let env = ctx.data();
42
43 #[cfg(feature = "journal")]
44 if env.enable_journal {
45 JournalEffector::save_path_symlink(&mut ctx, old_path_str, fd, new_path_str).map_err(
46 |err| {
47 tracing::error!("failed to save path symbolic link event - {}", err);
48 WasiError::Exit(ExitCode::from(Errno::Fault))
49 },
50 )?;
51 }
52
53 Ok(Errno::Success)
54}
55
56pub fn path_symlink_internal(
57 ctx: &mut FunctionEnvMut<'_, WasiEnv>,
58 old_path: &str,
59 fd: WasiFd,
60 new_path: &str,
61) -> Result<(), Errno> {
62 let env = ctx.data();
63 let (memory, mut state, inodes) = unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
64
65 let base_fd = state.fs.get_fd(fd)?;
66 if !base_fd.inner.rights.contains(Rights::PATH_SYMLINK) {
67 return Err(Errno::Access);
68 }
69
70 let new_path_path = std::path::Path::new(new_path);
71 let (target_parent_inode, entry_name) =
72 state
73 .fs
74 .get_parent_inode_at_path(inodes, fd, new_path_path, true)?;
75
76 let symlink_path = {
77 let guard = target_parent_inode.read();
78 match guard.deref() {
79 Kind::Dir { path, .. } => {
80 let mut symlink_path = path.clone();
81 symlink_path.push(&entry_name);
82 symlink_path
83 }
84 Kind::Root { .. } => {
85 let mut symlink_path = std::path::PathBuf::from("/");
86 symlink_path.push(&entry_name);
87 symlink_path
88 }
89 _ => unreachable!("parent inode should be a directory"),
90 }
91 };
92
93 let (base_po_dir, path_to_symlink) = state
96 .fs
97 .path_into_pre_open_and_relative_path_owned(&symlink_path)?;
98 let relative_path = std::path::PathBuf::from(old_path);
99
100 {
102 let guard = target_parent_inode.read();
103 match guard.deref() {
104 Kind::Dir { entries, .. } => {
105 if entries.contains_key(&entry_name) {
106 return Err(Errno::Exist);
107 }
108 }
109 Kind::Root { .. } => return Err(Errno::Notcapable),
110 Kind::Socket { .. }
111 | Kind::PipeRx { .. }
112 | Kind::PipeTx { .. }
113 | Kind::DuplexPipe { .. }
114 | Kind::EventNotifications { .. }
115 | Kind::Epoll { .. } => return Err(Errno::Inval),
116 Kind::File { .. } | Kind::Symlink { .. } | Kind::Buffer { .. } => {
117 unreachable!("get_parent_inode_at_path returned something other than a Dir or Root")
118 }
119 }
120 }
121
122 let source_path = std::path::Path::new(old_path);
123 let target_path = symlink_path.as_path();
124 let persisted_in_backing_fs = state.fs.root_fs.create_symlink(source_path, target_path);
125
126 let needs_ephemeral_fallback = match persisted_in_backing_fs {
127 Ok(()) => false,
128 Err(virtual_fs::FsError::Unsupported) => true,
129 Err(err) => return Err(fs_error_into_wasi_err(err)),
130 };
131
132 let kind = Kind::Symlink {
133 base_po_dir,
134 path_to_symlink: path_to_symlink.clone(),
135 relative_path: relative_path.clone(),
136 };
137 let new_inode =
138 state
139 .fs
140 .create_inode_with_default_stat(inodes, kind, false, entry_name.clone().into());
141
142 {
143 let mut guard = target_parent_inode.write();
144 if let Kind::Dir { entries, .. } = guard.deref_mut() {
145 entries.insert(entry_name, new_inode);
146 }
147 }
148
149 if needs_ephemeral_fallback {
151 state.fs.register_ephemeral_symlink(
152 symlink_path,
153 base_po_dir,
154 path_to_symlink,
155 relative_path,
156 );
157 } else {
158 state.fs.unregister_ephemeral_symlink(target_path);
159 }
160
161 Ok(())
162}