6 // Miguel de Icaza (miguel@ximian.com)
7 // Jim Richardson (develop@wtfo-guru.com)
8 // Dan Lewis (dihlewis@yahoo.co.uk)
9 // Ville Palo (vi64pa@kolumbus.fi)
11 // Copyright 2002 Ximian, Inc. http://www.ximian.com
12 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
13 // Copyright (C) 2004, 2006, 2010 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections.Generic;
37 using System.Diagnostics;
38 using System.Security;
40 using System.Runtime.InteropServices;
43 using System.Security.AccessControl;
49 public static class File
51 public static void AppendAllText (string path, string contents)
53 using (TextWriter w = new StreamWriter (path, true)) {
58 public static void AppendAllText (string path, string contents, Encoding encoding)
60 using (TextWriter w = new StreamWriter (path, true, encoding)) {
65 public static StreamWriter AppendText (string path)
67 return new StreamWriter (path, true);
70 public static void Copy (string sourceFileName, string destFileName)
72 Copy (sourceFileName, destFileName, false);
75 public static void Copy (string sourceFileName, string destFileName, bool overwrite)
79 if (sourceFileName == null)
80 throw new ArgumentNullException ("sourceFileName");
81 if (destFileName == null)
82 throw new ArgumentNullException ("destFileName");
83 if (sourceFileName.Length == 0)
84 throw new ArgumentException ("An empty file name is not valid.", "sourceFileName");
85 if (sourceFileName.Trim ().Length == 0 || sourceFileName.IndexOfAny (Path.InvalidPathChars) != -1)
86 throw new ArgumentException ("The file name is not valid.");
87 if (destFileName.Length == 0)
88 throw new ArgumentException ("An empty file name is not valid.", "destFileName");
89 if (destFileName.Trim ().Length == 0 || destFileName.IndexOfAny (Path.InvalidPathChars) != -1)
90 throw new ArgumentException ("The file name is not valid.");
92 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
94 if (!MonoIO.Exists (sourceFileName, out error))
95 throw new FileNotFoundException (Locale.GetText ("{0} does not exist", sourceFileName), sourceFileName);
96 if ((GetAttributes (sourceFileName) & FileAttributes.Directory) == FileAttributes.Directory)
97 throw new ArgumentException (Locale.GetText ("{0} is a directory", sourceFileName));
99 if (MonoIO.Exists (destFileName, out error)) {
100 if ((GetAttributes (destFileName) & FileAttributes.Directory) == FileAttributes.Directory)
101 throw new ArgumentException (Locale.GetText ("{0} is a directory", destFileName));
103 throw new IOException (Locale.GetText ("{0} already exists", destFileName));
106 string DirName = Path.GetDirectoryName (destFileName);
107 if (DirName != String.Empty && !Directory.Exists (DirName))
108 throw new DirectoryNotFoundException (Locale.GetText ("Destination directory not found: {0}",DirName));
110 if (!MonoIO.CopyFile (sourceFileName, destFileName, overwrite, out error)) {
111 string p = Locale.GetText ("{0}\" or \"{1}", sourceFileName, destFileName);
112 throw MonoIO.GetException (p, error);
116 internal static String InternalCopy (String sourceFileName, String destFileName, bool overwrite, bool checkHost)
118 String fullSourceFileName = Path.GetFullPathInternal(sourceFileName);
119 String fullDestFileName = Path.GetFullPathInternal(destFileName);
123 if (!MonoIO.CopyFile (fullSourceFileName, fullDestFileName, overwrite, out error)) {
124 string p = Locale.GetText ("{0}\" or \"{1}", sourceFileName, destFileName);
125 throw MonoIO.GetException (p, error);
128 return fullDestFileName;
131 public static FileStream Create (string path)
133 return Create (path, 8192);
136 public static FileStream Create (string path, int bufferSize)
138 return new FileStream (path, FileMode.Create, FileAccess.ReadWrite,
139 FileShare.None, bufferSize);
143 [MonoLimitation ("FileOptions are ignored")]
144 public static FileStream Create (string path, int bufferSize,
147 return Create (path, bufferSize, options, null);
150 [MonoLimitation ("FileOptions and FileSecurity are ignored")]
151 public static FileStream Create (string path, int bufferSize,
153 FileSecurity fileSecurity)
155 return new FileStream (path, FileMode.Create, FileAccess.ReadWrite,
156 FileShare.None, bufferSize, options);
160 public static StreamWriter CreateText (string path)
162 return new StreamWriter (path, false);
165 public static void Delete (string path)
167 Path.Validate (path);
168 if (Directory.Exists (path))
169 throw new UnauthorizedAccessException(Locale.GetText ("{0} is a directory", path));
171 string DirName = Path.GetDirectoryName(path);
172 if (DirName != String.Empty && !Directory.Exists (DirName))
173 throw new DirectoryNotFoundException (Locale.GetText ("Could not find a part of the path \"{0}\".", path));
175 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
179 if (!MonoIO.DeleteFile (path, out error)){
180 if (error != MonoIOError.ERROR_FILE_NOT_FOUND)
181 throw MonoIO.GetException (path, error);
185 public static bool Exists (string path)
187 // For security reasons no exceptions are
188 // thrown, only false is returned if there is
189 // any problem with the path or permissions.
190 // Minimizes what information can be
191 // discovered by using this method.
192 if (String.IsNullOrWhiteSpace (path) || path.IndexOfAny(Path.InvalidPathChars) >= 0)
195 // on Moonlight this does not throw but returns false
196 if (!SecurityManager.CheckElevatedPermissions ())
200 return MonoIO.ExistsFile (path, out error);
204 public static FileSecurity GetAccessControl (string path)
206 // AccessControlSections.Audit requires special permissions.
207 return GetAccessControl (path,
208 AccessControlSections.Owner |
209 AccessControlSections.Group |
210 AccessControlSections.Access);
213 public static FileSecurity GetAccessControl (string path, AccessControlSections includeSections)
215 return new FileSecurity (path, includeSections);
219 public static FileAttributes GetAttributes (string path)
221 Path.Validate (path);
222 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
225 FileAttributes attrs;
227 attrs = MonoIO.GetFileAttributes (path, out error);
228 if (error != MonoIOError.ERROR_SUCCESS)
229 throw MonoIO.GetException (path, error);
233 public static DateTime GetCreationTime (string path)
237 Path.Validate (path);
238 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
240 if (!MonoIO.GetFileStat (path, out stat, out error)) {
241 if (error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_FILE_NOT_FOUND)
242 return DefaultLocalFileTime;
244 throw new IOException (path);
246 return DateTime.FromFileTime (stat.CreationTime);
249 public static DateTime GetCreationTimeUtc (string path)
251 return GetCreationTime (path).ToUniversalTime ();
254 public static DateTime GetLastAccessTime (string path)
258 Path.Validate (path);
259 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
261 if (!MonoIO.GetFileStat (path, out stat, out error)) {
262 if (error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_FILE_NOT_FOUND)
263 return DefaultLocalFileTime;
265 throw new IOException (path);
267 return DateTime.FromFileTime (stat.LastAccessTime);
270 public static DateTime GetLastAccessTimeUtc (string path)
272 return GetLastAccessTime (path).ToUniversalTime ();
275 public static DateTime GetLastWriteTime (string path)
279 Path.Validate (path);
280 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
282 if (!MonoIO.GetFileStat (path, out stat, out error)) {
283 if (error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_FILE_NOT_FOUND)
284 return DefaultLocalFileTime;
286 throw new IOException (path);
288 return DateTime.FromFileTime (stat.LastWriteTime);
291 public static DateTime GetLastWriteTimeUtc (string path)
293 return GetLastWriteTime (path).ToUniversalTime ();
296 public static void Move (string sourceFileName, string destFileName)
298 if (sourceFileName == null)
299 throw new ArgumentNullException ("sourceFileName");
300 if (destFileName == null)
301 throw new ArgumentNullException ("destFileName");
302 if (sourceFileName.Length == 0)
303 throw new ArgumentException ("An empty file name is not valid.", "sourceFileName");
304 if (sourceFileName.Trim ().Length == 0 || sourceFileName.IndexOfAny (Path.InvalidPathChars) != -1)
305 throw new ArgumentException ("The file name is not valid.");
306 if (destFileName.Length == 0)
307 throw new ArgumentException ("An empty file name is not valid.", "destFileName");
308 if (destFileName.Trim ().Length == 0 || destFileName.IndexOfAny (Path.InvalidPathChars) != -1)
309 throw new ArgumentException ("The file name is not valid.");
311 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
314 if (!MonoIO.Exists (sourceFileName, out error))
315 throw new FileNotFoundException (Locale.GetText ("{0} does not exist", sourceFileName), sourceFileName);
317 // Don't check for this error here to allow the runtime
318 // to check if sourceFileName and destFileName are equal.
319 // Comparing sourceFileName and destFileName is not enough.
320 //if (MonoIO.Exists (destFileName, out error))
321 // throw new IOException (Locale.GetText ("{0} already exists", destFileName));
324 DirName = Path.GetDirectoryName (destFileName);
325 if (DirName != String.Empty && !Directory.Exists (DirName))
326 throw new DirectoryNotFoundException (Locale.GetText ("Could not find a part of the path."));
328 if (!MonoIO.MoveFile (sourceFileName, destFileName, out error)) {
329 if (error == MonoIOError.ERROR_ALREADY_EXISTS)
330 throw MonoIO.GetException (error);
331 else if (error == MonoIOError.ERROR_SHARING_VIOLATION)
332 throw MonoIO.GetException (sourceFileName, error);
334 throw MonoIO.GetException (error);
338 public static FileStream Open (string path, FileMode mode)
340 return new FileStream (path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, FileShare.None);
343 public static FileStream Open (string path, FileMode mode, FileAccess access)
345 return new FileStream (path, mode, access, FileShare.None);
348 public static FileStream Open (string path, FileMode mode, FileAccess access,
351 return new FileStream (path, mode, access, share);
354 public static FileStream OpenRead (string path)
356 return new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read);
359 public static StreamReader OpenText (string path)
361 return new StreamReader (path);
364 public static FileStream OpenWrite (string path)
366 return new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
369 public static void Replace (string sourceFileName,
370 string destinationFileName,
371 string destinationBackupFileName)
373 Replace (sourceFileName, destinationFileName, destinationBackupFileName, false);
376 public static void Replace (string sourceFileName,
377 string destinationFileName,
378 string destinationBackupFileName,
379 bool ignoreMetadataErrors)
383 if (sourceFileName == null)
384 throw new ArgumentNullException ("sourceFileName");
385 if (destinationFileName == null)
386 throw new ArgumentNullException ("destinationFileName");
387 if (sourceFileName.Trim ().Length == 0 || sourceFileName.IndexOfAny (Path.InvalidPathChars) != -1)
388 throw new ArgumentException ("sourceFileName");
389 if (destinationFileName.Trim ().Length == 0 || destinationFileName.IndexOfAny (Path.InvalidPathChars) != -1)
390 throw new ArgumentException ("destinationFileName");
392 string fullSource = Path.GetFullPath (sourceFileName);
393 string fullDest = Path.GetFullPath (destinationFileName);
394 if (MonoIO.ExistsDirectory (fullSource, out error))
395 throw new IOException (Locale.GetText ("{0} is a directory", sourceFileName));
396 if (MonoIO.ExistsDirectory (fullDest, out error))
397 throw new IOException (Locale.GetText ("{0} is a directory", destinationFileName));
399 if (!Exists (fullSource))
400 throw new FileNotFoundException (Locale.GetText ("{0} does not exist", sourceFileName),
402 if (!Exists (fullDest))
403 throw new FileNotFoundException (Locale.GetText ("{0} does not exist", destinationFileName),
404 destinationFileName);
405 if (fullSource == fullDest)
406 throw new IOException (Locale.GetText ("Source and destination arguments are the same file."));
408 string fullBackup = null;
409 if (destinationBackupFileName != null) {
410 if (destinationBackupFileName.Trim ().Length == 0 ||
411 destinationBackupFileName.IndexOfAny (Path.InvalidPathChars) != -1)
412 throw new ArgumentException ("destinationBackupFileName");
414 fullBackup = Path.GetFullPath (destinationBackupFileName);
415 if (MonoIO.ExistsDirectory (fullBackup, out error))
416 throw new IOException (Locale.GetText ("{0} is a directory", destinationBackupFileName));
417 if (fullSource == fullBackup)
418 throw new IOException (Locale.GetText ("Source and backup arguments are the same file."));
419 if (fullDest == fullBackup)
420 throw new IOException (Locale.GetText (
421 "Destination and backup arguments are the same file."));
424 var attrs = GetAttributes (fullDest);
426 // TODO: Should be done in wapi, win32 api handles this already
427 if ((attrs & FileAttributes.ReadOnly) != 0)
428 throw MonoIO.GetException (MonoIOError.ERROR_ACCESS_DENIED);
430 if (!MonoIO.ReplaceFile (fullSource, fullDest, fullBackup,
431 ignoreMetadataErrors, out error)) {
432 throw MonoIO.GetException (error);
437 public static void SetAccessControl (string path,
438 FileSecurity fileSecurity)
440 if (null == fileSecurity)
441 throw new ArgumentNullException ("fileSecurity");
443 fileSecurity.PersistModifications (path);
447 public static void SetAttributes (string path,
448 FileAttributes fileAttributes)
451 Path.Validate (path);
453 if (!MonoIO.SetFileAttributes (path, fileAttributes, out error))
454 throw MonoIO.GetException (path, error);
457 public static void SetCreationTime (string path, DateTime creationTime)
460 Path.Validate (path);
461 if (!MonoIO.Exists (path, out error))
462 throw MonoIO.GetException (path, error);
463 if (!MonoIO.SetCreationTime (path, creationTime, out error))
464 throw MonoIO.GetException (path, error);
467 public static void SetCreationTimeUtc (string path, DateTime creationTimeUtc)
469 SetCreationTime (path, creationTimeUtc.ToLocalTime ());
472 public static void SetLastAccessTime (string path, DateTime lastAccessTime)
475 Path.Validate (path);
476 if (!MonoIO.Exists (path, out error))
477 throw MonoIO.GetException (path, error);
478 if (!MonoIO.SetLastAccessTime (path, lastAccessTime, out error))
479 throw MonoIO.GetException (path, error);
482 public static void SetLastAccessTimeUtc (string path, DateTime lastAccessTimeUtc)
484 SetLastAccessTime (path, lastAccessTimeUtc.ToLocalTime ());
487 public static void SetLastWriteTime (string path,
488 DateTime lastWriteTime)
491 Path.Validate (path);
492 if (!MonoIO.Exists (path, out error))
493 throw MonoIO.GetException (path, error);
494 if (!MonoIO.SetLastWriteTime (path, lastWriteTime, out error))
495 throw MonoIO.GetException (path, error);
498 public static void SetLastWriteTimeUtc (string path,
499 DateTime lastWriteTimeUtc)
501 SetLastWriteTime (path, lastWriteTimeUtc.ToLocalTime ());
505 // The documentation for this method is most likely wrong, it
506 // talks about doing a "binary read", but the remarks say
507 // that this "detects the encoding".
509 // This can not detect and do anything useful with the encoding
510 // since the result is a byte [] not a char [].
512 public static byte [] ReadAllBytes (string path)
514 using (FileStream s = OpenRead (path)) {
515 long size = s.Length;
516 // limited to 2GB according to http://msdn.microsoft.com/en-us/library/system.io.file.readallbytes.aspx
517 if (size > Int32.MaxValue)
518 throw new IOException ("Reading more than 2GB with this call is not supported");
521 int count = (int) size;
522 byte [] result = new byte [size];
524 int n = s.Read (result, pos, count);
526 throw new IOException ("Unexpected end of stream");
534 public static string [] ReadAllLines (string path)
536 using (StreamReader reader = File.OpenText (path)) {
537 return ReadAllLines (reader);
541 public static string [] ReadAllLines (string path, Encoding encoding)
543 using (StreamReader reader = new StreamReader (path, encoding)) {
544 return ReadAllLines (reader);
548 static string [] ReadAllLines (StreamReader reader)
550 List<string> list = new List<string> ();
551 while (!reader.EndOfStream)
552 list.Add (reader.ReadLine ());
553 return list.ToArray ();
556 public static string ReadAllText (string path)
558 using (StreamReader sr = new StreamReader (path)) {
559 return sr.ReadToEnd ();
563 public static string ReadAllText (string path, Encoding encoding)
565 using (StreamReader sr = new StreamReader (path, encoding)) {
566 return sr.ReadToEnd ();
570 public static void WriteAllBytes (string path, byte [] bytes)
572 using (Stream stream = File.Create (path)) {
573 stream.Write (bytes, 0, bytes.Length);
577 public static void WriteAllLines (string path, string [] contents)
579 using (StreamWriter writer = new StreamWriter (path)) {
580 WriteAllLines (writer, contents);
584 public static void WriteAllLines (string path, string [] contents, Encoding encoding)
586 using (StreamWriter writer = new StreamWriter (path, false, encoding)) {
587 WriteAllLines (writer, contents);
591 static void WriteAllLines (StreamWriter writer, string [] contents)
593 foreach (string line in contents)
594 writer.WriteLine (line);
597 public static void WriteAllText (string path, string contents)
599 WriteAllText (path, contents, EncodingHelper.UTF8Unmarked);
602 public static void WriteAllText (string path, string contents, Encoding encoding)
604 using (StreamWriter sw = new StreamWriter (path, false, encoding)) {
609 static DateTime? defaultLocalFileTime;
610 static DateTime DefaultLocalFileTime {
612 if (defaultLocalFileTime == null)
613 defaultLocalFileTime = new DateTime (1601, 1, 1).ToLocalTime ();
615 return defaultLocalFileTime.Value;
620 [MonoLimitation ("File encryption isn't supported (even on NTFS).")]
621 public static void Encrypt (string path)
623 // MS.NET support this only on NTFS file systems, i.e. it's a file-system (not a framework) feature.
624 // otherwise it throws a NotSupportedException (or a PlatformNotSupportedException on older OS).
625 // we throw the same (instead of a NotImplementedException) because most code should already be
626 // handling this exception to work properly.
627 throw new NotSupportedException (Locale.GetText ("File encryption isn't supported on any file system."));
630 [MonoLimitation ("File encryption isn't supported (even on NTFS).")]
631 public static void Decrypt (string path)
633 // MS.NET support this only on NTFS file systems, i.e. it's a file-system (not a framework) feature.
634 // otherwise it throws a NotSupportedException (or a PlatformNotSupportedException on older OS).
635 // we throw the same (instead of a NotImplementedException) because most code should already be
636 // handling this exception to work properly.
637 throw new NotSupportedException (Locale.GetText ("File encryption isn't supported on any file system."));
640 public static IEnumerable<string> ReadLines (string path)
642 return ReadLines (File.OpenText (path));
645 public static IEnumerable<string> ReadLines (string path, Encoding encoding)
647 return ReadLines (new StreamReader (path, encoding));
650 // refactored in order to avoid compiler-generated names for Moonlight tools
651 static IEnumerable<string> ReadLines (StreamReader reader)
655 while ((s = reader.ReadLine ()) != null) {
661 public static void AppendAllLines (string path, IEnumerable<string> contents)
663 Path.Validate (path);
665 if (contents == null)
668 using (TextWriter w = new StreamWriter (path, true)) {
669 foreach (var line in contents)
674 public static void AppendAllLines (string path, IEnumerable<string> contents, Encoding encoding)
676 Path.Validate (path);
678 if (contents == null)
681 using (TextWriter w = new StreamWriter (path, true, encoding)) {
682 foreach (var line in contents)
687 public static void WriteAllLines (string path, IEnumerable<string> contents)
689 Path.Validate (path);
691 if (contents == null)
694 using (TextWriter w = new StreamWriter (path, false)) {
695 foreach (var line in contents)
700 public static void WriteAllLines (string path, IEnumerable<string> contents, Encoding encoding)
702 Path.Validate (path);
704 if (contents == null)
707 using (TextWriter w = new StreamWriter (path, false, encoding)) {
708 foreach (var line in contents)
713 internal static int FillAttributeInfo (String path, ref MonoIOStat data, bool tryagain, bool returnErrorOnNotFound)
716 throw new NotImplementedException ();
719 MonoIO.GetFileStat (path, out data, out error);
721 if (!returnErrorOnNotFound && (error == MonoIOError.ERROR_FILE_NOT_FOUND || error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_NOT_READY)) {
722 data = default (MonoIOStat);
723 data.fileAttributes = (FileAttributes) (-1);