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