]> git.ipfire.org Git - thirdparty/dracut.git/blobdiff - dracut-functions.sh
test(ISCSI): make test-30 use the test dracut modules
[thirdparty/dracut.git] / dracut-functions.sh
index 575bd4a69a1aac0a41042a1d19fe6d671ca341b5..5807b8c59eeb2d4b48b74a1addd22ee77c04ce51 100755 (executable)
@@ -22,37 +22,73 @@ export LC_MESSAGES=C
 # is_func <command>
 # Check whether $1 is a function.
 is_func() {
-    [[ "$(type -t "$1")" = "function" ]]
+    [[ "$(type -t "$1")" == "function" ]]
 }
 
-
 # Generic substring function.  If $2 is in $1, return 0.
-strstr() { [[ $1 = *"$2"* ]]; }
+strstr() { [[ $1 == *"$2"* ]]; }
 # Generic glob matching function. If glob pattern $2 matches anywhere in $1, OK
-strglobin() { [[ $1 = *$2* ]]; }
+strglobin() { [[ $1 == *$2* ]]; }
 # Generic glob matching function. If glob pattern $2 matches all of $1, OK
-strglob() { [[ $1 = $2 ]]; }
+# shellcheck disable=SC2053
+strglob() { [[ $1 == $2 ]]; }
 # returns OK if $1 contains literal string $2 at the beginning, and isn't empty
 str_starts() { [ "${1#"$2"*}" != "$1" ]; }
 # returns OK if $1 contains literal string $2 at the end, and isn't empty
 str_ends() { [ "${1%*"$2"}" != "$1" ]; }
 
