1
- use crate :: collections:: HashMap ;
2
- use crate :: ffi:: { OsStr , OsString } ;
1
+ use crate :: ffi:: { CStr , CString , OsStr , OsString } ;
3
2
use crate :: fmt;
4
3
use crate :: io:: { self , IoVec , IoVecMut , SeekFrom } ;
5
4
use crate :: iter;
6
5
use crate :: mem:: { self , ManuallyDrop } ;
7
6
use crate :: os:: wasi:: ffi:: { OsStrExt , OsStringExt } ;
8
7
use crate :: path:: { Path , PathBuf } ;
9
8
use crate :: ptr;
10
- use crate :: sync:: atomic:: { AtomicPtr , Ordering :: SeqCst } ;
11
9
use crate :: sync:: Arc ;
12
10
use crate :: sys:: fd:: { DirCookie , WasiFd } ;
13
11
use crate :: sys:: time:: SystemTime ;
14
- use crate :: sys:: { cvt_wasi , unsupported} ;
12
+ use crate :: sys:: unsupported;
15
13
use crate :: sys_common:: FromInner ;
16
14
17
15
pub use crate :: sys_common:: fs:: copy;
@@ -230,7 +228,11 @@ impl DirEntry {
230
228
}
231
229
232
230
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
+ )
234
236
}
235
237
236
238
pub fn file_type ( & self ) -> io:: Result < FileType > {
@@ -377,8 +379,8 @@ impl OpenOptions {
377
379
378
380
impl File {
379
381
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)
382
384
}
383
385
384
386
pub fn open_at ( & self , path : & Path , opts : & OpenOptions ) -> io:: Result < File > {
@@ -475,7 +477,7 @@ impl DirBuilder {
475
477
}
476
478
477
479
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 ) ?;
479
481
dir. create_directory ( file. as_os_str ( ) . as_bytes ( ) )
480
482
}
481
483
}
@@ -506,13 +508,13 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
506
508
}
507
509
508
510
pub 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 ) ?;
510
512
dir. unlink_file ( file. as_os_str ( ) . as_bytes ( ) )
511
513
}
512
514
513
515
pub 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 ) ?;
516
518
old. rename (
517
519
old_file. as_os_str ( ) . as_bytes ( ) ,
518
520
& new,
@@ -527,13 +529,13 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
527
529
}
528
530
529
531
pub 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 ) ?;
531
533
dir. remove_directory ( file. as_os_str ( ) . as_bytes ( ) )
532
534
}
533
535
534
536
pub 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)
537
539
}
538
540
539
541
fn read_link ( fd : & WasiFd , file : & Path ) -> io:: Result < PathBuf > {
@@ -568,13 +570,13 @@ fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
568
570
}
569
571
570
572
pub 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 ) ?;
572
574
dst. symlink ( src. as_os_str ( ) . as_bytes ( ) , dst_file. as_os_str ( ) . as_bytes ( ) )
573
575
}
574
576
575
577
pub 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 ) ?;
578
580
src. link (
579
581
libc:: __WASI_LOOKUP_SYMLINK_FOLLOW,
580
582
src_file. as_os_str ( ) . as_bytes ( ) ,
@@ -584,13 +586,13 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
584
586
}
585
587
586
588
pub 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)
589
591
}
590
592
591
593
pub 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)
594
596
}
595
597
596
598
fn metadata_at (
@@ -621,72 +623,69 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
621
623
Ok ( File { fd } )
622
624
}
623
625
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) ) ;
690
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 ;
691
690
}
692
691
}
0 commit comments