LDP/LDP/howto/docbook/Linux-Complete-Backup-and-R.../scripts/cooked/make.fdisk

1036 lines
39 KiB
Plaintext

<![ CDATA [#! /usr/bin/perl
# A perl script to create a script and input file for fdisk to
# re-create the partitions on the hard disk, and format the Linux and
# Linux swap partitions. The first parameter is the fully qualified
# path of the device of the hard disk, e.g. /dev/hda. The two
# resulting files are the script make.dev.x and the data file dev.x
# (where x is the hard drive described, e.g. hda, sdc). make.dev.x is
# run at restore time to rebuild hard drive x, prior to running
# restore.metadata. dev.x is the input file for fdisk.
# The directory tree where everything is put must already exist and be
# specified in the environment variable $zip.
# Time-stamp: <2007-07-08 10:26:04 ccurley make.fdisk>
# Copyright 2001 through the last date of modification Charles Curley
# except for the subroutine cut2fmt.
# cut2fmt Copyright (c) 1998 Tom Christiansen, Nathan Torkington and
# O'Reilly & Associates, Inc. Permission is granted to use this code
# freely EXCEPT for book publication. You may use this code for book
# publication only with the explicit permission of O'Reilly &
# Associates, Inc.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA
# In addition, as a special exception, Tom Christiansen, Nathan
# Torkington and O'Reilly & Associates, Inc. give permission to use
# the code of this program with the subroutine cut2fmt (or with
# modified versions of the subroutine cut2fmt that use the same
# license as the subroutine cut2fmt), and distribute linked
# combinations including the two. You must obey the GNU General
# Public License in all respects for all of the code used other than
# the subroutine cut2fmt. If you modify this file, you may extend
# this exception to your version of the file, but you are not
# obligated to do so. If you do not wish to do so, delete this
# exception statement and the subroutine cut2fmt from your version.
# You can also contact the Free Software Foundation at
# http://www.fsf.org/
# Changes:
# 2007-06-10: In addition to scanning /etc/fstab for LVM partitions
# (logical volumes), we also check the device files in /dev. This is
# because some logical volumes may be mounted by label, and scanning
# fstab won't pick those up.
# 2007-05-22: Changes for FHS compliance. Removed commented out
# references to ZIP drives. N.B.: we now take the location of where to
# put things as an environment variable, $zip.
# 2006-04-15: Added support for partition type 0x12, "Compaq
# diagnostic". This type is used for so-called "hidden diagnostics"
# partitions, e.g. on Lenovo/IBM computers.
# 2006-04-08: Primitive LVM support. It is kludgy in that it uses
# first stage restoration distribution (finnix) specific code to turn
# LVM on and off, but otherwise seems to work.
# 2006-03-28: We have a problem if swap partitions have
# labels. There's no way to retrieve the label from a swap
# partition. If we have one & only one swap partition, then we can
# pull it out of /etc/fstab. Otherwise the user is on her own. We scan
# fstab for swap mount points that have labels for their devices. If
# there is one and only one, we assume that's it, otherwise pass.
# 2005-10-29: We now provide the geometry as an argument to fdisk
# (which does not work on tomsrtbt). We also save data for sfdisk, and
# write out make.dev.xxx so that it will use sfdisk if it finds it.
# 2005-08-14: Due to experience on Knoppix, we now add the code to
# change the partition types to the end of the fdisk input file
# instead of right after creating the partition.
# 2004 04 10: fdisk v > 2.11 has wider columns. Added code to select
# the appropriate cut string based on fdisk's version.
# 2004 04 09: Added support for Mandrake's idea of devfs. On Mandrake,
# everything is mounted with devfs. So the mount devices are buried
# deep in places like /dev/ide/host0/bus0/target0/lun0/part1 instead
# of places like /dev/hda1, where $DEITY intended they should be. We
# have to reverse from the long devfs device to the shorter old style
# that tomsrtbt uses. The alternative is to keep track in an array of
# which devfs device belongs to which short device.
# 2003 12 29: Changed the regex for detecting whether a file system is
# read-write in the code that builds the mount file(s). The old test
# does not work if mount returns multiple parameters in the 5th field,
# e.g. (rw,errors=remount-ro) on some debian systems. This regex
# assumes that the rw parameter is always listed first, which may not
# always be the case. If it fails, take out the '\('. Thanks to Pasi
# Oja-Nisula <pon at iki dot fi> for pointing this out.
# 2003 01 09: Added support for FAT32. We now create two scripts for
# each hard drive, make.dev.[as]dx and mount.dev.[as]dx. These create
# and make file systems on each partition, and make mount points and
# mount them.
# 2002 12 25: added support to handle W95 extended (LBA) (f) and W95
# FAT 32 partitions. I have tested this for primary but not logical
# partitions.
# 2002 09 08: Added minimal support for ext3fs. We now detect mounted
# ext3fs partitions & rebuild but with no options. The detection
# depends on the command line "dumpe2fs <device> 2>/dev/null | grep -i
# journal" producing no output for an ext2fs, and output (we don't
# care what) for an ext3fs.
# This could stand extension to support non-default ext3 options such
# as the type of journaling. Volunteers?
# 2002 07 25: Bad block checking is now a command line option (-c) at
# the time the product script is run.
# 2002 07 03: Corrected the mechanism for specifying the default
# drive.
# 2001 11 25: Changed the way mke2fs gets its bad block
# list. badblocks does not guess at the block size, so you have to get
# it (from dumpe2fs) and feed it to badblocks. It is simpler to just
# have mke2fs call badblocks, but you do loose the ability to have a
# writing test easily. -- C^2
# 2001 11 25: Changed the regex that extracts partition labels from
# the mount command. This change does not affect the results at all,
# it just makes it possible to use Emacs' perl mode to indent
# correctly. I just escaped the left bracket in the regex. -- C^2
# Discussion:
# fdisk will spit out a file of the form below if you run it as "fdisk
# -l".
# root@tester ~/bin $ fdisk -l /dev/hda
# Disk /dev/hda: 64 heads, 63 sectors, 1023 cylinders
# Units = cylinders of 4032 * 512 bytes
# Device Boot Start End Blocks Id System
# /dev/hda1 1 9 18112+ 83 Linux
# /dev/hda2 10 1023 2044224 5 Extended
# /dev/hda5 10 368 723712+ 83 Linux
# /dev/hda6 369 727 723712+ 83 Linux
# /dev/hda7 728 858 264064+ 83 Linux
# /dev/hda8 859 989 264064+ 83 Linux
# /dev/hda9 990 1022 66496+ 82 Linux swap
# What fdisk does not do is provide output suitable for later
# importing into fdisk, a la sfdisk. This script parses the output
# from fdisk and creates an input file for fdisk. Use the input file
# like so:
# fdisk /dev/hdx < dev.hdx
# For the bare metal restore package, this script also builds a script
# that will execute the above command so you can run it from your zip
# disk. Because the bare metal restore scripts all are in /root/bin,
# the data file and script created by this script are also placed
# there. The same script also creates appropriate Linux file systems,
# either ext2fs, or Linux swap. There is limited support for FAT12,
# FAT16 and FAT32. For anything else, you're on your own.
# Note for FAT32: According to the MS KB, there are more than one
# reserved sectors for FAT32, usually 32, but it can vary. Do a search
# in M$'s KB for "boot sector" or BPB for the gory details. For more
# info than you really need on how boot sectors are used, see
# http://support.microsoft.com/support/kb/articles/Q140/4/18.asp
# You can also edit dev.x to change the sizes of partitions. Don't
# forget, if you change the size of a FAT partition across the 32MB
# boundary, you need to change the type as well! Run "fdisk /dev/hda"
# or some such, then the l command to see the available partition
# types. Then go ahead and edit dev.x appropriately. Also, when moving
# partition boundarys with hand edits, make sure you move both logical
# and extended partition boundaries appropriately.
# Bad block checking right now is a quick read of the partition. A
# writing check is also possible but more difficult. You have to run
# badblocks as a separate command, and pass the bad block list to
# mke2fs in a file (in /tmp, which is a ram disk). You also have to
# know how large the blocks are, which you learn by running
# dumpe2fs. It gets messy and I haven't done it yet. You probably
# don't need it for a new hard drive, but if you have had a hard drive
# crash on you and you are reusing it (while you are waiting for its
# replacement to come in, I presume), then I highly recommend it. Let
# me know how you do it.
# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.
# cut2fmt figures out the format string for the unpack function we use
# to slice and dice the output from fdisk. From Christiansen and
# Torkington, Perl Cookbook 5.
sub cut2fmt {
my (@positions) = @_;
my $template = '';
my $lastpos = 1;
foreach $place (@positions) {
$template .= "A" . ($place - $lastpos) . " ";
$lastpos = $place;
}
$template .= "A*";
return $template;
}
# Sub gpl, a subroutine to ship the GPL and other header information
# to the current output file.
sub gpl {
my $FILE = shift;
my $year = shift;
print $FILE <<FINIS;
# Copyright $year through the last date of modification Charles Curley.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA
# You can also contact the Free Software Foundation at http://www.fsf.org/
# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.
FINIS
}
sub getBootSector {
my $infile = $_[0];
my $outfile = $_[1];
$systemcmd = "dd if=$infile of=$outfile bs=512 count=1 &> /dev/null ";
system ($systemcmd);
}
# If we have one & only one swap partition, then this must be
# it. Otherwise the user is on her own. We scan fstab for swap mount
# points that have labels for their devices. If there is one and only
# one, we assume that's it, otherwise pass.
sub getswaplabel {
my $dev = $_[0];
open (FSTAB, "< /etc/fstab")
or die "Couldn't fork: $!\n";
while (defined (my $line = <FSTAB>)) {
chop ($line);
@fstabs = split (" ", $line);
if (@fstabs[1] eq "swap") {
$swaplabel = @fstabs[0];
if ($swaplabel =~ /LABEL/) {
$swaps++;
$sl = substr ($swaplabel, 6);
}
# print ("\"@fstabs[0]\", \"@fstabs[1]\", \"$sl\", $swaps.\n");
break;
}
}
close (FSTAB);
# print "label is $sl.\n";
if ($swaps == 1) {
$ret = "mkswap \$blockcheck -L $sl";
$ret .= " $dev\n\n";
} else {
$ret = "mkswap \$blockcheck $dev\n\n";
}
# print ("Returning :$ret\n");
return $ret;
}
# dolvm is a subroutine to handle LVM partitions. This is
# experimental....
$lvms = 0; # true if we've been here before
sub dolvm {
# print ("In dolvm ()...\n");
if ($lvms == 0) {
$lvms = 1;
# Scan /etc/fstab for the logical volumes and write a script to
# make file systems on them and another to mount 'em later on.
$mklvs = open (MKLVS, "> ${outputfilepath}bin/make.lvs")
or die "Couldn't fork: $!\n";
print MKLVS <<FINIS;
#! /bin/sh
# A script to create file systems on logical volumes. Created at bare
# metal backup time by the Perl script make.fdisk.
FINIS
&gpl (*MKLVS, "2006");
print MKLVS <<FINIS;
export blockcheck=\$1;
if [ "\$blockcheck" != "-c" ] && [ -n "\$blockcheck" ]
then
echo "\${0}: Build file systems on logical volumes."
echo "\${0}: -c: block check during file system making."
exit 1;
fi
FINIS
$mtlvs = open (MTLVS, "> ${outputfilepath}bin/mount.lvs")
or die "Couldn't fork: $!\n";
print MTLVS <<FINIS;
#! /bin/sh
# A script to mount file systems on logical volumes. Created at bare
# metal backup time by the Perl script make.fdisk.
FINIS
&gpl (*MTLVS, "2007");
# Now cycle through all the known logical volumes & set them
# up. N.B.: This has been tested on a machine with only one
# LV. But it *should* work.
$pvdisp = open (PVDISP, "pvdisplay -c |")
or die ("Can't open LVM display.\n");
while (defined (my $pv = <PVDISP>)) {
chop ($pv);
# print ("$pv\n");
@pv = split (":", $pv);
$uid = @pv[11];
$pvname = @pv[1];
$phv = @pv[0];
# print ("pv $pvname has uid $uid.\n");
# back up the LVM's lvm details. Get the config files.
system ("vgcfgbackup -f ${outputfilepath}metadata/LVM.backs.$pvname $pvname");
print (MKLVS "echo \"y\\n\" | pvcreate -ff --uuid \"$uid\"\\\n");
print (MKLVS " --restorefile ../metadata/lvm/backup/${pvname} $phv\n");
print (MKLVS "vgcfgrestore --file ../metadata/LVM.backs.$pvname $pvname\n\n");
}
print (MKLVS "# Hideously disty dependent! turn on LVM.\n");
print (MKLVS "if [ -e /etc/init.d/lvm ] ; then\n");
print (MKLVS " /etc/init.d/lvm start\nfi\n\n");
# Now walk fstab in search of logical volumes. This is
# necessary to pick up swap partitions, and it may pick up
# others. We need fstab below to match the partitions up with
# their mount points, so we keep the array around.
%volsfound = ();
open (FSTAB, "< /etc/fstab")
or die "Couldn't fork: $!\n";
@fstab = <FSTAB>;
foreach $line (@fstab) {
@fstabs = split (" ", $line);
if (@fstabs[0] =~ /VolGroup/ ) {
# print ("$line\n");
if (@fstabs[2] eq "swap") {
print (MKLVS "echo\necho making LV @fstabs[0] a swap partition.\n");
print (MKLVS "mkswap \$blockcheck @fstabs[0]\n\n");
} elsif (@fstabs[2] == "ext3") {
print (MKLVS "echo\necho making LV @fstabs[0], @fstabs[1],");
print (MKLVS " an ext3 partition.\n");
print (MKLVS "mke2fs -j \$blockcheck @fstabs[0]\n\n");
print (MTLVS "mkdir -p /target$fstabs[1]\n");
print (MTLVS "mount @fstabs[0] /target$fstabs[1]\n\n");
$volsfound{@fstabs[0]} = 3;
} elsif (@fstabs[2] == "ext2") {
print (MKLVS "echo\necho making LV @fstabs[0], @fstabs[1],");
print (MKLVS " an ext2 partition.\n");
print (MKLVS "mke2fs \$blockcheck @fstabs[0]\n\n");
print (MTLVS "mkdir -p /target$fstabs[1]\n");
print (MTLVS "mount @fstabs[0] /target$fstabs[1]\n\n");
$volsfound{@fstabs[0]} = 2;
} else {
print ("Opps, unknown type of logical volume, @fstabs[0]\n");
}
}
}
# print ("Volumes already found are: ");
# while ( ($k, $v) = each %volsfound ) {
# print ("$k ==> $v ");
# }
# print ("\n");
# Now walk the logical volume devices and pick up any
# partitions formated ext3/ext2. This may result in duplicates
# if the partitions have labels but are mounted by device name
# rather than by label.
opendir (DEVHANDLE, "/dev") or die ("Can't open /dev!!\n");
while ( defined ($fname = readdir (DEVHANDLE))) {
@fnames = (@fnames, $fname);
}
@sorted = sort (@fnames);
foreach $fname (@sorted) {
if ($fname =~ /^VolGroup/ && -d "/dev/$fname") {
# print ("Inside /dev is $fname.\n");
opendir (VOLHANDLE, "/dev/$fname")
or die ("Can't open /dev/$fname!!\n");
while ( defined ($vname = readdir (VOLHANDLE))) {
@vnames = (@vnames, $vname);
}
@vsorted = sort (@vnames);
foreach $vname (@vsorted) {
# print "/dev/$fname/$vname: " . $volsfound{"/dev/$fname/$vname"} . "\n";
if($vname ne "." && $vname ne ".."
&& $volsfound{"/dev/$fname/$vname"} < 1) {
# print ("Inside /dev/$fname is $vname.\n");
my $journal = 0;
# FIX ME: add tests to be sure it's a symlink
# to a block device.
# Is it extX?
open (DUMP, "dumpe2fs /dev/$fname/$vname 2> /dev/null|");
@lines = <DUMP>;
if (scalar (@lines) > 1) {
# If we've gotten here we have a valid
# ext[2|3] file system. Now prepare to
# spit out the commands to recreate
# it. Get the label, if any, and whether
# there is a journal or not.
foreach $_ (@lines) {
if (/Filesystem volume name:/) {
$label = substr ($_, 26);
chop ($label);
# print ("\$label is \"$label\".\n");
}
if (/has_journal/) {
$journal = 1;
}
}
# get the mount point from fstab so we can mount it.
foreach $fstab (@fstab) {
@fstabs = split (" ", $line);
if (@fstabs[0] eq "LABEL=$label" ) {
$mountpoint = @fstabs[1];
# print ("mount point is \"$mountpoint\".\n");
last;
}
}
if (length ($label) ) {
$label = "-L \"" . $label . "\"";
}
if ($journal > 0) {
print (MKLVS "echo\necho making LV /dev/$fname/$vname");
print (MKLVS " an ext3 partition.\n");
print (MKLVS "mke2fs -j $label \$blockcheck /dev/$fname/$vname\n\n");
print (MTLVS "mkdir -p /target$mountpoint\n");
print (MTLVS "mount /dev/$fname/$vname /target$mountpoint\n\n");
} else {
print (MKLVS "echo\necho making LV /dev/$fname/$vname");
print (MKLVS " an ext2 partition.\n");
print (MKLVS "mke2fs $label \$blockcheck /dev/$fname/$vname\n\n");
print (MTLVS "mkdir -p /target$mountpoint\n");
print (MTLVS "mount /dev/$fname/$vname /target$mountpoint\n\n");
}
}
}
}
closedir (VOLHANDLE);
}
}
print (MTLVS "mount | grep -i \"/target\"\n");
closedir (DEVHANDLE);
close (FSTAB);
close (MKLVS);
close (MTLVS);
chmod 0700, "${outputfilepath}bin/make.lvs";
chmod 0700, "${outputfilepath}bin/mount.lvs";
# Copy the LVM configuration to where we can get at it...
system ("cp -rp /etc/lvm ${outputfilepath}metadata/");
}
# print ("Leaving dolvm ()...\n");
return ($ret);
}
# Begin main line code.
# Provide a default device.
# print "\$ARGV[0] is $ARGV[0].\n";
$device = defined ($ARGV[0]) ? $ARGV[0] : "/dev/hda";
# Need to check to see if $device is a sym link. If it is, the mount
# point is the target of the link. (Mandrake) Otherwise we search for
# mount points on $device. Fedora, Red Hat.
if ( -l $device) {
# It is a sym link. Get the target of the link, then make it into
# an absolute path, preserving the numbering.
$mountdev = '/dev/' . readlink ($device);
$mountdev =~ s|ide/host(\d+)/bus(\d+)/target(\d+)/lun(\d+)/disc
|ide/host\1/bus\2/target\3/lun\4|x;
} else {
# not a sym link; just assign it.
$mountdev = $device;
}
# print "Device is $device; mount device is $mountdev.\n";
# Prepare format string. Here are two format strings I have found
# useful. Here, column numbers are 1 based, i.e. the leftmost column
# is column 1, not column 0 as in Emacs.
# We select a format string according to fdisk's version.
$fdpid = open (FDVER, "fdisk -v |") or die "Couldn't fork: $!\n";
while (<FDVER>) {
@_ = unpack ("A7 A*", $_);
$fdver=$_[1];
$fdver =~ s/[^\d.]//g; # strip non-numbers, non-periods, as in "2.12pre".
}
# print "fdisk version is $fdver\n";
if ($fdver < 2.12) {
# fdisk to 2.11?? Red Hat, Fedora Core 1
$fmt = cut2fmt (11, 19, 24, 34, 45, 49);
} else {
# fdisk 2.12 & up?? Mandrake 10.0, Fedora Core 2
$fmt = cut2fmt (12, 14, 26, 38, 50, 55);
}
# print "Format string is $fmt.\n";
# define fields in the array @_.
$dev = 0;
$bootable = 1;
$firstcyl = 2;
$lastcyl = 3;
$parttype = 5;
$partstring = 6;
$target = "\/target";
$outputfilename = $device;
$outputfilename =~ s/\//./g;
$outputfilename = substr ($outputfilename, 1, 100);
# $outputfilepath = "/root/bin/";
$outputfilepath = $ENV{"zip"} . "/";
# print "\$outputfilepath is ${outputfilepath}\n";
# Make a hash of the labels.
$mpid = open (MOUNT, "mount -l |") or die "Couldn't fork: $!\n";
while (<MOUNT>) {
if ($_ =~ /^$mountdev/i) { # is this a line with a partition in it?
# print $_; # print it just for grins
split;
if ($_[6] ne "") { # only process if there actually is a label
$_[6] =~ s/[\[\]]//g; # strike [ and ].
$labels{$_[0]} = $_[6];
# print "The label of file device $_[0] is $labels{$_[0]}.\n";
}
# We only mount if it's ext2fs or ext3fs and read and write.
if ($_[4] =~ /ext[23]/ and $_[5] =~ /\(rw/ ) {
if ($_[0] =~ /ide/i) {
# We have a devfs system, e.g. Mandrake. This code
# converts back from the devfs designation to the old
# /dev/hd* designation for tomsrtb. I have NOT checked
# this out for drives other than /dev/hda. Also, this
# code does not handle SCSI drives.
if ( $_[0] =~ /target0/ && $_[0] =~ /bus0/ ) {
$letter = 'a';
} elsif ( $_[0] =~ /target1/ && $_[0] =~ /bus0/) {
$letter = 'b';
} elsif ( $_[0] =~ /target0/ && $_[0] =~ /bus1/) {
$letter = 'c';
} else {
$letter = 'd';
}
$_[0] =~ s|/ide/host\d+/bus\d+/target\d+/lun\d+/part|/hd|g;
$_[0] =~ s/hd/hd$letter/;
}
$mountpoints{$_[2]} = $_[0];
# print "$_[2] is the mountpoint for tomsrtbt";
# print " device $mountpoints{$_[2]}.\n";
}
}
}
close (MOUNT);
# Get sfdisk output. If we have sfdisk at restore time (e.g. Knoppix),
# we'll use it.
system "sfdisk -d $device > ${outputfilepath}metadata/${outputfilename}.sfd";
# Otherwise we'll use the output from fdisk, which may or may not be
# any more accurate.
$fpid = open (FDISK, "fdisk -l $device |") or die "Couldn't fork: $!\n";
open (OUTPUT, "> ${outputfilepath}metadata/${outputfilename}")
or die "Couldn't open output file ${outputfilepath}metadata/${outputfilename}.\n";
while (<FDISK>) {
if ($_ =~ /^$device/i) { # is this a line with a partition in it?
# print $_; # print it just for grins
chop; # kill trailing \r
@_ = unpack ($fmt, $_);
# Now strip white spaces from cylinder numbers, white space &
# leading plus signs from partition type.
@_[$firstcyl] =~ s/[ \t]+//;
@_[$lastcyl] =~ s/[ \t]+//;
@_[$parttype] =~ s/[+ \t]+//;
$partnumber = substr(@_[$dev], 8, 10); # get partition number for this line
# just for grins
# print " $partnumber, @_[$firstcyl], @_[$lastcyl],";
# print " @_[$parttype], @_[$partstring]\n";
# Here we start creating the input to recreate the partition
# this line represents.
print OUTPUT "n\n";
if ($partnumber < 5) {
# primary Linux partition
if (@_[$parttype] == 83) {
print OUTPUT "p\n$partnumber\n@_[$firstcyl]\n";
# in case it's all on one cylinder
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
# Now detect if this is an ext3 (journaling)
# partition. We do this using dumpe2fs to dump the
# partition and grepping on "journal". If the
# partition is ext2, there will be no output. If it is
# ext3, there will be output, and we use that fact to
# set a command line switch. The command line switch
# goes into an associative array (hash) so we don't
# have to remember to reset it to the null string when
# we're done.
$dpid = open (DUMPE2FS,
"dumpe2fs @_[$dev] 2>/dev/null | grep -i journal |")
or die "Couldn't fork: $!\n";
while (<DUMPE2FS>) {
# print "Dumpe2fs: $_";
$ext3{$_[$dev]} = "-j ";
last;
}
close (DUMPE2FS);
if ($labels{@_[$dev]}) { # do we have a label?
$format .= "echo\necho formatting $checking@_[$dev]\n";
$format .= "mke2fs $ext3{$_[$dev]}\$blockcheck";
$format .= " -L $labels{@_[$dev]} @_[$dev]\n\n";
} else {
$format .= "echo\necho formatting $checking@_[$dev]\n";
$format .= "mke2fs $ext3{$_[$dev]}\$blockcheck @_[$dev]\n\n";
}
# extended partition
} elsif (@_[$parttype] == 5) {
# print ("Creating Extended Partition.\n");
print OUTPUT "e\n$partnumber\n@_[$firstcyl]\n";
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
# extended partition, Win95 Ext'd (LBA)
} elsif (@_[$parttype] eq "f") {
# print ("Creating Extended LBA Partition.\n");
print OUTPUT "e\n$partnumber\n@_[$firstcyl]\n";
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
$typechanges .= "t\n$partnumber\nf\n";
# primary Linux swap partition
} elsif (@_[$parttype] == 82) {
print OUTPUT "p\n$partnumber\n@_[$firstcyl]\n";
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
$typechanges .= "t\n$partnumber\n82\n";
$format .= "echo\necho Making @_[$dev] a swap partition.\n";
if ($labels{@_[$dev]}) { # do we have a label?
$format .= "mkswap \$blockcheck -L $labels{@_[$dev]}";
$format .= " @_[$dev]\n\n";
} else {
$format .= getswaplabel (@_[$dev]);
}
# Primary mess-dos partition. We don't handle hidden
# partitions.
} elsif ( @_[$parttype] == 1 || @_[$parttype] == 4 || @_[$parttype] == 6
|| @_[$parttype] eq "b" || @_[$parttype] eq "c"
|| @_[$parttype] eq "e" || @_[$parttype] eq "12" ) {
# print ("Making DOS primary partition.\n");
getBootSector (@_[$dev], "${outputfilepath}metadata/$outputfilename$partnumber");
print OUTPUT "p\n$partnumber\n@_[$firstcyl]\n";
# in case it's all on one cylinder
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
$typechanges .= "t\n$partnumber\n@_[$parttype]\n";
$format .= "echo\necho formatting $checking@_[$dev]\n";
$format .= "mkdosfs \$blockcheck";
if ( @_[$parttype] == b || @_[$parttype] == c
|| @_[$parttype] eq "12" ) {
# We have a W9x FAT32 partition. Add a command line switch.
$format .= " -F 32";
}
$format .= " @_[$dev]\n";
$format .= "# restore FAT boot sector.\n";
$format .= "dd if=$outputfilename$partnumber";
$format .= " of=@_[$dev] bs=512 count=1\n\n";
} elsif ( @_[$parttype] == "8e") {
$format .= dolvm ();
} else {
# anything else partition
print OUTPUT "p\n@_[$firstcyl]\n";
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
$typechanges .= "t\n$partnumber\n@_[$parttype]\n";
}
} else {
# logical Linux partition
if (@_[$parttype] == 83) {
print OUTPUT "l\n@_[$firstcyl]\n";
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
# Now detect if this is an ext3 (journaling)
# partition. We do this using dumpe2fs to dump the
# partition and grepping on "journal". If the
# partition is ext2, there will be no output. If it is
# ext3, there will be output, and we use that fact to
# set a command line switch. The command line switch
# goes into an associative array (hash) so we don't
# have to remember to reset it to the null string when
# we're done.
$dpid = open (DUMPE2FS,
"dumpe2fs @_[$dev] 2>/dev/null | grep -i journal |")
or die "Couldn't fork: $!\n";
while (<DUMPE2FS>) {
# print "Dumpe2fs: $_";
$ext3{$_[$dev]} = "-j ";
last;
}
close (DUMPE2FS);
if ($labels{@_[$dev]}) { # do we have a label?
$format .= "echo\necho formatting $checking@_[$dev]\n";
$format .= "mke2fs $ext3{@_[$dev]}\$blockcheck";
$format .= " -L $labels{@_[$dev]} @_[$dev]\n\n";
} else {
$format .= "echo\necho formatting $checking@_[$dev]\n";
$format .= "mke2fs $ext3{@_[$dev]}\$blockcheck @_[$dev]\n\n";
}
# logical Linux swap partition
} elsif (@_[$parttype] == 82 ) {
print OUTPUT "l\n@_[$firstcyl]\n";
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
$typechanges .= "t\n$partnumber\n82\n";
$format .= "echo\necho Making @_[$dev] a swap partition.\n";
if ($labels{@_[$dev]}) { # do we have a label?
$format .= "mkswap \$blockcheck -L $labels{@_[$dev]}";
$format .= " @_[$dev]\n\n";
} else {
$format .= getswaplabel (@_[$dev]);
}
# Logical mess-dos partition. We don't handle hidden
# partitions.
} elsif ( @_[$parttype] == 1 || @_[$parttype] == 4 || @_[$parttype] == 6
|| @_[$parttype] eq "b" || @_[$parttype] eq "c"
|| @_[$parttype] eq "e" || @_[$parttype] eq "12" ) {
# print ("Making DOS logical partition.\n");
getBootSector (@_[$dev], "${outputfilepath}metadata/$outputfilename$partnumber");
print OUTPUT "l\n$partnumber\n@_[$firstcyl]\n";
# in case it's all on one cylinder
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
$typechanges .= "t\n$partnumber\n@_[$parttype]\n";
$format .= "echo\necho formatting $checking@_[$dev]\n";
$format .= "mkdosfs \$blockcheck";
if ( @_[$parttype] == b || @_[$parttype] == c
|| @_[$parttype] eq "12" ) {
# We have a W9x FAT32 partition. Add a command line switch.
$format .= " -F 32";
}
$format .= " @_[$dev]\n";
$format .= "# restore FAT boot sector.\n";
$format .= "dd if=$outputfilename$partnumber";
$format .= " of=@_[$dev] bs=512 count=1\n\n";
} elsif ( @_[$parttype] == "8e") {
$format .= dolvm ();
} else {
# anything else partition
print OUTPUT "l\n@_[$firstcyl]\n";
if (@_[$firstcyl] ne @_[$lastcyl]) {
print OUTPUT "@_[$lastcyl]\n";
}
$typechanges .= "t\n$partnumber\n@_[$parttype]\n";
}
}
# handle bootable partitions
if (@_[$bootable] =~ /\*/) {
print OUTPUT "a\n$partnumber\n";
}
} else {
# If we got here, the current line does not have a partition in it.
# Get the geometry for fdisk. Force fdisk to use the current
# geometry at restoration time. Comment this out for
# tomstrbt's fdisk; it doesn't like it.
if ($_ =~ /heads.*sectors.*cylinders/i) {
# print $_; # again, for grins.
chop;
@geometry = split (/ /, $_);
$geometry = "-H $geometry[0] -S $geometry[2] -C $geometry[4]";
# print $geometry;
}
}
}
# Append all the partition type changes, validate, and print out the
# results.
print OUTPUT "${typechanges}v\nw\n";
close (OUTPUT);
close (FDISK);
open (OUTPUT, "> ${outputfilepath}bin/make.$outputfilename")
or die "Couldn't open output file ${outputfilepath}bin/make.$outputfilename.\n";
print OUTPUT <<FINIS;
#! /bin/sh
# A script to restore the partition data of a hard drive and format
# the partitions. Created at bare metal backup time by the Perl script
# make.fdisk.
FINIS
&gpl (*OUTPUT, "2001");
print OUTPUT <<FINIS;
swapoff -a
# Hideously disty dependent! Turn off LVM.
if [ -e /etc/init.d/lvm ] ; then
/etc/init.d/lvm stop
fi
export blockcheck=\$1;
if [ "\$blockcheck" != "-c" ] && [ -n "\$blockcheck" ]
then
echo "\${0}: automated restore with no human interaction."
echo "\${0}: -c: block check during file system making."
exit 1;
fi
FINIS
# Clean the old partition table out. Turn off swap in case we're using
# it.
# print OUTPUT "dd if=/dev/zero of=$device bs=512 count=2\n\nsync\n\n";
print OUTPUT "dd if=/dev/zero of=$device bs=1024 count=2000\n\nsync\n\n";
# command for fdisk
$fdiskcmd .= "# see if we have sfdisk & if so use it.\n";
$fdiskcmd .= "if which sfdisk ; then\n";
$fdiskcmd .= " echo \"Using sfdisk.\"\n";
$fdiskcmd .= " sfdisk $geometry $device < ../metadata/${outputfilename}.sfd\n";
$fdiskcmd .= "else\n";
$fdiskcmd .= " echo \"using fdisk.\"\n";
$fdiskcmd .= " fdisk $geometry $device \< ../metadata/$outputfilename\n";
$fdiskcmd .= "fi\n\nsync\n\n";
print OUTPUT $fdiskcmd;
print OUTPUT $format;
print OUTPUT "fdisk -l \"$device\"\n";
close (OUTPUT);
# Now build the script that will build the mount points on the root
# and other partitions.
open (OUTPUT, "> ${outputfilepath}bin/mount.$outputfilename")
or die "Couldn't open output file ${outputfilepath}bin/make.$outputfilename.\n";
print OUTPUT <<FINIS;
#! /bin/sh
# A script to create a minimal directory tree on the target hard drive
# and mount the partitions on it. Created at bare metal backup time by
# the Perl script make.fdisk.
FINIS
&gpl (*OUTPUT, "2001");
print OUTPUT <<FINIS;
# WARNING: If your Linux system mount partitions across hard drive
# boundaries, you will have multiple "mount.dev.* scripts. You must
# ensure that they run in the proper order. The root partition should
# be mounted first, then the rest in the order they cascade. If they
# cross mount, you'll have to handle that manually.
FINIS
# We have a hash of mount points and devices in %mountpoints. However,
# we have to process them such that directories are built on the
# appropriate target partition. E.g. where /usr/local is on its own
# partition, we have to mount /usr before we build /usr/local. We can
# ensure this by sorting them. Shorter mount point paths will be built
# first. We can't sort a hash directly, so we use an array.
# We build commands to create the appropriate mount points and then
# mount the partitions to the mount points. This is in preparation for
# untarring the contents of the ZIP disk, done in restore.metadata.
foreach $point ( sort keys %mountpoints) {
print OUTPUT "\n# $point is the mountpoint for";
print OUTPUT " tomsrtbt device $mountpoints{$point}.\n";
print OUTPUT "mkdir -p $target$point\n";
print OUTPUT "mount $mountpoints{$point} $target$point\n";
}
print OUTPUT "\nmount | grep -i \"/target\"\n";
close (OUTPUT);
# These scripts are dangerous & should only be visible to root.
chmod 0700, "${outputfilepath}bin/make.$outputfilename";
chmod 0700, "${outputfilepath}bin/mount.$outputfilename";
chmod 0600, "${outputfilepath}metadata/${outputfilename}*";
]]>