1 Fixes detection of multiple linux installations on different subvolumes of the
2 same partition. This patch is a combination of https://src.fedoraproject.org/rpms/os-prober/blob/a27e5121193e2222ada672db3521a7d9de70991b/f/os-prober-btrfsfix.patch and https://build.opensuse.org/package/view_file/openSUSE:Factory/os-prober/Improve-btrfs-handling-on-os-probing-for-grub2.patch?rev=56 .
3
4 Bug: https://bugs.gentoo.org/790434
5 https://bugs.gentoo.org/817905
6 https://bugs.debian.org/688336
7 https://bugzilla.redhat.com/888341
8
9 --- a/common.sh
10 +++ b/common.sh
11 @@ -155,6 +155,7 @@ parse_proc_mounts () {
12 done
13 }
14
15 +# add forth parameter to pickup btrfs subvol info
16 parsefstab () {
17 while read -r line; do
18 case "$line" in
19 @@ -165,12 +166,22 @@ parsefstab () {
20 set -f
21 set -- $line
22 set +f
23 - printf '%s %s %s\n' "$1" "$2" "$3"
24 + printf '%s %s %s %s\n' "$1" "$2" "$3" "$4"
25 ;;
26 esac
27 done
28 }
29
30 +#check_btrfs_mounted $bootsv $bootuuid)
31 +check_btrfs_mounted () {
32 + bootsv="$1"
33 + bootuuid="$2"
34 + bootdev=$(blkid | grep "$bootuuid" | cut -d ':' -f 1)
35 + bindfrom=$(grep " btrfs " /proc/self/mountinfo |
36 + grep " $bootdev " | grep " /$bootsv " | cut -d ' ' -f 5)
37 + printf "%s" "$bindfrom"
38 +}
39 +
40 unescape_mount () {
41 printf %s "$1" | \
42 sed 's/\\011/ /g; s/\\012/\n/g; s/\\040/ /g; s/\\134/\\/g'
43 --- a/linux-boot-prober
44 +++ b/linux-boot-prober
45 @@ -5,16 +5,143 @@ set -e
46
47 newns "$@"
48 require_tmpdir
49 +ERR="n"
50 +
51 +tmpmnt=/var/lib/os-prober/mount
52 +if [ ! -d "$tmpmnt" ]; then
53 + mkdir "$tmpmnt"
54 +fi
55 +
56 +mounted=
57 +bootmnt=
58 +bootsv=
59 +bootuuid=
60
61 grep "^/dev/" /proc/mounts | parse_proc_mounts >"$OS_PROBER_TMP/mounted-map" || true
62
63 -partition="$1"
64 +if [ -z "$1" ]; then
65 + ERR=y
66 +elif [ "$1" = btrfs -a -z "$2" ]; then
67 + ERR=y
68 +elif [ "$1" = btrfs -a -z "$3" ]; then
69 + ERR=y
70 +elif [ "$1" = btrfs ]; then
71 + type=btrfs
72 + echo "$2" | grep -q "^UUID=" || ERR=y
73 + echo "$3" | grep -q "^subvol=" || ERR=y
74 + export "$2"
75 + export "$3"
76 + partition=$(blkid | grep "$UUID" | cut -d ':' -f 1 | tr '\n' ' ' | cut -d ' ' -f 1)
77 + debug "btrfs: partition=$partition, UUID=$UUID, subvol=$subvol"
78 +else
79 + partition="$1"
80 + type=other
81 +fi
82
83 -if [ -z "$partition" ]; then
84 +if [ "x$ERR" != xn ]; then
85 echo "usage: linux-boot-prober partition" >&2
86 + echo " linux-boot-prober btrfs UUID=<> subvol=<>" >&2
87 exit 1
88 fi
89
90 +if [ "$type" = btrfs ]; then
91 + # handle all of the btrfs stuff here
92 + if [ ! -e "/proc/self/mountinfo" ]; then
93 + warn "/proc/self/mountinfo does not exist, exiting"
94 + umount "$tmpmnt" 2>/dev/null
95 + rmdir "$tmpmnt" 2>/dev/null
96 + exit 1
97 + fi
98 + mpoint=$(grep "btrfs" /proc/self/mountinfo | grep " /$subvol " | grep " $partition " | cut -d ' ' -f 5)
99 + if [ "$mpoint" = "/" ]; then
100 + warn "specifying active root not valid, exiting"
101 + umount "$tmpmnt" 2>/dev/null
102 + rmdir "$tmpmnt" 2>/dev/null
103 + exit 1
104 + fi
105 + if [ "$mpoint" = "$tmpmnt" ]; then
106 + warn "btrfs subvol=$subvool, UUID=$UUID, already mounted on $tmpmnt **ERROR**"
107 + umount "$tmpmnt" 2>/dev/null
108 + rmdir "$tmpmnt" 2>/dev/null
109 + exit 1
110 + fi
111 + if [ -z "$mpoint" ]; then
112 + # mount the btrfs root
113 + if ! mount -o subvol=$subvol -t btrfs -U $UUID "$tmpmnt" 2>/dev/null; then
114 + warn "error mounting btrfs subvol=$subvol UUID=$UUID"
115 + umount "$tmpmnt/boot" 2>/dev/null
116 + umount "$tmpmnt" 2>/dev/null
117 + rmdir "$tmpmnt" 2>/dev/null
118 + exit 1
119 + fi
120 + else
121 + # bind-mount
122 + if ! mount -o bind "$mpoint" "$tmpmnt" 2>/dev/null; then
123 + warn "error mounting btrfs bindfrom=$mpoint subvol=$subvol UUID=$UUID"
124 + umount "$tmpmnt/boot" 2>/dev/null
125 + umount "$tmpmnt" 2>/dev/null
126 + rmdir "$tmpmnt" 2>/dev/null
127 + exit 1
128 + fi
129 + fi
130 + debug "mounted btrfs $partition, subvol=$subvol on $tmpmnt"
131 + if [ ! -e "$tmpmnt/etc/fstab" ]; then
132 + warn "btrfs subvol=$subvol not root"
133 + umount "$tmpmnt" 2>/dev/null
134 + rmdir "$tmpmnt" 2>/dev/null
135 + exit 1
136 + fi
137 + bootmnt=$(parsefstab < "$tmpmnt/etc/fstab" | grep " /boot ") || true
138 + if [ -z "$bootmnt" ]; then
139 + # /boot is part of the root
140 + bootpart="$partition"
141 + bootsv="$subvol"
142 + elif echo "$bootmnt" | cut -d ' ' -f 3 | grep -q "btrfs"; then
143 + # separate btrfs /boot subvolume
144 + bootsv=$(echo "$bootmnt" | cut -d ' ' -f 4 | grep "^subvol=" | sed "s/subvol=//" )
145 + bootuuid=$(echo "$bootmnt" | cut -d ' ' -f 1 | grep "^UUID=" | sed "s/UUID=//" )
146 + debug "mounting btrfs $tmpmnt/boot UUID=$bootuuid subvol=$bootsv"
147 + bindfrom=$(check_btrfs_mounted $bootsv $bootuuid)
148 + if [ -n "$bindfrom" ]; then
149 + # already mounted some place
150 + if ! mount -o bind $bindfrom "$tmpmnt/boot" 2>/dev/null; then
151 + warn "error bind mounting btrfs boot subvol=$bootsv, from=$bindfrom"
152 + umount "$tmpmnt/boot" 2>/dev/null
153 + umount "$tmpmnt" 2>/dev/null
154 + rmdir "$tmpmnt" 2>/dev/null
155 + exit 1
156 + fi
157 + elif ! mount -o subvol=$bootsv -t btrfs -U $bootuuid "$tmpmnt/boot" 2>/dev/null; then
158 + warn "error mounting btrfs boot partition subvol=$bootsv, UUID=$bootuuid"
159 + umount "$tmpmnt/boot" 2>/dev/null
160 + umount "$tmpmnt" 2>/dev/null
161 + rmdir "$tmpmnt" 2>/dev/null
162 + exit 1
163 + fi
164 + bootpart=$(grep " btrfs " /proc/self/mountinfo | grep " /$bootsv " | cut -d ' ' -f 10)
165 + else
166 + # non-btrfs partition or logical volume
167 + linux_mount_boot $partition $tmpmnt
168 + bootpart="${mountboot%% *}"
169 + bootsv=
170 + fi
171 +
172 + test="/usr/lib/linux-boot-probes/mounted/40grub2"
173 + if [ -f $test ] && [ -x $test ]; then
174 + debug "running $test $partition $bootpart $tmpmnt $type $subvol $bootsv"
175 + if $test "$partition" "$bootpart" "$tmpmnt" "$type" "$subvol" "$bootsv"; then
176 + debug "$test succeeded"
177 + fi
178 + fi
179 + umount "$tmpmnt/boot" 2>/dev/null || true
180 + if ! umount "$tmpmnt" 2>/dev/null; then
181 + warn "problem umount $tmpmnt"
182 + fi
183 + rmdir "$tmpmnt" 2>/dev/null || true
184 +
185 + exit 0
186 +fi
187 +
188 if ! mapped="$(mapdevfs "$partition")"; then
189 log "Device '$partition' does not exist; skipping"
190 continue
191 @@ -22,8 +149,8 @@ fi
192
193 if ! grep -q "^$mapped " "$OS_PROBER_TMP/mounted-map"; then
194 for test in /usr/lib/linux-boot-probes/*; do
195 - debug "running $test"
196 if [ -x $test ] && [ -f $test ]; then
197 + debug "running $test"
198 if $test "$partition"; then
199 debug "linux detected by $test"
200 break
201 --- a/linux-boot-probes/mounted/common/40grub2
202 +++ b/linux-boot-probes/mounted/common/40grub2
203 @@ -2,17 +2,42 @@
204 . /usr/share/os-prober/common.sh
205 set -e
206
207 +# add support for btrfs with no separate /boot
208 +# that is, rootsv = bootsv
209 partition="$1"
210 bootpart="$2"
211 mpoint="$3"
212 type="$4"
213 +rootsv="$5"
214 +bootsv="$6"
215
216 found_item=0
217
218 entry_result () {
219 + if [ "x$type" = "xbtrfs" ]; then
220 + bsv=${bootsv:+/}${bootsv}
221 + # if path is not relative to subvolume make it relative
222 + kernel=${kernel#${bsv}}
223 + kernelfile=$kernel
224 + initrd=${initrd#${bsv}}
225 + if [ "x$GRUB_FS" != "xbtrfs" ]; then
226 + # absolute path needed: prepend subvolume if $kernel isn't empty
227 + kernel=${kernel:+${bsv}}${kernel}
228 + # handle multiple initrd paths
229 + local initrd_path=
230 + for path in ${initrd}; do
231 + initrd_path+="${bsv}${path} "
232 + done
233 + initrd="${initrd_path% }"
234 + fi
235 + # assumed: rootsv != bootsv if rootsv isn't ancestor of bootsv
236 + [ "$partition" != "$bootpart" -o "$rootsv" != "$bootsv" ] && kernelfile="/boot${kernelfile}"
237 + else
238 + kernelfile=$kernel
239 + fi
240 if [ "$ignore_item" = 0 ] && \
241 [ -n "$kernel" ] && \
242 - [ -e "$mpoint/$kernel" ]; then
243 + [ -e "$mpoint/$kernelfile" ]; then
244 result "$rootpart:$bootpart:$title:$kernel:$initrd:$parameters"
245 found_item=1
246 fi
247 @@ -64,7 +88,7 @@ parse_grub_menu () {
248 ignore_item=1
249 fi
250 ;;
251 - linux)
252 + linux|linuxefi|linux16)
253 # Hack alert: sed off any (hdn,n) but
254 # assume the kernel is on the same
255 # partition.
256 @@ -73,18 +98,18 @@ parse_grub_menu () {
257 parameters="$@"
258 # Systems with a separate /boot will not have
259 # the path to the kernel in grub.cfg.
260 - if [ "$partition" != "$bootpart" ]; then
261 + if [ "$partition" != "$bootpart" -a "$type" != "btrfs" ]; then
262 kernel="/boot$kernel"
263 fi
264 ;;
265 - initrd)
266 + initrd|initrdefi|initrd16)
267 shift
268 initrd=""
269 for initrd_path in "$@"; do
270 # sed hack, as above
271 initrd_path="$(echo "$initrd_path" | sed 's/(.*)//')"
272 # Initrd same.
273 - if [ "$partition" != "$bootpart" ]; then
274 + if [ "$partition" != "$bootpart" -a "$type" != "btrfs" ]; then
275 initrd_path="/boot$initrd_path"
276 fi
277 if [ -z "$initrd" ]; then
278 --- a/os-prober
279 +++ b/os-prober
280 @@ -76,9 +76,12 @@ partitions () {
281
282 # Also detect OSes on LVM volumes (assumes LVM is active)
283 if type lvs >/dev/null 2>&1; then
284 - echo "$(LVM_SUPPRESS_FD_WARNINGS=1 log_output lvs --noheadings --separator : -o vg_name,lv_name |
285 + echo "$(LVM_SUPPRESS_FD_WARNINGS=1 log_output lvs --noheadings --separator : -o vg_name,lv_name 2>/dev/null |
286 sed "s|-|--|g;s|^[[:space:]]*\(.*\):\(.*\)$|/dev/mapper/\1-\2|")"
287 fi
288 +
289 + # now lets make sure we got all of the btrfs partitions and disks
290 + blkid | grep 'TYPE="btrfs"' | cut -d ':' -f 1
291 }
292
293 parse_proc_swaps () {
294 @@ -136,6 +139,8 @@ if [ -f /proc/mdstat ] ; then
295 grep "^md" /proc/mdstat | cut -d: -f2- | parse_proc_mdstat >"$OS_PROBER_TMP/raided-map" || true
296 fi
297
298 +: >"$OS_PROBER_TMP/btrfs-vols"
299 +
300 for partition in $(partitions); do
301 if ! mapped="$(mapdevfs "$partition")"; then
302 log "Device '$partition' does not exist; skipping"
303 @@ -154,7 +159,26 @@ for partition in $(partitions); do
304 continue
305 fi
306
307 - if ! grep -q "^$mapped " "$OS_PROBER_TMP/mounted-map" ; then
308 + # do btrfs processing here; both mounted and unmounted will
309 + # be handled by 50mounted-tests so we can do a subvol only once.
310 + type=$(blkid -o value -s TYPE $mapped || true)
311 + if [ "$type" = btrfs ]; then
312 + uuid=$(blkid -o value -s UUID $mapped)
313 + if grep -q "^$uuid" "$OS_PROBER_TMP/btrfs-vols" ; then
314 + continue
315 + fi
316 + debug "btrfs volume uuid=$uuid partition=$partition"
317 + echo "$uuid" >>"$OS_PROBER_TMP/btrfs-vols"
318 + test="/usr/lib/os-probes/50mounted-tests"
319 + if [ -f "$test" ] && [ -x "$test" ]; then
320 + debug "running $test on btrfs $partition"
321 + if "$test" btrfs "$uuid" "$partition"; then
322 + debug "os detected by $test"
323 + continue
324 + fi
325 + fi
326 +
327 + elif ! grep -q "^$mapped " "$OS_PROBER_TMP/mounted-map" ; then
328 for test in /usr/lib/os-probes/*; do
329 if [ -f "$test" ] && [ -x "$test" ]; then
330 debug "running $test on $partition"
331 --- a/os-probes/common/50mounted-tests
332 +++ b/os-probes/common/50mounted-tests
333 @@ -14,19 +14,31 @@ do_unmount() {
334 rmdir "$tmpmnt" || true
335 }
336
337 -types="$(fs_type "$partition")"
338 +if [ "x$1" = xbtrfs ]; then
339 + types=btrfs
340 + if [ -z "$2" -o -z "$3" ]; then
341 + debug "missing btrfs parameters, exiting"
342 + exit 1
343 + fi
344 + UUID="$2"
345 + BTRFSDEV="$3"
346 +else
347 + partition="$1"
348 + types="$(fs_type "$partition")" || types=NOT-DETECTED
349 +fi
350 +
351 if [ "$types" = NOT-DETECTED ]; then
352 debug "$1 type not recognised; skipping"
353 - exit 0
354 + exit 1
355 elif [ "$types" = swap ]; then
356 debug "$1 is a swap partition; skipping"
357 - exit 0
358 + exit 1
359 elif [ "$types" = crypto_LUKS ]; then
360 debug "$1 is a LUKS partition; skipping"
361 - exit 0
362 + exit 1
363 elif [ "$types" = LVM2_member ]; then
364 debug "$1 is an LVM member; skipping"
365 - exit 0
366 + exit 1
367 elif [ "$types" = ntfs ]; then
368 if type ntfs-3g >/dev/null 2>&1; then
369 types='ntfs-3g ntfs'
370 @@ -35,7 +47,7 @@ elif [ -z "$types" ]; then
371 if type cryptsetup >/dev/null 2>&1 && \
372 cryptsetup luksDump "$partition" >/dev/null 2>&1; then
373 debug "$1 is a LUKS partition; skipping"
374 - exit 0
375 + exit 1
376 fi
377 for type in $(grep -v nodev /proc/filesystems); do
378 # hfsplus filesystems are mountable as hfs. Try hfs last so
379 @@ -58,6 +70,108 @@ if [ ! -d "$tmpmnt" ]; then
380 fi
381
382 mounted=
383 +
384 +# all btrfs processing here. Handle both unmounted and
385 +# mounted subvolumes.
386 +if [ "$types" = btrfs ]; then
387 + partition="$BTRFSDEV"
388 + debug "begin btrfs processing for $UUID"
389 + # note that the btrfs volume must not be mounted ro
390 + if mount -t btrfs -U "$UUID" "$tmpmnt" 2>/dev/null; then
391 + debug "btrfs volume $UUID mounted"
392 + else
393 + warn "cannot mount btrfs volume $UUID, exiting"
394 + rmdir "$tmpmnt" || true
395 + exit 1
396 + fi
397 + # besides regular subvols, get ro and snapshot so thet can be excluded
398 + subvols=$(btrfs subvolume list "$tmpmnt" | cut -d ' ' -f 9)
399 + rosubvols=$(btrfs subvolume list -r "$tmpmnt" | cut -d ' ' -f 9)
400 + sssubvols=$(btrfs subvolume list -s "$tmpmnt" | cut -d ' ' -f 14)
401 + if ! umount "$tmpmnt"; then
402 + warn "failed to umount btrfs volume on $tmpmnt"
403 + rmdir "$tmpmnt" || true
404 + exit 1
405 + fi
406 +
407 + found=
408 + mounted=
409 +
410 + mpoint="$(grep btrfs /proc/self/mountinfo | grep "$partition " | cut -d ' ' -f 5)"
411 + if [ -n "$mpoint" -a "x$mpoint" = "x/" ]; then
412 + debug "This is the root for the running system" #running system must be done elsewhere
413 + else
414 + #partition was not root of running system, so lets look for bootable subvols
415 + if [ -n "$mpoint" ] ; then
416 + mounted=1 #partition was already mounted,so lets not unmount it when done
417 + else
418 + # again, do not mount btrfs ro
419 + mount -t btrfs -U "$UUID" "$tmpmnt"
420 + mpoint="$tmpmnt"
421 + fi
422 +
423 + test="/usr/lib/os-probes/mounted/90linux-distro"
424 + if [ -f "$test" ] && [ -x "$test" ]; then
425 + debug "running subtest $test"
426 + if "$test" "$partition" "$mpoint" btrfs "UUID=$UUID"; then
427 + debug "os found by subtest $test on $partition"
428 + found=1
429 + fi
430 + fi
431 + if [ -z "$mounted" ]; then
432 + if ! umount "$tmpmnt"; then
433 + warn "failed to umount $tmpmnt"
434 + fi
435 + fi
436 + fi
437 +
438 + if [ -z "$subvols" ]; then
439 + debug "no subvols found on btrfs volume $UUID"
440 + else
441 + found=
442 + for subvol in $subvols; do
443 + debug "begin btrfs processing for $UUID subvol=$subvol"
444 + if echo "$rosubvols" | grep -q -x "$subvol"; then
445 + continue
446 + fi
447 + if echo "$sssubvols" | grep -q -x "$subvol"; then
448 + continue
449 + fi
450 + mounted=
451 + mpoint="$(grep btrfs /proc/self/mountinfo | grep "$partition " | grep "/$subvol " | cut -d ' ' -f 5)"
452 + if [ -n "$mpoint" ]; then
453 + if [ "x$mpoint" = "x/" ]; then
454 + continue # this is the root for the running system
455 + fi
456 + mounted=1
457 + else
458 + # again, do not mount btrfs ro
459 + mount -t btrfs -o subvol="$subvol" -U "$UUID" "$tmpmnt"
460 + mpoint="$tmpmnt"
461 + fi
462 + test="/usr/lib/os-probes/mounted/90linux-distro"
463 + if [ -f "$test" ] && [ -x "$test" ]; then
464 + debug "running subtest $test"
465 + if "$test" "$partition" "$mpoint" btrfs "UUID=$UUID" "subvol=$subvol"; then
466 + debug "os found by subtest $test on subvol $subvol"
467 + found=1
468 + fi
469 + fi
470 + if [ -z "$mounted" ]; then
471 + if ! umount "$tmpmnt"; then
472 + warn "failed to umount $tmpmnt"
473 + fi
474 + fi
475 + done
476 + fi
477 + rmdir "$tmpmnt" || true
478 + if [ "$found" ]; then
479 + exit 0
480 + else
481 + exit 1
482 + fi
483 +fi
484 +
485 if type grub-mount >/dev/null 2>&1 && \
486 type grub-probe >/dev/null 2>&1 && \
487 grub-mount "$partition" "$tmpmnt" 2>/dev/null; then
488 --- a/os-probes/mounted/common/90linux-distro
489 +++ b/os-probes/mounted/common/90linux-distro
490 @@ -7,6 +7,8 @@ set -e
491 partition="$1"
492 dir="$2"
493 type="$3"
494 +uuid="$4"
495 +subvol="$5"
496
497 # This test is inaccurate, but given separate / and /boot partitions and the
498 # fact that only some architectures have ld-linux.so, I can't see anything
499 @@ -143,7 +145,11 @@ if (ls "$dir"/lib*/ld*.so* && [ -d "$dir/boot" ] || ls "$dir"/usr/lib*/ld*.so*)
500 fi
501
502 label="$(count_next_label "$short")"
503 - result "$partition:$long:$label:linux"
504 + if [ "x$type" = "xbtrfs" -a "x$uuid" != "x" -a "x$subvol" != "x" ]; then
505 + result "$partition:$long:$label:linux:$type:$uuid:$subvol"
506 + else
507 + result "$partition:$long:$label:linux"
508 + fi
509 exit 0
510 else
511 exit 1
|