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