|
|
how to set the compression level, plus an updated script
Here is a script I wrote which creates a backup, then compresses and encrypts the image. It performs some sanity checks and prints copious output. Note that the tar I'm using is from the Fink distribution, in /sw/bin. Without this version, you need to adjust the pathname (I haven't tested this).
#!/usr/bin/perl
use Getopt::Std;
use Cwd;
use File::Basename;
$DF='/bin/df';
$DU='/usr/bin/du';
$HDIUTIL='/usr/bin/hdiutil';
$HDID='/usr/bin/hdid';
$DITTO='/usr/bin/ditto';
$DISKUTIL='/usr/sbin/diskutil';
$DISKTOOL='/usr/sbin/disktool';
$MOUNT='/sbin/mount';
$UMOUNT='/sbin/umount';
$ID='/usr/bin/id';
$STTY='/bin/stty';
$TAR='/sw/bin/tar';
$SUDO = '/usr/bin/sudo';
$NEWFS_HFS = '/sbin/newfs_hfs';
$CHFLAGS = '/usr/bin/chflags';
$MAX_DU_LEN = 160;
$MIN_SLACK = 32;
$SLACK_FACTOR = 0.05;
$MIN_HFS = 4 * 1024 + 64;
sub checkexist(@);
sub getpasswd($);
sub getinput($);
sub diskusage(@);
sub changeflags(@);
sub addslack($);
sub setuser();
sub makevolume($$);
sub attach($);
sub makehfs($$);
sub mount($$);
sub ditto($@);
sub unmount($);
sub detach($);
sub convert2cryptcompressed($$$);
sub attachcrypt($$);
sub mountreadonly($$);
sub ducom($);
sub debugdu($$$);
sub ditto_link($$);
sub ditto_dir($$);
sub ditto_file($$);
sub ditto_path($$);
sub docommand($);
sub real_docommand($$);
############################################################################
%opts = ();
getopts('i:m:p:v:', \%opts) or
die("usage:\n" .
" $0 [-i] [-m] [-p] " .
"[-v] node1 [...]\n");
checkexist(@ARGV) || die("$0: checkexist() failed\n");
$mypass = ($opts{"p"}) ? $opts{"p"} : getpasswd("Passwd");
$myvolume = ($opts{"v"}) ? $opts{"v"} : getinput("Volume Name");
$imagename = ($opts{"i"}) ? $opts{"i"} : getinput("Image Filename");
unless ($imagename =~ /^.+\.dmg$/)
{
$imagename =~ s/$/.dmg/;
}
$tmpimage = $imagename;
unless ($tmpimage =~ s/^(.+)(\.dmg)$/\1.TEMP\2/)
{
die("$0: trying to create temp filename from '${imagename}'\n" .
" '${tmpimage}'\n");
}
if (-e $tmpimage)
{
die("$0: temporary image filename '${tmpimage}' exists\n");
}
$mountdir = ($opts{"m"}) ? $opts{"m"} : getinput("Mount Directory");
unless (-d $mountdir)
{
die("$0: directory '${mountdir}' doesn't exist\n");
}
($dulist, $origsize) = diskusage(@ARGV);
changeflags(@ARGV);
$totsize = addslack($origsize);
setuser();
makevolume($totsize, $tmpimage);
$diskname = attach($tmpimage);
makehfs($diskname, $myvolume);
mount($diskname, $mountdir);
$targetlist = ditto($mountdir, @ARGV);
unmount($mountdir);
detach($diskname);
convert2cryptcompressed($tmpimage, $imagename, $mypass);
print("@@ unlink(${tmpimage})\n");
unlink($tmpimage) ||
warn("$0: couldn't remove temp image '${tmpimage}': $!\n");
$diskname = attachcrypt($imagename, $mypass);
mountreadonly($diskname, $mountdir);
($imgdulist, $imgsize) = diskusage(@$targetlist);
unmount($mountdir);
detach($diskname);
print("\n\n");
print("original disk usage = ${origsize} K\n");
print("image disk usage = ${imgsize} K\n");
print("volume name = '${myvolume}'\n");
print("image file = '${imagename}'\n");
print("====================================================\n");
debugdu($dulist, $imgdulist, \*STDOUT);
print("\n");
############################################################################
############################################################################
############################################################################
sub checkexist(@)
{
my @nodes = @_;
my $node;
my $retval = 1;
foreach $node (@nodes)
{
unless (-r $node)
{
warn("$0: checkexist(): `$node' doesn't exist or isn't readable\n");
$retval = 0;
}
}
return($retval);
}
sub getpasswd($)
{
my ($prompt) = @_;
my $word;
my $command;
open(TTY, ">/dev/tty") ||
die("$0: getpasswd(): couldn't open /dev/tty: $!\n");
$command = "${STTY} -echo";
unless (docommand($command))
{
docommand("${STTY} echo");
die("$0: bailing\n" .
"'${command}'\n");
}
print TTY ("$prompt: ");
chomp($word = );
print TTY ("\n");
$command = "${STTY} echo";
docommand($command) ||
die("$0: bailing\n" .
"'${command}'\n");
close(TTY);
return($word);
}
sub getinput($)
{
my ($prompt) = @_;
my $word;
open(TTY, ">/dev/tty") ||
die("$0: getvolname(): couldn't open /dev/tty: $!\n");;
print TTY ("$prompt: ");
chomp($word = );
close(TTY);
return($word);
}
sub diskusage(@)
{
my @nodes = @_;
my ($node, $size, $totk, $fullist);
$totk = 0;
$fulllist = [ ];
foreach $node (@nodes)
{
$size = ducom($node);
$totk += $size;
push(@$fulllist, [ $node, $size ]);
}
return($fulllist, $totk);
}
sub changeflags(@)
{
my $command;
my $node;
foreach $node (@_)
{
$command = qq(${CHFLAGS} -R nouchg,noschg "$node");
docommand($command) ||
die("$0: changeflags(): command failed\n" .
"'${command}'\n");
}
}
sub addslack($)
{
my ($original) = @_;
my $slack;
$slack = int(0.5 + $original * $SLACK_FACTOR);
$slack = ($slack > $MIN_SLACK) ? $slack : $MIN_SLACK;
return ($original + $slack);
}
sub setuser()
{
my $command;
$command = "${DISKTOOL} -c $;
print $line;
unless ($line =~ m{^/dev/disk\d+\s+Apple_partition_scheme\s*$})
{
die("$0: attach(): unexpected output (line 1) from command\n" .
"'${command}'\n");
}
$line = ;
print $line;
unless ($line =~ m{^/dev/disk\d+s1\s+Apple_partition_map\s*$})
{
die("$0: attach(): unexpected output (line 2) from command\n" .
"'${command}'\n");
}
$line = ;
print $line;
unless (($diskname) = ($line =~ m{^/dev/(disk\d+)s2\s+Apple_HFS\s*$}))
{
die("$0: attach(): unexpected output (line 3) from command\n" .
"'${command}'\n");
}
close(HDID);
return($diskname);
}
sub makehfs($$)
{
my ($diskname, $volname) = @_;
my $command;
$command = qq(${SUDO} ${NEWFS_HFS} -v "${volname}" /dev/${diskname}s2);
docommand($command) ||
die("$0: makehfs(): command failed\n" .
"'${command}'\n");
}
sub mount($$)
{
my ($diskname, $mountdir) = @_;
my $command;
$command = qq(${MOUNT} -thfs /dev/${diskname}s2 "${mountdir}");
docommand($command) ||
die("$0: mount(): command failed\n" .
"'${command}'\n");
}
sub ditto($@)
{
my $mountdir = shift(@_);
my ($node, $targetlist, $target);
$targetlist = [ ];
foreach $node (@_)
{
if (-l $node)
{
$target = ditto_link($mountdir, $node);
}
elsif ((-f $node) || (-b $node) || (-c $node))
{
$target = ditto_file($mountdir, $node);
}
elsif (-d $node)
{
$target = ditto_dir($mountdir, $node);
}
else
{
warn("$0: ditto(): skipping pipe/socket/unknown:\n '$node'\n");
}
push(@$targetlist, $target);
}
return($targetlist);
}
sub unmount($)
{
my ($mountdir) = @_;
my $command;
$command = qq(${UMOUNT} "${mountdir}");
docommand($command) ||
die("$0: unmount(): command failed\n" .
"'${command}'\n");
}
sub detach($)
{
my ($diskname) = @_;
my $command;
$command = "${HDIUTIL} detach ${diskname}";
docommand($command) ||
die("$0: detach(): command failed\n" .
"'${command}'\n");
}
sub convert2cryptcompressed($$$)
{
my ($oldimage, $newimage, $password) = @_;
my ($command, $printcommand);
$command = qq(${HDIUTIL} convert -encryption -passphrase "${password}" ) .
qq(-format 'UDZO UDIF' -tgtimagekey zlib-level=9 "${oldimage}" ) .
qq(-o "${newimage}");
$printcommand = $command;
$printcommand =~ s/-passphrase "${password}"/-passphrase "********"/;
real_docommand($command, $printcommand) ||
die("$0: convert2cryptcompressed(): command failed\n" .
"'${command}'\n");
}
sub attachcrypt($$)
{
my ($image, $password) = @_;
my ($command, $line, $diskname, $printcommand);
$command = qq(${HDID} -encryption -passphrase "${mypass}" -nomount "$image");
$printcommand = $command;
$printcommand =~ s/-passphrase "${password}"/-passphrase "********"/;
print("@ ${printcommand}\n");
open(HDID, "${command} |") ||
die("$0: attachcrypt(): command failed: $!\n'${command}'\n");
$line = ;
print $line;
unless ($line =~ m{^/dev/disk\d+\s+Apple_partition_scheme\s*$})
{
die("$0: attachcrypt(): unexpected output (line 1) from command\n" .
"'${command}'\n");
}
$line = ;
print $line;
unless ($line =~ m{^/dev/disk\d+s1\s+Apple_partition_map\s*$})
{
die("$0: attachcrypt(): unexpected output (line 2) from command\n" .
"'${command}'\n");
}
$line = ;
print $line;
unless (($diskname) = ($line =~ m{^/dev/(disk\d+)s2\s+Apple_HFS\s*$}))
{
die("$0: attachcrypt(): unexpected output (line 3) from command\n" .
"'${command}'\n");
}
close(HDID);
return($diskname);
}
sub mountreadonly($$)
{
my ($diskname, $mountdir) = @_;
my $command;
$command = qq(${MOUNT} -r -thfs /dev/${diskname}s2 "${mountdir}");
docommand($command) ||
die("$0: mountreadonly(): command failed\n" .
"'${command}'\n");
}
sub ducom($)
{
my ($node) = @_;
my ($dulist, $command, $line, $expectnode, $nodename,
$size, $totsize);
$command = qq(${DU} -sk "${node}");
print("@ ${command}\n");
open(DU, "${command} |") ||
die("$0: ducom(): command failed : $!\n" .
"'${command}'\n");
chomp($line = );
print("$line\n");
close(DU);
unless (($size, $nodename) = ($line =~ /^(\d+)\s+($node)\s*$/))
{
die("$0: ducom(): parse error checking for " .
"'$node' in line:\n'$line'\n");
}
return($size);
}
sub debugdu($$$)
{
my ($dulist_orig, $dulist_new, $fh) = @_;
my $maxlen = 0;
my $maxsize = 0;
my ($item, $name, $size, $mylen, $mysizelen, $i, $matchall);
my ($nname, $nsize);
foreach $item (@$dulist_orig)
{
($name, $size) = @$item;
$mylen = length($name);
$mysizelen = length($size);
$maxlen = ($maxlen [$i]};
($nname, $nsize) = @{$dulist_new->[$i]};
($size == $nsize) or
$matchall = 0;
printf $fh ("%-*s : %*d\n",
$maxlen, $name,
$maxsize, $size);
printf $fh ("%-*s : %*d\n",
$maxlen, $nname,
$maxsize, $nsize);
}
print $fh "\n";
if ($matchall)
{
print $fh "Disk usage matches\n";
}
else
{
print $fh "ERROR: DISK USAGE DOES NOT MATCH\n";
}
}
sub ditto_link($$)
{
my ($mountdir, $srcnode) = @_;
my ($srcdir, $destdir, $command);
my ($srcname, $path, $srcpath, $orig_wd, $target);
unless (-l $srcnode)
{
die("$0: ditto_link(): invalid argument (not a link):\n" .
" '${srcnode}'\n");
}
($srcname, $path) = fileparse($srcnode);
$srcpath = &Cwd::realpath($path);
unless (-d $srcpath)
{
die("$0: ditto_link(): missing directory:\n" .
" '${srcpath}'\n" .
"resolved from link name:\n" .
" '${srcnode}'\n" .
" = ('${path}', '${name}')\n");
}
unless ($srcpath =~ m{^/})
{
die("$0: ditto_link(): srcpath missing leading /:\n" .
" '${srcpath}'\n" .
"resolved from link name:\n" .
" '${srcnode}'\n" .
" = ('${path}', '${name}')\n");
}
$destdir = "${mountdir}${srcpath}";
$target = "${destdir}/${srcname}";
unless (-d $destdir)
{
ditto_path(${srcpath}, ${destdir});
}
$orig_wd = &Cwd::getcwd;
chdir(${srcpath}) ||
die("$0: ditto_link(): couldn't chdir to '${srcpath}': $!\n");
unless (-l ${srcname})
{
die("$0: ditto_link(): link '${srcname}' not found in '${srcpath}'\n");
}
$command = qq[${TAR} cf - "${srcname}" | ( cd "${destdir}" ; ${TAR} xf - )];
docommand($command) ||
die("$0: ditto_link(): command failed\n" .
"'${command}'\n");
chdir(${orig_wd}) ||
die("$0: ditto_link(): couldn't chdir back to '${orig_wd}': $!\n");
unless (-l $target)
{
die("$0: ditto_link(): target not created:\n" .
" source = '${srcnode}'\n" .
" target = '${target}'\n");
}
return($target);
}
sub ditto_dir($$)
{
my ($mountdir, $srcdir) = @_;
my ($srcpath, $destdir, $target);
unless (-d $srcdir)
{
die("$0: ditto_dir(): invalid argument (not a directory):\n" .
" '${srcdir}'\n");
}
$srcpath = &Cwd::realpath($srcdir);
unless (-d $srcpath)
{
die("$0: ditto_dir(): missing directory:\n" .
" '${srcpath}'\n" .
"resolved from directory name:\n" .
" '${srcdir}'\n");
}
$destdir = "${mountdir}${srcpath}";
$target = $destdir;
$command = qq(${DITTO} -v -rsrcFork "${srcpath}" "${destdir}");
docommand($command) ||
die("$0: ditto_dir(): command failed\n" .
"'${command}'\n");
unless (-d $target)
{
die("$0: ditto_dir(): target not created:\n" .
" source = '${srcnode}'\n" .
" target = '${target}'\n");
}
return($target);
}
sub ditto_file($$)
{
my ($mountdir, $srcnode) = @_;
my ($srcdir, $destdir, $command);
my ($srcname, $path, $srcpath, $orig_wd, $target);
unless ((-f $srcnode) || (-c $srcnode) || (-b $srcnode))
{
die("$0: ditto_file(): invalid argument (not a file/char/block):\n" .
" '${srcnode}'\n");
}
($srcname, $path) = fileparse($srcnode);
$srcpath = &Cwd::realpath($path);
unless (-d $srcpath)
{
die("$0: ditto_file(): missing directory:\n" .
" '${srcpath}'\n" .
"resolved from file name:\n" .
" '${srcnode}'\n" .
" = ('${path}', '${name}')\n");
}
unless ($srcpath =~ m{^/})
{
die("$0: ditto_file(): srcpath missing leading /:\n" .
" '${srcpath}'\n" .
"resolved from file name:\n" .
" '${srcnode}'\n" .
" = ('${path}', '${name}')\n");
}
$destdir = "${mountdir}${srcpath}";
$target = "${destdir}/${srcname}";
unless (-d $destdir)
{
ditto_path(${srcpath}, ${destdir});
}
$command = qq(${DITTO} -v -rsrcFork "${srcpath}/${srcname}" ) .
qq("${target}");
docommand($command) ||
die("$0: ditto_file(): command failed\n" .
"'${command}'\n");
unless ((-f $target) || (-c $target) || (-b $target))
{
die("$0: ditto_file(): target not created:\n" .
" source = '${srcnode}'\n" .
" target = '${target}'\n");
}
return($target);
}
sub ditto_path($$)
{
my ($srcpath, $destpath) = @_;
my ($tmpdir, $tmpnum, $command, $dir, $srctemp, $desttemp);
$tmpnum = 0;
$tmpdir = sprintf("TEMPDIR.%04d.$$", $tmpnum);
$srctemp = "${srcpath}/${tmpdir}";
$desttemp = "${destpath}/${tmpdir}";
until ((! -e $srctemp) && (! -e $desttemp))
{
if ($tmpnum > 9999)
{
die("$0: ditto_path(): could not find available tmpname:\n" .
" '${srctemp}'\n" .
" '${desttemp}'\n");
}
$tmpnum++;
$tmpdir = sprintf("TEMPDIR.%04d.$$", $tmpnum);
$srctemp = "${srcpath}/${tmpdir}";
$desttemp = "${destpath}/${tmpdir}";
}
if (-d $desttemp)
{
die("$0: ditto_path(): I was going to copy '${srctemp}'\n" .
" to '${desttemp}' but it already exists!\n");
}
print("@@ mkdir(${srctemp}, 0700)\n");
mkdir($srctemp, 0700) ||
die("$0: ditto_path(): mkdir('${srctemp}', 0700) failed: $!\n");
$command = qq(${DITTO} -v "${srctemp}" "${desttemp}");
docommand($command) ||
die("$0: ditto_path(): command failed\n" .
"'${command}'\n");
$dir = "${srctemp}";
print("@@ rmdir(${dir})\n");
rmdir($dir) ||
die("$0: ditto_path(): rmdir('${dir}') failed: $!\n");
$dir = "${desttemp}";
print("@@ rmdir(${dir})\n");
rmdir($dir) ||
die("$0: ditto_path(): rmdir('${dir}') failed: $!\n");
}
sub docommand($)
{
my ($command) = @_;
real_docommand($command, $command);
}
sub real_docommand($$)
{
my ($command, $printcommand) = @_;
my $rc;
print("@ '${printcommand}'\n");
$rc = 0xffff & system($command);
if ($rc == 0)
{
return(1);
}
if ($rc == 0xff00)
{
warn("command failed: $!\n'$command'\n");
}
elsif ($rc > 0x80)
{
$rc >>= 8;
warn("non-zero exit status $rc\n'$command'\n");
}
else
{
$core = ($rc & 0x80);
if ($core)
{
$rc &= ~0x80;
}
warn("signal $rc");
if ($core)
{
warn(" (core dump)");
}
warn("\n'$command'\n");
}
return(0);
}
|
SearchFrom our Sponsor...Latest Mountain Lion HintsWhat's New:HintsNo new hintsComments last 2 daysLinks last 2 weeksNo recent new linksWhat's New in the Forums?
Hints by TopicNews from Macworld
From Our Sponsors |
|
Copyright © 2014 IDG Consumer & SMB (Privacy Policy) Contact Us All trademarks and copyrights on this page are owned by their respective owners. |
Visit other IDG sites: |
|
|
|
Created this page in 0.14 seconds |
|