Archive for May 27th, 2008

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”

Ok, I’m starting to get fond of these “a slice of reality” or “what am I going to show you” title blog posts…

So once again this piece of code is a bit drastic. It’s neatly written if you ask me and should show you how you can replace the file security settings (ACLs) with some other files security settings (same for directories). The only meaningfull place to do this (off the top of my head) is when you have two identical files and for some reason the one file has the correct permissions and the other doesn’t. But daily developer business is always full of these little request, and it’s like a personal search for the holy grail – you just have to give it a try.

So if you ever want to copy the exact permission structure from one directory tree to another (for example you used some copy tool that doesn’t copy the permissions) then you can use this little tool to do just that.

A word of warning (as always). Have a backup of your permissions (most probably you will, because you need to copy them from somewhere) and of course run this tool at your own risk. The binary is available here and is compiled from the source you see below. Have fun and tell me if you find bugs or if it’s useful.

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 } }


Subscribe / Search


XING

 

May 2008
M T W T F S S
« Apr   Jun »
 1234
567891011
12131415161718
19202122232425
262728293031  

Twitter

Blog Stats

  • 356,722 hits

Follow

Get every new post delivered to your Inbox.

Join 57 other followers