private static void RemoveDirectoryHelper(string fullPath, bool recursive, bool throwOnTopLevelDirectoryNotFound)
{
bool r;
int errorCode;
Exception ex = null;
// Do not recursively delete through reparse points. Perhaps in a
// future version we will add a new flag to control this behavior,
// but for now we're much safer if we err on the conservative side.
// This applies to symbolic links and mount points.
// Note the logic to check whether fullPath is a reparse point is
// in Delete(String, String, bool), and will set "recursive" to false.
// Note that Win32's DeleteFile and RemoveDirectory will just delete
// the reparse point itself.
if (recursive)
{
Interop.Kernel32.WIN32_FIND_DATA data = new Interop.Kernel32.WIN32_FIND_DATA();
// Open a Find handle
using (SafeFindHandle hnd = Interop.Kernel32.FindFirstFile(Directory.EnsureTrailingDirectorySeparator(fullPath) + "*", ref data))
{
if (hnd.IsInvalid)
throw Win32Marshal.GetExceptionForLastWin32Error(fullPath);
do
{
bool isDir = (0 != (data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY));
if (isDir)
{
// Skip ".", "..".
if (data.cFileName.Equals(".") || data.cFileName.Equals(".."))
continue;
// Recurse for all directories, unless they are
// reparse points. Do not follow mount points nor
// symbolic links, but do delete the reparse point
// itself.
bool shouldRecurse = (0 == (data.dwFileAttributes & (int)FileAttributes.ReparsePoint));
if (shouldRecurse)
{
string newFullPath = Path.Combine(fullPath, data.cFileName);
try
{
RemoveDirectoryHelper(newFullPath, recursive, false);
}
catch (Exception e)
{
if (ex == null)
ex = e;
}
}
else
{
// Check to see if this is a mount point, and
// unmount it.
if (data.dwReserved0 == Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_MOUNT_POINT)
{
// Use full path plus a trailing '\'
String mountPoint = Path.Combine(fullPath, data.cFileName + PathHelpers.DirectorySeparatorCharAsString);
if (!Interop.Kernel32.DeleteVolumeMountPoint(mountPoint))
{
errorCode = Marshal.GetLastWin32Error();
if (errorCode != Interop.Errors.ERROR_SUCCESS &&
errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND)
{
try
{
throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName);
}
catch (Exception e)
{
if (ex == null)
ex = e;
}
}
}
}
// RemoveDirectory on a symbolic link will
// remove the link itself.
String reparsePoint = Path.Combine(fullPath, data.cFileName);
r = Interop.Kernel32.RemoveDirectory(reparsePoint);
if (!r)
{
errorCode = Marshal.GetLastWin32Error();
if (errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND)
{
try
{
throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName);
}
catch (Exception e)
{
if (ex == null)
ex = e;
}
}
}
}
}
else
{
String fileName = Path.Combine(fullPath, data.cFileName);
r = Interop.Kernel32.DeleteFile(fileName);
if (!r)
{
errorCode = Marshal.GetLastWin32Error();
if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND)
{
try
{
throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName);
}
catch (Exception e)
{
if (ex == null)
ex = e;
}
}
}
}
} while (Interop.Kernel32.FindNextFile(hnd, ref data));
// Make sure we quit with a sensible error.
errorCode = Marshal.GetLastWin32Error();
}
if (ex != null)
throw ex;
if (errorCode != 0 && errorCode != Interop.Errors.ERROR_NO_MORE_FILES)
throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
}
r = Interop.Kernel32.RemoveDirectory(fullPath);
if (!r)
{
errorCode = Marshal.GetLastWin32Error();
if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) // A dubious error code.
errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND;
// This check was originally put in for Win9x (unfortunately without special casing it to be for Win9x only). We can't change the NT codepath now for backcomp reasons.
if (errorCode == Interop.Errors.ERROR_ACCESS_DENIED)
throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, fullPath));
// don't throw the DirectoryNotFoundException since this is a subdir and
// there could be a race condition between two Directory.Delete callers
if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && !throwOnTopLevelDirectoryNotFound)
return;
throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
}
}