3 # functions used by dracut and other tools.
5 # Copyright 2005-2009 Red Hat, Inc. All rights reserved.
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.
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.
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/>.
23 # Check whether $1 is a function.
25 [[ "$(type -t "$1")" = "function" ]]
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" ]; }
40 # find a binary. If we were not passed the full path directly,
41 # search in the usual places to find the binary.
46 [[ -z ${1##/*} ]] || _delim
="/"
48 if [[ "$1" == *.so
* ]]; then
50 if { $DRACUT_LDD "$dracutsysrootdir$l$_delim$1" &>/dev
/null
; }; then
55 if { $DRACUT_LDD "$dracutsysrootdir$_delim$1" &>/dev
/null
; }; then
60 if [[ "$1" == */* ]]; then
61 if [[ -L $dracutsysrootdir$_delim$1 ]] ||
[[ -x $dracutsysrootdir$_delim$1 ]]; then
66 for p
in $DRACUT_PATH ; do
67 if [[ -L $dracutsysrootdir$p$_delim$1 ]] ||
[[ -x $dracutsysrootdir$p$_delim$1 ]]; then
73 [[ -n "$dracutsysrootdir" ]] && return 1
79 $DRACUT_LDCONFIG ${dracutsysrootdir:+-r ${dracutsysrootdir} -f /etc/ld.so.conf} -pN 2>/dev
/null |
grep -E -v '/(lib|lib64|usr/lib|usr/lib64)/[^/]*$' |
sed -n 's,.* => \(.*\)/.*,\1,p' |
sort |
uniq
82 # Version comparision function. Assumes Linux style version scheme.
84 # $2 = comparision op (gt, ge, eq, le, lt, ne)
87 local _n1
=(${1//./ }) _op
=$2 _n2
=(${3//./ }) _i _res
91 if [[ ! ${_n1[_i]}${_n2[_i]} ]]; then _res
=0
92 elif ((${_n1[_i]:-0} > ${_n2[_i]:-0})); then _res
=1
93 elif ((${_n1[_i]:-0} < ${_n2[_i]:-0})); then _res
=2
109 # Create all subdirectories for given path without creating the last element.
112 [[ -e ${1%/*} ]] || mkdir
-m 0755 -p -- "${1%/*}"
115 # Function prints global variables in format name=value line by line.
116 # $@ = list of global variables' name
122 eval printf -v _value
"%s" \""\$$_var"\"
123 [[ ${_value} ]] && printf '%s="%s"\n' "$_var" "$_value"
127 # normalize_path <path>
128 # Prints the normalized path, where it removes any duplicated
129 # and trailing slashes.
131 # $ normalize_path ///test/test//
135 set -- "${1//+(\/)//}"
137 printf "%s\n" "${1%/}"
140 # convert_abs_rel <from> <to>
141 # Prints the relative path, when creating a symlink to <to> from <from>.
143 # $ convert_abs_rel /usr/bin/test /bin/test-2
145 # $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
147 local __current __absolute __abssize __cursize __newpath
150 set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
152 # corner case #1 - self looping link
153 [[ "$1" == "$2" ]] && { printf "%s\n" "${1##*/}"; return; }
155 # corner case #2 - own dir link
156 [[ "${1%/*}" == "$2" ]] && { printf ".\n"; return; }
158 IFS
="/" __current
=($1)
159 IFS
="/" __absolute
=($2)
161 __abssize
=${#__absolute[@]}
162 __cursize
=${#__current[@]}
164 while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]
167 if (( __level
> __abssize || __level
> __cursize
))
173 for ((__i
= __level
; __i
< __cursize-1
; __i
++))
177 __newpath
=$__newpath"/"
179 __newpath
=$__newpath".."
182 for ((__i
= __level
; __i
< __abssize
; __i
++))
184 if [[ -n $__newpath ]]
186 __newpath
=$__newpath"/"
188 __newpath
=$__newpath${__absolute[__i]}
191 printf "%s\n" "$__newpath"
195 # get_fs_env <device>
196 # Get and the ID_FS_TYPE variable from udev for a device.
198 # $ get_fs_env /dev/sda2
203 ID_FS_TYPE
=$
(blkid
-u filesystem
-o export -- "$1" \
204 |
while read line ||
[ -n "$line" ]; do
205 if [[ "$line" == TYPE\
=* ]]; then
206 printf "%s" "${line#TYPE=}";
210 if [[ $ID_FS_TYPE ]]; then
211 printf "%s" "$ID_FS_TYPE"
217 # get_maj_min <device>
218 # Prints the major and minor of a device node.
220 # $ get_maj_min /dev/sda2
224 _majmin
="$(stat -L -c '%t:%T' "$1" 2>/dev/null)"
225 printf "%s" "$((0x${_majmin%:*})):$((0x${_majmin#*:}))"
229 # get_devpath_block <device>
230 # get the DEVPATH in /sys of a block device
231 get_devpath_block
() {
233 _majmin
=$
(get_maj_min
"$1")
235 for _i
in /sys
/block
/*/dev
/sys
/block
/*/*/dev
; do
236 [[ -e "$_i" ]] ||
continue
237 if [[ "$_majmin" == "$(<"$_i")" ]]; then
238 printf "%s" "${_i%/dev}"
245 # get a persistent path from a device
246 get_persistent_dev
() {
247 local i _tmp _dev _pol
249 _dev
=$
(get_maj_min
"$1")
250 [ -z "$_dev" ] && return
252 if [[ -n "$persistent_policy" ]]; then
253 _pol
="/dev/disk/${persistent_policy}/*"
261 /dev
/disk
/by-uuid
/* \
262 /dev
/disk
/by-label
/* \
263 /dev
/disk
/by-partuuid
/* \
264 /dev
/disk
/by-partlabel
/* \
266 /dev
/disk
/by-path
/* \
268 [[ -e "$i" ]] ||
continue
269 [[ $i == /dev
/mapper
/control
]] && continue
270 [[ $i == /dev
/mapper
/mpath
* ]] && continue
271 _tmp
=$
(get_maj_min
"$i")
272 if [ "$_tmp" = "$_dev" ]; then
280 expand_persistent_dev
() {
285 _dev
="/dev/disk/by-label/${_dev#LABEL=}"
290 _dev
="/dev/disk/by-uuid/${_dev}"
293 _dev
="${_dev#PARTUUID=}"
295 _dev
="/dev/disk/by-partuuid/${_dev}"
298 _dev
="/dev/disk/by-partlabel/${_dev#PARTLABEL=}"
304 shorten_persistent_dev
() {
308 printf "%s" "UUID=${_dev##*/}";;
309 /dev
/disk
/by-label
/*)
310 printf "%s" "LABEL=${_dev##*/}";;
311 /dev
/disk
/by-partuuid
/*)
312 printf "%s" "PARTUUID=${_dev##*/}";;
313 /dev
/disk
/by-partlabel
/*)
314 printf "%s" "PARTLABEL=${_dev##*/}";;
316 printf "%s" "$_dev";;
320 # find_block_device <mountpoint>
321 # Prints the major and minor number of the block device
322 # for a given mountpoint.
323 # Unless $use_fstab is set to "yes" the functions
324 # uses /proc/self/mountinfo as the primary source of the
325 # information and only falls back to /etc/fstab, if the mountpoint
326 # is not found there.
328 # $ find_block_device /usr
330 find_block_device
() {
331 local _dev _majmin _find_mpt
333 if [[ $use_fstab != yes ]]; then
334 [[ -d $_find_mpt/.
]]
335 findmnt
-e -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" |
{ \
336 while read _majmin _dev ||
[ -n "$_dev" ]; do
337 if [[ -b $_dev ]]; then
338 if ! [[ $_majmin ]] ||
[[ $_majmin == 0:* ]]; then
339 _majmin
=$
(get_maj_min
$_dev)
341 if [[ $_majmin ]]; then
342 printf "%s\n" "$_majmin"
344 printf "%s\n" "$_dev"
348 if [[ $_dev = *:* ]]; then
349 printf "%s\n" "$_dev"
352 done; return 1; } && return 0
354 # fall back to /etc/fstab
356 findmnt
-e --fstab -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" |
{ \
357 while read _majmin _dev ||
[ -n "$_dev" ]; do
358 if ! [[ $_dev ]]; then
362 if [[ -b $_dev ]]; then
363 [[ $_majmin ]] || _majmin
=$
(get_maj_min
$_dev)
364 if [[ $_majmin ]]; then
365 printf "%s\n" "$_majmin"
367 printf "%s\n" "$_dev"
371 if [[ $_dev = *:* ]]; then
372 printf "%s\n" "$_dev"
375 done; return 1; } && return 0
380 # find_mp_fstype <mountpoint>
381 # Echo the filesystem type for a given mountpoint.
382 # /proc/self/mountinfo is taken as the primary source of information
383 # and /etc/fstab is used as a fallback.
384 # No newline is appended!
386 # $ find_mp_fstype /;echo
391 if [[ $use_fstab != yes ]]; then
392 findmnt
-e -v -n -o 'FSTYPE' --target "$1" |
{ \
393 while read _fs ||
[ -n "$_fs" ]; do
394 [[ $_fs ]] ||
continue
395 [[ $_fs = "autofs" ]] && continue
398 done; return 1; } && return 0
401 findmnt
--fstab -e -v -n -o 'FSTYPE' --target "$1" |
{ \
402 while read _fs ||
[ -n "$_fs" ]; do
403 [[ $_fs ]] ||
continue
404 [[ $_fs = "autofs" ]] && continue
407 done; return 1; } && return 0
412 # find_dev_fstype <device>
413 # Echo the filesystem type for a given device.
414 # /proc/self/mountinfo is taken as the primary source of information
415 # and /etc/fstab is used as a fallback.
416 # No newline is appended!
418 # $ find_dev_fstype /dev/sda2;echo
423 if ! [[ "$_find_dev" = /dev
* ]]; then
424 [[ -b "/dev/block/$_find_dev" ]] && _find_dev
="/dev/block/$_find_dev"
427 if [[ $use_fstab != yes ]]; then
428 findmnt
-e -v -n -o 'FSTYPE' --source "$_find_dev" |
{ \
429 while read _fs ||
[ -n "$_fs" ]; do
430 [[ $_fs ]] ||
continue
431 [[ $_fs = "autofs" ]] && continue
434 done; return 1; } && return 0
437 findmnt
--fstab -e -v -n -o 'FSTYPE' --source "$_find_dev" |
{ \
438 while read _fs ||
[ -n "$_fs" ]; do
439 [[ $_fs ]] ||
continue
440 [[ $_fs = "autofs" ]] && continue
443 done; return 1; } && return 0
448 # find_mp_fsopts <mountpoint>
449 # Echo the filesystem options for a given mountpoint.
450 # /proc/self/mountinfo is taken as the primary source of information
451 # and /etc/fstab is used as a fallback.
452 # No newline is appended!
454 # $ find_mp_fsopts /;echo
455 # rw,relatime,discard,data=ordered
457 if [[ $use_fstab != yes ]]; then
458 findmnt
-e -v -n -o 'OPTIONS' --target "$1" 2>/dev
/null
&& return 0
461 findmnt
--fstab -e -v -n -o 'OPTIONS' --target "$1"
464 # find_dev_fsopts <device>
465 # Echo the filesystem options for a given device.
466 # /proc/self/mountinfo is taken as the primary source of information
467 # and /etc/fstab is used as a fallback.
469 # $ find_dev_fsopts /dev/sda2
470 # rw,relatime,discard,data=ordered
472 local _find_dev _opts
474 if ! [[ "$_find_dev" = /dev
* ]]; then
475 [[ -b "/dev/block/$_find_dev" ]] && _find_dev
="/dev/block/$_find_dev"
478 if [[ $use_fstab != yes ]]; then
479 findmnt
-e -v -n -o 'OPTIONS' --source "$_find_dev" 2>/dev
/null
&& return 0
482 findmnt
--fstab -e -v -n -o 'OPTIONS' --source "$_find_dev"
486 # finds the major:minor of the block device backing the root filesystem.
487 find_root_block_device
() { find_block_device
/; }
489 # for_each_host_dev_fs <func>
490 # Execute "<func> <dev> <filesystem>" for every "<dev> <fs>" pair found
491 # in ${host_fs_types[@]}
492 for_each_host_dev_fs
()
498 [[ "${#host_fs_types[@]}" ]] ||
return 2
501 for _dev
in "${!host_fs_types[@]}"; do
502 $_func "$_dev" "${host_fs_types[$_dev]}" && _ret
=0
509 printf "%s\n" "${host_fs_types[@]}"
512 # Walk all the slave relationships for a given block device.
513 # Stop when our helper function returns success
514 # $1 = function to call on every found block device
515 # $2 = block device in major:minor format
516 check_block_and_slaves
() {
518 [[ -b /dev
/block
/$2 ]] ||
return 1 # Not a block device? So sorry.
519 if ! lvm_internal_dev
$2; then "$1" $2 && return; fi
520 check_vol_slaves
"$@" && return 0
521 if [[ -f /sys
/dev
/block
/$2/..
/dev
]] && [[ /sys
/dev
/block
/$2/..
/subsystem
-ef /sys
/class
/block
]]; then
522 check_block_and_slaves
$1 $
(<"/sys/dev/block/$2/../dev") && return 0
524 [[ -d /sys
/dev
/block
/$2/slaves
]] ||
return 1
525 for _x
in /sys
/dev
/block
/$2/slaves
/*; do
526 [[ -f $_x/dev
]] ||
continue
527 [[ $_x/subsystem
-ef /sys
/class
/block
]] ||
continue
528 check_block_and_slaves
$1 $
(<"$_x/dev") && return 0
533 check_block_and_slaves_all
() {
535 [[ -b /dev
/block
/$2 ]] ||
return 1 # Not a block device? So sorry.
536 if ! lvm_internal_dev
$2 && "$1" $2; then
539 check_vol_slaves_all
"$@" && return 0
540 if [[ -f /sys
/dev
/block
/$2/..
/dev
]] && [[ /sys
/dev
/block
/$2/..
/subsystem
-ef /sys
/class
/block
]]; then
541 check_block_and_slaves_all
$1 $
(<"/sys/dev/block/$2/../dev") && _ret
=0
543 [[ -d /sys
/dev
/block
/$2/slaves
]] ||
return 1
544 for _x
in /sys
/dev
/block
/$2/slaves
/*; do
545 [[ -f $_x/dev
]] ||
continue
546 [[ $_x/subsystem
-ef /sys
/class
/block
]] ||
continue
547 check_block_and_slaves_all
$1 $
(<"$_x/dev") && _ret
=0
551 # for_each_host_dev_and_slaves <func>
552 # Execute "<func> <dev>" for every "<dev>" found
553 # in ${host_devs[@]} and their slaves
554 for_each_host_dev_and_slaves_all
()
560 [[ "${host_devs[@]}" ]] ||
return 2
562 for _dev
in "${host_devs[@]}"; do
563 [[ -b "$_dev" ]] ||
continue
564 if check_block_and_slaves_all
$_func $
(get_maj_min
$_dev); then
571 for_each_host_dev_and_slaves
()
576 [[ "${host_devs[@]}" ]] ||
return 2
578 for _dev
in "${host_devs[@]}"; do
579 [[ -b "$_dev" ]] ||
continue
580 check_block_and_slaves
$_func $
(get_maj_min
$_dev) && return 0
585 # ugly workaround for the lvm design
586 # There is no volume group device,
587 # so, there are no slave devices for volume groups.
588 # Logical volumes only have the slave devices they really live on,
589 # but you cannot create the logical volume without the volume group.
590 # And the volume group might be bigger than the devices the LV needs.
592 local _lv _vg _pv _dm _majmin
594 _lv
="/dev/block/$_majmin"
595 _dm
=/sys
/dev
/block
/$_majmin/dm
596 [[ -f $_dm/uuid
&& $
(<$_dm/uuid
) =~ LVM-
* ]] ||
return 1
597 _vg
=$
(dmsetup splitname
--noheadings -o vg_name $
(<"$_dm/name") )
599 _vg
="${_vg//[[:space:]]/}"
601 for _pv
in $
(lvm vgs
--noheadings -o pv_name
"$_vg" 2>/dev
/null
)
603 check_block_and_slaves
$1 $
(get_maj_min
$_pv) && return 0
609 check_vol_slaves_all
() {
610 local _lv _vg _pv _majmin
612 _lv
="/dev/block/$_majmin"
613 _dm
="/sys/dev/block/$_majmin/dm"
614 [[ -f $_dm/uuid
&& $
(<$_dm/uuid
) =~ LVM-
* ]] ||
return 1
615 _vg
=$
(dmsetup splitname
--noheadings -o vg_name $
(<"$_dm/name") )
617 _vg
="${_vg//[[:space:]]/}"
619 # when filter/global_filter is set, lvm may be failed
620 lvm lvs
--noheadings -o vg_name
$_vg 2>/dev
/null
1>/dev
/null
621 if [ $?
-ne 0 ]; then
625 for _pv
in $
(lvm vgs
--noheadings -o pv_name
"$_vg" 2>/dev
/null
)
627 check_block_and_slaves_all
$1 $
(get_maj_min
$_pv)
636 # fs_get_option <filesystem options> <search for option>
637 # search for a specific option in a bunch of filesystem options
638 # and return the value
646 while [ $# -gt 0 ]; do
649 echo ${1#${_option}=}
656 check_kernel_config
()
658 local _config_opt
="$1"
660 [[ -f $dracutsysrootdir/boot
/config-
$kernel ]] \
661 && _config_file
="/boot/config-$kernel"
662 [[ -f $dracutsysrootdir/lib
/modules
/$kernel/config
]] \
663 && _config_file
="/lib/modules/$kernel/config"
665 # no kernel config file, so return true
666 [[ $_config_file ]] ||
return 0
668 grep -q -F "${_config_opt}=" "$_config_file" && return 0
674 # Only two values are returned: AMD or Intel
677 if grep -qE AMD
/proc
/cpuinfo
; then
680 if grep -qE Intel
/proc
/cpuinfo
; then
686 # Get the hosts' ucode file based on the /proc/cpuinfo
689 local family
=`grep -E "cpu family" /proc/cpuinfo | head -1 | sed s/.*:\ //`
690 local model
=`grep -E "model" /proc/cpuinfo |grep -v name | head -1 | sed s/.*:\ //`
691 local stepping
=`grep -E "stepping" /proc/cpuinfo | head -1 | sed s/.*:\ //`
693 if [[ "$(get_cpu_vendor)" == "AMD" ]]; then
694 if [[ $family -ge 21 ]]; then
695 printf "microcode_amd_fam%xh.bin" $family
697 printf "microcode_amd.bin"
700 if [[ "$(get_cpu_vendor)" == "Intel" ]]; then
701 # The /proc/cpuinfo are in decimal.
702 printf "%02x-%02x-%02x" ${family} ${model} ${stepping}
706 # Not every device in /dev/mapper should be examined.
707 # If it is an LVM device, touch only devices which have /dev/VG/LV symlink.
709 local dev_dm_dir
=/sys
/dev
/block
/$1/dm
710 [[ ! -f $dev_dm_dir/uuid || $
(<$dev_dm_dir/uuid
) != LVM-
* ]] && return 1 # Not an LVM device
711 local DM_VG_NAME DM_LV_NAME DM_LV_LAYER
712 eval $
(dmsetup splitname
--nameprefixes --noheadings --rows "$(<$dev_dm_dir/name)" 2>/dev
/null
)
713 [[ ${DM_VG_NAME} ]] && [[ ${DM_LV_NAME} ]] ||
return 0 # Better skip this!
714 [[ ${DM_LV_LAYER} ]] || [[ ! -L /dev/${DM_VG_NAME}/${DM_LV_NAME} ]]
719 btrfs device usage
"$_mp" \
720 |
while read _dev _rest
; do
721 str_starts
"$_dev" "/" ||
continue
723 printf -- "%s\n" "$_dev"