Index: os-prober/common.sh =================================================================== --- os-prober.orig/common.sh +++ os-prober/common.sh @@ -150,6 +150,7 @@ parse_proc_mounts () { done } +# add forth parameter to pickup btrfs subvol info parsefstab () { while read -r line; do case "$line" in @@ -160,12 +161,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' Index: os-prober/linux-boot-prober =================================================================== --- os-prober.orig/linux-boot-prober +++ os-prober/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 Index: os-prober/linux-boot-probes/mounted/common/40grub2 =================================================================== --- os-prober.orig/linux-boot-probes/mounted/common/40grub2 +++ os-prober/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 Index: os-prober/os-prober =================================================================== --- os-prober.orig/os-prober +++ os-prober/os-prober @@ -71,9 +71,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 () { @@ -131,6 +134,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" @@ -149,7 +154,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" Index: os-prober/os-probes/common/50mounted-tests =================================================================== --- os-prober.orig/os-probes/common/50mounted-tests +++ os-prober/os-probes/common/50mounted-tests @@ -1,20 +1,36 @@ #!/bin/sh # Sub-tests that require a mounted partition. set -e -partition="$1" . /usr/share/os-prober/common.sh -types="$(fs_type "$partition")" || types=NOT-DETECTED +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 [ "x$types" = "x" ]; then + exit 1 +elif [ "$types" = LVM2_member ]; then + debug "skipping LVM2 Volume Group on $partition" + 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" = ntfs ]; then if type ntfs-3g >/dev/null 2>&1; then types='ntfs-3g ntfs' @@ -23,7 +39,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 @@ -46,6 +62,76 @@ 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 9) + if ! umount "$tmpmnt"; then + warn "failed to umount btrfs volume on $tmpmnt" + rmdir "$tmpmnt" || true + exit 1 + fi + if [ -z "$subvols" ]; then + debug "no subvols found on btrfs volume $UUID" + exit 1 + fi + 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 + 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 Index: os-prober/os-probes/mounted/common/90linux-distro =================================================================== --- os-prober.orig/os-probes/mounted/common/90linux-distro +++ os-prober/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 @@ -135,7 +137,11 @@ if [ -L "$dir"/lib ] || (ls "$dir"/lib*/ 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 Index: os-prober/README.btrfs =================================================================== --- /dev/null +++ os-prober/README.btrfs @@ -0,0 +1,15 @@ +BTRFS is a new filesystem which combines the filesystem with logical volume +management (subvolumes). For further information, see: + https://btrfs.wiki.kernel.org/index.php/Main_Page + https://btrfs.wiki.kernel.org/index.php/FAQ + +In order to support BTRFS, a number of changes were necessary to os-prober, +os-probes/common/50mounted-tests, os-probes/mounted/common/90linux-distro, +linux-boot-prober, and linux-boot-probes/common/50mounted-tests. + +The biggest impact will be to grub2 where there is additional information +output by os-prober and where, if a BTRFS subvolume is being used for root, +the parameters for linux-boot-prober have changed. + +Sun 30 Dec 2012 11:49:52 AM EST Gene Czarcinski +