205fadc0026b852b25a17c83bdd4cf76aaff0239
[mono.git] / mcs / class / corlib / System.IO / DirectoryInfo.cs
1 // 
2 // System.IO.DirectoryInfo.cs 
3 //
4 // Authors:
5 //   Miguel de Icaza, miguel@ximian.com
6 //   Jim Richardson, develop@wtfo-guru.com
7 //   Dan Lewis, dihlewis@yahoo.co.uk
8 //   Sebastien Pouliot  <sebastien@ximian.com>
9 //   Marek Safar  <marek.safar@gmail.com>
10 //
11 // Copyright (C) 2002 Ximian, Inc.
12 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
13 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
14 // Copyright (C) 2014 Xamarin, Inc (http://www.xamarin.com)
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System.Collections;
37 using System.Collections.Generic;
38 using System.Runtime.InteropServices;
39 using System.Runtime.Serialization;
40 using System.Security;
41 using System.Text;
42 using System.Security.AccessControl;
43
44 namespace System.IO {
45         
46         [Serializable]
47         [ComVisible (true)]
48         public sealed class DirectoryInfo : FileSystemInfo {
49
50                 private string current;
51                 private string parent;
52         
53                 public DirectoryInfo (string path) : this (path, false)
54                 {
55                 }
56
57                 internal DirectoryInfo (string path, bool simpleOriginalPath)
58                 {
59                         CheckPath (path);
60
61                         SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
62
63                         FullPath = Path.GetFullPath (path);
64                         if (simpleOriginalPath)
65                                 OriginalPath = Path.GetFileName (FullPath);
66                         else
67                                 OriginalPath = path;
68
69                         Initialize ();
70                 }
71
72                 private DirectoryInfo (SerializationInfo info, StreamingContext context)
73                         : base (info, context)
74                 {
75                         Initialize ();
76                 }
77
78                 void Initialize ()
79                 {
80                         int len = FullPath.Length - 1;
81                         if ((len > 1) && (FullPath [len] == Path.DirectorySeparatorChar))
82                                 len--;
83                         int last = FullPath.LastIndexOf (Path.DirectorySeparatorChar, len);
84                         if ((last == -1) || ((last == 0) && (len == 0))) {
85                                 current = FullPath;
86                                 parent = null;
87                         } else {
88                                 current = FullPath.Substring (last + 1, len - last);
89                                 if (last == 0 && !Environment.IsRunningOnWindows)
90                                         parent = Path.DirectorySeparatorStr;
91                                 else
92                                         parent = FullPath.Substring (0, last);
93                                 // adjust for drives, i.e. a special case for windows
94                                 if (Environment.IsRunningOnWindows) {
95                                         if ((parent.Length == 2) && (parent [1] == ':') && Char.IsLetter (parent [0]))
96                                                 parent += Path.DirectorySeparatorChar;
97                                 }
98                         }
99                 }
100
101                 // properties
102
103                 public override bool Exists {
104                         get {
105                                 if (_dataInitialised == -1)
106                                         Refresh ();
107
108                                 if (_data.fileAttributes == MonoIO.InvalidFileAttributes)
109                                         return false;
110
111                                 if ((_data.fileAttributes & FileAttributes.Directory) == 0)
112                                         return false;
113
114                                 return true;
115                         }
116                 }
117
118                 public override string Name {
119                         get { return current; }
120                 }
121
122                 public DirectoryInfo Parent {
123                         get {
124                                 if ((parent == null) || (parent.Length == 0))
125                                         return null;
126                                 return new DirectoryInfo (parent);
127                         }
128                 }
129
130                 public DirectoryInfo Root {
131                         get {
132                                 string root = Path.GetPathRoot (FullPath);
133                                 if (root == null)
134                                         return null;
135
136                                 return new DirectoryInfo (root);
137                         }
138                 }
139
140                 // creational methods
141
142                 public void Create ()
143                 {
144                         Directory.CreateDirectory (FullPath);
145                 }
146
147                 public DirectoryInfo CreateSubdirectory (string path)
148                 {
149                         CheckPath (path);
150
151                         path = Path.Combine (FullPath, path);
152                         Directory.CreateDirectory (path);
153                         return new DirectoryInfo (path);
154                 }
155
156                 // directory listing methods
157
158                 public FileInfo [] GetFiles ()
159                 {
160                         return GetFiles ("*");
161                 }
162
163                 public FileInfo [] GetFiles (string searchPattern)
164                 {
165                         if (searchPattern == null)
166                                 throw new ArgumentNullException ("searchPattern");
167
168                         string [] names = Directory.GetFiles (FullPath, searchPattern);
169
170                         FileInfo[] infos = new FileInfo [names.Length];
171                         int i = 0;
172                         foreach (string name in names)
173                                 infos [i++] = new FileInfo (name);
174
175                         return infos;
176                 }
177
178                 public DirectoryInfo [] GetDirectories ()
179                 {
180                         return GetDirectories ("*");
181                 }
182
183                 public DirectoryInfo [] GetDirectories (string searchPattern)
184                 {
185                         if (searchPattern == null)
186                                 throw new ArgumentNullException ("searchPattern");
187
188                         string [] names = Directory.GetDirectories (FullPath, searchPattern);
189
190                         DirectoryInfo[] infos = new DirectoryInfo [names.Length];
191                         int i = 0;
192                         foreach (string name in names)
193                                 infos [i++] = new DirectoryInfo (name);
194
195                         return infos;
196                 }
197
198                 public FileSystemInfo [] GetFileSystemInfos ()
199                 {
200                         return GetFileSystemInfos ("*");
201                 }
202
203                 public FileSystemInfo [] GetFileSystemInfos (string searchPattern)
204                 {
205                         return GetFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
206                 }
207
208                 public
209                 FileSystemInfo [] GetFileSystemInfos (string searchPattern, SearchOption searchOption)
210                 {
211                         if (searchPattern == null)
212                                 throw new ArgumentNullException ("searchPattern");
213                         if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
214                                 throw new ArgumentOutOfRangeException ("searchOption", "Must be TopDirectoryOnly or AllDirectories");
215                         if (!Directory.Exists (FullPath))
216                                 throw new IOException ("Invalid directory");
217
218                         List<FileSystemInfo> infos = new List<FileSystemInfo> ();
219                         InternalGetFileSystemInfos (searchPattern, searchOption, infos);
220                         return infos.ToArray ();
221                 }
222
223                 void InternalGetFileSystemInfos (string searchPattern, SearchOption searchOption, List<FileSystemInfo> infos)
224                 {
225                         // UnauthorizedAccessExceptions might happen here and break everything for SearchOption.AllDirectories
226                         string [] dirs = Directory.GetDirectories (FullPath, searchPattern);
227                         string [] files = Directory.GetFiles (FullPath, searchPattern);
228
229                         Array.ForEach<string> (dirs, (dir) => { infos.Add (new DirectoryInfo (dir)); });
230                         Array.ForEach<string> (files, (file) => { infos.Add (new FileInfo (file)); });
231                         if (dirs.Length == 0 || searchOption == SearchOption.TopDirectoryOnly)
232                                 return;
233
234                         foreach (string dir in dirs) {
235                                 DirectoryInfo dinfo = new DirectoryInfo (dir);
236                                 dinfo.InternalGetFileSystemInfos (searchPattern, searchOption, infos);
237                         }
238                 }
239
240                 // directory management methods
241
242                 public override void Delete ()
243                 {
244                         Delete (false);
245                 }
246
247                 public void Delete (bool recursive)
248                 {
249                         Directory.Delete (FullPath, recursive);
250                 }
251
252                 public void MoveTo (string destDirName)
253                 {
254                         if (destDirName == null)
255                                 throw new ArgumentNullException ("destDirName");
256                         if (destDirName.Length == 0)
257                                 throw new ArgumentException ("An empty file name is not valid.", "destDirName");
258
259                         Directory.Move (FullPath, Path.GetFullPath (destDirName));
260                         FullPath = OriginalPath = destDirName;
261                         Initialize ();
262                 }
263
264                 public override string ToString ()
265                 {
266                         return OriginalPath;
267                 }
268
269                 public DirectoryInfo[] GetDirectories (string searchPattern, SearchOption searchOption)
270                 {
271                     //NULL-check of searchPattern is done in Directory.GetDirectories
272                         string [] names = Directory.GetDirectories (FullPath, searchPattern, searchOption);
273                         //Convert the names to DirectoryInfo instances
274                         DirectoryInfo[] infos = new DirectoryInfo [names.Length];
275                         for (int i = 0; i<names.Length; ++i){
276                                 string name = names[i];
277                                 infos [i] = new DirectoryInfo (name);
278                         }
279                         return infos;
280                 }       
281
282                 internal int GetFilesSubdirs (ArrayList l, string pattern)
283                 {
284                         int count;
285                         FileInfo [] thisdir = null;
286
287                         try {
288                                 thisdir = GetFiles (pattern);
289                         } catch (System.UnauthorizedAccessException){
290                                 return 0;
291                         }
292                         
293                         count = thisdir.Length;
294                         l.Add (thisdir);
295
296                         foreach (DirectoryInfo subdir in GetDirectories ()){
297                                 count += subdir.GetFilesSubdirs (l, pattern);
298                         }
299                         return count;
300                 }
301                 
302                 public FileInfo[] GetFiles (string searchPattern, SearchOption searchOption)
303                 {
304                         switch (searchOption) {
305                         case SearchOption.TopDirectoryOnly:
306                                 return GetFiles (searchPattern);
307                         case SearchOption.AllDirectories: {
308                                 ArrayList groups = new ArrayList ();
309                                 int count = GetFilesSubdirs (groups, searchPattern);
310                                 int current = 0;
311                                 
312                                 FileInfo [] all = new FileInfo [count];
313                                 foreach (FileInfo [] p in groups){
314                                         p.CopyTo (all, current);
315                                         current += p.Length;
316                                 }
317                                 return all;
318                         }
319                         default:
320                                 string msg = Locale.GetText ("Invalid enum value '{0}' for '{1}'.", searchOption, "SearchOption");
321                                 throw new ArgumentOutOfRangeException ("searchOption", msg);
322                         }
323                 }
324
325                 // access control methods
326
327                 [MonoLimitation ("DirectorySecurity isn't implemented")]
328                 public void Create (DirectorySecurity directorySecurity)
329                 {
330                         if (directorySecurity != null)
331                                 throw new UnauthorizedAccessException ();
332                         Create ();
333                 }
334
335                 [MonoLimitation ("DirectorySecurity isn't implemented")]
336                 public DirectoryInfo CreateSubdirectory (string path, DirectorySecurity directorySecurity)
337                 {
338                         if (directorySecurity != null)
339                                 throw new UnauthorizedAccessException ();
340                         return CreateSubdirectory (path);
341                 }
342
343                 public DirectorySecurity GetAccessControl ()
344                 {
345                         return Directory.GetAccessControl (FullPath);
346                 }
347
348                 public DirectorySecurity GetAccessControl (AccessControlSections includeSections)
349                 {
350                         return Directory.GetAccessControl (FullPath, includeSections);
351                 }
352
353                 public void SetAccessControl (DirectorySecurity directorySecurity)
354                 {
355                         Directory.SetAccessControl (FullPath, directorySecurity);
356                 }
357
358
359                 public IEnumerable<DirectoryInfo> EnumerateDirectories ()
360                 {
361                         return EnumerateDirectories ("*", SearchOption.TopDirectoryOnly);
362                 }
363
364                 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern)
365                 {
366                         return EnumerateDirectories (searchPattern, SearchOption.TopDirectoryOnly);
367                 }
368
369                 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern, SearchOption searchOption)
370                 {
371                         if (searchPattern == null)
372                                 throw new ArgumentNullException ("searchPattern");
373
374                         return CreateEnumerateDirectoriesIterator (searchPattern, searchOption);
375                 }
376
377                 IEnumerable<DirectoryInfo> CreateEnumerateDirectoriesIterator (string searchPattern, SearchOption searchOption)
378                 {
379                         foreach (string name in Directory.EnumerateDirectories (FullPath, searchPattern, searchOption))
380                                 yield return new DirectoryInfo (name);
381                 }
382
383                 public IEnumerable<FileInfo> EnumerateFiles ()
384                 {
385                         return EnumerateFiles ("*", SearchOption.TopDirectoryOnly);
386                 }
387
388                 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern)
389                 {
390                         return EnumerateFiles (searchPattern, SearchOption.TopDirectoryOnly);
391                 }
392
393                 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern, SearchOption searchOption)
394                 {
395                         if (searchPattern == null)
396                                 throw new ArgumentNullException ("searchPattern");
397
398                         return CreateEnumerateFilesIterator (searchPattern, searchOption);
399                 }
400
401                 IEnumerable<FileInfo> CreateEnumerateFilesIterator (string searchPattern, SearchOption searchOption)
402                 {
403                         foreach (string name in Directory.EnumerateFiles (FullPath, searchPattern, searchOption))
404                                 yield return new FileInfo (name);
405                 }
406
407                 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos ()
408                 {
409                         return EnumerateFileSystemInfos ("*", SearchOption.TopDirectoryOnly);
410                 }
411
412                 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern)
413                 {
414                         return EnumerateFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
415                 }
416
417                 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern, SearchOption searchOption)
418                 {
419                         if (searchPattern == null)
420                                 throw new ArgumentNullException ("searchPattern");
421                         if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
422                                 throw new ArgumentOutOfRangeException ("searchoption");
423
424                         return EnumerateFileSystemInfos (FullPath, searchPattern, searchOption);
425                 }
426
427                 static internal IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string full, string searchPattern, SearchOption searchOption)
428                 {
429                         string path_with_pattern = Path.Combine (full, searchPattern);
430                         IntPtr handle = IntPtr.Zero;
431                         MonoIOError error;
432                         FileAttributes rattr;
433                         bool subdirs = searchOption == SearchOption.AllDirectories;
434
435                         Path.Validate (full);
436                         
437                         try {
438                                 string s = MonoIO.FindFirst (full, path_with_pattern, out rattr, out error, out handle);
439                                 if (s == null)
440                                         yield break;
441                                 if (error != 0)
442                                         throw MonoIO.GetException (Path.GetDirectoryName (path_with_pattern), (MonoIOError) error);
443
444                                 do {
445                                         if (((rattr & FileAttributes.ReparsePoint) == 0)){
446                                                 if ((rattr & FileAttributes.Directory) != 0)
447                                                         yield return new DirectoryInfo (s);
448                                                 else
449                                                         yield return new FileInfo (s);
450                                         }
451
452                                         if (((rattr & FileAttributes.Directory) != 0) && subdirs)
453                                                 foreach (FileSystemInfo child in EnumerateFileSystemInfos (s, searchPattern, searchOption))
454                                                         yield return child;
455
456                                 } while ((s = MonoIO.FindNext (handle, out rattr, out error)) != null);
457                         } finally {
458                                 if (handle != IntPtr.Zero)
459                                         MonoIO.FindClose (handle);
460                         }
461                 }
462                 
463                 internal void CheckPath (string path)
464                 {
465                         if (path == null)
466                                 throw new ArgumentNullException ("path");
467                         if (path.Length == 0)
468                                 throw new ArgumentException ("An empty file name is not valid.");
469                         if (path.IndexOfAny (Path.InvalidPathChars) != -1)
470                                 throw new ArgumentException ("Illegal characters in path.");
471                         if (Environment.IsRunningOnWindows) {
472                                 int idx = path.IndexOf (':');
473                                 if (idx >= 0 && idx != 1)
474                                         throw new ArgumentException ("path");
475                         }
476                 }
477         }
478 }