b5d16977ae5651afb705e6a7968598630fe4cdc7
[mono.git] / mcs / class / corlib / System.IO / Directory.cs
1 // 
2 // System.IO.Directory.cs 
3 //
4 // Authors:
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)
10 //
11 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
12 // Copyright (C) 2002 Ximian, Inc.
13 // 
14 // Created:        Monday, August 13, 2001 
15 //
16 //------------------------------------------------------------------------------
17
18 //
19 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
20 //
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:
28 // 
29 // The above copyright notice and this permission notice shall be
30 // included in all copies or substantial portions of the Software.
31 // 
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.
39 //
40
41 using System.Collections;
42 using System.Security;
43 using System.Security.Permissions;
44 using System.Text;
45 #if NET_2_0
46 using System.Security.AccessControl;
47 #endif
48
49 namespace System.IO
50 {
51         public
52 #if NET_2_0
53         static
54 #else
55         sealed
56 #endif
57         class Directory
58         {
59
60 #if !NET_2_0
61                 private Directory () {}
62 #endif
63                 
64                 public static DirectoryInfo CreateDirectory (string path)
65                 {
66                         if (path == null)
67                                 throw new ArgumentNullException ("path");
68                         
69                         path = path.TrimEnd ();
70                         
71                         if (path == "")
72                                 throw new ArgumentException ("Path is empty");
73                         
74                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
75                                 throw new ArgumentException ("Path contains invalid chars");
76
77 #if NET_2_0
78                         if (File.Exists(path))
79                                 throw new IOException ("Cannot create " + path + " because a file with the same name already exists.");
80 #endif
81                         
82                         // LAMESPEC: with .net 1.0 version this throw NotSupportedException and msdn says so too
83                         // but v1.1 throws ArgumentException.
84                         if (path == ":")
85                                 throw new ArgumentException ("Only ':' In path");
86                         
87                         return CreateDirectoriesInternal (path);
88                 }
89
90                 static DirectoryInfo CreateDirectoriesInternal (string path)
91                 {
92                         if (SecurityManager.SecurityEnabled) {
93                                 new FileIOPermission (FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, path).Demand ();
94                         }
95
96                         DirectoryInfo info = new DirectoryInfo (path);
97                         if (info.Parent != null && !info.Parent.Exists)
98                                  info.Parent.Create ();
99
100                         path = Path.GetFullPath (path);
101                         
102                         MonoIOError error;
103                         if (!MonoIO.CreateDirectory (path, out error)) {
104                                 // LAMESPEC: 1.1 and 1.2alpha allow CreateDirectory on a file path.
105                                 // So CreateDirectory ("/tmp/somefile") will succeed if 'somefile' is
106                                 // not a directory. However, 1.0 will throw an exception.
107                                 // We behave like 1.0 here (emulating 1.1-like behavior is just a matter
108                                 // of comparing error to ERROR_FILE_EXISTS, but it's lame to do:
109                                 //    DirectoryInfo di = Directory.CreateDirectory (something);
110                                 // and having di.Exists return false afterwards.
111                                 // I hope we don't break anyone's code, as they should be catching
112                                 // the exception anyway.
113                                 if (error != MonoIOError.ERROR_ALREADY_EXISTS &&
114                                     error != MonoIOError.ERROR_FILE_EXISTS)
115                                         throw MonoIO.GetException (path, error);
116                         }
117
118                         return info;
119                 }
120                 
121                 public static void Delete (string path)
122                 {
123                         if (path == null)
124                                 throw new ArgumentNullException ("path");
125                         
126                         if (path == "")
127                                 throw new ArgumentException ("Path is empty");
128                         
129                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
130                                 throw new ArgumentException ("Path contains invalid chars");
131
132                         if (path.Trim().Length == 0)
133                                 throw new ArgumentException ("Only blank characters in path");
134
135                         if (path == ":")
136                                 throw new NotSupportedException ("Only ':' In path");
137
138                         MonoIOError error;
139                         bool success;
140                         
141                         if (MonoIO.ExistsSymlink (path, out error)) {
142                                 /* RemoveDirectory maps to rmdir()
143                                  * which fails on symlinks (ENOTDIR)
144                                  */
145                                 success = MonoIO.DeleteFile (path, out error);
146                         } else {
147                                 success = MonoIO.RemoveDirectory (path,
148                                                                   out error);
149                         }
150                         
151                         if (!success) {
152                                 /*
153                                  * FIXME:
154                                  * In io-layer/io.c rmdir returns error_file_not_found if directory does not exists.
155                                  * So maybe this could be handled somewhere else?
156                                  */
157                                 if (error == MonoIOError.ERROR_FILE_NOT_FOUND) 
158                                         throw new DirectoryNotFoundException ("Directory '" + path + "' doesnt exists.");
159                                 else
160                                         throw MonoIO.GetException (path, error);
161                         }
162                 }
163
164                 static void RecursiveDelete (string path)
165                 {
166                         MonoIOError error;
167                         
168                         foreach (string dir in GetDirectories (path)) {
169                                 if (MonoIO.ExistsSymlink (dir, out error)) {
170                                         MonoIO.DeleteFile (dir, out error);
171                                 } else {
172                                         RecursiveDelete (dir);
173                                 }
174                         }
175
176                         foreach (string file in GetFiles (path))
177                                 File.Delete (file);
178
179                         Directory.Delete (path);
180                 }
181                 
182                 public static void Delete (string path, bool recurse)
183                 {
184                         CheckPathExceptions (path);
185                         
186                         if (recurse == false){
187                                 Delete (path);
188                                 return;
189                         }
190
191                         RecursiveDelete (path);
192                 }
193
194                 public static bool Exists (string path)
195                 {
196                         if (path == null || path.Trim ().Length == 0)
197                                 return false;
198                                 
199                         MonoIOError error;
200                         bool exists;
201                         
202                         exists = MonoIO.ExistsDirectory (path, out error);
203                         if (error != MonoIOError.ERROR_SUCCESS &&
204                             error != MonoIOError.ERROR_PATH_NOT_FOUND &&
205                             error != MonoIOError.ERROR_INVALID_HANDLE &&
206                             error != MonoIOError.ERROR_ACCESS_DENIED) {
207
208                                 // INVALID_HANDLE might happen if the file is moved
209                                 // while testing for the existence, a kernel issue
210                                 // according to Larry Ewing.
211                                 
212                                 throw MonoIO.GetException (path, error);
213                         }
214
215                         return(exists);
216                 }
217
218                 public static DateTime GetLastAccessTime (string path)
219                 {
220                         return File.GetLastAccessTime (path);
221                 }
222                 
223                 public static DateTime GetLastAccessTimeUtc (string path)
224                 {
225                         return GetLastAccessTime (path).ToUniversalTime ();
226                 }
227                       
228                 public static DateTime GetLastWriteTime (string path)
229                 {
230                         return File.GetLastWriteTime (path);
231                 }
232                 
233                 public static DateTime GetLastWriteTimeUtc (string path)
234                 {
235                         return GetLastWriteTime (path).ToUniversalTime ();
236                 }
237
238                 public static DateTime GetCreationTime (string path)
239                 {
240                         return File.GetCreationTime (path);
241                 }
242
243                 public static DateTime GetCreationTimeUtc (string path)
244                 {
245                         return GetCreationTime (path).ToUniversalTime ();
246                 }
247
248                 public static string GetCurrentDirectory ()
249                 {
250                         MonoIOError error;
251                                 
252                         string result = MonoIO.GetCurrentDirectory (out error);
253                         if (error != MonoIOError.ERROR_SUCCESS)
254                                 throw MonoIO.GetException (error);
255
256                         if ((result != null) && (result.Length > 0) && SecurityManager.SecurityEnabled) {
257                                 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, result).Demand ();
258                         }
259                         return result;
260                 }
261                 
262                 public static string [] GetDirectories (string path)
263                 {
264                         return GetDirectories (path, "*");
265                 }
266                 
267                 public static string [] GetDirectories (string path, string pattern)
268                 {
269                         return GetFileSystemEntries (path, pattern, FileAttributes.Directory, FileAttributes.Directory);
270                 }
271                 
272 #if NET_2_0
273                 public static string [] GetDirectories (string path, string pattern, SearchOption option)
274                 {
275                         if (option == SearchOption.TopDirectoryOnly)
276                                 return GetDirectories (path, pattern);
277                         ArrayList all = new ArrayList ();
278                         GetDirectoriesRecurse (path, pattern, all);
279                         return (string []) all.ToArray (typeof (string));
280                 }
281                 
282                 static void GetDirectoriesRecurse (string path, string pattern, ArrayList all)
283                 {
284                         all.AddRange (GetDirectories (path, pattern));
285                         foreach (string dir in GetDirectories (path))
286                                 GetDirectoriesRecurse (dir, pattern, all);
287                 }
288 #endif
289
290                 public static string GetDirectoryRoot (string path)
291                 {
292                         return new String(Path.DirectorySeparatorChar,1);
293                 }
294                 
295                 public static string [] GetFiles (string path)
296                 {
297                         return GetFiles (path, "*");
298                 }
299                 
300                 public static string [] GetFiles (string path, string pattern)
301                 {
302                         return GetFileSystemEntries (path, pattern, FileAttributes.Directory, 0);
303                 }
304
305 #if NET_2_0
306                 public static string[] GetFiles (string path, string searchPattern, SearchOption searchOption)
307                 {
308                         if (searchOption == SearchOption.TopDirectoryOnly)
309                                 return GetFiles (path, searchPattern);
310                         ArrayList all = new ArrayList ();
311                         GetFilesRecurse (path, searchPattern, all);
312                         return (string []) all.ToArray (typeof (string));
313                 }
314                 
315                 static void GetFilesRecurse (string path, string pattern, ArrayList all)
316                 {
317                         all.AddRange (GetFiles (path, pattern));
318                         foreach (string dir in GetDirectories (path))
319                                 GetFilesRecurse (dir, pattern, all);
320                 }
321 #endif
322
323                 public static string [] GetFileSystemEntries (string path)
324                 {
325                         return GetFileSystemEntries (path, "*");
326                 }
327
328                 public static string [] GetFileSystemEntries (string path, string pattern)
329                 {
330                         return GetFileSystemEntries (path, pattern, 0, 0);
331                 }
332                 
333                 public static string[] GetLogicalDrives ()
334                 { 
335                         return Environment.GetLogicalDrives ();
336                 }
337
338                 static bool IsRootDirectory (string path)
339                 {
340                         // Unix
341                        if (Path.DirectorySeparatorChar == '/' && path == "/")
342                                return true;
343
344                        // Windows
345                        if (Path.DirectorySeparatorChar == '\\')
346                                if (path.Length == 3 && path.EndsWith (":\\"))
347                                        return true;
348
349                        return false;
350                 }
351
352                 public static DirectoryInfo GetParent (string path)
353                 {
354                         if (path == null)
355                                 throw new ArgumentNullException ();
356                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
357                                 throw new ArgumentException ("Path contains invalid characters");
358                         if (path == "")
359                                 throw new ArgumentException ("The Path do not have a valid format");
360
361                         // return null if the path is the root directory
362                         if (IsRootDirectory (path))
363                                 return null;
364
365                         string parent_name = Path.GetDirectoryName (path);
366                         if (parent_name == "")
367                                 parent_name = GetCurrentDirectory();
368
369                         return new DirectoryInfo (parent_name);
370                 }
371
372                 public static void Move (string src, string dest)
373                 {
374                         if (src == null)
375                                 throw new ArgumentNullException ("src");
376
377                         if (dest == null)
378                                 throw new ArgumentNullException ("dest");
379
380                         if (src.Trim () == "" || src.IndexOfAny (Path.InvalidPathChars) != -1)
381                                 throw new ArgumentException ("Invalid source directory name: " + src, "src");
382
383                         if (dest.Trim () == "" || dest.IndexOfAny (Path.InvalidPathChars) != -1)
384                                 throw new ArgumentException ("Invalid target directory name: " + dest, "dest");
385
386                         if (src == dest)
387                                 throw new IOException ("Source directory cannot be same as a target directory.");
388
389                         if (Exists (dest))
390                                 throw new IOException (dest + " already exists.");
391
392                         if (!Exists (src))
393                                 throw new DirectoryNotFoundException (src + " does not exist");
394
395                         MonoIOError error;
396                         if (!MonoIO.MoveFile (src, dest, out error))
397                                 throw MonoIO.GetException (error);
398                 }
399
400                 public static void SetCreationTime (string path, DateTime creation_time)
401                 {
402                         File.SetCreationTime (path, creation_time);
403                 }
404
405                 public static void SetCreationTimeUtc (string path, DateTime creation_time)
406                 {
407                         SetCreationTime (path, creation_time.ToLocalTime ());
408                 }
409
410                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
411                 public static void SetCurrentDirectory (string path)
412                 {
413                         if (path == null)
414                                 throw new ArgumentNullException ("path");
415                         if (path.Trim () == String.Empty)
416                                 throw new ArgumentException ("path string must not be an empty string or whitespace string");
417
418                         MonoIOError error;
419                                 
420                         if (!Exists (path))
421                                 throw new DirectoryNotFoundException ("Directory \"" +
422                                                                         path + "\" not found.");
423
424                         MonoIO.SetCurrentDirectory (path, out error);
425                         if (error != MonoIOError.ERROR_SUCCESS)
426                                 throw MonoIO.GetException (path, error);
427                 }
428
429                 public static void SetLastAccessTime (string path, DateTime last_access_time)
430                 {
431                         File.SetLastAccessTime (path, last_access_time);
432                 }
433
434                 public static void SetLastAccessTimeUtc (string path, DateTime last_access_time)
435                 {
436                         SetLastAccessTime (path, last_access_time.ToLocalTime ());
437                 }
438
439                 public static void SetLastWriteTime (string path, DateTime last_write_time)
440                 {
441                         File.SetLastWriteTime (path, last_write_time);
442                 }
443
444                 public static void SetLastWriteTimeUtc (string path, DateTime last_write_time)
445                 {
446                         SetLastWriteTime (path, last_write_time.ToLocalTime ());
447                 }
448
449                 // private
450                 
451                 private static void CheckPathExceptions (string path)
452                 {
453                         if (path == null)
454                                 throw new System.ArgumentNullException("Path is Null");
455                         if (path == "")
456                                 throw new System.ArgumentException("Path is Empty");
457                         if (path.Trim().Length == 0)
458                                 throw new ArgumentException ("Only blank characters in path");
459                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
460                                 throw new ArgumentException ("Path contains invalid chars");
461                 }
462
463                 private static string [] GetFileSystemEntries (string path, string pattern, FileAttributes mask, FileAttributes attrs)
464                 {
465                         if (path == null || pattern == null)
466                                 throw new ArgumentNullException ();
467
468                         if (pattern == String.Empty)
469                                 return new string [] {};
470                         
471                         if (path.Trim () == "")
472                                 throw new ArgumentException ("The Path does not have a valid format");
473
474                         string wild = Path.Combine (path, pattern);
475                         string wildpath = Path.GetDirectoryName (wild);
476                         if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1)
477                                 throw new ArgumentException ("Path contains invalid characters");
478
479                         if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1) {
480                                 if (path.IndexOfAny (SearchPattern.InvalidChars) == -1)
481                                         throw new ArgumentException ("Path contains invalid characters", "path");
482
483                                 throw new ArgumentException ("Pattern contains invalid characters", "pattern");
484                         }
485
486                         MonoIOError error;
487                         if (!MonoIO.ExistsDirectory (wildpath, out error)) {
488                                 if (error == MonoIOError.ERROR_SUCCESS) {
489                                         MonoIOError file_error;
490                                         if (MonoIO.ExistsFile (wildpath, out file_error)) {
491                                                 return new string [] { wildpath };
492                                         }
493                                 }
494
495                                 if (error != MonoIOError.ERROR_PATH_NOT_FOUND)
496                                         throw MonoIO.GetException (wildpath, error);
497
498                                 if (wildpath.IndexOfAny (SearchPattern.WildcardChars) == -1)
499                                         throw new DirectoryNotFoundException ("Directory '" + wildpath + "' not found.");
500
501                                 if (path.IndexOfAny (SearchPattern.WildcardChars) == -1)
502                                         throw new ArgumentException ("Pattern is invalid", "pattern");
503
504                                 throw new ArgumentException ("Path is invalid", "path");
505                         }
506
507                         string [] result = MonoIO.GetFileSystemEntries (wildpath, pattern, (int) attrs, (int) mask, out error);
508                         if (error != 0)
509                                 throw MonoIO.GetException (wildpath, error);
510
511                         return result;
512                 }
513
514 #if NET_2_0
515                 [MonoLimitation ("Mono always throws PlatformNotSupported regardless of the platform")]
516                 public static DirectorySecurity GetAccessControl (string path, AccessControlSections includeSections)
517                 {
518                         throw new PlatformNotSupportedException ();
519                 }
520
521                 [MonoLimitation ("Mono always throws PlatformNotSupported regardless of the platform")]
522                 public static DirectorySecurity GetAccessControl (string path)
523                 {
524                         throw new PlatformNotSupportedException ();
525                 }
526 #endif
527         }
528 }
529