// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Enumeration; using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace System.IO { public static partial class Directory { public static DirectoryInfo? GetParent(string path) { ArgumentException.ThrowIfNullOrEmpty(path); string fullPath = Path.GetFullPath(path); string? s = Path.GetDirectoryName(fullPath); if (s == null) return null; return new DirectoryInfo(s); } public static DirectoryInfo CreateDirectory(string path) { ArgumentException.ThrowIfNullOrEmpty(path); string fullPath = Path.GetFullPath(path); FileSystem.CreateDirectory(fullPath); return new DirectoryInfo(path, fullPath, isNormalized: true); } /// <summary> /// Creates all directories and subdirectories in the specified path with the specified permissions unless they already exist. /// </summary> /// <param name="path">The directory to create.</param> /// <param name="unixCreateMode">Unix file mode used to create directories.</param> /// <returns>An object that represents the directory at the specified path. This object is returned regardless of whether a directory at the specified path already exists.</returns> /// <exception cref="T:System.ArgumentException"><paramref name="path" /> is a zero-length string, or contains one or more invalid characters. You can query for invalid characters by using the <see cref="M:System.IO.Path.GetInvalidPathChars" /> method.</exception> /// <exception cref="T:System.ArgumentNullException"><paramref name="path" /> is <see langword="null" />.</exception> /// <exception cref="T:System.ArgumentException">The caller attempts to use an invalid file mode.</exception> /// <exception cref="T:System.UnauthorizedAccessException">The caller does not have the required permission.</exception> /// <exception cref="T:System.IO.PathTooLongException">The specified path exceeds the system-defined maximum length.</exception> /// <exception cref="T:System.IO.IOException"><paramref name="path" /> is a file.</exception> /// <exception cref="T:System.IO.DirectoryNotFoundException">A component of the <paramref name="path" /> is not a directory.</exception> [UnsupportedOSPlatform("windows")] public static DirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode) => CreateDirectoryCore(path, unixCreateMode); /// <summary> /// Creates a uniquely-named, empty directory in the current user's temporary directory. /// </summary> /// <param name="prefix">An optional string to add to the beginning of the subdirectory name.</param> /// <returns>An object that represents the directory that was created.</returns> /// <exception cref="ArgumentException"><paramref name="prefix" /> contains a directory separator.</exception> /// <exception cref="IOException">A new directory cannot be created.</exception> public static DirectoryInfo CreateTempSubdirectory(string? prefix = null) { EnsureNoDirectorySeparators(prefix); string path = CreateTempSubdirectoryCore(prefix); return new DirectoryInfo(path, isNormalized: true); } private static void EnsureNoDirectorySeparators(string? value, [CallerArgumentExpression(nameof(value))] string? paramName = null) { if (value is not null && value.AsSpan().ContainsAny(PathInternal.DirectorySeparators)) { throw new ArgumentException(SR.Argument_DirectorySeparatorInvalid, paramName); } } // Tests if the given path refers to an existing DirectoryInfo on disk. public static bool Exists([NotNullWhen(true)] string? path) { try { if (path == null) return false; if (path.Length == 0) return false; string fullPath = Path.GetFullPath(path); return FileSystem.DirectoryExists(fullPath); } catch (ArgumentException) { } catch (IOException) { } catch (UnauthorizedAccessException) { } return false; } public static void SetCreationTime(string path, DateTime creationTime) { string fullPath = Path.GetFullPath(path); FileSystem.SetCreationTime(fullPath, creationTime, asDirectory: true); } public static void SetCreationTimeUtc(string path, DateTime creationTimeUtc) { string fullPath = Path.GetFullPath(path); FileSystem.SetCreationTime(fullPath, File.GetUtcDateTimeOffset(creationTimeUtc), asDirectory: true); } public static DateTime GetCreationTime(string path) { return File.GetCreationTime(path); } public static DateTime GetCreationTimeUtc(string path) { return File.GetCreationTimeUtc(path); } public static void SetLastWriteTime(string path, DateTime lastWriteTime) { string fullPath = Path.GetFullPath(path); FileSystem.SetLastWriteTime(fullPath, lastWriteTime, asDirectory: true); } public static void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) { string fullPath = Path.GetFullPath(path); FileSystem.SetLastWriteTime(fullPath, File.GetUtcDateTimeOffset(lastWriteTimeUtc), asDirectory: true); } public static DateTime GetLastWriteTime(string path) { return File.GetLastWriteTime(path); } public static DateTime GetLastWriteTimeUtc(string path) { return File.GetLastWriteTimeUtc(path); } public static void SetLastAccessTime(string path, DateTime lastAccessTime) { string fullPath = Path.GetFullPath(path); FileSystem.SetLastAccessTime(fullPath, lastAccessTime, asDirectory: true); } public static void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) { string fullPath = Path.GetFullPath(path); FileSystem.SetLastAccessTime(fullPath, File.GetUtcDateTimeOffset(lastAccessTimeUtc), asDirectory: true); } public static DateTime GetLastAccessTime(string path) { return File.GetLastAccessTime(path); } public static DateTime GetLastAccessTimeUtc(string path) { return File.GetLastAccessTimeUtc(path); } public static string[] GetFiles(string path) => GetFiles(path, "*", enumerationOptions: EnumerationOptions.Compatible); public static string[] GetFiles(string path, string searchPattern) => GetFiles(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible); public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption) => GetFiles(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption)); public static string[] GetFiles(string path, string searchPattern, EnumerationOptions enumerationOptions) => new List<string>(InternalEnumeratePaths(path, searchPattern, SearchTarget.Files, enumerationOptions)).ToArray(); public static string[] GetDirectories(string path) => GetDirectories(path, "*", enumerationOptions: EnumerationOptions.Compatible); public static string[] GetDirectories(string path, string searchPattern) => GetDirectories(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible); public static string[] GetDirectories(string path, string searchPattern, SearchOption searchOption) => GetDirectories(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption)); public static string[] GetDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions) => new List<string>(InternalEnumeratePaths(path, searchPattern, SearchTarget.Directories, enumerationOptions)).ToArray(); public static string[] GetFileSystemEntries(string path) => GetFileSystemEntries(path, "*", enumerationOptions: EnumerationOptions.Compatible); public static string[] GetFileSystemEntries(string path, string searchPattern) => GetFileSystemEntries(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible); public static string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption) => GetFileSystemEntries(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption)); public static string[] GetFileSystemEntries(string path, string searchPattern, EnumerationOptions enumerationOptions) => new List<string>(InternalEnumeratePaths(path, searchPattern, SearchTarget.Both, enumerationOptions)).ToArray(); internal static IEnumerable<string> InternalEnumeratePaths( string path, string searchPattern, SearchTarget searchTarget, EnumerationOptions enumerationOptions) { ArgumentNullException.ThrowIfNull(path); ArgumentNullException.ThrowIfNull(searchPattern); ArgumentNullException.ThrowIfNull(enumerationOptions); FileSystemEnumerableFactory.NormalizeInputs(ref path, ref searchPattern, enumerationOptions.MatchType); return searchTarget switch { SearchTarget.Files => FileSystemEnumerableFactory.UserFiles(path, searchPattern, enumerationOptions), SearchTarget.Directories => FileSystemEnumerableFactory.UserDirectories(path, searchPattern, enumerationOptions), SearchTarget.Both => FileSystemEnumerableFactory.UserEntries(path, searchPattern, enumerationOptions), _ => throw new ArgumentOutOfRangeException(nameof(searchTarget)), }; } public static IEnumerable<string> EnumerateDirectories(string path) => EnumerateDirectories(path, "*", enumerationOptions: EnumerationOptions.Compatible); public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern) => EnumerateDirectories(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible); public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern, SearchOption searchOption) => EnumerateDirectories(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption)); public static IEnumerable<string> EnumerateDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions) => InternalEnumeratePaths(path, searchPattern, SearchTarget.Directories, enumerationOptions); public static IEnumerable<string> EnumerateFiles(string path) => EnumerateFiles(path, "*", enumerationOptions: EnumerationOptions.Compatible); public static IEnumerable<string> EnumerateFiles(string path, string searchPattern) => EnumerateFiles(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible); public static IEnumerable<string> EnumerateFiles(string path, string searchPattern, SearchOption searchOption) => EnumerateFiles(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption)); public static IEnumerable<string> EnumerateFiles(string path, string searchPattern, EnumerationOptions enumerationOptions) => InternalEnumeratePaths(path, searchPattern, SearchTarget.Files, enumerationOptions); public static IEnumerable<string> EnumerateFileSystemEntries(string path) => EnumerateFileSystemEntries(path, "*", enumerationOptions: EnumerationOptions.Compatible); public static IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern) => EnumerateFileSystemEntries(path, searchPattern, enumerationOptions: EnumerationOptions.Compatible); public static IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption) => EnumerateFileSystemEntries(path, searchPattern, EnumerationOptions.FromSearchOption(searchOption)); public static IEnumerable<string> EnumerateFileSystemEntries(string path, string searchPattern, EnumerationOptions enumerationOptions) => InternalEnumeratePaths(path, searchPattern, SearchTarget.Both, enumerationOptions); public static string GetDirectoryRoot(string path) { ArgumentNullException.ThrowIfNull(path); string fullPath = Path.GetFullPath(path); string root = Path.GetPathRoot(fullPath)!; return root; } public static string GetCurrentDirectory() => Environment.CurrentDirectory; public static void SetCurrentDirectory(string path) { ArgumentException.ThrowIfNullOrEmpty(path); Environment.CurrentDirectory = Path.GetFullPath(path); } public static void Move(string sourceDirName, string destDirName) { ArgumentException.ThrowIfNullOrEmpty(sourceDirName); ArgumentException.ThrowIfNullOrEmpty(destDirName); FileSystem.MoveDirectory(Path.GetFullPath(sourceDirName), Path.GetFullPath(destDirName)); } public static void Delete(string path) { string fullPath = Path.GetFullPath(path); FileSystem.RemoveDirectory(fullPath, false); } public static void Delete(string path, bool recursive) { string fullPath = Path.GetFullPath(path); FileSystem.RemoveDirectory(fullPath, recursive); } public static string[] GetLogicalDrives() { return FileSystem.GetLogicalDrives(); } /// <summary> /// Creates a directory symbolic link identified by <paramref name="path"/> that points to <paramref name="pathToTarget"/>. /// </summary> /// <param name="path">The absolute path where the symbolic link should be created.</param> /// <param name="pathToTarget">The target directory of the symbolic link.</param> /// <returns>A <see cref="DirectoryInfo"/> instance that wraps the newly created directory symbolic link.</returns> /// <exception cref="ArgumentNullException"><paramref name="path"/> or <paramref name="pathToTarget"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentException"><paramref name="path"/> or <paramref name="pathToTarget"/> is empty. /// -or- /// <paramref name="path"/> is not an absolute path. /// -or- /// <paramref name="path"/> or <paramref name="pathToTarget"/> contains invalid path characters.</exception> /// <exception cref="IOException">A file or directory already exists in the location of <paramref name="path"/>. /// -or- /// An I/O error occurred.</exception> public static FileSystemInfo CreateSymbolicLink(string path, string pathToTarget) { string fullPath = Path.GetFullPath(path); FileSystem.VerifyValidPath(pathToTarget, nameof(pathToTarget)); FileSystem.CreateSymbolicLink(path, pathToTarget, isDirectory: true); return new DirectoryInfo(originalPath: path, fullPath: fullPath, isNormalized: true); } /// <summary> /// Gets the target of the specified directory link. /// </summary> /// <param name="linkPath">The path of the directory link.</param> /// <param name="returnFinalTarget"><see langword="true"/> to follow links to the final target; <see langword="false"/> to return the immediate next link.</param> /// <returns>A <see cref="DirectoryInfo"/> instance if <paramref name="linkPath"/> exists, independently if the target exists or not. <see langword="null"/> if <paramref name="linkPath"/> is not a link.</returns> /// <exception cref="IOException">The directory on <paramref name="linkPath"/> does not exist. /// -or- /// The link's file system entry type is inconsistent with that of its target. /// -or- /// Too many levels of symbolic links.</exception> /// <remarks>When <paramref name="returnFinalTarget"/> is <see langword="true"/>, the maximum number of symbolic links that are followed are 40 on Unix and 63 on Windows.</remarks> public static FileSystemInfo? ResolveLinkTarget(string linkPath, bool returnFinalTarget) { FileSystem.VerifyValidPath(linkPath, nameof(linkPath)); return FileSystem.ResolveLinkTarget(linkPath, returnFinalTarget, isDirectory: true); } } }