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);
142 [MonoLimitation ("FileOptions are ignored")]
143 public static FileStream Create (string path, int bufferSize,
146 return new FileStream (path, FileMode.Create, FileAccess.ReadWrite,
147 FileShare.None, bufferSize, options);
151 [MonoLimitation ("FileOptions and FileSecurity are ignored")]
152 public static FileStream Create (string path, int bufferSize,
154 FileSecurity fileSecurity)
156 return new FileStream (path, FileMode.Create, FileAccess.ReadWrite,
157 FileShare.None, bufferSize, options);
161 public static StreamWriter CreateText (string path)
163 return new StreamWriter (path, false);
166 public static void Delete (string path)
168 Path.Validate (path);
169 if (Directory.Exists (path))
170 throw new UnauthorizedAccessException(Locale.GetText ("{0} is a directory", path));
172 string DirName = Path.GetDirectoryName(path);
173 if (DirName != String.Empty && !Directory.Exists (DirName))
174 throw new DirectoryNotFoundException (Locale.GetText ("Could not find a part of the path \"{0}\".", path));
176 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
180 if (!MonoIO.DeleteFile (path, out error)){
181 if (error != MonoIOError.ERROR_FILE_NOT_FOUND)
182 throw MonoIO.GetException (path, error);
186 public static bool Exists (string path)
188 // For security reasons no exceptions are
189 // thrown, only false is returned if there is
190 // any problem with the path or permissions.
191 // Minimizes what information can be
192 // discovered by using this method.
193 if (String.IsNullOrWhiteSpace (path) || path.IndexOfAny(Path.InvalidPathChars) >= 0)
196 // on Moonlight this does not throw but returns false
197 if (!SecurityManager.CheckElevatedPermissions ())
201 return MonoIO.ExistsFile (path, out error);
205 public static FileSecurity GetAccessControl (string path)
207 // AccessControlSections.Audit requires special permissions.
208 return GetAccessControl (path,
209 AccessControlSections.Owner |
210 AccessControlSections.Group |
211 AccessControlSections.Access);
214 public static FileSecurity GetAccessControl (string path, AccessControlSections includeSections)
216 return new FileSecurity (path, includeSections);
220 public static FileAttributes GetAttributes (string path)
222 Path.Validate (path);
223 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
226 FileAttributes attrs;
228 attrs = MonoIO.GetFileAttributes (path, out error);
229 if (error != MonoIOError.ERROR_SUCCESS)
230 throw MonoIO.GetException (path, error);
234 public static DateTime GetCreationTime (string path)
238 Path.Validate (path);
239 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
241 if (!MonoIO.GetFileStat (path, out stat, out error)) {
242 if (error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_FILE_NOT_FOUND)
243 return DefaultLocalFileTime;
245 throw new IOException (path);
247 return DateTime.FromFileTime (stat.CreationTime);
250 public static DateTime GetCreationTimeUtc (string path)
252 return GetCreationTime (path).ToUniversalTime ();
255 public static DateTime GetLastAccessTime (string path)
259 Path.Validate (path);
260 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
262 if (!MonoIO.GetFileStat (path, out stat, out error)) {
263 if (error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_FILE_NOT_FOUND)
264 return DefaultLocalFileTime;
266 throw new IOException (path);
268 return DateTime.FromFileTime (stat.LastAccessTime);
271 public static DateTime GetLastAccessTimeUtc (string path)
273 return GetLastAccessTime (path).ToUniversalTime ();
276 public static DateTime GetLastWriteTime (string path)
280 Path.Validate (path);
281 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
283 if (!MonoIO.GetFileStat (path, out stat, out error)) {
284 if (error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_FILE_NOT_FOUND)
285 return DefaultLocalFileTime;
287 throw new IOException (path);
289 return DateTime.FromFileTime (stat.LastWriteTime);
292 public static DateTime GetLastWriteTimeUtc (string path)
294 return GetLastWriteTime (path).ToUniversalTime ();
297 public static void Move (string sourceFileName, string destFileName)
299 if (sourceFileName == null)
300 throw new ArgumentNullException ("sourceFileName");
301 if (destFileName == null)
302 throw new ArgumentNullException ("destFileName");
303 if (sourceFileName.Length == 0)
304 throw new ArgumentException ("An empty file name is not valid.", "sourceFileName");
305 if (sourceFileName.Trim ().Length == 0 || sourceFileName.IndexOfAny (Path.InvalidPathChars) != -1)
306 throw new ArgumentException ("The file name is not valid.");
307 if (destFileName.Length == 0)
308 throw new ArgumentException ("An empty file name is not valid.", "destFileName");
309 if (destFileName.Trim ().Length == 0 || destFileName.IndexOfAny (Path.InvalidPathChars) != -1)
310 throw new ArgumentException ("The file name is not valid.");
312 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
315 if (!MonoIO.Exists (sourceFileName, out error))
316 throw new FileNotFoundException (Locale.GetText ("{0} does not exist", sourceFileName), sourceFileName);
318 // Don't check for this error here to allow the runtime
319 // to check if sourceFileName and destFileName are equal.
320 // Comparing sourceFileName and destFileName is not enough.
321 //if (MonoIO.Exists (destFileName, out error))
322 // throw new IOException (Locale.GetText ("{0} already exists", destFileName));
325 DirName = Path.GetDirectoryName (destFileName);
326 if (DirName != String.Empty && !Directory.Exists (DirName))
327 throw new DirectoryNotFoundException (Locale.GetText ("Could not find a part of the path."));
329 if (!MonoIO.MoveFile (sourceFileName, destFileName, out error)) {
330 if (error == MonoIOError.ERROR_ALREADY_EXISTS)
331 throw MonoIO.GetException (error);
332 else if (error == MonoIOError.ERROR_SHARING_VIOLATION)
333 throw MonoIO.GetException (sourceFileName, error);
335 throw MonoIO.GetException (error);
339 public static FileStream Open (string path, FileMode mode)
341 return new FileStream (path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, FileShare.None);
344 public static FileStream Open (string path, FileMode mode, FileAccess access)
346 return new FileStream (path, mode, access, FileShare.None);
349 public static FileStream Open (string path, FileMode mode, FileAccess access,
352 return new FileStream (path, mode, access, share);
355 public static FileStream OpenRead (string path)
357 return new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read);
360 public static StreamReader OpenText (string path)
362 return new StreamReader (path);
365 public static FileStream OpenWrite (string path)
367 return new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
370 public static void Replace (string sourceFileName,
371 string destinationFileName,
372 string destinationBackupFileName)
374 Replace (sourceFileName, destinationFileName, destinationBackupFileName, false);
377 public static void Replace (string sourceFileName,
378 string destinationFileName,
379 string destinationBackupFileName,
380 bool ignoreMetadataErrors)
384 if (sourceFileName == null)
385 throw new ArgumentNullException ("sourceFileName");
386 if (destinationFileName == null)
387 throw new ArgumentNullException ("destinationFileName");
388 if (sourceFileName.Trim ().Length == 0 || sourceFileName.IndexOfAny (Path.InvalidPathChars) != -1)
389 throw new ArgumentException ("sourceFileName");
390 if (destinationFileName.Trim ().Length == 0 || destinationFileName.IndexOfAny (Path.InvalidPathChars) != -1)
391 throw new ArgumentException ("destinationFileName");
393 string fullSource = Path.GetFullPath (sourceFileName);
394 string fullDest = Path.GetFullPath (destinationFileName);
395 if (MonoIO.ExistsDirectory (fullSource, out error))
396 throw new IOException (Locale.GetText ("{0} is a directory", sourceFileName));
397 if (MonoIO.ExistsDirectory (fullDest, out error))
398 throw new IOException (Locale.GetText ("{0} is a directory", destinationFileName));
400 if (!Exists (fullSource))
401 throw new FileNotFoundException (Locale.GetText ("{0} does not exist", sourceFileName),
403 if (!Exists (fullDest))
404 throw new FileNotFoundException (Locale.GetText ("{0} does not exist", destinationFileName),
405 destinationFileName);
406 if (fullSource == fullDest)
407 throw new IOException (Locale.GetText ("Source and destination arguments are the same file."));
409 string fullBackup = null;
410 if (destinationBackupFileName != null) {
411 if (destinationBackupFileName.Trim ().Length == 0 ||
412 destinationBackupFileName.IndexOfAny (Path.InvalidPathChars) != -1)
413 throw new ArgumentException ("destinationBackupFileName");
415 fullBackup = Path.GetFullPath (destinationBackupFileName);
416 if (MonoIO.ExistsDirectory (fullBackup, out error))
417 throw new IOException (Locale.GetText ("{0} is a directory", destinationBackupFileName));
418 if (fullSource == fullBackup)
419 throw new IOException (Locale.GetText ("Source and backup arguments are the same file."));
420 if (fullDest == fullBackup)
421 throw new IOException (Locale.GetText (
422 "Destination and backup arguments are the same file."));
425 var attrs = GetAttributes (fullDest);
427 // TODO: Should be done in wapi, win32 api handles this already
428 if ((attrs & FileAttributes.ReadOnly) != 0)
429 throw MonoIO.GetException (MonoIOError.ERROR_ACCESS_DENIED);
431 if (!MonoIO.ReplaceFile (fullSource, fullDest, fullBackup,
432 ignoreMetadataErrors, out error)) {
433 throw MonoIO.GetException (error);
438 public static void SetAccessControl (string path,
439 FileSecurity fileSecurity)
441 if (null == fileSecurity)
442 throw new ArgumentNullException ("fileSecurity");
444 fileSecurity.PersistModifications (path);
448 public static void SetAttributes (string path,
449 FileAttributes fileAttributes)
452 Path.Validate (path);
454 if (!MonoIO.SetFileAttributes (path, fileAttributes, out error))
455 throw MonoIO.GetException (path, error);
458 public static void SetCreationTime (string path, DateTime creationTime)
461 Path.Validate (path);
462 if (!MonoIO.Exists (path, out error))
463 throw MonoIO.GetException (path, error);
464 if (!MonoIO.SetCreationTime (path, creationTime, out error))
465 throw MonoIO.GetException (path, error);
468 public static void SetCreationTimeUtc (string path, DateTime creationTimeUtc)
470 SetCreationTime (path, creationTimeUtc.ToLocalTime ());
473 public static void SetLastAccessTime (string path, DateTime lastAccessTime)
476 Path.Validate (path);
477 if (!MonoIO.Exists (path, out error))
478 throw MonoIO.GetException (path, error);
479 if (!MonoIO.SetLastAccessTime (path, lastAccessTime, out error))
480 throw MonoIO.GetException (path, error);
483 public static void SetLastAccessTimeUtc (string path, DateTime lastAccessTimeUtc)
485 SetLastAccessTime (path, lastAccessTimeUtc.ToLocalTime ());
488 public static void SetLastWriteTime (string path,
489 DateTime lastWriteTime)
492 Path.Validate (path);
493 if (!MonoIO.Exists (path, out error))
494 throw MonoIO.GetException (path, error);
495 if (!MonoIO.SetLastWriteTime (path, lastWriteTime, out error))
496 throw MonoIO.GetException (path, error);
499 public static void SetLastWriteTimeUtc (string path,
500 DateTime lastWriteTimeUtc)
502 SetLastWriteTime (path, lastWriteTimeUtc.ToLocalTime ());
506 // The documentation for this method is most likely wrong, it
507 // talks about doing a "binary read", but the remarks say
508 // that this "detects the encoding".
510 // This can not detect and do anything useful with the encoding
511 // since the result is a byte [] not a char [].
513 public static byte [] ReadAllBytes (string path)
515 using (FileStream s = OpenRead (path)) {
516 long size = s.Length;
517 // limited to 2GB according to http://msdn.microsoft.com/en-us/library/system.io.file.readallbytes.aspx
518 if (size > Int32.MaxValue)
519 throw new IOException ("Reading more than 2GB with this call is not supported");
522 int count = (int) size;
523 byte [] result = new byte [size];
525 int n = s.Read (result, pos, count);
527 throw new IOException ("Unexpected end of stream");
535 public static string [] ReadAllLines (string path)
537 using (StreamReader reader = File.OpenText (path)) {
538 return ReadAllLines (reader);
542 public static string [] ReadAllLines (string path, Encoding encoding)
544 using (StreamReader reader = new StreamReader (path, encoding)) {
545 return ReadAllLines (reader);
549 static string [] ReadAllLines (StreamReader reader)
551 List<string> list = new List<string> ();
552 while (!reader.EndOfStream)
553 list.Add (reader.ReadLine ());
554 return list.ToArray ();
557 public static string ReadAllText (string path)
559 using (StreamReader sr = new StreamReader (path)) {
560 return sr.ReadToEnd ();
564 public static string ReadAllText (string path, Encoding encoding)
566 using (StreamReader sr = new StreamReader (path, encoding)) {
567 return sr.ReadToEnd ();
571 public static void WriteAllBytes (string path, byte [] bytes)
573 using (Stream stream = File.Create (path)) {
574 stream.Write (bytes, 0, bytes.Length);
578 public static void WriteAllLines (string path, string [] contents)
580 using (StreamWriter writer = new StreamWriter (path)) {
581 WriteAllLines (writer, contents);
585 public static void WriteAllLines (string path, string [] contents, Encoding encoding)
587 using (StreamWriter writer = new StreamWriter (path, false, encoding)) {
588 WriteAllLines (writer, contents);
592 static void WriteAllLines (StreamWriter writer, string [] contents)
594 foreach (string line in contents)
595 writer.WriteLine (line);
598 public static void WriteAllText (string path, string contents)
600 WriteAllText (path, contents, EncodingHelper.UTF8Unmarked);
603 public static void WriteAllText (string path, string contents, Encoding encoding)
605 using (StreamWriter sw = new StreamWriter (path, false, encoding)) {
610 static DateTime? defaultLocalFileTime;
611 static DateTime DefaultLocalFileTime {
613 if (defaultLocalFileTime == null)
614 defaultLocalFileTime = new DateTime (1601, 1, 1).ToLocalTime ();
616 return defaultLocalFileTime.Value;
621 [MonoLimitation ("File encryption isn't supported (even on NTFS).")]
622 public static void Encrypt (string path)
624 // MS.NET support this only on NTFS file systems, i.e. it's a file-system (not a framework) feature.
625 // otherwise it throws a NotSupportedException (or a PlatformNotSupportedException on older OS).
626 // we throw the same (instead of a NotImplementedException) because most code should already be
627 // handling this exception to work properly.
628 throw new NotSupportedException (Locale.GetText ("File encryption isn't supported on any file system."));
631 [MonoLimitation ("File encryption isn't supported (even on NTFS).")]
632 public static void Decrypt (string path)
634 // MS.NET support this only on NTFS file systems, i.e. it's a file-system (not a framework) feature.
635 // otherwise it throws a NotSupportedException (or a PlatformNotSupportedException on older OS).
636 // we throw the same (instead of a NotImplementedException) because most code should already be
637 // handling this exception to work properly.
638 throw new NotSupportedException (Locale.GetText ("File encryption isn't supported on any file system."));
641 public static IEnumerable<string> ReadLines (string path)
643 return ReadLines (File.OpenText (path));
646 public static IEnumerable<string> ReadLines (string path, Encoding encoding)
648 return ReadLines (new StreamReader (path, encoding));
651 // refactored in order to avoid compiler-generated names for Moonlight tools
652 static IEnumerable<string> ReadLines (StreamReader reader)
656 while ((s = reader.ReadLine ()) != null) {
662 public static void AppendAllLines (string path, IEnumerable<string> contents)
664 Path.Validate (path);
666 if (contents == null)
669 using (TextWriter w = new StreamWriter (path, true)) {
670 foreach (var line in contents)
675 public static void AppendAllLines (string path, IEnumerable<string> contents, Encoding encoding)
677 Path.Validate (path);
679 if (contents == null)
682 using (TextWriter w = new StreamWriter (path, true, encoding)) {
683 foreach (var line in contents)
688 public static void WriteAllLines (string path, IEnumerable<string> contents)
690 Path.Validate (path);
692 if (contents == null)
695 using (TextWriter w = new StreamWriter (path, false)) {
696 foreach (var line in contents)
701 public static void WriteAllLines (string path, IEnumerable<string> contents, Encoding encoding)
703 Path.Validate (path);
705 if (contents == null)
708 using (TextWriter w = new StreamWriter (path, false, encoding)) {
709 foreach (var line in contents)
714 internal static int FillAttributeInfo (String path, ref MonoIOStat data, bool tryagain, bool returnErrorOnNotFound)
717 throw new NotImplementedException ();
720 MonoIO.GetFileStat (path, out data, out error);
722 if (!returnErrorOnNotFound && (error == MonoIOError.ERROR_FILE_NOT_FOUND || error == MonoIOError.ERROR_PATH_NOT_FOUND || error == MonoIOError.ERROR_NOT_READY)) {
723 data = default (MonoIOStat);
724 data.fileAttributes = (FileAttributes) (-1);