//
using System.Collections;
+using System.Collections.Generic;
using System.Security;
using System.Security.Permissions;
using System.Text;
+using System.Runtime.InteropServices;
+
+#if !MOONLIGHT
+using System.Security.AccessControl;
+#endif
namespace System.IO
{
- public
-#if NET_2_0
- static
-#else
- sealed
-#endif
- class Directory
+ [ComVisible (true)]
+ public static class Directory
{
-#if !NET_2_0
- private Directory () {}
-#endif
-
public static DirectoryInfo CreateDirectory (string path)
{
if (path == null)
throw new ArgumentNullException ("path");
- if (path == "")
+ if (path.Length == 0)
throw new ArgumentException ("Path is empty");
if (path.IndexOfAny (Path.InvalidPathChars) != -1)
if (path.Trim ().Length == 0)
throw new ArgumentException ("Only blank characters in path");
+
+ // after validations but before File.Exists to avoid an oracle
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
+ if (File.Exists(path))
+ throw new IOException ("Cannot create " + path + " because a file with the same name already exists.");
// LAMESPEC: with .net 1.0 version this throw NotSupportedException and msdn says so too
// but v1.1 throws ArgumentException.
- if (path == ":")
+ if (Environment.IsRunningOnWindows && path == ":")
throw new ArgumentException ("Only ':' In path");
return CreateDirectoriesInternal (path);
}
+#if !MOONLIGHT
+ [MonoLimitation ("DirectorySecurity not implemented")]
+ public static DirectoryInfo CreateDirectory (string path, DirectorySecurity directorySecurity)
+ {
+ return(CreateDirectory (path));
+ }
+#endif
+
static DirectoryInfo CreateDirectoriesInternal (string path)
{
+#if !MOONLIGHT
if (SecurityManager.SecurityEnabled) {
new FileIOPermission (FileIOPermissionAccess.Read | FileIOPermissionAccess.Write, path).Demand ();
}
-
- DirectoryInfo info = new DirectoryInfo (path);
+#endif
+ DirectoryInfo info = new DirectoryInfo (path, true);
if (info.Parent != null && !info.Parent.Exists)
info.Parent.Create ();
// and having di.Exists return false afterwards.
// I hope we don't break anyone's code, as they should be catching
// the exception anyway.
- if (error != MonoIOError.ERROR_ALREADY_EXISTS)
+ if (error != MonoIOError.ERROR_ALREADY_EXISTS &&
+ error != MonoIOError.ERROR_FILE_EXISTS)
throw MonoIO.GetException (path, error);
}
public static void Delete (string path)
{
- if (path == null)
- throw new ArgumentNullException ("path");
-
- if (path == "")
- throw new ArgumentException ("Path is empty");
-
- if (path.IndexOfAny (Path.InvalidPathChars) != -1)
- throw new ArgumentException ("Path contains invalid chars");
-
- if (path.Trim().Length == 0)
- throw new ArgumentException ("Only blank characters in path");
+ Path.Validate (path);
- if (path == ":")
+ if (Environment.IsRunningOnWindows && path == ":")
throw new NotSupportedException ("Only ':' In path");
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
MonoIOError error;
+ bool success;
+
+ if (MonoIO.ExistsSymlink (path, out error)) {
+ /* RemoveDirectory maps to rmdir()
+ * which fails on symlinks (ENOTDIR)
+ */
+ success = MonoIO.DeleteFile (path, out error);
+ } else {
+ success = MonoIO.RemoveDirectory (path, out error);
+ }
- if (!MonoIO.RemoveDirectory (path, out error)) {
+ if (!success) {
/*
* FIXME:
* In io-layer/io.c rmdir returns error_file_not_found if directory does not exists.
* So maybe this could be handled somewhere else?
*/
- if (error == MonoIOError.ERROR_FILE_NOT_FOUND)
- throw new DirectoryNotFoundException ("Directory '" + path + "' doesnt exists.");
- else
+ if (error == MonoIOError.ERROR_FILE_NOT_FOUND) {
+ if (File.Exists (path))
+ throw new IOException ("Directory does not exist, but a file of the same name exists.");
+ else
+ throw new DirectoryNotFoundException ("Directory does not exist.");
+ } else
throw MonoIO.GetException (path, error);
}
}
static void RecursiveDelete (string path)
{
- foreach (string dir in GetDirectories (path))
- RecursiveDelete (dir);
+ MonoIOError error;
+
+ foreach (string dir in GetDirectories (path)) {
+ if (MonoIO.ExistsSymlink (dir, out error)) {
+ MonoIO.DeleteFile (dir, out error);
+ } else {
+ RecursiveDelete (dir);
+ }
+ }
foreach (string file in GetFiles (path))
File.Delete (file);
Directory.Delete (path);
}
- public static void Delete (string path, bool recurse)
+ public static void Delete (string path, bool recursive)
{
- CheckPathExceptions (path);
-
- if (recurse == false){
- Delete (path);
- return;
- }
+ Path.Validate (path);
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
- RecursiveDelete (path);
+ if (recursive)
+ RecursiveDelete (path);
+ else
+ Delete (path);
}
public static bool Exists (string path)
{
if (path == null)
return false;
+
+ // on Moonlight this does not throw but returns false
+ if (!SecurityManager.CheckElevatedPermissions ())
+ return false;
MonoIOError error;
bool exists;
exists = MonoIO.ExistsDirectory (path, out error);
- if (error != MonoIOError.ERROR_SUCCESS &&
- error != MonoIOError.ERROR_PATH_NOT_FOUND &&
- error != MonoIOError.ERROR_ACCESS_DENIED) {
- throw MonoIO.GetException (path, error);
- }
-
- return(exists);
+ /* This should not throw exceptions */
+ return exists;
}
public static DateTime GetLastAccessTime (string path)
{
return File.GetLastAccessTime (path);
}
-
+
public static DateTime GetLastAccessTimeUtc (string path)
{
return GetLastAccessTime (path).ToUniversalTime ();
}
-
+
public static DateTime GetLastWriteTime (string path)
{
return File.GetLastWriteTime (path);
public static string GetCurrentDirectory ()
{
MonoIOError error;
+
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
string result = MonoIO.GetCurrentDirectory (out error);
if (error != MonoIOError.ERROR_SUCCESS)
throw MonoIO.GetException (error);
-
+#if !MOONLIGHT
if ((result != null) && (result.Length > 0) && SecurityManager.SecurityEnabled) {
new FileIOPermission (FileIOPermissionAccess.PathDiscovery, result).Demand ();
}
+#endif
return result;
}
return GetDirectories (path, "*");
}
- public static string [] GetDirectories (string path, string pattern)
+ public static string [] GetDirectories (string path, string searchPattern)
{
- return GetFileSystemEntries (path, pattern, FileAttributes.Directory, FileAttributes.Directory);
+ return GetFileSystemEntries (path, searchPattern, FileAttributes.Directory, FileAttributes.Directory);
}
+#if !MOONLIGHT
+ public static string [] GetDirectories (string path, string searchPattern, SearchOption searchOption)
+ {
+ if (searchOption == SearchOption.TopDirectoryOnly)
+ return GetDirectories (path, searchPattern);
+ ArrayList all = new ArrayList ();
+ GetDirectoriesRecurse (path, searchPattern, all);
+ return (string []) all.ToArray (typeof (string));
+ }
+
+ static void GetDirectoriesRecurse (string path, string searchPattern, ArrayList all)
+ {
+ all.AddRange (GetDirectories (path, searchPattern));
+ foreach (string dir in GetDirectories (path))
+ GetDirectoriesRecurse (dir, searchPattern, all);
+ }
+#endif
+
public static string GetDirectoryRoot (string path)
{
+ Path.Validate (path);
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
+ // FIXME nice hack but that does not work under windows
return new String(Path.DirectorySeparatorChar,1);
}
return GetFiles (path, "*");
}
- public static string [] GetFiles (string path, string pattern)
+ public static string [] GetFiles (string path, string searchPattern)
{
- return GetFileSystemEntries (path, pattern, FileAttributes.Directory, 0);
+ return GetFileSystemEntries (path, searchPattern, FileAttributes.Directory, 0);
}
-#if NET_2_0
- [MonoTODO]
+#if !MOONLIGHT
public static string[] GetFiles (string path, string searchPattern, SearchOption searchOption)
{
- throw new NotImplementedException ();
+ if (searchOption == SearchOption.TopDirectoryOnly)
+ return GetFiles (path, searchPattern);
+ ArrayList all = new ArrayList ();
+ GetFilesRecurse (path, searchPattern, all);
+ return (string []) all.ToArray (typeof (string));
+ }
+
+ static void GetFilesRecurse (string path, string searchPattern, ArrayList all)
+ {
+ all.AddRange (GetFiles (path, searchPattern));
+ foreach (string dir in GetDirectories (path))
+ GetFilesRecurse (dir, searchPattern, all);
}
#endif
return GetFileSystemEntries (path, "*");
}
- public static string [] GetFileSystemEntries (string path, string pattern)
+ public static string [] GetFileSystemEntries (string path, string searchPattern)
{
- return GetFileSystemEntries (path, pattern, 0, 0);
+ return GetFileSystemEntries (path, searchPattern, 0, 0);
}
public static string[] GetLogicalDrives ()
static bool IsRootDirectory (string path)
{
// Unix
- if (Path.DirectorySeparatorChar == '/' && path == "/")
- return true;
+ if (Path.DirectorySeparatorChar == '/' && path == "/")
+ return true;
- // Windows
- if (Path.DirectorySeparatorChar == '\\')
- if (path.Length == 3 && path.EndsWith (":\\"))
- return true;
+ // Windows
+ if (Path.DirectorySeparatorChar == '\\')
+ if (path.Length == 3 && path.EndsWith (":\\"))
+ return true;
- return false;
+ return false;
}
public static DirectoryInfo GetParent (string path)
{
- if (path == null)
- throw new ArgumentNullException ();
- if (path.IndexOfAny (Path.InvalidPathChars) != -1)
- throw new ArgumentException ("Path contains invalid characters");
- if (path == "")
- throw new ArgumentException ("The Path do not have a valid format");
+ Path.Validate (path);
// return null if the path is the root directory
if (IsRootDirectory (path))
return null;
string parent_name = Path.GetDirectoryName (path);
- if (parent_name == "")
+ if (parent_name.Length == 0)
parent_name = GetCurrentDirectory();
return new DirectoryInfo (parent_name);
}
- public static void Move (string src, string dest)
+ public static void Move (string sourceDirName, string destDirName)
{
- if (src == null)
- throw new ArgumentNullException ("src");
+ if (sourceDirName == null)
+ throw new ArgumentNullException ("sourceDirName");
+
+ if (destDirName == null)
+ throw new ArgumentNullException ("destDirName");
- if (dest == null)
- throw new ArgumentNullException ("dest");
+ if (sourceDirName.Trim ().Length == 0 || sourceDirName.IndexOfAny (Path.InvalidPathChars) != -1)
+ throw new ArgumentException ("Invalid source directory name: " + sourceDirName, "sourceDirName");
- if (src.Trim () == "" || src.IndexOfAny (Path.InvalidPathChars) != -1)
- throw new ArgumentException ("Invalid source directory name: " + src, "src");
+ if (destDirName.Trim ().Length == 0 || destDirName.IndexOfAny (Path.InvalidPathChars) != -1)
+ throw new ArgumentException ("Invalid target directory name: " + destDirName, "destDirName");
- if (dest.Trim () == "" || dest.IndexOfAny (Path.InvalidPathChars) != -1)
- throw new ArgumentException ("Invalid target directory name: " + dest, "dest");
+ if (sourceDirName == destDirName)
+ throw new IOException ("Source and destination path must be different.");
- if (src == dest)
- throw new IOException ("Source directory cannot be same as a target directory.");
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
- if (Exists (dest))
- throw new IOException (dest + " already exists.");
+ if (Exists (destDirName))
+ throw new IOException (destDirName + " already exists.");
- if (!Exists (src))
- throw new DirectoryNotFoundException (src + " does not exist");
+ if (!Exists (sourceDirName) && !File.Exists (sourceDirName))
+ throw new DirectoryNotFoundException (sourceDirName + " does not exist");
MonoIOError error;
- if (!MonoIO.MoveFile (src, dest, out error))
+ if (!MonoIO.MoveFile (sourceDirName, destDirName, out error))
throw MonoIO.GetException (error);
}
- public static void SetCreationTime (string path, DateTime creation_time)
+#if !MOONLIGHT
+ public static void SetAccessControl (string path, DirectorySecurity directorySecurity)
{
- File.SetCreationTime (path, creation_time);
+ throw new NotImplementedException ();
}
+#endif
- public static void SetCreationTimeUtc (string path, DateTime creation_time)
+ public static void SetCreationTime (string path, DateTime creationTime)
{
- SetCreationTime (path, creation_time.ToLocalTime ());
+ File.SetCreationTime (path, creationTime);
+ }
+
+ public static void SetCreationTimeUtc (string path, DateTime creationTimeUtc)
+ {
+ SetCreationTime (path, creationTimeUtc.ToLocalTime ());
}
[SecurityPermission (SecurityAction.Demand, UnmanagedCode = true)]
{
if (path == null)
throw new ArgumentNullException ("path");
- if (path.Trim () == String.Empty)
+ if (path.Trim ().Length == 0)
throw new ArgumentException ("path string must not be an empty string or whitespace string");
MonoIOError error;
throw MonoIO.GetException (path, error);
}
- public static void SetLastAccessTime (string path, DateTime last_access_time)
+ public static void SetLastAccessTime (string path, DateTime lastAccessTime)
{
- File.SetLastAccessTime (path, last_access_time);
+ File.SetLastAccessTime (path, lastAccessTime);
}
- public static void SetLastAccessTimeUtc (string path, DateTime last_access_time)
+ public static void SetLastAccessTimeUtc (string path, DateTime lastAccessTimeUtc)
{
- SetLastAccessTime (path, last_access_time.ToLocalTime ());
+ SetLastAccessTime (path, lastAccessTimeUtc.ToLocalTime ());
}
- public static void SetLastWriteTime (string path, DateTime last_write_time)
+ public static void SetLastWriteTime (string path, DateTime lastWriteTime)
{
- File.SetLastWriteTime (path, last_write_time);
+ File.SetLastWriteTime (path, lastWriteTime);
}
- public static void SetLastWriteTimeUtc (string path, DateTime last_write_time)
+ public static void SetLastWriteTimeUtc (string path, DateTime lastWriteTimeUtc)
{
- SetLastWriteTime (path, last_write_time.ToLocalTime ());
+ SetLastWriteTime (path, lastWriteTimeUtc.ToLocalTime ());
}
// private
- private static void CheckPathExceptions (string path)
+ // Does the common validation, searchPattern has already been checked for not-null
+ static string ValidateDirectoryListing (string path, string searchPattern, out bool stop)
{
- if (path == null)
- throw new System.ArgumentNullException("Path is Null");
- if (path == "")
- throw new System.ArgumentException("Path is Empty");
- if (path.Trim().Length == 0)
- throw new ArgumentException ("Only blank characters in path");
- if (path.IndexOfAny (Path.InvalidPathChars) != -1)
- throw new ArgumentException ("Path contains invalid chars");
- }
+ Path.Validate (path);
- private static string [] GetFileSystemEntries (string path, string pattern, FileAttributes mask, FileAttributes attrs)
- {
- if (path == null || pattern == null)
- throw new ArgumentNullException ();
-
- if (pattern == String.Empty)
- return new string [] {};
-
- if (path.Trim () == "")
- throw new ArgumentException ("The Path does not have a valid format");
-
- string wild = Path.Combine (path, pattern);
+ string wild = Path.Combine (path, searchPattern);
string wildpath = Path.GetDirectoryName (wild);
if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1)
- throw new ArgumentException ("Path contains invalid characters");
-
- if (wildpath.IndexOfAny (Path.InvalidPathChars) != -1) {
- if (path.IndexOfAny (SearchPattern.InvalidChars) == -1)
- throw new ArgumentException ("Path contains invalid characters", "path");
-
throw new ArgumentException ("Pattern contains invalid characters", "pattern");
- }
MonoIOError error;
if (!MonoIO.ExistsDirectory (wildpath, out error)) {
if (error == MonoIOError.ERROR_SUCCESS) {
MonoIOError file_error;
if (MonoIO.ExistsFile (wildpath, out file_error)) {
- return new string [] { wildpath };
+ stop = true;
+ return wildpath;
}
}
throw new DirectoryNotFoundException ("Directory '" + wildpath + "' not found.");
if (path.IndexOfAny (SearchPattern.WildcardChars) == -1)
- throw new ArgumentException ("Pattern is invalid", "pattern");
+ throw new ArgumentException ("Pattern is invalid", "searchPattern");
throw new ArgumentException ("Path is invalid", "path");
}
- string [] result = MonoIO.GetFileSystemEntries (wildpath, pattern, (int) attrs, (int) mask, out error);
- if (error != 0)
- throw MonoIO.GetException (wildpath, error);
+ stop = false;
+ return Path.Combine (wildpath, searchPattern);
+ }
+
+ private static string [] GetFileSystemEntries (string path, string searchPattern, FileAttributes mask, FileAttributes attrs)
+ {
+ if (searchPattern == null)
+ throw new ArgumentNullException ("searchPattern");
+ if (searchPattern.Length == 0)
+ return new string [] {};
+ bool stop;
+ string path_with_pattern = ValidateDirectoryListing (path, searchPattern, out stop);
+ if (stop)
+ return new string [] { path_with_pattern };
+ MonoIOError error;
+ string [] result = MonoIO.GetFileSystemEntries (path, path_with_pattern, (int) attrs, (int) mask, out error);
+ if (error != 0)
+ throw MonoIO.GetException (Path.GetDirectoryName (Path.Combine (path, searchPattern)), error);
+
return result;
}
+
+#if NET_4_0 || MOONLIGHT
+ public static string[] GetFileSystemEntries (string path, string searchPattern, SearchOption searchOption)
+ {
+ // Take the simple way home:
+ return new List<string> (EnumerateFileSystemEntries (path, searchPattern, searchOption)).ToArray ();
+ }
+
+ static void EnumerateCheck (string path, string searchPattern, SearchOption searchOption)
+ {
+ if (searchPattern == null)
+ throw new ArgumentNullException ("searchPattern");
+
+ if (searchPattern.Length == 0)
+ return;
+
+ if (searchOption != SearchOption.TopDirectoryOnly && searchOption != SearchOption.AllDirectories)
+ throw new ArgumentOutOfRangeException ("searchoption");
+
+ Path.Validate (path);
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+ }
+
+ internal static IEnumerable<string> EnumerateKind (string path, string searchPattern, SearchOption searchOption, FileAttributes kind)
+ {
+ if (searchPattern.Length == 0)
+ yield break;
+
+ bool stop;
+ string path_with_pattern = ValidateDirectoryListing (path, searchPattern, out stop);
+ if (stop){
+ yield return path_with_pattern;
+ yield break;
+ }
+
+ IntPtr handle;
+ MonoIOError error;
+ FileAttributes rattr;
+ bool subdirs = searchOption == SearchOption.AllDirectories;
+
+ string s = MonoIO.FindFirst (path, path_with_pattern, out rattr, out error, out handle);
+ if (s == null)
+ yield break;
+ if (error != 0)
+ throw MonoIO.GetException (Path.GetDirectoryName (Path.Combine (path, searchPattern)), (MonoIOError) error);
+
+ try {
+ if (((rattr & FileAttributes.ReparsePoint) == 0) && ((rattr & kind) != 0))
+ yield return s;
+
+ while ((s = MonoIO.FindNext (handle, out rattr, out error)) != null){
+ if ((rattr & FileAttributes.ReparsePoint) != 0)
+ continue;
+ if ((rattr & kind) != 0)
+ yield return s;
+
+ if (((rattr & FileAttributes.Directory) != 0) && subdirs)
+ foreach (string child in EnumerateKind (s, searchPattern, searchOption, kind))
+ yield return child;
+ }
+ } finally {
+ MonoIO.FindClose (handle);
+ }
+ }
+
+ public static IEnumerable<string> EnumerateDirectories (string path, string searchPattern, SearchOption searchOption)
+ {
+ EnumerateCheck (path, searchPattern, searchOption);
+ return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Directory);
+ }
+
+ public static IEnumerable<string> EnumerateDirectories (string path, string searchPattern)
+ {
+ EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
+ return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Directory);
+ }
+
+ public static IEnumerable<string> EnumerateDirectories (string path)
+ {
+ Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+ return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Directory);
+ }
+
+ public static IEnumerable<string> EnumerateFiles (string path, string searchPattern, SearchOption searchOption)
+ {
+ EnumerateCheck (path, searchPattern, searchOption);
+ return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Normal);
+ }
+
+ public static IEnumerable<string> EnumerateFiles (string path, string searchPattern)
+ {
+ EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
+ return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Normal);
+ }
+
+ public static IEnumerable<string> EnumerateFiles (string path)
+ {
+ Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+ return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Normal);
+ }
+
+ public static IEnumerable<string> EnumerateFileSystemEntries (string path, string searchPattern, SearchOption searchOption)
+ {
+ EnumerateCheck (path, searchPattern, searchOption);
+ return EnumerateKind (path, searchPattern, searchOption, FileAttributes.Normal | FileAttributes.Directory);
+ }
+
+ public static IEnumerable<string> EnumerateFileSystemEntries (string path, string searchPattern)
+ {
+ EnumerateCheck (path, searchPattern, SearchOption.TopDirectoryOnly);
+ return EnumerateKind (path, searchPattern, SearchOption.TopDirectoryOnly, FileAttributes.Normal | FileAttributes.Directory);
+ }
+
+ public static IEnumerable<string> EnumerateFileSystemEntries (string path)
+ {
+ Path.Validate (path); // no need for EnumerateCheck since we supply valid arguments
+ SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+ return EnumerateKind (path, "*", SearchOption.TopDirectoryOnly, FileAttributes.Normal | FileAttributes.Directory);
+ }
+
+#endif
+
+#if !MOONLIGHT
+ [MonoNotSupported ("DirectorySecurity isn't implemented")]
+ public static DirectorySecurity GetAccessControl (string path, AccessControlSections includeSections)
+ {
+ throw new PlatformNotSupportedException ();
+ }
+
+ [MonoNotSupported ("DirectorySecurity isn't implemented")]
+ public static DirectorySecurity GetAccessControl (string path)
+ {
+ throw new PlatformNotSupportedException ();
+ }
+#endif
}
}
-