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 // LAMESPEC: with .net 1.0 version this throw NotSupportedException and msdn says so too
76 // but v1.1 throws ArgumentException.
77 if (Environment.IsRunningOnWindows && path == ":")
78 throw new ArgumentException ("Only ':' In path");
80 return CreateDirectoriesInternal (path);
83 [MonoLimitation ("DirectorySecurity not implemented")]
84 public static DirectoryInfo CreateDirectory (string path, DirectorySecurity directorySecurity)
86 return(CreateDirectory (path));
89 static DirectoryInfo CreateDirectoriesInternal (string path)
92 if (SecurityManager.SecurityEnabled) {
93 new FileIOPermission (FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, path).Demand ();
96 DirectoryInfo info = new DirectoryInfo (path, true);
97 if (info.Parent != null && !info.Parent.Exists)
98 info.Parent.Create ();
101 if (!MonoIO.CreateDirectory (path, out error)) {
102 // LAMESPEC: 1.1 and 1.2alpha allow CreateDirectory on a file path.
103 // So CreateDirectory ("/tmp/somefile") will succeed if 'somefile' is
104 // not a directory. However, 1.0 will throw an exception.
105 // We behave like 1.0 here (emulating 1.1-like behavior is just a matter
106 // of comparing error to ERROR_FILE_EXISTS, but it's lame to do:
107 // DirectoryInfo di = Directory.CreateDirectory (something);
108 // and having di.Exists return false afterwards.
109 // I hope we don't break anyone's code, as they should be catching
110 // the exception anyway.
111 if (error != MonoIOError.ERROR_ALREADY_EXISTS &&
112 error != MonoIOError.ERROR_FILE_EXISTS)
113 throw MonoIO.GetException (path, error);
119 public static void Delete (string path)
121 Path.Validate (path);
123 if (Environment.IsRunningOnWindows && path == ":")
124 throw new NotSupportedException ("Only ':' In path");
126 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
131 if (MonoIO.ExistsSymlink (path, out error)) {
132 /* RemoveDirectory maps to rmdir()
133 * which fails on symlinks (ENOTDIR)
135 success = MonoIO.DeleteFile (path, out error);
137 success = MonoIO.RemoveDirectory (path, out error);
143 * In io-layer/io.c rmdir returns error_file_not_found if directory does not exist.
144 * So maybe this could be handled somewhere else?
146 if (error == MonoIOError.ERROR_FILE_NOT_FOUND) {
147 if (File.Exists (path))
148 throw new IOException ("Directory does not exist, but a file of the same name exists.");
150 throw new DirectoryNotFoundException ("Directory does not exist.");
152 throw MonoIO.GetException (path, error);
156 static void RecursiveDelete (string path)
160 foreach (string dir in GetDirectories (path)) {
161 if (MonoIO.ExistsSymlink (dir, out error)) {
162 MonoIO.DeleteFile (dir, out error);
164 RecursiveDelete (dir);
168 foreach (string file in GetFiles (path))
171 Directory.Delete (path);
174 public static void Delete (string path, bool recursive)
176 Path.Validate (path);
177 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
180 RecursiveDelete (path);
185 public static bool Exists (string path)
190 // on Moonlight this does not throw but returns false
191 if (!SecurityManager.CheckElevatedPermissions ())
197 exists = MonoIO.ExistsDirectory (path, out error);
198 /* This should not throw exceptions */
202 public static DateTime GetLastAccessTime (string path)
204 return File.GetLastAccessTime (path);
207 public static DateTime GetLastAccessTimeUtc (string path)
209 return GetLastAccessTime (path).ToUniversalTime ();
212 public static DateTime GetLastWriteTime (string path)
214 return File.GetLastWriteTime (path);
217 public static DateTime GetLastWriteTimeUtc (string path)
219 return GetLastWriteTime (path).ToUniversalTime ();
222 public static DateTime GetCreationTime (string path)
224 return File.GetCreationTime (path);
227 public static DateTime GetCreationTimeUtc (string path)
229 return GetCreationTime (path).ToUniversalTime ();
232 public static string GetCurrentDirectory ()
234 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
236 string result = InsecureGetCurrentDirectory();
238 if ((result != null) && (result.Length > 0) && SecurityManager.SecurityEnabled) {
239 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, result).Demand ();
245 internal static string InsecureGetCurrentDirectory()
248 string result = MonoIO.GetCurrentDirectory(out error);
250 if (error != MonoIOError.ERROR_SUCCESS)
251 throw MonoIO.GetException(error);
256 public static string [] GetDirectories (string path)
258 return GetDirectories (path, "*");
261 public static string [] GetDirectories (string path, string searchPattern)
263 return GetFileSystemEntries (path, searchPattern, FileAttributes.Directory, FileAttributes.Directory);
266 public static string [] GetDirectories (string path, string searchPattern, SearchOption searchOption)
268 if (searchOption == SearchOption.TopDirectoryOnly)
269 return GetDirectories (path, searchPattern);
270 var all = new List<string> ();
271 GetDirectoriesRecurse (path, searchPattern, all);
272 return all.ToArray ();
275 static void GetDirectoriesRecurse (string path, string searchPattern, List<string> all)
277 all.AddRange (GetDirectories (path, searchPattern));
278 foreach (string dir in GetDirectories (path))
279 GetDirectoriesRecurse (dir, searchPattern, all);
282 public static string GetDirectoryRoot (string path)
284 Path.Validate (path);
285 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
287 // FIXME nice hack but that does not work under windows
288 return new String(Path.DirectorySeparatorChar,1);
291 public static string [] GetFiles (string path)
293 return GetFiles (path, "*");
296 public static string [] GetFiles (string path, string searchPattern)
298 return GetFileSystemEntries (path, searchPattern, FileAttributes.Directory, 0);
301 public static string[] GetFiles (string path, string searchPattern, SearchOption searchOption)
303 if (searchOption == SearchOption.TopDirectoryOnly)
304 return GetFiles (path, searchPattern);
305 var all = new List<string> ();
306 GetFilesRecurse (path, searchPattern, all);
307 return all.ToArray ();
310 static void GetFilesRecurse (string path, string searchPattern, List<string> all)
312 all.AddRange (GetFiles (path, searchPattern));
313 foreach (string dir in GetDirectories (path))
314 GetFilesRecurse (dir, searchPattern, all);
317 public static string [] GetFileSystemEntries (string path)
319 return GetFileSystemEntries (path, "*");
322 public static string [] GetFileSystemEntries (string path, string searchPattern)
324 return GetFileSystemEntries (path, searchPattern, 0, 0);
327 public static string[] GetLogicalDrives ()
329 return Environment.GetLogicalDrives ();
332 static bool IsRootDirectory (string path)
335 if (Path.DirectorySeparatorChar == '/' && path == "/")
339 if (Path.DirectorySeparatorChar == '\\')
340 if (path.Length == 3 && path.EndsWith (":\\"))
346 public static DirectoryInfo GetParent (string path)
348 Path.Validate (path);
350 // return null if the path is the root directory
351 if (IsRootDirectory (path))
354 string parent_name = Path.GetDirectoryName (path);
355 if (parent_name.Length == 0)
356 parent_name = GetCurrentDirectory();
358 return new DirectoryInfo (parent_name);
361 public static void Move (string sourceDirName, string destDirName)
363 if (sourceDirName == null)
364 throw new ArgumentNullException ("sourceDirName");
366 if (destDirName == null)
367 throw new ArgumentNullException ("destDirName");
369 if (sourceDirName.Trim ().Length == 0 || sourceDirName.IndexOfAny (Path.InvalidPathChars) != -1)
370 throw new ArgumentException ("Invalid source directory name: " + sourceDirName, "sourceDirName");
372 if (destDirName.Trim ().Length == 0 || destDirName.IndexOfAny (Path.InvalidPathChars) != -1)
373 throw new ArgumentException ("Invalid target directory name: " + destDirName, "destDirName");
375 if (sourceDirName == destDirName)
376 throw new IOException ("Source and destination path must be different.");
378 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
380 if (Exists (destDirName))
381 throw new IOException (destDirName + " already exists.");
383 if (!Exists (sourceDirName) && !File.Exists (sourceDirName))
384 throw new DirectoryNotFoundException (sourceDirName + " does not exist");
387 if (!MonoIO.MoveFile (sourceDirName, destDirName, out error))
388 throw MonoIO.GetException (error);
391 public static void SetAccessControl (string path, DirectorySecurity directorySecurity)
393 if (null == directorySecurity)
394 throw new ArgumentNullException ("directorySecurity");
396 directorySecurity.PersistModifications (path);
399 public static void SetCreationTime (string path, DateTime creationTime)
401 File.SetCreationTime (path, creationTime);
404 public static void SetCreationTimeUtc (string path, DateTime creationTimeUtc)
406 SetCreationTime (path, creationTimeUtc.ToLocalTime ());
409 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
410 public static void SetCurrentDirectory (string path)
413 throw new ArgumentNullException ("path");
414 if (path.Trim ().Length == 0)
415 throw new ArgumentException ("path string must not be an empty string or whitespace string");
420 throw new DirectoryNotFoundException ("Directory \"" +
421 path + "\" not found.");
423 MonoIO.SetCurrentDirectory (path, out error);
424 if (error != MonoIOError.ERROR_SUCCESS)
425 throw MonoIO.GetException (path, error);
428 public static void SetLastAccessTime (string path, DateTime lastAccessTime)
430 File.SetLastAccessTime (path, lastAccessTime);
433 public static void SetLastAccessTimeUtc (string path, DateTime lastAccessTimeUtc)
435 SetLastAccessTime (path, lastAccessTimeUtc.ToLocalTime ());
438 public static void SetLastWriteTime (string path, DateTime lastWriteTime)
440 File.SetLastWriteTime (path, lastWriteTime);
443 public static void SetLastWriteTimeUtc (string path, DateTime lastWriteTimeUtc)
445 SetLastWriteTime (path, lastWriteTimeUtc.ToLocalTime ());
450 // Does the common validation, searchPattern has already been checked for not-null
451 static string ValidateDirectoryListing (string path, string searchPattern, out bool stop)
453 Path.Validate (path);
455 string wild = Path.Combine (path, searchPattern);
456 string wildpath = Path.GetDirectoryName (wild);
457 if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1)
458 throw new ArgumentException ("Pattern contains invalid characters", "pattern");
461 if (!MonoIO.ExistsDirectory (wildpath, out error)) {
462 if (error == MonoIOError.ERROR_SUCCESS) {
463 MonoIOError file_error;
464 if (MonoIO.ExistsFile (wildpath, out file_error))
465 throw new IOException ("The directory name is invalid.");
468 if (error != MonoIOError.ERROR_PATH_NOT_FOUND)
469 throw MonoIO.GetException (wildpath, error);
471 if (wildpath.IndexOfAny (SearchPattern.WildcardChars) == -1)
472 throw new DirectoryNotFoundException ("Directory '" + wildpath + "' not found.");
474 if (path.IndexOfAny (SearchPattern.WildcardChars) == -1)
475 throw new ArgumentException ("Pattern is invalid", "searchPattern");
477 throw new ArgumentException ("Path is invalid", "path");
484 private static string [] GetFileSystemEntries (string path, string searchPattern, FileAttributes mask, FileAttributes attrs)
486 if (searchPattern == null)
487 throw new ArgumentNullException ("searchPattern");
488 if (searchPattern.Length == 0)
489 return new string [] {};
491 string path_with_pattern = ValidateDirectoryListing (path, searchPattern, out stop);
493 return new string [] { path_with_pattern };
496 string [] result = MonoIO.GetFileSystemEntries (path, path_with_pattern, (int) attrs, (int) mask, out error);
498 throw MonoIO.GetException (Path.GetDirectoryName (Path.Combine (path, searchPattern)), error);
504 public static string[] GetFileSystemEntries (string path, string searchPattern, SearchOption searchOption)
506 // Take the simple way home:
507 return new List<string> (EnumerateFileSystemEntries (path, searchPattern, searchOption)).ToArray ();
510 static void EnumerateCheck (string path, string searchPattern, SearchOption searchOption)
512 if (searchPattern == null)
513 throw new ArgumentNullException ("searchPattern");
515 if (searchPattern.Length == 0)
518 if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
519 throw new ArgumentOutOfRangeException ("searchoption");
521 Path.Validate (path);
522 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
525 internal static IEnumerable<string> EnumerateKind (string path, string searchPattern, SearchOption searchOption, FileAttributes kind)
527 if (searchPattern.Length == 0)
531 string path_with_pattern = ValidateDirectoryListing (path, searchPattern, out stop);
533 yield return path_with_pattern;
539 FileAttributes rattr;
540 string s = MonoIO.FindFirst (path, path_with_pattern, out rattr, out error, out handle);
543 // Convert any file specific flag to FileAttributes.Normal which is used as include files flag
544 if (((rattr & FileAttributes.Directory) == 0) && rattr != 0)
545 rattr |= FileAttributes.Normal;
547 if ((rattr & kind) != 0)
550 s = MonoIO.FindNext (handle, out rattr, out error);
554 throw MonoIO.GetException (Path.GetDirectoryName (Path.Combine (path, searchPattern)), (MonoIOError) error);
556 if (handle != IntPtr.Zero)
557 MonoIO.FindClose (handle);
560 if (searchOption == SearchOption.AllDirectories) {
561 s = MonoIO.FindFirst (path, Path.Combine (path, "*"), out rattr, out error, out handle);
565 if ((rattr & FileAttributes.Directory) != 0 && (rattr & FileAttributes.ReparsePoint) == 0)
566 foreach (string child in EnumerateKind (s, searchPattern, searchOption, kind))
568 s = MonoIO.FindNext (handle, out rattr, out error);
572 throw MonoIO.GetException (path, (MonoIOError) error);
574 if (handle != IntPtr.Zero)
575 MonoIO.FindClose (handle);
580 public static IEnumerable<string> EnumerateDirectories (string path, string searchPattern, SearchOption searchOption)
582 EnumerateCheck (path, searchPattern, searchOption);
583 return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Directory);
586 public static IEnumerable<string> EnumerateDirectories (string path, string searchPattern)
588 EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
589 return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Directory);
592 public static IEnumerable<string> EnumerateDirectories (string path)
594 Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
595 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
596 return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Directory);
599 public static IEnumerable<string> EnumerateFiles (string path, string searchPattern, SearchOption searchOption)
601 EnumerateCheck (path, searchPattern, searchOption);
602 return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Normal);
605 public static IEnumerable<string> EnumerateFiles (string path, string searchPattern)
607 EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
608 return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Normal);
611 public static IEnumerable<string> EnumerateFiles (string path)
613 Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
614 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
615 return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Normal);
618 public static IEnumerable<string> EnumerateFileSystemEntries (string path, string searchPattern, SearchOption searchOption)
620 EnumerateCheck (path, searchPattern, searchOption);
621 return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Normal | FileAttributes.Directory);
624 public static IEnumerable<string> EnumerateFileSystemEntries (string path, string searchPattern)
626 EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
627 return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Normal | FileAttributes.Directory);
630 public static IEnumerable<string> EnumerateFileSystemEntries (string path)
632 Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
633 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
634 return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Normal | FileAttributes.Directory);
639 public static DirectorySecurity GetAccessControl (string path, AccessControlSections includeSections)
641 return new DirectorySecurity (path, includeSections);
644 public static DirectorySecurity GetAccessControl (string path)
646 // AccessControlSections.Audit requires special permissions.
647 return GetAccessControl (path,
648 AccessControlSections.Owner |
649 AccessControlSections.Group |
650 AccessControlSections.Access);