/// <summary> /// Copies the <paramref name="sourceFileInfo"/> to the <paramref name="targetFileInfo"/>. /// </summary> /// <param name="sourceFileInfo">Source file to be copied.</param> /// <param name="targetFileInfo">Target file.</param> /// <returns><see langword="true"/>, if the file was created or overwritten; <see langword="false"/>, if the file was skipped.</returns> /// <exception cref="ArgumentNullException"><paramref name="sourceFileInfo"/> or <paramref name="targetFileInfo"/> is <see langword="null"/>.</exception> /// <exception cref="FileNotFoundException"><paramref name="sourceFileInfo"/> does not exist.</exception> public static bool CopyFile(LongPathFileInfo sourceFileInfo, LongPathFileInfo targetFileInfo) { if (sourceFileInfo == null) { throw new ArgumentNullException(nameof(sourceFileInfo)); } if (targetFileInfo == null) { throw new ArgumentNullException(nameof(targetFileInfo)); } // Will throw an exception, if the source file does not exist. if (sourceFileInfo.Match(targetFileInfo)) { return(false); } LongPathDirectory.CreateDirectory(targetFileInfo.DirectoryName); sourceFileInfo.CopyTo(targetFileInfo.FullName, true); LongPathCommon.SetAttributes(targetFileInfo.FullName, FileAttributes.Normal); LongPathCommon.SetTimestamps(targetFileInfo.FullName, sourceFileInfo.CreationTime, sourceFileInfo.LastAccessTime, sourceFileInfo.LastWriteTime); return(true); }
/// <summary> /// Gets the LazyCopy reparse data from the <paramref name="path"/> given. /// </summary> /// <param name="path">Path to the file to get the reparse data from.</param> /// <returns> /// Reparse data found or <see langword="null"/>, if it's not set. /// </returns> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception> /// <exception cref="IOException">File cannot be opened.</exception> /// <exception cref="InvalidOperationException">Reparse point data cannot be retrieved.</exception> public static LazyCopyFileData GetReparseData(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } string normalizedPath = LongPathCommon.NormalizePath(path); LongPathFileInfo fileInfo = new LongPathFileInfo(normalizedPath); if (!fileInfo.Exists || !fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)) { return(null); } try { LazyCopyReparseData data = ReparsePointHelper.GetReparsePointData <LazyCopyReparseData>(path, LazyCopyFileHelper.LazyCopyReparseTag, LazyCopyFileHelper.LazyCopyReparseGuid); bool useCustomHandler = data.UseCustomHandler > 0; return(new LazyCopyFileData { UseCustomHandler = useCustomHandler, FileSize = data.FileSize, RemotePath = useCustomHandler ? data.RemotePath : PathHelper.ChangeDeviceNameToDriveLetter(data.RemotePath) }); } catch (InvalidOperationException) { return(null); } }
string IDirectoryAdapter.GetFullPath(string path) { AssertAllowed(path); #if !MONO var mTx = CurrentTransaction(); if (mTx.HasValue) { return(((IDirectoryAdapter)mTx.Value).GetFullPath(path)); } #endif return(LongPathCommon.NormalizeLongPath(path)); }
public static string GetFullPath(string path) { Contract.Requires(!string.IsNullOrEmpty(path)); Contract.Ensures(Contract.Result <string>() != null); if (path.StartsWith("file:///")) { return(new Uri(path).LocalPath); } return(LongPathCommon.NormalizeLongPath(path)); }
/// <summary> /// Changes the DOS device name for the <paramref name="path"/> given to the according drive letter. /// </summary> /// <param name="path">Path to replace DOS device name for.</param> /// <returns>New path string.</returns> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception> /// <exception cref="ArgumentException"><paramref name="path"/> does not start with a DOS device name.</exception> /// <exception cref="InvalidOperationException">Drive letter could not be found for the <paramref name="path"/> given.</exception> /// <remarks> /// This method also supports UNC paths. /// </remarks> public static string ChangeDeviceNameToDriveLetter(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } path = LongPathCommon.RemoveLongPathPrefix(path); // Path is already converted. if (Regex.IsMatch(path, @"^(?:\\\\|[a-z]:)", RegexOptions.IgnoreCase)) { return(path); } if (!path.StartsWith(@"\Device\", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Path given does not start with a device name: {0}", path), nameof(path)); } // Convert Network Redirector path to UNC. if (path.StartsWith(@"\Device\Mup\", StringComparison.OrdinalIgnoreCase)) { return(@"\\" + path.Substring(12)); } string driveLetter; string deviceName = null; // Find the proper device name. lock (PathHelper.DriveLetterToDeviceName) { Func <string> findDeviceName = () => deviceName = PathHelper.DriveLetterToDeviceName.Values.FirstOrDefault(dn => path.StartsWith(dn, StringComparison.OrdinalIgnoreCase)); if (findDeviceName() == null) { PathHelper.UpdateDeviceMappings(); if (findDeviceName() == null) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to find root path for {0}", path)); } } // Find the root path for the device name given. driveLetter = PathHelper.DriveLetterToDeviceName.Keys.First(rp => string.Equals(PathHelper.DriveLetterToDeviceName[rp], deviceName)); } // Change parent. return(driveLetter + path.Substring(deviceName.Length)); }
/// <summary> /// Changes the drive letter for the <paramref name="path"/> given to the according DOS device name. /// </summary> /// <param name="path">Path to replace the drive letter for.</param> /// <returns>New path string.</returns> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception> /// <exception cref="ArgumentException"><paramref name="path"/> is too short.</exception> /// <exception cref="InvalidOperationException">The according DOS device name could not be found.</exception> /// <remarks> /// This method also supports UNC paths. /// </remarks> public static string ChangeDriveLetterToDeviceName(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (path.Length < 2) { throw new ArgumentException("Path is too short.", nameof(path)); } // If the path is already converted, skip. if (path.StartsWith(@"\Device\", StringComparison.OrdinalIgnoreCase)) { return(path); } path = LongPathCommon.RemoveLongPathPrefix(path); // Convert UNC path to a Network Redirector path. if (path.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase) || path.StartsWith(@"//", StringComparison.OrdinalIgnoreCase)) { return(@"\Device\Mup\" + path.Substring(2)); } // C:\, for example. Or will contain invalid string and will fail later. string driveLetter = path.Substring(0, 2) + "\\"; string deviceName; lock (PathHelper.DriveLetterToDeviceName) { // Update the device-root caches, if the root path is not there. if (!PathHelper.DriveLetterToDeviceName.ContainsKey(driveLetter)) { PathHelper.UpdateDeviceMappings(); if (!PathHelper.DriveLetterToDeviceName.ContainsKey(driveLetter)) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to find DOS device name for path: {0}", path)); } } deviceName = PathHelper.DriveLetterToDeviceName[driveLetter]; } // Change parent. return(deviceName + path.Substring(driveLetter.Length)); }
// http://stackoverflow.com/questions/8991192/check-filesize-without-opening-file-in-c public override long GetSize() { using (var fh = LongPathFile.GetFileHandle(Path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, FileOptions.None)) { if (fh.IsInvalid) { throw LongPathCommon.GetExceptionFromLastWin32Error(); } long size; if (NativeMethods.GetFileSizeEx(fh, out size)) { return(size); } throw LongPathCommon.GetExceptionFromLastWin32Error(); } }
public override DateTimeOffset?GetLastModifiedTimeUtc() { if (!Exists()) { return(null); } WIN32_FILE_ATTRIBUTE_DATA data; if (!NativeMethods.GetFileAttributesEx( Path.LongFullPath, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, out data)) { throw LongPathCommon.GetExceptionFromLastWin32Error(); } var ft = data.ftLastWriteTime; return(DateTime.FromFileTime((((long)ft.dwHighDateTime) << 32) + ft.dwLowDateTime)); }
/// <summary> /// Creates the symbolic link. /// </summary> /// <param name="linkPath">Link path.</param> /// <param name="targetPath">Target path.</param> /// <param name="linkFlag">Type of the symbolic link.</param> /// <exception cref="ArgumentNullException"><paramref name="targetPath"/> or <paramref name="linkPath"/> is <see langword="null"/> or empty.</exception> /// <exception cref="ArgumentException"><paramref name="targetPath"/> is equal to the <paramref name="linkPath"/>.</exception> /// <exception cref="FileNotFoundException"><paramref name="targetPath"/> was not found and <paramref name="linkFlag"/> is <see cref="SymbolicLinkFlag.File"/>.</exception> /// <exception cref="DirectoryNotFoundException"><paramref name="targetPath"/> was not found and <paramref name="linkFlag"/> is <see cref="SymbolicLinkFlag.Directory"/>.</exception> /// <exception cref="InvalidOperationException">Symbolic link was not created.</exception> /// <remarks> /// The <c>SE_CREATE_SYMBOLIC_LINK_NAME</c> privilege is required to create symbolic links. /// </remarks> private static void CreateSymbolicLink(string linkPath, string targetPath, SymbolicLinkFlag linkFlag) { if (string.IsNullOrEmpty(targetPath)) { throw new ArgumentNullException(nameof(targetPath)); } if (string.IsNullOrEmpty(linkPath)) { throw new ArgumentNullException(nameof(linkPath)); } if (string.Equals(targetPath, linkPath, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Target path is equal to the link path: {0}", targetPath)); } string normalizedLinkPath = LongPathCommon.NormalizePath(linkPath); string normalizedTargetPath = LongPathCommon.NormalizePath(targetPath); if (!LongPathCommon.Exists(targetPath)) { if (linkFlag == SymbolicLinkFlag.Directory) { throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, "Target directory not found: {0}", targetPath)); } throw new FileNotFoundException("Target file not found.", targetPath); } if (!NativeMethods.CreateSymbolicLink(normalizedLinkPath, normalizedTargetPath, linkFlag)) { Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to create symbolic link: {0} -> {1}", linkPath, targetPath), nativeException); } }
/// <summary> /// Creates a new <c>LazyCopy</c> file. /// </summary> /// <param name="path">Path to the reparse point to get data from.</param> /// <param name="fileData">Reparse file data to be set for the <paramref name="path"/>.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="path"/> is <see langword="null"/> or empty. /// <para>-or-</para> /// <paramref name="fileData"/> is <see langword="null"/>. /// <para>-or-</para> /// <paramref name="fileData"/> contains <see langword="null"/> or empty file path. /// </exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="fileData"/> contains negative file size.</exception> /// <exception cref="IOException">File cannot be created.</exception> /// <exception cref="InvalidOperationException">Reparse point data cannot be set.</exception> public static void CreateLazyCopyFile(string path, LazyCopyFileData fileData) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (fileData == null) { throw new ArgumentNullException(nameof(fileData)); } if (string.IsNullOrEmpty(fileData.RemotePath)) { throw new ArgumentNullException(nameof(fileData)); } if (fileData.FileSize < 0) { throw new ArgumentOutOfRangeException(nameof(fileData), fileData.FileSize, "File size is negative."); } string normalizedPath = LongPathCommon.NormalizePath(path); LongPathFileInfo fileInfo = new LongPathFileInfo(normalizedPath); bool shouldCreateFile = false; if (!fileInfo.Exists || fileInfo.Length > 0) { LongPathDirectory.CreateDirectory(fileInfo.DirectoryName); shouldCreateFile = true; } else if (!fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)) { shouldCreateFile = true; } else if (fileInfo.Attributes.HasFlag(FileAttributes.ReadOnly)) { fileInfo.Attributes &= ~FileAttributes.ReadOnly; } if (shouldCreateFile) { using (fileInfo.Create()) { // Do nothing. } } // If the original file is empty, we don't need to set a reparse point for it. if (fileData.FileSize == 0) { return; } ReparsePointHelper.SetReparsePointData( path, new object[] // Custom serialization layout for the LazyCopyFileData object. { (long)(fileData.UseCustomHandler ? 1L : 0L), (long)fileData.FileSize, // Add the prefix, if the custom handling is needed for the file. Encoding.Unicode.GetBytes( (fileData.UseCustomHandler ? fileData.RemotePath : PathHelper.ChangeDriveLetterToDeviceName(fileData.RemotePath)) + '\0') }, LazyCopyFileHelper.LazyCopyReparseTag, LazyCopyFileHelper.LazyCopyReparseGuid); // Set the proper file attributes. LongPathCommon.SetAttributes(path, FileAttributes.ReparsePoint | FileAttributes.NotContentIndexed | FileAttributes.Offline); }
/// <summary> /// Sets the reparse point data for the <paramref name="path"/> given. /// </summary> /// <param name="path">File or directory to set the reparse point data for.</param> /// <param name="data"> /// Reparse point data to be set. /// It should be a value type, or an <see cref="IEnumerable"/>, and it should not contain /// reparse point data header information, because this function handles it separately. /// </param> /// <param name="reparseTag">Reparse point tag.</param> /// <param name="reparseGuid">Reparse point <see cref="Guid"/>. Must be specified, if the <paramref name="reparseTag"/> is a non-Microsoft tag.</param> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception> /// <exception cref="ArgumentException"> /// <paramref name="data"/> is not a collection or a value type. /// <para>-or-</para> /// <paramref name="reparseTag"/> is a Microsoft tag, but the <paramref name="reparseGuid"/> is not <see langword="null"/>. /// <para>-or-</para> /// <paramref name="reparseTag"/> is a non-Microsoft tag, but the <paramref name="reparseGuid"/> is <see langword="null"/>. /// </exception> /// <exception cref="InvalidOperationException"> /// Reparse point <paramref name="data"/> cannot be set for the <paramref name="path"/>. /// </exception> /// <exception cref="IOException"> /// <paramref name="path"/> cannot be accessed. /// </exception> /// <remarks> /// This method will <i>NOT</i> update file attributes.<br/> /// For example, the <see cref="FileAttributes.ReparsePoint"/> will not be set. /// </remarks> public static void SetReparsePointData(string path, object data, int reparseTag, Guid?reparseGuid) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } ReparsePointHelper.ValidateTagAndGuid(reparseTag, reparseGuid); bool isMicrosoftTag = ReparsePointHelper.IsMicrosoftTag(reparseTag); string normalizedPath = LongPathCommon.NormalizePath(path); if (!LongPathCommon.Exists(normalizedPath)) { throw new IOException(string.Format(CultureInfo.InvariantCulture, "{0} cannot be found.", path)); } using (SafeFileHandle handle = NativeMethods.CreateFile( normalizedPath, AccessRights.GenericWrite, FileShare.None, IntPtr.Zero, FileMode.Open, EFileAttributes.OpenReparsePoint | EFileAttributes.BackupSemantics, IntPtr.Zero)) { if (handle.IsInvalid) { Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); throw new IOException(string.Format(CultureInfo.InvariantCulture, "Unable to open: {0}", path), nativeException); } int dataSize = MarshalingHelper.GetObjectSize(data); object header = isMicrosoftTag ? (object)new ReparseDataBufferHeader { ReparseDataLength = unchecked ((ushort)dataSize), ReparseTag = reparseTag } : new ReparseGuidDataBufferHeader { ReparseDataLength = unchecked ((ushort)dataSize), ReparseTag = reparseTag, ReparseGuid = reparseGuid.Value }; int headerSize = Marshal.SizeOf(header); int tagDataLength = headerSize + dataSize; using (ResizableBuffer buffer = new ResizableBuffer(Math.Max(ReparsePointHelper.BufferSize, tagDataLength))) { MarshalingHelper.MarshalObjectToPointer(new[] { header, data }, buffer.DangerousGetPointer()); // Set the reparse point data. int bytesReturned; bool success = NativeMethods.DeviceIoControl( handle, ReparsePointHelper.SetReparsePointControlCode, buffer.DangerousGetPointer(), tagDataLength, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!success) { Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to set the reparse point data: {0}", path), nativeException); } } } }
/// <summary> /// Gets the reparse point data from the <paramref name="path"/> given. /// </summary> /// <typeparam name="T"> /// Reparse point buffer data type. /// It should not contain reparse point data header information, because this function handles it separately. /// </typeparam> /// <param name="path">Path to the reparse point to get data from.</param> /// <param name="reparseTag">Reparse point tag.</param> /// <param name="reparseGuid">Reparse point <see cref="Guid"/>. Must be specified, if the <paramref name="reparseTag"/> is a non-Microsoft tag.</param> /// <returns>Reparse point data found.</returns> /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/> or empty.</exception> /// <exception cref="ArgumentException"> /// <paramref name="reparseTag"/> is a Microsoft tag, but the <paramref name="reparseGuid"/> is not <see langword="null"/>. /// <para>-or-</para> /// <paramref name="reparseTag"/> is a non-Microsoft tag, but the <paramref name="reparseGuid"/> is <see langword="null"/>. /// </exception> /// <exception cref="InvalidOperationException"> /// <paramref name="path"/> cannot be found. /// <para>-or-</para> /// <paramref name="path"/> is not a reparse point. /// <para>-or-</para> /// <paramref name="path"/> reparse point cannot be opened. /// <para>-or-</para> /// <paramref name="path"/> reparse point data cannot be retrieved. /// <para>-or-</para> /// <paramref name="path"/> reparse point tag or GUID is invalid. /// </exception> /// <remarks> /// See <c>http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx</c> for more details about reparse point tags. /// </remarks> public static T GetReparsePointData <T>(string path, int reparseTag, Guid?reparseGuid) where T : struct { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } ReparsePointHelper.ValidateTagAndGuid(reparseTag, reparseGuid); bool isMicrosoftTag = ReparsePointHelper.IsMicrosoftTag(reparseTag); string normalizedPath = LongPathCommon.NormalizePath(path); bool isDirectory; if (!LongPathCommon.Exists(normalizedPath, out isDirectory)) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Path does not exist: {0}", path)); } // Check, whether the path given is a reparse point. FileAttributes attributes = isDirectory ? new LongPathDirectoryInfo(normalizedPath).Attributes : LongPathFile.GetAttributes(normalizedPath); if (!attributes.HasFlag(FileAttributes.ReparsePoint)) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Path given is not a reparse point: {0}", path)); } using (SafeFileHandle handle = NativeMethods.CreateFile( normalizedPath, AccessRights.GenericRead, FileShare.Read, IntPtr.Zero, FileMode.Open, EFileAttributes.OpenReparsePoint | EFileAttributes.BackupSemantics, IntPtr.Zero)) { if (handle.IsInvalid) { Exception nativeException = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to open reparse point: {0}", path), nativeException); } // Make sure that the buffer will be large enough to hold reparse point data. int initialBufferSize = Math.Max(ReparsePointHelper.BufferSize, Marshal.SizeOf(typeof(ReparseGuidDataBufferHeader)) + Marshal.SizeOf(typeof(T))); using (ResizableBuffer buffer = new ResizableBuffer(initialBufferSize)) { // Query the reparse point data. int bytesReturned; bool success = NativeMethods.DeviceIoControl( handle, ReparsePointHelper.GetReparsePointControlCode, IntPtr.Zero, 0, buffer.DangerousGetPointer(), buffer.ByteLength, out bytesReturned, IntPtr.Zero); int headerSize = isMicrosoftTag ? Marshal.SizeOf(typeof(ReparseDataBufferHeader)) : Marshal.SizeOf(typeof(ReparseGuidDataBufferHeader)); if (!success) { int hr = Marshal.GetHRForLastWin32Error(); if (hr != NativeMethods.ErrorMoreData) { Exception nativeException = Marshal.GetExceptionForHR(hr); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to get the reparse point data: {0}", path), nativeException); } // Read the ReparseDataLength value, and resize buffer to fit the data. int dataSize = headerSize + Marshal.ReadInt16(buffer.DangerousGetPointer(), 4 /* sizeof(uint) */); buffer.Resize(dataSize); success = NativeMethods.DeviceIoControl( handle, ReparsePointHelper.GetReparsePointControlCode, IntPtr.Zero, 0, buffer.DangerousGetPointer(), buffer.ByteLength, out bytesReturned, IntPtr.Zero); if (!success) { Exception nativeException = Marshal.GetExceptionForHR(hr); throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Unable to get the reparse point data: {0}", path), nativeException); } } // Make sure that the reparse tag is correct. uint tag = unchecked ((uint)Marshal.ReadInt32(buffer.DangerousGetPointer())); if (tag != unchecked ((uint)reparseTag)) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Reparse point tag is invalid. Path has 0x{0:X8}, but 0x{1:X8} is specified.", tag, reparseTag)); } // Make sure that the reparse point GUID is correct, if needed. if (!isMicrosoftTag) { ReparseGuidDataBufferHeader header = (ReparseGuidDataBufferHeader)Marshal.PtrToStructure(buffer.DangerousGetPointer(), typeof(ReparseGuidDataBufferHeader)); if (header.ReparseGuid != reparseGuid) { throw new InvalidOperationException(string.Format( CultureInfo.InvariantCulture, "Reparse point GUID is invalid. Path has {0}, but {1} is specified.", header.ReparseGuid.ToString("N"), reparseGuid.Value.ToString("N"))); } } return((T)Marshal.PtrToStructure(buffer.DangerousGetPointer() + headerSize, typeof(T))); } } }