+trim() {
+    local var="$*"
+    var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
+    var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
+    printf "%s" "$var"
+}
+
 # find a binary.  If we were not passed the full path directly,
 # search in the usual places to find the binary.
 find_binary() {
-    if [[ -z ${1##/*} ]]; then
-        if [[ -x $1 ]] || { [[ "$1" == *.so* ]] && ldd "$1" &>/dev/null; };  then
-            printf "%s\n" "$1"
+    local _delim
+    local _path
+    local l
+    local p
+    [[ -z ${1##/*} ]] || _delim="/"
+
+    if [[ $1 == *.so* ]]; then
+        # shellcheck disable=SC2154
+        for l in $libdirs; do
+            _path="${l}${_delim}${1}"
+            if { $DRACUT_LDD "${dracutsysrootdir}${_path}" &> /dev/null; }; then
+                printf "%s\n" "${_path}"
+                return 0
+            fi
+        done
+        _path="${_delim}${1}"
+        if { $DRACUT_LDD "${dracutsysrootdir}${_path}" &> /dev/null; }; then
+            printf "%s\n" "${_path}"
+            return 0
+        fi
+    fi
+    if [[ $1 == */* ]]; then
+        _path="${_delim}${1}"
+        if [[ -L ${dracutsysrootdir}${_path} ]] || [[ -x ${dracutsysrootdir}${_path} ]]; then
+            printf "%s\n" "${_path}"
             return 0
         fi
     fi
+    for p in $DRACUT_PATH; do
+        _path="${p}${_delim}${1}"
+        if [[ -L ${dracutsysrootdir}${_path} ]] || [[ -x ${dracutsysrootdir}${_path} ]]; then
+            printf "%s\n" "${_path}"
+            return 0
+        fi
+    done
 
+    [[ -n $dracutsysrootdir ]] && return 1
     type -P "${1##*/}"
 }
 
-ldconfig_paths()
-{
-    ldconfig -pN 2>/dev/null | grep -E -v '/(lib|lib64|usr/lib|usr/lib64)/[^/]*$' | sed -n 's,.* => \(.*\)/.*,\1,p' | sort | uniq
+ldconfig_paths() {
+    $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
 }
 
 # Version comparision function.  Assumes Linux style version scheme.
@@ -60,31 +96,40 @@ ldconfig_paths()
 # $2 = comparision op (gt, ge, eq, le, lt, ne)
 # $3 = version b
 vercmp() {
-    local _n1=(${1//./ }) _op=$2 _n2=(${3//./ }) _i _res
-
-    for ((_i=0; ; _i++))
-    do
-        if [[ ! ${_n1[_i]}${_n2[_i]} ]]; then _res=0
-        elif ((${_n1[_i]:-0} > ${_n2[_i]:-0})); then _res=1
-        elif ((${_n1[_i]:-0} < ${_n2[_i]:-0})); then _res=2
-        else continue
+    local _n1
+    read -r -a _n1 <<< "${1//./ }"
+    local _op=$2
+    local _n2
+    read -r -a _n2 <<< "${3//./ }"
+    local _i _res
+
+    for ((_i = 0; ; _i++)); do
+        if [[ ! ${_n1[_i]}${_n2[_i]} ]]; then
+            _res=0
+        elif ((${_n1[_i]:-0} > ${_n2[_i]:-0})); then
+            _res=1
+        elif ((${_n1[_i]:-0} < ${_n2[_i]:-0})); then
+            _res=2
+        else
+            continue
         fi
         break
     done
 
     case $_op in
-        gt) ((_res == 1));;
-        ge) ((_res != 2));;
-        eq) ((_res == 0));;
-        le) ((_res != 1));;
-        lt) ((_res == 2));;
-        ne) ((_res != 0));;
+        gt) ((_res == 1)) ;;
+        ge) ((_res != 2)) ;;
+        eq) ((_res == 0)) ;;
+        le) ((_res != 1)) ;;
+        lt) ((_res == 2)) ;;
+        ne) ((_res != 0)) ;;
     esac
 }
 
 # Create all subdirectories for given path without creating the last element.
 # $1 = path
 mksubdirs() {
+    # shellcheck disable=SC2174
     [[ -e ${1%/*} ]] || mkdir -m 0755 -p -- "${1%/*}"
 }
 
@@ -93,8 +138,7 @@ mksubdirs() {
 print_vars() {
     local _var _value
 
-    for _var in "$@"
-    do
+    for _var in "$@"; do
         eval printf -v _value "%s" \""\$$_var"\"
         [[ ${_value} ]] && printf '%s="%s"\n' "$_var" "$_value"
     done
@@ -107,10 +151,11 @@ print_vars() {
 # $ normalize_path ///test/test//
 # /test/test
 normalize_path() {
+    # shellcheck disable=SC2064
+    trap "$(shopt -p extglob)" RETURN
     shopt -q -s extglob
-    set -- "${1//+(\/)//}"
-    shopt -q -u extglob
-    printf "%s\n" "${1%/}"
+    local p=${1//+(\/)//}
+    printf "%s\n" "${p%/}"
 }
 
 # convert_abs_rel <from> <to>
@@ -126,66 +171,62 @@ convert_abs_rel() {
     set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
 
     # corner case #1 - self looping link
-    [[ "$1" == "$2" ]] && { printf "%s\n" "${1##*/}"; return; }
+    [[ $1 == "$2" ]] && {
+        printf "%s\n" "${1##*/}"
+        return
+    }
 
     # corner case #2 - own dir link
-    [[ "${1%/*}" == "$2" ]] && { printf ".\n"; return; }
+    [[ ${1%/*} == "$2" ]] && {
+        printf ".\n"
+        return
+    }
 
-    IFS="/" __current=($1)
-    IFS="/" __absolute=($2)
+    IFS=/ read -r -a __current <<< "$1"
+    IFS=/ read -r -a __absolute <<< "$2"
 
     __abssize=${#__absolute[@]}
     __cursize=${#__current[@]}
 
-    while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]
-    do
-        (( __level++ ))
-        if (( __level > __abssize || __level > __cursize ))
-        then
+    while [[ ${__absolute[__level]} == "${__current[__level]}" ]]; do
+        ((__level++))
+        if ((__level > __abssize || __level > __cursize)); then
             break
         fi
     done
 
-    for ((__i = __level; __i < __cursize-1; __i++))
-    do
-        if ((__i > __level))
-        then
+    for ((__i = __level; __i < __cursize - 1; __i++)); do
+        if ((__i > __level)); then
             __newpath=$__newpath"/"
         fi
         __newpath=$__newpath".."
     done
 
-    for ((__i = __level; __i < __abssize; __i++))
-    do
-        if [[ -n $__newpath ]]
-        then
+    for ((__i = __level; __i < __abssize; __i++)); do
+        if [[ -n $__newpath ]]; then
             __newpath=$__newpath"/"
         fi
         __newpath=$__newpath${__absolute[__i]}
     done
 
-    printf "%s\n" "$__newpath"
+    printf -- "%s\n" "$__newpath"
 }
 
-
 # get_fs_env <device>
 # Get and the ID_FS_TYPE variable from udev for a device.
 # Example:
 # $ get_fs_env /dev/sda2
 # ext4
 get_fs_env() {
-    local evalstr
-    local found
-
     [[ $1 ]] || return
     unset ID_FS_TYPE
     ID_FS_TYPE=$(blkid -u filesystem -o export -- "$1" \
-        | while read line || [ -n "$line" ]; do
-            if [[ "$line" == TYPE\=* ]]; then
-                printf "%s" "${line#TYPE=}";
-                exit 0;
+        | while read -r line || [ -n "$line" ]; do
+            if [[ $line == "TYPE="* ]]; then
+                printf "%s" "${line#TYPE=}"
+                exit 0
             fi
-            done)
+        done)
     if [[ $ID_FS_TYPE ]]; then
         printf "%s" "$ID_FS_TYPE"
         return 0
@@ -199,11 +240,22 @@ get_fs_env() {
 # $ get_maj_min /dev/sda2
 # 8:2
 get_maj_min() {
-    local _maj _min _majmin
-    _majmin="$(stat -L -c '%t:%T' "$1" 2>/dev/null)"
-    printf "%s" "$((0x${_majmin%:*})):$((0x${_majmin#*:}))"
-}
+    local _majmin
+    local _out
+
+    if [[ $get_maj_min_cache_file ]]; then
+        _out="$(grep -m1 -oE "^$1 \S+$" "$get_maj_min_cache_file" | awk '{print $NF}')"
+    fi
 
+    if ! [[ "$_out" ]]; then
+        _majmin="$(stat -L -c '%t:%T' "$1" 2> /dev/null)"
+        _out="$(printf "%s" "$((0x${_majmin%:*})):$((0x${_majmin#*:}))")"
+        if [[ $get_maj_min_cache_file ]]; then
+            echo "$1 $_out" >> "$get_maj_min_cache_file"
+        fi
+    fi
+    echo -n "$_out"
+}
 
 # get_devpath_block <device>
 # get the DEVPATH in /sys of a block device
@@ -212,8 +264,8 @@ get_devpath_block() {
     _majmin=$(get_maj_min "$1")
 
     for _i in /sys/block/*/dev /sys/block/*/*/dev; do
-        [[ -e "$_i" ]] || continue
-        if [[ "$_majmin" == "$(<"$_i")" ]]; then
+        [[ -e $_i ]] || continue
+        if [[ $_majmin == "$(< "$_i")" ]]; then
             printf "%s" "${_i%/dev}"
             return 0
         fi
@@ -223,22 +275,27 @@ get_devpath_block() {
 
 # get a persistent path from a device
 get_persistent_dev() {
-    local i _tmp _dev
+    local i _tmp _dev _pol
 
     _dev=$(get_maj_min "$1")
     [ -z "$_dev" ] && return
 
+    if [[ -n $persistent_policy ]]; then
+        _pol="/dev/disk/${persistent_policy}/*"
+    else
+        _pol=
+    fi
+
     for i in \
+        $_pol \
         /dev/mapper/* \
-        /dev/disk/${persistent_policy:-by-uuid}/* \
         /dev/disk/by-uuid/* \
         /dev/disk/by-label/* \
         /dev/disk/by-partuuid/* \
         /dev/disk/by-partlabel/* \
         /dev/disk/by-id/* \
-        /dev/disk/by-path/* \
-        ; do
-        [[ -e "$i" ]] || continue
+        /dev/disk/by-path/*; do
+        [[ -e $i ]] || continue
         [[ $i == /dev/mapper/control ]] && continue
         [[ $i == /dev/mapper/mpath* ]] && continue
         _tmp=$(get_maj_min "$i")
@@ -278,15 +335,20 @@ shorten_persistent_dev() {
     local _dev="$1"
     case "$_dev" in
         /dev/disk/by-uuid/*)
-            printf "%s" "UUID=${_dev##*/}";;
+            printf "%s" "UUID=${_dev##*/}"
+            ;;
         /dev/disk/by-label/*)
-            printf "%s" "LABEL=${_dev##*/}";;
+            printf "%s" "LABEL=${_dev##*/}"
+            ;;
         /dev/disk/by-partuuid/*)
-            printf "%s" "PARTUUID=${_dev##*/}";;
+            printf "%s" "PARTUUID=${_dev##*/}"
+            ;;
         /dev/disk/by-partlabel/*)
-            printf "%s" "PARTLABEL=${_dev##*/}";;
+            printf "%s" "PARTLABEL=${_dev##*/}"
+            ;;
         *)
-            printf "%s" "$_dev";;
+            printf "%s" "$_dev"
+            ;;
     esac
 }
 
@@ -303,13 +365,14 @@ shorten_persistent_dev() {
 find_block_device() {
     local _dev _majmin _find_mpt
     _find_mpt="$1"
+
     if [[ $use_fstab != yes ]]; then
         [[ -d $_find_mpt/. ]]
-        findmnt -e -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | { \
-            while read _majmin _dev || [ -n "$_dev" ]; do
+        findmnt -e -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | {
+            while read -r _majmin _dev || [ -n "$_dev" ]; do
                 if [[ -b $_dev ]]; then
                     if ! [[ $_majmin ]] || [[ $_majmin == 0:* ]]; then
-                        _majmin=$(get_maj_min $_dev)
+                        _majmin=$(get_maj_min "$_dev")
                     fi
                     if [[ $_majmin ]]; then
                         printf "%s\n" "$_majmin"
@@ -318,22 +381,25 @@ find_block_device() {
                     fi
                     return 0
                 fi
-                if [[ $_dev = *:* ]]; then
+                if [[ $_dev == *:* ]]; then
                     printf "%s\n" "$_dev"
                     return 0
                 fi
-            done; return 1; } && return 0
+            done
+            return 1
+        } && return 0
     fi
     # fall back to /etc/fstab
+    [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
 
-    findmnt -e --fstab -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | { \
-        while read _majmin _dev || [ -n "$_dev" ]; do
+    findmnt -e --fstab -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | {
+        while read -r _majmin _dev || [ -n "$_dev" ]; do
             if ! [[ $_dev ]]; then
                 _dev="$_majmin"
                 unset _majmin
             fi
             if [[ -b $_dev ]]; then
-                [[ $_majmin ]] || _majmin=$(get_maj_min $_dev)
+                [[ $_majmin ]] || _majmin=$(get_maj_min "$_dev")
                 if [[ $_majmin ]]; then
                     printf "%s\n" "$_majmin"
                 else
@@ -341,11 +407,13 @@ find_block_device() {
                 fi
                 return 0
             fi
-            if [[ $_dev = *:* ]]; then
+            if [[ $_dev == *:* ]]; then
                 printf "%s\n" "$_dev"
                 return 0
             fi
-        done; return 1; } && return 0
+        done
+        return 1
+    } && return 0
 
     return 1
 }
@@ -362,22 +430,28 @@ find_mp_fstype() {
     local _fs
 
     if [[ $use_fstab != yes ]]; then
-        findmnt -e -v -n -o 'FSTYPE' --target "$1" | { \
-            while read _fs || [ -n "$_fs" ]; do
+        findmnt -e -v -n -o 'FSTYPE' --target "$1" | {
+            while read -r _fs || [ -n "$_fs" ]; do
                 [[ $_fs ]] || continue
-                [[ $_fs = "autofs" ]] && continue
+                [[ $_fs == "autofs" ]] && continue
                 printf "%s" "$_fs"
                 return 0
-            done; return 1; } && return 0
+            done
+            return 1
+        } && return 0
     fi
 
-    findmnt --fstab -e -v -n -o 'FSTYPE' --target "$1" | { \
-        while read _fs || [ -n "$_fs" ]; do
+    [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
+
+    findmnt --fstab -e -v -n -o 'FSTYPE' --target "$1" | {
+        while read -r _fs || [ -n "$_fs" ]; do
             [[ $_fs ]] || continue
-            [[ $_fs = "autofs" ]] && continue
+            [[ $_fs == "autofs" ]] && continue
             printf "%s" "$_fs"
             return 0
-        done; return 1; } && return 0
+        done
+        return 1
+    } && return 0
 
     return 1
 }
@@ -393,27 +467,33 @@ find_mp_fstype() {
 find_dev_fstype() {
     local _find_dev _fs
     _find_dev="$1"
-    if ! [[ "$_find_dev" = /dev* ]]; then
+    if ! [[ $_find_dev == /dev* ]]; then
         [[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
     fi
 
     if [[ $use_fstab != yes ]]; then
-        findmnt -e -v -n -o 'FSTYPE' --source "$_find_dev" | { \
-            while read _fs || [ -n "$_fs" ]; do
+        findmnt -e -v -n -o 'FSTYPE' --source "$_find_dev" | {
+            while read -r _fs || [ -n "$_fs" ]; do
                 [[ $_fs ]] || continue
-                [[ $_fs = "autofs" ]] && continue
+                [[ $_fs == "autofs" ]] && continue
                 printf "%s" "$_fs"
                 return 0
-            done; return 1; } && return 0
+            done
+            return 1
+        } && return 0
     fi
 
-    findmnt --fstab -e -v -n -o 'FSTYPE' --source "$_find_dev" | { \
-        while read _fs || [ -n "$_fs" ]; do
+    [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
+
+    findmnt --fstab -e -v -n -o 'FSTYPE' --source "$_find_dev" | {
+        while read -r _fs || [ -n "$_fs" ]; do
             [[ $_fs ]] || continue
-            [[ $_fs = "autofs" ]] && continue
+            [[ $_fs == "autofs" ]] && continue
             printf "%s" "$_fs"
             return 0
-        done; return 1; } && return 0
+        done
+        return 1
+    } && return 0
 
     return 1
 }
@@ -428,9 +508,11 @@ find_dev_fstype() {
 # rw,relatime,discard,data=ordered
 find_mp_fsopts() {
     if [[ $use_fstab != yes ]]; then
-        findmnt -e -v -n -o 'OPTIONS' --target "$1" 2>/dev/null && return 0
+        findmnt -e -v -n -o 'OPTIONS' --target "$1" 2> /dev/null && return 0
     fi
 
+    [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
+
     findmnt --fstab -e -v -n -o 'OPTIONS' --target "$1"
 }
 
@@ -438,47 +520,47 @@ find_mp_fsopts() {
 # Echo the filesystem options for a given device.
 # /proc/self/mountinfo is taken as the primary source of information
 # and /etc/fstab is used as a fallback.
+# if `use_fstab == yes`, then only `/etc/fstab` is used.
+#
 # Example:
 # $ find_dev_fsopts /dev/sda2
 # rw,relatime,discard,data=ordered
 find_dev_fsopts() {
-    local _find_dev _opts
+    local _find_dev
     _find_dev="$1"
-    if ! [[ "$_find_dev" = /dev* ]]; then
+    if ! [[ $_find_dev == /dev* ]]; then
         [[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
     fi
 
     if [[ $use_fstab != yes ]]; then
-        findmnt -e -v -n -o 'OPTIONS' --source "$_find_dev" 2>/dev/null && return 0
+        findmnt -e -v -n -o 'OPTIONS' --source "$_find_dev" 2> /dev/null && return 0
     fi
 
+    [[ ! -f "$dracutsysrootdir"/etc/fstab ]] && return 1
+
     findmnt --fstab -e -v -n -o 'OPTIONS' --source "$_find_dev"
 }
 
-
 # finds the major:minor of the block device backing the root filesystem.
 find_root_block_device() { find_block_device /; }
 
 # for_each_host_dev_fs <func>
 # Execute "<func> <dev> <filesystem>" for every "<dev> <fs>" pair found
 # in ${host_fs_types[@]}
-for_each_host_dev_fs()
-{
+for_each_host_dev_fs() {
     local _func="$1"
     local _dev
     local _ret=1
 
     [[ "${#host_fs_types[@]}" ]] || return 2
 
-
     for _dev in "${!host_fs_types[@]}"; do
         $_func "$_dev" "${host_fs_types[$_dev]}" && _ret=0
     done
     return $_ret
 }
 
-host_fs_all()
-{
+host_fs_all() {
     printf "%s\n" "${host_fs_types[@]}"
 }
 
@@ -489,15 +571,15 @@ host_fs_all()
 check_block_and_slaves() {
     local _x
     [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry.
-    if ! lvm_internal_dev $2; then "$1" $2 && return; fi
+    if ! lvm_internal_dev "$2"; then "$1" "$2" && return; fi
     check_vol_slaves "$@" && return 0
-    if [[ -f /sys/dev/block/$2/../dev ]]; then
-        check_block_and_slaves $1 $(<"/sys/dev/block/$2/../dev") && return 0
+    if [[ -f /sys/dev/block/$2/../dev ]] && [[ /sys/dev/block/$2/../subsystem -ef /sys/class/block ]]; then
+        check_block_and_slaves "$1" "$(< "/sys/dev/block/$2/../dev")" && return 0
     fi
-    [[ -d /sys/dev/block/$2/slaves ]] || return 1
-    for _x in /sys/dev/block/$2/slaves/*/dev; do
-        [[ -f $_x ]] || continue
-        check_block_and_slaves $1 $(<"$_x") && return 0
+    for _x in /sys/dev/block/"$2"/slaves/*; do
+        [[ -f $_x/dev ]] || continue
+        [[ $_x/subsystem -ef /sys/class/block ]] || continue
+        check_block_and_slaves "$1" "$(< "$_x/dev")" && return 0
     done
     return 1
 }
@@ -505,54 +587,73 @@ check_block_and_slaves() {
 check_block_and_slaves_all() {
     local _x _ret=1
     [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry.
-    if ! lvm_internal_dev $2 && "$1" $2; then
+    if ! lvm_internal_dev "$2" && "$1" "$2"; then
         _ret=0
     fi
     check_vol_slaves_all "$@" && return 0
-    if [[ -f /sys/dev/block/$2/../dev ]]; then
-        check_block_and_slaves_all $1 $(<"/sys/dev/block/$2/../dev") && _ret=0
+    if [[ -f /sys/dev/block/$2/../dev ]] && [[ /sys/dev/block/$2/../subsystem -ef /sys/class/block ]]; then
+        check_block_and_slaves_all "$1" "$(< "/sys/dev/block/$2/../dev")" && _ret=0
     fi
-    [[ -d /sys/dev/block/$2/slaves ]] || return 1
-    for _x in /sys/dev/block/$2/slaves/*/dev; do
-        [[ -f $_x ]] || continue
-        check_block_and_slaves_all $1 $(<"$_x") && _ret=0
+    for _x in /sys/dev/block/"$2"/slaves/*; do
+        [[ -f $_x/dev ]] || continue
+        [[ $_x/subsystem -ef /sys/class/block ]] || continue
+        check_block_and_slaves_all "$1" "$(< "$_x/dev")" && _ret=0
     done
     return $_ret
 }
 # for_each_host_dev_and_slaves <func>
 # Execute "<func> <dev>" for every "<dev>" found
 # in ${host_devs[@]} and their slaves
-for_each_host_dev_and_slaves_all()
-{
+for_each_host_dev_and_slaves_all() {
     local _func="$1"
     local _dev
     local _ret=1
 
-    [[ "${host_devs[@]}" ]] || return 2
+    [[ "${host_devs[*]}" ]] || return 2
 
     for _dev in "${host_devs[@]}"; do
-        [[ -b "$_dev" ]] || continue
-        if check_block_and_slaves_all $_func $(get_maj_min $_dev); then
+        [[ -b $_dev ]] || continue
+        if check_block_and_slaves_all "$_func" "$(get_maj_min "$_dev")"; then
             _ret=0
         fi
     done
     return $_ret
 }
 
-for_each_host_dev_and_slaves()
-{
+for_each_host_dev_and_slaves() {
     local _func="$1"
     local _dev
 
-    [[ "${host_devs[@]}" ]] || return 2
+    [[ "${host_devs[*]}" ]] || return 2
 
     for _dev in "${host_devs[@]}"; do
-        [[ -b "$_dev" ]] || continue
-        check_block_and_slaves $_func $(get_maj_min $_dev) && return 0
+        [[ -b $_dev ]] || continue
+        check_block_and_slaves "$_func" "$(get_maj_min "$_dev")" && return 0
     done
     return 1
 }
 
+# /sys/dev/block/major:minor is symbol link to real hardware device
+# go downstream $(realpath /sys/dev/block/major:minor) to detect driver
+get_blockdev_drv_through_sys() {
+    local _block_mods=""
+    local _path
+
+    _path=$(realpath "$1")
+    while true; do
+        if [[ -L "$_path"/driver/module ]]; then
+            _mod=$(realpath "$_path"/driver/module)
+            _mod=$(basename "$_mod")
+            _block_mods="$_block_mods $_mod"
+        fi
+        _path=$(dirname "$_path")
+        if [[ $_path == '/sys/devices' ]] || [[ $_path == '/' ]]; then
+            break
+        fi
+    done
+    echo "$_block_mods"
+}
+
 # ugly workaround for the lvm design
 # There is no volume group device,
 # so, there are no slave devices for volume groups.
@@ -560,50 +661,43 @@ for_each_host_dev_and_slaves()
 # but you cannot create the logical volume without the volume group.
 # And the volume group might be bigger than the devices the LV needs.
 check_vol_slaves() {
-    local _lv _vg _pv _dm
-    for i in /dev/mapper/*; do
-        [[ $i == /dev/mapper/control ]] && continue
-        _lv=$(get_maj_min $i)
-        _dm=/sys/dev/block/$_lv/dm
-        [[ -f $_dm/uuid  && $(<$_dm/uuid) =~ LVM-* ]] || continue
-        if [[ $_lv = $2 ]]; then
-            _vg=$(lvm lvs --noheadings -o vg_name $i 2>/dev/null)
-            # strip space
-            _vg="${_vg//[[:space:]]/}"
-            if [[ $_vg ]]; then
-                for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2>/dev/null)
-                do
-                    check_block_and_slaves $1 $(get_maj_min $_pv) && return 0
-                done
-            fi
-        fi
-    done
+    local _vg _pv _dm _majmin
+    _majmin="$2"
+    _dm=/sys/dev/block/$_majmin/dm
+    [[ -f $_dm/uuid && $(< "$_dm"/uuid) =~ LVM-* ]] || return 1
+    _vg=$(dmsetup splitname --noheadings -o vg_name "$(< "$_dm/name")")
+    # strip space
+    _vg="${_vg//[[:space:]]/}"
+    if [[ $_vg ]]; then
+        for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2> /dev/null); do
+            check_block_and_slaves "$1" "$(get_maj_min "$_pv")" && return 0
+        done
+    fi
     return 1
 }
 
 check_vol_slaves_all() {
-    local _lv _vg _pv
-    for i in /dev/mapper/*; do
-        [[ $i == /dev/mapper/control ]] && continue
-        _lv=$(get_maj_min $i)
-        if [[ $_lv = $2 ]]; then
-            _vg=$(lvm lvs --noheadings -o vg_name $i 2>/dev/null)
-            # strip space
-            _vg="${_vg//[[:space:]]/}"
-            if [[ $_vg ]]; then
-                for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2>/dev/null)
-                do
-                    check_block_and_slaves_all $1 $(get_maj_min $_pv)
-                done
-                return 0
-            fi
+    local _vg _pv _majmin
+    _majmin="$2"
+    _dm="/sys/dev/block/$_majmin/dm"
+    [[ -f $_dm/uuid && $(< "$_dm"/uuid) =~ LVM-* ]] || return 1
+    _vg=$(dmsetup splitname --noheadings -o vg_name "$(< "$_dm/name")")
+    # strip space
+    _vg="${_vg//[[:space:]]/}"
+    if [[ $_vg ]]; then
+        # when filter/global_filter is set, lvm may be failed
+        if ! lvm lvs --noheadings -o vg_name "$_vg" 2> /dev/null 1> /dev/null; then
+            return 1
         fi
-    done
+
+        for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2> /dev/null); do
+            check_block_and_slaves_all "$1" "$(get_maj_min "$_pv")"
+        done
+        return 0
+    fi
     return 1
 }
 
-
-
 # fs_get_option <filesystem options> <search for option>
 # search for a specific option in a bunch of filesystem options
 # and return the value
@@ -612,39 +706,44 @@ fs_get_option() {
     local _option=$2
     local OLDIFS="$IFS"
     IFS=,
+    # shellcheck disable=SC2086
     set -- $_fsopts
     IFS="$OLDIFS"
     while [ $# -gt 0 ]; do
         case $1 in
             $_option=*)
-                echo ${1#${_option}=}
+                echo "${1#"${_option}"=}"
                 break
+                ;;
         esac
         shift
     done
 }
 
-check_kernel_config()
-{
+check_kernel_config() {
     local _config_opt="$1"
     local _config_file
-    [[ -f /boot/config-$kernel ]] \
+    [[ -f $dracutsysrootdir/boot/config-$kernel ]] \
         && _config_file="/boot/config-$kernel"
-    [[ -f /lib/modules/$kernel/config ]] \
+    [[ -f $dracutsysrootdir/lib/modules/$kernel/config ]] \
         && _config_file="/lib/modules/$kernel/config"
 
     # no kernel config file, so return true
     [[ $_config_file ]] || return 0
 
-    grep -q -F "${_config_opt}=" "$_config_file" && return 0
+    grep -q -F "${_config_opt}=" "$dracutsysrootdir$_config_file" && return 0
     return 1
 }
 
+# 0 if the kernel module is either built-in or available
+# 1 if the kernel module is not enabled
+check_kernel_module() {
+    modprobe -d "$dracutsysrootdir" -S "$kernel" --dry-run "$1" &> /dev/null || return 1
+}
 
 # get_cpu_vendor
 # Only two values are returned: AMD or Intel
-get_cpu_vendor ()
-{
+get_cpu_vendor() {
     if grep -qE AMD /proc/cpuinfo; then
         printf "AMD"
     fi
@@ -655,25 +754,24 @@ get_cpu_vendor ()
 
 # get_host_ucode
 # Get the hosts' ucode file based on the /proc/cpuinfo
-get_ucode_file ()
-{
-    local family=`grep -E "cpu family" /proc/cpuinfo | head -1 | sed s/.*:\ //`
-    local model=`grep -E "model" /proc/cpuinfo |grep -v name | head -1 | sed s/.*:\ //`
-    local stepping=`grep -E "stepping" /proc/cpuinfo | head -1 | sed s/.*:\ //`
+get_ucode_file() {
+    local family
+    local model
+    local stepping
+    family=$(grep -E "cpu family" /proc/cpuinfo | head -1 | sed "s/.*:\ //")
+    model=$(grep -E "model" /proc/cpuinfo | grep -v name | head -1 | sed "s/.*:\ //")
+    stepping=$(grep -E "stepping" /proc/cpuinfo | head -1 | sed "s/.*:\ //")
 
     if [[ "$(get_cpu_vendor)" == "AMD" ]]; then
-        # If family greater than or equal to 0x16
-        if [[ $family -ge 22 ]]; then
-            printf "microcode_amd_fam16h.bin"
-        elif [[ $family -eq 21 ]]; then
-            printf "microcode_amd_fam15h.bin"
+        if [[ $family -ge 21 ]]; then
+            printf "microcode_amd_fam%xh.bin" "$family"
         else
             printf "microcode_amd.bin"
         fi
     fi
     if [[ "$(get_cpu_vendor)" == "Intel" ]]; then
         # The /proc/cpuinfo are in decimal.
-        printf "%02x-%02x-%02x" ${family} ${model} ${stepping}
+        printf "%02x-%02x-%02x" "${family}" "${model}" "${stepping}"
     fi
 }
 
@@ -681,9 +779,9 @@ get_ucode_file ()
 # If it is an LVM device, touch only devices which have /dev/VG/LV symlink.
 lvm_internal_dev() {
     local dev_dm_dir=/sys/dev/block/$1/dm
-    [[ ! -f $dev_dm_dir/uuid || $(<$dev_dm_dir/uuid) != LVM-* ]] && return 1 # Not an LVM device
+    [[ ! -f $dev_dm_dir/uuid || $(< "$dev_dm_dir"/uuid) != LVM-* ]] && return 1 # Not an LVM device
     local DM_VG_NAME DM_LV_NAME DM_LV_LAYER
-    eval $(dmsetup splitname --nameprefixes --noheadings --rows "$(<$dev_dm_dir/name)" 2>/dev/null)
+    eval "$(dmsetup splitname --nameprefixes --noheadings --rows "$(< "$dev_dm_dir"/name)" 2> /dev/null)"
     [[ ${DM_VG_NAME} ]] && [[ ${DM_LV_NAME} ]] || return 0 # Better skip this!
     [[ ${DM_LV_LAYER} ]] || [[ ! -L /dev/${DM_VG_NAME}/${DM_LV_NAME} ]]
 }
@@ -691,9 +789,296 @@ lvm_internal_dev() {
 btrfs_devs() {
     local _mp="$1"
     btrfs device usage "$_mp" \
-        | while read _dev _rest; do
-        str_starts "$_dev" "/" || continue
-        _dev=${_dev%,}
-        printf -- "%s\n" "$_dev"
+        | while read -r _dev _; do
+            str_starts "$_dev" "/" || continue
+            _dev=${_dev%,}
+            printf -- "%s\n" "$_dev"
+        done
+}
+
+zfs_devs() {
+    local _mp="$1"
+    zpool list -H -v -P "${_mp%%/*}" | awk -F$'\t' '$2 ~ /^\// {print $2}' \
+        | while read -r _dev; do
+            realpath "${_dev}"
         done
 }
+
+iface_for_remote_addr() {
+    # shellcheck disable=SC2046
+    set -- $(ip -o route get to "$1")
+    while [ $# -gt 0 ]; do
+        case $1 in
+            dev)
+                echo "$2"
+                return
+                ;;
+        esac
+        shift
+    done
+}
+
+local_addr_for_remote_addr() {
+    # shellcheck disable=SC2046
+    set -- $(ip -o route get to "$1")
+    while [ $# -gt 0 ]; do
+        case $1 in
+            src)
+                echo "$2"
+                return
+                ;;
+        esac
+        shift
+    done
+}
+
+peer_for_addr() {
+    local addr=$1
+    local qtd
+
+    # quote periods in IPv4 address
+    qtd=${addr//./\\.}
+    ip -o addr show \
+        | sed -n 's%^.* '"$qtd"' peer \([0-9a-f.:]\{1,\}\(/[0-9]*\)\?\).*$%\1%p'
+}
+
+netmask_for_addr() {
+    local addr=$1
+    local qtd
+
+    # quote periods in IPv4 address
+    qtd=${addr//./\\.}
+    ip -o addr show | sed -n 's,^.* '"$qtd"'/\([0-9]*\) .*$,\1,p'
+}
+
+gateway_for_iface() {
+    local ifname=$1 addr=$2
+
+    case $addr in
+        *.*) proto=4 ;;
+        *:*) proto=6 ;;
+        *) return ;;
+    esac
+    ip -o -$proto route show \
+        | sed -n "s/^default via \([0-9a-z.:]\{1,\}\) dev $ifname .*\$/\1/p"
+}
+
+# This works only for ifcfg-style network configuration!
+bootproto_for_iface() {
+    local ifname=$1
+    local dir
+
+    # follow ifcfg settings for boot protocol
+    for dir in network-scripts network; do
+        [ -f "/etc/sysconfig/$dir/ifcfg-$ifname" ] && {
+            sed -n "s/BOOTPROTO=[\"']\?\([[:alnum:]]\{1,\}\)[\"']\?.*\$/\1/p" \
+                "/etc/sysconfig/$dir/ifcfg-$ifname"
+            return
+        }
+    done
+}
+
+is_unbracketed_ipv6_address() {
+    strglob "$1" '*:*' && ! strglob "$1" '\[*:*\]'
+}
+
+# Create an ip= string to set up networking such that the given
+# remote address can be reached
+ip_params_for_remote_addr() {
+    local remote_addr=$1
+    local ifname local_addr peer netmask gateway ifmac
+
+    [[ $remote_addr ]] || return 1
+    ifname=$(iface_for_remote_addr "$remote_addr")
+    [[ $ifname ]] || {
+        berror "failed to determine interface to connect to $remote_addr"
+        return 1
+    }
+
+    # ifname clause to bind the interface name to a MAC address
+    if [ -d "/sys/class/net/$ifname/bonding" ]; then
+        dinfo "Found bonded interface '${ifname}'. Make sure to provide an appropriate 'bond=' cmdline."
+    elif [ -e "/sys/class/net/$ifname/address" ]; then
+        ifmac=$(cat "/sys/class/net/$ifname/address")
+        [[ $ifmac ]] && printf 'ifname=%s:%s ' "${ifname}" "${ifmac}"
+    fi
+
+    bootproto=$(bootproto_for_iface "$ifname")
+    case $bootproto in
+        dhcp | dhcp6 | auto6) ;;
+        dhcp4)
+            bootproto=dhcp
+            ;;
+        static* | "")
+            bootproto=
+            ;;
+        *)
+            derror "bootproto \"$bootproto\" is unsupported by dracut, trying static configuration"
+            bootproto=
+            ;;
+    esac
+    if [[ $bootproto ]]; then
+        printf 'ip=%s:%s ' "${ifname}" "${bootproto}"
+    else
+        local_addr=$(local_addr_for_remote_addr "$remote_addr")
+        [[ $local_addr ]] || {
+            berror "failed to determine local address to connect to $remote_addr"
+            return 1
+        }
+        peer=$(peer_for_addr "$local_addr")
+        # Set peer or netmask, but not both
+        [[ $peer ]] || netmask=$(netmask_for_addr "$local_addr")
+        gateway=$(gateway_for_iface "$ifname" "$local_addr")
+        # Quote IPv6 addresses with brackets
+        is_unbracketed_ipv6_address "$local_addr" && local_addr="[$local_addr]"
+        is_unbracketed_ipv6_address "$peer" && peer="[$peer]"
+        is_unbracketed_ipv6_address "$gateway" && gateway="[$gateway]"
+        printf 'ip=%s:%s:%s:%s::%s:none ' \
+            "${local_addr}" "${peer}" "${gateway}" "${netmask}" "${ifname}"
+    fi
+
+}
+
+# block_is_nbd <maj:min>
+# Check whether $1 is an nbd device
+block_is_nbd() {
+    [[ -b /dev/block/$1 && $1 == 43:* ]]
+}
+
+# block_is_iscsi <maj:min>
+# Check whether $1 is an iSCSI device
+block_is_iscsi() {
+    local _dir
+    local _dev=$1
+    [[ -L "/sys/dev/block/$_dev" ]] || return
+    _dir="$(readlink -f "/sys/dev/block/$_dev")" || return
+    until [[ -d "$_dir/sys" || -d "$_dir/iscsi_session" ]]; do
+        _dir="$_dir/.."
+    done
+    [[ -d "$_dir/iscsi_session" ]]
+}
+
+# block_is_fcoe <maj:min>
+# Check whether $1 is an FCoE device
+# Will not work for HBAs that hide the ethernet aspect
+# completely and present a pure FC device
+block_is_fcoe() {
+    local _dir
+    local _dev=$1
+    [[ -L "/sys/dev/block/$_dev" ]] || return
+    _dir="$(readlink -f "/sys/dev/block/$_dev")"
+    until [[ -d "$_dir/sys" ]]; do
+        _dir="$_dir/.."
+        if [[ -d "$_dir/subsystem" ]]; then
+            subsystem=$(basename "$(readlink "$_dir"/subsystem)")
+            [[ $subsystem == "fcoe" ]] && return 0
+        fi
+    done
+    return 1
+}
+
+# block_is_netdevice <maj:min>
+# Check whether $1 is a net device
+block_is_netdevice() {
+    block_is_nbd "$1" || block_is_iscsi "$1" || block_is_fcoe "$1"
+}
+
+# convert the driver name given by udevadm to the corresponding kernel module name
+get_module_name() {
+    local dev_driver
+    while read -r dev_driver; do
+        case "$dev_driver" in
+            mmcblk)
+                echo "mmc_block"
+                ;;
+            *)
+                echo "$dev_driver"
+                ;;
+        esac
+    done
+}
+
+# get the corresponding kernel modules of a /sys/class/*/* or/dev/* device
+get_dev_module() {
+    local dev_attr_walk
+    local dev_drivers
+    local dev_paths
+    dev_attr_walk=$(udevadm info -a "$1")
+    dev_drivers=$(echo "$dev_attr_walk" \
+        | sed -n 's/\s*DRIVERS=="\(\S\+\)"/\1/p' \
+        | get_module_name)
+
+    # also return modalias info from sysfs paths parsed by udevadm
+    dev_paths=$(echo "$dev_attr_walk" | sed -n 's/.*\(\/devices\/.*\)'\'':/\1/p')
+    local dev_path
+    for dev_path in $dev_paths; do
+        local modalias_file="/sys$dev_path/modalias"
+        if [ -e "$modalias_file" ]; then
+            dev_drivers="$(printf "%s\n%s" "$dev_drivers" "$(cat "$modalias_file")")"
+        fi
+    done
+
+    # if no kernel modules found and device is in a virtual subsystem, follow symlinks
+    if [[ -z $dev_drivers && $(udevadm info -q path "$1") == "/devices/virtual"* ]]; then
+        local dev_vkernel
+        local dev_vsubsystem
+        local dev_vpath
+        dev_vkernel=$(echo "$dev_attr_walk" | sed -n 's/\s*KERNELS=="\(\S\+\)"/\1/p' | tail -1)
+        dev_vsubsystem=$(echo "$dev_attr_walk" | sed -n 's/\s*SUBSYSTEMS=="\(\S\+\)"/\1/p' | tail -1)
+        dev_vpath="/sys/devices/virtual/$dev_vsubsystem/$dev_vkernel"
+        if [[ -n $dev_vkernel && -n $dev_vsubsystem && -d $dev_vpath ]]; then
+            local dev_links
+            local dev_link
+            dev_links=$(find "$dev_vpath" -maxdepth 1 -type l ! -name "subsystem" -exec readlink {} \;)
+            for dev_link in $dev_links; do
+                [[ -n $dev_drivers && ${dev_drivers: -1} != $'\n' ]] && dev_drivers+=$'\n'
+                dev_drivers+=$(udevadm info -a "$dev_vpath/$dev_link" \
+                    | sed -n 's/\s*DRIVERS=="\(\S\+\)"/\1/p' \
+                    | get_module_name \
+                    | grep -v -e pcieport)
+            done
+        fi
+    fi
+    echo "$dev_drivers"
+}
+
+# Check if file is in PE format
+pe_file_format() {
+    if [[ $# -eq 1 ]]; then
+        local magic
+        magic=$(objdump -p "$1" \
+            | awk '{if ($1 == "Magic"){print strtonum("0x"$2)}}')
+        magic=$(printf "0x%x" "$magic")
+        # 0x10b (PE32), 0x20b (PE32+)
+        [[ $magic == 0x20b || $magic == 0x10b ]] && return 0
+    fi
+    return 1
+}
+
+# Get specific data from the PE header
+pe_get_header_data() {
+    local data_header
+    [[ $# -ne "2" ]] && return 1
+    [[ $(pe_file_format "$1") -eq 1 ]] && return 1
+    data_header=$(objdump -p "$1" \
+        | awk -v data="$2" '{if ($1 == data){print $2}}')
+    echo "$data_header"
+}
+
+# Get the SectionAlignment data from the PE header
+pe_get_section_align() {
+    local align_hex
+    [[ $# -ne "1" ]] && return 1
+    align_hex=$(pe_get_header_data "$1" "SectionAlignment")
+    [[ $? -eq 1 ]] && return 1
+    echo "$((16#$align_hex))"
+}
+
+# Get the ImageBase data from the PE header
+pe_get_image_base() {
+    local base_image
+    [[ $# -ne "1" ]] && return 1
+    base_image=$(pe_get_header_data "$1" "ImageBase")
+    [[ $? -eq 1 ]] && return 1
+    echo "$((16#$base_image))"
+}