Linux-System von einem Unterverzeichnis auf einer Partition booten?

11

Ich möchte versuchen, einen Computer so einzurichten, dass mehrere Linux-Installationen alle im selben Dateisystem installiert sind. Zum Beispiel würde die Dateisystem - 3 Ordner: /Ubuntu_Precise, /Ubuntu_Oneiric, und /Ubuntu_Natty.

(Ich weiß, dass Sie dies mit BTRFS und Subvolumes tun können, aber ich möchte EXT4 für die Geschwindigkeit verwenden).

Ich habe einmal mehrere Installationen verschiedener Distributionen mit BTRFS eingerichtet, und nachdem dies funktioniert hat, weiß ich, dass Grub das vmlinuz- und initrd-Image gut von "nicht standardmäßigen" Pfaden booten kann. Aber als ich das BTRFS-Ding machte, gab es das rootflags=subvol=@<subvolume_name>, was dem Kernel sagte, dass er dieses Subvolume als / im Dateisystem mounten soll. Gibt es ein Argument dafür, dass Sie den Kernel übergeben könnten, der dazu führen würde, dass ein Unterordner in einer Partition als / gebunden und dann gestartet wird?

Ich denke für die anderen Teile bin ich ziemlich nah. Ich weiß, wie man ein Bind-Mount spezifiziert /etc/fstab. Wenn ich mein System mit mehreren Linux-Installationen in BTRFS-Subvolumes einrichte, bin ich es gewohnt, eine Distribution in einer VM zu installieren und sie dann mit rsync zu migrieren, sodass ich mir keine Sorgen darüber mache, was ich tun müsste Holen Sie sich die richtige Konfiguration, ich versuche nur herauszufinden, was die richtige Konfiguration wäre. Sobald ich das weiß, sollte ich in der Lage sein, die Migration in die Unterordner und die Dateibearbeitung einfach genug durchzuführen.

Ich weiß bereits über Virtualisierung und Partitionen Bescheid, aber das ist nicht das, wonach ich suche. Der Zielcomputer verfügt nicht über genügend Strom für die Virtualisierung, und Partitionen teilen sich keinen freien Speicherplatz. Ich möchte ein System einrichten, das Dual / Triple / Quad / etc Linux-Distributionen bootet, aber das tut es mit einem Dateisystem, so dass es keinen Fall von "Ich habe freien Speicherplatz, aber es ist in der falschen Partition!" Gibt.

Wenn jemand Vorschläge hat, wie ich meine Frage oder ihren Titel klarer bearbeiten kann, bin ich ganz Ohr.

Azendale
quelle
1
Es ist AFAIK nichts in das System eingebaut. Was Sie wahrscheinlich tun müssten, ist, einen weiteren Bootparameter hinzuzufügen und Ihre initramfs so zu ändern, dass sie in das Unterverzeichnis chroot werden, bevor init
Ulrich Dangel
@UlrichDangel das wollte ich vorschlagen. Mach es zu einer Antwort!
Nils
@Nils ok ich habe gerade eine Antwort gegeben, tbh. Ich wollte zuerst keinen schreiben, da ich den Patch / das Skript nicht bereitstellen wollte
Ulrich Dangel

Antworten:

10

Kurze Antwort - Soweit ich weiß, gibt es keine sofort einsatzbereite Arbeitslösung für Ihre spezifischen Anforderungen. Sie müssen jedes Initramfs jeder Distribution anpassen, um Ihre spezifischen Anforderungen zu erfüllen.

Lange Antwort - ja, das ist möglich. Heutzutage verwenden die meisten Linux-Distributionen ein initramfs, das vom Bootloader in den Speicher geladen und dann vom Kernel entpackt wird. Dort wird es ausgeführt, /sbin/initdas für das Einrichten des frühen Benutzerbereichs verantwortlich ist (Ausführen von udev, Laden von Modulen, Starten von Plymouth, Abfragen der Krypto-Passphrase, Einrichten des Netzwerks für Netzwerk-Mounts, ... Sie nennen es). Da können Sie Ihre eigenen Skripte ausführen und benutzerdefinierte Boot-Parmater auswerten.

Beispiel für Debian

Wenn Sie Debian verwenden (sollte mit Ubuntu identisch sein), sollten Sie in der Lage sein, ein Skript zu platzieren, /etc/initramfs-tools/scripts/init-bottom/das ausgeführt wird, bevor init gestartet wird. Weitere Informationen zum Skript, den verschiedenen Verzeichnissen und dem Layout finden Sie unter man initramfs-tools . Sie müssen rootmntdas Zielverzeichnis anpassen und hinzufügen.

Beispielskript (ungetestet), das entweder als /etc/initramfs-tools/scripts/local-bottom/00-myrootoder installiert werden sollte /usr/share/initramfs-tools/scripts/init-top/00-myroot:

#!/bin/sh -e

PREREQS=""

prereqs() { echo "$PREREQS"; }

case "$1" in
  prereqs)
  prereqs
  exit 0
;;
esac

