wasmer_wasix/syscalls/wasix/
path_open2.rs1use super::*;
2use crate::VIRTUAL_ROOT_FD;
3use crate::syscalls::*;
4
5#[instrument(level = "trace", skip_all, fields(%dirfd, path = field::Empty, follow_symlinks = field::Empty, ret_fd = field::Empty), ret)]
30pub fn path_open2<M: MemorySize>(
31 mut ctx: FunctionEnvMut<'_, WasiEnv>,
32 dirfd: WasiFd,
33 dirflags: LookupFlags,
34 path: WasmPtr<u8, M>,
35 path_len: M::Offset,
36 o_flags: Oflags,
37 fs_rights_base: Rights,
38 fs_rights_inheriting: Rights,
39 fs_flags: Fdflags,
40 fd_flags: Fdflagsext,
41 fd: WasmPtr<WasiFd, M>,
42) -> Result<Errno, WasiError> {
43 WasiEnv::do_pending_operations(&mut ctx)?;
44
45 if dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 {
46 Span::current().record("follow_symlinks", true);
47 }
48 let env = ctx.data();
49 let (memory, mut state, mut inodes) =
50 unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
51 let path_len64: u64 = path_len.into();
53 if path_len64 > 1024u64 * 1024u64 {
54 return Ok(Errno::Nametoolong);
55 }
56
57 if path_len64 == 0 {
58 return Ok(Errno::Noent);
59 }
60
61 let path_string = unsafe { get_input_str_ok!(&memory, path, path_len) };
68 Span::current().record("path", path_string.as_str());
69
70 let out_fd = wasi_try_ok!(path_open_internal(
71 ctx.data(),
72 dirfd,
73 dirflags,
74 &path_string,
75 o_flags,
76 fs_rights_base,
77 fs_rights_inheriting,
78 fs_flags,
79 fd_flags,
80 None,
81 )?);
82 let env = ctx.data();
83
84 #[cfg(feature = "journal")]
85 if env.enable_journal {
86 JournalEffector::save_path_open(
87 &mut ctx,
88 out_fd,
89 dirfd,
90 dirflags,
91 path_string,
92 o_flags,
93 fs_rights_base,
94 fs_rights_inheriting,
95 fs_flags,
96 fd_flags,
97 )
98 .map_err(|err| {
99 tracing::error!("failed to save unlink event - {}", err);
100 WasiError::Exit(ExitCode::from(Errno::Fault))
101 })?;
102 }
103
104 let env = ctx.data();
105 let (memory, mut state, mut inodes) =
106 unsafe { env.get_memory_and_wasi_state_and_inodes(&ctx, 0) };
107
108 Span::current().record("ret_fd", out_fd);
109
110 let fd_ref = fd.deref(&memory);
111 wasi_try_mem_ok!(fd_ref.write(out_fd));
112
113 Ok(Errno::Success)
114}
115
116pub(crate) fn path_open_internal(
117 env: &WasiEnv,
118 dirfd: WasiFd,
119 dirflags: LookupFlags,
120 path: &str,
121 o_flags: Oflags,
122 fs_rights_base: Rights,
123 fs_rights_inheriting: Rights,
124 fs_flags: Fdflags,
125 fd_flags: Fdflagsext,
126 with_fd: Option<WasiFd>,
127) -> Result<Result<WasiFd, Errno>, WasiError> {
128 let state = env.state.deref();
129 let inodes = &state.inodes;
130 let follow_symlinks = dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0;
131
132 let path_arg = std::path::PathBuf::from(&path);
133 let maybe_inode = state
134 .fs
135 .get_inode_at_path(inodes, dirfd, path, follow_symlinks);
136
137 let working_dir = wasi_try_ok_ok!(state.fs.get_fd(dirfd));
138 let working_dir_rights_inheriting = working_dir.inner.rights_inheriting;
139
140 if !working_dir.inner.rights.contains(Rights::PATH_OPEN) {
142 return Ok(Err(Errno::Access));
143 }
144
145 let mut open_flags = 0;
146 let adjusted_rights = working_dir_rights_inheriting;
153 let mut open_options = state.fs_new_open_options();
154
155 let target_rights = match maybe_inode {
156 Ok(_) => {
157 let write_permission = adjusted_rights.contains(Rights::FD_WRITE);
158
159 let (append_permission, truncate_permission, create_permission) = if write_permission {
161 (
162 fs_flags.contains(Fdflags::APPEND),
163 o_flags.contains(Oflags::TRUNC),
164 o_flags.contains(Oflags::CREATE),
165 )
166 } else {
167 (false, false, false)
168 };
169
170 virtual_fs::OpenOptionsConfig {
171 read: fs_rights_base.contains(Rights::FD_READ),
172 write: write_permission,
173 create_new: create_permission && o_flags.contains(Oflags::EXCL),
174 create: create_permission,
175 append: append_permission,
176 truncate: truncate_permission,
177 }
178 }
179 Err(_) => virtual_fs::OpenOptionsConfig {
180 append: fs_flags.contains(Fdflags::APPEND),
181 write: fs_rights_base.contains(Rights::FD_WRITE),
182 read: fs_rights_base.contains(Rights::FD_READ),
183 create_new: o_flags.contains(Oflags::CREATE) && o_flags.contains(Oflags::EXCL),
184 create: o_flags.contains(Oflags::CREATE),
185 truncate: o_flags.contains(Oflags::TRUNC),
186 },
187 };
188
189 let parent_rights = virtual_fs::OpenOptionsConfig {
190 read: working_dir.inner.rights.contains(Rights::FD_READ),
191 write: working_dir.inner.rights.contains(Rights::FD_WRITE),
192 create_new: true,
195 create: true,
196 append: true,
197 truncate: true,
198 };
199
200 let minimum_rights = target_rights.minimum_rights(&parent_rights);
201
202 open_options.options(minimum_rights.clone());
203
204 let orig_path = path;
205
206 let inode = if let Ok(inode) = maybe_inode {
207 let processing_inode = inode.clone();
209 let mut guard = processing_inode.write();
210
211 let deref_mut = guard.deref_mut();
212
213 if o_flags.contains(Oflags::EXCL) && o_flags.contains(Oflags::CREATE) {
214 return Ok(Err(Errno::Exist));
215 }
216
217 match deref_mut {
218 Kind::File {
219 handle, path, fd, ..
220 } => {
221 if let Some(special_fd) = fd {
222 assert!(handle.is_some());
224 return Ok(Ok(*special_fd));
225 }
226 if o_flags.contains(Oflags::DIRECTORY) || orig_path.ends_with('/') {
227 return Ok(Err(Errno::Notdir));
228 }
229
230 let open_options = open_options
231 .write(minimum_rights.write)
232 .create(minimum_rights.create)
233 .append(false)
234 .truncate(minimum_rights.truncate);
235
236 if minimum_rights.read {
237 open_flags |= Fd::READ;
238 }
239 if minimum_rights.write {
240 open_flags |= Fd::WRITE;
241 }
242 if minimum_rights.create {
243 open_flags |= Fd::CREATE;
244 }
245 if minimum_rights.truncate {
246 open_flags |= Fd::TRUNCATE;
247 }
248 *handle = Some(Arc::new(std::sync::RwLock::new(wasi_try_ok_ok!(
251 open_options.open(&path).map_err(fs_error_into_wasi_err)
252 ))));
253
254 if let Some(handle) = handle {
255 let handle = handle.read().unwrap();
256 if let Some(fd) = handle.get_special_fd() {
257 let dup_fd = wasi_try_ok_ok!(state.fs.clone_fd(fd));
260 trace!(
261 %dup_fd
262 );
263
264 return Ok(Ok(dup_fd));
267 }
268 }
269 }
270 Kind::Buffer { .. } => unimplemented!("wasi::path_open for Buffer type files"),
271 Kind::Root { .. } => {
272 if !o_flags.contains(Oflags::DIRECTORY) {
273 return Ok(Err(Errno::Isdir));
274 }
275 }
276 Kind::Dir { .. } => {
277 if fs_rights_base.contains(Rights::FD_WRITE) {
278 return Ok(Err(Errno::Isdir));
279 }
280 }
281 Kind::Socket { .. }
282 | Kind::PipeTx { .. }
283 | Kind::PipeRx { .. }
284 | Kind::DuplexPipe { .. }
285 | Kind::EventNotifications { .. }
286 | Kind::Epoll { .. } => {}
287 Kind::Symlink {
288 base_po_dir,
289 path_to_symlink,
290 relative_path,
291 } => {
292 let (resolved_base_fd, resolved_path) = if relative_path.is_absolute() {
295 (VIRTUAL_ROOT_FD, relative_path.clone())
296 } else {
297 let mut resolved_path = path_to_symlink.clone();
298 resolved_path.pop();
299 resolved_path.push(relative_path);
300 (*base_po_dir, resolved_path)
301 };
302 return path_open_internal(
303 env,
304 resolved_base_fd,
305 __WASI_LOOKUP_SYMLINK_FOLLOW,
306 &resolved_path.to_string_lossy(),
307 o_flags,
308 fs_rights_base,
309 fs_rights_inheriting,
310 fs_flags,
311 fd_flags,
312 with_fd,
313 );
314 }
315 }
316 inode
317 } else {
318 if o_flags.contains(Oflags::CREATE) {
320 if o_flags.contains(Oflags::DIRECTORY) {
321 return Ok(Err(Errno::Notdir));
322 }
323
324 if path.ends_with('/') {
326 return Ok(Err(Errno::Isdir));
327 }
328
329 let (parent_inode, new_entity_name) = wasi_try_ok_ok!(
332 state
333 .fs
334 .get_parent_inode_at_path(inodes, dirfd, &path_arg, follow_symlinks)
335 );
336 let new_file_host_path = {
337 let guard = parent_inode.read();
338 match guard.deref() {
339 Kind::Dir { path, .. } => {
340 let mut new_path = path.clone();
341 new_path.push(&new_entity_name);
342 new_path
343 }
344 Kind::Root { .. } => {
345 let mut new_path = std::path::PathBuf::new();
346 new_path.push(&new_entity_name);
347 new_path
348 }
349 _ => return Ok(Err(Errno::Inval)),
350 }
351 };
352 let handle = {
355 let open_options = open_options
358 .read(minimum_rights.read)
359 .append(minimum_rights.append)
360 .write(minimum_rights.write)
361 .create_new(true);
362
363 if minimum_rights.read {
364 open_flags |= Fd::READ;
365 }
366 if minimum_rights.write {
367 open_flags |= Fd::WRITE;
368 }
369 if minimum_rights.create_new {
370 open_flags |= Fd::CREATE;
371 }
372 if minimum_rights.truncate {
373 open_flags |= Fd::TRUNCATE;
374 }
375
376 match open_options.open(&new_file_host_path) {
377 Ok(handle) => Some(handle),
378 Err(err) => {
379 if err == FsError::AlreadyExists {
383 return Ok(Err(Errno::Perm));
384 }
385
386 return Ok(Err(fs_error_into_wasi_err(err)));
387 }
388 }
389 };
390
391 let new_inode = {
392 let kind = Kind::File {
393 handle: handle.map(|a| Arc::new(std::sync::RwLock::new(a))),
394 path: new_file_host_path,
395 fd: None,
396 };
397 wasi_try_ok_ok!(
398 state
399 .fs
400 .create_inode(inodes, kind, false, new_entity_name.clone())
401 )
402 };
403
404 {
405 let mut guard = parent_inode.write();
406 if let Kind::Dir { entries, .. } = guard.deref_mut() {
407 entries.insert(new_entity_name, new_inode.clone());
408 }
409 }
410
411 new_inode
412 } else {
413 return Ok(Err(maybe_inode.unwrap_err()));
414 }
415 };
416
417 let out_fd = wasi_try_ok_ok!(if let Some(fd) = with_fd {
420 state
421 .fs
422 .with_fd(
423 adjusted_rights,
424 fs_rights_inheriting,
425 fs_flags,
426 fd_flags,
427 open_flags,
428 inode,
429 fd,
430 )
431 .map(|_| fd)
432 } else {
433 state.fs.create_fd(
434 adjusted_rights,
435 fs_rights_inheriting,
436 fs_flags,
437 fd_flags,
438 open_flags,
439 inode,
440 )
441 });
442
443 Ok(Ok(out_fd))
444}