Merge pull request #4248 from Unity-Technologies/boehm-gc-alloc-fixed
[mono.git] / mcs / class / referencesource / mscorlib / system / io / fileinfo.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** <OWNER>[....]</OWNER>
9 ** 
10 ** Class:  File
11 **
12 **
13 ** Purpose: A collection of methods for manipulating Files.
14 **
15 **          April 09,2000 (some design refactorization)
16 **
17 ===========================================================*/
18
19 using System;
20 #if FEATURE_MACL || MONO
21 using System.Security.AccessControl;
22 #endif
23 using System.Security.Permissions;
24 using PermissionSet = System.Security.PermissionSet;
25 using Win32Native = Microsoft.Win32.Win32Native;
26 using System.Runtime.InteropServices;
27 using System.Text;
28 using System.Runtime.Serialization;
29 using System.Globalization;
30 using System.Runtime.Versioning;
31 using System.Diagnostics.Contracts;
32
33 namespace System.IO {    
34     // Class for creating FileStream objects, and some basic file management
35     // routines such as Delete, etc.
36     [Serializable]
37     [ComVisible(true)]
38     public sealed class FileInfo: FileSystemInfo
39     {
40         private String _name;
41
42 #if FEATURE_CORECLR
43         // Migrating InheritanceDemands requires this default ctor, so we can annotate it.
44 #if FEATURE_CORESYSTEM
45         [System.Security.SecurityCritical]
46 #else
47         [System.Security.SecuritySafeCritical]
48 #endif //FEATURE_CORESYSTEM
49         private FileInfo(){}
50
51         [System.Security.SecurityCritical]
52         [ResourceExposure(ResourceScope.Machine)]
53         [ResourceConsumption(ResourceScope.Machine)]
54         public static FileInfo UnsafeCreateFileInfo(String fileName)
55         {
56             if (fileName == null)
57                 throw new ArgumentNullException("fileName");
58             Contract.EndContractBlock();
59
60             FileInfo fi = new FileInfo();
61             fi.Init(fileName, false);
62             return fi;
63         }
64 #endif
65
66         [System.Security.SecuritySafeCritical]
67         [ResourceExposure(ResourceScope.Machine)]
68         [ResourceConsumption(ResourceScope.Machine)]
69         public FileInfo(String fileName)
70         {
71             if (fileName == null)
72                 throw new ArgumentNullException("fileName");
73             Contract.EndContractBlock();
74
75 #if FEATURE_LEGACYNETCF
76             if(CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
77             {
78                 System.Reflection.Assembly callingAssembly = System.Reflection.Assembly.GetCallingAssembly();
79                 if(callingAssembly != null && !callingAssembly.IsProfileAssembly)
80                 {
81                     string caller = new System.Diagnostics.StackFrame(1).GetMethod().FullName;
82                     string callee = System.Reflection.MethodBase.GetCurrentMethod().FullName;
83                     throw new MethodAccessException(String.Format(
84                         CultureInfo.CurrentCulture,
85                         Environment.GetResourceString("Arg_MethodAccessException_WithCaller"),
86                         caller,
87                         callee));
88                 }
89             }
90 #endif // FEATURE_LEGACYNETCF
91
92             Init(fileName, true);
93         }
94
95         [System.Security.SecurityCritical]
96         [ResourceExposure(ResourceScope.Machine)]
97         [ResourceConsumption(ResourceScope.Machine)]
98         private void Init(String fileName, bool checkHost)
99         {
100             OriginalPath = fileName;
101             // Must fully qualify the path for the security check
102             String fullPath = Path.GetFullPathInternal(fileName);
103 #if !MONO
104 #if FEATURE_CORECLR
105             if (checkHost)
106             {
107                 FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, fileName, fullPath);
108                 state.EnsureState();
109             }
110 #else
111             FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, fullPath, false, false);
112 #endif
113 #endif
114
115             _name = Path.GetFileName(fileName);
116             FullPath = fullPath;
117             DisplayPath = GetDisplayPath(fileName);
118         }
119
120         private String GetDisplayPath(String originalPath)
121         {
122 #if FEATURE_CORECLR
123             return Path.GetFileName(originalPath);
124 #else
125             return originalPath;
126 #endif
127
128         }
129
130         [System.Security.SecurityCritical]  // auto-generated
131         private FileInfo(SerializationInfo info, StreamingContext context) : base(info, context)
132         {
133 #if MONO_FEATURE_CAS
134 #if !FEATURE_CORECLR
135             FileIOPermission.QuickDemand(FileIOPermissionAccess.Read, FullPath, false, false);
136 #endif
137 #endif
138             _name = Path.GetFileName(OriginalPath);
139             DisplayPath = GetDisplayPath(OriginalPath);
140         }
141
142 #if FEATURE_CORESYSTEM
143         [System.Security.SecuritySafeCritical]
144 #endif //FEATURE_CORESYSTEM
145         internal FileInfo(String fullPath, bool ignoreThis)
146         {
147 #if !MONO
148             Contract.Assert(Path.GetRootLength(fullPath) > 0, "fullPath must be fully qualified!");
149 #endif
150             _name = Path.GetFileName(fullPath);
151             OriginalPath = _name;
152             FullPath = fullPath;
153             DisplayPath = _name;
154         }
155
156         public override String Name {
157             get { return _name; }
158         }
159     
160    
161         public long Length {
162             [System.Security.SecuritySafeCritical]  // auto-generated
163             get {
164                 if (_dataInitialised == -1)
165                     Refresh();
166                 
167                 if (_dataInitialised != 0) // Refresh was unable to initialise the data
168                     __Error.WinIOError(_dataInitialised, DisplayPath);
169         
170                 if ((_data.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) != 0)
171                     __Error.WinIOError(Win32Native.ERROR_FILE_NOT_FOUND, DisplayPath);
172                 
173 #if MONO
174                 return _data.Length;
175 #else
176                 return ((long)_data.fileSizeHigh) << 32 | ((long)_data.fileSizeLow & 0xFFFFFFFFL);
177 #endif
178             }
179         }
180
181         /* Returns the name of the directory that the file is in */
182         public String DirectoryName
183         {
184             [System.Security.SecuritySafeCritical]
185             get
186             {
187                 String directoryName = Path.GetDirectoryName(FullPath);
188                 if (directoryName != null)
189                 {
190 #if MONO_FEATURE_CAS
191 #if FEATURE_CORECLR
192                     FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Read, DisplayPath, FullPath);
193                     state.EnsureState();
194 #else
195                     FileIOPermission.QuickDemand(FileIOPermissionAccess.PathDiscovery, directoryName, false, false);
196 #endif
197 #endif
198                 }
199                 return directoryName;
200             }
201         }
202
203         /* Creates an instance of the the parent directory */
204         public DirectoryInfo Directory
205         {
206             [ResourceExposure(ResourceScope.Machine)]
207             [ResourceConsumption(ResourceScope.Machine)]
208             get
209             {
210                 String dirName = DirectoryName;
211                 if (dirName == null)
212                     return null;
213                 return new DirectoryInfo(dirName);    
214             }
215         } 
216
217         public bool IsReadOnly {
218             get {
219                 return (Attributes & FileAttributes.ReadOnly) != 0;
220             }
221             set {
222                 if (value)
223                     Attributes |= FileAttributes.ReadOnly;
224                 else
225                     Attributes &= ~FileAttributes.ReadOnly;
226             }
227         }
228
229 #if FEATURE_MACL || MONO
230         [ResourceExposure(ResourceScope.None)]
231         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
232         public FileSecurity GetAccessControl()
233         {
234             return File.GetAccessControl(FullPath, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
235         }
236
237         [ResourceExposure(ResourceScope.None)]
238         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
239         public FileSecurity GetAccessControl(AccessControlSections includeSections)
240         {
241             return File.GetAccessControl(FullPath, includeSections);
242         }
243
244         [ResourceExposure(ResourceScope.None)]
245         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
246         public void SetAccessControl(FileSecurity fileSecurity)
247         {
248             File.SetAccessControl(FullPath, fileSecurity);
249         }
250 #endif
251
252         [System.Security.SecuritySafeCritical]  // auto-generated
253         [ResourceExposure(ResourceScope.Machine)]
254         [ResourceConsumption(ResourceScope.Machine)]
255         public StreamReader OpenText()
256         {
257             return new StreamReader(FullPath, Encoding.UTF8, true, StreamReader.DefaultBufferSize, false);
258         }
259
260         [ResourceExposure(ResourceScope.Machine)]
261         [ResourceConsumption(ResourceScope.Machine)]
262         public StreamWriter CreateText()
263         {
264             return new StreamWriter(FullPath,false);
265         }
266
267         [ResourceExposure(ResourceScope.Machine)]
268         [ResourceConsumption(ResourceScope.Machine)]
269         public StreamWriter AppendText()
270         {
271             return new StreamWriter(FullPath,true);
272         }
273
274         
275         // Copies an existing file to a new file. An exception is raised if the
276         // destination file already exists. Use the 
277         // Copy(String, String, boolean) method to allow 
278         // overwriting an existing file.
279         //
280         // The caller must have certain FileIOPermissions.  The caller must have
281         // Read permission to sourceFileName 
282         // and Write permissions to destFileName.
283         // 
284         [ResourceExposure(ResourceScope.Machine)]
285         [ResourceConsumption(ResourceScope.Machine)]
286         public FileInfo CopyTo(String destFileName) {
287             if (destFileName == null)
288                 throw new ArgumentNullException("destFileName", Environment.GetResourceString("ArgumentNull_FileName"));
289             if (destFileName.Length == 0)
290                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName");
291             Contract.EndContractBlock();
292
293             destFileName = File.InternalCopy(FullPath, destFileName, false, true);
294             return new FileInfo(destFileName, false);
295         }
296
297
298         // Copies an existing file to a new file. If overwrite is 
299         // false, then an IOException is thrown if the destination file 
300         // already exists.  If overwrite is true, the file is 
301         // overwritten.
302         //
303         // The caller must have certain FileIOPermissions.  The caller must have
304         // Read permission to sourceFileName and Create
305         // and Write permissions to destFileName.
306         // 
307         [ResourceExposure(ResourceScope.Machine)]
308         [ResourceConsumption(ResourceScope.Machine)]
309         public FileInfo CopyTo(String destFileName, bool overwrite) {
310             if (destFileName == null)
311                 throw new ArgumentNullException("destFileName", Environment.GetResourceString("ArgumentNull_FileName"));
312             if (destFileName.Length == 0)
313                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName");
314             Contract.EndContractBlock();
315
316             destFileName = File.InternalCopy(FullPath, destFileName, overwrite, true);
317             return new FileInfo(destFileName, false);
318         }
319
320         [ResourceExposure(ResourceScope.None)]
321         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
322         public FileStream Create() {
323             return File.Create(FullPath);
324         }
325
326         // Deletes a file. The file specified by the designated path is deleted. 
327         // If the file does not exist, Delete succeeds without throwing
328         // an exception.
329         // 
330         // On NT, Delete will fail for a file that is open for normal I/O
331         // or a file that is memory mapped.  On Win95, the file will be 
332         // deleted irregardless of whether the file is being used.
333         // 
334         // Your application must have Delete permission to the target file.
335         // 
336         [System.Security.SecuritySafeCritical]
337         [ResourceExposure(ResourceScope.None)]
338         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
339         public override void Delete()
340         {
341 #if MONO_FEATURE_CAS
342 #if FEATURE_CORECLR
343             FileSecurityState state = new FileSecurityState(FileSecurityStateAccess.Write, DisplayPath, FullPath);
344             state.EnsureState();
345 #else
346             // For security check, path should be resolved to an absolute path.
347             FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, FullPath, false, false);
348 #endif
349 #endif
350
351 #if MONO
352             MonoIOError error;
353
354             if (MonoIO.ExistsDirectory (FullPath, out error))
355                 __Error.WinIOError (Win32Native.ERROR_ACCESS_DENIED, DisplayPath);
356
357             if (!MonoIO.DeleteFile (FullPath, out error)) {
358                 int hr = (int) error;
359 #else
360             bool r = Win32Native.DeleteFile(FullPath);
361             if (!r) {
362                 int hr = Marshal.GetLastWin32Error();
363 #endif
364                 if (hr==Win32Native.ERROR_FILE_NOT_FOUND)
365                     return;
366                 else
367                     __Error.WinIOError(hr, DisplayPath);
368             }
369         }
370
371         [ComVisible(false)]
372         [ResourceExposure(ResourceScope.None)]
373         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
374         public void Decrypt()
375         {
376             File.Decrypt(FullPath);
377         }
378
379         [ComVisible(false)]
380         [ResourceExposure(ResourceScope.None)]
381         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
382         public void Encrypt()
383         {
384             File.Encrypt(FullPath);
385         }
386
387         // Tests if the given file exists. The result is true if the file
388         // given by the specified path exists; otherwise, the result is
389         // false.  
390         //
391         // Your application must have Read permission for the target directory.
392         public override bool Exists {
393             [System.Security.SecuritySafeCritical]  // auto-generated
394             get {
395                 try {
396                     if (_dataInitialised == -1)
397                         Refresh();
398                     if (_dataInitialised != 0) {
399                         // Refresh was unable to initialise the data.
400                         // We should normally be throwing an exception here, 
401                         // but Exists is supposed to return true or false.
402                         return false;
403                     }
404                     return (_data.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) == 0;
405                 }
406                 catch
407                 {
408                     return false;
409                 }
410             }
411         }
412
413         
414       
415       
416         // User must explicitly specify opening a new file or appending to one.
417         [ResourceExposure(ResourceScope.Machine)]
418         [ResourceConsumption(ResourceScope.Machine)]
419         public FileStream Open(FileMode mode) {
420             return Open(mode, FileAccess.ReadWrite, FileShare.None);
421         }
422
423         [ResourceExposure(ResourceScope.Machine)]
424         [ResourceConsumption(ResourceScope.Machine)]
425         public FileStream Open(FileMode mode, FileAccess access) {
426             return Open(mode, access, FileShare.None);
427         }
428
429         [ResourceExposure(ResourceScope.Machine)]
430         [ResourceConsumption(ResourceScope.Machine)]
431         public FileStream Open(FileMode mode, FileAccess access, FileShare share) {
432             return new FileStream(FullPath, mode, access, share);
433         }
434
435         
436 #if FEATURE_CORECLR
437         [System.Security.SecuritySafeCritical]  // auto-generated
438 #endif
439         [ResourceExposure(ResourceScope.Machine)]
440         [ResourceConsumption(ResourceScope.Machine)]
441         public FileStream OpenRead()
442         {
443             return new FileStream(FullPath, FileMode.Open, FileAccess.Read,
444                                   FileShare.Read, 4096, false);
445         }
446
447
448         [ResourceExposure(ResourceScope.Machine)]
449         [ResourceConsumption(ResourceScope.Machine)]
450         public FileStream OpenWrite() {
451             return new FileStream(FullPath, FileMode.OpenOrCreate, 
452                                   FileAccess.Write, FileShare.None);
453         }
454
455       
456
457        
458         
459
460         // Moves a given file to a new location and potentially a new file name.
461         // This method does work across volumes.
462         //
463         // The caller must have certain FileIOPermissions.  The caller must
464         // have Read and Write permission to 
465         // sourceFileName and Write 
466         // permissions to destFileName.
467         // 
468         [System.Security.SecuritySafeCritical]
469         [ResourceExposure(ResourceScope.Machine)]
470         [ResourceConsumption(ResourceScope.Machine)]
471         public void MoveTo(String destFileName) {
472             if (destFileName==null)
473                 throw new ArgumentNullException("destFileName");
474             if (destFileName.Length==0)
475                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destFileName");
476             Contract.EndContractBlock();
477
478             String fullDestFileName = Path.GetFullPathInternal(destFileName);
479 #if !MONO
480 #if FEATURE_CORECLR
481             FileSecurityState sourceState = new FileSecurityState(FileSecurityStateAccess.Write | FileSecurityStateAccess.Read, DisplayPath, FullPath);
482             FileSecurityState destState = new FileSecurityState(FileSecurityStateAccess.Write, destFileName, fullDestFileName);
483             sourceState.EnsureState();
484             destState.EnsureState();
485 #else
486             FileIOPermission.QuickDemand(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, FullPath, false, false);
487             FileIOPermission.QuickDemand(FileIOPermissionAccess.Write, fullDestFileName, false, false);
488 #endif
489 #endif
490
491 #if MONO
492             MonoIOError error;
493             if (!MonoIO.MoveFile (FullPath, fullDestFileName, out error))
494                 __Error.WinIOError ((int) error, String.Empty);
495 #else
496             if (!Win32Native.MoveFile(FullPath, fullDestFileName))
497                 __Error.WinIOError();
498 #endif
499             FullPath = fullDestFileName;
500             OriginalPath = destFileName;
501             _name = Path.GetFileName(fullDestFileName);
502             DisplayPath = GetDisplayPath(destFileName);
503             // Flush any cached information about the file.
504             _dataInitialised = -1;
505         }
506
507         [ComVisible(false)]
508         [ResourceExposure(ResourceScope.Machine)]
509         [ResourceConsumption(ResourceScope.Machine)]
510         public FileInfo Replace(String destinationFileName, String destinationBackupFileName)
511         {
512             return Replace(destinationFileName, destinationBackupFileName, false);
513         }
514
515         [ComVisible(false)]
516         [ResourceExposure(ResourceScope.Machine)]
517         [ResourceConsumption(ResourceScope.Machine)]
518         public FileInfo Replace(String destinationFileName, String destinationBackupFileName, bool ignoreMetadataErrors)
519         {
520             File.Replace(FullPath, destinationFileName, destinationBackupFileName, ignoreMetadataErrors);
521             return new FileInfo(destinationFileName);
522         }
523
524         // Returns the display path
525         public override String ToString()
526         {
527             return DisplayPath;
528         }
529     }
530 }