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;
49 using System.Security.AccessControl;
55 public static class Directory
58 public static DirectoryInfo CreateDirectory (string path)
61 throw new ArgumentNullException ("path");
64 throw new ArgumentException ("Path is empty");
66 if (path.IndexOfAny (Path.InvalidPathChars) != -1)
67 throw new ArgumentException ("Path contains invalid chars");
69 if (path.Trim ().Length == 0)
70 throw new ArgumentException ("Only blank characters in path");
72 // after validations but before File.Exists to avoid an oracle
73 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
75 if (File.Exists(path))
76 throw new IOException ("Cannot create " + path + " because a file with the same name already exists.");
78 // LAMESPEC: with .net 1.0 version this throw NotSupportedException and msdn says so too
79 // but v1.1 throws ArgumentException.
80 if (Environment.IsRunningOnWindows && path == ":")
81 throw new ArgumentException ("Only ':' In path");
83 return CreateDirectoriesInternal (path);
87 [MonoLimitation ("DirectorySecurity not implemented")]
88 public static DirectoryInfo CreateDirectory (string path, DirectorySecurity directorySecurity)
90 return(CreateDirectory (path));
94 static DirectoryInfo CreateDirectoriesInternal (string path)
97 if (SecurityManager.SecurityEnabled) {
98 new FileIOPermission (FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, path).Demand ();
101 DirectoryInfo info = new DirectoryInfo (path, true);
102 if (info.Parent != null && !info.Parent.Exists)
103 info.Parent.Create ();
106 if (!MonoIO.CreateDirectory (path, out error)) {
107 // LAMESPEC: 1.1 and 1.2alpha allow CreateDirectory on a file path.
108 // So CreateDirectory ("/tmp/somefile") will succeed if 'somefile' is
109 // not a directory. However, 1.0 will throw an exception.
110 // We behave like 1.0 here (emulating 1.1-like behavior is just a matter
111 // of comparing error to ERROR_FILE_EXISTS, but it's lame to do:
112 // DirectoryInfo di = Directory.CreateDirectory (something);
113 // and having di.Exists return false afterwards.
114 // I hope we don't break anyone's code, as they should be catching
115 // the exception anyway.
116 if (error != MonoIOError.ERROR_ALREADY_EXISTS &&
117 error != MonoIOError.ERROR_FILE_EXISTS)
118 throw MonoIO.GetException (path, error);
124 public static void Delete (string path)
126 Path.Validate (path);
128 if (Environment.IsRunningOnWindows && path == ":")
129 throw new NotSupportedException ("Only ':' In path");
131 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
136 if (MonoIO.ExistsSymlink (path, out error)) {
137 /* RemoveDirectory maps to rmdir()
138 * which fails on symlinks (ENOTDIR)
140 success = MonoIO.DeleteFile (path, out error);
142 success = MonoIO.RemoveDirectory (path, out error);
148 * In io-layer/io.c rmdir returns error_file_not_found if directory does not exists.
149 * So maybe this could be handled somewhere else?
151 if (error == MonoIOError.ERROR_FILE_NOT_FOUND) {
152 if (File.Exists (path))
153 throw new IOException ("Directory does not exist, but a file of the same name exists.");
155 throw new DirectoryNotFoundException ("Directory does not exist.");
157 throw MonoIO.GetException (path, error);
161 static void RecursiveDelete (string path)
165 foreach (string dir in GetDirectories (path)) {
166 if (MonoIO.ExistsSymlink (dir, out error)) {
167 MonoIO.DeleteFile (dir, out error);
169 RecursiveDelete (dir);
173 foreach (string file in GetFiles (path))
176 Directory.Delete (path);
179 public static void Delete (string path, bool recursive)
181 Path.Validate (path);
182 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
185 RecursiveDelete (path);
190 public static bool Exists (string path)
195 // on Moonlight this does not throw but returns false
196 if (!SecurityManager.CheckElevatedPermissions ())
202 exists = MonoIO.ExistsDirectory (path, out error);
203 /* This should not throw exceptions */
207 public static DateTime GetLastAccessTime (string path)
209 return File.GetLastAccessTime (path);
212 public static DateTime GetLastAccessTimeUtc (string path)
214 return GetLastAccessTime (path).ToUniversalTime ();
217 public static DateTime GetLastWriteTime (string path)
219 return File.GetLastWriteTime (path);
222 public static DateTime GetLastWriteTimeUtc (string path)
224 return GetLastWriteTime (path).ToUniversalTime ();
227 public static DateTime GetCreationTime (string path)
229 return File.GetCreationTime (path);
232 public static DateTime GetCreationTimeUtc (string path)
234 return GetCreationTime (path).ToUniversalTime ();
237 public static string GetCurrentDirectory ()
239 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
241 string result = InsecureGetCurrentDirectory();
243 if ((result != null) && (result.Length > 0) && SecurityManager.SecurityEnabled) {
244 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, result).Demand ();
250 internal static string InsecureGetCurrentDirectory()
253 string result = MonoIO.GetCurrentDirectory(out error);
255 if (error != MonoIOError.ERROR_SUCCESS)
256 throw MonoIO.GetException(error);
261 public static string [] GetDirectories (string path)
263 return GetDirectories (path, "*");
266 public static string [] GetDirectories (string path, string searchPattern)
268 return GetFileSystemEntries (path, searchPattern, FileAttributes.Directory, FileAttributes.Directory);
272 public static string [] GetDirectories (string path, string searchPattern, SearchOption searchOption)
274 if (searchOption == SearchOption.TopDirectoryOnly)
275 return GetDirectories (path, searchPattern);
276 var all = new List<string> ();
277 GetDirectoriesRecurse (path, searchPattern, all);
278 return all.ToArray ();
281 static void GetDirectoriesRecurse (string path, string searchPattern, List<string> all)
283 all.AddRange (GetDirectories (path, searchPattern));
284 foreach (string dir in GetDirectories (path))
285 GetDirectoriesRecurse (dir, searchPattern, all);
289 public static string GetDirectoryRoot (string path)
291 Path.Validate (path);
292 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
294 // FIXME nice hack but that does not work under windows
295 return new String(Path.DirectorySeparatorChar,1);
298 public static string [] GetFiles (string path)
300 return GetFiles (path, "*");
303 public static string [] GetFiles (string path, string searchPattern)
305 return GetFileSystemEntries (path, searchPattern, FileAttributes.Directory, 0);
309 public static string[] GetFiles (string path, string searchPattern, SearchOption searchOption)
311 if (searchOption == SearchOption.TopDirectoryOnly)
312 return GetFiles (path, searchPattern);
313 var all = new List<string> ();
314 GetFilesRecurse (path, searchPattern, all);
315 return all.ToArray ();
318 static void GetFilesRecurse (string path, string searchPattern, List<string> all)
320 all.AddRange (GetFiles (path, searchPattern));
321 foreach (string dir in GetDirectories (path))
322 GetFilesRecurse (dir, searchPattern, all);
326 public static string [] GetFileSystemEntries (string path)
328 return GetFileSystemEntries (path, "*");
331 public static string [] GetFileSystemEntries (string path, string searchPattern)
333 return GetFileSystemEntries (path, searchPattern, 0, 0);
336 public static string[] GetLogicalDrives ()
338 return Environment.GetLogicalDrives ();
341 static bool IsRootDirectory (string path)
344 if (Path.DirectorySeparatorChar == '/' && path == "/")
348 if (Path.DirectorySeparatorChar == '\\')
349 if (path.Length == 3 && path.EndsWith (":\\"))
355 public static DirectoryInfo GetParent (string path)
357 Path.Validate (path);
359 // return null if the path is the root directory
360 if (IsRootDirectory (path))
363 string parent_name = Path.GetDirectoryName (path);
364 if (parent_name.Length == 0)
365 parent_name = GetCurrentDirectory();
367 return new DirectoryInfo (parent_name);
370 public static void Move (string sourceDirName, string destDirName)
372 if (sourceDirName == null)
373 throw new ArgumentNullException ("sourceDirName");
375 if (destDirName == null)
376 throw new ArgumentNullException ("destDirName");
378 if (sourceDirName.Trim ().Length == 0 || sourceDirName.IndexOfAny (Path.InvalidPathChars) != -1)
379 throw new ArgumentException ("Invalid source directory name: " + sourceDirName, "sourceDirName");
381 if (destDirName.Trim ().Length == 0 || destDirName.IndexOfAny (Path.InvalidPathChars) != -1)
382 throw new ArgumentException ("Invalid target directory name: " + destDirName, "destDirName");
384 if (sourceDirName == destDirName)
385 throw new IOException ("Source and destination path must be different.");
387 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
389 if (Exists (destDirName))
390 throw new IOException (destDirName + " already exists.");
392 if (!Exists (sourceDirName) && !File.Exists (sourceDirName))
393 throw new DirectoryNotFoundException (sourceDirName + " does not exist");
396 if (!MonoIO.MoveFile (sourceDirName, destDirName, out error))
397 throw MonoIO.GetException (error);
401 public static void SetAccessControl (string path, DirectorySecurity directorySecurity)
403 if (null == directorySecurity)
404 throw new ArgumentNullException ("directorySecurity");
406 directorySecurity.PersistModifications (path);
410 public static void SetCreationTime (string path, DateTime creationTime)
412 File.SetCreationTime (path, creationTime);
415 public static void SetCreationTimeUtc (string path, DateTime creationTimeUtc)
417 SetCreationTime (path, creationTimeUtc.ToLocalTime ());
420 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
421 public static void SetCurrentDirectory (string path)
424 throw new ArgumentNullException ("path");
425 if (path.Trim ().Length == 0)
426 throw new ArgumentException ("path string must not be an empty string or whitespace string");
431 throw new DirectoryNotFoundException ("Directory \"" +
432 path + "\" not found.");
434 MonoIO.SetCurrentDirectory (path, out error);
435 if (error != MonoIOError.ERROR_SUCCESS)
436 throw MonoIO.GetException (path, error);
439 public static void SetLastAccessTime (string path, DateTime lastAccessTime)
441 File.SetLastAccessTime (path, lastAccessTime);
444 public static void SetLastAccessTimeUtc (string path, DateTime lastAccessTimeUtc)
446 SetLastAccessTime (path, lastAccessTimeUtc.ToLocalTime ());
449 public static void SetLastWriteTime (string path, DateTime lastWriteTime)
451 File.SetLastWriteTime (path, lastWriteTime);
454 public static void SetLastWriteTimeUtc (string path, DateTime lastWriteTimeUtc)
456 SetLastWriteTime (path, lastWriteTimeUtc.ToLocalTime ());
461 // Does the common validation, searchPattern has already been checked for not-null
462 static string ValidateDirectoryListing (string path, string searchPattern, out bool stop)
464 Path.Validate (path);
466 string wild = Path.Combine (path, searchPattern);
467 string wildpath = Path.GetDirectoryName (wild);
468 if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1)
469 throw new ArgumentException ("Pattern contains invalid characters", "pattern");
472 if (!MonoIO.ExistsDirectory (wildpath, out error)) {
473 if (error == MonoIOError.ERROR_SUCCESS) {
474 MonoIOError file_error;
475 if (MonoIO.ExistsFile (wildpath, out file_error))
476 throw new IOException ("The directory name is invalid.");
479 if (error != MonoIOError.ERROR_PATH_NOT_FOUND)
480 throw MonoIO.GetException (wildpath, error);
482 if (wildpath.IndexOfAny (SearchPattern.WildcardChars) == -1)
483 throw new DirectoryNotFoundException ("Directory '" + wildpath + "' not found.");
485 if (path.IndexOfAny (SearchPattern.WildcardChars) == -1)
486 throw new ArgumentException ("Pattern is invalid", "searchPattern");
488 throw new ArgumentException ("Path is invalid", "path");
495 private static string [] GetFileSystemEntries (string path, string searchPattern, FileAttributes mask, FileAttributes attrs)
497 if (searchPattern == null)
498 throw new ArgumentNullException ("searchPattern");
499 if (searchPattern.Length == 0)
500 return new string [] {};
502 string path_with_pattern = ValidateDirectoryListing (path, searchPattern, out stop);
504 return new string [] { path_with_pattern };
507 string [] result = MonoIO.GetFileSystemEntries (path, path_with_pattern, (int) attrs, (int) mask, out error);
509 throw MonoIO.GetException (Path.GetDirectoryName (Path.Combine (path, searchPattern)), error);
514 #if NET_4_0 || MOONLIGHT || MOBILE
515 public static string[] GetFileSystemEntries (string path, string searchPattern, SearchOption searchOption)
517 // Take the simple way home:
518 return new List<string> (EnumerateFileSystemEntries (path, searchPattern, searchOption)).ToArray ();
521 static void EnumerateCheck (string path, string searchPattern, SearchOption searchOption)
523 if (searchPattern == null)
524 throw new ArgumentNullException ("searchPattern");
526 if (searchPattern.Length == 0)
529 if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
530 throw new ArgumentOutOfRangeException ("searchoption");
532 Path.Validate (path);
533 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
536 internal static IEnumerable<string> EnumerateKind (string path, string searchPattern, SearchOption searchOption, FileAttributes kind)
538 if (searchPattern.Length == 0)
542 string path_with_pattern = ValidateDirectoryListing (path, searchPattern, out stop);
544 yield return path_with_pattern;
550 FileAttributes rattr;
552 string s = MonoIO.FindFirst (path, path_with_pattern, out rattr, out error, out handle);
555 // Convert any file specific flag to FileAttributes.Normal which is used as include files flag
556 if (((rattr & FileAttributes.Directory) == 0) && rattr != 0)
557 rattr |= FileAttributes.Normal;
559 if ((rattr & FileAttributes.ReparsePoint) == 0 && (rattr & kind) != 0)
562 s = MonoIO.FindNext (handle, out rattr, out error);
566 throw MonoIO.GetException (Path.GetDirectoryName (Path.Combine (path, searchPattern)), (MonoIOError) error);
568 if (handle != IntPtr.Zero)
569 MonoIO.FindClose (handle);
572 if (searchOption == SearchOption.AllDirectories) {
573 s = MonoIO.FindFirst (path, Path.Combine (path, "*"), out rattr, out error, out handle);
577 if ((rattr & FileAttributes.Directory) != 0)
578 foreach (string child in EnumerateKind (s, searchPattern, searchOption, kind))
580 s = MonoIO.FindNext (handle, out rattr, out error);
584 throw MonoIO.GetException (path, (MonoIOError) error);
586 if (handle != IntPtr.Zero)
587 MonoIO.FindClose (handle);
592 public static IEnumerable<string> EnumerateDirectories (string path, string searchPattern, SearchOption searchOption)
594 EnumerateCheck (path, searchPattern, searchOption);
595 return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Directory);
598 public static IEnumerable<string> EnumerateDirectories (string path, string searchPattern)
600 EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
601 return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Directory);
604 public static IEnumerable<string> EnumerateDirectories (string path)
606 Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
607 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
608 return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Directory);
611 public static IEnumerable<string> EnumerateFiles (string path, string searchPattern, SearchOption searchOption)
613 EnumerateCheck (path, searchPattern, searchOption);
614 return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Normal);
617 public static IEnumerable<string> EnumerateFiles (string path, string searchPattern)
619 EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
620 return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Normal);
623 public static IEnumerable<string> EnumerateFiles (string path)
625 Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
626 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
627 return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Normal);
630 public static IEnumerable<string> EnumerateFileSystemEntries (string path, string searchPattern, SearchOption searchOption)
632 EnumerateCheck (path, searchPattern, searchOption);
633 return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Normal | FileAttributes.Directory);
636 public static IEnumerable<string> EnumerateFileSystemEntries (string path, string searchPattern)
638 EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
639 return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Normal | FileAttributes.Directory);
642 public static IEnumerable<string> EnumerateFileSystemEntries (string path)
644 Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
645 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
646 return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Normal | FileAttributes.Directory);
652 public static DirectorySecurity GetAccessControl (string path, AccessControlSections includeSections)
654 return new DirectorySecurity (path, includeSections);
657 public static DirectorySecurity GetAccessControl (string path)
659 // AccessControlSections.Audit requires special permissions.
660 return GetAccessControl (path,
661 AccessControlSections.Owner |
662 AccessControlSections.Group |
663 AccessControlSections.Access);