commit:     bbf60434d536741d561d99faf2965f7f351d2d75
Author:     Peter Levine <plevine457 <AT> gmail <DOT> com>
AuthorDate: Thu Mar 25 02:10:37 2021 +0000
Commit:     Ben Kohler <bkohler <AT> gentoo <DOT> org>
CommitDate: Fri Mar 26 17:44:39 2021 +0000
URL:        https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=bbf60434

sys-boot/os-prober: Fix OS discovery on multiple btrfs subvolumes

Fixes discovery of OSes on multiple subvolumes of the same btrfs
partition, instead of just one that's mounted at root.

Package-Manager: Portage-3.0.17, Repoman-3.0.2
Signed-off-by: Peter Levine <plevine457 <AT> gmail.com>
Signed-off-by: Ben Kohler <bkohler <AT> gentoo.org>

 .../os-prober/files/os-prober-1.78-btrfsfix.patch  | 474 +++++++++++++++++++++
 sys-boot/os-prober/os-prober-1.78.ebuild           |   5 +-
 2 files changed, 478 insertions(+), 1 deletion(-)

diff --git a/sys-boot/os-prober/files/os-prober-1.78-btrfsfix.patch 
b/sys-boot/os-prober/files/os-prober-1.78-btrfsfix.patch
new file mode 100644
index 00000000000..5a74285dc73
--- /dev/null
+++ b/sys-boot/os-prober/files/os-prober-1.78-btrfsfix.patch
@@ -0,0 +1,474 @@
+Bug: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=688336
+
+diff --git a/common.sh b/common.sh
+index c2c5f46..8fb3c5f 100644
+--- a/common.sh
++++ b/common.sh
+@@ -155,6 +155,7 @@ parse_proc_mounts () {
+       done
+ }
+ 
++# add forth parameter to pickup btrfs subvol info
+ parsefstab () {
+       while read -r line; do
+               case "$line" in
+@@ -165,12 +166,22 @@ parsefstab () {
+                               set -f
+                               set -- $line
+                               set +f
+-                              printf '%s %s %s\n' "$1" "$2" "$3"
++                              printf '%s %s %s %s\n' "$1" "$2" "$3" "$4"
+                       ;;
+               esac
+       done
+ }
+ 
++#check_btrfs_mounted $bootsv $bootuuid)
++check_btrfs_mounted () {
++      bootsv="$1"
++      bootuuid="$2"
++      bootdev=$(blkid | grep "$bootuuid" | cut -d ':' -f  1)
++      bindfrom=$(grep " btrfs " /proc/self/mountinfo |
++                 grep " $bootdev " | grep " /$bootsv " | cut -d ' ' -f 5)
++      printf "%s" "$bindfrom"
++}
++
+ unescape_mount () {
+       printf %s "$1" | \
+               sed 's/\\011/   /g; s/\\012/\n/g; s/\\040/ /g; s/\\134/\\/g'
+diff --git a/linux-boot-prober b/linux-boot-prober
+index e32dc84..2a60fa2 100755
+--- a/linux-boot-prober
++++ b/linux-boot-prober
+@@ -5,16 +5,143 @@ set -e
+ 
+ newns "$@"
+ require_tmpdir
++ERR="n"
++
++tmpmnt=/var/lib/os-prober/mount
++if [ ! -d "$tmpmnt" ]; then
++      mkdir "$tmpmnt"
++fi
++
++mounted=
++bootmnt=
++bootsv=
++bootuuid=
+ 
+ grep "^/dev/" /proc/mounts | parse_proc_mounts >"$OS_PROBER_TMP/mounted-map" 
|| true
+ 
+-partition="$1"
++if [ -z "$1" ]; then
++      ERR=y
++elif [ "$1" = btrfs -a -z "$2" ]; then
++      ERR=y
++elif [ "$1" = btrfs -a -z "$3" ]; then
++      ERR=y
++elif [ "$1" = btrfs ]; then
++      type=btrfs
++      echo "$2" | grep -q "^UUID=" || ERR=y
++      echo "$3" | grep -q "^subvol=" || ERR=y
++      export "$2"
++      export "$3"
++      partition=$(blkid | grep "$UUID" | cut -d ':' -f 1 | tr '\n' ' ' | cut 
-d ' ' -f 1)
++      debug "btrfs: partition=$partition, UUID=$UUID, subvol=$subvol"
++else
++      partition="$1"
++      type=other
++fi
+ 
+-if [ -z "$partition" ]; then
++if [ "x$ERR" != xn ]; then
+       echo "usage: linux-boot-prober partition" >&2
++      echo "       linux-boot-prober btrfs UUID=<> subvol=<>" >&2
+       exit 1
+ fi
+ 
++if [ "$type" = btrfs ]; then
++      # handle all of the btrfs stuff here
++      if [ ! -e "/proc/self/mountinfo" ]; then
++              warn "/proc/self/mountinfo does not exist, exiting"
++              umount "$tmpmnt" 2>/dev/null
++              rmdir "$tmpmnt" 2>/dev/null
++              exit 1
++      fi
++      mpoint=$(grep "btrfs" /proc/self/mountinfo | grep " /$subvol " | grep " 
$partition " | cut -d ' ' -f 5)
++      if [ "$mpoint" = "/" ]; then
++              warn "specifying active root not valid, exiting"
++              umount "$tmpmnt" 2>/dev/null
++              rmdir "$tmpmnt" 2>/dev/null
++              exit 1
++      fi
++      if [ "$mpoint" = "$tmpmnt" ]; then
++              warn "btrfs subvol=$subvool, UUID=$UUID, already mounted on 
$tmpmnt **ERROR**"
++              umount "$tmpmnt" 2>/dev/null
++              rmdir "$tmpmnt" 2>/dev/null
++              exit 1
++      fi
++      if [ -z "$mpoint" ]; then
++              # mount the btrfs root
++              if ! mount -o subvol=$subvol -t btrfs -U $UUID "$tmpmnt" 
2>/dev/null; then
++                      warn "error mounting btrfs subvol=$subvol UUID=$UUID"
++                      umount "$tmpmnt/boot" 2>/dev/null
++                      umount "$tmpmnt" 2>/dev/null
++                      rmdir "$tmpmnt" 2>/dev/null
++                      exit 1
++              fi
++      else
++              # bind-mount
++              if ! mount -o bind "$mpoint" "$tmpmnt" 2>/dev/null; then
++                      warn "error mounting btrfs bindfrom=$mpoint 
subvol=$subvol UUID=$UUID"
++                      umount "$tmpmnt/boot" 2>/dev/null
++                      umount "$tmpmnt" 2>/dev/null
++                      rmdir "$tmpmnt" 2>/dev/null
++                      exit 1
++              fi
++      fi
++      debug "mounted btrfs $partition, subvol=$subvol on $tmpmnt"
++      if [ ! -e "$tmpmnt/etc/fstab" ]; then
++              warn "btrfs subvol=$subvol not root"
++              umount "$tmpmnt" 2>/dev/null
++              rmdir "$tmpmnt" 2>/dev/null
++              exit 1
++      fi
++      bootmnt=$(parsefstab < "$tmpmnt/etc/fstab" | grep " /boot ") || true
++      if [ -z "$bootmnt" ]; then
++              # /boot is part of the root
++              bootpart="$partition"
++              bootsv="$subvol"
++      elif echo "$bootmnt" | cut -d ' ' -f 3 | grep -q "btrfs"; then
++              # separate btrfs /boot subvolume
++              bootsv=$(echo "$bootmnt" | cut -d ' ' -f 4 | grep "^subvol=" | 
sed "s/subvol=//" )
++              bootuuid=$(echo "$bootmnt" | cut -d ' ' -f 1 | grep "^UUID=" | 
sed "s/UUID=//" )
++              debug "mounting btrfs $tmpmnt/boot UUID=$bootuuid 
subvol=$bootsv"
++              bindfrom=$(check_btrfs_mounted $bootsv $bootuuid)
++              if [ -n "$bindfrom" ]; then
++                      # already mounted some place
++                      if ! mount -o bind $bindfrom "$tmpmnt/boot" 
2>/dev/null; then
++                              warn "error bind mounting btrfs boot 
subvol=$bootsv, from=$bindfrom"
++                              umount "$tmpmnt/boot" 2>/dev/null
++                              umount "$tmpmnt" 2>/dev/null
++                              rmdir "$tmpmnt" 2>/dev/null
++                              exit 1
++                      fi
++              elif ! mount -o subvol=$bootsv -t btrfs -U $bootuuid 
"$tmpmnt/boot" 2>/dev/null; then
++                      warn "error mounting btrfs boot partition 
subvol=$bootsv, UUID=$bootuuid"
++                      umount "$tmpmnt/boot" 2>/dev/null
++                      umount "$tmpmnt" 2>/dev/null
++                      rmdir "$tmpmnt" 2>/dev/null
++                      exit 1
++              fi
++              bootpart=$(grep " btrfs " /proc/self/mountinfo | grep " 
/$bootsv " | cut -d ' ' -f 10)
++      else
++              # non-btrfs partition or logical volume
++              linux_mount_boot $partition $tmpmnt
++              bootpart="${mountboot%% *}"
++              bootsv=
++      fi
++
++      test="/usr/lib/linux-boot-probes/mounted/40grub2"
++      if [ -f $test ] && [ -x $test ]; then
++              debug "running $test $partition $bootpart $tmpmnt $type $subvol 
$bootsv"
++              if $test "$partition" "$bootpart" "$tmpmnt" "$type" "$subvol" 
"$bootsv"; then
++                      debug "$test succeeded"
++              fi
++      fi
++      umount "$tmpmnt/boot" 2>/dev/null || true
++      if ! umount "$tmpmnt" 2>/dev/null; then
++              warn "problem umount $tmpmnt"
++      fi
++      rmdir "$tmpmnt" 2>/dev/null || true
++
++      exit 0
++fi
++
+ if ! mapped="$(mapdevfs "$partition")"; then
+       log "Device '$partition' does not exist; skipping"
+       continue
+@@ -22,8 +149,8 @@ fi
+ 
+ if ! grep -q "^$mapped " "$OS_PROBER_TMP/mounted-map"; then
+       for test in /usr/lib/linux-boot-probes/*; do
+-              debug "running $test"
+               if [ -x $test ] && [ -f $test ]; then
++                      debug "running $test"
+                       if $test "$partition"; then
+                               debug "linux detected by $test"
+                               break
+diff --git a/linux-boot-probes/mounted/common/40grub2 
b/linux-boot-probes/mounted/common/40grub2
+index 885614e..db5cbfd 100755
+--- a/linux-boot-probes/mounted/common/40grub2
++++ b/linux-boot-probes/mounted/common/40grub2
+@@ -2,17 +2,30 @@
+ . /usr/share/os-prober/common.sh
+ set -e
+ 
++# add support for btrfs with no separate /boot
++# that is, rootsv = bootsv
+ partition="$1"
+ bootpart="$2"
+ mpoint="$3"
+ type="$4"
++rootsv="$5"
++bootsv="$6"
+ 
+ found_item=0
+ 
+ entry_result () {
++      if [ "x$type" = "xbtrfs" -a "$partition" = "$bootpart" ]; then
++              # trim off the leading subvol
++              kernelfile=$(echo "$kernel" | cut -d '/' -f 2- | cut -d '/' -f 
2-)
++              if [ "x$rootsv" != "x$bootsv" ]; then
++                 kernelfile="/boot/$kernelfile"
++              fi
++      else
++              kernelfile=$kernel
++      fi
+       if [ "$ignore_item" = 0 ] && \
+          [ -n "$kernel" ] && \
+-         [ -e "$mpoint/$kernel" ]; then
++         [ -e "$mpoint/$kernelfile" ]; then
+               result "$rootpart:$bootpart:$title:$kernel:$initrd:$parameters"
+               found_item=1
+       fi
+diff --git a/os-prober b/os-prober
+index 8852887..482c3c2 100755
+--- a/os-prober
++++ b/os-prober
+@@ -76,9 +76,12 @@ partitions () {
+ 
+       # Also detect OSes on LVM volumes (assumes LVM is active)
+       if type lvs >/dev/null 2>&1; then
+-              echo "$(LVM_SUPPRESS_FD_WARNINGS=1 log_output lvs --noheadings 
--separator : -o vg_name,lv_name |
++              echo "$(LVM_SUPPRESS_FD_WARNINGS=1 log_output lvs --noheadings 
--separator : -o vg_name,lv_name 2>/dev/null |
+                       sed 
"s|-|--|g;s|^[[:space:]]*\(.*\):\(.*\)$|/dev/mapper/\1-\2|")"
+       fi
++
++      # now lets make sure we got all of the btrfs partitions and disks
++      blkid | grep 'TYPE="btrfs"' | cut -d ':' -f 1
+ }
+ 
+ parse_proc_swaps () {
+@@ -136,6 +139,8 @@ if [ -f /proc/mdstat ] ; then
+       grep "^md" /proc/mdstat | cut -d: -f2- | parse_proc_mdstat 
>"$OS_PROBER_TMP/raided-map" || true
+ fi
+ 
++: >"$OS_PROBER_TMP/btrfs-vols"
++
+ for partition in $(partitions); do
+       if ! mapped="$(mapdevfs "$partition")"; then
+               log "Device '$partition' does not exist; skipping"
+@@ -154,7 +159,26 @@ for partition in $(partitions); do
+               continue
+       fi
+ 
+-      if ! grep -q "^$mapped " "$OS_PROBER_TMP/mounted-map" ; then
++      # do btrfs processing here; both mounted and unmounted will
++      # be handled by 50mounted-tests so we can do a subvol only once.
++      type=$(blkid -o value -s TYPE $mapped || true)
++      if [ "$type" = btrfs ]; then
++              uuid=$(blkid -o value -s UUID $mapped)
++              if grep -q "^$uuid" "$OS_PROBER_TMP/btrfs-vols" ; then
++                      continue
++              fi
++              debug "btrfs volume uuid=$uuid partition=$partition"
++              echo "$uuid" >>"$OS_PROBER_TMP/btrfs-vols"
++              test="/usr/lib/os-probes/50mounted-tests"
++              if [ -f "$test" ] && [ -x "$test" ]; then
++                      debug "running $test on btrfs $partition"
++                      if "$test" btrfs "$uuid" "$partition"; then
++                              debug "os detected by $test"
++                              continue
++                      fi
++              fi
++
++      elif ! grep -q "^$mapped " "$OS_PROBER_TMP/mounted-map" ; then
+               for test in /usr/lib/os-probes/*; do
+                       if [ -f "$test" ] && [ -x "$test" ]; then
+                               debug "running $test on $partition"
+diff --git a/os-probes/common/50mounted-tests 
b/os-probes/common/50mounted-tests
+index 2951ef9..e33eb82 100755
+--- a/os-probes/common/50mounted-tests
++++ b/os-probes/common/50mounted-tests
+@@ -19,19 +19,31 @@ do_unmount() {
+       rmdir "$tmpmnt" || true
+ }
+ 
+-types="$(fs_type "$partition")"
++if [ "x$1" = xbtrfs ]; then
++      types=btrfs
++      if [ -z "$2" -o -z "$3" ]; then
++              debug "missing btrfs parameters, exiting"
++              exit 1
++      fi
++      UUID="$2"
++      BTRFSDEV="$3"
++else
++      partition="$1"
++      types="$(fs_type "$partition")" || types=NOT-DETECTED
++fi
++
+ if [ "$types" = NOT-DETECTED ]; then
+       debug "$1 type not recognised; skipping"
+-      exit 0
++      exit 1
+ elif [ "$types" = swap ]; then
+       debug "$1 is a swap partition; skipping"
+-      exit 0
++      exit 1
+ elif [ "$types" = crypto_LUKS ]; then
+       debug "$1 is a LUKS partition; skipping"
+-      exit 0
++      exit 1
+ elif [ "$types" = LVM2_member ]; then
+       debug "$1 is an LVM member; skipping"
+-      exit 0
++      exit 1
+ elif [ "$types" = ntfs ]; then
+       if type ntfs-3g >/dev/null 2>&1; then
+               types='ntfs-3g ntfs'
+@@ -40,7 +52,7 @@ elif [ -z "$types" ]; then
+       if type cryptsetup >/dev/null 2>&1 && \
+          cryptsetup luksDump "$partition" >/dev/null 2>&1; then
+               debug "$1 is a LUKS partition; skipping"
+-              exit 0
++              exit 1
+       fi
+       for type in $(grep -v nodev /proc/filesystems); do
+               # hfsplus filesystems are mountable as hfs. Try hfs last so
+@@ -63,6 +75,108 @@ if [ ! -d "$tmpmnt" ]; then
+ fi
+ 
+ mounted=
++
++# all btrfs processing here.  Handle both unmounted and
++# mounted subvolumes.
++if [ "$types" = btrfs ]; then
++      partition="$BTRFSDEV"
++      debug "begin btrfs processing for $UUID"
++      # note that the btrfs volume must not be mounted ro
++      if mount -t btrfs -U "$UUID" "$tmpmnt"  2>/dev/null; then
++              debug "btrfs volume $UUID mounted"
++      else
++              warn "cannot mount btrfs volume $UUID, exiting"
++              rmdir "$tmpmnt" || true
++              exit 1
++      fi
++      # besides regular subvols, get ro and snapshot so thet can be excluded
++        subvols=$(btrfs subvolume list "$tmpmnt" | cut -d ' ' -f 9)
++        rosubvols=$(btrfs subvolume list -r "$tmpmnt" | cut -d ' ' -f 9)
++        sssubvols=$(btrfs subvolume list -s "$tmpmnt" | cut -d ' ' -f 14)
++        if ! umount "$tmpmnt"; then
++            warn "failed to umount btrfs volume on $tmpmnt"
++            rmdir "$tmpmnt" || true
++            exit 1
++        fi
++
++      found=
++      mounted=
++
++      mpoint="$(grep btrfs /proc/self/mountinfo | grep "$partition " | cut -d 
' ' -f 5)"
++      if [ -n "$mpoint" -a "x$mpoint" = "x/" ]; then
++              debug "This is the root for the running system" #running system 
must be done elsewhere
++      else
++          #partition was not root of running system, so lets look for 
bootable subvols
++          if [ -n "$mpoint" ] ; then
++              mounted=1  #partition was already mounted,so lets not unmount 
it when done
++          else
++              # again, do not mount btrfs ro
++              mount -t btrfs -U "$UUID" "$tmpmnt"
++              mpoint="$tmpmnt"
++          fi
++
++          test="/usr/libexec/os-probes/mounted/90linux-distro"
++          if [ -f "$test" ] && [ -x "$test" ]; then
++              debug "running subtest $test"
++              if "$test" "$partition" "$mpoint" btrfs "UUID=$UUID"; then
++                  debug "os found by subtest $test on $partition"
++                  found=1
++              fi
++          fi
++          if [ -z "$mounted" ]; then
++              if ! umount "$tmpmnt"; then
++                  warn "failed to umount $tmpmnt"
++              fi
++          fi
++      fi
++
++      if [ -z "$subvols" ]; then
++              debug "no subvols found on btrfs volume $UUID"
++      else
++              found=
++                for subvol in $subvols; do
++                      debug "begin btrfs processing for $UUID subvol=$subvol"
++                      if echo "$rosubvols" | grep -q -x "$subvol"; then
++                              continue
++                      fi
++                      if echo "$sssubvols" | grep -q -x "$subvol"; then
++                              continue
++                      fi
++                      mounted=
++                      mpoint="$(grep btrfs /proc/self/mountinfo | grep 
"$partition " | grep "/$subvol " | cut -d ' ' -f 5)"
++                      if [ -n "$mpoint" ]; then
++                              if [ "x$mpoint" = "x/" ]; then
++                                      continue # this is the root for the 
running system
++                              fi
++                              mounted=1
++                      else
++                              # again, do not mount btrfs ro
++                              mount -t btrfs -o subvol="$subvol" -U "$UUID" 
"$tmpmnt"
++                              mpoint="$tmpmnt"
++                      fi
++                      test="/usr/lib/os-probes/mounted/90linux-distro"
++                      if [ -f "$test" ] && [ -x "$test" ]; then
++                              debug "running subtest $test"
++                              if "$test" "$partition" "$mpoint" btrfs 
"UUID=$UUID" "subvol=$subvol"; then
++                                      debug "os found by subtest $test on 
subvol $subvol"
++                                      found=1
++                              fi
++                      fi
++                      if [ -z "$mounted" ]; then
++                              if ! umount "$tmpmnt"; then
++                                  warn "failed to umount $tmpmnt"
++                              fi
++                      fi
++              done
++      fi
++      rmdir "$tmpmnt" || true
++      if [ "$found" ]; then
++              exit 0
++      else
++              exit 1
++      fi
++fi
++
+ if type grub-mount >/dev/null 2>&1 && \
+    type grub-probe >/dev/null 2>&1 && \
+    grub-mount "$partition" "$tmpmnt" 2>/dev/null; then
+diff --git a/os-probes/mounted/common/90linux-distro 
b/os-probes/mounted/common/90linux-distro
+index badfbb1..9bc5154 100755
+--- a/os-probes/mounted/common/90linux-distro
++++ b/os-probes/mounted/common/90linux-distro
+@@ -7,6 +7,8 @@ set -e
+ partition="$1"
+ dir="$2"
+ type="$3"
++uuid="$4"
++subvol="$5"
+ 
+ # This test is inaccurate, but given separate / and /boot partitions and the
+ # fact that only some architectures have ld-linux.so, I can't see anything
+@@ -143,7 +145,11 @@ if (ls "$dir"/lib*/ld*.so* && [ -d "$dir/boot" ] || ls 
"$dir"/usr/lib*/ld*.so*)
+       fi
+       
+         label="$(count_next_label "$short")"
+-      result "$partition:$long:$label:linux"
++      if [ "x$type" = "xbtrfs" -a "x$uuid" != "x" -a "x$subvol" != "x" ]; then
++              result "$partition:$long:$label:linux:$type:$uuid:$subvol"
++      else
++              result "$partition:$long:$label:linux"
++      fi
+       exit 0
+ else
+       exit 1

diff --git a/sys-boot/os-prober/os-prober-1.78.ebuild 
b/sys-boot/os-prober/os-prober-1.78.ebuild
index 0ed65355dd7..5c4721ad5c0 100644
--- a/sys-boot/os-prober/os-prober-1.78.ebuild
+++ b/sys-boot/os-prober/os-prober-1.78.ebuild
@@ -20,7 +20,10 @@ DEPEND=""
 # bug 594250
 QA_MULTILIB_PATHS="usr/lib/os-prober/.*"
 
-PATCHES=( "${FILESDIR}"/${PN}-1.76-exherbo.patch )
+PATCHES=(
+       "${FILESDIR}"/${PN}-1.76-exherbo.patch
+       "${FILESDIR}"/${PN}-1.78-btrfsfix.patch
+)
 
 DOC_CONTENTS="
        If you intend for os-prober to detect versions of Windows installed on

Reply via email to