diff --git a/dracut/99kdumpbase/module-setup.sh b/dracut/99kdumpbase/module-setup.sh index 4dc11ac6..32819ab5 100755 --- a/dracut/99kdumpbase/module-setup.sh +++ b/dracut/99kdumpbase/module-setup.sh @@ -683,16 +683,21 @@ kdump_install_pre_post_conf() { } default_dump_target_install_conf() { - local _target _fstype + local _target _fstype _subvol _options local _mntpoint _save_path is_user_configured_dump_target && return _save_path=$(get_bind_mount_source "$(get_save_path)") _target=$(get_target_from_path "$_save_path") - _mntpoint=$(get_mntpoint_from_target "$_target") - + _options=$(get_mount_info OPTIONS target "$_save_path" -f) _fstype=$(get_fs_type_from_target "$_target") + if [[ $_fstype == btrfs ]]; then + _subvol=$(get_btrfs_subvol_from_mntopt "$_options") + fi + + _mntpoint=$(get_mntpoint_from_target "$_target" "$_subvol") + if is_fs_type_nfs "$_fstype"; then kdump_collect_netif_usage "$_target" _fstype="nfs" diff --git a/kdump-lib-initramfs.sh b/kdump-lib-initramfs.sh index c4bc6d2e..39e52a6f 100755 --- a/kdump-lib-initramfs.sh +++ b/kdump-lib-initramfs.sh @@ -110,15 +110,29 @@ get_fs_type_from_target() get_mntpoint_from_target() { - # get the first TARGET when SOURCE doesn't end with ]. - # In most cases, a SOURCE ends with ] when fsroot or subvol exists. - _mntpoint=$(get_mount_info TARGET,SOURCE source "$1" | grep -v "\]$" | awk 'NR==1 { print $1 }') - + _subvol="$2" + if [ -z "$_subvol" ]; then + # get the first TARGET when SOURCE doesn't end with ]. + # In most cases, a SOURCE ends with ] when fsroot or subvol exists. + _mntpoint=$(get_mount_info TARGET,SOURCE source "$1" | grep -v "\]$" | awk 'NR==1 { print $1 }') + else + # btrfs with subvol + _mntpoint=$(get_mount_info TARGET,SOURCE source "$1" | grep "\[$_subvol\]$" | awk 'NR==1 { print $1 }') + fi # fallback to the old way when _mntpoint is empty. [ -n "$_mntpoint" ] || _mntpoint=$(get_mount_info TARGET source "$1" -f) echo "$_mntpoint" } +get_btrfs_subvol_from_mntopt() +{ + _subvol=${1#*subvol=} + # mount option may not contain subvol + [ "$1" != "$_subvol" ] || return 0 + _subvol=${_subvol%%,*} + echo "$_subvol" +} + is_ssh_dump_target() { kdump_get_conf_val ssh | grep -q @ diff --git a/kdump-lib.sh b/kdump-lib.sh index 232d065f..4196b0e5 100755 --- a/kdump-lib.sh +++ b/kdump-lib.sh @@ -177,7 +177,7 @@ get_kdump_targets() # part is the bind mounted directory which quotes by bracket "[]". get_bind_mount_source() { - local _mnt _path _src _opt _fstype + local _mnt _path _src _opt _fstype _subvol local _fsroot _src_nofsroot _mnt=$(df "$1" | tail -1 | awk '{print $NF}') @@ -200,15 +200,13 @@ get_bind_mount_source() _fsroot=${_src#"${_src_nofsroot}"[} _fsroot=${_fsroot%]} - _mnt=$(get_mntpoint_from_target "$_src_nofsroot") - # for btrfs, _fsroot will also contain the subvol value as well, strip it if [[ $_fstype == btrfs ]]; then - local _subvol - _subvol=${_opt#*subvol=} - _subvol=${_subvol%,*} + _subvol=$(get_btrfs_subvol_from_mntopt "$_opt") _fsroot=${_fsroot#"$_subvol"} fi + _mnt=$(get_mntpoint_from_target "$_src_nofsroot" "$_subvol") + echo "$_mnt$_fsroot$_path" } @@ -219,11 +217,12 @@ get_mntopt_from_target() # Get the path where the target will be mounted in kdump kernel # $1: kdump target device +# $2: btrfs subvol get_kdump_mntpoint_from_target() { local _mntpoint - _mntpoint=$(get_mntpoint_from_target "$1") + _mntpoint=$(get_mntpoint_from_target "$1" "$2") # mount under /sysroot if dump to root disk or mount under # mount under /kdumproot if dump target is not mounted in first kernel # mount under /kdumproot/$_mntpoint in other cases in 2nd kernel. diff --git a/mkdumprd b/mkdumprd index e0392da9..06174de1 100644 --- a/mkdumprd +++ b/mkdumprd @@ -76,10 +76,13 @@ to_mount() { local _target=$1 _fstype=$2 _options=$3 _sed_cmd _new_mntpoint _pdev - _new_mntpoint=$(get_kdump_mntpoint_from_target "$_target") _fstype="${_fstype:-$(get_fs_type_from_target "$_target")}" _options="${_options:-$(get_mntopt_from_target "$_target")}" _options="${_options:-defaults}" + if [[ $_fstype == btrfs ]]; then + _subvol=$(get_btrfs_subvol_from_mntopt "$_options") + fi + _new_mntpoint=$(get_kdump_mntpoint_from_target "$_target" "$_subvol") if [[ $_fstype == "nfs"* ]]; then _pdev=$_target @@ -146,7 +149,7 @@ mkdir_save_path_ssh() #$1=dump target get_fs_size() { - df --output=avail "$(get_mntpoint_from_target "$1")/$SAVE_PATH" | tail -1 + df --output=avail "$(get_mntpoint_from_target "$1" "$2")/$SAVE_PATH" | tail -1 } #Function: get_raw_size @@ -159,6 +162,7 @@ get_raw_size() #Function: check_size #$1: dump type string ('raw', 'fs', 'ssh') #$2: dump target +#$3: btrfs subvol check_size() { local avail memtotal @@ -172,7 +176,7 @@ check_size() avail=$(get_ssh_size "$2") ;; fs) - avail=$(get_fs_size "$2") + avail=$(get_fs_size "$2" "$3") ;; *) return @@ -302,20 +306,24 @@ add_mount() #handle the case user does not specify the dump target explicitly handle_default_dump_target() { - local _target - local _mntpoint + local _target _mntpoint _fstype _subvol _options is_user_configured_dump_target && return check_save_path_fs "$SAVE_PATH" _save_path=$(get_bind_mount_source "$SAVE_PATH") + _options=$(get_mount_info OPTIONS target "$_save_path" -f) _target=$(get_target_from_path "$_save_path") - _mntpoint=$(get_mntpoint_from_target "$_target") + _fstype=$(get_fs_type_from_target "$_target") + if [[ $_fstype == btrfs ]]; then + _subvol=$(get_btrfs_subvol_from_mntopt "$_options") + fi + _mntpoint=$(get_mntpoint_from_target "$_target" "$_subvol") SAVE_PATH=${_save_path##"$_mntpoint"} - add_mount "$_target" - check_size fs "$_target" + add_mount "$_target" "$_fstype" "$_options" + check_size fs "$_target" "$_subvol" } # firstly get right SSH_KEY_LOCATION diff --git a/spec/kdump-lib-initramfs_spec.sh b/spec/kdump-lib-initramfs_spec.sh index acabc2ff..d12b758d 100644 --- a/spec/kdump-lib-initramfs_spec.sh +++ b/spec/kdump-lib-initramfs_spec.sh @@ -67,6 +67,12 @@ Describe 'kdump-lib-initramfs' else printf '/var %s[/ostree/deploy/default/var]\n/sysroot %s\n' "$7" "$7" fi + elif [[ "$7" == '/dev/nvme0n1p3' ]]; then + if [[ "$8" == "-f" ]]; then + printf '/ /dev/nvme0n1p3[/root]\n' + else + printf '/ /dev/nvme0n1p3[/root]\n/var /dev/nvme0n1p3[/var]\n' + fi fi } @@ -76,19 +82,51 @@ Describe 'kdump-lib-initramfs' # - IPv6 NFS target also contain '[' in the export # - local dumping target that has '[' in the name # - has bind mint + # - has subvol Parameters - 'eng.redhat.com:/srv/[nfs]' '/mnt' - '[2620:52:0:a1:217:38ff:fe01:131]:/srv/[nfs]' '/mnt' - '/dev/mapper/rhel[disk]' '/' - '/dev/vda4' '/sysroot' + 'eng.redhat.com:/srv/[nfs]' '' '/mnt' + '[2620:52:0:a1:217:38ff:fe01:131]:/srv/[nfs]' '' '/mnt' + '/dev/mapper/rhel[disk]' '' '/' + '/dev/vda4' '' '/sysroot' + '/dev/nvme0n1p3' '/var' '/var' End It 'should handle all cases correctly' - When call get_mntpoint_from_target "$1" - The output should equal "$2" + When call get_mntpoint_from_target "$1" "$2" + The output should equal "$3" End End End + Describe 'Test get_btrfs_subvol_from_mntopt' + Context 'Given different mount option formats' + # Test the following cases: + # - Standard subvol option at the beginning + # - Standard subvol option in the middle + # - Standard subvol option at the end + # - Subvol with @ prefix (common btrfs pattern) + # - Subvol with complex path + # - No subvol option present + # - Empty mount options + # - Subvol option with no value + # - Similar but different option names + Parameters + "subvol=/home,rw,relatime" "/home" + "rw,subvol=/root,defaults" "/root" + "rw,relatime,subvol=/root" "/root" + "subvol=@home" "@home" + "subvol=/path/to/subvolume,rw" "/path/to/subvolume" + "rw,relatime,defaults" "" + true '' + "subvol=,rw" "" + "rw,subvolume=@home" "" + End + + It 'should extract subvolume correctly' + When call get_btrfs_subvol_from_mntopt "$1" + The output should equal "$2" + End + End + End End diff --git a/spec/kdump-lib_spec.sh b/spec/kdump-lib_spec.sh index 41755a40..8ced9172 100644 --- a/spec/kdump-lib_spec.sh +++ b/spec/kdump-lib_spec.sh @@ -133,4 +133,52 @@ Describe 'kdump-lib' End End + Describe "get_kdump_mntpoint_from_target with btrfs subvolume support" + # Mock get_mntpoint_from_target to simulate different mount scenarios + get_mntpoint_from_target() { + local device="$1" subvol="$2" + case "$device" in + '/dev/sda1') + if [[ "$subvol" == "@home" ]]; then + echo "/mnt/btrfs" + elif [[ "$subvol" == "/root" ]]; then + echo "/" + else + echo "/" + fi + ;; + '/dev/sdb1') + if [[ "$subvol" == "@backup" ]]; then + echo "/backup" + else + echo "/backup" + fi + ;; + *) + echo "/" + ;; + esac + } + + Context 'Given various mount point scenarios with subvolumes' + # Test the following cases: + # - Root filesystem with @ subvolume -> /sysroot + # - Home subvolume on non-root -> /kdumproot/mnt/btrfs + # - Backup subvolume -> /kdumproot/backup + # - Regular filesystem without subvolume -> /sysroot + Parameters + '/dev/sda1 /root /sysroot' + '/dev/sda1 @home /kdumproot/mnt/btrfs' + '/dev/sdb1 @backup /kdumproot/backup' + '/dev/vda2 "" /sysroot' + End + + It 'should handle btrfs subvolumes correctly' + # Parse parameters: device subvol expected_result + read -r device subvol expected_result <<< "$1" + When call get_kdump_mntpoint_from_target "$device" "$subvol" + The output should equal "$expected_result" + End + End + End End