1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 /*============================================================ 7 ** 8 ** Class: DriveInfo 9 ** 10 ** <OWNER>Microsoft</OWNER> 11 ** 12 ** 13 ** Purpose: Exposes routines for exploring a drive. 14 ** 15 ** 16 ===========================================================*/ 17 18 using System; 19 using System.Text; 20 using System.Runtime.InteropServices; 21 using Microsoft.Win32; 22 using System.Security.Permissions; 23 using System.Runtime.Serialization; 24 using System.Runtime.Versioning; 25 using System.Diagnostics.Contracts; 26 27 namespace System.IO 28 { 29 // Matches Win32's DRIVE_XXX #defines from winbase.h 30 [Serializable] 31 [System.Runtime.InteropServices.ComVisible(true)] 32 public enum DriveType 33 { 34 Unknown = 0, 35 NoRootDirectory = 1, 36 Removable = 2, 37 Fixed = 3, 38 Network = 4, 39 CDRom = 5, 40 Ram = 6 41 } 42 43 // Ideally we'll get a better security permission, but possibly 44 // not for Whidbey. 45 [Serializable] 46 [ComVisible(true)] 47 public sealed class DriveInfo : ISerializable 48 { 49 private String _name; 50 51 private const String NameField = "_name"; // For serialization 52 53 [System.Security.SecuritySafeCritical] // auto-generated 54 [ResourceExposure(ResourceScope.Machine)] 55 [ResourceConsumption(ResourceScope.Machine)] DriveInfo(String driveName)56 public DriveInfo(String driveName) 57 { 58 if (driveName == null) 59 throw new ArgumentNullException("driveName"); 60 Contract.EndContractBlock(); 61 if (driveName.Length == 1) 62 _name = driveName + ":\\"; 63 else { 64 // GetPathRoot does not check all invalid characters 65 Path.CheckInvalidPathChars(driveName); 66 _name = Path.GetPathRoot(driveName); 67 // Disallow null or empty drive letters and UNC paths 68 if (_name == null || _name.Length == 0 || _name.StartsWith("\\\\", StringComparison.Ordinal)) 69 throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDriveLetterOrRootDir")); 70 } 71 // We want to normalize to have a trailing backslash so we don't have two equivalent forms and 72 // because some Win32 API don't work without it. 73 if (_name.Length == 2 && _name[1] == ':') { 74 _name = _name + "\\"; 75 } 76 77 // Now verify that the drive letter could be a real drive name. 78 // On Windows this means it's between A and Z, ignoring case. 79 // On a Unix platform, perhaps this should be a device name with 80 // a partition like /dev/hdc0, or possibly a mount point. 81 char letter = driveName[0]; 82 if (!((letter >= 'A' && letter <= 'Z') || (letter >= 'a' && letter <= 'z'))) 83 throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDriveLetterOrRootDir")); 84 85 // Now do a security check. 86 String demandPath = _name + '.'; 87 new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPath).Demand(); 88 } 89 90 [System.Security.SecurityCritical] // auto-generated DriveInfo(SerializationInfo info, StreamingContext context)91 private DriveInfo(SerializationInfo info, StreamingContext context) 92 { 93 // Need to add in a security check here once it has been spec'ed. 94 _name = (String) info.GetValue(NameField, typeof(String)); 95 96 // Now do a security check. 97 String demandPath = _name + '.'; 98 new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPath).Demand(); 99 } 100 101 public String Name { 102 get { return _name; } 103 } 104 105 public DriveType DriveType { 106 [System.Security.SecuritySafeCritical] // auto-generated 107 get { 108 // GetDriveType can't fail 109 return (DriveType) Win32Native.GetDriveType(Name); 110 } 111 } 112 113 public String DriveFormat { 114 [System.Security.SecuritySafeCritical] // auto-generated 115 [ResourceExposure(ResourceScope.None)] 116 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 117 get { 118 const int volNameLen = 50; 119 StringBuilder volumeName = new StringBuilder(volNameLen); 120 const int fileSystemNameLen = 50; 121 StringBuilder fileSystemName = new StringBuilder(fileSystemNameLen); 122 int serialNumber, maxFileNameLen, fileSystemFlags; 123 124 int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); 125 try { 126 bool r = Win32Native.GetVolumeInformation(Name, volumeName, volNameLen, out serialNumber, out maxFileNameLen, out fileSystemFlags, fileSystemName, fileSystemNameLen); 127 if (!r) { 128 int errorCode = Marshal.GetLastWin32Error(); 129 __Error.WinIODriveError(Name, errorCode); 130 } 131 } 132 finally { 133 Win32Native.SetErrorMode(oldMode); 134 } 135 return fileSystemName.ToString(); 136 } 137 } 138 139 public bool IsReady { 140 [System.Security.SecuritySafeCritical] // auto-generated 141 [ResourceExposure(ResourceScope.None)] 142 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] 143 get { 144 return Directory.InternalExists(Name); 145 } 146 } 147 148 public long AvailableFreeSpace { 149 [System.Security.SecuritySafeCritical] // auto-generated 150 [ResourceExposure(ResourceScope.None)] 151 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 152 get { 153 long userBytes, totalBytes, freeBytes; 154 int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); 155 try { 156 bool r = Win32Native.GetDiskFreeSpaceEx(Name, out userBytes, out totalBytes, out freeBytes); 157 if (!r) 158 __Error.WinIODriveError(Name); 159 } 160 finally { 161 Win32Native.SetErrorMode(oldMode); 162 } 163 return userBytes; 164 } 165 } 166 167 public long TotalFreeSpace { 168 [System.Security.SecuritySafeCritical] // auto-generated 169 [ResourceExposure(ResourceScope.None)] 170 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 171 get { 172 long userBytes, totalBytes, freeBytes; 173 int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); 174 try { 175 bool r = Win32Native.GetDiskFreeSpaceEx(Name, out userBytes, out totalBytes, out freeBytes); 176 if (!r) 177 __Error.WinIODriveError(Name); 178 } 179 finally { 180 Win32Native.SetErrorMode(oldMode); 181 } 182 return freeBytes; 183 } 184 } 185 186 public long TotalSize { 187 [System.Security.SecuritySafeCritical] // auto-generated 188 [ResourceExposure(ResourceScope.None)] 189 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 190 get { 191 // Don't cache this, to handle variable sized floppy drives 192 // or other various removable media drives. 193 long userBytes, totalBytes, freeBytes; 194 int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); 195 try { 196 bool r = Win32Native.GetDiskFreeSpaceEx(Name, out userBytes, out totalBytes, out freeBytes); 197 if (!r) 198 __Error.WinIODriveError(Name); 199 } 200 finally { 201 Win32Native.SetErrorMode(oldMode); 202 } 203 return totalBytes; 204 } 205 } 206 207 [ResourceExposure(ResourceScope.Machine)] 208 [ResourceConsumption(ResourceScope.Machine)] GetDrives()209 public static DriveInfo[] GetDrives() 210 { 211 // Directory.GetLogicalDrives demands unmanaged code permission 212 String[] drives = Directory.GetLogicalDrives(); 213 DriveInfo[] di = new DriveInfo[drives.Length]; 214 for(int i=0; i<drives.Length; i++) 215 di[i] = new DriveInfo(drives[i]); 216 return di; 217 } 218 219 public DirectoryInfo RootDirectory { 220 [ResourceExposure(ResourceScope.Machine)] 221 [ResourceConsumption(ResourceScope.Machine)] 222 get { 223 return new DirectoryInfo(Name); 224 } 225 } 226 227 // Null is a valid volume label. 228 public String VolumeLabel { 229 [System.Security.SecuritySafeCritical] // auto-generated 230 [ResourceExposure(ResourceScope.None)] 231 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 232 get { 233 // NTFS uses a limit of 32 characters for the volume label, 234 // as of Windows Server 2003. 235 const int volNameLen = 50; 236 StringBuilder volumeName = new StringBuilder(volNameLen); 237 const int fileSystemNameLen = 50; 238 StringBuilder fileSystemName = new StringBuilder(fileSystemNameLen); 239 int serialNumber, maxFileNameLen, fileSystemFlags; 240 241 int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); 242 try { 243 bool r = Win32Native.GetVolumeInformation(Name, volumeName, volNameLen, out serialNumber, out maxFileNameLen, out fileSystemFlags, fileSystemName, fileSystemNameLen); 244 if (!r) { 245 int errorCode = Marshal.GetLastWin32Error(); 246 // Win9x appears to return ERROR_INVALID_DATA when a 247 // drive doesn't exist. 248 if (errorCode == Win32Native.ERROR_INVALID_DATA) 249 errorCode = Win32Native.ERROR_INVALID_DRIVE; 250 __Error.WinIODriveError(Name, errorCode); 251 } 252 } 253 finally { 254 Win32Native.SetErrorMode(oldMode); 255 } 256 return volumeName.ToString(); 257 } 258 [System.Security.SecuritySafeCritical] // auto-generated 259 [ResourceExposure(ResourceScope.None)] 260 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] 261 set { 262 String demandPath = _name + '.'; 263 new FileIOPermission(FileIOPermissionAccess.Write, demandPath).Demand(); 264 265 int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); 266 try { 267 bool r = Win32Native.SetVolumeLabel(Name, value); 268 if (!r) { 269 int errorCode = Marshal.GetLastWin32Error(); 270 // Provide better message 271 if (errorCode == Win32Native.ERROR_ACCESS_DENIED) 272 throw new UnauthorizedAccessException(Environment.GetResourceString("InvalidOperation_SetVolumeLabelFailed")); 273 __Error.WinIODriveError(Name, errorCode); 274 } 275 } 276 finally { 277 Win32Native.SetErrorMode(oldMode); 278 } 279 } 280 } 281 ToString()282 public override String ToString() 283 { 284 return Name; 285 } 286 287 #if FEATURE_SERIALIZATION 288 /// <internalonly/> 289 [System.Security.SecurityCritical] ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)290 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 291 { 292 // No need for an additional security check - everything is public. 293 info.AddValue(NameField, _name, typeof(String)); 294 } 295 #endif 296 297 } 298 } 299