// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== /*============================================================ ** ** Class: Directory ** ** ** Purpose: Exposes routines for enumerating through a ** directory. ** ** Date: March 5, 2000 ** April 11,2000 ** ===========================================================*/ using System; using System.Collections; using Microsoft.Singularity; using System.Text; using System.Runtime.InteropServices; using System.Globalization; using Microsoft.Singularity.Directory; namespace System.IO { //| public sealed class Directory { private Directory() { } //| public static DirectoryInfo GetParent(String path) { if (path==null) throw new ArgumentNullException("path"); if (path.Length==0) throw new ArgumentException("Argument_PathEmpty", "path"); String fullPath = Path.GetFullPathInternal(path); String s = Path.GetDirectoryName(fullPath); if (s==null) return null; return new DirectoryInfo(s); } //| public static DirectoryInfo CreateDirectory(String path) { if (path==null) throw new ArgumentNullException("path"); if (path.Length == 0) throw new ArgumentException("Argument_PathEmpty"); String fullPath = Path.GetFullPathInternal(path); // You need read access to the directory to be returned back and write access to all the directories // that you need to create. If we fail any security checks we will not create any directories at all. // We attempt to create directories only after all the security checks have passed. This is avoid doing // a demand at every level. InternalCreateDirectory(fullPath,path); return new DirectoryInfo(fullPath, false); } internal static void InternalCreateDirectory(String! fullPath, String path) { int length = fullPath.Length; // We need to trim the trailing slash or the code will try to create 2 directories of the same name. if (length >= 2 && Path.IsDirectorySeparator(fullPath[length - 1])) length--; int i = Path.GetRootLength(fullPath); // For UNC paths that are only // or /// if (length == 2 && Path.IsDirectorySeparator(fullPath[1])) throw new IOException(String.Format("IO.IO_CannotCreateDirectory",path)); ArrayList list = new ArrayList(); while (i < length) { i++; while (i < length && fullPath[i] != Path.DirectorySeparatorChar && fullPath[i] != Path.AltDirectorySeparatorChar) i++; String dir = fullPath.Substring(0, i); if (!InternalExists(dir)) { // Create only the ones missing list.Add(dir); } } if (list.Count != 0) { String [] securityList = (String[])list.ToArray(typeof(String)); for (int j = 0 ; j < securityList.Length; j++) securityList[j] += "\\."; // leafs will never has a slash at the end } // We need this check to mask OS differences // Handle CreateDirectory("X:\\") when X: doesn't exist. Similarly for n/w paths. String root = InternalGetDirectoryRoot(fullPath); if (!InternalExists(root)) { // Extract the root from the passed in path again for security. __Error.WinIOError(Native.ERROR_PATH_NOT_FOUND, InternalGetDirectoryRoot(path)); } bool r = true; int firstError = 0; // If all the security checks succeeded create all the directories for (int j = 0; j < list.Count; j++) { String name = (String!)list[j]; if (name.Length > Path.MAX_DIRECTORY_PATH) throw new PathTooLongException("IO.PathTooLong"); r = Native.CreateDirectory(name, 0); if (!r && (firstError == 0)) { firstError = Native.ERROR_PATH_EXISTS; } } if (!r && (firstError != 0)) { __Error.WinIOError(firstError, path); } } // Tests if the given path refers to an existing DirectoryInfo on disk. // // Your application must have Read permission to the directory's // contents. // //| public static bool Exists(String path) { try { if (path==null) return false; if (path.Length==0) return false; // Get fully qualified file name ending in \* for security check String fullPath = Path.GetFullPathInternal(path); return InternalExists(fullPath); } catch(ArgumentException) {} catch(NotSupportedException) {} // To deal with the fact that security can now throw this on : catch(IOException) {} return false; } // Determine whether path describes an existing directory // on disk, avoiding security checks. internal static bool InternalExists(String path) { Native.FILE_ATTRIBUTE_DATA data = new Native.FILE_ATTRIBUTE_DATA(); int dataInitialised = File.FillAttributeInfo(path,ref data); if (dataInitialised != 0) return false; return data.fileAttributes != -1 && (data.fileAttributes & Native.FILE_ATTRIBUTE_DIRECTORY) != 0; } //| public static void SetCreationTime(String! path, DateTime creationTime) { SetCreationTimeUtc(path, creationTime.ToUniversalTime()); } //| public static void SetCreationTimeUtc(String! path, DateTime creationTimeUtc) { IntPtr handle = Directory.OpenHandle(path); bool r = Native.SetFileTime(handle, new long[] {creationTimeUtc.ToFileTimeUtc()}, null, null); if (!r) { Native.CloseHandle(handle); __Error.WinIOError(1, path); } Native.CloseHandle(handle); } //| public static DateTime GetCreationTimeUtc(String! path) { return File.GetCreationTimeUtc(path); } //| public static void SetLastWriteTimeUtc(String! path, DateTime lastWriteTimeUtc) { IntPtr handle = Directory.OpenHandle(path); bool r = Native.SetFileTime(handle, null, null, new long[] {lastWriteTimeUtc.ToFileTimeUtc()}); if (!r) { Native.CloseHandle(handle); __Error.WinIOError(1, path); } Native.CloseHandle(handle); } //| public static DateTime GetLastWriteTimeUtc(String! path) { return File.GetLastWriteTimeUtc(path); } //| public static void SetLastAccessTimeUtc(String! path, DateTime lastAccessTimeUtc) { IntPtr handle = Directory.OpenHandle(path); bool r = Native.SetFileTime(handle, null, new long[] {lastAccessTimeUtc.ToFileTimeUtc()}, null); if (!r) { Native.CloseHandle(handle); __Error.WinIOError(1, path); } Native.CloseHandle(handle); } //| public static DateTime GetLastAccessTimeUtc(String! path) { return File.GetLastAccessTimeUtc(path); } // Returns an array of Files in the DirectoryInfo specified by path //| public static String[] GetFiles(String path) { return GetFiles(path,"*"); } // Returns an array of Files in the current DirectoryInfo matching the // given search criteria (ie, "*.txt"). //| public static String[] GetFiles(String path,String searchPattern) { if (path==null) throw new ArgumentNullException("path"); if (searchPattern==null) throw new ArgumentNullException("searchPattern"); searchPattern = searchPattern.TrimEnd(); if (searchPattern.Length == 0) return new String[0]; // Must fully qualify the path for the security check String fullPath = Path.GetFullPathInternal(path); String searchPath = Path.GetDirectoryName(searchPattern); if (searchPath != null && searchPath != String.Empty) // For filters like foo\*.cs we need to verify if the directory foo is not denied access. { path = Path.Combine(path,searchPath); // Need to add the searchPath to return correct path and for right security checks } // Note - fileNames returned by InternalGetFiles are not fully qualified. String [] fileNames = InternalGetFiles(fullPath, path, searchPattern); for(int i=0; i public static String[] GetDirectories(String path) { return GetDirectories(path,"*"); } // Returns an array of Directories in the current DirectoryInfo matching the // given search criteria (ie, "*.txt"). //| public static String[] GetDirectories(String path,String searchPattern) { if (path==null) throw new ArgumentNullException("path"); if (searchPattern==null) throw new ArgumentNullException("searchPattern"); searchPattern = searchPattern.TrimEnd(); if (searchPattern.Length == 0) return new String[0]; // Must fully qualify the path for the security check String fullPath = Path.GetFullPathInternal(path); String searchPath = Path.GetDirectoryName(searchPattern); if (searchPath != null && searchPath != String.Empty) // For filters like foo\*.cs we need to verify if the directory foo is not denied access. { path = Path.Combine(path,searchPath); // Need to add the searchPath to return correct path and for right security checks } String [] dirNames = InternalGetDirectories(fullPath, path, searchPattern); for(int i=0; i public static String[] GetFileSystemEntries(String path) { return GetFileSystemEntries(path,"*"); } // Returns an array of strongly typed FileSystemInfo entries in the path with the // given search criteria (ie, "*.txt"). We disallow .. as a part of the search criteria //| public static String[] GetFileSystemEntries(String path,String searchPattern) { if (path==null) throw new ArgumentNullException("path"); if (searchPattern==null) throw new ArgumentNullException("searchPattern"); searchPattern = searchPattern.TrimEnd(); if (searchPattern.Length == 0) return new String[0]; // Must fully qualify the path for the security check String fullPath = Path.GetFullPathInternal(path); String searchPath = Path.GetDirectoryName(searchPattern); if (searchPath != null && searchPath != String.Empty) // For filters like foo\*.cs we need to verify if the directory foo is not denied access. { path = Path.Combine(path,searchPath); // Need to add the searchPath to return correct path and for right security checks } String [] dirs = InternalGetDirectories(fullPath, path, searchPattern); String [] files = InternalGetFiles(fullPath, path, searchPattern); String [] fileSystemEntries = new String[dirs.Length + files.Length]; int count = 0; for (int i = 0;i public static String[] GetLogicalDrives() { int drives = Native.GetLogicalDrives(); if (drives==0) __Error.WinIOError(); uint d = (uint)drives; int count = 0; while (d != 0) { if (((int)d & 1) != 0) count++; d >>= 1; } String[] result = new String[count]; char[] root = new char[] {'A', ':', '\\'}; d = (uint)drives; count = 0; while (d != 0) { if (((int)d & 1) != 0) { result[count++] = new String(root); } d >>= 1; root[0]++; } return result; } //| public static String GetDirectoryRoot(String path) { if (path==null) throw new ArgumentNullException("path"); String fullPath = Path.GetFullPathInternal(path); return fullPath.Substring(0, Path.GetRootLength(fullPath)); } //| internal static String InternalGetDirectoryRoot(String path) { if (path == null) return null; return path.Substring(0, Path.GetRootLength(path)); } /*===============================CurrentDirectory=============================== **Action: Provides a getter and setter for the current directory. The original ** current DirectoryInfo is the one from which the process was started. **Returns: The current DirectoryInfo (from the getter). Void from the setter. **Arguments: The current DirectoryInfo to which to switch to the setter. **Exceptions: ==============================================================================*/ //| public static String GetCurrentDirectory() { StringBuilder sb = new StringBuilder(Path.MAX_PATH + 1); if (Native.GetCurrentDirectory(sb.Capacity, sb) == 0) System.IO.__Error.WinIOError(); String currentDirectory = sb.ToString(); return currentDirectory; } //| public static void SetCurrentDirectory(String path) { if (path==null) throw new ArgumentNullException("value"); if (path.Length==0) throw new ArgumentException("Argument_PathEmpty"); if (path.Length >= Path.MAX_PATH) throw new PathTooLongException("IO.PathTooLong"); // This will have some huge effects on the rest of the runtime // and every other application. Make sure app is highly trusted. String fulldestDirName = Path.GetFullPathInternal(path); // If path doesn't exist, this sets last error to 3 (Path not Found). if (!Native.SetCurrentDirectory(fulldestDirName)) System.IO.__Error.WinIOError(1, path); } //| public static void Move(String sourceDirName,String destDirName) { if (sourceDirName==null) throw new ArgumentNullException("sourceDirName"); if (sourceDirName.Length==0) throw new ArgumentException("Argument_EmptyFileName", "sourceDirName"); if (destDirName==null) throw new ArgumentNullException("destDirName"); if (destDirName.Length==0) throw new ArgumentException("Argument_EmptyFileName", "destDirName"); String fullsourceDirName = Path.GetFullPathInternal(sourceDirName); String fulldestDirName = Path.GetFullPathInternal(destDirName); String sourcePath; if (fullsourceDirName.EndsWith( '\\' )) sourcePath = fullsourceDirName; else sourcePath = fullsourceDirName + "\\"; String destPath; if (fulldestDirName.EndsWith( '\\' )) destPath = fulldestDirName; else destPath = fulldestDirName + "\\"; if (CompareInfo.Compare(sourcePath, destPath, CompareOptions.IgnoreCase) == 0) throw new IOException("IO.IO_SourceDestMustBeDifferent"); String sourceRoot = Path.GetPathRoot(sourcePath); String destinationRoot = Path.GetPathRoot(destPath); if (CompareInfo.Compare(sourceRoot, destinationRoot, CompareOptions.IgnoreCase) != 0) throw new IOException("IO.IO_SourceDestMustHaveSameRoot"); if (!Native.MoveFile(sourceDirName, destDirName)) { __Error.WinIOError(1,String.Empty); } } //| public static void Delete(String! path) { String fullPath = Path.GetFullPathInternal(path); Delete(fullPath, path, false); } //| public static void Delete(String! path, bool recursive) { String fullPath = Path.GetFullPathInternal(path); Delete(fullPath, path, recursive); } // Called from DirectoryInfo as well. FullPath is fully qualified, // while the user path is used for feedback in exceptions. internal static void Delete(String fullPath, String userPath, bool recursive) { DeleteHelper(fullPath, userPath, recursive); } // Note that fullPath is fully qualified, while userPath may be // relative. Use userPath for all exception messages to avoid leaking // fully qualified path information. private static void DeleteHelper(String fullPath, String userPath, bool recursive) { bool r; Exception ex = null; ErrorCode error; if (recursive) { Native.FIND_DATA data = new Native.FIND_DATA(); // Open a Find handle (Win32 is weird) IntPtr hnd = Native.FindFirstFile(fullPath+Path.DirectorySeparatorChar+"*", out data); if (hnd == IntPtr.Zero) { __Error.WinIOError(1, userPath); } do { assert data != null; // implied by FindFirst/NextFile and return status bool isDir = (0!=(data.dwFileAttributes & Native.FILE_ATTRIBUTE_DIRECTORY)); if (isDir) { if (data.cFileName.Equals(".") || data.cFileName.Equals("..")) continue; // recurse String newFullPath = Path.InternalCombine(fullPath, data.cFileName); String newUserPath = Path.InternalCombine(userPath, data.cFileName); try { DeleteHelper(newFullPath, newUserPath, recursive); } catch(Exception e) { if (ex == null) { ex = e; } } } else { String fileName = fullPath + Path.DirectorySeparatorChar + data.cFileName; r = Native.DeleteFile(fileName, out error); if (!r) { // __Error.WinIOError(1, data.cFileName); __Error.SingularityIOError(error,data.cFileName); } } } while (Native.FindNextFile(hnd, out data)); // Make sure we quit with a sensible error. Native.FindClose(hnd); // Close Find handle in all cases. if (ex != null) throw ex; } r = Native.RemoveDirectory(fullPath, out error); if (!r) //__Error.WinIOError(1, userPath); __Error.SingularityIOError(error,fullPath); } internal static void VerifyDriveExists(String! root) { int drives = Native.GetLogicalDrives(); if (drives==0) __Error.WinIOError(); uint d = (uint)drives; char drive = Char.ToLower(root[0]); if ((d & (1 << (drive - 'a'))) == 0) throw new DirectoryNotFoundException(String.Format("IO.DriveNotFound_Drive", root)); } private static IntPtr OpenHandle(String! path) { String fullPath = Path.GetFullPathInternal(path); String root = Path.GetPathRoot(fullPath); assert root != null; if (root == fullPath && root[1] == Path.VolumeSeparatorChar) throw new ArgumentException("Arg_PathIsVolume"); IntPtr handle = Native.CreateFile( fullPath, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_DELETE, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS ); if (handle == IntPtr.Zero) { __Error.WinIOError(1, path); } return handle; } private const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010; private const int GENERIC_WRITE = unchecked((int)0x40000000); private const int FILE_SHARE_WRITE = 0x00000002; private const int FILE_SHARE_DELETE = 0x00000004; private const int OPEN_EXISTING = 0x00000003; private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; } }