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;
44 using Microsoft.Win32.SafeHandles;
50 public sealed class DirectoryInfo : FileSystemInfo {
52 private string current;
53 private string parent;
55 public DirectoryInfo (string path) : this (path, false)
59 internal DirectoryInfo (string path, bool simpleOriginalPath)
63 SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
65 FullPath = Path.GetFullPath (path);
66 if (simpleOriginalPath)
67 OriginalPath = Path.GetFileName (FullPath);
74 private DirectoryInfo (SerializationInfo info, StreamingContext context)
75 : base (info, context)
82 int len = FullPath.Length - 1;
83 if ((len > 1) && (FullPath [len] == Path.DirectorySeparatorChar))
85 int last = FullPath.LastIndexOf (Path.DirectorySeparatorChar, len);
86 if ((last == -1) || ((last == 0) && (len == 0))) {
90 current = FullPath.Substring (last + 1, len - last);
91 if (last == 0 && !Environment.IsRunningOnWindows)
92 parent = Path.DirectorySeparatorStr;
94 parent = FullPath.Substring (0, last);
95 // adjust for drives, i.e. a special case for windows
96 if (Environment.IsRunningOnWindows) {
97 if ((parent.Length == 2) && (parent [1] == ':') && Char.IsLetter (parent [0]))
98 parent += Path.DirectorySeparatorChar;
105 public override bool Exists {
107 if (_dataInitialised == -1)
110 if (_data.fileAttributes == MonoIO.InvalidFileAttributes)
113 if ((_data.fileAttributes & FileAttributes.Directory) == 0)
120 public override string Name {
121 get { return current; }
124 public DirectoryInfo Parent {
126 if ((parent == null) || (parent.Length == 0))
128 return new DirectoryInfo (parent);
132 public DirectoryInfo Root {
134 string root = Path.GetPathRoot (FullPath);
138 return new DirectoryInfo (root);
142 // creational methods
144 public void Create ()
146 Directory.CreateDirectory (FullPath);
149 public DirectoryInfo CreateSubdirectory (string path)
153 path = Path.Combine (FullPath, path);
154 Directory.CreateDirectory (path);
155 return new DirectoryInfo (path);
158 // directory listing methods
160 public FileInfo [] GetFiles ()
162 return GetFiles ("*");
165 public FileInfo [] GetFiles (string searchPattern)
167 if (searchPattern == null)
168 throw new ArgumentNullException ("searchPattern");
170 string [] names = Directory.GetFiles (FullPath, searchPattern);
172 FileInfo[] infos = new FileInfo [names.Length];
174 foreach (string name in names)
175 infos [i++] = new FileInfo (name);
180 public DirectoryInfo [] GetDirectories ()
182 return GetDirectories ("*");
185 public DirectoryInfo [] GetDirectories (string searchPattern)
187 if (searchPattern == null)
188 throw new ArgumentNullException ("searchPattern");
190 string [] names = Directory.GetDirectories (FullPath, searchPattern);
192 DirectoryInfo[] infos = new DirectoryInfo [names.Length];
194 foreach (string name in names)
195 infos [i++] = new DirectoryInfo (name);
200 public FileSystemInfo [] GetFileSystemInfos ()
202 return GetFileSystemInfos ("*");
205 public FileSystemInfo [] GetFileSystemInfos (string searchPattern)
207 return GetFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
211 FileSystemInfo [] GetFileSystemInfos (string searchPattern, SearchOption searchOption)
213 if (searchPattern == null)
214 throw new ArgumentNullException ("searchPattern");
215 if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
216 throw new ArgumentOutOfRangeException ("searchOption", "Must be TopDirectoryOnly or AllDirectories");
217 if (!Directory.Exists (FullPath))
218 throw new IOException ("Invalid directory");
220 List<FileSystemInfo> infos = new List<FileSystemInfo> ();
221 InternalGetFileSystemInfos (searchPattern, searchOption, infos);
222 return infos.ToArray ();
225 void InternalGetFileSystemInfos (string searchPattern, SearchOption searchOption, List<FileSystemInfo> infos)
227 // UnauthorizedAccessExceptions might happen here and break everything for SearchOption.AllDirectories
228 string [] dirs = Directory.GetDirectories (FullPath, searchPattern);
229 string [] files = Directory.GetFiles (FullPath, searchPattern);
231 Array.ForEach<string> (dirs, (dir) => { infos.Add (new DirectoryInfo (dir)); });
232 Array.ForEach<string> (files, (file) => { infos.Add (new FileInfo (file)); });
233 if (dirs.Length == 0 || searchOption == SearchOption.TopDirectoryOnly)
236 foreach (string dir in dirs) {
237 DirectoryInfo dinfo = new DirectoryInfo (dir);
238 dinfo.InternalGetFileSystemInfos (searchPattern, searchOption, infos);
242 // directory management methods
244 public override void Delete ()
249 public void Delete (bool recursive)
251 Directory.Delete (FullPath, recursive);
254 public void MoveTo (string destDirName)
256 if (destDirName == null)
257 throw new ArgumentNullException ("destDirName");
258 if (destDirName.Length == 0)
259 throw new ArgumentException ("An empty file name is not valid.", "destDirName");
261 Directory.Move (FullPath, Path.GetFullPath (destDirName));
262 FullPath = OriginalPath = destDirName;
266 public override string ToString ()
271 public DirectoryInfo[] GetDirectories (string searchPattern, SearchOption searchOption)
273 //NULL-check of searchPattern is done in Directory.GetDirectories
274 string [] names = Directory.GetDirectories (FullPath, searchPattern, searchOption);
275 //Convert the names to DirectoryInfo instances
276 DirectoryInfo[] infos = new DirectoryInfo [names.Length];
277 for (int i = 0; i<names.Length; ++i){
278 string name = names[i];
279 infos [i] = new DirectoryInfo (name);
284 internal int GetFilesSubdirs (ArrayList l, string pattern)
287 FileInfo [] thisdir = null;
290 thisdir = GetFiles (pattern);
291 } catch (System.UnauthorizedAccessException){
295 count = thisdir.Length;
298 foreach (DirectoryInfo subdir in GetDirectories ()){
299 count += subdir.GetFilesSubdirs (l, pattern);
304 public FileInfo[] GetFiles (string searchPattern, SearchOption searchOption)
306 switch (searchOption) {
307 case SearchOption.TopDirectoryOnly:
308 return GetFiles (searchPattern);
309 case SearchOption.AllDirectories: {
310 ArrayList groups = new ArrayList ();
311 int count = GetFilesSubdirs (groups, searchPattern);
314 FileInfo [] all = new FileInfo [count];
315 foreach (FileInfo [] p in groups){
316 p.CopyTo (all, current);
322 string msg = Locale.GetText ("Invalid enum value '{0}' for '{1}'.", searchOption, "SearchOption");
323 throw new ArgumentOutOfRangeException ("searchOption", msg);
327 // access control methods
329 [MonoLimitation ("DirectorySecurity isn't implemented")]
330 public void Create (DirectorySecurity directorySecurity)
332 if (directorySecurity != null)
333 throw new UnauthorizedAccessException ();
337 [MonoLimitation ("DirectorySecurity isn't implemented")]
338 public DirectoryInfo CreateSubdirectory (string path, DirectorySecurity directorySecurity)
340 if (directorySecurity != null)
341 throw new UnauthorizedAccessException ();
342 return CreateSubdirectory (path);
345 public DirectorySecurity GetAccessControl ()
347 return Directory.GetAccessControl (FullPath);
350 public DirectorySecurity GetAccessControl (AccessControlSections includeSections)
352 return Directory.GetAccessControl (FullPath, includeSections);
355 public void SetAccessControl (DirectorySecurity directorySecurity)
357 Directory.SetAccessControl (FullPath, directorySecurity);
361 public IEnumerable<DirectoryInfo> EnumerateDirectories ()
363 return EnumerateDirectories ("*", SearchOption.TopDirectoryOnly);
366 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern)
368 return EnumerateDirectories (searchPattern, SearchOption.TopDirectoryOnly);
371 public IEnumerable<DirectoryInfo> EnumerateDirectories (string searchPattern, SearchOption searchOption)
373 if (searchPattern == null)
374 throw new ArgumentNullException ("searchPattern");
376 return CreateEnumerateDirectoriesIterator (searchPattern, searchOption);
379 IEnumerable<DirectoryInfo> CreateEnumerateDirectoriesIterator (string searchPattern, SearchOption searchOption)
381 foreach (string name in Directory.EnumerateDirectories (FullPath, searchPattern, searchOption))
382 yield return new DirectoryInfo (name);
385 public IEnumerable<FileInfo> EnumerateFiles ()
387 return EnumerateFiles ("*", SearchOption.TopDirectoryOnly);
390 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern)
392 return EnumerateFiles (searchPattern, SearchOption.TopDirectoryOnly);
395 public IEnumerable<FileInfo> EnumerateFiles (string searchPattern, SearchOption searchOption)
397 if (searchPattern == null)
398 throw new ArgumentNullException ("searchPattern");
400 return CreateEnumerateFilesIterator (searchPattern, searchOption);
403 IEnumerable<FileInfo> CreateEnumerateFilesIterator (string searchPattern, SearchOption searchOption)
405 foreach (string name in Directory.EnumerateFiles (FullPath, searchPattern, searchOption))
406 yield return new FileInfo (name);
409 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos ()
411 return EnumerateFileSystemInfos ("*", SearchOption.TopDirectoryOnly);
414 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern)
416 return EnumerateFileSystemInfos (searchPattern, SearchOption.TopDirectoryOnly);
419 public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string searchPattern, SearchOption searchOption)
421 if (searchPattern == null)
422 throw new ArgumentNullException ("searchPattern");
423 if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
424 throw new ArgumentOutOfRangeException ("searchoption");
426 return EnumerateFileSystemInfos (FullPath, searchPattern, searchOption);
429 static internal IEnumerable<FileSystemInfo> EnumerateFileSystemInfos (string basePath, string searchPattern, SearchOption searchOption)
431 Path.Validate (basePath);
433 SafeFindHandle findHandle = null;
439 string basePathWithPattern = Path.Combine (basePath, searchPattern);
443 findHandle = new SafeFindHandle (MonoIO.FindFirstFile (basePathWithPattern, out filePath, out nativeAttrs, out nativeError));
446 if (findHandle.IsInvalid) {
447 MonoIOError error = (MonoIOError) nativeError;
448 if (error != MonoIOError.ERROR_FILE_NOT_FOUND)
449 throw MonoIO.GetException (Path.GetDirectoryName (basePathWithPattern), error);
455 if (filePath == null)
458 if (filePath == "." || filePath == "..")
461 FileAttributes attrs = (FileAttributes) nativeAttrs;
463 string fullPath = Path.Combine (basePath, filePath);
465 if ((attrs & FileAttributes.ReparsePoint) == 0) {
466 if ((attrs & FileAttributes.Directory) != 0)
467 yield return new DirectoryInfo (fullPath);
469 yield return new FileInfo (fullPath);
472 if ((attrs & FileAttributes.Directory) != 0 && searchOption == SearchOption.AllDirectories) {
473 foreach (FileSystemInfo child in EnumerateFileSystemInfos (fullPath, searchPattern, searchOption))
476 } while (MonoIO.FindNextFile (findHandle.DangerousGetHandle (), out filePath, out nativeAttrs, out int _));
478 if (findHandle != null)
479 findHandle.Dispose ();
483 internal void CheckPath (string path)
486 throw new ArgumentNullException ("path");
487 if (path.Length == 0)
488 throw new ArgumentException ("An empty file name is not valid.");
489 if (path.IndexOfAny (Path.InvalidPathChars) != -1)
490 throw new ArgumentException ("Illegal characters in path.");
491 if (Environment.IsRunningOnWindows) {
492 int idx = path.IndexOf (':');
493 if (idx >= 0 && idx != 1)
494 throw new ArgumentException ("path");