for opt in $(cat /proc/cmdline); do
  case $opt in
    rootdir=*)
      new_mntdir="${opt#rootdir=}"
      ;;
    esac
done

if [ -n "$new_mntdir" ] ; then
  echo rootmnt="$rootmnt/$new_mntdir" >> /conf/param.conf
fi

Die Idee ist, anzupassen, rootmnt welche im initramfs- initSkript verwendet wird, um die echte Init zu starten / auszuführen. Da das Root-Gerät bereits in der init-bootomPhase bereitgestellt ist, können Sie das Zielverzeichnis einfach anpassen / ändern.

Um dieses Skript zu verwenden, fügen Sie einfach einen neuen Boot-Parameter hinzu, kopieren Sie das Skript, machen Sie es ausführbar, generieren Sie Ihre initramfs neu und fügen Sie einen Boot-Parameter für Ihre Linux-Distribution hinzu, z rootdir=/Ubuntu_Precise.

Ulrich Dangel
quelle
Sie möchten wahrscheinlich auch den Mount des echten Stammverzeichnisses in ein Unterverzeichnis des Betriebssystemstammverzeichnisses binden, damit Sie die anderen Betriebssystemdateien von demjenigen sehen können, das Sie starten.
Psusi
@psusi Sie können dies über fstab oder direkt mount /dev/rootdevice /mountpointnach dem Ausführen des Systems tun
Ulrich Dangel
Ich frage mich, wann sich das geändert hat? Früher war es nicht möglich, dasselbe Blockgerät erneut zu mounten. Sie würden ein EBUSY bekommen.
Psusi
1
@psusi nicht sicher, aber wahrscheinlich mit der Einführung von Bind Mounts
Ulrich Dangel
@UlrichDangel Danke für die (sehr) ausführliche Antwort!
Azendale
2

Hier sind zwei Möglichkeiten, die in Ubuntu Bionic (und möglicherweise anderswo) funktionieren. Ich habe nicht genug Repräsentanten, um zu kommentieren, aber bionisch: / usr / share / initramfs-tools / init sucht in / etc / fstab nach / usr direkt nach dem Aufruf von mountroot und vor dem Aufrufen der * -bottom-Skripte. Das untere Skript (wie in einer anderen Antwort hier vorgeschlagen) ist "zu spät". stattdessen empfehle ich diese:

#!/bin/bash -f
#copyleft 2018 greg mott

#set a subdirectory as root (so multiple installs don't need partitions)
#these work in ubuntu bionic, could be different elsewhere
#1st choice:  tweak initramfs-tools/scripts/local
#   pro:  subdirectory becomes root directly, nothing gets any chance to see the partition root
#   con:  only works if the subdirectory's initramfs/initrd is tweaked and rebuilt
#2nd choice:  specify this script as init= on the kernel commandline
#   pro:  no need to rebuild initramfs
#   con:  if the partition root etc/fstab mounts /usr the initramfs will have already mounted it
#   con:  it's conceivable some initramfs script might still look in the partition root rather than your subdirectory
#   con:  this script requires bin/bash et al in the partition root

#for either choice copy /etc/grub.d/40_custom to /etc/grub.d/07_custom and add one or more menuentries that specify subroot:
#menuentry "subroot foo" {
#     echo "subroot foo"
#              sub=/foo
#              uuid=22e7c84a-a416-43e9-ae9d-ee0119fc3894        #use your partition's uuid
#     search --no-floppy --fs-uuid --set=root $uuid
#            linux $sub/vmlinuz ro root=UUID=$uuid subroot=$sub                                                                                         
#     echo "initrd $sub/initrd.img"
#           initrd $sub/initrd.img      #works in recent releases where the /initrd.img softlink is relative
#}

#to use this script, in addition to subroot= on the kernel commandline also specify:
#   init=/path/to/script        #pathname from partition root to this script (chmod 744)

#the tweak for bionic:/usr/share/initramfs-tools/scripts/local is replace:
#          mount ${roflag} ${FSTYPE:+-t ${FSTYPE} }${ROOTFLAGS} ${ROOT} ${rootmnt}
#          mountroot_status="$?"
#with:
#          set -x
#          karg=" $(cat<proc/cmdline) " m=${karg#* subroot=}
#          [ "$m" = "$karg" ]||subroot=${m%% *}                                         #extract subroot from kernel commandline
#          [ $subroot ]&&part=part||part=$rootmnt                                       #no subroot, just mount partition as root
#          mkdir part
#          mount ${roflag} ${FSTYPE:+-t ${FSTYPE} }${ROOTFLAGS} ${ROOT} $part&&         #mount partition
#             if [ "$subroot" ]
#             then mount --bind part/$subroot $rootmnt&&                                #mount subroot
#                  umount part                       #&&sleep 15                        #unmount partition root (uncomment sleep for time to look)
#             fi
#          mountroot_status="$?"
#          [ $mountroot_status = 0 ]||sleep 90                                          #if error pause to look
#          set +x
#once you've edited /usr/share/initramfs-tools/scripts/local, update-initramfs -u will rebuild for the current kernel,
#and it will automatically build into every new kernel installed

