Restoring a backup. Wait, where are my permissions? or "How to replace all permissions in one directory tree with the permissions from another identical tree"

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Security.AccessControl; using System.Diagnostics; using System.Security.Principal;

namespace AlexDuggleby.Tools.MirrorNTFSPermissions { class Program { #region Properties

/// /// The root we are copying from /// private static DirectoryInfo m_diFromRoot;

/// /// The root we are copying to /// private static DirectoryInfo m_diToRoot;

/// /// The log file to write to /// private static FileInfo m_fiLog;

#endregion

#region Main Operation

static void Main(string[] args) { // Check args and output usage if required if (args.Length != 3) { OutputUsage(); System.Environment.Exit(-1); return; }

if (PopulateAndCheckDirectoriesExist(args) && ConfirmUserAction()) { try { Trace.Listeners.Add(new ConsoleTraceListener()); Trace.Listeners.Add(new TextWriterTraceListener(m_fiLog.FullName));

WriteTraceHeader();

Console.Clear(); CopyPermissions(m_diFromRoot, m_diToRoot, true); WriteTraceFooter(); } catch (ApplicationException aex) { OutputError("Application", aex); } catch (Exception ex) { OutputError("Critical", ex); } }

}

private static void CopyDirectorySecurity(DirectoryInfo diFrom, DirectoryInfo diTo, bool skipRootNameCheck, out bool continueInThisDirectory) { continueInThisDirectory = true;

try { // Name comparison if (diFrom.Name != diTo.Name && !skipRootNameCheck) throw new ApplicationException(string.Format("During processing the directory names changed! '{0}'-'{1}'", diFrom.Name, diTo.Name));

// Set my own permissions DirectorySecurity _dsFrom = Directory.GetAccessControl(diFrom.FullName, AccessControlSections.All); DirectorySecurity _dsTo = new DirectorySecurity(); _dsTo.SetSecurityDescriptorSddlForm(_dsFrom.GetSecurityDescriptorSddlForm(AccessControlSections.All)); Directory.SetAccessControl(diTo.FullName, _dsTo);

// Tell the user what we are doing Trace.WriteLine(string.Format("Copying directory security from {0} to {1}", diFrom.FullName, diTo.FullName)); } catch (PathTooLongException _plex) { Trace.WriteLine(string.Format("[WARN] Path too long do continue with this directory '{0}'", diFrom.FullName)); continueInThisDirectory = false; // Abort because all files and subdirs will be affected } catch (Exception _ex) // General catch here, because we would like the batch to continue { Trace.WriteLine(string.Format("[ERROR] Error while doing current directory '{0}': {1}", diFrom.FullName, _ex.ToString())); // We will continue here, since error type is undefined. continueInThisDirectory = true; } }

#endregion

#region File/Directory I/O and Security

private static void CopyPermissions(DirectoryInfo diFrom, DirectoryInfo diTo, bool skipRootNameCheck) { bool _continueInThisDirectory;

CopyDirectorySecurity(diFrom, diTo, skipRootNameCheck, out _continueInThisDirectory);

if (_continueInThisDirectory) { // Now set files foreach (FileInfo _fiTo in diTo.GetFiles()) { CopyFileSecurity(diFrom, _fiTo); }

// Get all the -to- subdirs ... foreach (DirectoryInfo _toSubDir in diTo.GetDirectories()) { // ... and check if they also exist in the from directory DirectoryInfo _fromSubDir = diFrom.GetDirectories(_toSubDir.Name, SearchOption.TopDirectoryOnly).FirstOrDefault(d => d.Name == _toSubDir.Name); if (_fromSubDir != null) { // Recursive CopyPermissions(_fromSubDir, _toSubDir, false); } } } }

private static void CopyFileSecurity(DirectoryInfo diFrom, FileInfo fiTo) { // Turn the paths into relative paths based on the root dirs we started from string _relativeFromPath = fiTo.Directory.FullName.ToLower() .Replace(m_diToRoot.FullName.ToLower(), ""); string _relativeToPath = diFrom.FullName.ToLower() .Replace(m_diFromRoot.FullName.ToLower(), "");

// now compare those relative paths, just to be sure if (_relativeFromPath != _relativeToPath) throw new ApplicationException(string.Format( "Subdirectories went out of sync during processing! '{0}'-'{1}'", _relativeFromPath, _relativeToPath));

FileInfo _fiFrom = new FileInfo(Path.Combine(diFrom.FullName, fiTo.Name));

if (_fiFrom.Exists) // if it doesn't exist in the from we will ignore... { // Copy the file permissions FileSecurity _fsFrom = File.GetAccessControl(_fiFrom.FullName);

// Sometimes there are now access rules on the item, so only the owner can access it if (File.GetAccessControl(fiTo.FullName).GetAccessRules(true, true, typeof(NTAccount)).Count == 0) { // Tell the user what we are doing Trace.WriteLine(string.Format("File can only be accessed by owner: {0}", fiTo.FullName)); } else { try { // Copy the file permissions FileSecurity _fsTo = new FileSecurity(); _fsTo.SetSecurityDescriptorSddlForm(_fsFrom.GetSecurityDescriptorSddlForm(AccessControlSections.All)); File.SetAccessControl(fiTo.FullName, _fsTo);

// Tell the user what we are doing Trace.WriteLine(string.Format("Copying file security from {0} to {1}", _fiFrom.FullName, fiTo.FullName)); } catch (Exception _ex) { // Tell the user what went wrong Trace.WriteLine(string.Format("[ERROR] Error while copying file security from {0} to {1}: {2}", _fiFrom.FullName, fiTo.FullName, _ex.ToString())); } } } else { Trace.WriteLine(string.Format("File did not exist in original tree: {0}", fiTo.FullName)); } }

#endregion

#region User I/O and Tracing

private static void WriteTraceHeader() { Trace.WriteLine("*****************************************************"); Trace.WriteLine("Mirror NTFS Permissions"); Trace.WriteLine("Version 1.0 - 27.05.2008"); Trace.WriteLine("Alex Duggleby - http://alexduggleby.com"); Trace.WriteLine("*****************************************************"); Trace.WriteLine("== New run @ " + DateTime.Now.ToString() + ""); Trace.WriteLine("== From : " + m_diFromRoot.FullName + ""); Trace.WriteLine("== To : " + m_diToRoot.FullName + ""); Trace.WriteLine("========================================="); }

private static void WriteTraceFooter() { Console.ForegroundColor = ConsoleColor.Green; Trace.WriteLine("========================================="); Trace.WriteLine("Finished!"); Trace.WriteLine("========================================="); Console.ForegroundColor = ConsoleColor.White; Trace.Flush(); }

private static void OutputError(string errType, Exception ex) { Console.ForegroundColor = ConsoleColor.Red; string _s = string.Format("*********************************{1}{2} error: {0}", ex.ToString(), Environment.NewLine, errType); Trace.WriteLine(_s); Console.ForegroundColor = ConsoleColor.White; OutputPressKeyToExit(); Trace.Flush(); }

private static void OutputPressKeyToExit() { Console.WriteLine(); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("{0}Press any key to exit", Environment.NewLine); Console.ReadKey(); }

private static void OutputUsage() { Console.WriteLine("Mirror NTFS Permissions"); Console.WriteLine("by Alex Duggleby - http://alexduggleby.com"); Console.WriteLine(); Console.WriteLine("Usage: application.exe "); Console.WriteLine("Replaces all permissions on all subdirectory and files in path2 with those from path1 if they exist there."); Console.WriteLine("The is used to write the appplications trace output."); Console.WriteLine(); }

private static bool ConfirmUserAction() { Console.WriteLine("Do you wish to copy all permissions form '{0}' to '{1}'? [y/n]", m_diFromRoot.FullName, m_diToRoot.FullName);

if (Console.ReadKey().KeyChar.ToString().ToLower() != "y") { Console.WriteLine("User aborted!"); OutputPressKeyToExit(); System.Environment.Exit(-1); return false; }

return true; }

#endregion

#region Prechecks

private static bool PopulateAndCheckDirectoriesExist(string[] args) { string _dirFrom = args[0]; string _dirTo = args[1]; string _logFile = args[2];

if (_dirFrom.EndsWith("\\")) _dirFrom = _dirFrom.Substring(0, _dirFrom.Length - 1); if (_dirTo.EndsWith("\\")) _dirTo = _dirTo.Substring(0, _dirTo.Length - 1);

m_diFromRoot = new DirectoryInfo(_dirFrom); m_diToRoot = new DirectoryInfo(_dirTo); m_fiLog = new FileInfo(_logFile);

if (!m_fiLog.Exists) { try { m_fiLog.Create().Close(); } catch (Exception _ex) { Console.WriteLine("Could not create log file: " + _ex.ToString()); }

m_fiLog.Refresh(); }

if (!m_diFromRoot.Exists) Console.WriteLine("Directory '{0}' does not exist", m_diFromRoot.FullName);

if (!m_diToRoot.Exists) Console.WriteLine("Directory '{0}' does not exist", m_diToRoot.FullName);

if (!m_fiLog.Exists) Console.WriteLine("File '{0}' does not exist", m_fiLog.FullName);

return m_diFromRoot.Exists && m_diToRoot.Exists && m_fiLog.Exists; }

#endregion } }