Merge pull request #3389 from lambdageek/bug-43099
[mono.git] / mcs / class / referencesource / mscorlib / system / io / filesysteminfo.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  FileSystemInfo    
9 ** 
10 ** <OWNER>[....]</OWNER>
11 **
12 **
13 ** Purpose: 
14 **
15 **
16 ===========================================================*/
17
18 using System;
19 using System.Collections;
20 using System.Security;
21 #if FEATURE_MONO_CAS
22 using System.Security.Permissions;
23 #endif
24 using Microsoft.Win32;
25 using System.Text;
26 using System.Runtime.InteropServices;
27 using System.Runtime.Serialization;
28 using System.Runtime.Versioning;
29 using System.Diagnostics.Contracts;
30
31 namespace System.IO {
32     [Serializable]
33 #if !FEATURE_CORECLR && FEATURE_MONO_CAS
34     [FileIOPermissionAttribute(SecurityAction.InheritanceDemand,Unrestricted=true)]
35 #endif
36     [ComVisible(true)]
37 #if FEATURE_REMOTING        
38     public abstract class FileSystemInfo : MarshalByRefObject, ISerializable {
39 #else // FEATURE_REMOTING
40     public abstract class FileSystemInfo : ISerializable {   
41 #endif  //FEATURE_REMOTING      
42 #if MONO
43         internal MonoIOStat _data;
44 #else
45         [System.Security.SecurityCritical] // auto-generated
46         internal Win32Native.WIN32_FILE_ATTRIBUTE_DATA _data; // Cache the file information
47 #endif
48         internal int _dataInitialised = -1; // We use this field in conjunction with the Refresh methods, if we succeed
49                                             // we store a zero, on failure we store the HResult in it so that we can
50                                             // give back a generic error back.
51
52         private const int ERROR_INVALID_PARAMETER = 87;
53         internal const int ERROR_ACCESS_DENIED = 0x5;
54
55         protected String FullPath;          // fully qualified path of the directory
56         protected String OriginalPath;      // path passed in by the user
57         private String _displayPath = "";   // path that can be displayed to the user
58
59         #if FEATURE_CORECLR
60 #if FEATURE_CORESYSTEM
61         [System.Security.SecurityCritical]
62 #else
63         [System.Security.SecuritySafeCritical]
64 #endif //FEATURE_CORESYSTEM
65 #endif
66         protected FileSystemInfo()
67         {
68         }
69
70         [ResourceExposure(ResourceScope.None)]
71         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
72         protected FileSystemInfo(SerializationInfo info, StreamingContext context)
73         {
74             if (info == null)
75                 throw new ArgumentNullException("info");
76             Contract.EndContractBlock();
77             
78             // Must use V1 field names here, since V1 didn't implement 
79             // ISerializable.
80             FullPath = Path.GetFullPathInternal(info.GetString("FullPath"));
81             OriginalPath = info.GetString("OriginalPath");
82
83             // Lazily initialize the file attributes.
84             _dataInitialised = -1;
85         }
86 #if !MONO
87         [System.Security.SecurityCritical]
88         internal void InitializeFrom(Win32Native.WIN32_FIND_DATA findData)
89         {
90             _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
91             _data.PopulateFrom(findData);
92             _dataInitialised = 0;
93         }
94 #endif
95         // Full path of the direcory/file
96         public virtual String FullName {
97             [System.Security.SecuritySafeCritical]
98             get 
99             {
100                 String demandDir;
101                 if (this is DirectoryInfo)
102                     demandDir = Directory.GetDemandDir(FullPath, true);
103                 else
104                     demandDir = FullPath;
105 #if FEATURE_MONO_CAS
106 #if FEATURE_CORECLR
107                 FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.PathDiscovery, String.Empty, demandDir);
108                 sourceState.EnsureState();
109 #else
110                 FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandDir);
111 #endif
112 #endif
113                 return FullPath;
114             }
115         }
116
117         internal virtual String UnsafeGetFullName
118         {
119             [System.Security.SecurityCritical]
120             get
121             {
122                 String demandDir;
123                 if (this is DirectoryInfo)
124                     demandDir = Directory.GetDemandDir(FullPath, true);
125                 else
126                     demandDir = FullPath;
127 #if FEATURE_MONO_CAS
128 #if !FEATURE_CORECLR
129                 FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, demandDir);
130 #endif
131 #endif
132                 return FullPath;
133             }
134         }
135
136         public String Extension 
137         {
138             get
139             {
140                 // GetFullPathInternal would have already stripped out the terminating "." if present.
141                int length = FullPath.Length;
142                 for (int i = length; --i >= 0;) {
143                     char ch = FullPath[i];
144                     if (ch == '.')
145                         return FullPath.Substring(i, length - i);
146                     if (ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar || ch == Path.VolumeSeparatorChar)
147                         break;
148                 }
149                 return String.Empty;
150             }
151         }
152
153         // For files name of the file is returned, for directories the last directory in hierarchy is returned if possible,
154         // otherwise the fully qualified name s returned
155         public abstract String Name {
156             get;
157         }
158         
159         // Whether a file/directory exists
160         public abstract bool Exists
161         {
162             get;
163         }
164
165         // Delete a file/directory
166         public abstract void Delete();
167
168         public DateTime CreationTime
169         {
170             get {
171                     // depends on the security check in get_CreationTimeUtc
172                     return CreationTimeUtc.ToLocalTime();
173             }
174
175             set {
176                 CreationTimeUtc = value.ToUniversalTime();
177             }
178         }
179
180        [ComVisible(false)]
181        public DateTime CreationTimeUtc {
182            [System.Security.SecuritySafeCritical]
183             get {
184 #if FEATURE_CORECLR
185                 // get_CreationTime also depends on this security check
186                 FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, FullPath);
187                 sourceState.EnsureState();
188 #endif
189                 if (_dataInitialised == -1) {
190 #if !MONO
191                     _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
192 #endif
193                     Refresh();
194                 }
195
196                 if (_dataInitialised != 0) // Refresh was unable to initialise the data
197                     __Error.WinIOError(_dataInitialised, DisplayPath);
198                 
199 #if MONO
200                 long fileTime = _data.CreationTime;
201 #else
202                 long fileTime = ((long)_data.ftCreationTimeHigh << 32) | _data.ftCreationTimeLow;
203 #endif
204                 return DateTime.FromFileTimeUtc(fileTime);
205                 
206             }
207         
208             [ResourceExposure(ResourceScope.None)]
209             [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
210             set {
211                 if (this is DirectoryInfo)
212                     Directory.SetCreationTimeUtc(FullPath,value);
213                 else
214                     File.SetCreationTimeUtc(FullPath,value);
215                 _dataInitialised = -1;
216             }
217         }
218
219
220         public DateTime LastAccessTime
221        {
222            get {
223                 // depends on the security check in get_LastAccessTimeUtc
224                 return LastAccessTimeUtc.ToLocalTime();
225            }
226            set {
227                 LastAccessTimeUtc = value.ToUniversalTime();
228             }
229         }
230
231         [ComVisible(false)]
232         public DateTime LastAccessTimeUtc {
233             [System.Security.SecuritySafeCritical]
234             get {
235 #if FEATURE_CORECLR
236                 // get_LastAccessTime also depends on this security check
237                 FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, FullPath);
238                 sourceState.EnsureState();
239 #endif
240                 if (_dataInitialised == -1) {
241 #if !MONO
242                     _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
243 #endif
244                     Refresh();
245                 }
246
247                 if (_dataInitialised != 0) // Refresh was unable to initialise the data
248                     __Error.WinIOError(_dataInitialised, DisplayPath);
249                     
250 #if MONO
251                 long fileTime = _data.LastAccessTime;
252 #else
253                 long fileTime = ((long)_data.ftLastAccessTimeHigh << 32) | _data.ftLastAccessTimeLow;
254 #endif
255                 return DateTime.FromFileTimeUtc(fileTime);
256     
257             }
258
259             [ResourceExposure(ResourceScope.None)]
260             [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
261             set {
262                 if (this is DirectoryInfo)
263                     Directory.SetLastAccessTimeUtc(FullPath,value);
264                 else
265                     File.SetLastAccessTimeUtc(FullPath,value);
266                 _dataInitialised = -1;
267             }
268         }
269
270         public DateTime LastWriteTime
271         {
272             get {
273                 // depends on the security check in get_LastWriteTimeUtc
274                 return LastWriteTimeUtc.ToLocalTime();
275             }
276
277             set {
278                 LastWriteTimeUtc = value.ToUniversalTime();
279             }
280         }
281
282         [ComVisible(false)]
283         public DateTime LastWriteTimeUtc {
284             [System.Security.SecuritySafeCritical]
285             get {
286 #if FEATURE_CORECLR
287                 // get_LastWriteTime also depends on this security check
288                 FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, FullPath);
289                 sourceState.EnsureState();
290 #endif
291                 if (_dataInitialised == -1) {
292 #if !MONO
293                     _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
294 #endif
295                     Refresh();
296                 }
297
298                 if (_dataInitialised != 0) // Refresh was unable to initialise the data
299                     __Error.WinIOError(_dataInitialised, DisplayPath);
300         
301 #if MONO
302                 long fileTime = _data.LastWriteTime;
303 #else
304                 long fileTime = ((long)_data.ftLastWriteTimeHigh << 32) | _data.ftLastWriteTimeLow;
305 #endif
306                 return DateTime.FromFileTimeUtc(fileTime);
307             }
308
309             [ResourceExposure(ResourceScope.None)]
310             [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
311             set {
312                 if (this is DirectoryInfo)
313                     Directory.SetLastWriteTimeUtc(FullPath,value);
314                 else
315                     File.SetLastWriteTimeUtc(FullPath,value);
316                 _dataInitialised = -1;
317             }
318         }
319
320         [System.Security.SecuritySafeCritical]  // auto-generated
321         [ResourceExposure(ResourceScope.None)]
322         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
323         public void Refresh()
324         {
325             _dataInitialised = File.FillAttributeInfo(FullPath, ref _data, false, false);
326         }
327
328         public FileAttributes Attributes {
329             [System.Security.SecuritySafeCritical]
330             get
331             {
332 #if FEATURE_CORECLR
333                 FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Read, String.Empty, FullPath);
334                 sourceState.EnsureState();
335 #endif
336                 if (_dataInitialised == -1) {
337 #if !MONO
338                     _data = new Win32Native.WIN32_FILE_ATTRIBUTE_DATA();
339 #endif
340                     Refresh(); // Call refresh to intialise the data
341                 }
342
343                 if (_dataInitialised != 0) // Refresh was unable to initialise the data
344                     __Error.WinIOError(_dataInitialised, DisplayPath);
345
346                 return (FileAttributes) _data.fileAttributes;
347             }
348             #if FEATURE_CORECLR
349             [System.Security.SecurityCritical] // auto-generated
350             #else
351             [System.Security.SecuritySafeCritical]
352             #endif
353             set {
354 #if FEATURE_MONO_CAS
355 #if !FEATURE_CORECLR
356                 FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, FullPath);
357 #endif
358 #endif
359 #if MONO
360                 MonoIOError error;
361                 if (!MonoIO.SetFileAttributes (FullPath, value, out error)) {
362                     int hr = (int) error;
363 #else
364                 bool r = Win32Native.SetFileAttributes(FullPath, (int) value);
365                 if (!r) {
366                     int hr = Marshal.GetLastWin32Error();
367 #endif
368                     
369                     if (hr==ERROR_INVALID_PARAMETER)
370                         throw new ArgumentException(Environment.GetResourceString("Arg_InvalidFileAttrs"));
371                     
372                     // For whatever reason we are turning ERROR_ACCESS_DENIED into 
373                     // ArgumentException here (probably done for some 9x code path).
374                     // We can't change this now but special casing the error message instead.
375                     if (hr == ERROR_ACCESS_DENIED)
376                         throw new ArgumentException(Environment.GetResourceString("UnauthorizedAccess_IODenied_NoPathName"));
377                     __Error.WinIOError(hr, DisplayPath);
378                 }
379                 _dataInitialised = -1;
380             }
381         }
382
383         [System.Security.SecurityCritical]  // auto-generated_required
384         [ComVisible(false)]
385         public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
386         {
387 #if FEATURE_MONO_CAS
388 #if !FEATURE_CORECLR
389             FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, FullPath);
390 #endif
391 #endif
392
393             info.AddValue("OriginalPath", OriginalPath, typeof(String));
394             info.AddValue("FullPath", FullPath, typeof(String));
395         }
396
397         internal String DisplayPath
398         {
399             get
400             {
401                 return _displayPath;
402             }
403             set
404             {
405                 _displayPath = value;
406             }
407         }
408     }       
409 }