]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/wasi/fs.rs
Auto merge of #97176 - kraktus:cmd_debug, r=the8472
[rust.git] / library / std / src / sys / wasi / fs.rs
1 #![deny(unsafe_op_in_unsafe_fn)]
2
3 use super::fd::WasiFd;
4 use crate::ffi::{CStr, OsStr, OsString};
5 use crate::fmt;
6 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
7 use crate::iter;
8 use crate::mem::{self, ManuallyDrop};
9 use crate::os::raw::c_int;
10 use crate::os::wasi::ffi::{OsStrExt, OsStringExt};
11 use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
12 use crate::path::{Path, PathBuf};
13 use crate::ptr;
14 use crate::sync::Arc;
15 use crate::sys::common::small_c_string::run_path_with_cstr;
16 use crate::sys::time::SystemTime;
17 use crate::sys::unsupported;
18 use crate::sys_common::{AsInner, FromInner, IntoInner};
19
20 pub use crate::sys_common::fs::try_exists;
21
22 pub struct File {
23     fd: WasiFd,
24 }
25
26 #[derive(Clone)]
27 pub struct FileAttr {
28     meta: wasi::Filestat,
29 }
30
31 pub struct ReadDir {
32     inner: Arc<ReadDirInner>,
33     cookie: Option<wasi::Dircookie>,
34     buf: Vec<u8>,
35     offset: usize,
36     cap: usize,
37 }
38
39 struct ReadDirInner {
40     root: PathBuf,
41     dir: File,
42 }
43
44 pub struct DirEntry {
45     meta: wasi::Dirent,
46     name: Vec<u8>,
47     inner: Arc<ReadDirInner>,
48 }
49
50 #[derive(Clone, Debug, Default)]
51 pub struct OpenOptions {
52     read: bool,
53     write: bool,
54     append: bool,
55     dirflags: wasi::Lookupflags,
56     fdflags: wasi::Fdflags,
57     oflags: wasi::Oflags,
58     rights_base: Option<wasi::Rights>,
59     rights_inheriting: Option<wasi::Rights>,
60 }
61
62 #[derive(Clone, PartialEq, Eq, Debug)]
63 pub struct FilePermissions {
64     readonly: bool,
65 }
66
67 #[derive(Copy, Clone, Debug, Default)]
68 pub struct FileTimes {
69     accessed: Option<SystemTime>,
70     modified: Option<SystemTime>,
71 }
72
73 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
74 pub struct FileType {
75     bits: wasi::Filetype,
76 }
77
78 #[derive(Debug)]
79 pub struct DirBuilder {}
80
81 impl FileAttr {
82     pub fn size(&self) -> u64 {
83         self.meta.size
84     }
85
86     pub fn perm(&self) -> FilePermissions {
87         // not currently implemented in wasi yet
88         FilePermissions { readonly: false }
89     }
90
91     pub fn file_type(&self) -> FileType {
92         FileType { bits: self.meta.filetype }
93     }
94
95     pub fn modified(&self) -> io::Result<SystemTime> {
96         Ok(SystemTime::from_wasi_timestamp(self.meta.mtim))
97     }
98
99     pub fn accessed(&self) -> io::Result<SystemTime> {
100         Ok(SystemTime::from_wasi_timestamp(self.meta.atim))
101     }
102
103     pub fn created(&self) -> io::Result<SystemTime> {
104         Ok(SystemTime::from_wasi_timestamp(self.meta.ctim))
105     }
106
107     pub fn as_wasi(&self) -> &wasi::Filestat {
108         &self.meta
109     }
110 }
111
112 impl FilePermissions {
113     pub fn readonly(&self) -> bool {
114         self.readonly
115     }
116
117     pub fn set_readonly(&mut self, readonly: bool) {
118         self.readonly = readonly;
119     }
120 }
121
122 impl FileTimes {
123     pub fn set_accessed(&mut self, t: SystemTime) {
124         self.accessed = Some(t);
125     }
126
127     pub fn set_modified(&mut self, t: SystemTime) {
128         self.modified = Some(t);
129     }
130 }
131
132 impl FileType {
133     pub fn is_dir(&self) -> bool {
134         self.bits == wasi::FILETYPE_DIRECTORY
135     }
136
137     pub fn is_file(&self) -> bool {
138         self.bits == wasi::FILETYPE_REGULAR_FILE
139     }
140
141     pub fn is_symlink(&self) -> bool {
142         self.bits == wasi::FILETYPE_SYMBOLIC_LINK
143     }
144
145     pub fn bits(&self) -> wasi::Filetype {
146         self.bits
147     }
148 }
149
150 impl ReadDir {
151     fn new(dir: File, root: PathBuf) -> ReadDir {
152         ReadDir {
153             cookie: Some(0),
154             buf: vec![0; 128],
155             offset: 0,
156             cap: 0,
157             inner: Arc::new(ReadDirInner { dir, root }),
158         }
159     }
160 }
161
162 impl fmt::Debug for ReadDir {
163     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164         f.debug_struct("ReadDir").finish_non_exhaustive()
165     }
166 }
167
168 impl Iterator for ReadDir {
169     type Item = io::Result<DirEntry>;
170
171     fn next(&mut self) -> Option<io::Result<DirEntry>> {
172         loop {
173             // If we've reached the capacity of our buffer then we need to read
174             // some more from the OS, otherwise we pick up at our old offset.
175             let offset = if self.offset == self.cap {
176                 let cookie = self.cookie.take()?;
177                 match self.inner.dir.fd.readdir(&mut self.buf, cookie) {
178                     Ok(bytes) => self.cap = bytes,
179                     Err(e) => return Some(Err(e)),
180                 }
181                 self.offset = 0;
182                 self.cookie = Some(cookie);
183
184                 // If we didn't actually read anything, this is in theory the
185                 // end of the directory.
186                 if self.cap == 0 {
187                     self.cookie = None;
188                     return None;
189                 }
190
191                 0
192             } else {
193                 self.offset
194             };
195             let data = &self.buf[offset..self.cap];
196
197             // If we're not able to read a directory entry then that means it
198             // must have been truncated at the end of the buffer, so reset our
199             // offset so we can go back and reread into the buffer, picking up
200             // where we last left off.
201             let dirent_size = mem::size_of::<wasi::Dirent>();
202             if data.len() < dirent_size {
203                 assert!(self.cookie.is_some());
204                 assert!(self.buf.len() >= dirent_size);
205                 self.offset = self.cap;
206                 continue;
207             }
208             let (dirent, data) = data.split_at(dirent_size);
209             let dirent = unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) };
210
211             // If the file name was truncated, then we need to reinvoke
212             // `readdir` so we truncate our buffer to start over and reread this
213             // descriptor. Note that if our offset is 0 that means the file name
214             // is massive and we need a bigger buffer.
215             if data.len() < dirent.d_namlen as usize {
216                 if offset == 0 {
217                     let amt_to_add = self.buf.capacity();
218                     self.buf.extend(iter::repeat(0).take(amt_to_add));
219                 }
220                 assert!(self.cookie.is_some());
221                 self.offset = self.cap;
222                 continue;
223             }
224             self.cookie = Some(dirent.d_next);
225             self.offset = offset + dirent_size + dirent.d_namlen as usize;
226
227             let name = &data[..(dirent.d_namlen as usize)];
228
229             // These names are skipped on all other platforms, so let's skip
230             // them here too
231             if name == b"." || name == b".." {
232                 continue;
233             }
234
235             return Some(Ok(DirEntry {
236                 meta: dirent,
237                 name: name.to_vec(),
238                 inner: self.inner.clone(),
239             }));
240         }
241     }
242 }
243
244 impl DirEntry {
245     pub fn path(&self) -> PathBuf {
246         let name = OsStr::from_bytes(&self.name);
247         self.inner.root.join(name)
248     }
249
250     pub fn file_name(&self) -> OsString {
251         OsString::from_vec(self.name.clone())
252     }
253
254     pub fn metadata(&self) -> io::Result<FileAttr> {
255         metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref())
256     }
257
258     pub fn file_type(&self) -> io::Result<FileType> {
259         Ok(FileType { bits: self.meta.d_type })
260     }
261
262     pub fn ino(&self) -> wasi::Inode {
263         self.meta.d_ino
264     }
265 }
266
267 impl OpenOptions {
268     pub fn new() -> OpenOptions {
269         let mut base = OpenOptions::default();
270         base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW;
271         return base;
272     }
273
274     pub fn read(&mut self, read: bool) {
275         self.read = read;
276     }
277
278     pub fn write(&mut self, write: bool) {
279         self.write = write;
280     }
281
282     pub fn truncate(&mut self, truncate: bool) {
283         self.oflag(wasi::OFLAGS_TRUNC, truncate);
284     }
285
286     pub fn create(&mut self, create: bool) {
287         self.oflag(wasi::OFLAGS_CREAT, create);
288     }
289
290     pub fn create_new(&mut self, create_new: bool) {
291         self.oflag(wasi::OFLAGS_EXCL, create_new);
292         self.oflag(wasi::OFLAGS_CREAT, create_new);
293     }
294
295     pub fn directory(&mut self, directory: bool) {
296         self.oflag(wasi::OFLAGS_DIRECTORY, directory);
297     }
298
299     fn oflag(&mut self, bit: wasi::Oflags, set: bool) {
300         if set {
301             self.oflags |= bit;
302         } else {
303             self.oflags &= !bit;
304         }
305     }
306
307     pub fn append(&mut self, append: bool) {
308         self.append = append;
309         self.fdflag(wasi::FDFLAGS_APPEND, append);
310     }
311
312     pub fn dsync(&mut self, set: bool) {
313         self.fdflag(wasi::FDFLAGS_DSYNC, set);
314     }
315
316     pub fn nonblock(&mut self, set: bool) {
317         self.fdflag(wasi::FDFLAGS_NONBLOCK, set);
318     }
319
320     pub fn rsync(&mut self, set: bool) {
321         self.fdflag(wasi::FDFLAGS_RSYNC, set);
322     }
323
324     pub fn sync(&mut self, set: bool) {
325         self.fdflag(wasi::FDFLAGS_SYNC, set);
326     }
327
328     fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) {
329         if set {
330             self.fdflags |= bit;
331         } else {
332             self.fdflags &= !bit;
333         }
334     }
335
336     pub fn fs_rights_base(&mut self, rights: wasi::Rights) {
337         self.rights_base = Some(rights);
338     }
339
340     pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) {
341         self.rights_inheriting = Some(rights);
342     }
343
344     fn rights_base(&self) -> wasi::Rights {
345         if let Some(rights) = self.rights_base {
346             return rights;
347         }
348
349         // If rights haven't otherwise been specified try to pick a reasonable
350         // set. This can always be overridden by users via extension traits, and
351         // implementations may give us fewer rights silently than we ask for. So
352         // given that, just look at `read` and `write` and bucket permissions
353         // based on that.
354         let mut base = 0;
355         if self.read {
356             base |= wasi::RIGHTS_FD_READ;
357             base |= wasi::RIGHTS_FD_READDIR;
358         }
359         if self.write || self.append {
360             base |= wasi::RIGHTS_FD_WRITE;
361             base |= wasi::RIGHTS_FD_DATASYNC;
362             base |= wasi::RIGHTS_FD_ALLOCATE;
363             base |= wasi::RIGHTS_FD_FILESTAT_SET_SIZE;
364         }
365
366         // FIXME: some of these should probably be read-only or write-only...
367         base |= wasi::RIGHTS_FD_ADVISE;
368         base |= wasi::RIGHTS_FD_FDSTAT_SET_FLAGS;
369         base |= wasi::RIGHTS_FD_FILESTAT_GET;
370         base |= wasi::RIGHTS_FD_FILESTAT_SET_TIMES;
371         base |= wasi::RIGHTS_FD_SEEK;
372         base |= wasi::RIGHTS_FD_SYNC;
373         base |= wasi::RIGHTS_FD_TELL;
374         base |= wasi::RIGHTS_PATH_CREATE_DIRECTORY;
375         base |= wasi::RIGHTS_PATH_CREATE_FILE;
376         base |= wasi::RIGHTS_PATH_FILESTAT_GET;
377         base |= wasi::RIGHTS_PATH_LINK_SOURCE;
378         base |= wasi::RIGHTS_PATH_LINK_TARGET;
379         base |= wasi::RIGHTS_PATH_OPEN;
380         base |= wasi::RIGHTS_PATH_READLINK;
381         base |= wasi::RIGHTS_PATH_REMOVE_DIRECTORY;
382         base |= wasi::RIGHTS_PATH_RENAME_SOURCE;
383         base |= wasi::RIGHTS_PATH_RENAME_TARGET;
384         base |= wasi::RIGHTS_PATH_SYMLINK;
385         base |= wasi::RIGHTS_PATH_UNLINK_FILE;
386         base |= wasi::RIGHTS_POLL_FD_READWRITE;
387
388         return base;
389     }
390
391     fn rights_inheriting(&self) -> wasi::Rights {
392         self.rights_inheriting.unwrap_or_else(|| self.rights_base())
393     }
394
395     pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) {
396         self.dirflags = flags;
397     }
398 }
399
400 impl File {
401     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
402         let (dir, file) = open_parent(path)?;
403         open_at(&dir, &file, opts)
404     }
405
406     pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
407         open_at(&self.fd, path, opts)
408     }
409
410     pub fn file_attr(&self) -> io::Result<FileAttr> {
411         self.fd.filestat_get().map(|meta| FileAttr { meta })
412     }
413
414     pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
415         metadata_at(&self.fd, flags, path)
416     }
417
418     pub fn fsync(&self) -> io::Result<()> {
419         self.fd.sync()
420     }
421
422     pub fn datasync(&self) -> io::Result<()> {
423         self.fd.datasync()
424     }
425
426     pub fn truncate(&self, size: u64) -> io::Result<()> {
427         self.fd.filestat_set_size(size)
428     }
429
430     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
431         self.read_vectored(&mut [IoSliceMut::new(buf)])
432     }
433
434     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
435         self.fd.read(bufs)
436     }
437
438     #[inline]
439     pub fn is_read_vectored(&self) -> bool {
440         true
441     }
442
443     pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
444         crate::io::default_read_buf(|buf| self.read(buf), cursor)
445     }
446
447     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
448         self.write_vectored(&[IoSlice::new(buf)])
449     }
450
451     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
452         self.fd.write(bufs)
453     }
454
455     #[inline]
456     pub fn is_write_vectored(&self) -> bool {
457         true
458     }
459
460     pub fn flush(&self) -> io::Result<()> {
461         Ok(())
462     }
463
464     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
465         self.fd.seek(pos)
466     }
467
468     pub fn duplicate(&self) -> io::Result<File> {
469         // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup
470         unsupported()
471     }
472
473     pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
474         // Permissions haven't been fully figured out in wasi yet, so this is
475         // likely temporary
476         unsupported()
477     }
478
479     pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
480         let to_timestamp = |time: Option<SystemTime>| {
481             match time {
482                 Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts),
483                 Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")),
484                 None => Ok(0),
485             }
486         };
487         self.fd.filestat_set_times(
488             to_timestamp(times.accessed)?,
489             to_timestamp(times.modified)?,
490             times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM)
491                 | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM),
492         )
493     }
494
495     pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
496         read_link(&self.fd, file)
497     }
498 }
499
500 impl AsInner<WasiFd> for File {
501     fn as_inner(&self) -> &WasiFd {
502         &self.fd
503     }
504 }
505
506 impl IntoInner<WasiFd> for File {
507     fn into_inner(self) -> WasiFd {
508         self.fd
509     }
510 }
511
512 impl FromInner<WasiFd> for File {
513     fn from_inner(fd: WasiFd) -> File {
514         File { fd }
515     }
516 }
517
518 impl AsFd for File {
519     fn as_fd(&self) -> BorrowedFd<'_> {
520         self.fd.as_fd()
521     }
522 }
523
524 impl AsRawFd for File {
525     fn as_raw_fd(&self) -> RawFd {
526         self.fd.as_raw_fd()
527     }
528 }
529
530 impl IntoRawFd for File {
531     fn into_raw_fd(self) -> RawFd {
532         self.fd.into_raw_fd()
533     }
534 }
535
536 impl FromRawFd for File {
537     unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
538         unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
539     }
540 }
541
542 impl DirBuilder {
543     pub fn new() -> DirBuilder {
544         DirBuilder {}
545     }
546
547     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
548         let (dir, file) = open_parent(p)?;
549         dir.create_directory(osstr2str(file.as_ref())?)
550     }
551 }
552
553 impl fmt::Debug for File {
554     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
555         f.debug_struct("File").field("fd", &self.as_raw_fd()).finish()
556     }
557 }
558
559 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
560     let mut opts = OpenOptions::new();
561     opts.directory(true);
562     opts.read(true);
563     let dir = File::open(p, &opts)?;
564     Ok(ReadDir::new(dir, p.to_path_buf()))
565 }
566
567 pub fn unlink(p: &Path) -> io::Result<()> {
568     let (dir, file) = open_parent(p)?;
569     dir.unlink_file(osstr2str(file.as_ref())?)
570 }
571
572 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
573     let (old, old_file) = open_parent(old)?;
574     let (new, new_file) = open_parent(new)?;
575     old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?)
576 }
577
578 pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
579     // Permissions haven't been fully figured out in wasi yet, so this is
580     // likely temporary
581     unsupported()
582 }
583
584 pub fn rmdir(p: &Path) -> io::Result<()> {
585     let (dir, file) = open_parent(p)?;
586     dir.remove_directory(osstr2str(file.as_ref())?)
587 }
588
589 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
590     let (dir, file) = open_parent(p)?;
591     read_link(&dir, &file)
592 }
593
594 fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
595     // Try to get a best effort initial capacity for the vector we're going to
596     // fill. Note that if it's not a symlink we don't use a file to avoid
597     // allocating gigabytes if you read_link a huge movie file by accident.
598     // Additionally we add 1 to the initial size so if it doesn't change until
599     // when we call `readlink` the returned length will be less than the
600     // capacity, guaranteeing that we got all the data.
601     let meta = metadata_at(fd, 0, file)?;
602     let initial_size = if meta.file_type().is_symlink() {
603         (meta.size() as usize).saturating_add(1)
604     } else {
605         1 // this'll fail in just a moment
606     };
607
608     // Now that we have an initial guess of how big to make our buffer, call
609     // `readlink` in a loop until it fails or reports it filled fewer bytes than
610     // we asked for, indicating we got everything.
611     let file = osstr2str(file.as_ref())?;
612     let mut destination = vec![0u8; initial_size];
613     loop {
614         let len = fd.readlink(file, &mut destination)?;
615         if len < destination.len() {
616             destination.truncate(len);
617             destination.shrink_to_fit();
618             return Ok(PathBuf::from(OsString::from_vec(destination)));
619         }
620         let amt_to_add = destination.len();
621         destination.extend(iter::repeat(0).take(amt_to_add));
622     }
623 }
624
625 pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
626     let (link, link_file) = open_parent(link)?;
627     link.symlink(osstr2str(original.as_ref())?, osstr2str(link_file.as_ref())?)
628 }
629
630 pub fn link(original: &Path, link: &Path) -> io::Result<()> {
631     let (original, original_file) = open_parent(original)?;
632     let (link, link_file) = open_parent(link)?;
633     // Pass 0 as the flags argument, meaning don't follow symlinks.
634     original.link(0, osstr2str(original_file.as_ref())?, &link, osstr2str(link_file.as_ref())?)
635 }
636
637 pub fn stat(p: &Path) -> io::Result<FileAttr> {
638     let (dir, file) = open_parent(p)?;
639     metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file)
640 }
641
642 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
643     let (dir, file) = open_parent(p)?;
644     metadata_at(&dir, 0, &file)
645 }
646
647 fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
648     let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?;
649     Ok(FileAttr { meta })
650 }
651
652 pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
653     // This seems to not be in wasi's API yet, and we may need to end up
654     // emulating it ourselves. For now just return an error.
655     unsupported()
656 }
657
658 fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
659     let fd = fd.open(
660         opts.dirflags,
661         osstr2str(path.as_ref())?,
662         opts.oflags,
663         opts.rights_base(),
664         opts.rights_inheriting(),
665         opts.fdflags,
666     )?;
667     Ok(File { fd })
668 }
669
670 /// Attempts to open a bare path `p`.
671 ///
672 /// WASI has no fundamental capability to do this. All syscalls and operations
673 /// are relative to already-open file descriptors. The C library, however,
674 /// manages a map of pre-opened file descriptors to their path, and then the C
675 /// library provides an API to look at this. In other words, when you want to
676 /// open a path `p`, you have to find a previously opened file descriptor in a
677 /// global table and then see if `p` is relative to that file descriptor.
678 ///
679 /// This function, if successful, will return two items:
680 ///
681 /// * The first is a `ManuallyDrop<WasiFd>`. This represents a pre-opened file
682 ///   descriptor which we don't have ownership of, but we can use. You shouldn't
683 ///   actually drop the `fd`.
684 ///
685 /// * The second is a path that should be a part of `p` and represents a
686 ///   relative traversal from the file descriptor specified to the desired
687 ///   location `p`.
688 ///
689 /// If successful you can use the returned file descriptor to perform
690 /// file-descriptor-relative operations on the path returned as well. The
691 /// `rights` argument indicates what operations are desired on the returned file
692 /// descriptor, and if successful the returned file descriptor should have the
693 /// appropriate rights for performing `rights` actions.
694 ///
695 /// Note that this can fail if `p` doesn't look like it can be opened relative
696 /// to any pre-opened file descriptor.
697 fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
698     run_path_with_cstr(p, |p| {
699         let mut buf = Vec::<u8>::with_capacity(512);
700         loop {
701             unsafe {
702                 let mut relative_path = buf.as_ptr().cast();
703                 let mut abs_prefix = ptr::null();
704                 let fd = __wasilibc_find_relpath(
705                     p.as_ptr(),
706                     &mut abs_prefix,
707                     &mut relative_path,
708                     buf.capacity(),
709                 );
710                 if fd == -1 {
711                     if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) {
712                         // Trigger the internal buffer resizing logic of `Vec` by requiring
713                         // more space than the current capacity.
714                         let cap = buf.capacity();
715                         buf.set_len(cap);
716                         buf.reserve(1);
717                         continue;
718                     }
719                     let msg = format!(
720                         "failed to find a pre-opened file descriptor \
721                      through which {:?} could be opened",
722                         p
723                     );
724                     return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
725                 }
726                 let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
727
728                 return Ok((
729                     ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
730                     PathBuf::from(OsString::from_vec(relative)),
731                 ));
732             }
733         }
734
735         extern "C" {
736             pub fn __wasilibc_find_relpath(
737                 path: *const libc::c_char,
738                 abs_prefix: *mut *const libc::c_char,
739                 relative_path: *mut *const libc::c_char,
740                 relative_path_len: libc::size_t,
741             ) -> libc::c_int;
742         }
743     })
744 }
745
746 pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
747     f.to_str()
748         .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8"))
749 }
750
751 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
752     use crate::fs::File;
753
754     let mut reader = File::open(from)?;
755     let mut writer = File::create(to)?;
756
757     io::copy(&mut reader, &mut writer)
758 }
759
760 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
761     let (parent, path) = open_parent(path)?;
762     remove_dir_all_recursive(&parent, &path)
763 }
764
765 fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> {
766     // Open up a file descriptor for the directory itself. Note that we don't
767     // follow symlinks here and we specifically open directories.
768     //
769     // At the root invocation of this function this will correctly handle
770     // symlinks passed to the top-level `remove_dir_all`. At the recursive
771     // level this will double-check that after the `readdir` call deduced this
772     // was a directory it's still a directory by the time we open it up.
773     //
774     // If the opened file was actually a symlink then the symlink is deleted,
775     // not the directory recursively.
776     let mut opts = OpenOptions::new();
777     opts.lookup_flags(0);
778     opts.directory(true);
779     opts.read(true);
780     let fd = open_at(parent, path, &opts)?;
781     if fd.file_attr()?.file_type().is_symlink() {
782         return parent.unlink_file(osstr2str(path.as_ref())?);
783     }
784
785     // this "root" is only used by `DirEntry::path` which we don't use below so
786     // it's ok for this to be a bogus value
787     let dummy_root = PathBuf::new();
788
789     // Iterate over all the entries in this directory, and travel recursively if
790     // necessary
791     for entry in ReadDir::new(fd, dummy_root) {
792         let entry = entry?;
793         let path = crate::str::from_utf8(&entry.name).map_err(|_| {
794             io::const_io_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found")
795         })?;
796
797         if entry.file_type()?.is_dir() {
798             remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?;
799         } else {
800             entry.inner.dir.fd.unlink_file(path)?;
801         }
802     }
803
804     // Once all this directory's contents are deleted it should be safe to
805     // delete the directory tiself.
806     parent.remove_directory(osstr2str(path.as_ref())?)
807 }