예제 #1
0
        /// <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());
        }
예제 #2
0
        /// <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);
                }
            }
        }
예제 #3
0
        /// <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);
        }
예제 #4
0
        /// <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);
            }
        }
예제 #5
0
        /// <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;
        }
예제 #6
0
        /// <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;
        }
예제 #7
0
        /// <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;
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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);
        }
예제 #10
0
        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);
        }
예제 #11
0
 /// <summary>
 /// Updates the current class' properties.
 /// </summary>
 private void UpdateProperties()
 {
     this.DirectoryName = LongPathCommon.RemoveLongPathPrefix(LongPathCommon.GetDirectoryName(this.NormalizedPath));
 }
예제 #12
0
 /// <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));
 }
예제 #13
0
        /// <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));
        }