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

About these ads

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s