@@ -16,7 +16,7 @@ use crate::sys::time::SystemTime;
1616use crate :: sys:: unsupported;
1717use crate :: sys_common:: { AsInner , FromInner , IntoInner } ;
1818
19- pub use crate :: sys_common:: fs:: { remove_dir_all , try_exists} ;
19+ pub use crate :: sys_common:: fs:: try_exists;
2020
2121pub struct File {
2222 fd : WasiFd ,
@@ -130,6 +130,18 @@ impl FileType {
130130 }
131131}
132132
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+
133145impl fmt:: Debug for ReadDir {
134146 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
135147 f. debug_struct ( "ReadDir" ) . finish_non_exhaustive ( )
@@ -512,13 +524,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
512524 opts. directory ( true ) ;
513525 opts. read ( true ) ;
514526 let dir = File :: open ( p, & opts) ?;
515- Ok ( ReadDir {
516- cookie : Some ( 0 ) ,
517- buf : vec ! [ 0 ; 128 ] ,
518- offset : 0 ,
519- cap : 0 ,
520- inner : Arc :: new ( ReadDirInner { dir, root : p. to_path_buf ( ) } ) ,
521- } )
527+ Ok ( ReadDir :: new ( dir, p. to_path_buf ( ) ) )
522528}
523529
524530pub fn unlink ( p : & Path ) -> io:: Result < ( ) > {
@@ -712,3 +718,52 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
712718
713719 io:: copy ( & mut reader, & mut writer)
714720}
721+
722+ pub fn remove_dir_all ( path : & Path ) -> io:: Result < ( ) > {
723+ let ( parent, path) = open_parent ( path) ?;
724+ remove_dir_all_recursive ( & parent, & path)
725+ }
726+
727+ fn remove_dir_all_recursive ( parent : & WasiFd , path : & Path ) -> io:: Result < ( ) > {
728+ // Open up a file descriptor for the directory itself. Note that we don't
729+ // follow symlinks here and we specifically open directories.
730+ //
731+ // At the root invocation of this function this will correctly handle
732+ // symlinks passed to the top-level `remove_dir_all`. At the recursive
733+ // level this will double-check that after the `readdir` call deduced this
734+ // was a directory it's still a directory by the time we open it up.
735+ //
736+ // If the opened file was actually a symlink then the symlink is deleted,
737+ // not the directory recursively.
738+ let mut opts = OpenOptions :: new ( ) ;
739+ opts. lookup_flags ( 0 ) ;
740+ opts. directory ( true ) ;
741+ opts. read ( true ) ;
742+ let fd = open_at ( parent, path, & opts) ?;
743+ if fd. file_attr ( ) ?. file_type ( ) . is_symlink ( ) {
744+ return parent. unlink_file ( osstr2str ( path. as_ref ( ) ) ?) ;
745+ }
746+
747+ // this "root" is only used by `DirEntry::path` which we don't use below so
748+ // it's ok for this to be a bogus value
749+ let dummy_root = PathBuf :: new ( ) ;
750+
751+ // Iterate over all the entries in this directory, and travel recursively if
752+ // necessary
753+ for entry in ReadDir :: new ( fd, dummy_root) {
754+ let entry = entry?;
755+ let path = crate :: str:: from_utf8 ( & entry. name ) . map_err ( |_| {
756+ io:: Error :: new_const ( io:: ErrorKind :: Uncategorized , & "invalid utf-8 file name found" )
757+ } ) ?;
758+
759+ if entry. file_type ( ) ?. is_dir ( ) {
760+ remove_dir_all_recursive ( & entry. inner . dir . fd , path. as_ref ( ) ) ?;
761+ } else {
762+ entry. inner . dir . fd . unlink_file ( path) ?;
763+ }
764+ }
765+
766+ // Once all this directory's contents are deleted it should be safe to
767+ // delete the directory tiself.
768+ parent. remove_directory ( osstr2str ( path. as_ref ( ) ) ?)
769+ }
0 commit comments