/* 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
{
/// Provides access to information on a local or remote drive.
///
/// 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.
///
[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
/// Provides access to information on the specified drive.
///
///
///
/// 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.")]
[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
/// Indicates the amount of available free space on a drive.
/// The amount of free space available on the drive, in bytes.
/// This property indicates the amount of free space available on the drive. Note that this number may be different from the number because this property takes into account disk quotas.
public long AvailableFreeSpace
{
get
{
GetDeviceInfo(3, 0);
return _dsi == null ? 0 : _dsi.FreeBytesAvailable;
}
}
/// Gets the name of the file system, such as NTFS or FAT32.
/// Use DriveFormat to determine what formatting a drive uses.
public string DriveFormat
{
get { return (string)GetDeviceInfo(0, 1); }
}
/// Gets the drive type.
/// One of the values.
///
/// The DriveType property indicates whether a drive is any of: CDRom, Fixed, Unknown, Network, NoRootDirectory,
/// Ram, Removable, or Unknown. Values are listed in the enumeration.
///
public DriveType DriveType
{
get { return (DriveType)GetDeviceInfo(2, 0); }
}
/// Gets a value indicating whether a drive is ready.
/// if the drive is ready; otherwise, .
///
/// 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.
///
public bool IsReady
{
get { return File.ExistsCore(true, null, Name, PathFormat.LongFullPath); }
}
/// Gets the name of the drive.
/// The name of the drive.
/// This property is the name assigned to the drive, such as C:\ or E:\
public string Name
{
get { return _name; }
}
/// Gets the root directory of a drive.
/// A DirectoryInfo object that contains the root directory of the drive.
public DirectoryInfo RootDirectory
{
get { return (DirectoryInfo)GetDeviceInfo(2, 1); }
}
/// Gets the total amount of free space available on a drive.
/// The total free space available on a drive, in bytes.
/// This property indicates the total amount of free space available on the drive, not just what is available to the current user.
public long TotalFreeSpace
{
get
{
GetDeviceInfo(3, 0);
return _dsi == null ? 0 : _dsi.TotalNumberOfFreeBytes;
}
}
/// Gets the total size of storage space on a drive.
/// The total size of the drive, in bytes.
/// This property indicates the total size of the drive in bytes, not just what is available to the current user.
public long TotalSize
{
get
{
GetDeviceInfo(3, 0);
return _dsi == null ? 0 : _dsi.TotalNumberOfBytes;
}
}
/// Gets or sets the volume label of a drive.
/// The volume label.
///
/// 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 is a valid VolumeLabel.
///
public string VolumeLabel
{
get { return (string)GetDeviceInfo(0, 2); }
set { Volume.SetVolumeLabel(Name, value); }
}
/// [AlphaFS] Returns the instance.
public DiskSpaceInfo DiskSpaceInfo
{
get
{
GetDeviceInfo(3, 0);
return _dsi;
}
}
/// [AlphaFS] The MS-DOS device name.
public string DosDeviceName
{
get { return (string)GetDeviceInfo(1, 0); }
}
/// [AlphaFS] Indicates if this drive is a SUBST.EXE / DefineDosDevice drive mapping.
public bool IsDosDeviceSubstitute
{
get { return !Utils.IsNullOrWhiteSpace(DosDeviceName) && DosDeviceName.StartsWith(Path.SubstitutePrefix, StringComparison.OrdinalIgnoreCase); }
}
/// [AlphaFS] Indicates if this drive is a UNC path.
/// Only retrieve this information if we're dealing with a real network share mapping: http://alphafs.codeplex.com/discussions/316583
public bool IsUnc
{
get { return !IsDosDeviceSubstitute && DriveType == DriveType.Network; }
}
/// [AlphaFS] Determines whether the specified volume name is a defined volume on the current computer.
public bool IsVolume
{
get { return GetDeviceInfo(0, 0) != null; }
}
/// [AlphaFS] Contains information about a file-system volume.
/// A VolumeInfo object that contains file-system volume information of the drive.
public VolumeInfo VolumeInfo
{
get { return (VolumeInfo)GetDeviceInfo(0, 0); }
}
#endregion // Properties
#region Methods
/// Retrieves the drive names of all logical drives on a computer.
/// An array of type that represents the logical drives on a computer.
[SecurityCritical]
public static DriveInfo[] GetDrives()
{
return Directory.EnumerateLogicalDrivesCore(false, false).ToArray();
}
/// Returns a drive name as a string.
/// The name of the drive.
/// This method returns the Name property.
public override string ToString()
{
return _name;
}
/// [AlphaFS] Enumerates the drive names of all logical drives on a computer.
/// Retrieve logical drives as known by the Environment.
/// Retrieve only when accessible (IsReady) logical drives.
///
/// An IEnumerable of type that represents
/// the logical drives on a computer.
///
[SecurityCritical]
public static IEnumerable EnumerateDrives(bool fromEnvironment, bool isReady)
{
return Directory.EnumerateLogicalDrivesCore(fromEnvironment, isReady);
}
/// [AlphaFS] Gets the first available drive letter on the local system.
/// A drive letter as . When no drive letters are available, an exception is thrown.
/// The letters "A" and "B" are reserved for floppy drives and will never be returned by this function.
public static char GetFreeDriveLetter()
{
return GetFreeDriveLetter(false);
}
/// Gets an available drive letter on the local system.
/// When get the last available drive letter. When gets the first available drive letter.
/// A drive letter as . When no drive letters are available, an exception is thrown.
/// The letters "A" and "B" are reserved for floppy drives and will never be returned by this function.
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
[SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
public static char GetFreeDriveLetter(bool getLastAvailable)
{
IEnumerable 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
/// Retrieves information about the file system and volume associated with the specified root file or directorystream.
[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
}
}