Technology: October 2016 Archives

This was the problem: I had a hard drive that I took out of a Windows XP laptop and put into an external case. (The laptop's AC system failed; an adapter could no longer power the system or charge the battery.) The drive was formatted for NTFS, so files had ownership and permissions attributes. I could access most of the drive, but my profile was inaccessible, because it was owned by my username on the old laptop, which had a unique security ID (SID), different from the SID of my account on the new laptop. I needed to grant myself access to view and do other things with the files. To do this, however, I first had to take ownership of the files.

First attempt was to use TAKEOWN, a built-in Microsoft Windows 7 command, with the /R (recursive) flag. The problem with that is that, like most Windows utilities, when it hits an error (can't access a file, for example) it just stops, and there's no way to resume the operation where you left off.

The other thing I learned was that you also need to use ICACLS, another Microsoft utility, to add permissions to read files, traverse folders, etc.

I looked for existing solutions but didn't find anything satisfactory. For example, this looked like a more robust solution, but PowerShell was too much to learn from scratch.

So I wrote a Perl script that iterates down the folder tree, takes ownership of each file and folder for the Administrators group, then adds full control permission for Everyone to each file and folder (note that it doesn't replace any existing permission). Any error messages are written to a log file, specified as the second argument in the command line. The script has to be Run as Administrator, so you either need to run it in a CMD window or (my preference) Emacs shell buffer that you started by right-clicking and choosing "Run as Administrator" from the popup. (The advantage of an Emacs shell buffer is that you capture any messages within an unlimited buffer that you can easily search through and write to a file.)

It would be possible to customize this so that you gave ownership to a different user and were more selective about the access permissions you add (e.g. read only, permission only for certain users), but this method accomplishes my limited purposes.

Don't panic when the first few calls to ICACLS seem to take a long time to run. It isn't doing a recursive ICACLS operation, but a change to a higher-level folder could have impacts via inheritance that may take some time to complete.

In the spirit of giving back, here's my script for your reference, with no warranties or claims. Use at your own risk.

# Perl script to recurse the file tree from the specified starting
# point, taking ownership of each file for the Administrators group;
# then giving Everyone full control permissions.

# Takes two arguments: 
#
# (1) The path (relative to the current directory) of the folder at the
# root of the folder tree whose permissions you want to reset.  
#
# (2) A logfile showing which subfolders have been processed and any
# errors encountered.

# This is intended to be used when removing an NTFS disk from a
# laptop, so that you can put the disk into an external enclosure and
# access files that may have been private to a Profile that is no
# longer available.

use strict;

my $basedir = shift;
my $logfile = shift;
open LOGFILE, ">$logfile" or die "Could not open log file $logfile: $!";

# We have to take owner

&takeownperms ( $basedir );
&traversedir ( $basedir );

sub traversedir 
{
    my $currentdir = shift;

    print "Processing $currentdir\n";
    print LOGFILE "Processing $currentdir\n";

    my $dirhandle;
    my $opendirresult = opendir $dirhandle, $currentdir;
    if ( ! $opendirresult ) 
    {
	print LOGFILE "Could not open $currentdir: $!";
	return;
    }

    my @files = readdir $dirhandle;
    closedir $dirhandle;

    my @dirlist;

    # Iterate through files. Recurse for directories

    foreach my $file (@files)
    {
	# Current directory is already done
	next if $file =~ /^\.$/;

	# Skip "directory up" entry
	next if $file =~ /^\.\.$/;

	my $path = "$currentdir\\$file";

	&takeownperms ( $path );

	# Test for directory
	if ( -d $path )
	{
	    push @dirlist, $path;
	}
    }

    # Recurse to lower-level directories

    foreach my $dir ( @dirlist )
    {
	&traversedir($dir);
    }
}


sub takeownperms
{
    my $path = shift;

    # Take ownership

    my @takeownresultlines;
    my $takeownsucceeded = 0;
    my @icaclsresultlines;
    my $icaclssucceeded = 0;
    
    @takeownresultlines = readpipe "TAKEOWN /F \"$path\" /A 2>&1";
    
    # Log TAKEOWN errors
    
    foreach my $line ( @takeownresultlines )
    {
	next if $line =~ /^\s*$/;
	if ( $line =~ /^SUCCESS:/ ) 
	{
	    $takeownsucceeded = 1;
	}
    }
    
    if ( not $takeownsucceeded )
    {
	print LOGFILE "TAKEOWN FAILED: $path\n";
	foreach my $line ( @takeownresultlines )
	{
	    # Echo all non-blank lines to the logfile
	    next if $line =~ /^\s*$/;
	    print LOGFILE $line;
	}
    }
    
    
    # Add permission
    
    if ( $takeownsucceeded )
    {
    	@icaclsresultlines = 
    	    readpipe "ICACLS \"$path\" /grant Everyone:(F) 2>&1";
    	
    	foreach my $line ( @icaclsresultlines )
    	{
    	    next if $line =~ /^\s*$/;
    	    if ( $line =~ /^Successfully processed 1 files;/ ) 
    	    {
    		$icaclssucceeded = 1;
    	    }
    	}
    	
    	if ( not $icaclssucceeded )
    	{
    	    print LOGFILE "ICACLS FAILED: $path\n";
    	    foreach my $line ( @icaclsresultlines )
    	    {
    		# Echo all non-blank lines to the logfile
    		next if $line =~ /^\s*$/;
    		print LOGFILE $line;
    	    }
    	}
    	
    }
}
RESOURCES:

A good explanation of SIDs and why a multi-pass process is necessary, along with bibliography. This suggests an approach to make a new SID the creator-owner of a file.

Using the Windows Explorer GUI to accomplish the task: A bit more user friendly, but, as noted above, if Windows hits a problem in the middle of a long, recursive task, it will stop and there will be no easy way to tell how far things got or to resume the process where it left off.

The Win32::Security::ACL Perl module would have been another approach, but it would have required a better understanding of ACLs and inheritance, this is a beta version, and the documentation doesn't separate external behavior from internal design, so it's rather confusing.

About this Archive

This page is a archive of entries in the Technology category from October 2016.

Technology: October 2015 is the previous archive.

Find recent content on the main index or look in the archives to find all content.

Contact

Feeds

Subscribe to feed Subscribe to this blog's feed:
Atom
RSS
[What is this?]