You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

463 lines
18 KiB

  1. /* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. * copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. * THE SOFTWARE.
  20. */
  21. using Microsoft.Win32.SafeHandles;
  22. using System;
  23. using System.Diagnostics.CodeAnalysis;
  24. using System.Runtime.InteropServices;
  25. using System.Security;
  26. using System.Text;
  27. namespace Alphaleonis.Win32.Filesystem
  28. {
  29. /// <summary>Contains information about a filesystem Volume.</summary>
  30. [SerializableAttribute]
  31. [SecurityCritical]
  32. public sealed class VolumeInfo
  33. {
  34. #region Constructor
  35. /// <summary>Initializes a VolumeInfo instance.</summary>
  36. /// <exception cref="ArgumentNullException"/>
  37. /// <exception cref="ArgumentException"/>
  38. /// <param name="volumeName">A valid drive path or drive letter. This can be either uppercase or lowercase, 'a' to 'z' or a network share in the format: \\server\share.</param>
  39. [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Utils.IsNullOrWhiteSpace validates arguments.")]
  40. public VolumeInfo(string volumeName)
  41. {
  42. if (Utils.IsNullOrWhiteSpace(volumeName))
  43. throw new ArgumentNullException("volumeName");
  44. if (!volumeName.StartsWith(Path.LongPathPrefix, StringComparison.OrdinalIgnoreCase))
  45. volumeName = Path.IsUncPathCore(volumeName, false, false)
  46. ? Path.GetLongPathCore(volumeName, GetFullPathOptions.None)
  47. : Path.LongPathPrefix + volumeName;
  48. else
  49. {
  50. if (volumeName.Length == 1)
  51. volumeName += Path.VolumeSeparatorChar;
  52. else if (!volumeName.StartsWith(Path.GlobalRootPrefix, StringComparison.OrdinalIgnoreCase))
  53. volumeName = Path.GetPathRoot(volumeName, false);
  54. }
  55. if (Utils.IsNullOrWhiteSpace(volumeName))
  56. throw new ArgumentException("Argument must be a drive letter (\"C\"), RootDir (\"C:\\\") or UNC path (\"\\\\server\\share\")");
  57. Name = Path.AddTrailingDirectorySeparator(volumeName, false);
  58. _volumeHandle = null;
  59. }
  60. /// <summary>Initializes a VolumeInfo instance.</summary>
  61. /// <param name="driveName">A valid drive path or drive letter. This can be either uppercase or lowercase, 'a' to 'z' or a network share in the format: "\\server\share".</param>
  62. /// <param name="refresh">Refreshes the state of the object.</param>
  63. /// <param name="continueOnException"><see langword="true"/> suppress any Exception that might be thrown as a result from a failure, such as unavailable resources.</param>
  64. [SecurityCritical]
  65. public VolumeInfo(string driveName, bool refresh, bool continueOnException) : this(driveName)
  66. {
  67. _continueOnAccessError = continueOnException;
  68. if (refresh)
  69. Refresh();
  70. }
  71. /// <summary>Initializes a VolumeInfo instance.</summary>
  72. /// <param name="volumeHandle">An instance to a <see cref="SafeFileHandle"/> handle.</param>
  73. [SecurityCritical]
  74. public VolumeInfo(SafeFileHandle volumeHandle)
  75. {
  76. _volumeHandle = volumeHandle;
  77. }
  78. /// <summary>Initializes a VolumeInfo instance.</summary>
  79. /// <param name="volumeHandle">An instance to a <see cref="SafeFileHandle"/> handle.</param>
  80. /// <param name="refresh">Refreshes the state of the object.</param>
  81. /// <param name="continueOnException"><see langword="true"/> suppress any Exception that might be thrown as a result from a failure, such as unavailable resources.</param>
  82. [SecurityCritical]
  83. public VolumeInfo(SafeFileHandle volumeHandle, bool refresh, bool continueOnException) : this(volumeHandle)
  84. {
  85. _continueOnAccessError = continueOnException;
  86. if (refresh)
  87. Refresh();
  88. }
  89. #endregion // Constructor
  90. #region Fields
  91. [NonSerialized] private readonly bool _continueOnAccessError;
  92. [NonSerialized] private readonly SafeFileHandle _volumeHandle;
  93. [NonSerialized] private NativeMethods.VolumeInfoAttributes _volumeInfoAttributes;
  94. #endregion // Fields
  95. #region Methods
  96. #region Refresh
  97. /// <summary>Refreshes the state of the object.</summary>
  98. public void Refresh()
  99. {
  100. var volumeNameBuffer = new StringBuilder(NativeMethods.MaxPath + 1);
  101. var fileSystemNameBuffer = new StringBuilder(NativeMethods.MaxPath + 1);
  102. int maximumComponentLength;
  103. uint serialNumber;
  104. using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors))
  105. {
  106. // GetVolumeInformationXxx()
  107. // In the ANSI version of this function, the name is limited to 248 characters.
  108. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
  109. // 2013-07-18: MSDN does not confirm LongPath usage but a Unicode version of this function exists.
  110. uint lastError;
  111. do
  112. {
  113. if (!(_volumeHandle != null && NativeMethods.IsAtLeastWindowsVista
  114. // GetVolumeInformationByHandle() / GetVolumeInformation()
  115. // In the ANSI version of this function, the name is limited to 248 characters.
  116. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
  117. // 2013-07-18: MSDN does not confirm LongPath usage but a Unicode version of this function exists.
  118. ? NativeMethods.GetVolumeInformationByHandle(_volumeHandle, volumeNameBuffer, (uint) volumeNameBuffer.Capacity, out serialNumber, out maximumComponentLength, out _volumeInfoAttributes, fileSystemNameBuffer, (uint) fileSystemNameBuffer.Capacity)
  119. : NativeMethods.GetVolumeInformation(Path.AddTrailingDirectorySeparator(Name, false), volumeNameBuffer, (uint) volumeNameBuffer.Capacity, out serialNumber, out maximumComponentLength, out _volumeInfoAttributes, fileSystemNameBuffer, (uint) fileSystemNameBuffer.Capacity))
  120. // A trailing backslash is required.
  121. )
  122. {
  123. lastError = (uint) Marshal.GetLastWin32Error();
  124. switch (lastError)
  125. {
  126. case Win32Errors.ERROR_NOT_READY:
  127. if (!_continueOnAccessError)
  128. throw new DeviceNotReadyException();
  129. break;
  130. case Win32Errors.ERROR_MORE_DATA:
  131. // With a large enough buffer this code never executes.
  132. volumeNameBuffer.Capacity = volumeNameBuffer.Capacity*2;
  133. fileSystemNameBuffer.Capacity = fileSystemNameBuffer.Capacity*2;
  134. break;
  135. default:
  136. if (!_continueOnAccessError)
  137. NativeError.ThrowException(Name);
  138. break;
  139. }
  140. }
  141. else
  142. break;
  143. } while (lastError == Win32Errors.ERROR_MORE_DATA);
  144. }
  145. FullPath = Path.GetRegularPathCore(Name, GetFullPathOptions.None, false);
  146. Name = volumeNameBuffer.ToString();
  147. FileSystemName = fileSystemNameBuffer.ToString();
  148. FileSystemName = Utils.IsNullOrWhiteSpace(FileSystemName) ? null : FileSystemName;
  149. MaximumComponentLength = maximumComponentLength;
  150. SerialNumber = serialNumber;
  151. }
  152. #endregion // Refresh
  153. #region ToString
  154. /// <summary>Returns the full path of the volume.</summary>
  155. /// <returns>A string that represents this instance.</returns>
  156. public override string ToString()
  157. {
  158. return Guid;
  159. }
  160. #endregion // ToString
  161. #endregion // Methods
  162. #region Properties
  163. #region CasePreservedNames
  164. /// <summary>The specified volume supports preserved case of file names when it places a name on disk.</summary>
  165. public bool CasePreservedNames
  166. {
  167. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.CasePreservedNames) == NativeMethods.VolumeInfoAttributes.CasePreservedNames; }
  168. }
  169. #endregion // CasePreservedNames
  170. #region CaseSensitiveSearch
  171. /// <summary>The specified volume supports case-sensitive file names.</summary>
  172. public bool CaseSensitiveSearch
  173. {
  174. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.CaseSensitiveSearch) == NativeMethods.VolumeInfoAttributes.CaseSensitiveSearch; }
  175. }
  176. #endregion // CaseSensitiveSearch
  177. #region Compression
  178. /// <summary>The specified volume supports file-based compression.</summary>
  179. public bool Compression
  180. {
  181. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.Compression) == NativeMethods.VolumeInfoAttributes.Compression; }
  182. }
  183. #endregion // Compression
  184. #region FileSystemName
  185. /// <summary>Gets the name of the file system, for example, the FAT file system or the NTFS file system.</summary>
  186. /// <value>The name of the file system.</value>
  187. public string FileSystemName { get; private set; }
  188. #endregion // FileSystemName
  189. #region FullPath
  190. /// <summary>The full path to the volume.</summary>
  191. public string FullPath { get; private set; }
  192. #endregion // FullPath
  193. #region Guid
  194. private string _guid;
  195. /// <summary>The volume GUID.</summary>
  196. public string Guid
  197. {
  198. get
  199. {
  200. if (Utils.IsNullOrWhiteSpace(_guid))
  201. _guid = !Utils.IsNullOrWhiteSpace(FullPath) ? Volume.GetUniqueVolumeNameForPath(FullPath) : null;
  202. return _guid;
  203. }
  204. }
  205. #endregion // Guid
  206. #region MaximumComponentLength
  207. /// <summary>Gets the maximum length of a file name component that the file system supports.</summary>
  208. /// <value>The maximum length of a file name component that the file system supports.</value>
  209. public int MaximumComponentLength { get; set; }
  210. #endregion // MaximumComponentLength
  211. #region Name
  212. /// <summary>Gets the label of the volume.</summary>
  213. /// <returns>The label of the volume.</returns>
  214. /// <remarks>This property is the label assigned to the volume, such "MyDrive"</remarks>
  215. public string Name { get; private set; }
  216. #endregion // Name
  217. #region NamedStreams
  218. /// <summary>The specified volume supports named streams.</summary>
  219. public bool NamedStreams
  220. {
  221. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.NamedStreams) == NativeMethods.VolumeInfoAttributes.NamedStreams; }
  222. }
  223. #endregion // NamedStreams
  224. #region PersistentAcls
  225. /// <summary>The specified volume preserves and enforces access control lists (ACL).</summary>
  226. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Acls")]
  227. public bool PersistentAcls
  228. {
  229. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.PersistentAcls) == NativeMethods.VolumeInfoAttributes.PersistentAcls; }
  230. }
  231. #endregion // PersistentAcls
  232. #region ReadOnlyVolume
  233. /// <summary>The specified volume is read-only.</summary>
  234. public bool ReadOnlyVolume
  235. {
  236. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.ReadOnlyVolume) == NativeMethods.VolumeInfoAttributes.ReadOnlyVolume; }
  237. }
  238. #endregion // ReadOnlyVolume
  239. #region SequentialWriteOnce
  240. /// <summary>The specified volume supports a single sequential write.</summary>
  241. public bool SequentialWriteOnce
  242. {
  243. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SequentialWriteOnce) == NativeMethods.VolumeInfoAttributes.SequentialWriteOnce; }
  244. }
  245. #endregion // SequentialWriteOnce
  246. #region SerialNumber
  247. /// <summary>Gets the volume serial number that the operating system assigns when a hard disk is formatted.</summary>
  248. /// <value>The volume serial number that the operating system assigns when a hard disk is formatted.</value>
  249. public long SerialNumber { get; private set; }
  250. #endregion // SerialNumber
  251. #region SupportsEncryption
  252. /// <summary>The specified volume supports the Encrypted File System (EFS).</summary>
  253. public bool SupportsEncryption
  254. {
  255. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsEncryption) == NativeMethods.VolumeInfoAttributes.SupportsEncryption; }
  256. }
  257. #endregion // SupportsEncryption
  258. #region SupportsExtendedAttributes
  259. /// <summary>The specified volume supports extended attributes.</summary>
  260. public bool SupportsExtendedAttributes
  261. {
  262. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsExtendedAttributes) == NativeMethods.VolumeInfoAttributes.SupportsExtendedAttributes; }
  263. }
  264. #endregion // SupportsExtendedAttributes
  265. #region SupportsHardLinks
  266. /// <summary>The specified volume supports hard links.</summary>
  267. public bool SupportsHardLinks
  268. {
  269. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsHardLinks) == NativeMethods.VolumeInfoAttributes.SupportsHardLinks; }
  270. }
  271. #endregion // SupportsHardLinks
  272. #region SupportsObjectIds
  273. /// <summary>The specified volume supports object identifiers.</summary>
  274. public bool SupportsObjectIds
  275. {
  276. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsObjectIds) == NativeMethods.VolumeInfoAttributes.SupportsObjectIds; }
  277. }
  278. #endregion // SupportsObjectIds
  279. #region SupportsOpenByFileId
  280. /// <summary>The file system supports open by FileID.</summary>
  281. public bool SupportsOpenByFileId
  282. {
  283. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsOpenByFileId) == NativeMethods.VolumeInfoAttributes.SupportsOpenByFileId; }
  284. }
  285. #endregion // SupportsOpenByFileId
  286. #region SupportsRemoteStorage
  287. /// <summary>The specified volume supports remote storage. (This property does not appear on MSDN)</summary>
  288. public bool SupportsRemoteStorage
  289. {
  290. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsRemoteStorage) == NativeMethods.VolumeInfoAttributes.SupportsRemoteStorage; }
  291. }
  292. #endregion // SupportsRemoteStorage
  293. #region SupportsReparsePoints
  294. /// <summary>The specified volume supports re-parse points.</summary>
  295. public bool SupportsReparsePoints
  296. {
  297. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsReparsePoints) == NativeMethods.VolumeInfoAttributes.SupportsReparsePoints; }
  298. }
  299. #endregion // SupportsReparsePoints
  300. #region SupportsSparseFiles
  301. /// <summary>The specified volume supports sparse files.</summary>
  302. public bool SupportsSparseFiles
  303. {
  304. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsSparseFiles) == NativeMethods.VolumeInfoAttributes.SupportsSparseFiles; }
  305. }
  306. #endregion // SupportsSparseFiles
  307. #region SupportsTransactions
  308. /// <summary>The specified volume supports transactions.</summary>
  309. public bool SupportsTransactions
  310. {
  311. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsTransactions) == NativeMethods.VolumeInfoAttributes.SupportsTransactions; }
  312. }
  313. #endregion // SupportsTransactions
  314. #region SupportsUsnJournal
  315. /// <summary>The specified volume supports update sequence number (USN) journals.</summary>
  316. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Usn")]
  317. public bool SupportsUsnJournal
  318. {
  319. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsUsnJournal) == NativeMethods.VolumeInfoAttributes.SupportsUsnJournal; }
  320. }
  321. #endregion // SupportsUsnJournal
  322. #region UnicodeOnDisk
  323. /// <summary>The specified volume supports Unicode in file names as they appear on disk.</summary>
  324. public bool UnicodeOnDisk
  325. {
  326. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.UnicodeOnDisk) == NativeMethods.VolumeInfoAttributes.UnicodeOnDisk; }
  327. }
  328. #endregion // UnicodeOnDisk
  329. #region VolumeIsCompressed
  330. /// <summary>The specified volume is a compressed volume, for example, a DoubleSpace volume.</summary>
  331. public bool VolumeIsCompressed
  332. {
  333. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.VolumeIsCompressed) == NativeMethods.VolumeInfoAttributes.VolumeIsCompressed; }
  334. }
  335. #endregion // VolumeIsCompressed
  336. #region VolumeQuotas
  337. /// <summary>The specified volume supports disk quotas.</summary>
  338. public bool VolumeQuotas
  339. {
  340. get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.VolumeQuotas) == NativeMethods.VolumeInfoAttributes.VolumeQuotas; }
  341. }
  342. #endregion // VolumeQuotas
  343. #endregion // Properties
  344. }
  345. }