public static string[] GetLogicalDrives() { int drives = Interop.Kernel32.GetLogicalDrives(); if (drives == 0) { throw Win32Marshal.GetExceptionForLastWin32Error(); } // GetLogicalDrives returns a bitmask starting from // position 0 "A" indicating whether a drive is present. // Loop over each bit, creating a string for each one // that is set. uint d = (uint)drives; int count = 0; while (d != 0) { if (((int)d & 1) != 0) { count++; } d >>= 1; } string[] result = new string[count]; Span <char> root = stackalloc char[] { 'A', ':', '\\' }; d = (uint)drives; count = 0; while (d != 0) { if (((int)d & 1) != 0) { result[count++] = root.ToString(); } d >>= 1; root[0]++; } return(result); } }
public static DriveInfo[] GetDrives() { int drives = Interop.mincore.GetLogicalDrives(); if (drives == 0) { throw Win32Marshal.GetExceptionForLastWin32Error(); } // GetLogicalDrives returns a bitmask starting from // position 0 "A" indicating whether a drive is present. // Loop over each bit, creating a DriveInfo for each one // that is set. uint d = (uint)drives; int count = 0; while (d != 0) { if (((int)d & 1) != 0) { count++; } d >>= 1; } DriveInfo[] result = new DriveInfo[count]; char[] root = new char[] { 'A', ':', '\\' }; d = (uint)drives; count = 0; while (d != 0) { if (((int)d & 1) != 0) { result[count++] = new DriveInfo(new String(root)); } d >>= 1; root[0]++; } return(result); }
public override string GetCurrentDirectory() { StringBuilder sb = StringBuilderCache.Acquire(Interop.mincore.MAX_PATH + 1); if (Interop.mincore.GetCurrentDirectory(sb.Capacity, sb) == 0) { throw Win32Marshal.GetExceptionForLastWin32Error(); } String currentDirectory = sb.ToString(); // Note that if we have somehow put our command prompt into short // file name mode (ie, by running edlin or a DOS grep, etc), then // this will return a short file name. if (currentDirectory.IndexOf('~') >= 0) { int r = Interop.mincore.GetLongPathName(currentDirectory, sb, sb.Capacity); if (r == 0 || r >= Interop.mincore.MAX_PATH) { int errorCode = Marshal.GetLastWin32Error(); if (r >= Interop.mincore.MAX_PATH) { errorCode = Interop.mincore.Errors.ERROR_FILENAME_EXCED_RANGE; } if (errorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND && errorCode != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND && errorCode != Interop.mincore.Errors.ERROR_INVALID_FUNCTION && // by design - enough said. errorCode != Interop.mincore.Errors.ERROR_ACCESS_DENIED) { throw Win32Marshal.GetExceptionForWin32Error(errorCode); } } currentDirectory = sb.ToString(); } StringBuilderCache.Release(sb); return(currentDirectory); }
[System.Security.SecurityCritical] // auto-generated 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.mincore.WIN32_FIND_DATA data = new Interop.mincore.WIN32_FIND_DATA(); // Open a Find handle using (SafeFindHandle hnd = Interop.mincore.FindFirstFile(Directory.EnsureTrailingDirectorySeparator(fullPath) + "*", ref data)) { if (hnd.IsInvalid) { throw Win32Marshal.GetExceptionForLastWin32Error(fullPath); } do { bool isDir = (0 != (data.dwFileAttributes & Interop.mincore.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.mincore.IOReparseOptions.IO_REPARSE_TAG_MOUNT_POINT) { // Use full path plus a trailing '\' String mountPoint = Path.Combine(fullPath, data.cFileName + PathHelpers.DirectorySeparatorCharAsString); if (!Interop.mincore.DeleteVolumeMountPoint(mountPoint)) { errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.mincore.Errors.ERROR_SUCCESS && errorCode != Interop.mincore.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.mincore.RemoveDirectory(reparsePoint); if (!r) { errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.mincore.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.mincore.DeleteFile(fileName); if (!r) { errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND) { try { throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName); } catch (Exception e) { if (ex == null) { ex = e; } } } } } } while (Interop.mincore.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.mincore.Errors.ERROR_NO_MORE_FILES) { throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } } r = Interop.mincore.RemoveDirectory(fullPath); if (!r) { errorCode = Marshal.GetLastWin32Error(); if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND) // A dubious error code. { errorCode = Interop.mincore.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.mincore.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.mincore.Errors.ERROR_PATH_NOT_FOUND && !throwOnTopLevelDirectoryNotFound) { return; } throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } }
[System.Security.SecurityCritical] // auto-generated internal static int FillAttributeInfo(String path, ref Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound) { int errorCode = 0; if (tryagain) // someone has a handle to the file open, or other error { Interop.mincore.WIN32_FIND_DATA findData; findData = new Interop.mincore.WIN32_FIND_DATA(); // Remove trialing slash since this can cause grief to FindFirstFile. You will get an invalid argument error String tempPath = path.TrimEnd(PathHelpers.DirectorySeparatorChars); // For floppy drives, normally the OS will pop up a dialog saying // there is no disk in drive A:, please insert one. We don't want that. // SetErrorMode will let us disable this, but we should set the error // mode back, since this may have wide-ranging effects. uint oldMode = Interop.mincore.SetErrorMode(Interop.mincore.SEM_FAILCRITICALERRORS); try { bool error = false; SafeFindHandle handle = Interop.mincore.FindFirstFile(tempPath, ref findData); try { if (handle.IsInvalid) { error = true; errorCode = Marshal.GetLastWin32Error(); if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND || errorCode == Interop.mincore.Errors.ERROR_PATH_NOT_FOUND || errorCode == Interop.mincore.Errors.ERROR_NOT_READY) // floppy device not ready { if (!returnErrorOnNotFound) { // Return default value for backward compatibility errorCode = 0; data.fileAttributes = -1; } } return(errorCode); } } finally { // Close the Win32 handle try { handle.Dispose(); } catch { // if we're already returning an error, don't throw another one. if (!error) { throw Win32Marshal.GetExceptionForLastWin32Error(); } } } } finally { Interop.mincore.SetErrorMode(oldMode); } // Copy the information to data data.PopulateFrom(ref findData); } else { // For floppy drives, normally the OS will pop up a dialog saying // there is no disk in drive A:, please insert one. We don't want that. // SetErrorMode will let us disable this, but we should set the error // mode back, since this may have wide-ranging effects. bool success = false; uint oldMode = Interop.mincore.SetErrorMode(Interop.mincore.SEM_FAILCRITICALERRORS); try { success = Interop.mincore.GetFileAttributesEx(path, Interop.mincore.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data); } finally { Interop.mincore.SetErrorMode(oldMode); } if (!success) { errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND && errorCode != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND && errorCode != Interop.mincore.Errors.ERROR_NOT_READY) // floppy device not ready { // In case someone latched onto the file. Take the perf hit only for failure return(FillAttributeInfo(path, ref data, true, returnErrorOnNotFound)); } else { if (!returnErrorOnNotFound) { // Return default value for backward compatibility errorCode = 0; data.fileAttributes = -1; } } } } return(errorCode); }
// Returns 0 on success, otherwise a Win32 error code. Note that // classes should use -1 as the uninitialized state for dataInitialized. internal static int FillAttributeInfo(String path, ref Win32Native.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound) { int dataInitialised = 0; if (tryagain) // someone has a handle to the file open, or other error { Win32Native.WIN32_FIND_DATA findData; findData = new Win32Native.WIN32_FIND_DATA(); // Remove trialing slash since this can cause grief to FindFirstFile. You will get an invalid argument error String tempPath = path.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); #if !PLATFORM_UNIX // For floppy drives, normally the OS will pop up a dialog saying // there is no disk in drive A:, please insert one. We don't want that. // SetThreadErrorMode will let us disable this, but we should set the error // mode back, since this may have wide-ranging effects. uint oldMode; bool errorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); try { #endif bool error = false; SafeFindHandle handle = Win32Native.FindFirstFile(tempPath, findData); try { if (handle.IsInvalid) { error = true; dataInitialised = Marshal.GetLastWin32Error(); if (dataInitialised == Win32Native.ERROR_FILE_NOT_FOUND || dataInitialised == Win32Native.ERROR_PATH_NOT_FOUND || dataInitialised == Win32Native.ERROR_NOT_READY) // floppy device not ready { if (!returnErrorOnNotFound) { // Return default value for backward compatibility dataInitialised = 0; data.fileAttributes = -1; } } return(dataInitialised); } } finally { // Close the Win32 handle try { handle.Close(); } catch { // if we're already returning an error, don't throw another one. if (!error) { Debug.Assert(false, "File::FillAttributeInfo - FindClose failed!"); throw Win32Marshal.GetExceptionForLastWin32Error(); } } } #if !PLATFORM_UNIX } finally { if (errorModeSuccess) { Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); } } #endif // Copy the information to data data.PopulateFrom(findData); } else { bool success = false; #if !PLATFORM_UNIX // For floppy drives, normally the OS will pop up a dialog saying // there is no disk in drive A:, please insert one. We don't want that. // SetThreadErrorMode will let us disable this, but we should set the error // mode back, since this may have wide-ranging effects. uint oldMode; bool errorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); try { #endif success = Win32Native.GetFileAttributesEx(path, GetFileExInfoStandard, ref data); #if !PLATFORM_UNIX } finally { if (errorModeSuccess) { Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); } } #endif if (!success) { dataInitialised = Marshal.GetLastWin32Error(); if (dataInitialised != Win32Native.ERROR_FILE_NOT_FOUND && dataInitialised != Win32Native.ERROR_PATH_NOT_FOUND && dataInitialised != Win32Native.ERROR_NOT_READY) // floppy device not ready { // In case someone latched onto the file. Take the perf hit only for failure return(FillAttributeInfo(path, ref data, true, returnErrorOnNotFound)); } else { if (!returnErrorOnNotFound) { // Return default value for backward compbatibility dataInitialised = 0; data.fileAttributes = -1; } } } } return(dataInitialised); }
private static void RemoveDirectoryRecursive(string fullPath, ref Interop.Kernel32.WIN32_FIND_DATA findData, bool topLevel) { int errorCode; Exception exception = null; using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(Path.Join(fullPath, "*"), ref findData)) { if (handle.IsInvalid) { throw Win32Marshal.GetExceptionForLastWin32Error(fullPath); } do { if ((findData.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0) { // File string fileName = findData.cFileName.GetStringFromFixedBuffer(); if (!Interop.Kernel32.DeleteFile(Path.Combine(fullPath, fileName)) && exception == null) { errorCode = Marshal.GetLastWin32Error(); // We don't care if something else deleted the file first if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND) { exception = Win32Marshal.GetExceptionForWin32Error(errorCode, fileName); } } } else { // Directory, skip ".", "..". if (findData.cFileName.FixedBufferEqualsString(".") || findData.cFileName.FixedBufferEqualsString("..")) { continue; } string fileName = findData.cFileName.GetStringFromFixedBuffer(); if (!IsNameSurrogateReparsePoint(ref findData)) { // Not a reparse point, or the reparse point isn't a name surrogate, recurse. try { RemoveDirectoryRecursive( Path.Combine(fullPath, fileName), findData: ref findData, topLevel: false); } catch (Exception e) { if (exception == null) { exception = e; } } } else { // Name surrogate reparse point, don't recurse, simply remove the directory. // If a mount point, we have to delete the mount point first. if (findData.dwReserved0 == Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_MOUNT_POINT) { // Mount point. Unmount using full path plus a trailing '\'. // (Note: This doesn't remove the underlying directory) string mountPoint = Path.Join(fullPath, fileName, PathInternal.DirectorySeparatorCharAsString); if (!Interop.Kernel32.DeleteVolumeMountPoint(mountPoint) && exception == null) { errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.Errors.ERROR_SUCCESS && errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND) { exception = Win32Marshal.GetExceptionForWin32Error(errorCode, fileName); } } } // Note that RemoveDirectory on a symbolic link will remove the link itself. if (!Interop.Kernel32.RemoveDirectory(Path.Combine(fullPath, fileName)) && exception == null) { errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND) { exception = Win32Marshal.GetExceptionForWin32Error(errorCode, fileName); } } } } } while (Interop.Kernel32.FindNextFile(handle, ref findData)); if (exception != null) { throw exception; } errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.Errors.ERROR_SUCCESS && errorCode != Interop.Errors.ERROR_NO_MORE_FILES) { throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } } // As we successfully removed all of the files we shouldn't care about the directory itself // not being empty. As file deletion is just a marker to remove the file when all handles // are closed we could still have contents hanging around. RemoveDirectoryInternal(fullPath, topLevel: topLevel, allowDirectoryNotEmpty: true); }
private static void RemoveDirectoryRecursive(string fullPath, ref Interop.Kernel32.WIN32_FIND_DATA findData, bool topLevel) { int errorCode; Exception exception = null; using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(Directory.EnsureTrailingDirectorySeparator(fullPath) + "*", ref findData)) { if (handle.IsInvalid) { throw Win32Marshal.GetExceptionForLastWin32Error(fullPath); } do { if ((findData.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0) { // File string fileName = findData.cFileName.GetStringFromFixedBuffer(); if (!Interop.Kernel32.DeleteFile(Path.Combine(fullPath, fileName)) && exception == null) { errorCode = Marshal.GetLastWin32Error(); // We don't care if something else deleted the file first if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND) { exception = Win32Marshal.GetExceptionForWin32Error(errorCode, fileName); } } } else { // Directory, skip ".", "..". if (findData.cFileName.FixedBufferEqualsString(".") || findData.cFileName.FixedBufferEqualsString("..")) { continue; } string fileName = findData.cFileName.GetStringFromFixedBuffer(); if ((findData.dwFileAttributes & (int)FileAttributes.ReparsePoint) == 0) { // Not a reparse point, recurse. try { RemoveDirectoryRecursive( Path.Combine(fullPath, fileName), findData: ref findData, topLevel: false); } catch (Exception e) { if (exception == null) { exception = e; } } } else { // Reparse point, don't recurse, just remove. (dwReserved0 is documented for this flag) if (findData.dwReserved0 == Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_MOUNT_POINT) { // Mount point. Unmount using full path plus a trailing '\'. // (Note: This doesn't remove the underlying directory) string mountPoint = Path.Combine(fullPath, fileName + PathHelpers.DirectorySeparatorCharAsString); if (!Interop.Kernel32.DeleteVolumeMountPoint(mountPoint) && exception == null) { errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.Errors.ERROR_SUCCESS && errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND) { exception = Win32Marshal.GetExceptionForWin32Error(errorCode, fileName); } } } // Note that RemoveDirectory on a symbolic link will remove the link itself. if (!Interop.Kernel32.RemoveDirectory(Path.Combine(fullPath, fileName)) && exception == null) { errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND) { exception = Win32Marshal.GetExceptionForWin32Error(errorCode, fileName); } } } } } while (Interop.Kernel32.FindNextFile(handle, ref findData)); if (exception != null) { throw exception; } errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.Errors.ERROR_SUCCESS && errorCode != Interop.Errors.ERROR_NO_MORE_FILES) { throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } } RemoveDirectoryInternal(fullPath, topLevel: topLevel); }
internal unsafe int GetFullPathName() { if (useStackAlloc) { char *finalBuffer = stackalloc char[Path.MaxPath + 1]; int result = Interop.mincore.GetFullPathNameW(m_arrayPtr, Path.MaxPath + 1, finalBuffer, IntPtr.Zero); // If success, the return buffer length does not account for the terminating null character. // If in-sufficient buffer, the return buffer length does account for the path + the terminating null character. // If failure, the return buffer length is zero if (result > Path.MaxPath) { char *tempBuffer = stackalloc char[result]; finalBuffer = tempBuffer; result = Interop.mincore.GetFullPathNameW(m_arrayPtr, result, finalBuffer, IntPtr.Zero); } // Full path is genuinely long if (result >= Path.MaxPath) { throw new PathTooLongException(SR.IO_PathTooLong); } Contract.Assert(result < Path.MaxPath, "did we accidently remove a PathTooLongException check?"); if (result == 0 && m_arrayPtr[0] != '\0') { throw Win32Marshal.GetExceptionForLastWin32Error(); } else if (result < Path.MaxPath) { // Null terminate explicitly (may be only needed for some cases such as empty strings) // GetFullPathName return length doesn't account for null terminating char... finalBuffer[result] = '\0'; // Safe to write directly as result is < Path.MaxPath } // We have expanded the paths and GetLongPathName may or may not behave differently from before. // We need to call it again to see: doNotTryExpandShortFileName = false; Wstrcpy(m_arrayPtr, finalBuffer, result); // Doesn't account for null terminating char. Think of this as the last // valid index into the buffer but not the length of the buffer Length = result; return(result); } else { StringBuilder finalBuffer = new StringBuilder(m_capacity + 1); int result = Interop.mincore.GetFullPathNameW(m_sb.ToString(), m_capacity + 1, finalBuffer, IntPtr.Zero); // If success, the return buffer length does not account for the terminating null character. // If in-sufficient buffer, the return buffer length does account for the path + the terminating null character. // If failure, the return buffer length is zero if (result > m_maxPath) { finalBuffer.Length = result; result = Interop.mincore.GetFullPathNameW(m_sb.ToString(), result, finalBuffer, IntPtr.Zero); } // Fullpath is genuinely long if (result >= m_maxPath) { throw new PathTooLongException(SR.IO_PathTooLong); } Contract.Assert(result < m_maxPath, "did we accidentally remove a PathTooLongException check?"); if (result == 0 && m_sb[0] != '\0') { if (Length >= m_maxPath) { throw new PathTooLongException(SR.IO_PathTooLong); } throw Win32Marshal.GetExceptionForLastWin32Error(); } // We have expanded the paths and GetLongPathName may or may not behave differently from before. // We need to call it again to see: doNotTryExpandShortFileName = false; m_sb = finalBuffer; return(result); } }