/* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using Microsoft.Win32.SafeHandles; using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Security; using System.Text; namespace Alphaleonis.Win32.Filesystem { /// Contains information about a filesystem Volume. [SerializableAttribute] [SecurityCritical] public sealed class VolumeInfo { #region Constructor /// Initializes a VolumeInfo instance. /// /// /// 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. [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Utils.IsNullOrWhiteSpace validates arguments.")] public VolumeInfo(string volumeName) { if (Utils.IsNullOrWhiteSpace(volumeName)) throw new ArgumentNullException("volumeName"); if (!volumeName.StartsWith(Path.LongPathPrefix, StringComparison.OrdinalIgnoreCase)) volumeName = Path.IsUncPathCore(volumeName, false, false) ? Path.GetLongPathCore(volumeName, GetFullPathOptions.None) : Path.LongPathPrefix + volumeName; else { if (volumeName.Length == 1) volumeName += Path.VolumeSeparatorChar; else if (!volumeName.StartsWith(Path.GlobalRootPrefix, StringComparison.OrdinalIgnoreCase)) volumeName = Path.GetPathRoot(volumeName, false); } if (Utils.IsNullOrWhiteSpace(volumeName)) throw new ArgumentException("Argument must be a drive letter (\"C\"), RootDir (\"C:\\\") or UNC path (\"\\\\server\\share\")"); Name = Path.AddTrailingDirectorySeparator(volumeName, false); _volumeHandle = null; } /// Initializes a VolumeInfo instance. /// 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". /// Refreshes the state of the object. /// suppress any Exception that might be thrown as a result from a failure, such as unavailable resources. [SecurityCritical] public VolumeInfo(string driveName, bool refresh, bool continueOnException) : this(driveName) { _continueOnAccessError = continueOnException; if (refresh) Refresh(); } /// Initializes a VolumeInfo instance. /// An instance to a handle. [SecurityCritical] public VolumeInfo(SafeFileHandle volumeHandle) { _volumeHandle = volumeHandle; } /// Initializes a VolumeInfo instance. /// An instance to a handle. /// Refreshes the state of the object. /// suppress any Exception that might be thrown as a result from a failure, such as unavailable resources. [SecurityCritical] public VolumeInfo(SafeFileHandle volumeHandle, bool refresh, bool continueOnException) : this(volumeHandle) { _continueOnAccessError = continueOnException; if (refresh) Refresh(); } #endregion // Constructor #region Fields [NonSerialized] private readonly bool _continueOnAccessError; [NonSerialized] private readonly SafeFileHandle _volumeHandle; [NonSerialized] private NativeMethods.VolumeInfoAttributes _volumeInfoAttributes; #endregion // Fields #region Methods #region Refresh /// Refreshes the state of the object. public void Refresh() { var volumeNameBuffer = new StringBuilder(NativeMethods.MaxPath + 1); var fileSystemNameBuffer = new StringBuilder(NativeMethods.MaxPath + 1); int maximumComponentLength; uint serialNumber; using (new NativeMethods.ChangeErrorMode(NativeMethods.ErrorMode.FailCriticalErrors)) { // GetVolumeInformationXxx() // In the ANSI version of this function, the name is limited to 248 characters. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. // 2013-07-18: MSDN does not confirm LongPath usage but a Unicode version of this function exists. uint lastError; do { if (!(_volumeHandle != null && NativeMethods.IsAtLeastWindowsVista // GetVolumeInformationByHandle() / GetVolumeInformation() // In the ANSI version of this function, the name is limited to 248 characters. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. // 2013-07-18: MSDN does not confirm LongPath usage but a Unicode version of this function exists. ? NativeMethods.GetVolumeInformationByHandle(_volumeHandle, volumeNameBuffer, (uint) volumeNameBuffer.Capacity, out serialNumber, out maximumComponentLength, out _volumeInfoAttributes, fileSystemNameBuffer, (uint) fileSystemNameBuffer.Capacity) : NativeMethods.GetVolumeInformation(Path.AddTrailingDirectorySeparator(Name, false), volumeNameBuffer, (uint) volumeNameBuffer.Capacity, out serialNumber, out maximumComponentLength, out _volumeInfoAttributes, fileSystemNameBuffer, (uint) fileSystemNameBuffer.Capacity)) // A trailing backslash is required. ) { lastError = (uint) Marshal.GetLastWin32Error(); switch (lastError) { case Win32Errors.ERROR_NOT_READY: if (!_continueOnAccessError) throw new DeviceNotReadyException(); break; case Win32Errors.ERROR_MORE_DATA: // With a large enough buffer this code never executes. volumeNameBuffer.Capacity = volumeNameBuffer.Capacity*2; fileSystemNameBuffer.Capacity = fileSystemNameBuffer.Capacity*2; break; default: if (!_continueOnAccessError) NativeError.ThrowException(Name); break; } } else break; } while (lastError == Win32Errors.ERROR_MORE_DATA); } FullPath = Path.GetRegularPathCore(Name, GetFullPathOptions.None, false); Name = volumeNameBuffer.ToString(); FileSystemName = fileSystemNameBuffer.ToString(); FileSystemName = Utils.IsNullOrWhiteSpace(FileSystemName) ? null : FileSystemName; MaximumComponentLength = maximumComponentLength; SerialNumber = serialNumber; } #endregion // Refresh #region ToString /// Returns the full path of the volume. /// A string that represents this instance. public override string ToString() { return Guid; } #endregion // ToString #endregion // Methods #region Properties #region CasePreservedNames /// The specified volume supports preserved case of file names when it places a name on disk. public bool CasePreservedNames { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.CasePreservedNames) == NativeMethods.VolumeInfoAttributes.CasePreservedNames; } } #endregion // CasePreservedNames #region CaseSensitiveSearch /// The specified volume supports case-sensitive file names. public bool CaseSensitiveSearch { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.CaseSensitiveSearch) == NativeMethods.VolumeInfoAttributes.CaseSensitiveSearch; } } #endregion // CaseSensitiveSearch #region Compression /// The specified volume supports file-based compression. public bool Compression { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.Compression) == NativeMethods.VolumeInfoAttributes.Compression; } } #endregion // Compression #region FileSystemName /// Gets the name of the file system, for example, the FAT file system or the NTFS file system. /// The name of the file system. public string FileSystemName { get; private set; } #endregion // FileSystemName #region FullPath /// The full path to the volume. public string FullPath { get; private set; } #endregion // FullPath #region Guid private string _guid; /// The volume GUID. public string Guid { get { if (Utils.IsNullOrWhiteSpace(_guid)) _guid = !Utils.IsNullOrWhiteSpace(FullPath) ? Volume.GetUniqueVolumeNameForPath(FullPath) : null; return _guid; } } #endregion // Guid #region MaximumComponentLength /// Gets the maximum length of a file name component that the file system supports. /// The maximum length of a file name component that the file system supports. public int MaximumComponentLength { get; set; } #endregion // MaximumComponentLength #region Name /// Gets the label of the volume. /// The label of the volume. /// This property is the label assigned to the volume, such "MyDrive" public string Name { get; private set; } #endregion // Name #region NamedStreams /// The specified volume supports named streams. public bool NamedStreams { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.NamedStreams) == NativeMethods.VolumeInfoAttributes.NamedStreams; } } #endregion // NamedStreams #region PersistentAcls /// The specified volume preserves and enforces access control lists (ACL). [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Acls")] public bool PersistentAcls { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.PersistentAcls) == NativeMethods.VolumeInfoAttributes.PersistentAcls; } } #endregion // PersistentAcls #region ReadOnlyVolume /// The specified volume is read-only. public bool ReadOnlyVolume { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.ReadOnlyVolume) == NativeMethods.VolumeInfoAttributes.ReadOnlyVolume; } } #endregion // ReadOnlyVolume #region SequentialWriteOnce /// The specified volume supports a single sequential write. public bool SequentialWriteOnce { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SequentialWriteOnce) == NativeMethods.VolumeInfoAttributes.SequentialWriteOnce; } } #endregion // SequentialWriteOnce #region SerialNumber /// Gets the volume serial number that the operating system assigns when a hard disk is formatted. /// The volume serial number that the operating system assigns when a hard disk is formatted. public long SerialNumber { get; private set; } #endregion // SerialNumber #region SupportsEncryption /// The specified volume supports the Encrypted File System (EFS). public bool SupportsEncryption { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsEncryption) == NativeMethods.VolumeInfoAttributes.SupportsEncryption; } } #endregion // SupportsEncryption #region SupportsExtendedAttributes /// The specified volume supports extended attributes. public bool SupportsExtendedAttributes { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsExtendedAttributes) == NativeMethods.VolumeInfoAttributes.SupportsExtendedAttributes; } } #endregion // SupportsExtendedAttributes #region SupportsHardLinks /// The specified volume supports hard links. public bool SupportsHardLinks { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsHardLinks) == NativeMethods.VolumeInfoAttributes.SupportsHardLinks; } } #endregion // SupportsHardLinks #region SupportsObjectIds /// The specified volume supports object identifiers. public bool SupportsObjectIds { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsObjectIds) == NativeMethods.VolumeInfoAttributes.SupportsObjectIds; } } #endregion // SupportsObjectIds #region SupportsOpenByFileId /// The file system supports open by FileID. public bool SupportsOpenByFileId { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsOpenByFileId) == NativeMethods.VolumeInfoAttributes.SupportsOpenByFileId; } } #endregion // SupportsOpenByFileId #region SupportsRemoteStorage /// The specified volume supports remote storage. (This property does not appear on MSDN) public bool SupportsRemoteStorage { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsRemoteStorage) == NativeMethods.VolumeInfoAttributes.SupportsRemoteStorage; } } #endregion // SupportsRemoteStorage #region SupportsReparsePoints /// The specified volume supports re-parse points. public bool SupportsReparsePoints { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsReparsePoints) == NativeMethods.VolumeInfoAttributes.SupportsReparsePoints; } } #endregion // SupportsReparsePoints #region SupportsSparseFiles /// The specified volume supports sparse files. public bool SupportsSparseFiles { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsSparseFiles) == NativeMethods.VolumeInfoAttributes.SupportsSparseFiles; } } #endregion // SupportsSparseFiles #region SupportsTransactions /// The specified volume supports transactions. public bool SupportsTransactions { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsTransactions) == NativeMethods.VolumeInfoAttributes.SupportsTransactions; } } #endregion // SupportsTransactions #region SupportsUsnJournal /// The specified volume supports update sequence number (USN) journals. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Usn")] public bool SupportsUsnJournal { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.SupportsUsnJournal) == NativeMethods.VolumeInfoAttributes.SupportsUsnJournal; } } #endregion // SupportsUsnJournal #region UnicodeOnDisk /// The specified volume supports Unicode in file names as they appear on disk. public bool UnicodeOnDisk { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.UnicodeOnDisk) == NativeMethods.VolumeInfoAttributes.UnicodeOnDisk; } } #endregion // UnicodeOnDisk #region VolumeIsCompressed /// The specified volume is a compressed volume, for example, a DoubleSpace volume. public bool VolumeIsCompressed { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.VolumeIsCompressed) == NativeMethods.VolumeInfoAttributes.VolumeIsCompressed; } } #endregion // VolumeIsCompressed #region VolumeQuotas /// The specified volume supports disk quotas. public bool VolumeQuotas { get { return (_volumeInfoAttributes & NativeMethods.VolumeInfoAttributes.VolumeQuotas) == NativeMethods.VolumeInfoAttributes.VolumeQuotas; } } #endregion // VolumeQuotas #endregion // Properties } }