Wie kopiere ich eine (bootfähige) Linux-Distribution zwischen Geräten?

306
ivo Welch

Wie kopiere ich eine (bootfähige) Linux-Distribution zwischen Geräten? Dies ist z. B. nützlich, wenn ich eine bootfähige Partition unter Linux habe (möglicherweise aber nicht notwendigerweise), die ich jetzt auf einen USB-Stick kopieren möchte.

Ich habe ein Skript geschrieben, das einige Sicherheitsvorkehrungen enthält, und führt dann die erforderlichen Schritte aus. Ich werde es als Antwort posten. Die große Einschränkung im Skript besteht darin, dass die Distribution auf syslinux basieren muss. Offensichtlich ist das Skript nicht für jedermann. Ich bin auch kein Experte zu diesem Thema. Ich habe versucht, eine Lösung zu hacken, die für mich funktionierte . Die Idee ist, anderen zu helfen, die versuchen, etwas ähnliches oder besseres zu schaffen.

2

1 Antwort auf die Frage

0
ivo Welch

Obwohl das Skript langwierig erscheint, ist das meiste davon dafür gedacht, dass Sie nichts Dummes tun (z. B. den Versuch, auf Ihre eigene Festplatte zu schreiben). Das Skript ist in erster Linie nützlich, da es ein Arbeitsbeispiel für die erforderlichen Schritte (Stand Dezember 2014) liefern kann ... zumindest auf meinem Linux-System. es ist völlig frei und veränderbar.

#!/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`; ($isremovable =~ /ATTR=="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 $ > /dev/null", "cannot mark my only partition as active.\n");   print " ################ format the partition ";  system0("/sbin/mkfs.ext4 $1", "cannot make a file system on $1: $!\n");  system0("/bin/mount $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={$/dev/*,$/proc/*,$/sys/*,$/tmp/*,$/run/*,$/mnt/*,$/media/*,$/lost+found,$/home/*/.gvfs,/mnt/*}", "cannot copy device partition contents. do you have enough space?? $!\n");  my $destuuid = `blkid -s UUID -o value $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"; }