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