/// <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 { _logger.LogTrace("Creating directory: {0}", diDest.FullName); // create the destination directory diDest.Create(); results.DirectoriesCreated++; } catch (Exception ex) { _logger.LogError("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 { _logger.LogTrace("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) { _logger.LogError("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 { _logger.LogTrace("Deleting: {0} ", destFile.FullName); destFile.IsReadOnly = false; // delete the file destFile.Delete(); results.FilesDeleted++; } catch (Exception ex) { _logger.LogError("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 { _logger.LogTrace("Deleting directory: {0} ", diDestSubdir.FullName); // delete directory DeleteDirectory(diDestSubdir); results.DirectoriesDeleted++; } catch (Exception ex) { _logger.LogError("Failed to delete directory {0}. {1}", diDestSubdir.FullName, ex.Message); return(false); } } } } return(true); }