2 // System.IO.DirectoryInfo.cs
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>
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)
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:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
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.
36 using System.Collections;
37 using System.Collections.Generic;
38 using System.Runtime.InteropServices;
39 using System.Runtime.Serialization;
40 using System.Security;
42 using System.Security.AccessControl;
48 public sealed class DirectoryInfo : FileSystemInfo {
50 private string current;
51 private string parent;
53 public DirectoryInfo (string path) : this (path, false)
57 internal DirectoryInfo (string path, bool simpleOriginalPath)
61 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
63 FullPath = Path.GetFullPath (path);
64 if (simpleOriginalPath)
65 OriginalPath = Path.GetFileName (FullPath);
72 private DirectoryInfo (SerializationInfo info, StreamingContext context)
73 : base (info, context)
80 int len = FullPath.Length - 1;
81 if ((len > 1) && (FullPath [len] == Path.DirectorySeparatorChar))
83 int last = FullPath.LastIndexOf (Path.DirectorySeparatorChar, len);
84 if ((last == -1) || ((last == 0) && (len == 0))) {
88 current = FullPath.Substring (last + 1, len - last);
89 if (last == 0 && !Environment.IsRunningOnWindows)
90 parent = Path.DirectorySeparatorStr;
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;
103 public override bool Exists {
105 if (_dataInitialised == -1)
108 if (_data.fileAttributes == MonoIO.InvalidFileAttributes)
111 if ((_data.fileAttributes & FileAttributes.Directory) == 0)
118 public override string Name {
119 get { return current; }
122 public DirectoryInfo Parent {
124 if ((parent == null) || (parent.Length == 0))
126 return new DirectoryInfo (parent);
130 public DirectoryInfo Root {
132 string root = Path.GetPathRoot (FullPath);
136 return new DirectoryInfo (root);
140 // creational methods
142 public void Create ()
144 Directory.CreateDirectory (FullPath);
147 public DirectoryInfo CreateSubdirectory (string path)
151 path = Path.Combine (FullPath, path);
152 Directory.CreateDirectory (path);
153 return new DirectoryInfo (path);
156 // directory listing methods
158 public FileInfo [] GetFiles ()
160 return GetFiles ("*");
163 public FileInfo [] GetFiles (string searchPattern)
165 if (searchPattern == null)
166 throw new ArgumentNullException ("searchPattern");
168 string [] names = Directory.GetFiles (FullPath, searchPattern);
170 FileInfo[] infos = new FileInfo [names.Length];
172 foreach (string name in names)
173 infos [i++] = new FileInfo (name);
178 public DirectoryInfo [] GetDirectories ()
180 return GetDirectories ("*");
183 public DirectoryInfo [] GetDirectories (string searchPattern)
185 if (searchPattern == null)
186 throw new ArgumentNullException ("searchPattern");
188 string [] names = Directory.GetDirectories (FullPath, searchPattern);
190 DirectoryInfo[] infos = new DirectoryInfo [names.Length];
192 foreach (string name in names)
193 infos [i++] = new DirectoryInfo (name);
198 public FileSystemInfo [] GetFileSystemInfos ()
200 return GetFileSystemInfos ("*");
203 public FileSystemInfo [] GetFileSystemInfos (string searchPattern)
205 return GetFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
209 FileSystemInfo [] GetFileSystemInfos (string searchPattern, SearchOption searchOption)
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");
218 List<FileSystemInfo> infos = new List<FileSystemInfo> ();
219 InternalGetFileSystemInfos (searchPattern, searchOption, infos);
220 return infos.ToArray ();
223 void InternalGetFileSystemInfos (string searchPattern, SearchOption searchOption, List<FileSystemInfo> infos)
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);
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)
234 foreach (string dir in dirs) {
235 DirectoryInfo dinfo = new DirectoryInfo (dir);
236 dinfo.InternalGetFileSystemInfos (searchPattern, searchOption, infos);
240 // directory management methods
242 public override void Delete ()
247 public void Delete (bool recursive)
249 Directory.Delete (FullPath, recursive);
252 public void MoveTo (string destDirName)
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");
259 Directory.Move (FullPath, Path.GetFullPath (destDirName));
260 FullPath = OriginalPath = destDirName;
264 public override string ToString ()
269 public DirectoryInfo[] GetDirectories (string searchPattern, SearchOption searchOption)
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);
282 internal int GetFilesSubdirs (ArrayList l, string pattern)
285 FileInfo [] thisdir = null;
288 thisdir = GetFiles (pattern);
289 } catch (System.UnauthorizedAccessException){
293 count = thisdir.Length;
296 foreach (DirectoryInfo subdir in GetDirectories ()){
297 count += subdir.GetFilesSubdirs (l, pattern);
302 public FileInfo[] GetFiles (string searchPattern, SearchOption searchOption)
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);
312 FileInfo [] all = new FileInfo [count];
313 foreach (FileInfo [] p in groups){
314 p.CopyTo (all, current);
320 string msg = Locale.GetText ("Invalid enum value '{0}' for '{1}'.", searchOption, "SearchOption");
321 throw new ArgumentOutOfRangeException ("searchOption", msg);
325 // access control methods
327 [MonoLimitation ("DirectorySecurity isn't implemented")]
328 public void Create (DirectorySecurity directorySecurity)
330 if (directorySecurity != null)
331 throw new UnauthorizedAccessException ();
335 [MonoLimitation ("DirectorySecurity isn't implemented")]
336 public DirectoryInfo CreateSubdirectory (string path, DirectorySecurity directorySecurity)
338 if (directorySecurity != null)
339 throw new UnauthorizedAccessException ();
340 return CreateSubdirectory (path);
343 public DirectorySecurity GetAccessControl ()
345 return Directory.GetAccessControl (FullPath);
348 public DirectorySecurity GetAccessControl (AccessControlSections includeSections)
350 return Directory.GetAccessControl (FullPath, includeSections);
353 public void SetAccessControl (DirectorySecurity directorySecurity)
355 Directory.SetAccessControl (FullPath, directorySecurity);
359 public IEnumerable<DirectoryInfo> EnumerateDirectories ()
361 return EnumerateDirectories ("*", SearchOption.TopDirectoryOnly);
364 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern)
366 return EnumerateDirectories (searchPattern, SearchOption.TopDirectoryOnly);
369 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern, SearchOption searchOption)
371 if (searchPattern == null)
372 throw new ArgumentNullException ("searchPattern");
374 return CreateEnumerateDirectoriesIterator (searchPattern, searchOption);
377 IEnumerable<DirectoryInfo> CreateEnumerateDirectoriesIterator (string searchPattern, SearchOption searchOption)
379 foreach (string name in Directory.EnumerateDirectories (FullPath, searchPattern, searchOption))
380 yield return new DirectoryInfo (name);
383 public IEnumerable<FileInfo> EnumerateFiles ()
385 return EnumerateFiles ("*", SearchOption.TopDirectoryOnly);
388 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern)
390 return EnumerateFiles (searchPattern, SearchOption.TopDirectoryOnly);
393 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern, SearchOption searchOption)
395 if (searchPattern == null)
396 throw new ArgumentNullException ("searchPattern");
398 return CreateEnumerateFilesIterator (searchPattern, searchOption);
401 IEnumerable<FileInfo> CreateEnumerateFilesIterator (string searchPattern, SearchOption searchOption)
403 foreach (string name in Directory.EnumerateFiles (FullPath, searchPattern, searchOption))
404 yield return new FileInfo (name);
407 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos ()
409 return EnumerateFileSystemInfos ("*", SearchOption.TopDirectoryOnly);
412 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern)
414 return EnumerateFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
417 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern, SearchOption searchOption)
419 if (searchPattern == null)
420 throw new ArgumentNullException ("searchPattern");
421 if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
422 throw new ArgumentOutOfRangeException ("searchoption");
424 return EnumerateFileSystemInfos (FullPath, searchPattern, searchOption);
427 static internal IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string full, string searchPattern, SearchOption searchOption)
429 string path_with_pattern = Path.Combine (full, searchPattern);
432 FileAttributes rattr;
433 bool subdirs = searchOption == SearchOption.AllDirectories;
435 Path.Validate (full);
437 string s = MonoIO.FindFirst (full, path_with_pattern, out rattr, out error, out handle);
441 throw MonoIO.GetException (Path.GetDirectoryName (path_with_pattern), (MonoIOError) error);
445 if (((rattr & FileAttributes.ReparsePoint) == 0)){
446 if ((rattr & FileAttributes.Directory) != 0)
447 yield return new DirectoryInfo (s);
449 yield return new FileInfo (s);
452 if (((rattr & FileAttributes.Directory) != 0) && subdirs)
453 foreach (FileSystemInfo child in EnumerateFileSystemInfos (s, searchPattern, searchOption))
456 } while ((s = MonoIO.FindNext (handle, out rattr, out error)) != null);
458 MonoIO.FindClose (handle);
462 internal void CheckPath (string path)
465 throw new ArgumentNullException ("path");
466 if (path.Length == 0)
467 throw new ArgumentException ("An empty file name is not valid.");
468 if (path.IndexOfAny (Path.InvalidPathChars) != -1)
469 throw new ArgumentException ("Illegal characters in path.");
470 if (Environment.IsRunningOnWindows) {
471 int idx = path.IndexOf (':');
472 if (idx >= 0 && idx != 1)
473 throw new ArgumentException ("path");