The Labs \ Source Viewer \ SSCLI \ System.IO \ DirectoryInfo

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  DirectoryInfo
  18. **
  19. **
  20. ** Purpose: Exposes routines for enumerating through a
  21. ** directory.
  22. **
  23. **          April 11,2000
  24. **
  25. ===========================================================*/
  26. using System;
  27. using System.Collections;
  28. using System.Security;
  29. using System.Security.Permissions;
  30. using Microsoft.Win32;
  31. using System.Text;
  32. using System.Runtime.InteropServices;
  33. using System.Globalization;
  34. using System.Runtime.Serialization;
  35. using System.Runtime.Versioning;
  36. namespace System.IO
  37. {
  38.     [Serializable()]
  39.     [ComVisible(true)]
  40.     public sealed class DirectoryInfo : FileSystemInfo
  41.     {
  42.         private string[] demandDir;
  43.        
  44.         [ResourceExposure(ResourceScope.Machine)]
  45.         [ResourceConsumption(ResourceScope.Machine)]
  46.         public DirectoryInfo(string path)
  47.         {
  48.             if (path == null)
  49.                 throw new ArgumentNullException("path");
  50.            
  51.             // Special case "<DriveLetter>:" to point to "<CurrentDirectory>" instead
  52.             if ((path.Length == 2) && (path[1] == ':'))
  53.                 OriginalPath = ".";
  54.             else
  55.                 OriginalPath = path;
  56.            
  57.             // Must fully qualify the path for the security check
  58.             string fullPath = Path.GetFullPathInternal(path);
  59.            
  60.             demandDir = new string[] {Directory.GetDemandDir(fullPath, true)};
  61.             new FileIOPermission(FileIOPermissionAccess.Read, demandDir, false, false).Demand();
  62.            
  63.             FullPath = fullPath;
  64.         }
  65.        
  66.         [ResourceExposure(ResourceScope.Machine)]
  67.         [ResourceConsumption(ResourceScope.Machine)]
  68.         internal DirectoryInfo(string fullPath, bool junk)
  69.         {
  70.             BCLDebug.Assert(Path.GetRootLength(fullPath) > 0, "fullPath must be fully qualified!");
  71.             // Fast path when we know a DirectoryInfo exists.
  72.             OriginalPath = Path.GetFileName(fullPath);
  73.             FullPath = fullPath;
  74.             demandDir = new string[] {Directory.GetDemandDir(fullPath, true)};
  75.         }
  76.        
  77.         private DirectoryInfo(SerializationInfo info, StreamingContext context) : base(info, context)
  78.         {
  79.             demandDir = new string[] {Directory.GetDemandDir(FullPath, true)};
  80.             new FileIOPermission(FileIOPermissionAccess.Read, demandDir, false, false).Demand();
  81.         }
  82.        
  83.         public override string Name {
  84.             [ResourceExposure(ResourceScope.Machine)]
  85.             [ResourceConsumption(ResourceScope.Machine)]
  86.             get {
  87.                 // FullPath might be either "c:\bar" or "c:\bar\". Handle
  88.                 // those cases, as well as avoiding mangling "c:\".
  89.                 string s = FullPath;
  90.                 if (s.Length > 3) {
  91.                     if (s.EndsWith(Path.DirectorySeparatorChar))
  92.                         s = FullPath.Substring(0, FullPath.Length - 1);
  93.                     return Path.GetFileName(s);
  94.                 }
  95.                 return FullPath;
  96.                 // For rooted paths, like "c:\"
  97.             }
  98.         }
  99.        
  100.         public DirectoryInfo Parent {
  101.             [ResourceExposure(ResourceScope.Machine)]
  102.             [ResourceConsumption(ResourceScope.Machine)]
  103.             get {
  104.                 string parentName;
  105.                 // FullPath might be either "c:\bar" or "c:\bar\". Handle
  106.                 // those cases, as well as avoiding mangling "c:\".
  107.                 string s = FullPath;
  108.                 if (s.Length > 3 && s.EndsWith(Path.DirectorySeparatorChar))
  109.                     s = FullPath.Substring(0, FullPath.Length - 1);
  110.                 parentName = Path.GetDirectoryName(s);
  111.                 if (parentName == null)
  112.                     return null;
  113.                 DirectoryInfo dir = new DirectoryInfo(parentName, false);
  114.                 new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, dir.demandDir, false, false).Demand();
  115.                 return dir;
  116.             }
  117.         }
  118.        
  119.        
  120.         [ResourceExposure(ResourceScope.Machine)]
  121.         [ResourceConsumption(ResourceScope.Machine)]
  122.         public DirectoryInfo CreateSubdirectory(string path)
  123.         {
  124.             if (path == null)
  125.                 throw new ArgumentNullException("path");
  126.            
  127.             string newDirs = Path.InternalCombine(FullPath, path);
  128.             string fullPath = Path.GetFullPathInternal(newDirs);
  129.            
  130.             if (0 != String.Compare(FullPath, 0, fullPath, 0, FullPath.Length, StringComparison.OrdinalIgnoreCase)) {
  131.                 string displayPath = __Error.GetDisplayablePath(OriginalPath, false);
  132.                 throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Argument_InvalidSubPath"), path, displayPath));
  133.             }
  134.            
  135.             // Ensure we have permission to create this subdirectory.
  136.             string demandDir = Directory.GetDemandDir(fullPath, true);
  137.             new FileIOPermission(FileIOPermissionAccess.Write, new string[] {demandDir}, false, false).Demand();
  138.            
  139.             Directory.InternalCreateDirectory(fullPath, path);
  140.             // Check for read permission to directory we hand back by calling this constructor.
  141.             return new DirectoryInfo(fullPath);
  142.         }
  143.        
  144.         [ResourceExposure(ResourceScope.None)]
  145.         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  146.         public void Create()
  147.         {
  148.             Directory.InternalCreateDirectory(FullPath, OriginalPath);
  149.         }
  150.        
  151.        
  152.         // Tests if the given path refers to an existing DirectoryInfo on disk.
  153.         //
  154.         // Your application must have Read permission to the directory's
  155.         // contents.
  156.         //
  157.         public override bool Exists {
  158.             get {
  159.                 try {
  160.                     if (_dataInitialised == -1)
  161.                         Refresh();
  162.                     if (_dataInitialised != 0)
  163.                         // Refresh was unable to initialise the data
  164.                         return false;
  165.                    
  166.                     return _data.fileAttributes != -1 && (_data.fileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY) != 0;
  167.                 }
  168.                 catch {
  169.                     return false;
  170.                 }
  171.             }
  172.         }
  173.        
  174.        
  175.         // Returns an array of Files in the current DirectoryInfo matching the
  176.         // given search criteria (ie, "*.txt").
  177.         [ResourceExposure(ResourceScope.Machine)]
  178.         [ResourceConsumption(ResourceScope.Machine)]
  179.         public FileInfo[] GetFiles(string searchPattern)
  180.         {
  181.             return GetFiles(searchPattern, SearchOption.TopDirectoryOnly);
  182.         }
  183.        
  184.        
  185.         // Converts the fully qualified user path returned by InternalGetFileDirectoryNames
  186.         // into fullpath by combining the relevant portion of it with the fullpath of this directory
  187.         // For ex, converts foo\bar.txt into 'c:\temp\foo\bar.txt', where FullPath is 'c:\temp\foo'
  188.         // and OriginalPath (aka userpath) is 'foo'
  189.         private string FixupFileDirFullPath(string fileDirUserPath)
  190.         {
  191.             BCLDebug.Assert(fileDirUserPath != null, "InternalGetFileDirectoryNames returned paths should not be null!");
  192.             BCLDebug.Assert(fileDirUserPath.StartsWith(OriginalPath, StringComparison.Ordinal), "InternalGetFileDirectoryNames returned paths should start with user path!");
  193.            
  194.             string fileDirFullPath;
  195.            
  196.             if (OriginalPath.Length == 0) {
  197.                 fileDirFullPath = Path.InternalCombine(FullPath, fileDirUserPath);
  198.             }
  199.             else if (OriginalPath.EndsWith(Path.DirectorySeparatorChar) || OriginalPath.EndsWith(Path.AltDirectorySeparatorChar)) {
  200.                 BCLDebug.Assert((fileDirUserPath[OriginalPath.Length - 1] == Path.DirectorySeparatorChar) || (fileDirUserPath[OriginalPath.Length - 1] == Path.AltDirectorySeparatorChar), "InternalGetFileDirectoryNames returned incorrect user path!");
  201.                 fileDirFullPath = Path.InternalCombine(FullPath, fileDirUserPath.Substring(OriginalPath.Length));
  202.             }
  203.             else {
  204.                 BCLDebug.Assert((fileDirUserPath[OriginalPath.Length] == Path.DirectorySeparatorChar), "InternalGetFileDirectoryNames returned incorrect user path!");
  205.                 fileDirFullPath = Path.InternalCombine(FullPath, fileDirUserPath.Substring(OriginalPath.Length + 1));
  206.             }
  207.            
  208.             return fileDirFullPath;
  209.         }
  210.        
  211.         // Returns an array of Files in the current DirectoryInfo matching the
  212.         // given search criteria (ie, "*.txt").
  213.         [ResourceExposure(ResourceScope.Machine)]
  214.         [ResourceConsumption(ResourceScope.Machine)]
  215.         public FileInfo[] GetFiles(string searchPattern, SearchOption searchOption)
  216.         {
  217.             if (searchPattern == null)
  218.                 throw new ArgumentNullException("searchPattern");
  219.            
  220.             string[] fileNames = Directory.InternalGetFileDirectoryNames(FullPath, OriginalPath, searchPattern, true, false, searchOption);
  221.            
  222.             // We need full path for permission check.
  223.             // InternalGetFileDirectoryNames returns qualified user path,
  224.             // i.e, path starts from OriginalPath. We need to convert this to fullpath.
  225.             for (int i = 0; i < fileNames.Length; i++) {
  226.                 fileNames[i] = FixupFileDirFullPath(fileNames[i]);
  227.             }
  228.            
  229.             if (fileNames.Length != 0)
  230.                 new FileIOPermission(FileIOPermissionAccess.Read, fileNames, false, false).Demand();
  231.            
  232.             FileInfo[] files = new FileInfo[fileNames.Length];
  233.             for (int i = 0; i < fileNames.Length; i++)
  234.                 files[i] = new FileInfo(fileNames[i], false);
  235.             return files;
  236.         }
  237.        
  238.         // Returns an array of Files in the DirectoryInfo specified by path
  239.         [ResourceExposure(ResourceScope.Machine)]
  240.         [ResourceConsumption(ResourceScope.Machine)]
  241.         public FileInfo[] GetFiles()
  242.         {
  243.             return GetFiles("*");
  244.         }
  245.        
  246.         // Returns an array of Directories in the current directory.
  247.         [ResourceExposure(ResourceScope.Machine)]
  248.         [ResourceConsumption(ResourceScope.Machine)]
  249.         public DirectoryInfo[] GetDirectories()
  250.         {
  251.             return GetDirectories("*");
  252.         }
  253.        
  254.         // Returns an array of strongly typed FileSystemInfo entries in the path with the
  255.         // given search criteria (ie, "*.txt").
  256.         [ResourceExposure(ResourceScope.Machine)]
  257.         [ResourceConsumption(ResourceScope.Machine)]
  258.         public FileSystemInfo[] GetFileSystemInfos(string searchPattern)
  259.         {
  260.             return GetFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
  261.         }
  262.        
  263.         // Returns an array of strongly typed FileSystemInfo entries in the path with the
  264.         // given search criteria (ie, "*.txt").
  265.         [ResourceExposure(ResourceScope.Machine)]
  266.         [ResourceConsumption(ResourceScope.Machine)]
  267.         private FileSystemInfo[] GetFileSystemInfos(string searchPattern, SearchOption searchOption)
  268.         {
  269.             if (searchPattern == null)
  270.                 throw new ArgumentNullException("searchPattern");
  271.            
  272.             string[] dirNames = Directory.InternalGetFileDirectoryNames(FullPath, OriginalPath, searchPattern, false, true, searchOption);
  273.             string[] fileNames = Directory.InternalGetFileDirectoryNames(FullPath, OriginalPath, searchPattern, true, false, searchOption);
  274.             FileSystemInfo[] fileSystemEntries = new FileSystemInfo[dirNames.Length + fileNames.Length];
  275.             string[] permissionNames = new string[dirNames.Length];
  276.            
  277.             // We need full path for permission check.
  278.             // InternalGetFileDirectoryNames returns qualified user path,
  279.             // i.e, path starts from OriginalPath. We need to convert this to fullpath.
  280.            
  281.             for (int i = 0; i < dirNames.Length; i++) {
  282.                 BCLDebug.Assert(!dirNames[i].EndsWith(Path.DirectorySeparatorChar), "InternalGetFileDirectoryNames returned paths should not have trailing slash!");
  283.                
  284.                 dirNames[i] = FixupFileDirFullPath(dirNames[i]);
  285.                 permissionNames[i] = dirNames[i] + "\\.";
  286.                 // these will never have a slash at end
  287.             }
  288.             if (dirNames.Length != 0)
  289.                 new FileIOPermission(FileIOPermissionAccess.Read, permissionNames, false, false).Demand();
  290.            
  291.             for (int i = 0; i < fileNames.Length; i++) {
  292.                 fileNames[i] = FixupFileDirFullPath(fileNames[i]);
  293.             }
  294.            
  295.             if (fileNames.Length != 0)
  296.                 new FileIOPermission(FileIOPermissionAccess.Read, fileNames, false, false).Demand();
  297.            
  298.             int count = 0;
  299.             for (int i = 0; i < dirNames.Length; i++)
  300.                 fileSystemEntries[count++] = new DirectoryInfo(dirNames[i], false);
  301.            
  302.             for (int i = 0; i < fileNames.Length; i++)
  303.                 fileSystemEntries[count++] = new FileInfo(fileNames[i], false);
  304.            
  305.             return fileSystemEntries;
  306.         }
  307.        
  308.        
  309.        
  310.         // Returns an array of strongly typed FileSystemInfo entries which will contain a listing
  311.         // of all the files and directories.
  312.         [ResourceExposure(ResourceScope.Machine)]
  313.         [ResourceConsumption(ResourceScope.Machine)]
  314.         public FileSystemInfo[] GetFileSystemInfos()
  315.         {
  316.             return GetFileSystemInfos("*");
  317.         }
  318.        
  319.         // Returns an array of Directories in the current DirectoryInfo matching the
  320.         // given search criteria (ie, "System*" could match the System & System32
  321.         // directories).
  322.         [ResourceExposure(ResourceScope.Machine)]
  323.         [ResourceConsumption(ResourceScope.Machine)]
  324.         public DirectoryInfo[] GetDirectories(string searchPattern)
  325.         {
  326.             return GetDirectories(searchPattern, SearchOption.TopDirectoryOnly);
  327.         }
  328.        
  329.         // Returns an array of Directories in the current DirectoryInfo matching the
  330.         // given search criteria (ie, "System*" could match the System & System32
  331.         // directories).
  332.         [ResourceExposure(ResourceScope.Machine)]
  333.         [ResourceConsumption(ResourceScope.Machine)]
  334.         public DirectoryInfo[] GetDirectories(string searchPattern, SearchOption searchOption)
  335.         {
  336.             if (searchPattern == null)
  337.                 throw new ArgumentNullException("searchPattern");
  338.            
  339.             string[] dirNames = Directory.InternalGetFileDirectoryNames(FullPath, OriginalPath, searchPattern, false, true, searchOption);
  340.             string[] permissionNames = new string[dirNames.Length];
  341.            
  342.             // We need full path for permission check.
  343.             // InternalGetFileDirectoryNames returns qualified user path,
  344.             // i.e, path starts from OriginalPath. We need to convert this to fullpath.
  345.             for (int i = 0; i < dirNames.Length; i++) {
  346.                 BCLDebug.Assert(!dirNames[i].EndsWith(Path.DirectorySeparatorChar), "InternalGetFileDirectoryNames returned paths should not have trailing slash!");
  347.                
  348.                 dirNames[i] = FixupFileDirFullPath(dirNames[i]);
  349.                 permissionNames[i] = dirNames[i] + "\\.";
  350.                 // these will never have a slash at end
  351.             }
  352.             if (dirNames.Length != 0)
  353.                 new FileIOPermission(FileIOPermissionAccess.Read, permissionNames, false, false).Demand();
  354.            
  355.             DirectoryInfo[] dirs = new DirectoryInfo[dirNames.Length];
  356.             for (int i = 0; i < dirNames.Length; i++)
  357.                 dirs[i] = new DirectoryInfo(dirNames[i], false);
  358.             return dirs;
  359.         }
  360.        
  361.        
  362.        
  363.        
  364.         #if !PLATFORM_UNIX
  365.         // Returns the root portion of the given path. The resulting string
  366.         // consists of those rightmost characters of the path that constitute the
  367.         // root of the path. Possible patterns for the resulting string are: An
  368.         // empty string (a relative path on the current drive), "\" (an absolute
  369.         // path on the current drive), "X:" (a relative path on a given drive,
  370.         // where X is the drive letter), "X:\" (an absolute path on a given drive),
  371.         // and "\\server\share" (a UNC path for a given server and share name).
  372.         // The resulting string is null if path is null.
  373.         //
  374.         #else
  375.         // Returns the root portion of the given path. The resulting string
  376.         // consists of those rightmost characters of the path that constitute the
  377.         // root of the path. Possible patterns for the resulting string are: An
  378.         // empty string (a relative path on the current drive), "\" (an absolute
  379.         // path on the current drive)
  380.         // The resulting string is null if path is null.
  381.         //
  382.         #endif // !PLATFORM_UNIX
  383.        
  384.         public DirectoryInfo Root {
  385.             [ResourceExposure(ResourceScope.None)]
  386.             [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  387.             get {
  388.                 string demandPath;
  389.                 int rootLength = Path.GetRootLength(FullPath);
  390.                 string rootPath = FullPath.Substring(0, rootLength);
  391.                 demandPath = Directory.GetDemandDir(rootPath, true);
  392.                
  393.                 new FileIOPermission(FileIOPermissionAccess.PathDiscovery, new string[] {demandPath}, false, false).Demand();
  394.                 return new DirectoryInfo(rootPath);
  395.             }
  396.         }
  397.        
  398.         [ResourceExposure(ResourceScope.Machine)]
  399.         [ResourceConsumption(ResourceScope.Machine)]
  400.         public void MoveTo(string destDirName)
  401.         {
  402.             if (destDirName == null)
  403.                 throw new ArgumentNullException("destDirName");
  404.             if (destDirName.Length == 0)
  405.                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyFileName"), "destDirName");
  406.            
  407.             new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, demandDir, false, false).Demand();
  408.             string fullDestDirName = Path.GetFullPathInternal(destDirName);
  409.             string demandPath;
  410.             if (!fullDestDirName.EndsWith(Path.DirectorySeparatorChar))
  411.                 fullDestDirName = fullDestDirName + Path.DirectorySeparatorChar;
  412.            
  413.             demandPath = fullDestDirName + '.';
  414.            
  415.             // Demand read & write permission to destination. The reason is
  416.             // we hand back a DirectoryInfo to the destination that would allow
  417.             // you to read a directory listing from that directory. Sure, you
  418.             // had the ability to read the file contents in the old location,
  419.             // but you technically also need read permissions to the new
  420.             // location as well, and write is not a true superset of read.
  421.             new FileIOPermission(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, demandPath).Demand();
  422.            
  423.             string fullSourcePath;
  424.             if (FullPath.EndsWith(Path.DirectorySeparatorChar))
  425.                 fullSourcePath = FullPath;
  426.             else
  427.                 fullSourcePath = FullPath + Path.DirectorySeparatorChar;
  428.            
  429.             if (CultureInfo.InvariantCulture.CompareInfo.Compare(fullSourcePath, fullDestDirName, CompareOptions.IgnoreCase) == 0)
  430.                 throw new IOException(Environment.GetResourceString("IO.IO_SourceDestMustBeDifferent"));
  431.            
  432.             string sourceRoot = Path.GetPathRoot(fullSourcePath);
  433.             string destinationRoot = Path.GetPathRoot(fullDestDirName);
  434.            
  435.             if (CultureInfo.InvariantCulture.CompareInfo.Compare(sourceRoot, destinationRoot, CompareOptions.IgnoreCase) != 0)
  436.                 throw new IOException(Environment.GetResourceString("IO.IO_SourceDestMustHaveSameRoot"));
  437.            
  438.             if (Environment.IsWin9X() && !Directory.InternalExists(FullPath))
  439.                 // For Win9x
  440.                 throw new DirectoryNotFoundException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("IO.PathNotFound_Path"), destDirName));
  441.            
  442.            
  443.             if (!Win32Native.MoveFile(FullPath, destDirName)) {
  444.                 int hr = Marshal.GetLastWin32Error();
  445.                 // A dubious error code
  446.                 if (hr == Win32Native.ERROR_FILE_NOT_FOUND) {
  447.                     hr = Win32Native.ERROR_PATH_NOT_FOUND;
  448.                     __Error.WinIOError(hr, OriginalPath);
  449.                 }
  450.                
  451.                 if (hr == Win32Native.ERROR_ACCESS_DENIED)
  452.                     // We did this for Win9x. We can't change it for backcomp.
  453.                     throw new IOException(String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("UnauthorizedAccess_IODenied_Path"), OriginalPath));
  454.                
  455.                 __Error.WinIOError(hr, String.Empty);
  456.             }
  457.             FullPath = fullDestDirName;
  458.             OriginalPath = destDirName;
  459.             demandDir = new string[] {Directory.GetDemandDir(FullPath, true)};
  460.            
  461.             // Flush any cached information about the directory.
  462.             _dataInitialised = -1;
  463.         }
  464.        
  465.         [ResourceExposure(ResourceScope.None)]
  466.         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  467.         public override void Delete()
  468.         {
  469.             Directory.Delete(FullPath, OriginalPath, false);
  470.         }
  471.        
  472.         [ResourceExposure(ResourceScope.None)]
  473.         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  474.         public void Delete(bool recursive)
  475.         {
  476.             Directory.Delete(FullPath, OriginalPath, recursive);
  477.         }
  478.        
  479.         // Returns the fully qualified path
  480.         public override string ToString()
  481.         {
  482.             return OriginalPath;
  483.         }
  484.     }
  485. }

Developer Fusion