@@ -434,8 +434,12 @@ public static void MoveDirectory(string sourceFullPath, string destFullPath)
434434
435435 public static void RemoveDirectory ( string fullPath , bool recursive )
436436 {
437- if ( ! TryRemoveDirectory ( fullPath , topLevel : true , throwWhenNotEmpty : ! recursive ) )
437+ // Delete the directory.
438+ // If we're recursing, don't throw when it is not empty, and perform a recursive remove.
439+ if ( ! RemoveEmptyDirectory ( fullPath , topLevel : true , throwWhenNotEmpty : ! recursive ) )
438440 {
441+ Debug . Assert ( recursive ) ;
442+
439443 RemoveDirectoryRecursive ( fullPath ) ;
440444 }
441445 }
@@ -446,17 +450,16 @@ private static void RemoveDirectoryRecursive(string fullPath)
446450
447451 try
448452 {
449- foreach ( ( string childPath , bool isDirectory ) in
450- new FileSystemEnumerable < ( string , bool ) > (
451- fullPath ,
452- static ( ref FileSystemEntry entry ) =>
453- {
454- // Don't report symlinks to directories as directories.
455- bool isRealDirectory = ! entry . IsSymbolicLink && entry . IsDirectory ;
456-
457- return ( entry . ToFullPath ( ) , isRealDirectory ) ;
458- } ,
459- EnumerationOptions . Compatible ) )
453+ var fse = new FileSystemEnumerable < ( string , bool ) > ( fullPath ,
454+ static ( ref FileSystemEntry entry ) =>
455+ {
456+ // Don't report symlinks to directories as directories.
457+ bool isRealDirectory = ! entry . IsSymbolicLink && entry . IsDirectory ;
458+ return ( entry . ToFullPath ( ) , isRealDirectory ) ;
459+ } ,
460+ EnumerationOptions . Compatible ) ;
461+
462+ foreach ( ( string childPath , bool isDirectory ) in fse )
460463 {
461464 try
462465 {
@@ -485,11 +488,10 @@ private static void RemoveDirectoryRecursive(string fullPath)
485488 throw firstException ;
486489 }
487490
488- bool removed = TryRemoveDirectory ( fullPath ) ;
489- Debug . Assert ( removed ) ;
491+ RemoveEmptyDirectory ( fullPath ) ;
490492 }
491493
492- private static bool TryRemoveDirectory ( string fullPath , bool topLevel = false , bool throwWhenNotEmpty = true )
494+ private static bool RemoveEmptyDirectory ( string fullPath , bool topLevel = false , bool throwWhenNotEmpty = true )
493495 {
494496 if ( Interop . Sys . RmDir ( fullPath ) < 0 )
495497 {
@@ -502,20 +504,22 @@ private static bool TryRemoveDirectory(string fullPath, bool topLevel = false, b
502504 case Interop . Error . EISDIR :
503505 throw new IOException ( SR . Format ( SR . UnauthorizedAccess_IODenied_Path , fullPath ) ) ; // match Win32 exception
504506 case Interop . Error . ENOENT :
507+ // When we're recursing, don't throw for items that go missing.
505508 if ( ! topLevel )
506509 {
507510 return true ;
508511 }
509512 goto default ;
510513 case Interop . Error . ENOTDIR :
514+ // When the top-level path is a symlink to a directory, delete the link.
515+ // In other cases, throw because we expect path to be a real directory.
511516 if ( topLevel )
512517 {
513518 if ( ! DirectoryExists ( fullPath ) )
514519 {
515520 throw Interop . GetExceptionForIoErrno ( Interop . Error . ENOENT . Info ( ) , fullPath , isDirectory : true ) ;
516521 }
517522
518- // Path is symbolic link to a directory.
519523 DeleteFile ( fullPath ) ;
520524 return true ;
521525 }
0 commit comments