embora o script pareça longo, a maior parte é dedicada a garantir que você não esteja fazendo algo estúpido (como tentar gravar no seu próprio disco rígido interno). o script é principalmente útil, pois pode fornecer um exemplo de trabalho das etapas necessárias (a partir de dezembro de 2014) ... pelo menos no meu sistema linux. é totalmente gratuito e mutável.
#!/usr/bin/perl -w
use strict;
use warnings FATAL => qw{ uninitialized };
=pod
=head1 Title
distrocopy.pl -- copy a syslinux-loader based linux distribution from one device (e.g., a HD) to another (e.g., USB flash)
=head1 Options
-f actually execute. without it, we are just checking all the requirements
-rw allow running this script even when the root partition is not mounted rw
-unremovable allow writing to an unremovable drive (protects from the worst error!)
=head1 Description
This script can copy a syslinux-based linux distribution from one device to another and make it bootable.
However, we are making some assumptions:
=over 4
=item other non-root /etc/fstab partitions will be available after boot. this is good if your /home partition sits on another device. if your home partition sits on the same device, then you need to hand copy it (cp -a ...).
=item the root distribution is syslinux based. we need it to find the scripts that come with syslinux. note that you should be able to run this script while booted from a ubuntu grub partition when you want to copy a syslinux-based distribution from one external USB drive to another. you can also boot from a syslinux-based distribution and use the script.
=item we are creating one giant ext4 partition on the destination device. if you want it smaller, use the ext-tools to shrink it later.
=back
=head1 Bugs
many.
IMHO, even syslinux is a mess, but it is a lesser mess than grub. why can't we simply have a loader that passes control to just one file?
# mkbootable /dev/sdb /dev/sdb2
this would alter the boot sector on sdb, so that it passes control to whatever linux distro lives on sdb2
# mkbootable /dev/sdc 8538185f-e75c-4e43-823e-43cfb3d14774
this would alter the boot sector on sdc, so that it passes control to whatever linux distro lives on this uuid
and then
# whatboots /dev/sdb
MBR, will boot linux from /dev/sdb2. checks confirm boot-startup
=head1 License
Totally free. Please improve.
=head1 Versions
0.00: Ivo Welch, December 2014.
=cut
################################################################
my $usage= "Usage: $0 [-f] [-rw] /mounted-root-of-distro /dev/sd[a-z]";
($#ARGV >= 0) or die "Too few arguments: $#ARGV\n$usage";
my $testrun = 1;
if ($ARGV[0] eq "-f") { shift(@ARGV); $testrun= 0; print "\nACTUAL RUN\n"; } else { print "[test run only]\n"; }
my $allowrw = 0;
if ($ARGV[0] eq "-rw") { shift(@ARGV); $allowrw= 1; print "Work on rw mounted source\n"; }
my $allowunremovable = 0;
if ($ARGV[0] eq "-unremovable") { shift(@ARGV); $allowunremovable= 1; print "Allowing on unremovable destination\n"; }
($#ARGV == 1) or die "There must be exactly two further arguments, not ".($#ARGV+1)."\n$usage";
my ($src, $devdest) = @ARGV;
my $mounts= `mount | grep \/dev\/sd`."\n".`mount | grep \/dev\/mmcblk`; ## ok, so I lied. I am prepping for mmc cards here
################################################################################################################################
print "
################ extensive checks of the source directory
";
($src =~ /^\//) or die "root device '$src' must start with \/\n";
($src =~ /^\/dev\//) and die "root device '$src' must not be a device file, but a mounted partition.\n";
(-e "$src/boot") or die "root device '$src' must have boot directory.\n";
# could check that /boot lives on the same device
(-e "$src/bin") or die "root device '$src' must have bin directory.\n";
(-e "$src/etc/fstab") or die "root device '$src' must have etc/fstab file.\n";
# could warn if it references other partitions, esp on the same device
(-e "$src/boot/syslinux/syslinux.cfg") or die "root device '$src' must have syslinux.cfg in install.\n";
# (-e "/usr/bin/bash") or die "/usr/bin/syslinux-install_update is buggy. pls link /bin/bash to /usr/bin/bash";
$src =~ s/([a-z0-9])\/$/$1/; ## delete trailing /
my @srcmount= grep(/$src/, split(/\n/, $mounts));
($#srcmount == 0) or die "I do not understand the source mount output\n";
my $matchcount = ($srcmount[0] =~ /([^ ]+) on ([^ ]+) type ([^ ]+) \(([^ ]+)\)/);
($matchcount) or die "I cannot parse the source mount output in ".($srcmount[0])."\n";
my $sourcedevice = $1;
my $sourcemountloc = $2;
($sourcemountloc eq $src) or die "root device '$src' must be a first-order mount point in:\n$mounts\n";
my $filesystemtype = $3;
my $features = $4; ## things like ro
my $srcuuid = `blkid -s UUID -o value $sourcedevice`;
chomp($srcuuid);
if (!($allowrw)) {
($features =~ /ro/) or die "source file system '$src' was not mounted ro, and no -rw switch used\n";
}
(($filesystemtype =~ /ext[2-4]/)||($filesystemtype =~ /btrfs/)) or die "source file system '$src' was not ext[2-4] or btrfs\n";
print "
################ check the destination directory
";
(($devdest =~ /^\/dev\/sd[a-z]$/) || ($devdest =~ /^\/dev\/mmcblk[0-9]$/))
or die "dest device '$devdest' must be a device file in /dev/sdX format.\n";
($mounts =~ /$devdest/sg) and die "dest device '$devdest' must not have any mounts active:\n\n$mounts\n\n";
(my $destsysblock= $devdest) =~ s/\/dev\//\/sys\/block\//;
if (!($allowunremovable)) {
my $isremovable = `udevadm info -a -p $destsysblock | grep -i ATTR{removable}`;
($isremovable =~ /ATTR{removable}=="1"/) or
die "Sorry, but device '$destsysblock' is not removable\n";
}
print "
################ check available programs
";
## we could search some for these instead of assuming they are here.
my @needed= ("/bin/dd", "/sbin/sfdisk", "/sbin/mkfs.ext4", "/bin/mount", "/usr/bin/rsync",
"$src/usr/bin/syslinux-install_update", "/bin/bash", "$src/usr/bin/extlinux", "/bin/umount");
foreach (@needed) { (-e $_) or die "Sorry, but I refuse to work without '$_'\n"; }
print "[validity tests passed]\n";
($testrun) and die "now run with -f flag to actually execute it.\n";
################################################################
### this is the business end of the script
################################################################
## we will try to do this early. if it fails, we state that this command cannot be run.
my $tempmntloc= "/tmp/writermount";
(-d $tempmntloc) or mkdir( $tempmntloc ); ## could already exist. we don't care.
(-d $tempmntloc) or die "cannot make or see $tempmntloc ";
print "
################ partition the drive
";
## zero out old partition table and more: 512 bytes
system0("/bin/dd if=/dev/zero of=$devdest bs=4096 count=1024", "cannot dd $devdest --- do you have permission? $!\n");
print "[Crossed the Rubicon] I have destroyed the boot sector in $devdest and am writing a new one.\n";
open(my $SFD, "| /sbin/sfdisk -L $devdest > /dev/null") or die "cannot write to sfdisk: $!\n";
print $SFD ";\n";
close($SFD);
system0("/sbin/sfdisk -L -A1 ${devdest} > /dev/null", "cannot mark my only partition as active.\n");
print "
################ format the partition
";
system0("/sbin/mkfs.ext4 ${devdest}1", "cannot make a file system on ${devdest}1: $!\n");
system0("/bin/mount ${devdest}1 $tempmntloc", "cannot mount new file system: $!\n");
(-d "$tempmntloc/lost+found/") or die "cannot see '$tempmntloc/lost+found/'\n";
print "
################ copy the partition. note the trailing slash on src
";
system0("/usr/bin/rsync -aAX $src/ $tempmntloc --exclude={${src}/dev/*,${src}/proc/*,${src}/sys/*,${src}/tmp/*,${src}/run/*,${src}/mnt/*,${src}/media/*,${src}/lost+found,${src}/home/*/.gvfs,/mnt/*}", "cannot copy device partition contents. do you have enough space?? $!\n");
my $destuuid = `blkid -s UUID -o value ${devdest}1`;
chomp($destuuid);
print "
################ update the partition
";
replaceinfile("$tempmntloc/etc/fstab", $srcuuid, $destuuid);
replaceinfile("$tempmntloc/boot/syslinux/syslinux.cfg", $srcuuid, $destuuid);
print "
################ install bootloader
";
my $syscmd1= "$tempmntloc/usr/bin/syslinux-install_update -i -a -m -c $tempmntloc";
print "exec: $syscmd1 --- please ignore error\n";
system($syscmd1); # this can give an error. ignore it
my $syscmd2= "$tempmntloc/usr/bin/extlinux --install $tempmntloc/boot/syslinux";
system0($syscmd2, "cannot exec syslinux 2 install to boot\n");
print "
################ unmount and finish
";
open(my $FOUT, ">", "$tempmntloc/$0.log") or die "cannot open '$tempmntloc/$0.log' for write\n";
print $FOUT "Written by $0 on `date`\n";
close($FOUT);
system0("/bin/umount $tempmntloc", "failed umounting $tempmntloc\n");
print "written a new bootable distro copy to $devdest.\n";
################################################################
sub replaceinfile {
my ($fname, $instr, $ostr)= @_;
(-r $fname) or die "cannot open $fname for read: $!\n";
rename( $fname, "$fname.old" ) or die "cannot rename file $fname to $fname.old: $!\n";
open(my $FIN, "<", "$fname.old") or die "cannot open '$fname.old' again: $!\n";
open(my $FOUT, ">", $fname) or die "cannot open $fname for write: $!\n";
my $success=0;
while (<$FIN>) {
(s/$instr/$ostr/) and $success=1;
print $FOUT $_;
}
close($FIN);
close($FOUT);
($success) or die "Sorry, but I did not find string '$instr' in file '$fname'\n";
return "ok";
}
sub system0 {
print "\nsystem running '$_[0]'\n";
(system($_[0])==0) or die "failed: $_[1]\n";
}