VolumeControl.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Runtime.InteropServices;
  11. using System.Text;
  12. using Microsoft.Win32.SafeHandles;
  13. namespace DiskAccessLibrary
  14. {
  15. public class VolumeControl
  16. {
  17. private const uint FSCTL_IS_VOLUME_MOUNTED = 0x90028;
  18. private const uint FSCTL_DISMOUNT_VOLUME = 0x90020;
  19. private const uint FSCTL_LOCK_VOLUME = 0x90018;
  20. private const uint FSCTL_UNLOCK_VOLUME = 0x9001C;
  21. private const uint FSCTL_ALLOW_EXTENDED_DASD_IO = 0x90083;
  22. public const int MaxPath = 260;
  23. [DllImport("kernel32.dll", SetLastError = true)]
  24. private static extern bool GetVolumePathNamesForVolumeNameW(
  25. [MarshalAs(UnmanagedType.LPWStr)]
  26. string lpszVolumeName,
  27. [MarshalAs(UnmanagedType.LPWStr)]
  28. string lpszVolumePathNames,
  29. uint cchBuferLength,
  30. ref uint lpcchReturnLength);
  31. /// <summary>
  32. /// e.g.
  33. /// D:\
  34. /// E:\MountPoint\
  35. /// </summary>
  36. public static List<string> GetVolumeMountPoints(Guid volumeGuid)
  37. {
  38. string volumeGuidPath = String.Format(@"\\?\Volume{0}\", volumeGuid.ToString("B"));
  39. string lpszVolumeName = new string(new char[MaxPath]);
  40. uint returnLength = 0;
  41. bool success = GetVolumePathNamesForVolumeNameW(volumeGuidPath, lpszVolumeName, (uint)lpszVolumeName.Length, ref returnLength);
  42. if (success)
  43. {
  44. lpszVolumeName = lpszVolumeName.Substring(0, (int)returnLength);
  45. }
  46. else
  47. {
  48. if (Marshal.GetLastWin32Error() == (int)Win32Error.ERROR_MORE_DATA)
  49. {
  50. lpszVolumeName = new string(new char[returnLength]);
  51. success = GetVolumePathNamesForVolumeNameW(volumeGuidPath, lpszVolumeName, (uint)lpszVolumeName.Length, ref returnLength);
  52. }
  53. }
  54. if (success)
  55. {
  56. lpszVolumeName = lpszVolumeName.TrimEnd('\0');
  57. if (lpszVolumeName != String.Empty)
  58. {
  59. List<string> result = new List<string>(lpszVolumeName.Split('\0'));
  60. return result;
  61. }
  62. }
  63. return new List<string>();
  64. }
  65. /// <summary>
  66. /// Note: The NTFS file system treats a locked volume as a dismounted volume.
  67. /// </summary>
  68. public static bool IsVolumeMounted(char driveLetter)
  69. {
  70. SafeFileHandle handle = HandleUtils.GetVolumeHandle(driveLetter, FileAccess.Read, ShareMode.ReadWrite);
  71. return IsVolumeMounted(handle);
  72. }
  73. /// <summary>
  74. /// Note: The NTFS file system treats a locked volume as a dismounted volume.
  75. /// </summary>
  76. public static bool IsVolumeMounted(string path)
  77. {
  78. SafeFileHandle handle = HandleUtils.GetVolumeHandle(path, FileAccess.Read, ShareMode.ReadWrite);
  79. return IsVolumeMounted(handle);
  80. }
  81. /// <summary>
  82. /// Note: The NTFS file system treats a locked volume as a dismounted volume.
  83. /// </summary>
  84. public static bool IsVolumeMounted(Guid volumeGuid)
  85. {
  86. SafeFileHandle handle = HandleUtils.GetVolumeHandle(volumeGuid, FileAccess.Read, ShareMode.ReadWrite);
  87. return IsVolumeMounted(handle);
  88. }
  89. /// <summary>
  90. /// Note: The NTFS file system treats a locked volume as a dismounted volume.
  91. /// </summary>
  92. /// <param name="handle">When opening a volume, the dwShareMode parameter must have the FILE_SHARE_WRITE flag.</param>
  93. public static bool IsVolumeMounted(SafeFileHandle handle)
  94. {
  95. if (!handle.IsInvalid)
  96. {
  97. uint dummy;
  98. bool mounted = PhysicalDiskControl.DeviceIoControl(handle, FSCTL_IS_VOLUME_MOUNTED, IntPtr.Zero, 0, IntPtr.Zero, 0, out dummy, IntPtr.Zero);
  99. handle.Close();
  100. return mounted;
  101. }
  102. else
  103. {
  104. return false;
  105. }
  106. }
  107. // By locking the volume before you dismount it, you can ensure that the volume is dismounted cleanly
  108. // (because the system flushes all cached data to the volume before locking it)
  109. //
  110. // A locked volume remains locked until one of the following occurs:
  111. // * The application uses the FSCTL_UNLOCK_VOLUME control code to unlock the volume.
  112. // * The handle closes, either directly through CloseHandle, or indirectly when a process terminates.
  113. // http://msdn.microsoft.com/en-us/library/bb521494(v=winembedded.5).aspx
  114. // http://msdn.microsoft.com/en-us/library/Aa364575
  115. /// <param name="handle">
  116. /// The application must specify the FILE_SHARE_READ and FILE_SHARE_WRITE flags in the dwShareMode parameter of CreateFile.
  117. /// https://msdn.microsoft.com/en-us/library/Aa364575
  118. /// </param>
  119. public static bool LockVolume(SafeFileHandle handle)
  120. {
  121. if (!handle.IsInvalid)
  122. {
  123. uint dummy;
  124. bool success = PhysicalDiskControl.DeviceIoControl(handle, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out dummy, IntPtr.Zero);
  125. return success;
  126. }
  127. else
  128. {
  129. return false;
  130. }
  131. }
  132. // Forced dismount only does FSCTL_DISMOUNT_VOLUME (without locking the volume first),
  133. // and then does the formatting write/verify operations _from this very handle_.
  134. // Then the handle is just closed, and any next attempt to access the volume will automount it.
  135. /// <param name="handle">
  136. /// The application must specify the FILE_SHARE_READ and FILE_SHARE_WRITE flags in the dwShareMode parameter of CreateFile.
  137. /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa364562%28v=vs.85%29.aspx
  138. /// </param>
  139. public static bool DismountVolume(SafeFileHandle handle)
  140. {
  141. if (!handle.IsInvalid)
  142. {
  143. uint dummy;
  144. bool success = PhysicalDiskControl.DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out dummy, IntPtr.Zero);
  145. if (!success)
  146. {
  147. int errorCode = Marshal.GetLastWin32Error();
  148. if (errorCode == (int)Win32Error.ERROR_ACCESS_DENIED)
  149. {
  150. throw new UnauthorizedAccessException();
  151. }
  152. }
  153. return success;
  154. }
  155. else
  156. {
  157. return false;
  158. }
  159. }
  160. public static bool AllowExtendedIO(SafeFileHandle handle)
  161. {
  162. if (!handle.IsInvalid)
  163. {
  164. uint dummy;
  165. bool success = PhysicalDiskControl.DeviceIoControl(handle, FSCTL_ALLOW_EXTENDED_DASD_IO, IntPtr.Zero, 0, IntPtr.Zero, 0, out dummy, IntPtr.Zero);
  166. if (!success)
  167. {
  168. int errorCode = Marshal.GetLastWin32Error();
  169. if (errorCode == (int)Win32Error.ERROR_ACCESS_DENIED)
  170. {
  171. throw new UnauthorizedAccessException();
  172. }
  173. }
  174. return success;
  175. }
  176. else
  177. {
  178. return false;
  179. }
  180. }
  181. }
  182. }