@@ -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 ( )
@@ -516,13 +528,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
516528 opts. directory ( true ) ;
517529 opts. read ( true ) ;
518530 let dir = File :: open ( p, & opts) ?;
519- Ok ( ReadDir {
520- cookie : Some ( 0 ) ,
521- buf : vec ! [ 0 ; 128 ] ,
522- offset : 0 ,
523- cap : 0 ,
524- inner : Arc :: new ( ReadDirInner { dir, root : p. to_path_buf ( ) } ) ,
525- } )
531+ Ok ( ReadDir :: new ( dir, p. to_path_buf ( ) ) )
526532}
527533
528534pub fn unlink ( p : & Path ) -> io:: Result < ( ) > {
@@ -716,3 +722,52 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
716722
717723 io:: copy ( & mut reader, & mut writer)
718724}
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:: Error :: new_const ( 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+ }
0 commit comments