# functions used by dracut and other tools.
# Copyright 2005-2009 Red Hat, Inc. All rights reserved.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Check whether $1 is a function.
[[ "$(type -t "$1")" = "function" ]]
# Generic substring function. If $2 is in $1, return 0.
strstr() { [[ $1 = *"$2"* ]]; }
# Generic glob matching function. If glob pattern $2 matches anywhere in $1, OK
strglobin() { [[ $1 = *$2* ]]; }
# Generic glob matching function. If glob pattern $2 matches all of $1, OK
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" ]; }
# find a binary. If we were not passed the full path directly,
# search in the usual places to find the binary.
if [[ -z ${1##/*} ]]; then
if [[ -x $1 ]] || { [[ "$1" == *.so* ]] && ldd "$1" &>/dev/null; }; then
ldconfig -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.
# $2 = comparision op (gt, ge, eq, le, lt, ne)
local _n1=(${1//./ }) _op=$2 _n2=(${3//./ }) _i _res
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
# Create all subdirectories for given path without creating the last element.
[[ -e ${1%/*} ]] || mkdir -m 0755 -p -- "${1%/*}"
# Function prints global variables in format name=value line by line.
# $@ = list of global variables' name
eval printf -v _value "%s" \""\$$_var"\"
[[ ${_value} ]] && printf '%s="%s"\n' "$_var" "$_value"
# Prints the normalized path, where it removes any duplicated
# $ normalize_path ///test/test//
# convert_abs_rel <from> <to>
# Prints the relative path, when creating a symlink to <to> from <from>.
# $ convert_abs_rel /usr/bin/test /bin/test-2
# $ ln -s $(convert_abs_rel /usr/bin/test /bin/test-2) /usr/bin/test
local __current __absolute __abssize __cursize __newpath
set -- "$(normalize_path "$1")" "$(normalize_path "$2")"
# corner case #1 - self looping link
[[ "$1" == "$2" ]] && { printf "%s\n" "${1##*/}"; return; }
# corner case #2 - own dir link
[[ "${1%/*}" == "$2" ]] && { printf ".\n"; return; }
__abssize=${#__absolute[@]}
__cursize=${#__current[@]}
while [[ "${__absolute[__level]}" == "${__current[__level]}" ]]
if (( __level > __abssize || __level > __cursize ))
for ((__i = __level; __i < __cursize-1; __i++))
for ((__i = __level; __i < __abssize; __i++))
__newpath=$__newpath${__absolute[__i]}
printf "%s\n" "$__newpath"
# Get and the ID_FS_TYPE variable from udev for a device.
ID_FS_TYPE=$(blkid -u filesystem -o export -- "$1" \
| while read line || [ -n "$line" ]; do
if [[ "$line" == TYPE\=* ]]; then
printf "%s" "${line#TYPE=}";
if [[ $ID_FS_TYPE ]]; then
printf "%s" "$ID_FS_TYPE"
# Prints the major and minor of a device node.
# $ get_maj_min /dev/sda2
if [[ $get_maj_min_cache_file ]]; then
_out="$(grep -m1 -oP "^$1 \K\S+$" "$get_maj_min_cache_file")"
_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"
# get_devpath_block <device>
# get the DEVPATH in /sys of a block device
_majmin=$(get_maj_min "$1")
for _i in /sys/block/*/dev /sys/block/*/*/dev; do
[[ -e "$_i" ]] || continue
if [[ "$_majmin" == "$(<"$_i")" ]]; then
# get a persistent path from a device
if [[ -n "$persistent_policy" ]]; then
_pol="/dev/disk/${persistent_policy}/*"
/dev/disk/by-partuuid/* \
/dev/disk/by-partlabel/* \
[[ -e "$i" ]] || continue
[[ $i == /dev/mapper/control ]] && continue
[[ $i == /dev/mapper/mpath* ]] && continue
if [ "$_tmp" = "$_dev" ]; then
expand_persistent_dev() {
_dev="/dev/disk/by-label/${_dev#LABEL=}"
_dev="/dev/disk/by-uuid/${_dev}"
_dev="/dev/disk/by-partuuid/${_dev}"
_dev="/dev/disk/by-partlabel/${_dev#PARTLABEL=}"
shorten_persistent_dev() {
printf "%s" "UUID=${_dev##*/}";;
printf "%s" "LABEL=${_dev##*/}";;
printf "%s" "PARTUUID=${_dev##*/}";;
/dev/disk/by-partlabel/*)
printf "%s" "PARTLABEL=${_dev##*/}";;
# find_block_device <mountpoint>
# Prints the major and minor number of the block device
# for a given mountpoint.
# Unless $use_fstab is set to "yes" the functions
# uses /proc/self/mountinfo as the primary source of the
# information and only falls back to /etc/fstab, if the mountpoint
# $ find_block_device /usr
local _dev _majmin _find_mpt
if [[ $use_fstab != yes ]]; then
findmnt -e -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | { \
while read _majmin _dev || [ -n "$_dev" ]; do
if ! [[ $_majmin ]] || [[ $_majmin == 0:* ]]; then
_majmin=$(get_maj_min $_dev)
if [[ $_dev = *:* ]]; then
done; return 1; } && return 0
# fall back to /etc/fstab
findmnt -e --fstab -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | { \
while read _majmin _dev || [ -n "$_dev" ]; do
[[ $_majmin ]] || _majmin=$(get_maj_min $_dev)
if [[ $_dev = *:* ]]; then
done; return 1; } && return 0
# find_mp_fstype <mountpoint>
# Echo the filesystem type for a given mountpoint.
# /proc/self/mountinfo is taken as the primary source of information
# and /etc/fstab is used as a fallback.
# No newline is appended!
# $ find_mp_fstype /;echo
if [[ $use_fstab != yes ]]; then
findmnt -e -v -n -o 'FSTYPE' --target "$1" | { \
while read _fs || [ -n "$_fs" ]; do
[[ $_fs = "autofs" ]] && continue
done; return 1; } && return 0
findmnt --fstab -e -v -n -o 'FSTYPE' --target "$1" | { \
while read _fs || [ -n "$_fs" ]; do
[[ $_fs = "autofs" ]] && continue
done; return 1; } && return 0
# find_dev_fstype <device>
# Echo the filesystem type for a given device.
# /proc/self/mountinfo is taken as the primary source of information
# and /etc/fstab is used as a fallback.
# No newline is appended!
# $ find_dev_fstype /dev/sda2;echo
if ! [[ "$_find_dev" = /dev* ]]; then
[[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
if [[ $use_fstab != yes ]]; then
findmnt -e -v -n -o 'FSTYPE' --source "$_find_dev" | { \
while read _fs || [ -n "$_fs" ]; do
[[ $_fs = "autofs" ]] && continue
done; return 1; } && return 0
findmnt --fstab -e -v -n -o 'FSTYPE' --source "$_find_dev" | { \
while read _fs || [ -n "$_fs" ]; do
[[ $_fs = "autofs" ]] && continue
done; return 1; } && return 0
# find_mp_fsopts <mountpoint>
# Echo the filesystem options for a given mountpoint.
# /proc/self/mountinfo is taken as the primary source of information
# and /etc/fstab is used as a fallback.
# No newline is appended!
# $ find_mp_fsopts /;echo
# rw,relatime,discard,data=ordered
if [[ $use_fstab != yes ]]; then
findmnt -e -v -n -o 'OPTIONS' --target "$1" 2>/dev/null && return 0
findmnt --fstab -e -v -n -o 'OPTIONS' --target "$1"
# find_dev_fsopts <device>
# 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.
# $ find_dev_fsopts /dev/sda2
# rw,relatime,discard,data=ordered
if ! [[ "$_find_dev" = /dev* ]]; then
[[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
if [[ $use_fstab != yes ]]; then
findmnt -e -v -n -o 'OPTIONS' --source "$_find_dev" 2>/dev/null && return 0
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
[[ "${#host_fs_types[@]}" ]] || return 2
for _dev in "${!host_fs_types[@]}"; do
$_func "$_dev" "${host_fs_types[$_dev]}" && _ret=0
printf "%s\n" "${host_fs_types[@]}"