]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/wasi/fs.rs
Rollup merge of #60429 - estebank:pub-path, r=michaelwoerister
[rust.git] / src / libstd / sys / wasi / fs.rs
1 use crate::ffi::{CStr, CString, OsStr, OsString};
2 use crate::fmt;
3 use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
4 use crate::iter;
5 use crate::mem::{self, ManuallyDrop};
6 use crate::os::wasi::ffi::{OsStrExt, OsStringExt};
7 use crate::path::{Path, PathBuf};
8 use crate::ptr;
9 use crate::sync::Arc;
10 use crate::sys::fd::{DirCookie, WasiFd};
11 use crate::sys::time::SystemTime;
12 use crate::sys::unsupported;
13 use crate::sys_common::FromInner;
14
15 pub use crate::sys_common::fs::copy;
16 pub use crate::sys_common::fs::remove_dir_all;
17
18 pub struct File {
19     fd: WasiFd,
20 }
21
22 #[derive(Clone)]
23 pub struct FileAttr {
24     meta: libc::__wasi_filestat_t,
25 }
26
27 pub struct ReadDir {
28     inner: Arc<ReadDirInner>,
29     cookie: Option<DirCookie>,
30     buf: Vec<u8>,
31     offset: usize,
32     cap: usize,
33 }
34
35 struct ReadDirInner {
36     root: PathBuf,
37     dir: File,
38 }
39
40 pub struct DirEntry {
41     meta: libc::__wasi_dirent_t,
42     name: Vec<u8>,
43     inner: Arc<ReadDirInner>,
44 }
45
46 #[derive(Clone, Debug, Default)]
47 pub struct OpenOptions {
48     read: bool,
49     write: bool,
50     dirflags: libc::__wasi_lookupflags_t,
51     fdflags: libc::__wasi_fdflags_t,
52     oflags: libc::__wasi_oflags_t,
53     rights_base: Option<libc::__wasi_rights_t>,
54     rights_inheriting: Option<libc::__wasi_rights_t>,
55 }
56
57 #[derive(Clone, PartialEq, Eq, Debug)]
58 pub struct FilePermissions {
59     readonly: bool,
60 }
61
62 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
63 pub struct FileType {
64     bits: libc::__wasi_filetype_t,
65 }
66
67 #[derive(Debug)]
68 pub struct DirBuilder {}
69
70 impl FileAttr {
71     fn zero() -> FileAttr {
72         FileAttr {
73             meta: unsafe { mem::zeroed() },
74         }
75     }
76
77     pub fn size(&self) -> u64 {
78         self.meta.st_size
79     }
80
81     pub fn perm(&self) -> FilePermissions {
82         // not currently implemented in wasi yet
83         FilePermissions { readonly: false }
84     }
85
86     pub fn file_type(&self) -> FileType {
87         FileType {
88             bits: self.meta.st_filetype,
89         }
90     }
91
92     pub fn modified(&self) -> io::Result<SystemTime> {
93         Ok(SystemTime::from_wasi_timestamp(self.meta.st_mtim))
94     }
95
96     pub fn accessed(&self) -> io::Result<SystemTime> {
97         Ok(SystemTime::from_wasi_timestamp(self.meta.st_atim))
98     }
99
100     pub fn created(&self) -> io::Result<SystemTime> {
101         Ok(SystemTime::from_wasi_timestamp(self.meta.st_ctim))
102     }
103
104     pub fn as_wasi(&self) -> &libc::__wasi_filestat_t {
105         &self.meta
106     }
107 }
108
109 impl FilePermissions {
110     pub fn readonly(&self) -> bool {
111         self.readonly
112     }
113
114     pub fn set_readonly(&mut self, readonly: bool) {
115         self.readonly = readonly;
116     }
117 }
118
119 impl FileType {
120     pub fn is_dir(&self) -> bool {
121         self.bits == libc::__WASI_FILETYPE_DIRECTORY
122     }
123
124     pub fn is_file(&self) -> bool {
125         self.bits == libc::__WASI_FILETYPE_REGULAR_FILE
126     }
127
128     pub fn is_symlink(&self) -> bool {
129         self.bits == libc::__WASI_FILETYPE_SYMBOLIC_LINK
130     }
131
132     pub fn bits(&self) -> libc::__wasi_filetype_t {
133         self.bits
134     }
135 }
136
137 impl fmt::Debug for ReadDir {
138     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139         f.debug_struct("ReadDir").finish()
140     }
141 }
142
143 impl Iterator for ReadDir {
144     type Item = io::Result<DirEntry>;
145
146     fn next(&mut self) -> Option<io::Result<DirEntry>> {
147         loop {
148             // If we've reached the capacity of our buffer then we need to read
149             // some more from the OS, otherwise we pick up at our old offset.
150             let offset = if self.offset == self.cap {
151                 let cookie = self.cookie.take()?;
152                 match self.inner.dir.fd.readdir(&mut self.buf, cookie) {
153                     Ok(bytes) => self.cap = bytes,
154                     Err(e) => return Some(Err(e)),
155                 }
156                 self.offset = 0;
157                 self.cookie = Some(cookie);
158
159                 // If we didn't actually read anything, this is in theory the
160                 // end of the directory.
161                 if self.cap == 0 {
162                     self.cookie = None;
163                     return None;
164                 }
165
166                 0
167             } else {
168                 self.offset
169             };
170             let data = &self.buf[offset..self.cap];
171
172             // If we're not able to read a directory entry then that means it
173             // must have been truncated at the end of the buffer, so reset our
174             // offset so we can go back and reread into the buffer, picking up
175             // where we last left off.
176             let dirent_size = mem::size_of::<libc::__wasi_dirent_t>();
177             if data.len() < dirent_size {
178                 assert!(self.cookie.is_some());
179                 assert!(self.buf.len() >= dirent_size);
180                 self.offset = self.cap;
181                 continue;
182             }
183             let (dirent, data) = data.split_at(dirent_size);
184             let dirent =
185                 unsafe { ptr::read_unaligned(dirent.as_ptr() as *const libc::__wasi_dirent_t) };
186
187             // If the file name was truncated, then we need to reinvoke
188             // `readdir` so we truncate our buffer to start over and reread this
189             // descriptor. Note that if our offset is 0 that means the file name
190             // is massive and we need a bigger buffer.
191             if data.len() < dirent.d_namlen as usize {
192                 if offset == 0 {
193                     let amt_to_add = self.buf.capacity();
194                     self.buf.extend(iter::repeat(0).take(amt_to_add));
195                 }
196                 assert!(self.cookie.is_some());
197                 self.offset = self.cap;
198                 continue;
199             }
200             self.cookie = Some(dirent.d_next);
201             self.offset = offset + dirent_size + dirent.d_namlen as usize;
202
203             let name = &data[..(dirent.d_namlen as usize)];
204
205             // These names are skipped on all other platforms, so let's skip
206             // them here too
207             if name == b"." || name == b".." {
208                 continue;
209             }
210
211             return Some(Ok(DirEntry {
212                 meta: dirent,
213                 name: name.to_vec(),
214                 inner: self.inner.clone(),
215             }));
216         }
217     }
218 }
219
220 impl DirEntry {
221     pub fn path(&self) -> PathBuf {
222         let name = OsStr::from_bytes(&self.name);
223         self.inner.root.join(name)
224     }
225
226     pub fn file_name(&self) -> OsString {
227         OsString::from_vec(self.name.clone())
228     }
229
230     pub fn metadata(&self) -> io::Result<FileAttr> {
231         metadata_at(
232             &self.inner.dir.fd,
233             0,
234             OsStr::from_bytes(&self.name).as_ref(),
235         )
236     }
237
238     pub fn file_type(&self) -> io::Result<FileType> {
239         Ok(FileType {
240             bits: self.meta.d_type,
241         })
242     }
243
244     pub fn ino(&self) -> libc::__wasi_inode_t {
245         self.meta.d_ino
246     }
247 }
248
249 impl OpenOptions {
250     pub fn new() -> OpenOptions {
251         let mut base = OpenOptions::default();
252         base.dirflags = libc::__WASI_LOOKUP_SYMLINK_FOLLOW;
253         return base;
254     }
255
256     pub fn read(&mut self, read: bool) {
257         self.read = read;
258     }
259
260     pub fn write(&mut self, write: bool) {
261         self.write = write;
262     }
263
264     pub fn truncate(&mut self, truncate: bool) {
265         self.oflag(libc::__WASI_O_TRUNC, truncate);
266     }
267
268     pub fn create(&mut self, create: bool) {
269         self.oflag(libc::__WASI_O_CREAT, create);
270     }
271
272     pub fn create_new(&mut self, create_new: bool) {
273         self.oflag(libc::__WASI_O_EXCL, create_new);
274         self.oflag(libc::__WASI_O_CREAT, create_new);
275     }
276
277     pub fn directory(&mut self, directory: bool) {
278         self.oflag(libc::__WASI_O_DIRECTORY, directory);
279     }
280
281     fn oflag(&mut self, bit: libc::__wasi_oflags_t, set: bool) {
282         if set {
283             self.oflags |= bit;
284         } else {
285             self.oflags &= !bit;
286         }
287     }
288
289     pub fn append(&mut self, set: bool) {
290         self.fdflag(libc::__WASI_FDFLAG_APPEND, set);
291     }
292
293     pub fn dsync(&mut self, set: bool) {
294         self.fdflag(libc::__WASI_FDFLAG_DSYNC, set);
295     }
296
297     pub fn nonblock(&mut self, set: bool) {
298         self.fdflag(libc::__WASI_FDFLAG_NONBLOCK, set);
299     }
300
301     pub fn rsync(&mut self, set: bool) {
302         self.fdflag(libc::__WASI_FDFLAG_RSYNC, set);
303     }
304
305     pub fn sync(&mut self, set: bool) {
306         self.fdflag(libc::__WASI_FDFLAG_SYNC, set);
307     }
308
309     fn fdflag(&mut self, bit: libc::__wasi_fdflags_t, set: bool) {
310         if set {
311             self.fdflags |= bit;
312         } else {
313             self.fdflags &= !bit;
314         }
315     }
316
317     pub fn fs_rights_base(&mut self, rights: libc::__wasi_rights_t) {
318         self.rights_base = Some(rights);
319     }
320
321     pub fn fs_rights_inheriting(&mut self, rights: libc::__wasi_rights_t) {
322         self.rights_inheriting = Some(rights);
323     }
324
325     fn rights_base(&self) -> libc::__wasi_rights_t {
326         if let Some(rights) = self.rights_base {
327             return rights;
328         }
329
330         // If rights haven't otherwise been specified try to pick a reasonable
331         // set. This can always be overridden by users via extension traits, and
332         // implementations may give us fewer rights silently than we ask for. So
333         // given that, just look at `read` and `write` and bucket permissions
334         // based on that.
335         let mut base = 0;
336         if self.read {
337             base |= libc::__WASI_RIGHT_FD_READ;
338             base |= libc::__WASI_RIGHT_FD_READDIR;
339         }
340         if self.write {
341             base |= libc::__WASI_RIGHT_FD_WRITE;
342             base |= libc::__WASI_RIGHT_FD_DATASYNC;
343             base |= libc::__WASI_RIGHT_FD_ALLOCATE;
344             base |= libc::__WASI_RIGHT_FD_FILESTAT_SET_SIZE;
345         }
346
347         // FIXME: some of these should probably be read-only or write-only...
348         base |= libc::__WASI_RIGHT_FD_ADVISE;
349         base |= libc::__WASI_RIGHT_FD_FDSTAT_SET_FLAGS;
350         base |= libc::__WASI_RIGHT_FD_FILESTAT_SET_TIMES;
351         base |= libc::__WASI_RIGHT_FD_SEEK;
352         base |= libc::__WASI_RIGHT_FD_SYNC;
353         base |= libc::__WASI_RIGHT_FD_TELL;
354         base |= libc::__WASI_RIGHT_PATH_CREATE_DIRECTORY;
355         base |= libc::__WASI_RIGHT_PATH_CREATE_FILE;
356         base |= libc::__WASI_RIGHT_PATH_FILESTAT_GET;
357         base |= libc::__WASI_RIGHT_PATH_LINK_SOURCE;
358         base |= libc::__WASI_RIGHT_PATH_LINK_TARGET;
359         base |= libc::__WASI_RIGHT_PATH_OPEN;
360         base |= libc::__WASI_RIGHT_PATH_READLINK;
361         base |= libc::__WASI_RIGHT_PATH_REMOVE_DIRECTORY;
362         base |= libc::__WASI_RIGHT_PATH_RENAME_SOURCE;
363         base |= libc::__WASI_RIGHT_PATH_RENAME_TARGET;
364         base |= libc::__WASI_RIGHT_PATH_SYMLINK;
365         base |= libc::__WASI_RIGHT_PATH_UNLINK_FILE;
366         base |= libc::__WASI_RIGHT_POLL_FD_READWRITE;
367
368         return base;
369     }
370
371     fn rights_inheriting(&self) -> libc::__wasi_rights_t {
372         self.rights_inheriting.unwrap_or_else(|| self.rights_base())
373     }
374
375     pub fn lookup_flags(&mut self, flags: libc::__wasi_lookupflags_t) {
376         self.dirflags = flags;
377     }
378 }
379
380 impl File {
381     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
382         let (dir, file) = open_parent(path, libc::__WASI_RIGHT_PATH_OPEN)?;
383         open_at(&dir, &file, opts)
384     }
385
386     pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
387         open_at(&self.fd, path, opts)
388     }
389
390     pub fn file_attr(&self) -> io::Result<FileAttr> {
391         let mut ret = FileAttr::zero();
392         self.fd.filestat_get(&mut ret.meta)?;
393         Ok(ret)
394     }
395
396     pub fn metadata_at(
397         &self,
398         flags: libc::__wasi_lookupflags_t,
399         path: &Path,
400     ) -> io::Result<FileAttr> {
401         metadata_at(&self.fd, flags, path)
402     }
403
404     pub fn fsync(&self) -> io::Result<()> {
405         self.fd.sync()
406     }
407
408     pub fn datasync(&self) -> io::Result<()> {
409         self.fd.datasync()
410     }
411
412     pub fn truncate(&self, size: u64) -> io::Result<()> {
413         self.fd.filestat_set_size(size)
414     }
415
416     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
417         self.read_vectored(&mut [IoSliceMut::new(buf)])
418     }
419
420     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
421         self.fd.read(bufs)
422     }
423
424     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
425         self.write_vectored(&[IoSlice::new(buf)])
426     }
427
428     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
429         self.fd.write(bufs)
430     }
431
432     pub fn flush(&self) -> io::Result<()> {
433         Ok(())
434     }
435
436     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
437         self.fd.seek(pos)
438     }
439
440     pub fn duplicate(&self) -> io::Result<File> {
441         // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup
442         unsupported()
443     }
444
445     pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
446         // Permissions haven't been fully figured out in wasi yet, so this is
447         // likely temporary
448         unsupported()
449     }
450
451     pub fn fd(&self) -> &WasiFd {
452         &self.fd
453     }
454
455     pub fn into_fd(self) -> WasiFd {
456         self.fd
457     }
458
459     pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
460         read_link(&self.fd, file)
461     }
462 }
463
464 impl FromInner<u32> for File {
465     fn from_inner(fd: u32) -> File {
466         unsafe {
467             File {
468                 fd: WasiFd::from_raw(fd),
469             }
470         }
471     }
472 }
473
474 impl DirBuilder {
475     pub fn new() -> DirBuilder {
476         DirBuilder {}
477     }
478
479     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
480         let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_CREATE_DIRECTORY)?;
481         dir.create_directory(file.as_os_str().as_bytes())
482     }
483 }
484
485 impl fmt::Debug for File {
486     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487         f.debug_struct("File")
488             .field("fd", &self.fd.as_raw())
489             .finish()
490     }
491 }
492
493 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
494     let mut opts = OpenOptions::new();
495     opts.directory(true);
496     opts.read(true);
497     let dir = File::open(p, &opts)?;
498     Ok(ReadDir {
499         cookie: Some(0),
500         buf: vec![0; 128],
501         offset: 0,
502         cap: 0,
503         inner: Arc::new(ReadDirInner {
504             dir,
505             root: p.to_path_buf(),
506         }),
507     })
508 }
509
510 pub fn unlink(p: &Path) -> io::Result<()> {
511     let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_UNLINK_FILE)?;
512     dir.unlink_file(file.as_os_str().as_bytes())
513 }
514
515 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
516     let (old, old_file) = open_parent(old, libc::__WASI_RIGHT_PATH_RENAME_SOURCE)?;
517     let (new, new_file) = open_parent(new, libc::__WASI_RIGHT_PATH_RENAME_TARGET)?;
518     old.rename(
519         old_file.as_os_str().as_bytes(),
520         &new,
521         new_file.as_os_str().as_bytes(),
522     )
523 }
524
525 pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
526     // Permissions haven't been fully figured out in wasi yet, so this is
527     // likely temporary
528     unsupported()
529 }
530
531 pub fn rmdir(p: &Path) -> io::Result<()> {
532     let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_REMOVE_DIRECTORY)?;
533     dir.remove_directory(file.as_os_str().as_bytes())
534 }
535
536 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
537     let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_READLINK)?;
538     read_link(&dir, &file)
539 }
540
541 fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
542     // Try to get a best effort initial capacity for the vector we're going to
543     // fill. Note that if it's not a symlink we don't use a file to avoid
544     // allocating gigabytes if you read_link a huge movie file by accident.
545     // Additionally we add 1 to the initial size so if it doesn't change until
546     // when we call `readlink` the returned length will be less than the
547     // capacity, guaranteeing that we got all the data.
548     let meta = metadata_at(fd, 0, file)?;
549     let initial_size = if meta.file_type().is_symlink() {
550         (meta.size() as usize).saturating_add(1)
551     } else {
552         1 // this'll fail in just a moment
553     };
554
555     // Now that we have an initial guess of how big to make our buffer, call
556     // `readlink` in a loop until it fails or reports it filled fewer bytes than
557     // we asked for, indicating we got everything.
558     let file = file.as_os_str().as_bytes();
559     let mut destination = vec![0u8; initial_size];
560     loop {
561         let len = fd.readlink(file, &mut destination)?;
562         if len < destination.len() {
563             destination.truncate(len);
564             destination.shrink_to_fit();
565             return Ok(PathBuf::from(OsString::from_vec(destination)));
566         }
567         let amt_to_add = destination.len();
568         destination.extend(iter::repeat(0).take(amt_to_add));
569     }
570 }
571
572 pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
573     let (dst, dst_file) = open_parent(dst, libc::__WASI_RIGHT_PATH_SYMLINK)?;
574     dst.symlink(src.as_os_str().as_bytes(), dst_file.as_os_str().as_bytes())
575 }
576
577 pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
578     let (src, src_file) = open_parent(src, libc::__WASI_RIGHT_PATH_LINK_SOURCE)?;
579     let (dst, dst_file) = open_parent(dst, libc::__WASI_RIGHT_PATH_LINK_TARGET)?;
580     src.link(
581         libc::__WASI_LOOKUP_SYMLINK_FOLLOW,
582         src_file.as_os_str().as_bytes(),
583         &dst,
584         dst_file.as_os_str().as_bytes(),
585     )
586 }
587
588 pub fn stat(p: &Path) -> io::Result<FileAttr> {
589     let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_FILESTAT_GET)?;
590     metadata_at(&dir, libc::__WASI_LOOKUP_SYMLINK_FOLLOW, &file)
591 }
592
593 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
594     let (dir, file) = open_parent(p, libc::__WASI_RIGHT_PATH_FILESTAT_GET)?;
595     metadata_at(&dir, 0, &file)
596 }
597
598 fn metadata_at(
599     fd: &WasiFd,
600     flags: libc::__wasi_lookupflags_t,
601     path: &Path,
602 ) -> io::Result<FileAttr> {
603     let mut ret = FileAttr::zero();
604     fd.path_filestat_get(flags, path.as_os_str().as_bytes(), &mut ret.meta)?;
605     Ok(ret)
606 }
607
608 pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
609     // This seems to not be in wasi's API yet, and we may need to end up
610     // emulating it ourselves. For now just return an error.
611     unsupported()
612 }
613
614 fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
615     let fd = fd.open(
616         opts.dirflags,
617         path.as_os_str().as_bytes(),
618         opts.oflags,
619         opts.rights_base(),
620         opts.rights_inheriting(),
621         opts.fdflags,
622     )?;
623     Ok(File { fd })
624 }
625
626 /// Attempts to open a bare path `p`.
627 ///
628 /// WASI has no fundamental capability to do this. All syscalls and operations
629 /// are relative to already-open file descriptors. The C library, however,
630 /// manages a map of preopened file descriptors to their path, and then the C
631 /// library provides an API to look at this. In other words, when you want to
632 /// open a path `p`, you have to find a previously opened file descriptor in a
633 /// global table and then see if `p` is relative to that file descriptor.
634 ///
635 /// This function, if successful, will return two items:
636 ///
637 /// * The first is a `ManuallyDrop<WasiFd>`. This represents a preopened file
638 ///   descriptor which we don't have ownership of, but we can use. You shouldn't
639 ///   actually drop the `fd`.
640 ///
641 /// * The second is a path that should be a part of `p` and represents a
642 ///   relative traversal from the file descriptor specified to the desired
643 ///   location `p`.
644 ///
645 /// If successful you can use the returned file descriptor to perform
646 /// file-descriptor-relative operations on the path returned as well. The
647 /// `rights` argument indicates what operations are desired on the returned file
648 /// descriptor, and if successful the returned file descriptor should have the
649 /// appropriate rights for performing `rights` actions.
650 ///
651 /// Note that this can fail if `p` doesn't look like it can be opened relative
652 /// to any preopened file descriptor.
653 fn open_parent(
654     p: &Path,
655     rights: libc::__wasi_rights_t,
656 ) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
657     let p = CString::new(p.as_os_str().as_bytes())?;
658     unsafe {
659         let mut ret = ptr::null();
660         let fd = __wasilibc_find_relpath(p.as_ptr(), rights, 0, &mut ret);
661         if fd == -1 {
662             let msg = format!(
663                 "failed to find a preopened file descriptor \
664                  through which {:?} could be opened",
665                 p
666             );
667             return Err(io::Error::new(io::ErrorKind::Other, msg));
668         }
669         let path = Path::new(OsStr::from_bytes(CStr::from_ptr(ret).to_bytes()));
670
671         // FIXME: right now `path` is a pointer into `p`, the `CString` above.
672         // When we return `p` is deallocated and we can't use it, so we need to
673         // currently separately allocate `path`. If this becomes an issue though
674         // we should probably turn this into a closure-taking interface or take
675         // `&CString` and then pass off `&Path` tied to the same lifetime.
676         let path = path.to_path_buf();
677
678         return Ok((ManuallyDrop::new(WasiFd::from_raw(fd as u32)), path));
679     }
680
681     // FIXME(rust-lang/libc#1314) use the `libc` crate for this when the API
682     // there is published
683     extern "C" {
684         pub fn __wasilibc_find_relpath(
685             path: *const libc::c_char,
686             rights_base: libc::__wasi_rights_t,
687             rights_inheriting: libc::__wasi_rights_t,
688             relative_path: *mut *const libc::c_char,
689         ) -> libc::c_int;
690     }
691 }