Created
August 22, 2024 05:16
-
-
Save arrjay/f957ab0a50939a88d9c435cbedae1e26 to your computer and use it in GitHub Desktop.
dracut module to rewrite a partition table pre-mount
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/bash | |
| # called by dracut | |
| check() { | |
| require_binaries awk gdisk lsblk mknod parted partx sgdisk sfdisk || return 1 | |
| return 0 | |
| } | |
| # called by dracut | |
| depends() { | |
| return 0 | |
| } | |
| # called by dracut | |
| installkernel() { | |
| return 0 | |
| } | |
| # lsblk -A -e 1,2,11 -d -P -o name,pttype | |
| # called by dracut | |
| install() { | |
| inst_multiple awk gdisk lsblk mknod parted partx sgdisk sfdisk | |
| inst_hook cmdline za "$moddir/parse-cmdline-resizer.sh" | |
| inst_hook initqueue/finished za "$moddir/resizer.sh" | |
| dracut_need_initqueue | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/sh | |
| type getargbool > /dev/null 2>&1 || . /lib/dracut-lib.sh | |
| if getargbool 0 rd.growpart ; then | |
| [ -z "$root" ] && root=$(getarg root=) | |
| targetpart=$(getargs rd.growpart=) | |
| case "${targetpart}" in | |
| # we should not be here, but good to write out. | |
| 0|no|off) targetpart="" ;; | |
| # determine partition to resize from root partition spec if possible. | |
| 1|yes|on) targetpart="AUTO:${root}" ;; | |
| # we were handed a specific partition. | |
| *) : ;; | |
| esac | |
| # write that information for a later handoff. | |
| [ -n "${targetpart}" ] && { | |
| printf 'RESIZE_TARGET="%s"\n' "${targetpart}" > /etc/cmdline.d/12-resize-partition.conf | |
| } | |
| fi | |
| # we never claim any root devices. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/sh | |
| # if we have no config immediately get outta here. | |
| [ -e /etc/cmdline.d/12-resize-partition.conf ] || return 0 | |
| . /etc/cmdline.d/12-resize-partition.conf | |
| info "check disk/partition target ${RESIZE_TARGET} for resize" | |
| # pull AUTO: off the resize target it was just informational today | |
| _resize_target="${RESIZE_TARGET#AUTO:}" | |
| case "${_resize_target}" in | |
| # if we're handed a device..., we're done finding it | |
| /*) : ;; | |
| *=*) | |
| # handle KEY=VALUE pairing, kinda | |
| _key="$(printf '%s' "${_resize_target%%=*}"|awk '{ print tolower($0) }')" | |
| _value="${_resize_target#*=}" | |
| [ -e "/dev/disk/by-${_key}/${_value}" ] && _resize_target="/dev/disk/by-${_key}/${_value}" | |
| ;; | |
| esac | |
| # find the disk(s) that target sits on at this point | |
| disks="$(lsblk -Ps -o NAME,TYPE "${_resize_target}" | awk '$0 ~ "TYPE=\"disk\"" { sub(/\" TYPE=.*/,"") ; sub(/NAME=\"/,"") ; print }')" | |
| # check all disks wired to the partition for GPT header needing rework first. | |
| mod=0 | |
| for disk in ${disks} ; do | |
| info "checking disk ${disk} GPT table" | |
| majhx="$(stat -c "%t" "/dev/${disk}")" ; minhx="$(stat -c "%T" "/dev/${disk}")" | |
| mknod -m 0400 "/tmp/${disk}" b "0x${majhx}" "0x${minhx}" | |
| # sfdisk tells us the disk is wrong, but sgdisk is better at the header wrangle. | |
| sfdisk -l "/tmp/${disk}" 2>&1 1>/dev/null | grep -qF 'The backup GPT table is not on the end of the device.' && { | |
| info "updating disk ${disk} GPT table" | |
| sgdisk -e "/dev/${disk}" | |
| mod=1 | |
| } | |
| rm "/tmp/${disk}" | |
| done | |
| # wait on udev if we touched anything here, first. | |
| [ "${mod}" -gt 0 ] && udevadm settle | |
| # now do partitions | |
| parts="$(lsblk -Ps -o NAME,TYPE "${_resize_target}" | awk '$0 ~ "TYPE=\"part\"" { sub(/\" TYPE=.*/,"") ; sub(/NAME=\"/,"") ; print }')" | |
| mod=0 | |
| for part in ${parts} ; do | |
| # this is painful. none of the tools give a good working model of how much bigger the partition can get and I hate it. | |
| # we want to make sure that the rootfs can be extended at least 20M, to an aligned last _sector_. | |
| # otherwise we will _not_ bother extending the partition. | |
| # we're gonna pull the decision-making out of parted *output* | |
| # look to makediskimage.sh, ~L300 ("for image in...") | |
| disk="$(lsblk -Ps -o NAME,TYPE "/dev/${part}" | awk '$0 ~ "TYPE=\"disk\"" { sub(/\" TYPE=.*/,"") ; sub(/NAME=\"/,"") ; print }')" | |
| # _additionally(!)_ parted is triggering udev on reads. so make a kernel device it stops writing on... | |
| majhx="$(stat -c "%t" "/dev/${disk}")" ; minhx="$(stat -c "%T" "/dev/${disk}")" | |
| mknod -m 0400 "/tmp/${disk}" b "0x${majhx}" "0x${minhx}" | |
| majhx="$(stat -c "%t" "/dev/${part}")" ; minhx="$(stat -c "%T" "/dev/${part}")" | |
| mknod -m 0400 "/tmp/${part}" b "0x${majhx}" "0x${minhx}" | |
| sectorsz="$(parted "/tmp/${disk}" -s print | awk '$0 ~ "Sector size" { print $NF }')" | |
| sectorsz="${sectorsz%B/*}" | |
| sectorchunk=$(( 2097152 / sectorsz )) | |
| minimum_sect=$(( 20971520 / sectorsz )) | |
| partno="$(partx -s -g -o NR "/tmp/${part}")" | |
| info "checking part ${part} (${disk}:${partno}) for resizing" | |
| freestat="$(parted -s "/tmp/${disk}" unit s print free | awk '$1 == '"${partno}"' { pn=1 ; next } ; $1 ~ /^[0-9]+$/ { pn=0 } ; pn == 1 { $1=$1 ; print ; pn=0 }')" | |
| rm "/tmp/${part}" | |
| # now that we know what free space we have (if any), determine if we even want to resize the disk. | |
| case "${freestat}" in | |
| *"Free Space") | |
| # break the line down | |
| freestat="${freestat%s*Free Space}" | |
| start="${freestat%% *}" | |
| end="${freestat#"${start} "}" ; end="${end%% *}" | |
| size="${freestat#"${start} ${end} "}" ; size="${size%% *}" | |
| # file the units off | |
| start="${start%s}" | |
| end="${end%s}" | |
| size="${size%s}" | |
| # now we can check to see if the partition _should_ be resized | |
| [ "${size}" -gt "${minimum_sect}" ] && { | |
| # check for a hybrid mbr _here_ - parted resizing will stomp on it. | |
| parts="$(sfdisk -l -Y dos "/tmp/${disk}" | awk 'BEGIN { c=0 } ; $1 == "Device" { p=1;next };p == 1 { if ($0 == "") { p=0;next } ; c++;print $2,$3,$6 } END { print c }')" | |
| partct="$(echo "${parts}"|awk 'END { print }')" | |
| # more than one partition means someone tinkered with the mbr header | |
| [ "${partct}" -gt 1 ] && { | |
| hybridparts="" | |
| hybridcodes="" | |
| hmbr="$(mktemp)" | |
| gptparts="$(mktemp)" | |
| echo "${parts}" > "${hmbr}" | |
| # grab the gpt partition layout here | |
| parted -s "/tmp/${disk}" 'unit s print' | awk '$1 ~ /[0-9]+/ {sub(/s/,"",$2);sub(/s/,"",$3);print $1,$2,$3}' > "${gptparts}" | |
| # we're going to assemble arguments for gdisk as a big string, by going through the hybrid partition table, and finding matching sector alignments in the gpt table. | |
| while read -r mbrline ; do | |
| gptpart=0 | |
| mbrcode="${mbrline##* }" | |
| [ "${mbrcode}" = "${mbrline}" ] && continue # skip over the count at the end | |
| [ "${mbrcode}" = "ee" ] && continue # skip over protective partitions | |
| mbrline="${mbrline% "${mbrcode}"}" | |
| while read -r gptline ; do | |
| [ "${gptpart}" -ne 0 ] && continue | |
| case "${gptline}" in | |
| *" ${mbrline}") gptpart="${gptline%% *}" ;; | |
| esac | |
| done < "${gptparts}" | |
| [ "${gptpart}" -ne 0 ] && { | |
| hybridparts="${hybridparts} ${gptpart}" | |
| hybridcodes="${hybridcodes} ${mbrcode}" | |
| } | |
| done < "${hmbr}" | |
| rm "${hmbr}" | |
| rm "${gptparts}" | |
| } | |
| # align the partition end on a chunk boundary | |
| align=$((end % sectorchunk)) | |
| [ "${align}" -ne 0 ] && end=$((end - align)) | |
| info "resizing partition ${part} (${disk}:${partno}) to maximum available space (end ${end}s)" | |
| parted "/dev/${disk}" -s resizepart "${partno}" "${end}s" | |
| [ "${hybridparts}" ] && { | |
| # we need to reapply the hybrid mbr now. | |
| hybridparts="${hybridparts# }" | |
| # script to gdisk | |
| { | |
| # preamble... | |
| # invoke recovery menu, create hybrid mbr using partition numbers we have, do not put guard partitions first. | |
| printf '%s\n' \ | |
| 'r' \ | |
| 'h' \ | |
| "${hybridparts}" \ | |
| 'n' | |
| # we need to answer for each partition what code we want, and disable the bootable flag. | |
| for code in ${hybridcodes} ; do | |
| printf '%s\n' \ | |
| "${code}" \ | |
| 'n' | |
| done | |
| # the below won't work if we had 4 partitions hybridized, but I don't... | |
| # create guard partitions in unused slots, type 'ee' - write partition table and confirm. | |
| printf '%s\n' \ | |
| 'y' \ | |
| 'ee' \ | |
| 'w' \ | |
| 'y' | |
| } | gdisk "/dev/${disk}" | |
| } | |
| mod=1 | |
| } | |
| ;; | |
| esac | |
| # don't forget the disk device we were reading from (for hybrid check) | |
| rm "/tmp/${disk}" | |
| done | |
| # wait on udev if we touched something | |
| [ "${mod}" -gt 0 ] && udevadm settle | |
| return 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment