2 // System.IO.Directory.cs
5 // Jim Richardson (develop@wtfo-guru.com)
6 // Miguel de Icaza (miguel@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Eduardo Garcia (kiwnix@yahoo.es)
9 // Ville Palo (vi64pa@kolumbus.fi)
11 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
12 // Copyright (C) 2002 Ximian, Inc.
14 // Created: Monday, August 13, 2001
16 //------------------------------------------------------------------------------
19 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
21 // Permission is hereby granted, free of charge, to any person obtaining
22 // a copy of this software and associated documentation files (the
23 // "Software"), to deal in the Software without restriction, including
24 // without limitation the rights to use, copy, modify, merge, publish,
25 // distribute, sublicense, and/or sell copies of the Software, and to
26 // permit persons to whom the Software is furnished to do so, subject to
27 // the following conditions:
29 // The above copyright notice and this permission notice shall be
30 // included in all copies or substantial portions of the Software.
32 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 using System.Collections;
42 using System.Collections.Generic;
43 using System.Security;
44 using System.Security.Permissions;
46 using System.Runtime.InteropServices;
47 using System.Security.AccessControl;
52 public static class Directory
55 public static DirectoryInfo CreateDirectory (string path)
58 throw new ArgumentNullException ("path");
61 throw new ArgumentException ("Path is empty");
63 if (path.IndexOfAny (Path.InvalidPathChars) != -1)
64 throw new ArgumentException ("Path contains invalid chars");
66 if (path.Trim ().Length == 0)
67 throw new ArgumentException ("Only blank characters in path");
69 // after validations but before File.Exists to avoid an oracle
70 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
72 if (File.Exists(path))
73 throw new IOException ("Cannot create " + path + " because a file with the same name already exists.");
75 if (Environment.IsRunningOnWindows && path == ":")
76 throw new ArgumentException ("Only ':' In path");
78 return CreateDirectoriesInternal (path);
81 [MonoLimitation ("DirectorySecurity not implemented")]
82 public static DirectoryInfo CreateDirectory (string path, DirectorySecurity directorySecurity)
84 return(CreateDirectory (path));
87 static DirectoryInfo CreateDirectoriesInternal (string path)
90 if (SecurityManager.SecurityEnabled) {
91 new FileIOPermission (FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, path).Demand ();
94 DirectoryInfo info = new DirectoryInfo (path, true);
95 if (info.Parent != null && !info.Parent.Exists)
96 info.Parent.Create ();
99 if (!MonoIO.CreateDirectory (info.FullName, out error)) {
100 // LAMESPEC: 1.1 and 1.2alpha allow CreateDirectory on a file path.
101 // So CreateDirectory ("/tmp/somefile") will succeed if 'somefile' is
102 // not a directory. However, 1.0 will throw an exception.
103 // We behave like 1.0 here (emulating 1.1-like behavior is just a matter
104 // of comparing error to ERROR_FILE_EXISTS, but it's lame to do:
105 // DirectoryInfo di = Directory.CreateDirectory (something);
106 // and having di.Exists return false afterwards.
107 // I hope we don't break anyone's code, as they should be catching
108 // the exception anyway.
109 if (error != MonoIOError.ERROR_ALREADY_EXISTS &&
110 error != MonoIOError.ERROR_FILE_EXISTS)
111 throw MonoIO.GetException (path, error);
117 public static void Delete (string path)
119 Path.Validate (path);
121 if (Environment.IsRunningOnWindows && path == ":")
122 throw new NotSupportedException ("Only ':' In path");
124 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
129 if (MonoIO.ExistsSymlink (path, out error)) {
130 /* RemoveDirectory maps to rmdir()
131 * which fails on symlinks (ENOTDIR)
133 success = MonoIO.DeleteFile (path, out error);
135 success = MonoIO.RemoveDirectory (path, out error);
141 * In io-layer/io.c rmdir returns error_file_not_found if directory does not exist.
142 * So maybe this could be handled somewhere else?
144 if (error == MonoIOError.ERROR_FILE_NOT_FOUND) {
145 if (File.Exists (path))
146 throw new IOException ("Directory does not exist, but a file of the same name exists.");
148 throw new DirectoryNotFoundException ("Directory does not exist.");
150 throw MonoIO.GetException (path, error);
154 static void RecursiveDelete (string path)
158 foreach (string dir in GetDirectories (path)) {
159 if (MonoIO.ExistsSymlink (dir, out error)) {
160 MonoIO.DeleteFile (dir, out error);
162 RecursiveDelete (dir);
166 foreach (string file in GetFiles (path))
169 Directory.Delete (path);
172 public static void Delete (string path, bool recursive)
174 Path.Validate (path);
175 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
178 RecursiveDelete (path);
183 public static bool Exists (string path)
188 // on Moonlight this does not throw but returns false
189 if (!SecurityManager.CheckElevatedPermissions ())
195 exists = MonoIO.ExistsDirectory (path, out error);
196 /* This should not throw exceptions */
200 public static DateTime GetLastAccessTime (string path)
202 return File.GetLastAccessTime (path);
205 public static DateTime GetLastAccessTimeUtc (string path)
207 return GetLastAccessTime (path).ToUniversalTime ();
210 public static DateTime GetLastWriteTime (string path)
212 return File.GetLastWriteTime (path);
215 public static DateTime GetLastWriteTimeUtc (string path)
217 return GetLastWriteTime (path).ToUniversalTime ();
220 public static DateTime GetCreationTime (string path)
222 return File.GetCreationTime (path);
225 public static DateTime GetCreationTimeUtc (string path)
227 return GetCreationTime (path).ToUniversalTime ();
230 public static string GetCurrentDirectory ()
232 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
234 string result = InsecureGetCurrentDirectory();
236 if ((result != null) && (result.Length > 0) && SecurityManager.SecurityEnabled) {
237 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, result).Demand ();
243 internal static string InsecureGetCurrentDirectory()
246 string result = MonoIO.GetCurrentDirectory(out error);
248 if (error != MonoIOError.ERROR_SUCCESS)
249 throw MonoIO.GetException(error);
254 public static string [] GetDirectories (string path)
256 return GetDirectories (path, "*");
259 public static string [] GetDirectories (string path, string searchPattern)
261 return GetFileSystemEntries (path, searchPattern, FileAttributes.Directory, FileAttributes.Directory);
264 public static string [] GetDirectories (string path, string searchPattern, SearchOption searchOption)
266 if (searchOption == SearchOption.TopDirectoryOnly)
267 return GetDirectories (path, searchPattern);
268 var all = new List<string> ();
269 GetDirectoriesRecurse (path, searchPattern, all);
270 return all.ToArray ();
273 static void GetDirectoriesRecurse (string path, string searchPattern, List<string> all)
275 all.AddRange (GetDirectories (path, searchPattern));
276 foreach (string dir in GetDirectories (path))
277 GetDirectoriesRecurse (dir, searchPattern, all);
280 public static string GetDirectoryRoot (string path)
282 Path.Validate (path);
283 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
285 // FIXME nice hack but that does not work under windows
286 return new String(Path.DirectorySeparatorChar,1);
289 public static string [] GetFiles (string path)
291 return GetFiles (path, "*");
294 public static string [] GetFiles (string path, string searchPattern)
296 return GetFileSystemEntries (path, searchPattern, FileAttributes.Directory, 0);
299 public static string[] GetFiles (string path, string searchPattern, SearchOption searchOption)
301 if (searchOption == SearchOption.TopDirectoryOnly)
302 return GetFiles (path, searchPattern);
303 var all = new List<string> ();
304 GetFilesRecurse (path, searchPattern, all);
305 return all.ToArray ();
308 static void GetFilesRecurse (string path, string searchPattern, List<string> all)
310 all.AddRange (GetFiles (path, searchPattern));
311 foreach (string dir in GetDirectories (path))
312 GetFilesRecurse (dir, searchPattern, all);
315 public static string [] GetFileSystemEntries (string path)
317 return GetFileSystemEntries (path, "*");
320 public static string [] GetFileSystemEntries (string path, string searchPattern)
322 return GetFileSystemEntries (path, searchPattern, 0, 0);
325 public static string[] GetLogicalDrives ()
327 return Environment.GetLogicalDrives ();
330 static bool IsRootDirectory (string path)
333 if (Path.DirectorySeparatorChar == '/' && path == "/")
337 if (Path.DirectorySeparatorChar == '\\')
338 if (path.Length == 3 && path.EndsWith (":\\"))
344 public static DirectoryInfo GetParent (string path)
346 Path.Validate (path);
348 // return null if the path is the root directory
349 if (IsRootDirectory (path))
352 string parent_name = Path.GetDirectoryName (path);
353 if (parent_name.Length == 0)
354 parent_name = GetCurrentDirectory();
356 return new DirectoryInfo (parent_name);
359 public static void Move (string sourceDirName, string destDirName)
361 if (sourceDirName == null)
362 throw new ArgumentNullException ("sourceDirName");
364 if (destDirName == null)
365 throw new ArgumentNullException ("destDirName");
367 if (sourceDirName.Trim ().Length == 0 || sourceDirName.IndexOfAny (Path.InvalidPathChars) != -1)
368 throw new ArgumentException ("Invalid source directory name: " + sourceDirName, "sourceDirName");
370 if (destDirName.Trim ().Length == 0 || destDirName.IndexOfAny (Path.InvalidPathChars) != -1)
371 throw new ArgumentException ("Invalid target directory name: " + destDirName, "destDirName");
373 if (sourceDirName == destDirName)
374 throw new IOException ("Source and destination path must be different.");
376 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
378 if (Exists (destDirName))
379 throw new IOException (destDirName + " already exists.");
381 if (!Exists (sourceDirName) && !File.Exists (sourceDirName))
382 throw new DirectoryNotFoundException (sourceDirName + " does not exist");
385 if (!MonoIO.MoveFile (sourceDirName, destDirName, out error))
386 throw MonoIO.GetException (error);
389 public static void SetAccessControl (string path, DirectorySecurity directorySecurity)
391 if (null == directorySecurity)
392 throw new ArgumentNullException ("directorySecurity");
394 directorySecurity.PersistModifications (path);
397 public static void SetCreationTime (string path, DateTime creationTime)
399 File.SetCreationTime (path, creationTime);
402 public static void SetCreationTimeUtc (string path, DateTime creationTimeUtc)
404 SetCreationTime (path, creationTimeUtc.ToLocalTime ());
407 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
408 public static void SetCurrentDirectory (string path)
411 throw new ArgumentNullException ("path");
412 if (path.Trim ().Length == 0)
413 throw new ArgumentException ("path string must not be an empty string or whitespace string");
418 throw new DirectoryNotFoundException ("Directory \"" +
419 path + "\" not found.");
421 MonoIO.SetCurrentDirectory (path, out error);
422 if (error != MonoIOError.ERROR_SUCCESS)
423 throw MonoIO.GetException (path, error);
426 public static void SetLastAccessTime (string path, DateTime lastAccessTime)
428 File.SetLastAccessTime (path, lastAccessTime);
431 public static void SetLastAccessTimeUtc (string path, DateTime lastAccessTimeUtc)
433 SetLastAccessTime (path, lastAccessTimeUtc.ToLocalTime ());
436 public static void SetLastWriteTime (string path, DateTime lastWriteTime)
438 File.SetLastWriteTime (path, lastWriteTime);
441 public static void SetLastWriteTimeUtc (string path, DateTime lastWriteTimeUtc)
443 SetLastWriteTime (path, lastWriteTimeUtc.ToLocalTime ());
448 // Does the common validation, searchPattern has already been checked for not-null
449 static string ValidateDirectoryListing (string path, string searchPattern, out bool stop)
451 Path.Validate (path);
453 string wild = Path.Combine (path, searchPattern);
454 string wildpath = Path.GetDirectoryName (wild);
455 if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1)
456 throw new ArgumentException ("Pattern contains invalid characters", "pattern");
459 if (!MonoIO.ExistsDirectory (wildpath, out error)) {
460 if (error == MonoIOError.ERROR_SUCCESS) {
461 MonoIOError file_error;
462 if (MonoIO.ExistsFile (wildpath, out file_error))
463 throw new IOException ("The directory name is invalid.");
466 if (error != MonoIOError.ERROR_PATH_NOT_FOUND)
467 throw MonoIO.GetException (wildpath, error);
469 if (wildpath.IndexOfAny (SearchPattern.WildcardChars) == -1)
470 throw new DirectoryNotFoundException ("Directory '" + wildpath + "' not found.");
472 if (path.IndexOfAny (SearchPattern.WildcardChars) == -1)
473 throw new ArgumentException ("Pattern is invalid", "searchPattern");
475 throw new ArgumentException ("Path is invalid", "path");
482 private static string [] GetFileSystemEntries (string path, string searchPattern, FileAttributes mask, FileAttributes attrs)
484 if (searchPattern == null)
485 throw new ArgumentNullException ("searchPattern");
486 if (searchPattern.Length == 0)
487 return new string [] {};
489 string path_with_pattern = ValidateDirectoryListing (path, searchPattern, out stop);
491 return new string [] { path_with_pattern };
494 string [] result = MonoIO.GetFileSystemEntries (path, path_with_pattern, (int) attrs, (int) mask, out error);
496 throw MonoIO.GetException (Path.GetDirectoryName (Path.Combine (path, searchPattern)), error);
501 public static string[] GetFileSystemEntries (string path, string searchPattern, SearchOption searchOption)
503 // Take the simple way home:
504 return new List<string> (EnumerateFileSystemEntries (path, searchPattern, searchOption)).ToArray ();
507 static void EnumerateCheck (string path, string searchPattern, SearchOption searchOption)
509 if (searchPattern == null)
510 throw new ArgumentNullException ("searchPattern");
512 if (searchPattern.Length == 0)
515 if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
516 throw new ArgumentOutOfRangeException ("searchoption");
518 Path.Validate (path);
519 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
522 internal static IEnumerable<string> EnumerateKind (string path, string searchPattern, SearchOption searchOption, FileAttributes kind)
524 if (searchPattern.Length == 0)
528 string path_with_pattern = ValidateDirectoryListing (path, searchPattern, out stop);
530 yield return path_with_pattern;
536 FileAttributes rattr;
537 string s = MonoIO.FindFirst (path, path_with_pattern, out rattr, out error, out handle);
540 // Convert any file specific flag to FileAttributes.Normal which is used as include files flag
541 if (((rattr & FileAttributes.Directory) == 0) && rattr != 0)
542 rattr |= FileAttributes.Normal;
544 if ((rattr & kind) != 0)
547 s = MonoIO.FindNext (handle, out rattr, out error);
551 throw MonoIO.GetException (Path.GetDirectoryName (Path.Combine (path, searchPattern)), (MonoIOError) error);
553 if (handle != IntPtr.Zero)
554 MonoIO.FindClose (handle);
557 if (searchOption == SearchOption.AllDirectories) {
558 s = MonoIO.FindFirst (path, Path.Combine (path, "*"), out rattr, out error, out handle);
562 if ((rattr & FileAttributes.Directory) != 0 && (rattr & FileAttributes.ReparsePoint) == 0)
563 foreach (string child in EnumerateKind (s, searchPattern, searchOption, kind))
565 s = MonoIO.FindNext (handle, out rattr, out error);
569 throw MonoIO.GetException (path, (MonoIOError) error);
571 if (handle != IntPtr.Zero)
572 MonoIO.FindClose (handle);
577 public static IEnumerable<string> EnumerateDirectories (string path, string searchPattern, SearchOption searchOption)
579 EnumerateCheck (path, searchPattern, searchOption);
580 return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Directory);
583 public static IEnumerable<string> EnumerateDirectories (string path, string searchPattern)
585 EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
586 return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Directory);
589 public static IEnumerable<string> EnumerateDirectories (string path)
591 Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
592 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
593 return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Directory);
596 public static IEnumerable<string> EnumerateFiles (string path, string searchPattern, SearchOption searchOption)
598 EnumerateCheck (path, searchPattern, searchOption);
599 return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Normal);
602 public static IEnumerable<string> EnumerateFiles (string path, string searchPattern)
604 EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
605 return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Normal);
608 public static IEnumerable<string> EnumerateFiles (string path)
610 Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
611 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
612 return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Normal);
615 public static IEnumerable<string> EnumerateFileSystemEntries (string path, string searchPattern, SearchOption searchOption)
617 EnumerateCheck (path, searchPattern, searchOption);
618 return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Normal | FileAttributes.Directory);
621 public static IEnumerable<string> EnumerateFileSystemEntries (string path, string searchPattern)
623 EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
624 return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Normal | FileAttributes.Directory);
627 public static IEnumerable<string> EnumerateFileSystemEntries (string path)
629 Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
630 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
631 return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Normal | FileAttributes.Directory);
635 public static DirectorySecurity GetAccessControl (string path, AccessControlSections includeSections)
637 return new DirectorySecurity (path, includeSections);
640 public static DirectorySecurity GetAccessControl (string path)
642 // AccessControlSections.Audit requires special permissions.
643 return GetAccessControl (path,
644 AccessControlSections.Owner |
645 AccessControlSections.Group |
646 AccessControlSections.Access);
649 #region Copied from reference source
650 internal static String GetDemandDir(string fullPath, bool thisDirOnly)
655 if (fullPath.EndsWith( Path.DirectorySeparatorChar )
656 || fullPath.EndsWith( Path.AltDirectorySeparatorChar ) )
657 demandPath = fullPath + ".";
659 demandPath = fullPath + Path.DirectorySeparatorCharAsString + ".";
662 if (!(fullPath.EndsWith( Path.DirectorySeparatorChar )
663 || fullPath.EndsWith( Path.AltDirectorySeparatorChar )) )
664 demandPath = fullPath + Path.DirectorySeparatorCharAsString;
666 demandPath = fullPath;