]> git.ipfire.org Git - thirdparty/dracut.git/blob - dracut-functions.sh
fix(zfcp_rules): correct shellcheck regression when parsing ccw args
[thirdparty/dracut.git] / dracut-functions.sh
1 #!/bin/bash
2 #
3 # functions used by dracut and other tools.
4 #
5 # Copyright 2005-2009 Red Hat, Inc. All rights reserved.
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #
20 export LC_MESSAGES=C
21
22 # is_func <command>
23 # Check whether $1 is a function.
24 is_func() {
25 [[ "$(type -t "$1")" = "function" ]]
26 }
27
28
29 # Generic substring function. If $2 is in $1, return 0.
30 strstr() { [[ $1 = *"$2"* ]]; }
31 # Generic glob matching function. If glob pattern $2 matches anywhere in $1, OK
32 strglobin() { [[ $1 = *$2* ]]; }
33 # Generic glob matching function. If glob pattern $2 matches all of $1, OK
34 strglob() { [[ $1 = $2 ]]; }
35 # returns OK if $1 contains literal string $2 at the beginning, and isn't empty
36 str_starts() { [ "${1#"$2"*}" != "$1" ]; }
37 # returns OK if $1 contains literal string $2 at the end, and isn't empty
38 str_ends() { [ "${1%*"$2"}" != "$1" ]; }
39
40 # find a binary. If we were not passed the full path directly,
41 # search in the usual places to find the binary.
42 find_binary() {
43 if [[ -z ${1##/*} ]]; then
44 if [[ -x $1 ]] || { [[ "$1" == *.so* ]] && ldd "$1" &>/dev/null; }; then
45 printf "%s\n" "$1"
46 return 0
47 fi
48 fi
49
50 type -P "${1##*/}"
51 }
52
53 ldconfig_paths()
54 {
55 ldconfig -pN 2>/dev/null | grep -E -v '/(lib|lib64|usr/lib|usr/lib64)/[^/]*$' | sed -n 's,.* => \(.*\)/.*,\1,p' | sort | uniq
56 }
57
58 # Version comparision function. Assumes Linux style version scheme.
59 # $1 = version a
60 # $2 = comparision op (gt, ge, eq, le, lt, ne)
61 # $3 = version b
62 vercmp() {
63 local _n1=(${1//./ }) _op=$2 _n2=(${3//./ }) _i _res
64
65 for ((_i=0; ; _i++))
66 do
67 if [[ ! ${_n1[_i]}${_n2[_i]} ]]; then _res=0
68 elif ((${_n1[_i]:-0} > ${_n2[_i]:-0})); then _res=1
69 elif ((${_n1[_i]:-0} < ${_n2[_i]:-0})); then _res=2
70 else continue
71 fi
72 break
73 done
74
75 case $_op in
76 gt) ((_res == 1));;
77 ge) ((_res != 2));;
78 eq) ((_res == 0));;
79 le) ((_res != 1));;
80 lt) ((_res == 2));;
81 ne) ((_res != 0));;
82 esac
83 }
84
85 # Create all subdirectories for given path without creating the last element.
86 # $1 = path
87 mksubdirs() {
88 [[ -e ${1%/*} ]] || mkdir -m 0755 -p -- "${1%/*}"
89 }
90
91 # Function prints global variables in format name=value line by line.
92 # $@ = list of global variables' name
93 print_vars() {
94 local _var _value
95
96 for _var in "$@"
97 do
98 eval printf -v _value "%s" \""\$$_var"\"
99 [[ ${_value} ]] && printf '%s="%s"\n' "$_var" "$_value"
100 done
101 }
102
103 # normalize_path <path>
104 # Prints the normalized path, where it removes any duplicated
105 # and trailing slashes.
106 # Example:
107 # $ normalize_path ///test/test//
108 # /test/test
109 normalize_path() {
110 shopt -q -s extglob
111 set -- "${1//+(\/)//}"
112 shopt -q -u extglob
113 printf "%s\n" "${1%/}"
114 }
115
116 # convert_abs_rel <from> <to>
117 # Prints the relative path, when creating a symlink to <to> from <from>.
118 # Example:
119 # $ convert_abs_rel /usr/bin/test /bin/test-2
120 # ../../bin/test-2
121 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
122 convert_abs_rel() {
123 local __current __absolute __abssize __cursize __newpath
124 local -i __i __level
125
126 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
127
128 # corner case #1 - self looping link
129 [[ "$1" == "$2" ]] && { printf "%s\n" "${1##*/}"; return; }
130
131 # corner case #2 - own dir link
132 [[ "${1%/*}" == "$2" ]] && { printf ".\n"; return; }
133
134 IFS="/" __current=($1)
135 IFS="/" __absolute=($2)
136
137 __abssize=${#__absolute[@]}
138 __cursize=${#__current[@]}
139
140 while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]
141 do
142 (( __level++ ))
143 if (( __level > __abssize || __level > __cursize ))
144 then
145 break
146 fi
147 done
148
149 for ((__i = __level; __i < __cursize-1; __i++))
150 do
151 if ((__i > __level))
152 then
153 __newpath=$__newpath"/"
154 fi
155 __newpath=$__newpath".."
156 done
157
158 for ((__i = __level; __i < __abssize; __i++))
159 do
160 if [[ -n $__newpath ]]
161 then
162 __newpath=$__newpath"/"
163 fi
164 __newpath=$__newpath${__absolute[__i]}
165 done
166
167 printf "%s\n" "$__newpath"
168 }
169
170
171 # get_fs_env <device>
172 # Get and the ID_FS_TYPE variable from udev for a device.
173 # Example:
174 # $ get_fs_env /dev/sda2
175 # ext4
176 get_fs_env() {
177 local evalstr
178 local found
179
180 [[ $1 ]] || return
181 unset ID_FS_TYPE
182 ID_FS_TYPE=$(blkid -u filesystem -o export -- "$1" \
183 | while read line || [ -n "$line" ]; do
184 if [[ "$line" == TYPE\=* ]]; then
185 printf "%s" "${line#TYPE=}";
186 exit 0;
187 fi
188 done)
189 if [[ $ID_FS_TYPE ]]; then
190 printf "%s" "$ID_FS_TYPE"
191 return 0
192 fi
193 return 1
194 }
195
196 # get_maj_min <device>
197 # Prints the major and minor of a device node.
198 # Example:
199 # $ get_maj_min /dev/sda2
200 # 8:2
201 get_maj_min() {
202 local _maj _min _majmin
203 _majmin="$(stat -L -c '%t:%T' "$1" 2>/dev/null)"
204 printf "%s" "$((0x${_majmin%:*})):$((0x${_majmin#*:}))"
205 }
206
207
208 # get_devpath_block <device>
209 # get the DEVPATH in /sys of a block device
210 get_devpath_block() {
211 local _majmin _i
212 _majmin=$(get_maj_min "$1")
213
214 for _i in /sys/block/*/dev /sys/block/*/*/dev; do
215 [[ -e "$_i" ]] || continue
216 if [[ "$_majmin" == "$(<"$_i")" ]]; then
217 printf "%s" "${_i%/dev}"
218 return 0
219 fi
220 done
221 return 1
222 }
223
224 # get a persistent path from a device
225 get_persistent_dev() {
226 local i _tmp _dev _pol
227
228 _dev=$(get_maj_min "$1")
229 [ -z "$_dev" ] && return
230
231 if [[ -n "$persistent_policy" ]]; then
232 _pol="/dev/disk/${persistent_policy}/*"
233 else
234 _pol=
235 fi
236
237 for i in \
238 $_pol \
239 /dev/mapper/* \
240 /dev/disk/by-uuid/* \
241 /dev/disk/by-label/* \
242 /dev/disk/by-partuuid/* \
243 /dev/disk/by-partlabel/* \
244 /dev/disk/by-id/* \
245 /dev/disk/by-path/* \
246 ; do
247 [[ -e "$i" ]] || continue
248 [[ $i == /dev/mapper/control ]] && continue
249 [[ $i == /dev/mapper/mpath* ]] && continue
250 _tmp=$(get_maj_min "$i")
251 if [ "$_tmp" = "$_dev" ]; then
252 printf -- "%s" "$i"
253 return
254 fi
255 done
256 printf -- "%s" "$1"
257 }
258
259 expand_persistent_dev() {
260 local _dev=$1
261
262 case "$_dev" in
263 LABEL=*)
264 _dev="/dev/disk/by-label/${_dev#LABEL=}"
265 ;;
266 UUID=*)
267 _dev="${_dev#UUID=}"
268 _dev="${_dev,,}"
269 _dev="/dev/disk/by-uuid/${_dev}"
270 ;;
271 PARTUUID=*)
272 _dev="${_dev#PARTUUID=}"
273 _dev="${_dev,,}"
274 _dev="/dev/disk/by-partuuid/${_dev}"
275 ;;
276 PARTLABEL=*)
277 _dev="/dev/disk/by-partlabel/${_dev#PARTLABEL=}"
278 ;;
279 esac
280 printf "%s" "$_dev"
281 }
282
283 shorten_persistent_dev() {
284 local _dev="$1"
285 case "$_dev" in
286 /dev/disk/by-uuid/*)
287 printf "%s" "UUID=${_dev##*/}";;
288 /dev/disk/by-label/*)
289 printf "%s" "LABEL=${_dev##*/}";;
290 /dev/disk/by-partuuid/*)
291 printf "%s" "PARTUUID=${_dev##*/}";;
292 /dev/disk/by-partlabel/*)
293 printf "%s" "PARTLABEL=${_dev##*/}";;
294 *)
295 printf "%s" "$_dev";;
296 esac
297 }
298
299 # find_block_device <mountpoint>
300 # Prints the major and minor number of the block device
301 # for a given mountpoint.
302 # Unless $use_fstab is set to "yes" the functions
303 # uses /proc/self/mountinfo as the primary source of the
304 # information and only falls back to /etc/fstab, if the mountpoint
305 # is not found there.
306 # Example:
307 # $ find_block_device /usr
308 # 8:4
309 find_block_device() {
310 local _dev _majmin _find_mpt
311 _find_mpt="$1"
312 if [[ $use_fstab != yes ]]; then
313 [[ -d $_find_mpt/. ]]
314 findmnt -e -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | { \
315 while read _majmin _dev || [ -n "$_dev" ]; do
316 if [[ -b $_dev ]]; then
317 if ! [[ $_majmin ]] || [[ $_majmin == 0:* ]]; then
318 _majmin=$(get_maj_min $_dev)
319 fi
320 if [[ $_majmin ]]; then
321 printf "%s\n" "$_majmin"
322 else
323 printf "%s\n" "$_dev"
324 fi
325 return 0
326 fi
327 if [[ $_dev = *:* ]]; then
328 printf "%s\n" "$_dev"
329 return 0
330 fi
331 done; return 1; } && return 0
332 fi
333 # fall back to /etc/fstab
334
335 findmnt -e --fstab -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | { \
336 while read _majmin _dev || [ -n "$_dev" ]; do
337 if ! [[ $_dev ]]; then
338 _dev="$_majmin"
339 unset _majmin
340 fi
341 if [[ -b $_dev ]]; then
342 [[ $_majmin ]] || _majmin=$(get_maj_min $_dev)
343 if [[ $_majmin ]]; then
344 printf "%s\n" "$_majmin"
345 else
346 printf "%s\n" "$_dev"
347 fi
348 return 0
349 fi
350 if [[ $_dev = *:* ]]; then
351 printf "%s\n" "$_dev"
352 return 0
353 fi
354 done; return 1; } && return 0
355
356 return 1
357 }
358
359 # find_mp_fstype <mountpoint>
360 # Echo the filesystem type for a given mountpoint.
361 # /proc/self/mountinfo is taken as the primary source of information
362 # and /etc/fstab is used as a fallback.
363 # No newline is appended!
364 # Example:
365 # $ find_mp_fstype /;echo
366 # ext4
367 find_mp_fstype() {
368 local _fs
369
370 if [[ $use_fstab != yes ]]; then
371 findmnt -e -v -n -o 'FSTYPE' --target "$1" | { \
372 while read _fs || [ -n "$_fs" ]; do
373 [[ $_fs ]] || continue
374 [[ $_fs = "autofs" ]] && continue
375 printf "%s" "$_fs"
376 return 0
377 done; return 1; } && return 0
378 fi
379
380 findmnt --fstab -e -v -n -o 'FSTYPE' --target "$1" | { \
381 while read _fs || [ -n "$_fs" ]; do
382 [[ $_fs ]] || continue
383 [[ $_fs = "autofs" ]] && continue
384 printf "%s" "$_fs"
385 return 0
386 done; return 1; } && return 0
387
388 return 1
389 }
390
391 # find_dev_fstype <device>
392 # Echo the filesystem type for a given device.
393 # /proc/self/mountinfo is taken as the primary source of information
394 # and /etc/fstab is used as a fallback.
395 # No newline is appended!
396 # Example:
397 # $ find_dev_fstype /dev/sda2;echo
398 # ext4
399 find_dev_fstype() {
400 local _find_dev _fs
401 _find_dev="$1"
402 if ! [[ "$_find_dev" = /dev* ]]; then
403 [[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
404 fi
405
406 if [[ $use_fstab != yes ]]; then
407 findmnt -e -v -n -o 'FSTYPE' --source "$_find_dev" | { \
408 while read _fs || [ -n "$_fs" ]; do
409 [[ $_fs ]] || continue
410 [[ $_fs = "autofs" ]] && continue
411 printf "%s" "$_fs"
412 return 0
413 done; return 1; } && return 0
414 fi
415
416 findmnt --fstab -e -v -n -o 'FSTYPE' --source "$_find_dev" | { \
417 while read _fs || [ -n "$_fs" ]; do
418 [[ $_fs ]] || continue
419 [[ $_fs = "autofs" ]] && continue
420 printf "%s" "$_fs"
421 return 0
422 done; return 1; } && return 0
423
424 return 1
425 }
426
427 # find_mp_fsopts <mountpoint>
428 # Echo the filesystem options for a given mountpoint.
429 # /proc/self/mountinfo is taken as the primary source of information
430 # and /etc/fstab is used as a fallback.
431 # No newline is appended!
432 # Example:
433 # $ find_mp_fsopts /;echo
434 # rw,relatime,discard,data=ordered
435 find_mp_fsopts() {
436 if [[ $use_fstab != yes ]]; then
437 findmnt -e -v -n -o 'OPTIONS' --target "$1" 2>/dev/null && return 0
438 fi
439
440 findmnt --fstab -e -v -n -o 'OPTIONS' --target "$1"
441 }
442
443 # find_dev_fsopts <device>
444 # Echo the filesystem options for a given device.
445 # /proc/self/mountinfo is taken as the primary source of information
446 # and /etc/fstab is used as a fallback.
447 # Example:
448 # $ find_dev_fsopts /dev/sda2
449 # rw,relatime,discard,data=ordered
450 find_dev_fsopts() {
451 local _find_dev _opts
452 _find_dev="$1"
453 if ! [[ "$_find_dev" = /dev* ]]; then
454 [[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
455 fi
456
457 if [[ $use_fstab != yes ]]; then
458 findmnt -e -v -n -o 'OPTIONS' --source "$_find_dev" 2>/dev/null && return 0
459 fi
460
461 findmnt --fstab -e -v -n -o 'OPTIONS' --source "$_find_dev"
462 }
463
464
465 # finds the major:minor of the block device backing the root filesystem.
466 find_root_block_device() { find_block_device /; }
467
468 # for_each_host_dev_fs <func>
469 # Execute "<func> <dev> <filesystem>" for every "<dev> <fs>" pair found
470 # in ${host_fs_types[@]}
471 for_each_host_dev_fs()
472 {
473 local _func="$1"
474 local _dev
475 local _ret=1
476
477 [[ "${#host_fs_types[@]}" ]] || return 2
478
479
480 for _dev in "${!host_fs_types[@]}"; do
481 $_func "$_dev" "${host_fs_types[$_dev]}" && _ret=0
482 done
483 return $_ret
484 }
485
486 host_fs_all()
487 {
488 printf "%s\n" "${host_fs_types[@]}"
489 }
490
491 # Walk all the slave relationships for a given block device.
492 # Stop when our helper function returns success
493 # $1 = function to call on every found block device
494 # $2 = block device in major:minor format
495 check_block_and_slaves() {
496 local _x
497 [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry.
498 if ! lvm_internal_dev $2; then "$1" $2 && return; fi
499 check_vol_slaves "$@" && return 0
500 if [[ -f /sys/dev/block/$2/../dev ]] && [[ /sys/dev/block/$2/../subsystem -ef /sys/class/block ]]; then
501 check_block_and_slaves $1 $(<"/sys/dev/block/$2/../dev") && return 0
502 fi
503 [[ -d /sys/dev/block/$2/slaves ]] || return 1
504 for _x in /sys/dev/block/$2/slaves/*; do
505 [[ -f $_x/dev ]] || continue
506 [[ $_x/subsystem -ef /sys/class/block ]] || continue
507 check_block_and_slaves $1 $(<"$_x/dev") && return 0
508 done
509 return 1
510 }
511
512 check_block_and_slaves_all() {
513 local _x _ret=1
514 [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry.
515 if ! lvm_internal_dev $2 && "$1" $2; then
516 _ret=0
517 fi
518 check_vol_slaves_all "$@" && return 0
519 if [[ -f /sys/dev/block/$2/../dev ]] && [[ /sys/dev/block/$2/../subsystem -ef /sys/class/block ]]; then
520 check_block_and_slaves_all $1 $(<"/sys/dev/block/$2/../dev") && _ret=0
521 fi
522 [[ -d /sys/dev/block/$2/slaves ]] || return 1
523 for _x in /sys/dev/block/$2/slaves/*; do
524 [[ -f $_x/dev ]] || continue
525 [[ $_x/subsystem -ef /sys/class/block ]] || continue
526 check_block_and_slaves_all $1 $(<"$_x/dev") && _ret=0
527 done
528 return $_ret
529 }
530 # for_each_host_dev_and_slaves <func>
531 # Execute "<func> <dev>" for every "<dev>" found
532 # in ${host_devs[@]} and their slaves
533 for_each_host_dev_and_slaves_all()
534 {
535 local _func="$1"
536 local _dev
537 local _ret=1
538
539 [[ "${host_devs[@]}" ]] || return 2
540
541 for _dev in "${host_devs[@]}"; do
542 [[ -b "$_dev" ]] || continue
543 if check_block_and_slaves_all $_func $(get_maj_min $_dev); then
544 _ret=0
545 fi
546 done
547 return $_ret
548 }
549
550 for_each_host_dev_and_slaves()
551 {
552 local _func="$1"
553 local _dev
554
555 [[ "${host_devs[@]}" ]] || return 2
556
557 for _dev in "${host_devs[@]}"; do
558 [[ -b "$_dev" ]] || continue
559 check_block_and_slaves $_func $(get_maj_min $_dev) && return 0
560 done
561 return 1
562 }
563
564 # ugly workaround for the lvm design
565 # There is no volume group device,
566 # so, there are no slave devices for volume groups.
567 # Logical volumes only have the slave devices they really live on,
568 # but you cannot create the logical volume without the volume group.
569 # And the volume group might be bigger than the devices the LV needs.
570 check_vol_slaves() {
571 local _lv _vg _pv _dm _majmin
572 _majmin="$2"
573 _lv="/dev/block/$_majmin"
574 _dm=/sys/dev/block/$_majmin/dm
575 [[ -f $_dm/uuid && $(<$_dm/uuid) =~ LVM-* ]] || return 1
576 _vg=$(dmsetup splitname --noheadings -o vg_name $(<"$_dm/name") )
577 # strip space
578 _vg="${_vg//[[:space:]]/}"
579 if [[ $_vg ]]; then
580 for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2>/dev/null)
581 do
582 check_block_and_slaves $1 $(get_maj_min $_pv) && return 0
583 done
584 fi
585 return 1
586 }
587
588 check_vol_slaves_all() {
589 local _lv _vg _pv _majmin
590 _majmin="$2"
591 _lv="/dev/block/$_majmin"
592 _dm="/sys/dev/block/$_majmin/dm"
593 [[ -f $_dm/uuid && $(<$_dm/uuid) =~ LVM-* ]] || return 1
594 _vg=$(dmsetup splitname --noheadings -o vg_name $(<"$_dm/name") )
595 # strip space
596 _vg="${_vg//[[:space:]]/}"
597 if [[ $_vg ]]; then
598 for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2>/dev/null)
599 do
600 check_block_and_slaves_all $1 $(get_maj_min $_pv)
601 done
602 return 0
603 fi
604 return 1
605 }
606
607
608
609 # fs_get_option <filesystem options> <search for option>
610 # search for a specific option in a bunch of filesystem options
611 # and return the value
612 fs_get_option() {
613 local _fsopts=$1
614 local _option=$2
615 local OLDIFS="$IFS"
616 IFS=,
617 set -- $_fsopts
618 IFS="$OLDIFS"
619 while [ $# -gt 0 ]; do
620 case $1 in
621 $_option=*)
622 echo ${1#${_option}=}
623 break
624 esac
625 shift
626 done
627 }
628
629 check_kernel_config()
630 {
631 local _config_opt="$1"
632 local _config_file
633 [[ -f /boot/config-$kernel ]] \
634 && _config_file="/boot/config-$kernel"
635 [[ -f /lib/modules/$kernel/config ]] \
636 && _config_file="/lib/modules/$kernel/config"
637
638 # no kernel config file, so return true
639 [[ $_config_file ]] || return 0
640
641 grep -q -F "${_config_opt}=" "$_config_file" && return 0
642 return 1
643 }
644
645
646 # get_cpu_vendor
647 # Only two values are returned: AMD or Intel
648 get_cpu_vendor ()
649 {
650 if grep -qE AMD /proc/cpuinfo; then
651 printf "AMD"
652 fi
653 if grep -qE Intel /proc/cpuinfo; then
654 printf "Intel"
655 fi
656 }
657
658 # get_host_ucode
659 # Get the hosts' ucode file based on the /proc/cpuinfo
660 get_ucode_file ()
661 {
662 local family=`grep -E "cpu family" /proc/cpuinfo | head -1 | sed s/.*:\ //`
663 local model=`grep -E "model" /proc/cpuinfo |grep -v name | head -1 | sed s/.*:\ //`
664 local stepping=`grep -E "stepping" /proc/cpuinfo | head -1 | sed s/.*:\ //`
665
666 if [[ "$(get_cpu_vendor)" == "AMD" ]]; then
667 if [[ $family -ge 21 ]]; then
668 printf "microcode_amd_fam%xh.bin" $family
669 else
670 printf "microcode_amd.bin"
671 fi
672 fi
673 if [[ "$(get_cpu_vendor)" == "Intel" ]]; then
674 # The /proc/cpuinfo are in decimal.
675 printf "%02x-%02x-%02x" ${family} ${model} ${stepping}
676 fi
677 }
678
679 # Get currently loaded modules
680 # sorted, and delimited by newline
681 get_loaded_kernel_modules ()
682 {
683 local modules=( )
684 while read _module _size _used _used_by; do
685 modules+=( "$_module" )
686 done <<< "$(lsmod | sed -n '1!p')"
687 printf '%s\n' "${modules[@]}" | sort
688 }
689
690 # Not every device in /dev/mapper should be examined.
691 # If it is an LVM device, touch only devices which have /dev/VG/LV symlink.
692 lvm_internal_dev() {
693 local dev_dm_dir=/sys/dev/block/$1/dm
694 [[ ! -f $dev_dm_dir/uuid || $(<$dev_dm_dir/uuid) != LVM-* ]] && return 1 # Not an LVM device
695 local DM_VG_NAME DM_LV_NAME DM_LV_LAYER
696 eval $(dmsetup splitname --nameprefixes --noheadings --rows "$(<$dev_dm_dir/name)" 2>/dev/null)
697 [[ ${DM_VG_NAME} ]] && [[ ${DM_LV_NAME} ]] || return 0 # Better skip this!
698 [[ ${DM_LV_LAYER} ]] || [[ ! -L /dev/${DM_VG_NAME}/${DM_LV_NAME} ]]
699 }
700
701 btrfs_devs() {
702 local _mp="$1"
703 btrfs device usage "$_mp" \
704 | while read _dev _rest; do
705 str_starts "$_dev" "/" || continue
706 _dev=${_dev%,}
707 printf -- "%s\n" "$_dev"
708 done
709 }