/// <summary> /// Normalizes path (can be longer than <c>MAX_PATH</c>) and adds <c>\\?\</c> long path prefix, if needed. /// UNC paths are also supported. /// </summary> /// <param name="path">Path to be normalized.</param> /// <returns>Normalized path.</returns> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception> public static string NormalizePath(string path) { // This will also validate the path. path = LongPathCommon.AddLongPathPrefix(path); // Don't forget about the null terminator. StringBuilder buffer = new StringBuilder(path.Length + 1); uint normalizedPathLength = NativeMethods.GetFullPathName(path, (uint)buffer.Capacity, buffer, IntPtr.Zero); // Length returned does not include null terminator. if (normalizedPathLength > buffer.Capacity - 1) { // Resulting path longer than our buffer, so increase it. buffer.Capacity = unchecked ((int)normalizedPathLength) + 1; normalizedPathLength = NativeMethods.GetFullPathName(path, normalizedPathLength, buffer, IntPtr.Zero); } if (normalizedPathLength == 0) { throw LongPathCommon.GetExceptionForHr(Marshal.GetHRForLastWin32Error(), path); } if (normalizedPathLength > NativeMethods.MaxLongPath - 1) { throw LongPathCommon.GetExceptionForHr(NativeMethods.ErrorFilenameExcedRange, path); } return(buffer.ToString()); }
/// <summary> /// Updates the <paramref name="path"/> timestamps with the values given. /// </summary> /// <param name="path">Symbolic link to be updated.</param> /// <param name="creationTime">Creation time.</param> /// <param name="lastAccessTime">Last access time.</param> /// <param name="lastWriteTime">Last write time.</param> /// <exception cref="UnauthorizedAccessException">The caller does not have the required permission.</exception> /// <exception cref="ArgumentException"> /// <paramref name="path"/> is a zero-length string, contains only white space, or contains one or more invalid characters as defined by <see cref="Path.GetInvalidPathChars"/>. /// <para>-or-</para> /// <paramref name="path"/> is prefixed with, or contains only a colon character (<c>:</c>). /// </exception> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception> /// <exception cref="PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length.</exception> /// <exception cref="DirectoryNotFoundException">The specified path is invalid (for example, it is on an unmapped drive).</exception> /// <exception cref="NotSupportedException"><paramref name="path"/> contains a colon character (<c>:</c>) that is not part of a drive label (<c>"C:\"</c>).</exception> /// <remarks> /// The major difference between this method and the <see cref="File.SetAttributes"/> is that this method supports /// setting attributes for symbolic links (both files and directories), while the <see cref="File.SetAttributes"/> /// will set them for a target file, not the link itself. /// </remarks> public static void SetTimestamps(string path, DateTime creationTime, DateTime lastAccessTime, DateTime lastWriteTime) { string normalizedPath = LongPathCommon.NormalizePath(path); using (SafeFileHandle handle = NativeMethods.CreateFile( normalizedPath, EFileAccess.FileWriteAttributes, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, EFileAttributes.OpenReparsePoint | EFileAttributes.BackupSemantics, IntPtr.Zero)) { if (handle.IsInvalid) { throw LongPathCommon.GetExceptionForHr(Marshal.GetHRForLastWin32Error(), path); } long creationFileTime = creationTime.ToLocalTime().ToFileTime(); long lastAccessFileTime = lastAccessTime.ToLocalTime().ToFileTime(); long lastWriteFileTime = lastWriteTime.ToLocalTime().ToFileTime(); if (!NativeMethods.SetFileTime(handle, ref creationFileTime, ref lastAccessFileTime, ref lastWriteFileTime)) { throw LongPathCommon.GetExceptionForHr(Marshal.GetHRForLastWin32Error(), path); } } }
/// <summary> /// Returns the directory information for the specified path string. /// </summary> /// <param name="path">The path of a file or directory.</param> /// <returns>Directory information for path, or <see langword="null"/>, if path denotes a root directory or is <see langword="null"/>.</returns> public static string GetDirectoryName(string path) { // Get the full path without the long path prefix. path = LongPathCommon.RemoveLongPathPrefix(LongPathCommon.NormalizePath(path)); int lastSeparatorIndex = path.LastIndexOfAny(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); if (lastSeparatorIndex == -1) { return(null); } // Return null for root directories. if (path.Length == 3 && path.EndsWith(@":\", StringComparison.OrdinalIgnoreCase)) { return(null); } if (path.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase) && lastSeparatorIndex <= 2) { return(null); } string result = path.Substring(0, lastSeparatorIndex); // Append directory separator for root directories. if (result.EndsWith(":", StringComparison.OrdinalIgnoreCase)) { return(result + Path.DirectorySeparatorChar); } return(result); }
/// <summary> /// Sets the attributes for a file or directory. /// </summary> /// <param name="path">The name of the file whose attributes are to be set.</param> /// <param name="attributes">The file attributes to set for the file.</param> /// <exception cref="UnauthorizedAccessException">The caller does not have the required permission.</exception> /// <exception cref="ArgumentException"> /// <paramref name="path"/> is a zero-length string, contains only white space, or contains one or more invalid characters as defined by <see cref="Path.GetInvalidPathChars"/>. /// <para>-or-</para> /// <paramref name="path"/> is prefixed with, or contains only a colon character (<c>:</c>). /// </exception> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception> /// <exception cref="PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length.</exception> /// <exception cref="DirectoryNotFoundException">The specified path is invalid (for example, it is on an unmapped drive).</exception> /// <exception cref="NotSupportedException"><paramref name="path"/> contains a colon character (<c>:</c>) that is not part of a drive label (<c>"C:\"</c>).</exception> public static void SetAttributes(string path, FileAttributes attributes) { string normalizedPath = LongPathCommon.NormalizePath(path); if (!NativeMethods.SetFileAttributes(normalizedPath, (EFileAttributes)attributes)) { throw LongPathCommon.GetExceptionForHr(Marshal.GetHRForLastWin32Error(), path); } }
/// <summary> /// Initializes a new instance of the <see cref="LongPathFileSystemInfo"/> class. /// </summary> /// <param name="data">File data.</param> /// <exception cref="ArgumentNullException"><paramref name="data"/> is <see langword="null"/>.</exception> internal LongPathFileSystemInfo(Win32FindData data) { this.entryData = data; this.OriginalPath = this.entryData.Value.FileName; this.NormalizedPath = LongPathCommon.NormalizePath(this.entryData.Value.FileName); this.isDirectory = LongPathCommon.IsDirectory(this.entryData.Value); this.initialized = true; }
/// <summary> /// Initializes a new instance of the <see cref="LongPathFileSystemInfo"/> class. /// </summary> /// <param name="path">The fully qualified name of the file or directory, or the relative file or directory name.</param> /// <param name="isDirectory">If set to <see langword="true"/>, the directory system information object will be retrieved.</param> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception> protected LongPathFileSystemInfo(string path, bool isDirectory) { if (path == null) { throw new ArgumentNullException(nameof(path)); } this.OriginalPath = path; this.NormalizedPath = LongPathCommon.NormalizePath(path); this.isDirectory = isDirectory; this.initialized = false; }
/// <summary> /// Refreshes the state of the object. /// </summary> /// <exception cref="IOException">A device such as a disk drive is not ready.</exception> public void Refresh() { List <Win32FindData> foundFiles = LongPathDirectory.EnumerateFileSystemIterator( LongPathCommon.GetDirectoryName(this.normalizedPath), Path.GetFileName(this.normalizedPath), SearchOption.TopDirectoryOnly, this.isDirectory, !this.isDirectory).ToList(); this.entryData = foundFiles.Count > 0 ? foundFiles[0] : (Win32FindData?)null; this.initialized = true; }
/// <summary> /// Tries to normalize the <paramref name="path"/> to a long path. /// </summary> /// <param name="path">Path to be normalized.</param> /// <param name="result">Normalized path.</param> /// <returns><see langword="true"/>, if the <paramref name="path"/> was successfully normalized; otherwise, <see langword="false"/>.</returns> private static bool TryNormalizeLongPath(string path, out string result) { try { result = LongPathCommon.NormalizePath(path); return(true); } catch (ArgumentException) { // Ignore. } catch (PathTooLongException) { // Ignore. } result = null; return(false); }
/// <summary> /// Converts the specified <paramref name="hr"/> to a corresponding the managed exception type. /// </summary> /// <param name="hr">Error code.</param> /// <param name="path">Path to a file or directory.</param> /// <returns>Managed exception for the <paramref name="hr"/> given.</returns> internal static Exception GetExceptionForHr(int hr, string path) { string pathWithoutPrefix = LongPathCommon.RemoveLongPathPrefix(path); Exception nativeException = Marshal.GetExceptionForHR(hr); switch (hr) { case NativeMethods.ErrorFileNotFound: return(new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, "File not found: {0}", pathWithoutPrefix), nativeException)); case NativeMethods.ErrorPathNotFound: return(new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, "Path not found: {0}", pathWithoutPrefix), nativeException)); case NativeMethods.ErrorAccessDenied: return(new UnauthorizedAccessException(string.Format(CultureInfo.InvariantCulture, "Access denied to the path: {0}", pathWithoutPrefix), nativeException)); case NativeMethods.ErrorFilenameExcedRange: return(new PathTooLongException(string.Format(CultureInfo.InvariantCulture, "Path is too long: {0}", pathWithoutPrefix), nativeException)); case NativeMethods.ErrorInvalidDrive: return(new DriveNotFoundException(string.Format(CultureInfo.InvariantCulture, "Drive not found: {0}", pathWithoutPrefix), nativeException)); case NativeMethods.ErrorOperationAborted: return(new OperationCanceledException(string.Format(CultureInfo.InvariantCulture, "Operation aborted: {0}", pathWithoutPrefix), nativeException)); case NativeMethods.ErrorInvalidName: return(new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid name: {0}", pathWithoutPrefix), nativeException)); case NativeMethods.ErrorDirNotEmpty: return(new IOException(string.Format(CultureInfo.InvariantCulture, "Directory is not empty: {0}", pathWithoutPrefix), hr)); case NativeMethods.ErrorFileExists: return(new IOException(string.Format(CultureInfo.InvariantCulture, "File already exists: {0}", pathWithoutPrefix), hr)); } // If we don't know about the error code, rely on the Marshal results. return(nativeException); }
public static bool Exists(string path, out bool isDirectory) { if (path == null) { throw new ArgumentNullException(nameof(path)); } string normalizedPath; if (LongPathCommon.TryNormalizeLongPath(path, out normalizedPath)) { EFileAttributes attributes; if (LongPathCommon.TryGetFileAttributes(normalizedPath, out attributes)) { isDirectory = LongPathCommon.IsDirectory(attributes); return(true); } } isDirectory = false; return(false); }
/// <summary> /// Updates the current class' properties. /// </summary> private void UpdateProperties() { this.DirectoryName = LongPathCommon.RemoveLongPathPrefix(LongPathCommon.GetDirectoryName(this.NormalizedPath)); }
/// <summary> /// Determines whether the specified attributes belong to a directory. /// </summary> /// <param name="findData">File or directory data object.</param> /// <returns><see langword="true"/> if the specified attributes belong to a directory; otherwise, <see langword="false"/>. /// </returns> internal static bool IsDirectory(Win32FindData findData) { return(LongPathCommon.IsDirectory(findData.FileAttributes)); }
/// <summary> /// Determines whether the specified <paramref name="path"/> exists. /// </summary> /// <param name="path">The path.</param> /// <returns><see langword="true"/>, if the <paramref name="path"/> given exists; otherwise, <see langword="false"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception> public static bool Exists(string path) { bool isDirectory; return(LongPathCommon.Exists(path, out isDirectory)); }