]> git.ipfire.org Git - thirdparty/dracut.git/blob - dracut-functions.sh
Merge pull request #97 from FGrose/dmsquash-live-root
[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 if [[ "$(ln --help)" == *--relative* ]]; then
171 ln_r() {
172 ln -sfnr "${initdir}/$1" "${initdir}/$2"
173 }
174 else
175 ln_r() {
176 local _source=$1
177 local _dest=$2
178 [[ -d "${_dest%/*}" ]] && _dest=$(readlink -f "${_dest%/*}")/${_dest##*/}
179 ln -sfn -- "$(convert_abs_rel "${_dest}" "${_source}")" "${initdir}/${_dest}"
180 }
181 fi
182
183 # get_fs_env <device>
184 # Get and the ID_FS_TYPE variable from udev for a device.
185 # Example:
186 # $ get_fs_env /dev/sda2
187 # ext4
188 get_fs_env() {
189 local evalstr
190 local found
191
192 [[ $1 ]] || return
193 unset ID_FS_TYPE
194 ID_FS_TYPE=$(blkid -u filesystem -o export -- "$1" \
195 | while read line || [ -n "$line" ]; do
196 if [[ "$line" == TYPE\=* ]]; then
197 printf "%s" "${line#TYPE=}";
198 exit 0;
199 fi
200 done)
201 if [[ $ID_FS_TYPE ]]; then
202 printf "%s" "$ID_FS_TYPE"
203 return 0
204 fi
205 return 1
206 }
207
208 # get_maj_min <device>
209 # Prints the major and minor of a device node.
210 # Example:
211 # $ get_maj_min /dev/sda2
212 # 8:2
213 get_maj_min() {
214 local _maj _min _majmin
215 _majmin="$(stat -L -c '%t:%T' "$1" 2>/dev/null)"
216 printf "%s" "$((0x${_majmin%:*})):$((0x${_majmin#*:}))"
217 }
218
219
220 # get_devpath_block <device>
221 # get the DEVPATH in /sys of a block device
222 get_devpath_block() {
223 local _majmin _i
224 _majmin=$(get_maj_min "$1")
225
226 for _i in /sys/block/*/dev /sys/block/*/*/dev; do
227 [[ -e "$_i" ]] || continue
228 if [[ "$_majmin" == "$(<"$_i")" ]]; then
229 printf "%s" "${_i%/dev}"
230 return 0
231 fi
232 done
233 return 1
234 }
235
236 # get a persistent path from a device
237 get_persistent_dev() {
238 local i _tmp _dev
239
240 _dev=$(get_maj_min "$1")
241 [ -z "$_dev" ] && return
242
243 for i in \
244 /dev/mapper/* \
245 /dev/disk/${persistent_policy:-by-uuid}/* \
246 /dev/disk/by-uuid/* \
247 /dev/disk/by-label/* \
248 /dev/disk/by-partuuid/* \
249 /dev/disk/by-partlabel/* \
250 /dev/disk/by-id/* \
251 /dev/disk/by-path/* \
252 ; do
253 [[ -e "$i" ]] || continue
254 [[ $i == /dev/mapper/control ]] && continue
255 [[ $i == /dev/mapper/mpath* ]] && continue
256 _tmp=$(get_maj_min "$i")
257 if [ "$_tmp" = "$_dev" ]; then
258 printf -- "%s" "$i"
259 return
260 fi
261 done
262 printf -- "%s" "$1"
263 }
264
265 expand_persistent_dev() {
266 local _dev=$1
267
268 case "$_dev" in
269 LABEL=*)
270 _dev="/dev/disk/by-label/${_dev#LABEL=}"
271 ;;
272 UUID=*)
273 _dev="${_dev#UUID=}"
274 _dev="${_dev,,}"
275 _dev="/dev/disk/by-uuid/${_dev}"
276 ;;
277 PARTUUID=*)
278 _dev="${_dev#PARTUUID=}"
279 _dev="${_dev,,}"
280 _dev="/dev/disk/by-partuuid/${_dev}"
281 ;;
282 PARTLABEL=*)
283 _dev="/dev/disk/by-partlabel/${_dev#PARTLABEL=}"
284 ;;
285 esac
286 printf "%s" "$_dev"
287 }
288
289 shorten_persistent_dev() {
290 local _dev="$1"
291 case "$_dev" in
292 /dev/disk/by-uuid/*)
293 printf "%s" "UUID=${_dev##*/}";;
294 /dev/disk/by-label/*)
295 printf "%s" "LABEL=${_dev##*/}";;
296 /dev/disk/by-partuuid/*)
297 printf "%s" "PARTUUID=${_dev##*/}";;
298 /dev/disk/by-partlabel/*)
299 printf "%s" "PARTLABEL=${_dev##*/}";;
300 *)
301 printf "%s" "$_dev";;
302 esac
303 }
304
305 # find_block_device <mountpoint>
306 # Prints the major and minor number of the block device
307 # for a given mountpoint.
308 # Unless $use_fstab is set to "yes" the functions
309 # uses /proc/self/mountinfo as the primary source of the
310 # information and only falls back to /etc/fstab, if the mountpoint
311 # is not found there.
312 # Example:
313 # $ find_block_device /usr
314 # 8:4
315 find_block_device() {
316 local _dev _majmin _find_mpt
317 _find_mpt="$1"
318 if [[ $use_fstab != yes ]]; then
319 [[ -d $_find_mpt/. ]]
320 findmnt -e -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | { \
321 while read _majmin _dev || [ -n "$_dev" ]; do
322 if [[ -b $_dev ]]; then
323 if ! [[ $_majmin ]] || [[ $_majmin == 0:* ]]; then
324 _majmin=$(get_maj_min $_dev)
325 fi
326 if [[ $_majmin ]]; then
327 printf "%s\n" "$_majmin"
328 else
329 printf "%s\n" "$_dev"
330 fi
331 return 0
332 fi
333 if [[ $_dev = *:* ]]; then
334 printf "%s\n" "$_dev"
335 return 0
336 fi
337 done; return 1; } && return 0
338 fi
339 # fall back to /etc/fstab
340
341 findmnt -e --fstab -v -n -o 'MAJ:MIN,SOURCE' --target "$_find_mpt" | { \
342 while read _majmin _dev || [ -n "$_dev" ]; do
343 if ! [[ $_dev ]]; then
344 _dev="$_majmin"
345 unset _majmin
346 fi
347 if [[ -b $_dev ]]; then
348 [[ $_majmin ]] || _majmin=$(get_maj_min $_dev)
349 if [[ $_majmin ]]; then
350 printf "%s\n" "$_majmin"
351 else
352 printf "%s\n" "$_dev"
353 fi
354 return 0
355 fi
356 if [[ $_dev = *:* ]]; then
357 printf "%s\n" "$_dev"
358 return 0
359 fi
360 done; return 1; } && return 0
361
362 return 1
363 }
364
365 # find_mp_fstype <mountpoint>
366 # Echo the filesystem type for a given mountpoint.
367 # /proc/self/mountinfo is taken as the primary source of information
368 # and /etc/fstab is used as a fallback.
369 # No newline is appended!
370 # Example:
371 # $ find_mp_fstype /;echo
372 # ext4
373 find_mp_fstype() {
374 local _fs
375
376 if [[ $use_fstab != yes ]]; then
377 findmnt -e -v -n -o 'FSTYPE' --target "$1" | { \
378 while read _fs || [ -n "$_fs" ]; do
379 [[ $_fs ]] || continue
380 [[ $_fs = "autofs" ]] && continue
381 printf "%s" "$_fs"
382 return 0
383 done; return 1; } && return 0
384 fi
385
386 findmnt --fstab -e -v -n -o 'FSTYPE' --target "$1" | { \
387 while read _fs || [ -n "$_fs" ]; do
388 [[ $_fs ]] || continue
389 [[ $_fs = "autofs" ]] && continue
390 printf "%s" "$_fs"
391 return 0
392 done; return 1; } && return 0
393
394 return 1
395 }
396
397 # find_dev_fstype <device>
398 # Echo the filesystem type for a given device.
399 # /proc/self/mountinfo is taken as the primary source of information
400 # and /etc/fstab is used as a fallback.
401 # No newline is appended!
402 # Example:
403 # $ find_dev_fstype /dev/sda2;echo
404 # ext4
405 find_dev_fstype() {
406 local _find_dev _fs
407 _find_dev="$1"
408 if ! [[ "$_find_dev" = /dev* ]]; then
409 [[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
410 fi
411
412 if [[ $use_fstab != yes ]]; then
413 findmnt -e -v -n -o 'FSTYPE' --source "$_find_dev" | { \
414 while read _fs || [ -n "$_fs" ]; do
415 [[ $_fs ]] || continue
416 [[ $_fs = "autofs" ]] && continue
417 printf "%s" "$_fs"
418 return 0
419 done; return 1; } && return 0
420 fi
421
422 findmnt --fstab -e -v -n -o 'FSTYPE' --source "$_find_dev" | { \
423 while read _fs || [ -n "$_fs" ]; do
424 [[ $_fs ]] || continue
425 [[ $_fs = "autofs" ]] && continue
426 printf "%s" "$_fs"
427 return 0
428 done; return 1; } && return 0
429
430 return 1
431 }
432
433 # find_mp_fsopts <mountpoint>
434 # Echo the filesystem options for a given mountpoint.
435 # /proc/self/mountinfo is taken as the primary source of information
436 # and /etc/fstab is used as a fallback.
437 # No newline is appended!
438 # Example:
439 # $ find_mp_fsopts /;echo
440 # rw,relatime,discard,data=ordered
441 find_mp_fsopts() {
442 if [[ $use_fstab != yes ]]; then
443 findmnt -e -v -n -o 'OPTIONS' --target "$1" 2>/dev/null && return 0
444 fi
445
446 findmnt --fstab -e -v -n -o 'OPTIONS' --target "$1"
447 }
448
449 # find_dev_fsopts <device>
450 # Echo the filesystem options for a given device.
451 # /proc/self/mountinfo is taken as the primary source of information
452 # and /etc/fstab is used as a fallback.
453 # Example:
454 # $ find_dev_fsopts /dev/sda2
455 # rw,relatime,discard,data=ordered
456 find_dev_fsopts() {
457 local _find_dev _opts
458 _find_dev="$1"
459 if ! [[ "$_find_dev" = /dev* ]]; then
460 [[ -b "/dev/block/$_find_dev" ]] && _find_dev="/dev/block/$_find_dev"
461 fi
462
463 if [[ $use_fstab != yes ]]; then
464 findmnt -e -v -n -o 'OPTIONS' --source "$_find_dev" 2>/dev/null && return 0
465 fi
466
467 findmnt --fstab -e -v -n -o 'OPTIONS' --source "$_find_dev"
468 }
469
470
471 # finds the major:minor of the block device backing the root filesystem.
472 find_root_block_device() { find_block_device /; }
473
474 # for_each_host_dev_fs <func>
475 # Execute "<func> <dev> <filesystem>" for every "<dev> <fs>" pair found
476 # in ${host_fs_types[@]}
477 for_each_host_dev_fs()
478 {
479 local _func="$1"
480 local _dev
481 local _ret=1
482
483 [[ "${#host_fs_types[@]}" ]] || return 0
484
485 for _dev in "${!host_fs_types[@]}"; do
486 $_func "$_dev" "${host_fs_types[$_dev]}" && _ret=0
487 done
488 return $_ret
489 }
490
491 host_fs_all()
492 {
493 printf "%s\n" "${host_fs_types[@]}"
494 }
495
496 # Walk all the slave relationships for a given block device.
497 # Stop when our helper function returns success
498 # $1 = function to call on every found block device
499 # $2 = block device in major:minor format
500 check_block_and_slaves() {
501 local _x
502 [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry.
503 if ! lvm_internal_dev $2; then "$1" $2 && return; fi
504 check_vol_slaves "$@" && return 0
505 if [[ -f /sys/dev/block/$2/../dev ]]; then
506 check_block_and_slaves $1 $(<"/sys/dev/block/$2/../dev") && return 0
507 fi
508 [[ -d /sys/dev/block/$2/slaves ]] || return 1
509 for _x in /sys/dev/block/$2/slaves/*/dev; do
510 [[ -f $_x ]] || continue
511 check_block_and_slaves $1 $(<"$_x") && return 0
512 done
513 return 1
514 }
515
516 check_block_and_slaves_all() {
517 local _x _ret=1
518 [[ -b /dev/block/$2 ]] || return 1 # Not a block device? So sorry.
519 if ! lvm_internal_dev $2 && "$1" $2; then
520 _ret=0
521 fi
522 check_vol_slaves "$@" && return 0
523 if [[ -f /sys/dev/block/$2/../dev ]]; then
524 check_block_and_slaves_all $1 $(<"/sys/dev/block/$2/../dev") && _ret=0
525 fi
526 [[ -d /sys/dev/block/$2/slaves ]] || return 1
527 for _x in /sys/dev/block/$2/slaves/*/dev; do
528 [[ -f $_x ]] || continue
529 check_block_and_slaves_all $1 $(<"$_x") && _ret=0
530 done
531 return $_ret
532 }
533 # for_each_host_dev_and_slaves <func>
534 # Execute "<func> <dev>" for every "<dev>" found
535 # in ${host_devs[@]} and their slaves
536 for_each_host_dev_and_slaves_all()
537 {
538 local _func="$1"
539 local _dev
540 local _ret=1
541
542 [[ "${host_devs[@]}" ]] || return 0
543
544 for _dev in "${host_devs[@]}"; do
545 [[ -b "$_dev" ]] || continue
546 if check_block_and_slaves_all $_func $(get_maj_min $_dev); then
547 _ret=0
548 fi
549 done
550 return $_ret
551 }
552
553 for_each_host_dev_and_slaves()
554 {
555 local _func="$1"
556 local _dev
557
558 [[ "${host_devs[@]}" ]] || return 0
559
560 for _dev in "${host_devs[@]}"; do
561 [[ -b "$_dev" ]] || continue
562 check_block_and_slaves $_func $(get_maj_min $_dev) && return 0
563 done
564 return 1
565 }
566
567 # ugly workaround for the lvm design
568 # There is no volume group device,
569 # so, there are no slave devices for volume groups.
570 # Logical volumes only have the slave devices they really live on,
571 # but you cannot create the logical volume without the volume group.
572 # And the volume group might be bigger than the devices the LV needs.
573 check_vol_slaves() {
574 local _lv _vg _pv _dm
575 for i in /dev/mapper/*; do
576 [[ $i == /dev/mapper/control ]] && continue
577 _lv=$(get_maj_min $i)
578 _dm=/sys/dev/block/$_lv/dm
579 [[ -f $_dm/uuid && $(<$_dm/uuid) =~ LVM-* ]] || continue
580 if [[ $_lv = $2 ]]; then
581 _vg=$(lvm lvs --noheadings -o vg_name $i 2>/dev/null)
582 # strip space
583 _vg=$(printf "%s\n" "$_vg")
584 if [[ $_vg ]]; then
585 for _pv in $(lvm vgs --noheadings -o pv_name "$_vg" 2>/dev/null)
586 do
587 check_block_and_slaves $1 $(get_maj_min $_pv) && return 0
588 done
589 fi
590 fi
591 done
592 return 1
593 }
594
595 # fs_get_option <filesystem options> <search for option>
596 # search for a specific option in a bunch of filesystem options
597 # and return the value
598 fs_get_option() {
599 local _fsopts=$1
600 local _option=$2
601 local OLDIFS="$IFS"
602 IFS=,
603 set -- $_fsopts
604 IFS="$OLDIFS"
605 while [ $# -gt 0 ]; do
606 case $1 in
607 $_option=*)
608 echo ${1#${_option}=}
609 break
610 esac
611 shift
612 done
613 }
614
615 check_kernel_config()
616 {
617 local _config_opt="$1"
618 local _config_file
619 [[ -f /boot/config-$kernel ]] \
620 && _config_file="/boot/config-$kernel"
621 [[ -f /lib/modules/$kernel/config ]] \
622 && _config_file="/lib/modules/$kernel/config"
623
624 # no kernel config file, so return true
625 [[ $_config_file ]] || return 0
626
627 grep -q -F "${_config_opt}=" "$_config_file" && return 0
628 return 1
629 }
630
631
632 # get_cpu_vendor
633 # Only two values are returned: AMD or Intel
634 get_cpu_vendor ()
635 {
636 if grep -qE AMD /proc/cpuinfo; then
637 printf "AMD"
638 fi
639 if grep -qE Intel /proc/cpuinfo; then
640 printf "Intel"
641 fi
642 }
643
644 # get_host_ucode
645 # Get the hosts' ucode file based on the /proc/cpuinfo
646 get_ucode_file ()
647 {
648 local family=`grep -E "cpu family" /proc/cpuinfo | head -1 | sed s/.*:\ //`
649 local model=`grep -E "model" /proc/cpuinfo |grep -v name | head -1 | sed s/.*:\ //`
650 local stepping=`grep -E "stepping" /proc/cpuinfo | head -1 | sed s/.*:\ //`
651
652 if [[ "$(get_cpu_vendor)" == "AMD" ]]; then
653 # If family greater or equal than 0x15
654 if [[ $family -ge 21 ]]; then
655 printf "microcode_amd_fam15h.bin"
656 else
657 printf "microcode_amd.bin"
658 fi
659 fi
660 if [[ "$(get_cpu_vendor)" == "Intel" ]]; then
661 # The /proc/cpuinfo are in decimal.
662 printf "%02x-%02x-%02x" ${family} ${model} ${stepping}
663 fi
664 }
665
666 # Not every device in /dev/mapper should be examined.
667 # If it is an LVM device, touch only devices which have /dev/VG/LV symlink.
668 lvm_internal_dev() {
669 local dev_dm_dir=/sys/dev/block/$1/dm
670 [[ ! -f $dev_dm_dir/uuid || $(<$dev_dm_dir/uuid) != LVM-* ]] && return 1 # Not an LVM device
671 local DM_VG_NAME DM_LV_NAME DM_LV_LAYER
672 eval $(dmsetup splitname --nameprefixes --noheadings --rows "$(<$dev_dm_dir/name)" 2>/dev/null)
673 [[ ${DM_VG_NAME} ]] && [[ ${DM_LV_NAME} ]] || return 0 # Better skip this!
674 [[ ${DM_LV_LAYER} ]] || [[ ! -L /dev/${DM_VG_NAME}/${DM_LV_NAME} ]]
675 }
676
677 btrfs_devs() {
678 local _mp="$1"
679 btrfs device usage "$_mp" \
680 | while read _dev _rest; do
681 str_starts "$_dev" "/" || continue
682 _dev=${_dev%,}
683 printf -- "%s\n" "$_dev"
684 done
685 }