diff --git a/AlphaFS.UnitTest/Directory Class/AlphaFS_Directory_Copy.cs b/AlphaFS.UnitTest/Directory Class/AlphaFS_Directory_Copy.cs index 14e5f3400..24c717d2a 100644 --- a/AlphaFS.UnitTest/Directory Class/AlphaFS_Directory_Copy.cs +++ b/AlphaFS.UnitTest/Directory Class/AlphaFS_Directory_Copy.cs @@ -45,6 +45,17 @@ public void AlphaFS_Directory_Copy_Overwrite_DestinationFileAlreadyExists_LocalA } + [TestMethod] + public void AlphaFS_Directory_Copy_CopyOptions_CopySymbolicLink_SourceIsASymbolicLink_TargetMustAlsoBeASymbolicLink_LocalAndNetwork_Success() + { + if (!UnitTestConstants.IsAdmin()) + Assert.Inconclusive(); + + Directory_Copy_CopyOptions_CopySymbolicLink_SourceIsASymbolicLink_TargetMustAlsoBeASymbolicLink(false); + Directory_Copy_CopyOptions_CopySymbolicLink_SourceIsASymbolicLink_TargetMustAlsoBeASymbolicLink(true); + } + + [TestMethod] public void AlphaFS_Directory_Copy_CatchAlreadyExistsException_DestinationFileAlreadyExists_LocalAndNetwork_Success() { @@ -226,6 +237,51 @@ private void Directory_Copy_Overwrite_DestinationFileAlreadyExists(bool isNetwor } + private void Directory_Copy_CopyOptions_CopySymbolicLink_SourceIsASymbolicLink_TargetMustAlsoBeASymbolicLink(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + + var tempPath = System.IO.Path.GetTempPath(); + if (isNetwork) + tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); + + + using (var rootDir = new TemporaryDirectory(tempPath, MethodBase.GetCurrentMethod().Name)) + { + var sourceFolderLink = System.IO.Path.Combine(rootDir.Directory.FullName, "SourceDirectoryLink-ToOriginalDirectory"); + + var dirInfo = new System.IO.DirectoryInfo(System.IO.Path.Combine(rootDir.Directory.FullName, "OriginalDirectory")); + dirInfo.Create(); + + Console.WriteLine("\nInput Directory Path: [{0}]", dirInfo.FullName); + Console.WriteLine("Input Directory Link: [{0}]", sourceFolderLink); + + Alphaleonis.Win32.Filesystem.Directory.CreateSymbolicLink(sourceFolderLink, dirInfo.FullName); + + + var destinationFolderLink = System.IO.Path.Combine(rootDir.Directory.FullName, "DestinationDirectoryLink-ToOriginalDirectory"); + + Alphaleonis.Win32.Filesystem.Directory.Copy(sourceFolderLink, destinationFolderLink, Alphaleonis.Win32.Filesystem.CopyOptions.CopySymbolicLink); + + + var lviSrc = Alphaleonis.Win32.Filesystem.Directory.GetLinkTargetInfo(sourceFolderLink); + var lviDst = Alphaleonis.Win32.Filesystem.Directory.GetLinkTargetInfo(destinationFolderLink); + + Console.WriteLine("\n\tLink Info of source Link:"); + UnitTestConstants.Dump(lviSrc, -14); + + Console.WriteLine("\n\tLink Info of copied Link:"); + UnitTestConstants.Dump(lviDst, -14); + + + Assert.AreEqual(lviSrc.PrintName, lviDst.PrintName); + Assert.AreEqual(lviSrc.SubstituteName, lviDst.SubstituteName); + } + + Console.WriteLine(); + } + + private void Directory_Copy_CatchAlreadyExistsException_DestinationFileAlreadyExists(bool isNetwork) { UnitTestConstants.PrintUnitTestHeader(isNetwork); diff --git a/AlphaFS.UnitTest/Directory Class/AlphaFS_Directory_CreateSymbolicLink_And_GetLinkTargetInfo.cs b/AlphaFS.UnitTest/Directory Class/AlphaFS_Directory_CreateSymbolicLink_And_GetLinkTargetInfo.cs index 8cecca89d..e68c3dcee 100644 --- a/AlphaFS.UnitTest/Directory Class/AlphaFS_Directory_CreateSymbolicLink_And_GetLinkTargetInfo.cs +++ b/AlphaFS.UnitTest/Directory Class/AlphaFS_Directory_CreateSymbolicLink_And_GetLinkTargetInfo.cs @@ -117,13 +117,13 @@ private void Directory_CreateSymbolicLink_CatchIOException_FileExistsWithSameNam using (var rootDir = new TemporaryDirectory(tempPath, MethodBase.GetCurrentMethod().Name)) { - var folderLink = System.IO.Path.Combine(rootDir.Directory.FullName, "FolderLink-ToDestinationFolder"); + var folderLink = System.IO.Path.Combine(rootDir.Directory.FullName, "FolderLink-ToOriginalFolder"); - var fileInfo = new System.IO.FileInfo(System.IO.Path.Combine(rootDir.Directory.FullName, "DestinationFile.txt")); + var fileInfo = new System.IO.FileInfo(System.IO.Path.Combine(rootDir.Directory.FullName, "OriginalFile.txt")); using (fileInfo.Create()) {} - Console.WriteLine("\nInput File Path: [{0}]", fileInfo.FullName); - Console.WriteLine("Folder Link : [{0}]", folderLink); + Console.WriteLine("\nInput File Path : [{0}]", fileInfo.FullName); + Console.WriteLine("Input Folder Link: [{0}]", folderLink); var gotException = false; diff --git a/AlphaFS.UnitTest/File Class/AlphaFS_File_CreateSymbolicLinkAndGetLinkTargetInfo.cs b/AlphaFS.UnitTest/File Class/AlphaFS_File_CreateSymbolicLinkAndGetLinkTargetInfo.cs index 926cc8e27..ca81d980b 100644 --- a/AlphaFS.UnitTest/File Class/AlphaFS_File_CreateSymbolicLinkAndGetLinkTargetInfo.cs +++ b/AlphaFS.UnitTest/File Class/AlphaFS_File_CreateSymbolicLinkAndGetLinkTargetInfo.cs @@ -64,9 +64,9 @@ private void File_CreateSymbolicLink_And_GetLinkTargetInfo(bool isNetwork) using (var rootDir = new TemporaryDirectory(tempPath, MethodBase.GetCurrentMethod().Name)) { - var fileLink = System.IO.Path.Combine(rootDir.Directory.FullName, "FileLink-ToDestinationFile.txt"); + var fileLink = System.IO.Path.Combine(rootDir.Directory.FullName, "FileLink-ToOriginalFile.txt"); - var fileInfo = new System.IO.FileInfo(System.IO.Path.Combine(rootDir.Directory.FullName, "DestinationFile.txt")); + var fileInfo = new System.IO.FileInfo(System.IO.Path.Combine(rootDir.Directory.FullName, "OriginalFile.txt")); using (fileInfo.CreateText()) {} Console.WriteLine("\nInput File Path: [{0}]", fileInfo.FullName); diff --git a/AlphaFS.UnitTest/File Class/File_Copy.cs b/AlphaFS.UnitTest/File Class/File_Copy.cs index 76f58c258..2d9b74cc1 100644 --- a/AlphaFS.UnitTest/File Class/File_Copy.cs +++ b/AlphaFS.UnitTest/File Class/File_Copy.cs @@ -45,6 +45,17 @@ public void File_Copy_Overwrite_DestinationFileAlreadyExists_LocalAndNetwork_Suc } + [TestMethod] + public void AlphaFS_File_Copy_CopyOptions_CopySymbolicLink_SourceIsASymbolicLink_TargetMustAlsoBeASymbolicLink_LocalAndNetwork_Success() + { + if (!UnitTestConstants.IsAdmin()) + Assert.Inconclusive(); + + File_Copy_CopyOptions_CopySymbolicLink_SourceIsASymbolicLink_TargetMustAlsoBeASymbolicLink(false); + File_Copy_CopyOptions_CopySymbolicLink_SourceIsASymbolicLink_TargetMustAlsoBeASymbolicLink(true); + } + + [TestMethod] public void File_Copy_CatchAlreadyExistsException_DestinationFileAlreadyExists_LocalAndNetwork_Success() { @@ -177,6 +188,51 @@ private void File_Copy_Overwrite_DestinationFileAlreadyExists(bool isNetwork) } + private void File_Copy_CopyOptions_CopySymbolicLink_SourceIsASymbolicLink_TargetMustAlsoBeASymbolicLink(bool isNetwork) + { + UnitTestConstants.PrintUnitTestHeader(isNetwork); + + var tempPath = System.IO.Path.GetTempPath(); + if (isNetwork) + tempPath = Alphaleonis.Win32.Filesystem.Path.LocalToUnc(tempPath); + + + using (var rootDir = new TemporaryDirectory(tempPath, MethodBase.GetCurrentMethod().Name)) + { + var sourceFileLink = System.IO.Path.Combine(rootDir.Directory.FullName, "SourceFileLink-ToOriginalFile.txt"); + + var fileInfo = new System.IO.FileInfo(System.IO.Path.Combine(rootDir.Directory.FullName, "OriginalFile.txt")); + using (fileInfo.CreateText()) { } + + Console.WriteLine("\nInput File Path: [{0}]", fileInfo.FullName); + Console.WriteLine("Input File Link: [{0}]", sourceFileLink); + + Alphaleonis.Win32.Filesystem.File.CreateSymbolicLink(sourceFileLink, fileInfo.FullName); + + + var destinationFileLink = System.IO.Path.Combine(rootDir.Directory.FullName, "DestinationFileLink-ToOriginalFile.txt"); + + Alphaleonis.Win32.Filesystem.File.Copy(sourceFileLink, destinationFileLink, Alphaleonis.Win32.Filesystem.CopyOptions.CopySymbolicLink); + + + var lviSrc = Alphaleonis.Win32.Filesystem.File.GetLinkTargetInfo(sourceFileLink); + var lviDst = Alphaleonis.Win32.Filesystem.File.GetLinkTargetInfo(destinationFileLink); + + Console.WriteLine("\n\tLink Info of source Link:"); + UnitTestConstants.Dump(lviSrc, -14); + + Console.WriteLine("\n\tLink Info of copied Link:"); + UnitTestConstants.Dump(lviDst, -14); + + + Assert.AreEqual(lviSrc.PrintName, lviDst.PrintName); + Assert.AreEqual(lviSrc.SubstituteName, lviDst.SubstituteName); + } + + Console.WriteLine(); + } + + private void File_Copy_CatchAlreadyExistsException_DestinationFileAlreadyExists(bool isNetwork) { UnitTestConstants.PrintUnitTestHeader(isNetwork); diff --git a/AlphaFS/AlphaFS.csproj b/AlphaFS/AlphaFS.csproj index 5aa2ba1d3..5be4f8f86 100644 --- a/AlphaFS/AlphaFS.csproj +++ b/AlphaFS/AlphaFS.csproj @@ -392,8 +392,8 @@ - - + + diff --git a/AlphaFS/Filesystem/BackupFileStream.cs b/AlphaFS/Filesystem/BackupFileStream.cs index 5ed355e75..aa6a2c0e0 100644 --- a/AlphaFS/Filesystem/BackupFileStream.cs +++ b/AlphaFS/Filesystem/BackupFileStream.cs @@ -55,7 +55,7 @@ public sealed class BackupFileStream : Stream /// The file will be opened for exclusive access for both reading and writing. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(string path, FileMode mode) : this(File.CreateFileCore(null, path, ExtendedFileAttributes.Normal, null, mode, FileSystemRights.Read | FileSystemRights.Write, FileShare.None, true, PathFormat.RelativePath), FileSystemRights.Read | FileSystemRights.Write) + public BackupFileStream(string path, FileMode mode) : this(File.CreateFileCore(null, path, ExtendedFileAttributes.Normal, null, mode, FileSystemRights.Read | FileSystemRights.Write, FileShare.None, true, false, PathFormat.RelativePath), FileSystemRights.Read | FileSystemRights.Write) { } @@ -67,7 +67,7 @@ public BackupFileStream(string path, FileMode mode) : this(File.CreateFileCore(n /// The file will be opened for exclusive access. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(string path, FileMode mode, FileSystemRights access) : this(File.CreateFileCore(null, path, ExtendedFileAttributes.Normal, null, mode, access, FileShare.None, true, PathFormat.RelativePath), access) + public BackupFileStream(string path, FileMode mode, FileSystemRights access) : this(File.CreateFileCore(null, path, ExtendedFileAttributes.Normal, null, mode, access, FileShare.None, true, false, PathFormat.RelativePath), access) { } @@ -79,7 +79,7 @@ public BackupFileStream(string path, FileMode mode, FileSystemRights access) : t /// A constant that determines how the file will be shared by processes. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(string path, FileMode mode, FileSystemRights access, FileShare share) : this(File.CreateFileCore(null, path, ExtendedFileAttributes.Normal, null, mode, access, share, true, PathFormat.RelativePath), access) + public BackupFileStream(string path, FileMode mode, FileSystemRights access, FileShare share) : this(File.CreateFileCore(null, path, ExtendedFileAttributes.Normal, null, mode, access, share, true, false, PathFormat.RelativePath), access) { } @@ -92,7 +92,7 @@ public BackupFileStream(string path, FileMode mode, FileSystemRights access, Fil /// A constant that specifies additional file attributes. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes) : this(File.CreateFileCore(null, path, attributes, null, mode, access, share, true, PathFormat.RelativePath), access) + public BackupFileStream(string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes) : this(File.CreateFileCore(null, path, attributes, null, mode, access, share, true, false, PathFormat.RelativePath), access) { } @@ -106,7 +106,7 @@ public BackupFileStream(string path, FileMode mode, FileSystemRights access, Fil /// A constant that determines the access control and audit security for the file. This parameter This parameter may be . [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes, FileSecurity security) : this(File.CreateFileCore(null, path, attributes, security, mode, access, share, true, PathFormat.RelativePath), access) + public BackupFileStream(string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes, FileSecurity security) : this(File.CreateFileCore(null, path, attributes, security, mode, access, share, true, false, PathFormat.RelativePath), access) { } @@ -118,7 +118,7 @@ public BackupFileStream(string path, FileMode mode, FileSystemRights access, Fil /// The file will be opened for exclusive access for both reading and writing. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(KernelTransaction transaction, string path, FileMode mode) : this(File.CreateFileCore(transaction, path, ExtendedFileAttributes.Normal, null, mode, FileSystemRights.Read | FileSystemRights.Write, FileShare.None, true, PathFormat.RelativePath), FileSystemRights.Read | FileSystemRights.Write) + public BackupFileStream(KernelTransaction transaction, string path, FileMode mode) : this(File.CreateFileCore(transaction, path, ExtendedFileAttributes.Normal, null, mode, FileSystemRights.Read | FileSystemRights.Write, FileShare.None, true, false, PathFormat.RelativePath), FileSystemRights.Read | FileSystemRights.Write) { } @@ -131,7 +131,7 @@ public BackupFileStream(KernelTransaction transaction, string path, FileMode mod /// The file will be opened for exclusive access. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access) : this(File.CreateFileCore(transaction, path, ExtendedFileAttributes.Normal, null, mode, access, FileShare.None, true, PathFormat.RelativePath), access) + public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access) : this(File.CreateFileCore(transaction, path, ExtendedFileAttributes.Normal, null, mode, access, FileShare.None, true, false, PathFormat.RelativePath), access) { } @@ -144,7 +144,7 @@ public BackupFileStream(KernelTransaction transaction, string path, FileMode mod /// A constant that determines how the file will be shared by processes. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access, FileShare share) : this(File.CreateFileCore(transaction, path, ExtendedFileAttributes.Normal, null, mode, access, share, true, PathFormat.RelativePath), access) + public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access, FileShare share) : this(File.CreateFileCore(transaction, path, ExtendedFileAttributes.Normal, null, mode, access, share, true, false, PathFormat.RelativePath), access) { } @@ -158,7 +158,7 @@ public BackupFileStream(KernelTransaction transaction, string path, FileMode mod /// A constant that specifies additional file attributes. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes) : this(File.CreateFileCore(transaction, path, attributes, null, mode, access, share, true, PathFormat.RelativePath), access) + public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes) : this(File.CreateFileCore(transaction, path, attributes, null, mode, access, share, true, false, PathFormat.RelativePath), access) { } @@ -173,7 +173,7 @@ public BackupFileStream(KernelTransaction transaction, string path, FileMode mod /// A constant that determines the access control and audit security for the file. This parameter This parameter may be . [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] [SecurityCritical] - public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes, FileSecurity security) : this(File.CreateFileCore(transaction, path, attributes, security, mode, access, share, true, PathFormat.RelativePath), access) + public BackupFileStream(KernelTransaction transaction, string path, FileMode mode, FileSystemRights access, FileShare share, ExtendedFileAttributes attributes, FileSecurity security) : this(File.CreateFileCore(transaction, path, attributes, security, mode, access, share, true, false, PathFormat.RelativePath), access) { } diff --git a/AlphaFS/Filesystem/Device.cs b/AlphaFS/Filesystem/Device.cs index 7822aa2a6..f6ce7e12b 100644 --- a/AlphaFS/Filesystem/Device.cs +++ b/AlphaFS/Filesystem/Device.cs @@ -347,7 +347,7 @@ private static byte[] InvokeIoControlUnknownSize(SafeFileHandle handle, uint [SecurityCritical] internal static void ToggleCompressionCore(KernelTransaction transaction, bool isFolder, string path, bool compress, PathFormat pathFormat) { - using (var handle = File.CreateFileCore(transaction, path, isFolder ? ExtendedFileAttributes.BackupSemantics : ExtendedFileAttributes.Normal, null, FileMode.Open, FileSystemRights.Modify, FileShare.None, true, pathFormat)) + using (var handle = File.CreateFileCore(transaction, path, isFolder ? ExtendedFileAttributes.BackupSemantics : ExtendedFileAttributes.Normal, null, FileMode.Open, FileSystemRights.Modify, FileShare.None, true, false, pathFormat)) InvokeIoControlUnknownSize(handle, NativeMethods.FSCTL_SET_COMPRESSION, compress ? 1 : 0); } diff --git a/AlphaFS/Filesystem/Directory Class/Directory.Copy.cs b/AlphaFS/Filesystem/Directory Class/Directory.Copy.cs index 8bb782efe..f29eff87d 100644 --- a/AlphaFS/Filesystem/Directory Class/Directory.Copy.cs +++ b/AlphaFS/Filesystem/Directory Class/Directory.Copy.cs @@ -702,22 +702,41 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin // A file or folder will be deleted or renamed on Computer startup. bool delayUntilReboot; bool deleteOnStartup; + - - // Process paths. - File.ValidateAndUpdatePaths(transaction, sourcePath, destinationPath, copyOptions, moveOptions, pathFormat, out sourcePathLp, out destinationPathLp, out isCopy, out emulateMove, out delayUntilReboot, out deleteOnStartup); + File.ValidateAndUpdatePathsAndOptions(transaction, sourcePath, destinationPath, copyOptions, moveOptions, pathFormat, out sourcePathLp, out destinationPathLp, out isCopy, out emulateMove, out delayUntilReboot, out deleteOnStartup); // Process Move action options, possible fallback to Copy action. if (!isCopy && !deleteOnStartup) - ValidateAndUpdateMoveAction(sourcePathLp, destinationPathLp, copyOptions, moveOptions, out copyOptions, out moveOptions, out isCopy, out emulateMove); + ValidateAndUpdateCopyMoveAction(sourcePathLp, destinationPathLp, copyOptions, moveOptions, out copyOptions, out moveOptions, out isCopy, out emulateMove); + + pathFormat = PathFormat.LongFullPath; var cmr = copyMoveResult ?? new CopyMoveResult(sourcePath, destinationPath, isCopy, true, preserveDates, emulateMove); if (isCopy) - cmr = CopyDeleteCore(cmr, transaction, sourcePathLp, destinationPathLp, preserveDates, emulateMove, copyOptions, progressHandler, userProgressData); + { + // Copy folder SymbolicLinks. + // Cannot be done by CopyFileEx() so emulate this. + + if (File.HasCopySymbolicLink(copyOptions)) + { + var lvi = File.GetLinkTargetInfoCore(transaction, true, sourcePathLp, true, pathFormat); + + if (null != lvi) + { + File.CreateSymbolicLinkCore(transaction, destinationPathLp, lvi.SubstituteName, SymbolicLinkTarget.Directory, pathFormat); + + cmr.TotalFolders = 1; + } + } + + else + cmr = CopyDeleteCore(cmr, transaction, sourcePathLp, destinationPathLp, preserveDates, emulateMove, copyOptions, progressHandler, userProgressData); + } // Move else @@ -726,7 +745,7 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin // MoveOptions.ReplaceExisting: This value cannot be used if lpNewFileName or lpExistingFileName names a directory. if (!delayUntilReboot && File.CanOverwrite(moveOptions)) - DeleteDirectoryCore(transaction, null, destinationPathLp, true, true, true, PathFormat.LongFullPath); + DeleteDirectoryCore(transaction, null, destinationPathLp, true, true, true, pathFormat); // 2017-06-07: A large target directory will probably create a progress-less delay in UI. // One way to get around this is to perform the delete in the File.CopyMove method. @@ -735,13 +754,13 @@ internal static CopyMoveResult CopyMoveCore(KernelTransaction transaction, strin // Moves a file or directory, including its children. // Copies an existing directory, including its children to a new directory. - cmr = File.CopyMoveCore(cmr, transaction, true, sourcePathLp, destinationPathLp, copyOptions, moveOptions, preserveDates, progressHandler, userProgressData, PathFormat.LongFullPath); + cmr = File.CopyMoveCore(cmr, transaction, true, sourcePathLp, destinationPathLp, copyOptions, moveOptions, preserveDates, progressHandler, userProgressData, pathFormat); // If the move happened on the same drive, we have no knowledge of the number of files/folders. // However, we do know that the one folder was moved successfully. - if (cmr.ErrorCode == Win32Errors.ERROR_SUCCESS) + if (cmr.ErrorCode == Win32Errors.NO_ERROR) cmr.TotalFolders = 1; } @@ -758,6 +777,7 @@ private static CopyMoveResult CopyDeleteCore(CopyMoveResult cmr, KernelTransacti var dirs = new Queue(items); dirs.Enqueue(sourcePathLp); + CreateDirectoryCore(transaction, destinationPathLp, null, null, false, PathFormat.LongFullPath); @@ -837,7 +857,7 @@ private static CopyMoveResult CopyDeleteCore(CopyMoveResult cmr, KernelTransacti } - private static void ValidateAndUpdateMoveAction(string sourcePathLp, string destinationPathLp, CopyOptions? copyOptions, MoveOptions? moveOptions, out CopyOptions? newCopyOptions, out MoveOptions? newMoveOptions, out bool isCopy, out bool emulateMove) + private static void ValidateAndUpdateCopyMoveAction(string sourcePathLp, string destinationPathLp, CopyOptions? copyOptions, MoveOptions? moveOptions, out CopyOptions? newCopyOptions, out MoveOptions? newMoveOptions, out bool isCopy, out bool emulateMove) { // Determine if a Move action or Copy action-fallback is possible. isCopy = emulateMove = false; diff --git a/AlphaFS/Filesystem/Directory Class/Directory.CreateJunction.cs b/AlphaFS/Filesystem/Directory Class/Directory.CreateJunction.cs index e95723deb..c84060173 100644 --- a/AlphaFS/Filesystem/Directory Class/Directory.CreateJunction.cs +++ b/AlphaFS/Filesystem/Directory Class/Directory.CreateJunction.cs @@ -477,7 +477,7 @@ internal static string CreateJunctionCore(KernelTransaction transaction, string private static SafeFileHandle OpenDirectoryJunction(KernelTransaction transaction, string junctionPath, PathFormat pathFormat) { - return File.CreateFileCore(transaction, junctionPath, ExtendedFileAttributes.BackupSemantics | ExtendedFileAttributes.OpenReparsePoint, null, FileMode.Open, FileSystemRights.WriteData, FileShare.ReadWrite, false, pathFormat); + return File.CreateFileCore(transaction, junctionPath, ExtendedFileAttributes.BackupSemantics | ExtendedFileAttributes.OpenReparsePoint, null, FileMode.Open, FileSystemRights.WriteData, FileShare.ReadWrite, false, false, pathFormat); } } } diff --git a/AlphaFS/Filesystem/Directory Class/Directory.EnumerateFileIdBothDirectoryInfo.cs b/AlphaFS/Filesystem/Directory Class/Directory.EnumerateFileIdBothDirectoryInfo.cs index dce3935d3..4a0acf2b0 100644 --- a/AlphaFS/Filesystem/Directory Class/Directory.EnumerateFileIdBothDirectoryInfo.cs +++ b/AlphaFS/Filesystem/Directory Class/Directory.EnumerateFileIdBothDirectoryInfo.cs @@ -170,7 +170,7 @@ internal static IEnumerable EnumerateFileIdBothDirector pathLp = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck); - safeFileHandle = File.CreateFileCore(transaction, pathLp, ExtendedFileAttributes.BackupSemantics, null, FileMode.Open, FileSystemRights.ReadData, shareMode, true, PathFormat.LongFullPath); + safeFileHandle = File.CreateFileCore(transaction, pathLp, ExtendedFileAttributes.BackupSemantics, null, FileMode.Open, FileSystemRights.ReadData, shareMode, true, false, PathFormat.LongFullPath); } diff --git a/AlphaFS/Filesystem/Directory Class/Directory.GetLinkTargetInfo.cs b/AlphaFS/Filesystem/Directory Class/Directory.GetLinkTargetInfo.cs index 04b56236f..2885c0a75 100644 --- a/AlphaFS/Filesystem/Directory Class/Directory.GetLinkTargetInfo.cs +++ b/AlphaFS/Filesystem/Directory Class/Directory.GetLinkTargetInfo.cs @@ -40,7 +40,7 @@ public static partial class Directory [SecurityCritical] public static LinkTargetInfo GetLinkTargetInfo(string path) { - return File.GetLinkTargetInfoCore(null, true, path, PathFormat.RelativePath); + return File.GetLinkTargetInfoCore(null, true, path, false, PathFormat.RelativePath); } @@ -59,7 +59,7 @@ public static LinkTargetInfo GetLinkTargetInfo(string path) [SecurityCritical] public static LinkTargetInfo GetLinkTargetInfo(string path, PathFormat pathFormat) { - return File.GetLinkTargetInfoCore(null, true, path, pathFormat); + return File.GetLinkTargetInfoCore(null, true, path, false, pathFormat); } @@ -78,7 +78,7 @@ public static LinkTargetInfo GetLinkTargetInfo(string path, PathFormat pathForma [SecurityCritical] public static LinkTargetInfo GetLinkTargetInfoTransacted(KernelTransaction transaction, string path) { - return File.GetLinkTargetInfoCore(transaction, true, path, PathFormat.RelativePath); + return File.GetLinkTargetInfoCore(transaction, true, path, false, PathFormat.RelativePath); } @@ -103,7 +103,7 @@ public static LinkTargetInfo GetLinkTargetInfoTransacted(KernelTransaction trans [SecurityCritical] public static LinkTargetInfo GetLinkTargetInfoTransacted(KernelTransaction transaction, string path, PathFormat pathFormat) { - return File.GetLinkTargetInfoCore(transaction, true, path, pathFormat); + return File.GetLinkTargetInfoCore(transaction, true, path, false, pathFormat); } } } diff --git a/AlphaFS/Filesystem/Enumerations/CopyOptions.cs b/AlphaFS/Filesystem/Enumerations/CopyOptions.cs index 79cc495b2..f054c25eb 100644 --- a/AlphaFS/Filesystem/Enumerations/CopyOptions.cs +++ b/AlphaFS/Filesystem/Enumerations/CopyOptions.cs @@ -35,7 +35,7 @@ public enum CopyOptions /// COPY_FILE_FAIL_IF_EXISTS /// The copy operation fails immediately if the target file already exists. /// - FailIfExists = NativeMethods.COPY_FILE_OPTIONS.COPY_FILE_FAIL_IF_EXISTS, + FailIfExists = NativeMethods.COPY_FILE_FLAGS.COPY_FILE_FAIL_IF_EXISTS, /// COPY_FILE_RESTARTABLE @@ -46,30 +46,30 @@ public enum CopyOptions /// /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Restartable")] - Restartable = NativeMethods.COPY_FILE_OPTIONS.COPY_FILE_RESTARTABLE, + Restartable = NativeMethods.COPY_FILE_FLAGS.COPY_FILE_RESTARTABLE, /// COPY_FILE_OPEN_SOURCE_FOR_WRITE /// The file is copied and the original file is opened for write access. /// - OpenSourceForWrite = NativeMethods.COPY_FILE_OPTIONS.COPY_FILE_OPEN_SOURCE_FOR_WRITE, + OpenSourceForWrite = NativeMethods.COPY_FILE_FLAGS.COPY_FILE_OPEN_SOURCE_FOR_WRITE, /// COPY_FILE_ALLOW_DECRYPTED_DESTINATION /// An attempt to copy an encrypted file will succeed even if the destination copy cannot be encrypted. /// - AllowDecryptedDestination = NativeMethods.COPY_FILE_OPTIONS.COPY_FILE_ALLOW_DECRYPTED_DESTINATION, + AllowDecryptedDestination = NativeMethods.COPY_FILE_FLAGS.COPY_FILE_ALLOW_DECRYPTED_DESTINATION, - /// COPY_FILE_COPY_SYMLINK + /// COPY_FILE_COPY_SYMLINK, similar to XCOPY /B = Copies the Symbolic Link itself versus the target of the link. /// If the source file is a symbolic link, the destination file is also a symbolic link pointing to the same file that the source symbolic link is pointing to. /// - CopySymbolicLink = NativeMethods.COPY_FILE_OPTIONS.COPY_FILE_COPY_SYMLINK, + CopySymbolicLink = NativeMethods.COPY_FILE_FLAGS.COPY_FILE_COPY_SYMLINK, /// COPY_FILE_NO_BUFFERING /// The copy operation is performed using unbuffered I/O, bypassing system I/O cache resources. Recommended for very large file transfers. /// - NoBuffering = NativeMethods.COPY_FILE_OPTIONS.COPY_FILE_NO_BUFFERING + NoBuffering = NativeMethods.COPY_FILE_FLAGS.COPY_FILE_NO_BUFFERING } } diff --git a/AlphaFS/Filesystem/Enumerations/MoveOptions.cs b/AlphaFS/Filesystem/Enumerations/MoveOptions.cs index af5fad60b..ed7594309 100644 --- a/AlphaFS/Filesystem/Enumerations/MoveOptions.cs +++ b/AlphaFS/Filesystem/Enumerations/MoveOptions.cs @@ -37,14 +37,14 @@ public enum MoveOptions /// This value cannot be used if lpNewFileName or lpExistingFileName names a directory. /// This value cannot be used if either source or destination names a directory. /// - ReplaceExisting = NativeMethods.MOVE_FILE_OPTIONS.MOVE_FILE_REPLACE_EXISTSING, + ReplaceExisting = NativeMethods.MOVE_FILE_FLAGS.MOVE_FILE_REPLACE_EXISTSING, /// MOVE_FILE_COPY_ALLOWED /// If the file is to be moved to a different volume, the function simulates the move by using the CopyFile and DeleteFile functions. /// This value cannot be used with . /// - CopyAllowed = NativeMethods.MOVE_FILE_OPTIONS.MOVE_FILE_COPY_ALLOWED, + CopyAllowed = NativeMethods.MOVE_FILE_FLAGS.MOVE_FILE_COPY_ALLOWED, /// MOVE_FILE_DELAY_UNTIL_REBOOT @@ -58,7 +58,7 @@ public enum MoveOptions /// /// This value cannot be used with . /// - DelayUntilReboot = NativeMethods.MOVE_FILE_OPTIONS.MOVE_FILE_DELAY_UNTIL_REBOOT, + DelayUntilReboot = NativeMethods.MOVE_FILE_FLAGS.MOVE_FILE_DELAY_UNTIL_REBOOT, /// MOVE_FILE_WRITE_THROUGH @@ -69,14 +69,14 @@ public enum MoveOptions /// /// This value has no effect if is set. /// - WriteThrough = NativeMethods.MOVE_FILE_OPTIONS.MOVE_FILE_WRITE_THROUGH, + WriteThrough = NativeMethods.MOVE_FILE_FLAGS.MOVE_FILE_WRITE_THROUGH, /// MOVE_FILE_CREATE_HARDLINK /// Reserved for future use. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hardlink")] - CreateHardlink = NativeMethods.MOVE_FILE_OPTIONS.MOVE_FILE_CREATE_HARDLINK, + CreateHardlink = NativeMethods.MOVE_FILE_FLAGS.MOVE_FILE_CREATE_HARDLINK, /// MOVE_FILE_FAIL_IF_NOT_TRACKABLE @@ -84,6 +84,6 @@ public enum MoveOptions /// This situation can occur if the destination is a volume formatted with the FAT file system. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Trackable")] - FailIfNotTrackable = NativeMethods.MOVE_FILE_OPTIONS.MOVE_FILE_FAIL_IF_NOT_TRACKABLE + FailIfNotTrackable = NativeMethods.MOVE_FILE_FLAGS.MOVE_FILE_FAIL_IF_NOT_TRACKABLE } } diff --git a/AlphaFS/Filesystem/File Class/File.Copy.cs b/AlphaFS/Filesystem/File Class/File.Copy.cs index f5b04e749..3645b860c 100644 --- a/AlphaFS/Filesystem/File Class/File.Copy.cs +++ b/AlphaFS/Filesystem/File Class/File.Copy.cs @@ -760,8 +760,9 @@ internal static CopyMoveResult CopyMoveCore(CopyMoveResult copyMoveResult, Kerne // A file or folder will be deleted or renamed on Computer startup. bool delayUntilReboot; bool deleteOnStartup; + - ValidateAndUpdatePaths(transaction, sourcePath, destinationPath, copyOptions, moveOptions, pathFormat, out sourcePathLp, out destinationPathLp, out isCopy, out emulateMove, out delayUntilReboot, out deleteOnStartup); + ValidateAndUpdatePathsAndOptions(transaction, sourcePath, destinationPath, copyOptions, moveOptions, pathFormat, out sourcePathLp, out destinationPathLp, out isCopy, out emulateMove, out delayUntilReboot, out deleteOnStartup); var isMove = !isCopy; @@ -919,8 +920,8 @@ internal static CopyMoveResult CopyMoveCore(CopyMoveResult copyMoveResult, Kerne if (!isFolder) { - using (var safeHandle = CreateFileCore(transaction, sourcePathLp, ExtendedFileAttributes.Normal, null, FileMode.Open, 0, FileShare.Read, false, pathFormat)) - if (null != safeHandle && safeHandle.IsInvalid) + using (var safeHandle = CreateFileCore(transaction, sourcePathLp, ExtendedFileAttributes.Normal, null, FileMode.Open, 0, FileShare.Read, false, false, pathFormat)) + if (null != safeHandle) fileNameLp = sourcePathLp; } @@ -1022,6 +1023,13 @@ internal static bool CanOverwrite(MoveOptions? moveOptions) } + /// Checks if the flag is specified. + internal static bool HasCopySymbolicLink(CopyOptions? copyOptions) + { + return IsNotNull(copyOptions) && (copyOptions & CopyOptions.CopySymbolicLink) != 0; + } + + /// Checks if the flag is specified. private static bool HasDelayUntilReboot(MoveOptions? moveOptions) { @@ -1087,7 +1095,8 @@ private static bool IsReadOnlyOrHidden(FileAttributes fileAttributes) } - internal static void ValidateAndUpdatePaths(KernelTransaction transaction, string sourcePath, string destinationPath, CopyOptions? copyOptions, MoveOptions? moveOptions, PathFormat pathFormat, out string sourcePathLp, out string destinationPathLp, out bool isCopy, out bool emulateMove, out bool delayUntilReboot, out bool deleteOnStartup) + internal static void ValidateAndUpdatePathsAndOptions(KernelTransaction transaction, string sourcePath, string destinationPath, CopyOptions? copyOptions, MoveOptions? moveOptions, PathFormat pathFormat, + out string sourcePathLp, out string destinationPathLp, out bool isCopy, out bool emulateMove, out bool delayUntilReboot, out bool deleteOnStartup) { // MSDN: .NET3.5+: IOException: The sourceDirName and destDirName parameters refer to the same file or directory. // Do not use StringComparison.OrdinalIgnoreCase to allow renaming a folder with different casing. @@ -1108,7 +1117,7 @@ internal static void ValidateAndUpdatePaths(KernelTransaction transaction, strin // When destinationPath is null, the file or folder needs to be removed on Computer startup. deleteOnStartup = delayUntilReboot && null == destinationPath; - + if (pathFormat != PathFormat.LongFullPath) { // MSDN: .NET 4+ Trailing spaces are removed from the end of the path parameters before moving the directory. diff --git a/AlphaFS/Filesystem/File Class/File.Create.cs b/AlphaFS/Filesystem/File Class/File.Create.cs index cd74aa877..03c8f7d1c 100644 --- a/AlphaFS/Filesystem/File Class/File.Create.cs +++ b/AlphaFS/Filesystem/File Class/File.Create.cs @@ -320,13 +320,13 @@ internal static FileStream CreateFileStreamCore(KernelTransaction transaction, s try { - safeHandle = CreateFileCore(transaction, path, attributes, fileSecurity, mode, (FileSystemRights) access, share, true, pathFormat); + safeHandle = CreateFileCore(transaction, path, attributes, fileSecurity, mode, (FileSystemRights) access, share, true, false, pathFormat); return new FileStream(safeHandle, access, bufferSize, (attributes & ExtendedFileAttributes.Overlapped) != 0); } catch { - if (safeHandle != null) + if (null != safeHandle) safeHandle.Dispose(); throw; @@ -350,10 +350,14 @@ internal static FileStream CreateFileStreamCore(KernelTransaction transaction, s /// A constant that determines the access rights to use when creating access and audit rules for the file or directory. /// A constant that determines how the file or directory will be shared by processes. /// . - /// Indicates the format of the parameter. + /// + /// true suppress any Exception that might be thrown as a result from a failure, + /// such as ACLs protected directories or non-accessible reparse points. + /// + /// Indicates the format of the parameter. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Object needs to be disposed by caller.")] [SecurityCritical] - internal static SafeFileHandle CreateFileCore(KernelTransaction transaction, string path, ExtendedFileAttributes attributes, FileSecurity fileSecurity, FileMode fileMode, FileSystemRights fileSystemRights, FileShare fileShare, bool checkPath, PathFormat pathFormat) + internal static SafeFileHandle CreateFileCore(KernelTransaction transaction, string path, ExtendedFileAttributes attributes, FileSecurity fileSecurity, FileMode fileMode, FileSystemRights fileSystemRights, FileShare fileShare, bool checkPath, bool continueOnException, PathFormat pathFormat) { if (checkPath && pathFormat == PathFormat.RelativePath) Path.CheckSupportedPathFormat(path, true, true); @@ -403,21 +407,25 @@ internal static SafeFileHandle CreateFileCore(KernelTransaction transaction, str ? NativeMethods.CreateFile(pathLp, fileSystemRights, fileShare, securityAttributes, fileMode, attributes, IntPtr.Zero) : NativeMethods.CreateFileTransacted(pathLp, fileSystemRights, fileShare, securityAttributes, fileMode, attributes, IntPtr.Zero, transaction.SafeHandle, IntPtr.Zero, IntPtr.Zero); + var lastError = Marshal.GetLastWin32Error(); if (handle.IsInvalid) { handle.Close(); - NativeError.ThrowException(lastError, pathLp); - } + handle = null; + if (!continueOnException) + NativeError.ThrowException(lastError, pathLp); + } - if (isAppend) + else if (isAppend) { var stream = new FileStream(handle, FileAccess.Write, NativeMethods.DefaultFileBufferSize, (attributes & ExtendedFileAttributes.Overlapped) != 0); stream.Seek(0, SeekOrigin.End); } + return handle; } } diff --git a/AlphaFS/Filesystem/File Class/File.GetAccessControl.cs b/AlphaFS/Filesystem/File Class/File.GetAccessControl.cs index 680187df3..5f1b6b2c4 100644 --- a/AlphaFS/Filesystem/File Class/File.GetAccessControl.cs +++ b/AlphaFS/Filesystem/File Class/File.GetAccessControl.cs @@ -155,7 +155,7 @@ internal static T GetAccessControlCore(bool isFolder, string path, AccessCont // When GetNamedSecurityInfo() fails with ACCESS_DENIED, try again using GetSecurityInfo(). if (lastError == Win32Errors.ERROR_ACCESS_DENIED) - using (var handle = CreateFileCore(null, pathLp, ExtendedFileAttributes.BackupSemantics, null, FileMode.Open, FileSystemRights.Read, FileShare.Read, false, PathFormat.LongFullPath)) + using (var handle = CreateFileCore(null, pathLp, ExtendedFileAttributes.BackupSemantics, null, FileMode.Open, FileSystemRights.Read, FileShare.Read, false, false, PathFormat.LongFullPath)) return GetAccessControlHandleCore(true, isFolder, handle, includeSections, securityInfo); return GetSecurityDescriptor(lastError, isFolder, pathLp, pSecurityDescriptor); diff --git a/AlphaFS/Filesystem/File Class/File.GetChangeTime.cs b/AlphaFS/Filesystem/File Class/File.GetChangeTime.cs index caea4368d..239440cc8 100644 --- a/AlphaFS/Filesystem/File Class/File.GetChangeTime.cs +++ b/AlphaFS/Filesystem/File Class/File.GetChangeTime.cs @@ -169,7 +169,7 @@ internal static DateTime GetChangeTimeCore(KernelTransaction transaction, bool i var pathLp = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.CheckInvalidPathChars); - safeFileHandle = CreateFileCore(transaction, pathLp, isFolder ? ExtendedFileAttributes.BackupSemantics : ExtendedFileAttributes.Normal, null, FileMode.Open, FileSystemRights.ReadData, FileShare.ReadWrite, true, PathFormat.LongFullPath); + safeFileHandle = CreateFileCore(transaction, pathLp, isFolder ? ExtendedFileAttributes.BackupSemantics : ExtendedFileAttributes.Normal, null, FileMode.Open, FileSystemRights.ReadData, FileShare.ReadWrite, true, false, PathFormat.LongFullPath); } diff --git a/AlphaFS/Filesystem/File Class/File.GetLinkTargetInfo.cs b/AlphaFS/Filesystem/File Class/File.GetLinkTargetInfo.cs index 150e77478..7e4676a4b 100644 --- a/AlphaFS/Filesystem/File Class/File.GetLinkTargetInfo.cs +++ b/AlphaFS/Filesystem/File Class/File.GetLinkTargetInfo.cs @@ -36,7 +36,7 @@ public static partial class File [SecurityCritical] public static LinkTargetInfo GetLinkTargetInfo(string path) { - return GetLinkTargetInfoCore(null, false, path, PathFormat.RelativePath); + return GetLinkTargetInfoCore(null, false, path, false, PathFormat.RelativePath); } @@ -50,7 +50,7 @@ public static LinkTargetInfo GetLinkTargetInfo(string path) [SecurityCritical] public static LinkTargetInfo GetLinkTargetInfo(string path, PathFormat pathFormat) { - return GetLinkTargetInfoCore(null, false, path, pathFormat); + return GetLinkTargetInfoCore(null, false, path, false, pathFormat); } @@ -64,7 +64,7 @@ public static LinkTargetInfo GetLinkTargetInfo(string path, PathFormat pathForma [SecurityCritical] public static LinkTargetInfo GetLinkTargetInfoTransacted(KernelTransaction transaction, string path) { - return GetLinkTargetInfoCore(transaction, false, path, PathFormat.RelativePath); + return GetLinkTargetInfoCore(transaction, false, path, false, PathFormat.RelativePath); } @@ -79,7 +79,7 @@ public static LinkTargetInfo GetLinkTargetInfoTransacted(KernelTransaction trans [SecurityCritical] public static LinkTargetInfo GetLinkTargetInfoTransacted(KernelTransaction transaction, string path, PathFormat pathFormat) { - return GetLinkTargetInfoCore(transaction, false, path, pathFormat); + return GetLinkTargetInfoCore(transaction, false, path, false, pathFormat); } @@ -92,13 +92,17 @@ public static LinkTargetInfo GetLinkTargetInfoTransacted(KernelTransaction trans /// The transaction. /// Specifies that is a file or directory. /// The path to the reparse point. + /// + /// true suppress any Exception that might be thrown as a result from a failure, + /// such as ACLs protected directories or non-accessible reparse points. + /// /// Indicates the format of the path parameter(s). /// /// An instance of or containing information about the symbolic link /// or mount point pointed to by . /// [SecurityCritical] - internal static LinkTargetInfo GetLinkTargetInfoCore(KernelTransaction transaction, bool isFolder, string reparsePath, PathFormat pathFormat) + internal static LinkTargetInfo GetLinkTargetInfoCore(KernelTransaction transaction, bool isFolder, string reparsePath, bool continueOnException, PathFormat pathFormat) { var eAttributes = ExtendedFileAttributes.OpenReparsePoint; @@ -106,8 +110,8 @@ internal static LinkTargetInfo GetLinkTargetInfoCore(KernelTransaction transacti eAttributes |= ExtendedFileAttributes.BackupSemantics; - using (var safeHandle = CreateFileCore(transaction, reparsePath, eAttributes, null, FileMode.Open, 0, FileShare.ReadWrite, pathFormat != PathFormat.LongFullPath, pathFormat)) - return Device.GetLinkTargetInfo(safeHandle, reparsePath); + using (var safeHandle = CreateFileCore(transaction, reparsePath, eAttributes, null, FileMode.Open, 0, FileShare.ReadWrite, pathFormat != PathFormat.LongFullPath, continueOnException, pathFormat)) + return null != safeHandle ? Device.GetLinkTargetInfo(safeHandle, reparsePath) : null; } } } diff --git a/AlphaFS/Filesystem/File Class/File.GetSize.cs b/AlphaFS/Filesystem/File Class/File.GetSize.cs index 608986bd5..b7e2285e7 100644 --- a/AlphaFS/Filesystem/File Class/File.GetSize.cs +++ b/AlphaFS/Filesystem/File Class/File.GetSize.cs @@ -104,7 +104,7 @@ internal static long GetSizeCore(KernelTransaction transaction, SafeFileHandle s { var pathLp = Path.GetExtendedLengthPathCore(transaction, path, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.FullCheck); - safeFileHandle = CreateFileCore(transaction, pathLp, ExtendedFileAttributes.Normal, null, FileMode.Open, FileSystemRights.ReadData, FileShare.Read, true, PathFormat.LongFullPath); + safeFileHandle = CreateFileCore(transaction, pathLp, ExtendedFileAttributes.Normal, null, FileMode.Open, FileSystemRights.ReadData, FileShare.Read, true, false, PathFormat.LongFullPath); } diff --git a/AlphaFS/Filesystem/File Class/File.Open.cs b/AlphaFS/Filesystem/File Class/File.Open.cs index acc6195e3..53d3725ef 100644 --- a/AlphaFS/Filesystem/File Class/File.Open.cs +++ b/AlphaFS/Filesystem/File Class/File.Open.cs @@ -995,7 +995,7 @@ internal static FileStream OpenCore(KernelTransaction transaction, string path, try { - safeHandle = CreateFileCore(transaction, path, attributes, security, mode, rights, share, true, pathFormat); + safeHandle = CreateFileCore(transaction, path, attributes, security, mode, rights, share, true, false, pathFormat); return new FileStream(safeHandle, access, bufferSize ?? NativeMethods.DefaultFileBufferSize, (attributes & ExtendedFileAttributes.Overlapped) != 0); } diff --git a/AlphaFS/Filesystem/File Class/File.SetFileTime.cs b/AlphaFS/Filesystem/File Class/File.SetFileTime.cs index f07c52a9f..30b665767 100644 --- a/AlphaFS/Filesystem/File Class/File.SetFileTime.cs +++ b/AlphaFS/Filesystem/File Class/File.SetFileTime.cs @@ -878,7 +878,7 @@ internal static void SetFsoDateTimeCore(KernelTransaction transaction, bool isFo using (var lastAccessTime = SafeGlobalMemoryBufferHandle.FromLong(lastAccessTimeUtc.HasValue ? lastAccessTimeUtc.Value.ToFileTimeUtc() : (long?)null)) using (var lastWriteTime = SafeGlobalMemoryBufferHandle.FromLong(lastWriteTimeUtc.HasValue ? lastWriteTimeUtc.Value.ToFileTimeUtc() : (long?)null)) - using (var safeHandle = CreateFileCore(transaction, path, attributes, null, FileMode.Open, FileSystemRights.WriteAttributes, FileShare.Delete | FileShare.Write, false, pathFormat)) + using (var safeHandle = CreateFileCore(transaction, path, attributes, null, FileMode.Open, FileSystemRights.WriteAttributes, FileShare.Delete | FileShare.Write, false, false, pathFormat)) { var success = NativeMethods.SetFileTime(safeHandle, creationTime, lastAccessTime, lastWriteTime); diff --git a/AlphaFS/Filesystem/Native Structures And Enumerations/COPY_FILE_OPTIONS.cs b/AlphaFS/Filesystem/Native Structures And Enumerations/COPY_FILE_FLAGS.cs similarity index 98% rename from AlphaFS/Filesystem/Native Structures And Enumerations/COPY_FILE_OPTIONS.cs rename to AlphaFS/Filesystem/Native Structures And Enumerations/COPY_FILE_FLAGS.cs index 0da1299b2..0e107fe69 100644 --- a/AlphaFS/Filesystem/Native Structures And Enumerations/COPY_FILE_OPTIONS.cs +++ b/AlphaFS/Filesystem/Native Structures And Enumerations/COPY_FILE_FLAGS.cs @@ -25,7 +25,7 @@ namespace Alphaleonis.Win32.Filesystem { internal static partial class NativeMethods { - public enum COPY_FILE_OPTIONS + public enum COPY_FILE_FLAGS { /// COPY_FILE_FAIL_IF_EXISTS /// The copy operation fails immediately if the target file already exists. diff --git a/AlphaFS/Filesystem/Native Structures And Enumerations/MOVE_FILE_OPTIONS.cs b/AlphaFS/Filesystem/Native Structures And Enumerations/MOVE_FILE_FLAGS.cs similarity index 95% rename from AlphaFS/Filesystem/Native Structures And Enumerations/MOVE_FILE_OPTIONS.cs rename to AlphaFS/Filesystem/Native Structures And Enumerations/MOVE_FILE_FLAGS.cs index f1920eca6..8171d942e 100644 --- a/AlphaFS/Filesystem/Native Structures And Enumerations/MOVE_FILE_OPTIONS.cs +++ b/AlphaFS/Filesystem/Native Structures And Enumerations/MOVE_FILE_FLAGS.cs @@ -25,7 +25,7 @@ namespace Alphaleonis.Win32.Filesystem { internal static partial class NativeMethods { - public enum MOVE_FILE_OPTIONS + public enum MOVE_FILE_FLAGS { /// No MoveOptions used, this fails when the file name already exists. None = 0, @@ -39,7 +39,7 @@ public enum MOVE_FILE_OPTIONS /// MOVE_FILE_COPY_ALLOWED /// If the file is to be moved to a different volume, the function simulates the move by using the CopyFile and DeleteFile functions. - /// This value cannot be used with . + /// This value cannot be used with . /// MOVE_FILE_COPY_ALLOWED = 2, @@ -52,7 +52,7 @@ public enum MOVE_FILE_OPTIONS /// Consequently, this parameter enables the function to delete paging files from previous startups. /// This value can only be used if the process is in the context of a user who belongs to the administrators group or the LocalSystem account. /// - /// This value cannot be used with . + /// This value cannot be used with . /// MOVE_FILE_DELAY_UNTIL_REBOOT = 4, @@ -63,7 +63,7 @@ public enum MOVE_FILE_OPTIONS /// Setting this value guarantees that a move performed as a copy and delete operation is flushed to disk before the function returns. /// The flush occurs at the end of the copy operation. /// - /// This value has no effect if is set. + /// This value has no effect if is set. /// MOVE_FILE_WRITE_THROUGH = 8, diff --git a/AlphaFS/Filesystem/Path Class/Path.ShortLongConversions.cs b/AlphaFS/Filesystem/Path Class/Path.ShortLongConversions.cs index 6368f3aae..cf9f6f093 100644 --- a/AlphaFS/Filesystem/Path Class/Path.ShortLongConversions.cs +++ b/AlphaFS/Filesystem/Path Class/Path.ShortLongConversions.cs @@ -244,6 +244,14 @@ internal static string GetRegularPathCore(string path, GetFullPathOptions option if (options != GetFullPathOptions.None) path = ApplyFullPathOptions(path, options); + + if (path.StartsWith(DosDeviceUncPrefix, StringComparison.OrdinalIgnoreCase)) + return UncPrefix + path.Substring(DosDeviceUncPrefix.Length); + + if (path.StartsWith(NonInterpretedPathPrefix, StringComparison.OrdinalIgnoreCase)) + return path.Substring(NonInterpretedPathPrefix.Length); + + return path.StartsWith(GlobalRootPrefix, StringComparison.OrdinalIgnoreCase) || path.StartsWith(VolumePrefix, StringComparison.OrdinalIgnoreCase) || !path.StartsWith(LongPathPrefix, StringComparison.OrdinalIgnoreCase) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9754d67f..08186ccd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Version vNext (xxxx-xx-xx) ### Bugs Fixed - Issue #268: There are multiple warnings when building the documentation. +- Issue #292: `CopyOptions.CopySymbolicLink` not working in 2.1.2 (Thx v2kiran) - Issue #325: `DeleteEmptySubdirectories` (with `recursive=true`) throws `System.IO.DirectoryNotFoundException` (Thx kryvoplias) - Issue #328: Several instances of `ArgumentException.ParamName` not set/used correctly (Thx elgonzo) - Issue #330: Correct the parameter order for Privilege class constructors using the `ArgumentNullException`. @@ -34,6 +35,7 @@ Version vNext (xxxx-xx-xx) - Issue #345: `AlreadyExistsException` should only throw message from 1 error. - Issue #348: Implement method `Directory.GetLinkTargetInfo` - Issue #350: Add overloaded methods `Directory.GetFileSystemEntryInfo` +- Issue #351: Enable copying of Directory symbolic links. Version 2.1.3 (2017-06-05) -------------