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