/// <summary> /// This sample application accepts two input parameters: /// * source file - file with actual data which content should be copied to the target file, when it's opened. /// * target file - empty file to be created. When this file is opened, its contents are downloaded from the source file. /// </summary> /// <param name="args"></param> static void Main(string[] args) { if (args.Length != 2 || string.IsNullOrEmpty(args[0]) || string.IsNullOrEmpty(args[1])) { Console.Out.WriteLine("sampleclient.exe \"<source_file_with_data>\" \"<local_file>\""); return; } string sourceFileName = args[0].Trim(); string targetFileName = args[1].Trim(); var targetFile = new LongPathFileInfo(targetFileName); if (sourceFileName.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || sourceFileName.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { LazyCopyFileHelper.CreateLazyCopyFile(targetFile.FullName, new LazyCopyFileData { RemotePath = sourceFileName, FileSize = 404, UseCustomHandler = true }); } else { var sourceFile = new LongPathFileInfo(sourceFileName); if (!sourceFile.Exists) { Console.Out.WriteLine("Source file doesn't exist: " + sourceFile); return; } LazyCopyFileHelper.CreateLazyCopyFile(targetFile.FullName, new LazyCopyFileData { RemotePath = sourceFile.FullName, FileSize = sourceFile.Length }); } }
/// <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> /// Checks whether two files have the same size and timestamps. /// </summary> /// <param name="file1">First file info.</param> /// <param name="file2">Second file info.</param> /// <returns>Whether or not the files are likely the same.</returns> /// <exception cref="ArgumentNullException"><paramref name="file1"/> or <paramref name="file2"/> is <see langword="null"/>.</exception> /// <exception cref="FileNotFoundException"><paramref name="file1"/> file does not exist.</exception> public static bool Match(this LongPathFileInfo file1, LongPathFileInfo file2) { if (file1 == null) { throw new ArgumentNullException(nameof(file1)); } if (!file1.Exists) { throw new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, "File file does not exist: {0}", file1.FullName)); } if (file2 == null) { throw new ArgumentNullException(nameof(file2)); } return file2.Exists && file1.Length == file2.Length && file1.CreationTimeUtc == file2.CreationTimeUtc && file1.LastWriteTimeUtc == file2.LastWriteTimeUtc; }
/// <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> /// 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; } }