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