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