/// <summary> /// Performs one-way synchronization from source directory tree to destination directory tree /// </summary> public virtual SyncResults Start() { SyncResults results = new SyncResults(); if (Validate(this.SourceDirectory.FullName, this.DestinationDirectory.FullName, this.Configuration)) { // recursively process directories ProcessDirectory(this.SourceDirectory.FullName, this.DestinationDirectory.FullName, this.Configuration, ref results); } return results; }
/// <summary> /// Recursively performs one-way synchronization from a single source to destination directory /// </summary> /// <param name="srcDir"></param> /// <param name="destDir"></param> /// <param name="inputParams"></param> /// <param name="results"></param> private bool ProcessDirectory(string srcDir, string destDir, InputParams inputParams, ref SyncResults results) { DirectoryInfo diSrc = new DirectoryInfo(srcDir); DirectoryInfo diDest = new DirectoryInfo(destDir); // create destination directory if it doesn't exist if (!diDest.Exists) { try { Trace("Creating directory: {0}", diDest.FullName); // create the destination directory diDest.Create(); results.DirectoriesCreated++; } catch (Exception ex) { Trace("Error: failed to create directory {0}. {1}", destDir, ex.Message); return false; } } // get list of selected files from source directory FileInfo[] fiSrc = GetFiles(diSrc, inputParams, ref results); // get list of files in destination directory FileInfo[] fiDest = GetFiles(diDest, null, ref results); // put the source files and destination files into hash tables Hashtable hashSrc = new Hashtable(fiSrc.Length); foreach (FileInfo srcFile in fiSrc) { hashSrc.Add(srcFile.Name, srcFile); } Hashtable hashDest = new Hashtable(fiDest.Length); foreach (FileInfo destFile in fiDest) { hashDest.Add(destFile.Name, destFile); } // make sure all the selected source files exist in destination foreach (FileInfo srcFile in fiSrc) { bool isUpToDate = false; // look up in hash table to see if file exists in destination FileInfo destFile = (FileInfo)hashDest[srcFile.Name]; // if file exists and length, write time and attributes match, it's up to date if ((destFile != null) && (srcFile.Length == destFile.Length) && (srcFile.LastWriteTime == destFile.LastWriteTime) && (srcFile.Attributes == destFile.Attributes)) { isUpToDate = true; results.FilesUpToDate++; } // if the file doesn't exist or is different, copy the source file to destination if (!isUpToDate) { string destPath = Path.Combine(destDir, srcFile.Name); // make sure destination is not read-only if (destFile != null && destFile.IsReadOnly) { destFile.IsReadOnly = false; } try { Trace("Copying: {0} -> {1}", srcFile.FullName, Path.GetFullPath(destPath)); // copy the file srcFile.CopyTo(destPath, true); // set attributes appropriately File.SetAttributes(destPath, srcFile.Attributes); results.FilesCopied++; } catch (Exception ex) { Trace("Error: failed to copy file from {0} to {1}. {2}", srcFile.FullName, destPath, ex.Message); return false; } } } // delete extra files in destination directory if specified if (inputParams.DeleteFromDest) { foreach (FileInfo destFile in fiDest) { FileInfo srcFile = (FileInfo)hashSrc[destFile.Name]; if (srcFile == null) { // if this file is specified in exclude-from-deletion list, don't delete it if (ShouldExclude(inputParams.DeleteExcludeFiles, null, destFile.Name)) continue; try { Trace("Deleting: {0} ", destFile.FullName); destFile.IsReadOnly = false; // delete the file destFile.Delete(); results.FilesDeleted++; } catch (Exception ex) { Trace("Error: failed to delete file from {0}. {1}", destFile.FullName, ex.Message); return false; } } } } // Get list of selected subdirectories in source directory DirectoryInfo[] diSrcSubdirs = GetDirectories(diSrc, inputParams, ref results); // Get list of subdirectories in destination directory DirectoryInfo[] diDestSubdirs = GetDirectories(diDest, null, ref results); // add selected source subdirectories to hash table, and recursively process them Hashtable hashSrcSubdirs = new Hashtable(diSrcSubdirs.Length); foreach (DirectoryInfo diSrcSubdir in diSrcSubdirs) { hashSrcSubdirs.Add(diSrcSubdir.Name, diSrcSubdir); // recurse into this directory if (!ProcessDirectory(diSrcSubdir.FullName, Path.Combine(destDir, diSrcSubdir.Name), inputParams, ref results)) return false; } // delete extra directories in destination if specified if (inputParams.DeleteFromDest) { foreach (DirectoryInfo diDestSubdir in diDestSubdirs) { // does this destination subdirectory exist in the source subdirs? if (!hashSrcSubdirs.ContainsKey(diDestSubdir.Name)) { // if this directory is specified in exclude-from-deletion list, don't delete it if (ShouldExclude(inputParams.DeleteExcludeDirs, null, diDestSubdir.Name)) continue; try { Trace("Deleting directory: {0} ", diDestSubdir.FullName); // delete directory DeleteDirectory(diDestSubdir); results.DirectoriesDeleted++; } catch (Exception ex) { Trace("Error: failed to delete directory {0}. {1}", diDestSubdir.FullName, ex.Message); return false; } } } } return true; }
/// <summary> /// Recursively performs one-way synchronization from a single source to destination directory /// </summary> /// <param name="srcDir"></param> /// <param name="destDir"></param> /// <param name="inputParams"></param> /// <param name="results"></param> private bool ProcessDirectory(string srcDir, string destDir, InputParams inputParams, ref SyncResults results) { DirectoryInfo diSrc = new DirectoryInfo(srcDir); DirectoryInfo diDest = new DirectoryInfo(destDir); // create destination directory if it doesn't exist if (!diDest.Exists) { try { Trace("Creating directory: {0}", diDest.FullName); // create the destination directory diDest.Create(); results.DirectoriesCreated++; } catch (Exception ex) { Trace("Error: failed to create directory {0}. {1}", destDir, ex.Message); return(false); } } // get list of selected files from source directory FileInfo[] fiSrc = GetFiles(diSrc, inputParams, ref results); // get list of files in destination directory FileInfo[] fiDest = GetFiles(diDest, inputParams.IncludesApplyToDest ? inputParams : null, ref results); // put the source files and destination files into hash tables Hashtable hashSrc = new Hashtable(fiSrc.Length); foreach (FileInfo srcFile in fiSrc) { hashSrc.Add(srcFile.Name, srcFile); } Hashtable hashDest = new Hashtable(fiDest.Length); foreach (FileInfo destFile in fiDest) { hashDest.Add(destFile.Name, destFile); } // make sure all the selected source files exist in destination var compareMethod = inputParams.CompareMethod; foreach (FileInfo srcFile in fiSrc) { bool isUpToDate = false; // look up in hash table to see if file exists in destination FileInfo destFile = (FileInfo)hashDest[srcFile.Name]; // if file exists and length, write time and attributes match, it's up to date if ((destFile != null) && CompareFile.AreEqual(srcFile, destFile, compareMethod)) { isUpToDate = true; results.FilesUpToDate++; } // if the file doesn't exist or is different, copy the source file to destination if (!isUpToDate) { string destPath = Path.Combine(destDir, srcFile.Name); // make sure destination is not read-only if (destFile != null && destFile.IsReadOnly) { destFile.IsReadOnly = false; } try { Trace("Copying: {0} -> {1}", srcFile.FullName, Path.GetFullPath(destPath)); // copy the file srcFile.CopyTo(destPath, true); // set attributes appropriately File.SetAttributes(destPath, srcFile.Attributes); results.FilesCopied++; } catch (Exception ex) { Trace("Error: failed to copy file from {0} to {1}. {2}", srcFile.FullName, destPath, ex.Message); return(false); } } } // delete extra files in destination directory if specified if (inputParams.DeleteFromDest) { foreach (FileInfo destFile in fiDest) { FileInfo srcFile = (FileInfo)hashSrc[destFile.Name]; if (srcFile == null) { // if this file is specified in exclude-from-deletion list, don't delete it if (ShouldExclude(inputParams.DeleteExcludeFiles, null, destFile.Name)) { continue; } try { Trace("Deleting: {0} ", destFile.FullName); destFile.IsReadOnly = false; // delete the file destFile.Delete(); results.FilesDeleted++; } catch (Exception ex) { Trace("Error: failed to delete file from {0}. {1}", destFile.FullName, ex.Message); return(false); } } } } // Get list of selected subdirectories in source directory DirectoryInfo[] diSrcSubdirs = GetDirectories(diSrc, inputParams, ref results); // Get list of subdirectories in destination directory DirectoryInfo[] diDestSubdirs = GetDirectories(diDest, inputParams.IncludesApplyToDest ? inputParams : null, ref results); // add selected source subdirectories to hash table, and recursively process them Hashtable hashSrcSubdirs = new Hashtable(diSrcSubdirs.Length); foreach (DirectoryInfo diSrcSubdir in diSrcSubdirs) { hashSrcSubdirs.Add(diSrcSubdir.Name, diSrcSubdir); // recurse into this directory if (!ProcessDirectory(diSrcSubdir.FullName, Path.Combine(destDir, diSrcSubdir.Name), inputParams, ref results)) { return(false); } } // delete extra directories in destination if specified if (inputParams.DeleteFromDest) { foreach (DirectoryInfo diDestSubdir in diDestSubdirs) { // does this destination subdirectory exist in the source subdirs? if (!hashSrcSubdirs.ContainsKey(diDestSubdir.Name)) { // if this directory is specified in exclude-from-deletion list, don't delete it if (ShouldExclude(inputParams.DeleteExcludeDirs, null, diDestSubdir.Name)) { continue; } try { Trace("Deleting directory: {0} ", diDestSubdir.FullName); // delete directory DeleteDirectory(diDestSubdir); results.DirectoriesDeleted++; } catch (Exception ex) { Trace("Error: failed to delete directory {0}. {1}", diDestSubdir.FullName, ex.Message); return(false); } } } } return(true); }
/// <summary> /// Gets list of files in specified directory, optionally filtered by specified input parameters /// </summary> /// <param name="directoryInfo"></param> /// <param name="inputParams"></param> /// <param name="results"></param> public virtual FileInfo[] GetFiles(DirectoryInfo directoryInfo, InputParams inputParams, ref SyncResults results) { // get all files List<FileInfo> fileList = new List<FileInfo>(directoryInfo.GetFiles()); // do we need to do any filtering? bool needFilter = (inputParams != null) && (inputParams.AreSourceFilesFiltered); if (needFilter) { for (int i = 0; i < fileList.Count; i++) { FileInfo fileInfo = fileList[i]; // filter out any files based on hiddenness and exclude/include filespecs if ((inputParams.ExcludeHidden && ((fileInfo.Attributes & FileAttributes.Hidden) > 0)) || ShouldExclude(inputParams.ExcludeFiles, inputParams.IncludeFiles, fileInfo.Name)) { fileList.RemoveAt(i); results.FilesIgnored++; i--; } } } return fileList.ToArray(); }
/// <summary> /// Gets list of subdirectories of specified directory, optionally filtered by specified input parameters /// </summary> /// <param name="results"></param> /// <param name="inputParams"></param> /// <param name="directoryInfo"></param> public virtual DirectoryInfo[] GetDirectories(DirectoryInfo directoryInfo, InputParams inputParams, ref SyncResults results) { // get all directories List <DirectoryInfo> directoryList = new List <DirectoryInfo>(directoryInfo.GetDirectories()); // do we need to do any filtering? bool needFilter = (inputParams != null) && (inputParams.AreFilesFiltered); if (needFilter) { for (int i = 0; i < directoryList.Count; i++) { DirectoryInfo subdirInfo = directoryList[i]; // filter out directories based on hiddenness and exclude/include filespecs if ((inputParams.ExcludeHidden && ((subdirInfo.Attributes & FileAttributes.Hidden) > 0)) || ShouldExclude(inputParams.ExcludeDirs, inputParams.IncludeDirs, subdirInfo.Name)) { directoryList.RemoveAt(i); results.DirectoriesIgnored++; i--; } } } return(directoryList.ToArray()); }
/// <summary> /// Gets list of files in specified directory, optionally filtered by specified input parameters /// </summary> /// <param name="directoryInfo"></param> /// <param name="inputParams"></param> /// <param name="results"></param> public virtual FileInfo[] GetFiles(DirectoryInfo directoryInfo, InputParams inputParams, ref SyncResults results) { // get all files var fileInfos = directoryInfo.GetFiles(); // do we need to do any filtering? bool needFilter = (inputParams != null) && (inputParams.AreFilesFiltered); if (!needFilter) { return(fileInfos); } var fileList = new List <FileInfo>(fileInfos.Length); foreach (var fileInfo in fileInfos) { // filter out any files based on hiddenness and exclude/include filespecs if ((inputParams.ExcludeHidden && ((fileInfo.Attributes & FileAttributes.Hidden) > 0)) || ShouldExclude(inputParams.ExcludeFiles, inputParams.IncludeFiles, fileInfo.Name)) { results.FilesIgnored++; } else { fileList.Add(fileInfo); } } return(fileList.ToArray()); }
/// <summary> /// Runs a test case /// </summary> private static void TestOneCase(string[] srcDirectories, string[] srcFiles, string[] destDirectories, string[] destFiles, InputParams inputParams, SyncResults expectedResults) { // delete base directories in case they were hanging around from a previous failed test DeleteTestDirectory(baseDirSrc); DeleteTestDirectory(baseDirDest); // create source directories and files specified by test CreateTestDirectories(baseDirSrc, srcDirectories); CreateTestFiles(baseDirSrc, null, srcFiles); // create destination directories and files specified by test if (destDirectories != null) { CreateTestDirectories(baseDirDest, destDirectories); } if (destFiles != null) { CreateTestFiles(baseDirDest, baseDirSrc, destFiles); } // perform the directory sync SyncResults results = new SyncResults(); results = new Sync(baseDirSrc, baseDirDest).Start(inputParams); // Assert we have expected results Assert.IsTrue(SyncTools.CompareTo(expectedResults, results)); // If we are deleting extra files from destination, verify we have exactly the same files as filtered source files if (inputParams.DeleteFromDest && (!(inputParams.DeleteExcludeFiles != null) && !(inputParams.DeleteExcludeDirs != null))) { // calc hash of filtered files & directories in source tree byte[] hashSrc = CalcHash(baseDirSrc, inputParams); // calc hash of all files & directories in destination tree byte[] hashDest = CalcHash(baseDirDest, null); // hashes must match bool hashesMatch = SyncTools.CompareByteArrays(hashSrc, hashDest); Assert.IsTrue(hashesMatch); } DeleteTestDirectory(baseDirSrc); DeleteTestDirectory(baseDirDest); }
/// <summary> /// Fill a stream with relevant contents of directory tree for hashing. Directory tree is filtered by /// inputParams if non-null. /// </summary> private static void BuildCRCStream(string directory, InputParams inputParams, ref MemoryStream memoryStream) { DirectoryInfo directoryInfo = new DirectoryInfo(directory); SyncResults results = new SyncResults(); // get filtered list of files in this directory FileInfo[] files = new Sync().GetFiles(directoryInfo, inputParams, ref results); // sort by name for deterministic order Array.Sort(files, new FileInfoComparer()); // write information about each file to stream foreach (FileInfo fileInfo in files) { byte[] bytes = ASCIIEncoding.UTF8.GetBytes(fileInfo.Name); memoryStream.Write(bytes, 0, bytes.Length); bytes = BitConverter.GetBytes(fileInfo.Length); memoryStream.Write(bytes, 0, bytes.Length); bytes = BitConverter.GetBytes(fileInfo.LastWriteTime.ToBinary()); memoryStream.Write(bytes, 0, bytes.Length); bytes = ASCIIEncoding.UTF8.GetBytes(fileInfo.Attributes.ToString()); memoryStream.Write(bytes, 0, bytes.Length); } // get filtered list of subdirectories DirectoryInfo[] subdirs = new Sync().GetDirectories(directoryInfo, inputParams, ref results); // sort by name for deterministic order Array.Sort(subdirs, new DirectoryInfoComparer()); foreach (DirectoryInfo subdir in subdirs) { // write information about each subdirectory to stream byte[] bytes = ASCIIEncoding.UTF8.GetBytes(subdir.Name); memoryStream.Write(bytes, 0, bytes.Length); // recurse BuildCRCStream(Path.Combine(directory, subdir.Name), inputParams, ref memoryStream); } }
public void Initialization() { inputParams = new InputParams(); expectedResults = new SyncResults(); }