subroot(){ karg=" $(cat<proc/cmdline) " m=${karg#* subroot=}
           [ "$m" = "$karg" ]||subroot=${m%% *}                 #extract subroot from kernel commandline
           [ $subroot ]||return 0                               #no subroot, just proceed in partition root
           while read -r m r m
           do for m in $M x                                     #build list of what's already mounted
              do    [[ $r = $m* ]]&&break                       #exclude subtrees (ie dev/**)
              done||[[ $r = /   ]]||M=$M\ $r                    #exclude /
           done<proc/mounts
           (set -x;mount --bind $subroot mnt)||{ set -x         #mount subroot
                                                 sleep 30          #if not found pause to see error
                                                 return 0;}        #then reincarnate as partition root init
           for m in $M
           do (set -x;mount -n --move $m mnt$m)||return         #move listed mounts to subroot
           done
           set -x
           cd           mnt&&
           pivot_root . mnt&&                                   #subroot becomes root
           umount -l    mnt&&                                   #unmount partition root
          #sleep 15        &&                                   #so far so good?  uncomment for time to look
           exec chroot . init "$@"                              #reincarnate as subroot init
}
subroot "$@"&&exec init "$@"||exec bash                         #land in a shell if moves or pivot fail
gregrwm
quelle
Dies war ein Vergnügen für mich
paultop6
1

Das Booten verschiedener Linux-Versionen ohne Probleme mit der Partitionstabelle ist für verschiedene Zwecke interessant. Eine alternative Lösung für ein freigegebenes Dateisystem besteht darin, Loop-Volumes zu verwenden. Hier sind die wenigen erforderlichen Änderungen erforderlich, vorausgesetzt, Sie haben eine / debian-Loop-Datei / volume im Dateisystem / dev / sdb1 (Ich verwende die aktuelle GNU / Debian-Seite / unstable für Haupt- und Loop-Betriebssysteme).

/etc/grub.d/40_custom: # outside from loop volume
menuentry 'label' --class gnu-linux --class gnu --class os {
    ...
    loopback loop (hd2,msdos1)/debian
    linux   (loop)/boot/vmlinuz root=/dev/sdb1 loop=/debian ro
    initrd  (loop)/boot/initrd
}

In grub als Linux-Befehlszeile definierte Argumente werden von initrd / init auf env gesetzt, also:

ROOT=/dev/sdb1
rootmnt=/root
loop=/debian 

Schleife erlauben, das Volume über "sich selbst" zu mounten, der Standard-Skript-Flow führt eine mount /dev/sdb1 /rootOption aus, indem wir nur / dev / sdb1 als rw neu mounten, wenn es ro war, und dann immer a anhängen mount -o loop /root/debian /root.

/etc/initramfs-tools/scripts/local-bottom/loop: # inside the loop volume
#!/bin/sh

[ "$1" = "prereqs" ] && echo && exit 0

if [ -n "${loop}" ]; then
        if [ "${readonly}" = "y" ]; then
                roflag=-r
                mount -o remount,rw ${ROOT} ${rootmnt}
        else
                roflag=-w
        fi
        mount ${roflag} -o loop ${rootmnt}${loop} ${rootmnt}
fi

Sie müssen auch ein Modul in das Initram vorladen (dann vergessen Sie nicht, update-initramfs auszuführen).

/etc/initramfs-tools/modules: # inside the loop volume
...
loop
ext4

Ich weiß nicht, inwieweit die Verwendung von Schleifen die Leistung beeinflusst oder Ressourcen verschwendet. Ich frage mich, ob das Mounten von ext4 über ext4 die Wahrscheinlichkeit eines Dateisystemfehlers verdoppelt, aber ich vermute, dass eine Optimierung durchgeführt werden könnte. Vielleicht gibt es eine bessere Möglichkeit, Loop zu verwenden, weniger hackisch, wenn es eine gibt, lassen Sie es mich bitte wissen, weil ich nicht gefunden habe.

Alex
quelle
0

Dies ist keine Antwort, aber ich möchte einen Punkt über Ulrichs Antwort und Kommentare klarstellen (ich kann oben keinen Kommentar abgeben).

Die von Ulrich vorgeschlagene Lösung "funktioniert" (noch nicht getestet), aber dann erhalten Sie ein nicht wieder montierbares Dateisystem . Um dieses Problem zu umgehen (meiner Meinung nach hässlich), können Sie die fs vor dem Chrooten als rw bereitstellen ( wie hier vorgeschlagen ). Achten Sie jedoch auf fehlerhafte Init-Skripte. Ich denke, diese Problemumgehung hat mehr Nebenwirkungen (wie kaputte fs, die versuchen, ro erneut zu montieren und fehlschlagen).

Ich verwende Kernel 3.2 mit ext4 und das Mounten eines bereits gemounteten Entwicklers in der Chroot gibt EBUSY immer noch, wie Psusi kommentierte.

Alex
quelle