2

Как скопировать (загрузочный) дистрибутив Linux на устройства? это полезно, например, если у меня есть загрузочный раздел под управлением Linux (возможно, но не обязательно), и я хочу скопировать его на USB-накопитель.

Я написал сценарий, который имеет некоторые меры безопасности, а затем выполняет необходимые шаги. Я опубликую это как ответ. Большим ограничением в скрипте является то, что дистрибутив должен быть основан на syslinux. очевидно, сценарий не для всех. Я также не эксперт по этому вопросу. Я попытался взломать решение, которое помогло мне. идея состоит в том, чтобы помочь другим, борющимся с созданием чего-то подобного или лучшего.

1 ответ1

0

Хотя сценарий кажется длинным, большая часть его предназначена для того, чтобы убедиться, что вы не делаете глупостей (например, пытаетесь записать на свой внутренний жесткий диск). Этот сценарий в первую очередь полезен тем, что он может предоставить один рабочий пример необходимых шагов (по состоянию на декабрь 2014 года)... по крайней мере, в моей системе Linux. это абсолютно бесплатно и изменчиво.

#!/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";
}

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .