/// <summary> /// Similar to CopyFile, but engineered for large (>1GB) files, as it /// uses a copy marker /// </summary> public static void CopyLargeFile ( FileInfo sourceFile, FileInfo targetFile, CollisionRemediation cr) { // TODO Pri 2 (Perf): Use PowerShell to do the copy directly if sender // and receiver machines are not the current machine Debug.Assert(sourceFile != null); Common.WriteLine ( "BEGIN Copying {0} to {1}", sourceFile.FullName, targetFile.FullName ); if (File.Exists(targetFile.FullName + Constants.COPY_MARKER)) { Common.WriteVerboseLine ( "Deleting existing transfer artifact {0}", targetFile.FullName + Constants.COPY_MARKER ); File.Delete(targetFile.FullName + Constants.COPY_MARKER); } Boolean bail = false; Boolean throwex = false; Boolean proceed = true; if (targetFile.Exists) { proceed = false; switch (cr) { case CollisionRemediation.Break: case CollisionRemediation.Passive: case CollisionRemediation.FillIn: Common.WriteLine("Target file exists, exiting"); bail = true; break; case CollisionRemediation.Mirror: if (FileCompare.AreSameBinaryViaFullScan(sourceFile, targetFile)) // TODO Pri 2 (Perf): Avoid full scan? { Common.WriteLine("Target file already exists (and is identical), exiting"); } else { Common.WriteLine("Target file exists, but is different: deleting"); File.Delete(targetFile.FullName); proceed = true; } break; case CollisionRemediation.Overwrite: Common.WriteLine("Target file exists, deleting"); File.Delete(targetFile.FullName); proceed = true; break; case CollisionRemediation.Throw: throwex = true; break; } } if (throwex) { throw new ScarabException("Target file '{0}' already exists", targetFile.FullName); } else if (proceed) { Common.WriteVerboseLine ( "Copying {0} to {1}", sourceFile.FullName, targetFile.FullName + Constants.COPY_MARKER ); // E.g., to \\Path\LARGE_FILE_TEMPLATE.vhd.transferring sourceFile.CopyTo(targetFile.FullName + Constants.COPY_MARKER); Common.WriteVerboseLine ( "Renaming {0} to {1}", targetFile.FullName + Constants.COPY_MARKER, targetFile.FullName ); File.Move(targetFile.FullName + Constants.COPY_MARKER, targetFile.FullName); Common.WriteLine ( "COMPLETE Copying {0} to {1}", sourceFile.FullName, targetFile.FullName ); } else if (bail) { } }
/// <summary> /// Copies a single file /// </summary> /// <param name="sourceFile">Source directory node</param> /// <param name="targetFile">Directory node to where the source directory tree should be copied</param> /// <param name="cr">How to handle collisions</param> /// <param name="deleteSourceFile">Remove source file (a.k.a. MOVE)</param> private static void CopyFileWorker ( string sourceFilePath, string targetFilePath, CollisionRemediation cr) { Debug.Assert(sourceFilePath != null); Debug.Assert(targetFilePath != null); Common.WriteVerboseLine ( "--> FileSync.CopyFile: {0} {1} {2}", sourceFilePath, targetFilePath, cr.ToString() ); //string switches = "/F /Z /V "; Boolean throwEx = false; /* * XCOPY source [destination] [/A | /M] [/D[:date]] [/P] [/S [/E]] [/V] [/W] * [/C] [/I] [/Q] [/F] [/L] [/G] [/H] [/R] [/T] [/U] * [/K] [/N] [/O] [/X] [/Y] [/-Y] [/Z] [/B] [/J] * [/EXCLUDE:file1[+file2][+file3]...] * source Specifies the file(s) to copy. * destination Specifies the location and/or name of new files. * /A Copies only files with the archive attribute set, * doesn't change the attribute. * /M Copies only files with the archive attribute set, * turns off the archive attribute. * /D:m-d-y Copies files changed on or after the specified date. * If no date is given, copies only those files whose * source time is newer than the destination time. * /EXCLUDE:file1[+file2][+file3]... * Specifies a list of files containing strings. Each string * should be in a separate line in the files. When any of the * strings match any part of the absolute path of the file to be * copied, that file will be excluded from being copied. For * example, specifying a string like \obj\ or .obj will exclude * all files underneath the directory obj or all files with the * .obj extension respectively. * /P Prompts you before creating each destination file. * /S Copies directories and subdirectories except empty ones. * /E Copies directories and subdirectories, including empty ones. * Same as /S /E. May be used to modify /T. * /V Verifies the size of each new file. * /W Prompts you to press a key before copying. * /C Continues copying even if errors occur. * /I If destination does not exist and copying more than one file, * assumes that destination must be a directory. * /Q Does not display file names while copying. * /F Displays full source and destination file names while copying. * /L Displays files that would be copied. * /G Allows the copying of encrypted files to destination that does not support encryption. * /H Copies hidden and system files also. * /R Overwrites read-only files. * /T Creates directory structure, but does not copy files. Does not * include empty directories or subdirectories. /T /E includes * empty directories and subdirectories. * /U Copies only files that already exist in destination. * /K Copies attributes. Normal Xcopy will reset read-only attributes. * /N Copies using the generated Int16 names. * /O Copies file ownership and ACL information. * /X Copies file audit settings (implies /O). * /Y Suppresses prompting to confirm you want to overwrite an existing destination file. * /-Y Causes prompting to confirm you want to overwrite an existing destination file. * /Z Copies networked files in restartable mode. * /B Copies the Symbolic Link itself versus the target of the link. * /J Copies using unbuffered I/O. Recommended for very large files. * * The switch /Y may be preset in the COPYCMD environment variable. * This may be overridden with /-Y on the command line. */ Boolean proceed = true; if (File.Exists(targetFilePath)) { proceed = false; switch (cr) { case CollisionRemediation.Break: case CollisionRemediation.Passive: case CollisionRemediation.FillIn: Common.WriteLine("Target file exists, exiting"); break; case CollisionRemediation.Mirror: if (FileCompare.AreSameBinaryViaFullScan ( new FileInfo(sourceFilePath), new FileInfo(targetFilePath))) // TODO Pri 1: FASTER { Common.WriteLine("Target file already exists (and is identical), exiting"); } else { Common.WriteLine("Target file exists, but is different: deleting"); File.Delete(targetFilePath); proceed = true; } break; case CollisionRemediation.Overwrite: Common.WriteLine("Target file exists, deleting"); File.Delete(targetFilePath); proceed = true; break; case CollisionRemediation.Throw: throwEx = true; break; } } if (throwEx) { throw new ScarabException("Target file '{0}' already exists", targetFilePath); } if (proceed) { File.Copy(sourceFilePath, targetFilePath); } }
/// <summary> /// Returns true if the directories have the same content, false otherwise /// </summary> /// <param name="directory1"></param> /// <param name="directory2"></param> /// <param name="verbose">Whether to print to the command line</param> /// <returns></returns> public static Boolean AreSame( DirectoryInfo directory1, DirectoryInfo directory2, Boolean verbose ) { if( !Directory.Exists( directory1.FullName ) ) { if( verbose ) { Console.WriteLine( "--> Directory {0} does not exist, comparison failed", directory1.FullName ); }; return false; } if( !Directory.Exists( directory2.FullName ) ) { if( verbose ) { Console.WriteLine( "--> Directory {0} does not exist, comparison failed", directory2.FullName ); }; return false; } // Take a snapshot of the file system IEnumerable<System.IO.FileInfo> list1 = directory1.GetFiles( "*.*", System.IO.SearchOption.AllDirectories ); IEnumerable<System.IO.FileInfo> list2 = directory2.GetFiles( "*.*", System.IO.SearchOption.AllDirectories ); //A custom file comparer defined below FileCompare myFileCompare = new FileCompare(); // This query determines whether the two folders contain // identical file lists, based on the custom file comparer // that is defined in the FileCompare class. // The query executes immediately because it returns a Boolean. Boolean areIdentical = list1.SequenceEqual( list2, myFileCompare ); if( areIdentical == true ) { fr.Common.WriteVerboseLine ( "Directories '{0}' and '{1}' are identical", directory1.FullName, directory2.FullName ); } else { if( verbose ) { Console.WriteLine( "--------------------------------------------" ); fr.Common.WriteVerboseLine( "Directories not the same. Files:" ); Console.WriteLine(); // Find the common files. It produces a sequence and doesn't // execute until the foreach statement. var queryCommonFiles = list1.Intersect( list2, myFileCompare ); Console.WriteLine( "--> In both folders:" ); foreach( var v in queryCommonFiles ) { Console.WriteLine( " {0}", v.FullName ); //shows which items end up in result list } Console.WriteLine(); // Find the set difference between the two folders. var queryList1Only = ( from file in list1 select file ).Except( list2, myFileCompare ); Console.WriteLine( "--> Only in {0}:", directory1.FullName ); foreach( var v in queryList1Only ) { Console.WriteLine( " {0}", v.FullName ); } Console.WriteLine(); var queryList2Only = ( from file in list2 select file ).Except( list1, myFileCompare ); Console.WriteLine( "--> The following files are only in {0}:", directory2.FullName ); foreach( var v in queryList2Only ) { Console.WriteLine( " {0}", v.FullName ); } Console.WriteLine( "--------------------------------------------" ); } } return areIdentical; }