どうやって(ブート可能な)Linuxディストリビューションをデバイス間でコピーするのですか?


2

どうやって(ブート可能な)Linuxディストリビューションをデバイス間でコピーするのですか?これは、たとえばLinuxを実行しているブート可能なパーティションがある(おそらく実行されているとは限らない)場合に便利です。そして今、それをUSBスティックにコピーしたいと思います。

私はいくつかの安全性を持ち、それから必要なステップを実行するスクリプトを書きました。答えとして投稿します。スクリプトの大きな制限は、配布がsyslinuxベースでなければならないということです。明らかに、スクリプトは皆のためではありません。私もこの問題についての専門家ではありません。私はうまくいった解決策を一緒にハックしようとしました 私のために 。そのアイデアは、他の人が似たような、またはより優れたものを作成するのに苦労して手助けすることです。

回答:


0

スクリプトは長いように思えますが、そのほとんどは、あなたが愚かなことをしていないことを確認するためのものです(自分の内蔵ハードディスクに書き込もうとするなど)。このスクリプトは、主に(2014年12月現在)必要な手順の実用的な例を1つ提供できるという点で役に立ちます。少なくとも私の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";
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.