mirror of https://github.com/raandree/NTFSSecurity
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.
422 lines
16 KiB
422 lines
16 KiB
/* 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 System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Security;
|
|
|
|
namespace Alphaleonis.Win32.Filesystem
|
|
{
|
|
/// <summary>Provides access to information on a local or remote drive.</summary>
|
|
/// <remarks>
|
|
/// This class models a drive and provides methods and properties to query for drive information.
|
|
/// Use DriveInfo to determine what drives are available, and what type of drives they are.
|
|
/// You can also query to determine the capacity and available free space on the drive.
|
|
/// </remarks>
|
|
[Serializable]
|
|
[SecurityCritical]
|
|
public sealed class DriveInfo
|
|
{
|
|
#region Private Fields
|
|
|
|
[NonSerialized]
|
|
private readonly VolumeInfo _volumeInfo;
|
|
|
|
[NonSerialized]
|
|
private readonly DiskSpaceInfo _dsi;
|
|
|
|
[NonSerialized]
|
|
private bool _initDsie;
|
|
|
|
[NonSerialized]
|
|
private DriveType? _driveType;
|
|
|
|
[NonSerialized]
|
|
private string _dosDeviceName;
|
|
|
|
[NonSerialized]
|
|
private DirectoryInfo _rootDirectory;
|
|
|
|
private readonly string _name;
|
|
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>Provides access to information on the specified drive.</summary>
|
|
/// <exception cref="ArgumentNullException"/>
|
|
/// <exception cref="ArgumentException"/>
|
|
/// <param name="driveName">
|
|
/// A valid drive path or drive letter.
|
|
/// <para>This can be either uppercase or lowercase,</para>
|
|
/// <para>'a' to 'z' or a network share in the format: \\server\share</para>
|
|
/// </param>
|
|
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Utils.IsNullOrWhiteSpace validates arguments.")]
|
|
[SecurityCritical]
|
|
public DriveInfo(string driveName)
|
|
{
|
|
if (Utils.IsNullOrWhiteSpace(driveName))
|
|
throw new ArgumentNullException("driveName");
|
|
|
|
if (driveName.Length == 1)
|
|
_name += Path.VolumeSeparatorChar;
|
|
else
|
|
_name = Path.GetPathRoot(driveName, false);
|
|
|
|
if (Utils.IsNullOrWhiteSpace(_name))
|
|
throw new ArgumentException("Argument must be a drive letter (\"C\"), RootDir (\"C:\\\") or UNC path (\"\\\\server\\share\")");
|
|
|
|
// If an exception is thrown, the original drivePath is used.
|
|
_name = Path.AddTrailingDirectorySeparator(_name, false);
|
|
|
|
|
|
// Initiate VolumeInfo() lazyload instance.
|
|
_volumeInfo = new VolumeInfo(_name, false, true);
|
|
|
|
// Initiate DiskSpaceInfo() lazyload instance.
|
|
_dsi = new DiskSpaceInfo(_name, null, false, true);
|
|
}
|
|
|
|
#endregion // Constructors
|
|
|
|
#region Properties
|
|
|
|
/// <summary>Indicates the amount of available free space on a drive.</summary>
|
|
/// <returns>The amount of free space available on the drive, in bytes.</returns>
|
|
/// <remarks>This property indicates the amount of free space available on the drive. Note that this number may be different from the <see cref="TotalFreeSpace"/> number because this property takes into account disk quotas.</remarks>
|
|
public long AvailableFreeSpace
|
|
{
|
|
get
|
|
{
|
|
GetDeviceInfo(3, 0);
|
|
return _dsi == null ? 0 : _dsi.FreeBytesAvailable;
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets the name of the file system, such as NTFS or FAT32.</summary>
|
|
/// <remarks>Use DriveFormat to determine what formatting a drive uses.</remarks>
|
|
public string DriveFormat
|
|
{
|
|
get { return (string)GetDeviceInfo(0, 1); }
|
|
}
|
|
|
|
/// <summary>Gets the drive type.</summary>
|
|
/// <returns>One of the <see cref="System.IO.DriveType"/> values.</returns>
|
|
/// <remarks>
|
|
/// The DriveType property indicates whether a drive is any of: CDRom, Fixed, Unknown, Network, NoRootDirectory,
|
|
/// Ram, Removable, or Unknown. Values are listed in the <see cref="System.IO.DriveType"/> enumeration.
|
|
/// </remarks>
|
|
public DriveType DriveType
|
|
{
|
|
get { return (DriveType)GetDeviceInfo(2, 0); }
|
|
}
|
|
|
|
/// <summary>Gets a value indicating whether a drive is ready.</summary>
|
|
/// <returns><see langword="true"/> if the drive is ready; otherwise, <see langword="false"/>.</returns>
|
|
/// <remarks>
|
|
/// IsReady indicates whether a drive is ready. For example, it indicates whether a CD is in a CD drive or whether
|
|
/// a removable storage device is ready for read/write operations. If you do not test whether a drive is ready, and
|
|
/// it is not ready, querying the drive using DriveInfo will raise an IOException.
|
|
///
|
|
/// Do not rely on IsReady() to avoid catching exceptions from other members such as TotalSize, TotalFreeSpace, and DriveFormat.
|
|
/// Between the time that your code checks IsReady and then accesses one of the other properties
|
|
/// (even if the access occurs immediately after the check), a drive may have been disconnected or a disk may have been removed.
|
|
/// </remarks>
|
|
public bool IsReady
|
|
{
|
|
get { return File.ExistsCore(true, null, Name, PathFormat.LongFullPath); }
|
|
}
|
|
|
|
|
|
/// <summary>Gets the name of the drive.</summary>
|
|
/// <returns>The name of the drive.</returns>
|
|
/// <remarks>This property is the name assigned to the drive, such as C:\ or E:\</remarks>
|
|
public string Name
|
|
{
|
|
get { return _name; }
|
|
}
|
|
|
|
/// <summary>Gets the root directory of a drive.</summary>
|
|
/// <returns>A DirectoryInfo object that contains the root directory of the drive.</returns>
|
|
public DirectoryInfo RootDirectory
|
|
{
|
|
get { return (DirectoryInfo)GetDeviceInfo(2, 1); }
|
|
}
|
|
|
|
/// <summary>Gets the total amount of free space available on a drive.</summary>
|
|
/// <returns>The total free space available on a drive, in bytes.</returns>
|
|
/// <remarks>This property indicates the total amount of free space available on the drive, not just what is available to the current user.</remarks>
|
|
public long TotalFreeSpace
|
|
{
|
|
get
|
|
{
|
|
GetDeviceInfo(3, 0);
|
|
return _dsi == null ? 0 : _dsi.TotalNumberOfFreeBytes;
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets the total size of storage space on a drive.</summary>
|
|
/// <returns>The total size of the drive, in bytes.</returns>
|
|
/// <remarks>This property indicates the total size of the drive in bytes, not just what is available to the current user.</remarks>
|
|
public long TotalSize
|
|
{
|
|
get
|
|
{
|
|
GetDeviceInfo(3, 0);
|
|
return _dsi == null ? 0 : _dsi.TotalNumberOfBytes;
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets or sets the volume label of a drive.</summary>
|
|
/// <returns>The volume label.</returns>
|
|
/// <remarks>
|
|
/// The label length is determined by the operating system. For example, NTFS allows a volume label
|
|
/// to be up to 32 characters long. Note that <see langword="null"/> is a valid VolumeLabel.
|
|
/// </remarks>
|
|
|
|
public string VolumeLabel
|
|
{
|
|
get { return (string)GetDeviceInfo(0, 2); }
|
|
set { Volume.SetVolumeLabel(Name, value); }
|
|
}
|
|
|
|
/// <summary>[AlphaFS] Returns the <see ref="Alphaleonis.Win32.Filesystem.DiskSpaceInfo"/> instance.</summary>
|
|
public DiskSpaceInfo DiskSpaceInfo
|
|
{
|
|
get
|
|
{
|
|
GetDeviceInfo(3, 0);
|
|
return _dsi;
|
|
}
|
|
}
|
|
|
|
/// <summary>[AlphaFS] The MS-DOS device name.</summary>
|
|
public string DosDeviceName
|
|
{
|
|
get { return (string)GetDeviceInfo(1, 0); }
|
|
}
|
|
|
|
/// <summary>[AlphaFS] Indicates if this drive is a SUBST.EXE / DefineDosDevice drive mapping.</summary>
|
|
public bool IsDosDeviceSubstitute
|
|
{
|
|
get { return !Utils.IsNullOrWhiteSpace(DosDeviceName) && DosDeviceName.StartsWith(Path.SubstitutePrefix, StringComparison.OrdinalIgnoreCase); }
|
|
}
|
|
|
|
/// <summary>[AlphaFS] Indicates if this drive is a UNC path.</summary>
|
|
/// <remarks>Only retrieve this information if we're dealing with a real network share mapping: http://alphafs.codeplex.com/discussions/316583</remarks>
|
|
public bool IsUnc
|
|
{
|
|
get { return !IsDosDeviceSubstitute && DriveType == DriveType.Network; }
|
|
}
|
|
|
|
/// <summary>[AlphaFS] Determines whether the specified volume name is a defined volume on the current computer.</summary>
|
|
public bool IsVolume
|
|
{
|
|
get { return GetDeviceInfo(0, 0) != null; }
|
|
}
|
|
|
|
/// <summary>[AlphaFS] Contains information about a file-system volume.</summary>
|
|
/// <returns>A VolumeInfo object that contains file-system volume information of the drive.</returns>
|
|
public VolumeInfo VolumeInfo
|
|
{
|
|
get { return (VolumeInfo)GetDeviceInfo(0, 0); }
|
|
}
|
|
|
|
|
|
#endregion // Properties
|
|
|
|
#region Methods
|
|
|
|
/// <summary>Retrieves the drive names of all logical drives on a computer.</summary>
|
|
/// <returns>An array of type <see cref="Alphaleonis.Win32.Filesystem.DriveInfo"/> that represents the logical drives on a computer.</returns>
|
|
|
|
[SecurityCritical]
|
|
public static DriveInfo[] GetDrives()
|
|
{
|
|
return Directory.EnumerateLogicalDrivesCore(false, false).ToArray();
|
|
}
|
|
|
|
/// <summary>Returns a drive name as a string.</summary>
|
|
/// <returns>The name of the drive.</returns>
|
|
/// <remarks>This method returns the Name property.</remarks>
|
|
public override string ToString()
|
|
{
|
|
return _name;
|
|
}
|
|
|
|
|
|
/// <summary>[AlphaFS] Enumerates the drive names of all logical drives on a computer.</summary>
|
|
/// <param name="fromEnvironment">Retrieve logical drives as known by the Environment.</param>
|
|
/// <param name="isReady">Retrieve only when accessible (IsReady) logical drives.</param>
|
|
/// <returns>
|
|
/// An IEnumerable of type <see cref="Alphaleonis.Win32.Filesystem.DriveInfo"/> that represents
|
|
/// the logical drives on a computer.
|
|
/// </returns>
|
|
[SecurityCritical]
|
|
public static IEnumerable<DriveInfo> EnumerateDrives(bool fromEnvironment, bool isReady)
|
|
{
|
|
return Directory.EnumerateLogicalDrivesCore(fromEnvironment, isReady);
|
|
}
|
|
|
|
|
|
/// <summary>[AlphaFS] Gets the first available drive letter on the local system.</summary>
|
|
/// <returns>A drive letter as <see cref="char"/>. When no drive letters are available, an exception is thrown.</returns>
|
|
/// <remarks>The letters "A" and "B" are reserved for floppy drives and will never be returned by this function.</remarks>
|
|
|
|
public static char GetFreeDriveLetter()
|
|
{
|
|
return GetFreeDriveLetter(false);
|
|
}
|
|
|
|
/// <summary>Gets an available drive letter on the local system.</summary>
|
|
/// <param name="getLastAvailable">When <see langword="true"/> get the last available drive letter. When <see langword="false"/> gets the first available drive letter.</param>
|
|
/// <returns>A drive letter as <see cref="char"/>. When no drive letters are available, an exception is thrown.</returns>
|
|
/// <remarks>The letters "A" and "B" are reserved for floppy drives and will never be returned by this function.</remarks>
|
|
|
|
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
|
|
[SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
|
|
public static char GetFreeDriveLetter(bool getLastAvailable)
|
|
{
|
|
IEnumerable<char> freeDriveLetters = "CDEFGHIJKLMNOPQRSTUVWXYZ".Except(Directory.EnumerateLogicalDrivesCore(false, false).Select(d => d.Name[0]));
|
|
|
|
try
|
|
{
|
|
return getLastAvailable ? freeDriveLetters.Last() : freeDriveLetters.First();
|
|
}
|
|
catch
|
|
{
|
|
throw new Exception("There are no drive letters available.");
|
|
}
|
|
}
|
|
|
|
#endregion // Methods
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>Retrieves information about the file system and volume associated with the specified root file or directorystream.</summary>
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
|
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
|
|
[SecurityCritical]
|
|
private object GetDeviceInfo(int type, int mode)
|
|
{
|
|
try
|
|
{
|
|
switch (type)
|
|
{
|
|
#region Volume
|
|
|
|
// VolumeInfo properties.
|
|
case 0:
|
|
if (Utils.IsNullOrWhiteSpace(_volumeInfo.FullPath))
|
|
_volumeInfo.Refresh();
|
|
|
|
switch (mode)
|
|
{
|
|
case 0:
|
|
// IsVolume, VolumeInfo
|
|
return _volumeInfo;
|
|
|
|
case 1:
|
|
// DriveFormat
|
|
return _volumeInfo == null ? DriveType.Unknown.ToString() : _volumeInfo.FileSystemName ?? DriveType.Unknown.ToString();
|
|
|
|
case 2:
|
|
// VolumeLabel
|
|
return _volumeInfo == null ? string.Empty : _volumeInfo.Name ?? string.Empty;
|
|
}
|
|
break;
|
|
|
|
// Volume related.
|
|
case 1:
|
|
switch (mode)
|
|
{
|
|
case 0:
|
|
// DosDeviceName
|
|
|
|
// Do not use ?? expression here.
|
|
if (_dosDeviceName == null)
|
|
_dosDeviceName = Volume.QueryDosDevice(Name).FirstOrDefault();
|
|
|
|
return _dosDeviceName;
|
|
}
|
|
break;
|
|
|
|
#endregion // Volume
|
|
|
|
#region Drive
|
|
|
|
// Drive related.
|
|
case 2:
|
|
switch (mode)
|
|
{
|
|
case 0:
|
|
// DriveType
|
|
// Do not use ?? expression here.
|
|
if (_driveType == null)
|
|
_driveType = Volume.GetDriveType(Name);
|
|
|
|
return _driveType;
|
|
|
|
case 1:
|
|
// RootDirectory
|
|
|
|
// Do not use ?? expression here.
|
|
if (_rootDirectory == null)
|
|
_rootDirectory = new DirectoryInfo(null, Name, PathFormat.RelativePath);
|
|
|
|
return _rootDirectory;
|
|
}
|
|
break;
|
|
|
|
// DiskSpaceInfo related.
|
|
case 3:
|
|
switch (mode)
|
|
{
|
|
case 0:
|
|
// AvailableFreeSpace, TotalFreeSpace, TotalSize, DiskSpaceInfo
|
|
if (!_initDsie)
|
|
{
|
|
_dsi.Refresh();
|
|
_initDsie = true;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
#endregion // Drive
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
return type == 0 && mode > 0 ? string.Empty : null;
|
|
}
|
|
|
|
|
|
#endregion // Private
|
|
|
|
}
|
|
}
|
|
|