1- use crate :: collections:: HashMap ;
2- use crate :: ffi:: { OsStr , OsString } ;
1+ use crate :: ffi:: { CStr , CString , OsStr , OsString } ;
32use crate :: fmt;
43use crate :: io:: { self , IoVec , IoVecMut , SeekFrom } ;
54use crate :: iter;
65use crate :: mem:: { self , ManuallyDrop } ;
76use crate :: os:: wasi:: ffi:: { OsStrExt , OsStringExt } ;
87use crate :: path:: { Path , PathBuf } ;
98use crate :: ptr;
10- use crate :: sync:: atomic:: { AtomicPtr , Ordering :: SeqCst } ;
119use crate :: sync:: Arc ;
1210use crate :: sys:: fd:: { DirCookie , WasiFd } ;
1311use crate :: sys:: time:: SystemTime ;
14- use crate :: sys:: { cvt_wasi , unsupported} ;
12+ use crate :: sys:: unsupported;
1513use crate :: sys_common:: FromInner ;
1614
1715pub use crate :: sys_common:: fs:: copy;
@@ -230,7 +228,11 @@ impl DirEntry {
230228 }
231229
232230 pub fn metadata ( & self ) -> io:: Result < FileAttr > {
233- metadata_at ( & self . inner . dir . fd , 0 , OsStr :: from_bytes ( & self . name ) . as_ref ( ) )
231+ metadata_at (
232+ & self . inner . dir . fd ,
233+ 0 ,
234+ OsStr :: from_bytes ( & self . name ) . as_ref ( ) ,
235+ )
234236 }
235237
236238 pub fn file_type ( & self ) -> io:: Result < FileType > {
@@ -377,8 +379,8 @@ impl OpenOptions {
377379
378380impl File {
379381 pub fn open ( path : & Path , opts : & OpenOptions ) -> io:: Result < File > {
380- let ( dir, file) = open_parent ( path) ?;
381- open_at ( & dir, file, opts)
382+ let ( dir, file) = open_parent ( path, libc :: __WASI_RIGHT_PATH_OPEN ) ?;
383+ open_at ( & dir, & file, opts)
382384 }
383385
384386 pub fn open_at ( & self , path : & Path , opts : & OpenOptions ) -> io:: Result < File > {
@@ -475,7 +477,7 @@ impl DirBuilder {
475477 }
476478
477479 pub fn mkdir ( & self , p : & Path ) -> io:: Result < ( ) > {
478- let ( dir, file) = open_parent ( p) ?;
480+ let ( dir, file) = open_parent ( p, libc :: __WASI_RIGHT_PATH_CREATE_DIRECTORY ) ?;
479481 dir. create_directory ( file. as_os_str ( ) . as_bytes ( ) )
480482 }
481483}
@@ -506,13 +508,13 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
506508}
507509
508510pub fn unlink ( p : & Path ) -> io:: Result < ( ) > {
509- let ( dir, file) = open_parent ( p) ?;
511+ let ( dir, file) = open_parent ( p, libc :: __WASI_RIGHT_PATH_UNLINK_FILE ) ?;
510512 dir. unlink_file ( file. as_os_str ( ) . as_bytes ( ) )
511513}
512514
513515pub fn rename ( old : & Path , new : & Path ) -> io:: Result < ( ) > {
514- let ( old, old_file) = open_parent ( old) ?;
515- let ( new, new_file) = open_parent ( new) ?;
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 ) ?;
516518 old. rename (
517519 old_file. as_os_str ( ) . as_bytes ( ) ,
518520 & new,
@@ -527,13 +529,13 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
527529}
528530
529531pub fn rmdir ( p : & Path ) -> io:: Result < ( ) > {
530- let ( dir, file) = open_parent ( p) ?;
532+ let ( dir, file) = open_parent ( p, libc :: __WASI_RIGHT_PATH_REMOVE_DIRECTORY ) ?;
531533 dir. remove_directory ( file. as_os_str ( ) . as_bytes ( ) )
532534}
533535
534536pub fn readlink ( p : & Path ) -> io:: Result < PathBuf > {
535- let ( dir, file) = open_parent ( p) ?;
536- read_link ( & dir, file)
537+ let ( dir, file) = open_parent ( p, libc :: __WASI_RIGHT_PATH_READLINK ) ?;
538+ read_link ( & dir, & file)
537539}
538540
539541fn read_link ( fd : & WasiFd , file : & Path ) -> io:: Result < PathBuf > {
@@ -568,13 +570,13 @@ fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
568570}
569571
570572pub fn symlink ( src : & Path , dst : & Path ) -> io:: Result < ( ) > {
571- let ( dst, dst_file) = open_parent ( dst) ?;
573+ let ( dst, dst_file) = open_parent ( dst, libc :: __WASI_RIGHT_PATH_SYMLINK ) ?;
572574 dst. symlink ( src. as_os_str ( ) . as_bytes ( ) , dst_file. as_os_str ( ) . as_bytes ( ) )
573575}
574576
575577pub fn link ( src : & Path , dst : & Path ) -> io:: Result < ( ) > {
576- let ( src, src_file) = open_parent ( src) ?;
577- let ( dst, dst_file) = open_parent ( dst) ?;
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 ) ?;
578580 src. link (
579581 libc:: __WASI_LOOKUP_SYMLINK_FOLLOW,
580582 src_file. as_os_str ( ) . as_bytes ( ) ,
@@ -584,13 +586,13 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
584586}
585587
586588pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
587- let ( dir, file) = open_parent ( p) ?;
588- metadata_at ( & dir, libc:: __WASI_LOOKUP_SYMLINK_FOLLOW, file)
589+ let ( dir, file) = open_parent ( p, libc :: __WASI_RIGHT_PATH_FILESTAT_GET ) ?;
590+ metadata_at ( & dir, libc:: __WASI_LOOKUP_SYMLINK_FOLLOW, & file)
589591}
590592
591593pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
592- let ( dir, file) = open_parent ( p) ?;
593- metadata_at ( & dir, 0 , file)
594+ let ( dir, file) = open_parent ( p, libc :: __WASI_RIGHT_PATH_FILESTAT_GET ) ?;
595+ metadata_at ( & dir, 0 , & file)
594596}
595597
596598fn metadata_at (
@@ -621,72 +623,69 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
621623 Ok ( File { fd } )
622624}
623625
624- // FIXME: we shouldn't implement this. It'd be much better to share this between
625- // libc (the wasi-sysroot) and Rust as the logic here is likely far more tricky
626- // than what we're executing below. For now this is a stopgap to enable this
627- // module, but we should add an official API in upstream wasi-libc which looks
628- // like this.
629- //
630- // In the meantime this is highly unlikely to be correct. It allows some basic
631- // testing but is not at all robust.
632- fn open_parent ( p : & Path ) -> io:: Result < ( & ' static WasiFd , & Path ) > {
633- let map = preopened_map ( ) ;
634- for ancestor in p. ancestors ( ) {
635- if let Some ( fd) = map. get ( ancestor) {
636- let tail = p. strip_prefix ( ancestor) . unwrap ( ) ;
637- let tail = if tail == Path :: new ( "" ) {
638- "." . as_ref ( )
639- } else {
640- tail
641- } ;
642- return Ok ( ( fd, tail) )
643- }
644- }
645- let msg = format ! ( "failed to find a preopened file descriptor to open {:?}" , p) ;
646- return Err ( io:: Error :: new ( io:: ErrorKind :: Other , msg) ) ;
647-
648- type Preopened = HashMap < PathBuf , ManuallyDrop < WasiFd > > ;
649- fn preopened_map ( ) -> & ' static Preopened {
650- static PTR : AtomicPtr < Preopened > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
651- unsafe {
652- let ptr = PTR . load ( SeqCst ) ;
653- if !ptr. is_null ( ) {
654- return & * ptr;
655- }
656-
657- let mut map = Box :: new ( HashMap :: new ( ) ) ;
658- for fd in 3 .. {
659- let mut buf = mem:: zeroed ( ) ;
660- if cvt_wasi ( libc:: __wasi_fd_prestat_get ( fd, & mut buf) ) . is_err ( ) {
661- break ;
662- }
663- if buf. pr_type != libc:: __WASI_PREOPENTYPE_DIR {
664- continue ;
665- }
666- let len = buf. u . dir . pr_name_len ;
667- let mut v = vec ! [ 0u8 ; len] ;
668- let res = cvt_wasi ( libc:: __wasi_fd_prestat_dir_name (
669- fd,
670- v. as_mut_ptr ( ) as * mut i8 ,
671- v. len ( ) ,
672- ) ) ;
673- if res. is_err ( ) {
674- continue ;
675- }
676- let path = PathBuf :: from ( OsString :: from_vec ( v) ) ;
677- map. insert ( path, ManuallyDrop :: new ( WasiFd :: from_raw ( fd) ) ) ;
678- }
679- let ptr = Box :: into_raw ( map) ;
680- match PTR . compare_exchange ( ptr:: null_mut ( ) , ptr, SeqCst , SeqCst ) {
681- Ok ( _) => & * ptr,
682-
683- // If we lost the race for initialization clean up the map we
684- // made and just use the one that's already there
685- Err ( other) => {
686- drop ( Box :: from_raw ( ptr) ) ;
687- & * other
688- }
689- }
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) ) ;
690668 }
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 ;
691690 }
692691}
0 commit comments