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 let effective_dirfd = if path.starts_with('/') {
132 VIRTUAL_ROOT_FD
133 } else {
134 dirfd
135 };
136 let path_arg = std::path::PathBuf::from(path);
137 let working_dir = match state.fs.get_fd(effective_dirfd) {
138 Ok(fd) => fd,
139 Err(err) => return Ok(Err(err)),
140 };
141 let maybe_inode = state
142 .fs
143 .get_inode_at_path(inodes, effective_dirfd, path, follow_symlinks);
144 let working_dir_rights_inheriting = working_dir.inner.rights_inheriting;
145
146 if !working_dir.inner.rights.contains(Rights::PATH_OPEN) {
148 return Ok(Err(Errno::Access));
149 }
150
151 let mut open_flags = 0;
152 let adjusted_rights = working_dir_rights_inheriting;
159 let mut open_options = state.fs_new_open_options();
160
161 let target_rights = match maybe_inode {
162 Ok(_) => {
163 let write_permission = adjusted_rights.contains(Rights::FD_WRITE);
164
165 let (append_permission, truncate_permission, create_permission) = if write_permission {
167 (
168 fs_flags.contains(Fdflags::APPEND),
169 o_flags.contains(Oflags::TRUNC),
170 o_flags.contains(Oflags::CREATE),
171 )
172 } else {
173 (false, false, false)
174 };
175
176 virtual_fs::OpenOptionsConfig {
177 read: fs_rights_base.contains(Rights::FD_READ),
178 write: write_permission,
179 create_new: create_permission && o_flags.contains(Oflags::EXCL),
180 create: create_permission,
181 append: append_permission,
182 truncate: truncate_permission,
183 }
184 }
185 Err(_) => virtual_fs::OpenOptionsConfig {
186 append: fs_flags.contains(Fdflags::APPEND),
187 write: fs_rights_base.contains(Rights::FD_WRITE),
188 read: fs_rights_base.contains(Rights::FD_READ),
189 create_new: o_flags.contains(Oflags::CREATE) && o_flags.contains(Oflags::EXCL),
190 create: o_flags.contains(Oflags::CREATE),
191 truncate: o_flags.contains(Oflags::TRUNC),
192 },
193 };
194
195 let parent_rights = virtual_fs::OpenOptionsConfig {
196 read: working_dir.inner.rights.contains(Rights::FD_READ),
197 write: working_dir.inner.rights.contains(Rights::FD_WRITE),
198 create_new: true,
201 create: true,
202 append: true,
203 truncate: true,
204 };
205
206 let minimum_rights = target_rights.minimum_rights(&parent_rights);
207
208 open_options.options(minimum_rights.clone());
209
210 let orig_path = path;
211
212 let inode = if let Ok(inode) = maybe_inode {
213 let processing_inode = inode.clone();
215 let mut guard = processing_inode.write();
216
217 let deref_mut = guard.deref_mut();
218
219 if o_flags.contains(Oflags::EXCL) && o_flags.contains(Oflags::CREATE) {
220 return Ok(Err(Errno::Exist));
221 }
222
223 match deref_mut {
224 Kind::File {
225 handle, path, fd, ..
226 } => {
227 if let Some(special_fd) = fd {
228 assert!(handle.is_some());
230 return Ok(Ok(*special_fd));
231 }
232 if o_flags.contains(Oflags::DIRECTORY) || orig_path.ends_with('/') {
233 return Ok(Err(Errno::Notdir));
234 }
235
236 let open_options = open_options
237 .write(minimum_rights.write)
238 .create(minimum_rights.create)
239 .append(false)
240 .truncate(minimum_rights.truncate);
241
242 if minimum_rights.read {
243 open_flags |= Fd::READ;
244 }
245 if minimum_rights.write {
246 open_flags |= Fd::WRITE;
247 }
248 if minimum_rights.create {
249 open_flags |= Fd::CREATE;
250 }
251 if minimum_rights.truncate {
252 open_flags |= Fd::TRUNCATE;
253 }
254 let requires_stronger_handle =
257 minimum_rights.write || minimum_rights.truncate || minimum_rights.create;
258 if handle.is_none() {
259 *handle = Some(Arc::new(std::sync::RwLock::new(wasi_try_ok_ok!(
260 open_options.open(&path).map_err(fs_error_into_wasi_err)
261 ))));
262 } else if requires_stronger_handle {
263 let mut file = handle.as_ref().unwrap().write().unwrap();
264 *file =
265 wasi_try_ok_ok!(open_options.open(&path).map_err(fs_error_into_wasi_err));
266 }
267
268 if let Some(handle) = handle {
269 let handle = handle.read().unwrap();
270 if let Some(fd) = handle.get_special_fd() {
271 let dup_fd = wasi_try_ok_ok!(state.fs.clone_fd(fd));
274 trace!(
275 %dup_fd
276 );
277
278 return Ok(Ok(dup_fd));
281 }
282 }
283 }
284 Kind::Buffer { .. } => unimplemented!("wasi::path_open for Buffer type files"),
285 Kind::Root { .. } => {
286 if !o_flags.contains(Oflags::DIRECTORY) {
287 return Ok(Err(Errno::Isdir));
288 }
289 }
290 Kind::Dir { .. } => {
291 if fs_rights_base.contains(Rights::FD_WRITE) {
292 return Ok(Err(Errno::Isdir));
293 }
294 }
295 Kind::Socket { .. }
296 | Kind::PipeTx { .. }
297 | Kind::PipeRx { .. }
298 | Kind::DuplexPipe { .. }
299 | Kind::EventNotifications { .. }
300 | Kind::Epoll { .. } => {}
301 Kind::Symlink {
302 base_po_dir,
303 path_to_symlink,
304 relative_path,
305 } => {
306 let (resolved_base_fd, resolved_path) = if relative_path.is_absolute() {
309 (VIRTUAL_ROOT_FD, relative_path.clone())
310 } else {
311 let mut resolved_path = path_to_symlink.clone();
312 resolved_path.pop();
313 resolved_path.push(relative_path);
314 (*base_po_dir, resolved_path)
315 };
316 return path_open_internal(
317 env,
318 resolved_base_fd,
319 __WASI_LOOKUP_SYMLINK_FOLLOW,
320 &resolved_path.to_string_lossy(),
321 o_flags,
322 fs_rights_base,
323 fs_rights_inheriting,
324 fs_flags,
325 fd_flags,
326 with_fd,
327 );
328 }
329 }
330 inode
331 } else {
332 if o_flags.contains(Oflags::CREATE) {
334 if o_flags.contains(Oflags::DIRECTORY) {
335 return Ok(Err(Errno::Notdir));
336 }
337
338 if path.ends_with('/') {
340 return Ok(Err(Errno::Isdir));
341 }
342
343 let (parent_inode, new_entity_name) =
346 wasi_try_ok_ok!(state.fs.get_parent_inode_at_path(
347 inodes,
348 effective_dirfd,
349 &path_arg,
350 follow_symlinks
351 ));
352 let new_file_host_path = {
353 let guard = parent_inode.read();
354 match guard.deref() {
355 Kind::Dir { path, .. } => {
356 let mut new_path = path.clone();
357 new_path.push(&new_entity_name);
358 new_path
359 }
360 Kind::Root { .. } => {
361 let mut new_path = std::path::PathBuf::new();
362 new_path.push(&new_entity_name);
363 new_path
364 }
365 _ => return Ok(Err(Errno::Inval)),
366 }
367 };
368 let handle = {
371 let open_options = open_options
374 .read(minimum_rights.read)
375 .append(minimum_rights.append)
376 .write(minimum_rights.write)
377 .create_new(true);
378
379 if minimum_rights.read {
380 open_flags |= Fd::READ;
381 }
382 if minimum_rights.write {
383 open_flags |= Fd::WRITE;
384 }
385 if minimum_rights.create_new {
386 open_flags |= Fd::CREATE;
387 }
388 if minimum_rights.truncate {
389 open_flags |= Fd::TRUNCATE;
390 }
391
392 match open_options.open(&new_file_host_path) {
393 Ok(handle) => Some(handle),
394 Err(err) => {
395 if err == FsError::AlreadyExists {
399 return Ok(Err(Errno::Perm));
400 }
401
402 return Ok(Err(fs_error_into_wasi_err(err)));
403 }
404 }
405 };
406
407 let new_inode = {
408 let kind = Kind::File {
409 handle: handle.map(|a| Arc::new(std::sync::RwLock::new(a))),
410 path: new_file_host_path,
411 fd: None,
412 };
413 wasi_try_ok_ok!(
414 state
415 .fs
416 .create_inode(inodes, kind, false, new_entity_name.clone())
417 )
418 };
419
420 {
421 let mut guard = parent_inode.write();
422 if let Kind::Dir { entries, .. } = guard.deref_mut() {
423 entries.insert(new_entity_name, new_inode.clone());
424 }
425 }
426
427 new_inode
428 } else {
429 return Ok(Err(maybe_inode.unwrap_err()));
430 }
431 };
432
433 let out_fd = wasi_try_ok_ok!(if let Some(fd) = with_fd {
436 state
437 .fs
438 .with_fd(
439 adjusted_rights,
440 fs_rights_inheriting,
441 fs_flags,
442 fd_flags,
443 open_flags,
444 inode,
445 fd,
446 )
447 .map(|_| fd)
448 } else {
449 state.fs.create_fd(
450 adjusted_rights,
451 fs_rights_inheriting,
452 fs_flags,
453 fd_flags,
454 open_flags,
455 inode,
456 )
457 });
458
459 Ok(Ok(out_fd))
460}