#!/bin/bash

if [ -z "${__kickstart_sh_storage-}" ]; then
__kickstart_sh_storage=1

. kickstart-sh-storage-lvm
. kickstart-sh-storage-raid
. kickstart-sh-storage-btrfs
. kickstart-sh-storage-zfs

ks_devname()
{
	local devname=''
	case "$1" in
		/dev/root)
			[ -n "$ROOT" ] || fatal "/dev/root is not defined"
			ks_devname "$ROOT"
			return
			;;
		LABEL=*)
			devname="$(blkid -o device -L "${1#LABEL=}")"
			;;
		UUID=*)
			devname="$(blkid -o device -U "${1#UUID=}")"
			;;
		/dev/*)
			[ -L "$1" ] &&
				devname="$(readlink -f "$1")" ||
				devname="$1"
			;;
		*)
			if [ -e "/sys/class/block/$1" ]; then
				devname=$(sed -n -e 's,DEVNAME=,/dev/,p' "/sys/class/block/$1/uevent")
			elif [ -e "/dev/md/$1" ]; then
				devname="$(readlink -f "/dev/md/$1")"
			fi
			;;
	esac

	[ -n "$devname" ] && [ -b "$devname" ] ||
		fatal "unable to get devname for argument: $1"

	printf '%s\n' "${devname#/dev/}"
}

ks_devname_list()
{
	set -- ${1//,/ }

	while [ "$#" -gt 0 ]; do
		ks_devname "$1"
		shift
	done
}

ks_cat_exec()
{
	cat >"$1"
	chmod +x "$1"
}

ignoredisk()
{
	local PROG TEMP dev
	local drives='' onlyuse=''
	local ret=0

	PROG="kickstart"
	message "command: ${FUNCNAME[0]} $*"

	PROG="${FUNCNAME[0]}"
	TEMP=`getopt -n "$PROG" -l 'drives:,only-use:' -- "$PROG" "$@"` ||
		return 1
	eval set -- "$TEMP"

	while :; do
		case "$1" in
			--drives)
				shift
				drives="$(ks_devname_list "$1")"
				;;
			--only-use)
				shift
				onlyuse="$(ks_devname_list "$1")"
				;;
			--) shift; break
				;;
		esac
		shift
	done

	if [ -n "$drives" ] && [ -n "$onlyuse" ]; then
		message "options --drives and --only-use are mutually exclusive."
		return 1
	fi

	drives=",${drives//$'\n'/,},"
	onlyuse=",${onlyuse//$'\n'/,},"

	set --

	for dev in $BLOCK_DEVICES; do
		if [ "$onlyuse" != ',,' ] && [ -n "${onlyuse##*,$dev,*}" ]; then
			verbose "'$dev' disk ignored"
			continue
		elif [ "$drives" != ',,' ] && [ -z "${drives##*,$dev,*}" ]; then
			verbose "'$dev' disk ignored"
			continue
		fi
		set -- "$@" "$dev"
	done

	BLOCK_DEVICES="$*"
}

ks_requires_clearpart=("findmnt" "wipefs" "sfdisk" "umount")
clearpart()
{
	local PROG TEMP dev devname num skipped
	local all='' disks='' list='' initlabel=''

	PROG="kickstart"
	message "command: ${FUNCNAME[0]} $*"

	ks_check_requires "${FUNCNAME[0]}" ||
		return 1

	PROG="${FUNCNAME[0]}"
	TEMP=`getopt -n "$PROG" -l 'all,drives:,list:,none,initlabel,disklabel:' -- "$PROG" "$@"` ||
		return 1
	eval set -- "$TEMP"

	DISKLABEL=dos

	while :; do
		case "$1" in
			--all)
				all=1
				disks="$BLOCK_DEVICES"
				;;
			--drives)
				shift
				disks="$(ks_devname_list "$1")"
				;;
			--none)
				disks=''
				;;
			--list)
				shift
				list="$(ks_devname_list "$1")"
				;;
			--disklabel)
				shift
				DISKLABEL="${1##*/}"
				;;
			--initlabel)
				initlabel=1
				;;
			--) shift; break
				;;
		esac
		shift
	done

	case "$DISKLABEL" in
		''|gpt|dos)
			;;
		mbr)
			DISKLABEL=dos
			;;
		*)
			message "unsupported disklabel: $DISKLABEL"
			return 1
	esac

	if [ -n "$initlabel" ] && [ -z "$all" ]; then
		message "unable to initializes the disk label"
		return 1
	fi

	list=",${list//$'\n'/,},"

	for dev in $disks; do
		skipped=0
		num=0
		for devname in $(sfdisk -d "/dev/$dev" | sed -n -e 's,^/dev/\([^[:space:]]\+[0-9]\+\) : .*,\1,p'); do
			num=$(( $num + 1 ))

			if [ "$list" != ',,' ] && [ -n "${list##*,$devname,*}" ]; then
				skipped=$(( $skipped + 1 ))
				continue
			fi

			[ -b "/dev/$devname" ] ||
				continue

			findmnt -no TARGET "/dev/$devname" |
			while read -r mntpoint; do
				verbose "umounting $mntpoint"
				umount -fR "$mntpoint"
			done

			verbose "dropping /dev/$devname"

			wipefs -qfa "/dev/$devname" ||:
			sfdisk -q --delete "/dev/$dev" "$num" ||
				return 1
			dropped=1
		done

		[ -n "$initlabel" ] ||
			continue

		if [ "$skipped" -gt 0 ]; then
			message "unable to initializes the disk label because there are partitions left($skipped) on the disk"
			continue
		fi

		verbose "creating new $DISKLABEL partition table on /dev/$dev"

		echo "label: $DISKLABEL" |
			sfdisk -q -w always "/dev/$dev" ||
			return 1
	done
}

ks_get_dev_size()
{
	local name value dev
	dev="$1"; shift

	while read -r name value; do
		case "$name" in
			bytes)   totalsize_bytes=$value ;;
			sectors) totalsize_sectors=$value ;;
		esac
	done <<< "$(
		sfdisk "$@" "/dev/$dev" |
			head -1 |
			sed -n -e 's|^.*, \([0-9]\+\) bytes, \([0-9]\+\) sectors|bytes \1\nsectors \2|p'
		)"
}

ks_get_dev_sector_size()
{
	local value
	value="$(sfdisk "/dev/$1" --list |
		sed -n -e 's|^Sector size.*: \([0-9]\+\) bytes .*|\1|p' |
		head -1)"
	sector_size="${value:-512}"
}

# ----------------+-----------------------------+------------------------------+
# Amount of RAM   | Recommended swap space      | Recommended swap space       |
# in the system   |                             | if allowing for hibernation  |
# ----------------+-----------------------------+------------------------------+
# less than 2 GB  | 2 times the amount of RAM   | 3 times the amount of RAM    |
# 2 GB - 8 GB     | Equal to the amount of RAM  | 2 times the amount of RAM    |
# 8 GB - 64 GB    | 0.5 times the amount of RAM | 1.5 times the amount of RAM  |
# more than 64 GB | workload dependent          | 1.5 times the amount of RAM  |
# ----------------+-----------------------------+------------------------------+
ks_requires_ks_guess_swap_size=("numfmt")
ks_guess_swap_size()
{
	local memtotal

	ks_check_requires "${FUNCNAME[0]}" ||
		return 1

	memtotal="$(sed -ne 's|^MemTotal:[[:space:]]*\([0-9]\+\) kB$|\1|p' /proc/meminfo)"
	memtotal="$(numfmt --from=iec ${memtotal}K)"

	if [ "$memtotal" -lt "$(numfmt --from=iec 2G)" ]; then
		echo $(( $memtotal * 3 ))
		return
	fi
	if [ "$memtotal" -lt "$(numfmt --from=iec 8G)" ]; then
		echo $(( $memtotal * 2 ))
		return
	fi

	echo $(( $memtotal + ( $memtotal / 2 ) ))
}

ks_mountpoint_name()
{
	local fname

	fname="$(echo "$1" | sha256sum)"
	fname="${fname% *}"

	echo "$fname"
}

ks_is_mountpoint_exists()
{
	local fname
	fname="$(ks_mountpoint_name "$1")"
	[ -f "$ks_datadir/fstab.d/$fname" ]
}

ks_requires_ks_fstab=("blkid" "sha256sum")
ks_fstab()
{
	local fname dev mpoint fstype fsopts

	ks_check_requires "${FUNCNAME[0]}" ||
		return 1

	dev="$1"; shift
	mpoint="$1"; shift
	fsopts="$1"; shift

	if [ "$#" -eq 0 ]; then
		fstype="$(blkid -o value -s TYPE "$dev")"
	else
		fstype="$1"; shift
	fi
	fname="$(ks_mountpoint_name "$mpoint")"

	printf '%s %s %s %s 0 0\n' \
		"$dev" "$ks_rootdir$mpoint" "$fstype" "${fsopts:-defaults}" \
		> "$ks_datadir/fstab.d/$fname"

	find "$ks_datadir/fstab.d" -type f -exec cat '{}' '+' \
		> "$ks_datadir/fstab"
}

getopt_useexisting='useexisting,noformat'
ks_set_useexisting()
{
	case "$1" in
		--useexisting|--noformat)
			useexisting=1
			shift_args=1
			;;
	esac
}

getopt_makefs='fstype:,label:'
ks_set_makefs_args()
{
	case "$1" in
		--label|--fstype)
			makefs_args="${makefs_args-} \"$1=$2\""
			shift_args=2
			;;
	esac
}

ks_requires_makefs=("wipefs" "mkswap")
makefs()
{
	local PROG TEMP
	local dev=''
	local fstype='ext4' label=''

	PROG="kickstart"
	message "command: ${FUNCNAME[0]} $*"

	ks_check_requires "${FUNCNAME[0]}" ||
		return 1

	PROG="${FUNCNAME[0]}"
	TEMP=`getopt -n "$PROG" -l "$getopt_makefs" -- "$PROG" "$@"` ||
		return 1
	eval set -- "$TEMP"

	while :; do
		case "$1" in
			--fstype) shift; fstype="$1"
				;;
			--label) shift; label="$1"
				;;
			--) shift; break
				;;
		esac
		shift
	done

	if [ "$#" = 0 ]; then
		message "device required"
		return 1
	fi

	if [ -z "$fstype" ]; then
		message "filesystem type required"
		return 1
	fi

	dev="${1#/dev/}"

	verbose "clearing previous filesystem on /dev/$dev"

	wipefs -qfa "/dev/$dev" ||
		return 1

	verbose "creating $fstype filesystem${label:+ (LABEL=$label)} on /dev/$dev"

	case "$fstype" in
		swap) mkswap -f ${label:+-L "$label"} "/dev/$dev" >/dev/null ;;
		fat|vfat) mkfs."$fstype" ${label:+-n "$label"} "/dev/$dev" >/dev/null ;;
		f2fs) "mkfs.$fstype" -q ${label:+-l "$label"} "/dev/$dev" ;;
		*) "mkfs.$fstype" -q ${label:+-L "$label"} "/dev/$dev" ;;
	esac
}

getopt_crypto='cipher:,passphrase:,passfile:,pbkdf:,pbkdf-memory:,pbkdf-time:,pbkdf-iterations:'
ks_set_crypto_args()
{
	case "$1" in
		--cipher|--passphrase|--passfile|--pbkdf|--pbkdf-memory|--pbkdf-time|--pbkdf-iterations)
			crypto_args="${crypto_args-} \"$1=$2\""
			shift_args=2
			;;
	esac
}

ks_requires_crypto=("cryptsetup")
crypto()
{
	local PROG TEMP dev
	local luks_passphrase='' luks_cipher='' luks_pbkdf='' luks_pbkdf_memory=''
	local luks_pbkdf_time='' luks_pbkdf_iterations='' luks_passfile=''
	local name=''

	PROG="kickstart"
	message "command: ${FUNCNAME[0]} $*"

	ks_check_requires "${FUNCNAME[0]}" ||
		return 1

	PROG="${FUNCNAME[0]}"
	TEMP=`getopt -n "$PROG" -l "name:,$getopt_crypto" -- "$PROG" "$@"` ||
		return 1
	eval set -- "$TEMP"

	while :; do
		case "$1" in
			--name) shift; name="$1"
				;;
			--cipher) shift; luks_cipher="$1"
				;;
			--passphrase) shift; luks_passphrase="$1"
				;;
			--passfile) shift; luks_passfile="$1"
				;;
			--pbkdf) shift; luks_pbkdf="$1"
				;;
			--pbkdf-memory) shift; luks_pbkdf_memory="$1"
				;;
			--pbkdf-time) shift; luks_pbkdf_time="$1"
				;;
			--pbkdf-iterations) shift; luks_pbkdf_iterations="$1"
				;;
			--) shift; break
				;;
		esac
		shift
	done

	if [ -z "$luks_passphrase" ] && [ -z "$luks_passfile" ]; then
		message "specify the passphrase to use."
		return 1
	fi

	if [ -n "$luks_pbkdf_time" ] && [ -n "$luks_pbkdf_iterations" ]; then
		message "only one of --pbkdf-time and --pbkdf-iterations can be specified."
		return 1
	fi

	[ -z "${ONLYARGS-}" ] ||
		return 0

	if [ "$#" = 0 ] || [ -z "${1-}" ]; then
		message "device not specified"
		return 1
	fi

	[ -d /run/cryptsetup ] ||
		mkdir -p /run/cryptsetup

	dev="${1#/dev/}"

	verbose "creating LUKS encrypted volume on /dev/$dev"

	if [ -n "$luks_passfile" ] && [ ! -f "$luks_passfile" ]; then
		message "passfile does not exist: $luks_passfile"
		return 1
	fi

	local crypt_name="${name:-${dev}_crypt}"
	local passfile="$ks_datadir/crypto/$crypt_name/pass"

	mkdir -p -- "${passfile%/*}"

	if [ -n "$luks_passphrase" ]; then
		printf '%s' "$luks_passphrase" > "$passfile"
	elif [ -n "$luks_passfile" ]; then
		cp -f -- $luks_passfile "$passfile"
	else
		message "unable to create luks without password or passfile"
		return 1
	fi

	cryptsetup \
		--batch-mode \
		--type luks \
		--key-file "$passfile" \
		${luks_cipher:+--cipher="$luks_cipher"} \
		${luks_passphrase:+ --force-password} \
		${luks_pbkdf:+--pbkdf="$luks_pbkdf"} \
		${luks_pbkdf_memory:+--pbkdf-memory="$luks_pbkdf_memory"} \
		${luks_pbkdf_time:+--iter-time="$luks_pbkdf_time"} \
		${luks_pbkdf_iterations:+--pbkdf-force-iterations="$luks_pbkdf_iterations"} \
		luksFormat "/dev/$dev" || {
			message "unable to format luks device: /dev/$dev"
			return 1
		}

	cryptsetup \
		--key-file "$passfile" \
		open --type luks "/dev/$dev" "$crypt_name" || {
			message "unable to open luks device: /dev/$dev"
			return 1
		}

	message "luks device ready: /dev/$dev"
	return 0
}

ks_get_dev_id()
{
	local dev target

	for dev in $(set +f; printf '%s\n' /dev/disk/by-id/* |tac); do
		[ -b "$dev" ] && target="$(readlink -f "$dev")" ||
			continue

		target="${target#/dev/}"

		if [ "$1" = "$target" ]; then
			printf '%s\n' "${dev#/dev/}"
			return 0
		fi
	done

	printf '%s\n' "$1"
}

ks_requires_ext4=("e2fsck" "resize2fs")
ks_requires_xfs=("xfs_growfs" "mount")
ks_requires_f2fs=("resize.f2fs")
ks_growfs()
{
	local PROG
	local fs="" requires="" ret=0 rc=0

	PROG="kickstart"
	message "command: ${FUNCNAME[0]} $*"

	fs="$(blkid --output value --match-tag TYPE -c /dev/null "$1")" ||
		return 0

	case "$fs" in
		ext*) requires="ks_growfs_ext4" ;;
		xfs)  requires="ks_growfs_xfs"  ;;
		f2fs) requires="ks_growfs_f2fs" ;;
		*)
			# resize unsupported.
			return 0
			;;
	esac

	ks_check_requires "$requires" ||
		return 1

	verbose "Increase filesystem to partition size: $1"

	case "$fs" in
		ext*)
			e2fsck -y -p -f "$1" ||
				rc="$?"
			# 0 - No errors
			# 1 - File system errors corrected
			# 2 - File system errors corrected, system should be rebooted
			[ "$rc" -le 2 ] && resize2fs -f "$1" ||
				ret=1
			;;
		xfs)
			mount -n -o rw,X-mount.mkdir \
				"$1" \
				"$ks_datadir/xfs.dir"
			xfs_growfs \
				"$ks_datadir/xfs.dir" ||
				ret=1
			umount -f \
				"$ks_datadir/xfs.dir"
			;;
		f2fs)
			resize.f2fs "$1" ||
				ret=1
			;;
	esac

	return $ret
}


ks_requires_part=("numfmt" "sfdisk")
part()
{
	local PROG TEMP shift_args=0 sfdisk_args exe
	local mntpoint='' dev='' partdev='' parttype='' fstype='' fsoptions='' size='' grow='' useexisting='' resize=''
	local asprimary='' hibernation=''
	local encrypted='' makefs_args='' crypto_args=''
	local disks="$BLOCK_DEVICES"

	local ret=0

	PROG="kickstart"
	message "command: ${FUNCNAME[0]} $*"

	ks_check_requires "${FUNCNAME[0]}" ||
		return 1

	PROG="${FUNCNAME[0]}"
	TEMP=`getopt -n "$PROG" -l "asprimary,ondisk:,ondrive:,onpart:,usepart:,fsoptions:,size:,grow,resize,encrypted,hibernation,$getopt_makefs,$getopt_crypto,$getopt_useexisting" -- "$PROG" "$@"` ||
		return 1
	eval set -- "$TEMP"

	while :; do
		case "$1" in
			--) shift; break
				;;
			--asprimary) asprimary=1
				;;
			--ondisk|--ondrive)
				shift
				disks="$(ks_devname "$1")"
				;;
			--onpart|--usepart)
				shift
				partdev="$(ks_devname "$1")"
				if [ ! -f "/sys/class/block/$partdev/partition" ]; then
					message "argument is not like a disk partition: $1"
					return 1
				fi
				disks=''
				;;
			--fstype) shift; fstype="$1"
				;;
			--fsoptions) shift; fsoptions="$1"
				;;
			--size) shift;
				# B   -> B
				# MiB -> Mi
				# KiB -> Ki
				size="${1/i[Bb]/i}"
				[ -z "${size##*[!0-9]*}" ] || size="${size}Mi"
				;;
			--resize) resize=1
				;;
			--grow) grow=1; size=''
				;;
			--encrypted)
				encrypted=1
				;;
			--hibernation)
				hibernation=1
				;;
			*)
				shift_args=0
				ks_set_useexisting "$@"
				ks_set_makefs_args "$@"
				ks_set_crypto_args "$@"
				if [ "$shift_args" != 0 ]; then
					shift "$shift_args"
					continue
				fi
				;;
		esac
		shift
	done

	if [ -n "$encrypted" ]; then
		quote_shell_args crypto_args "$crypto_args"
		eval "ONLYARGS=1 crypto $crypto_args || return"
	fi

	mntpoint="${1:-none}"
	set --

	case "$mntpoint" in
		raid.*)    parttype="raid"  ;;
		pv.*)      parttype="lvm"   ;;
		zfs.boot.*)
			fstype=''
			parttype='be'
			;;
		zfs.raidz.*|zfs.mirror.*)
			fstype=''
			parttype='fd'
			;;
		zfs.*)
			fstype=''
			parttype='bf'
			;;
		swap)
			fstype="swap"
			;;
		biosboot)
			fstype="biosboot"
			;;
		/boot/efi)
			fstype="efi"
			;;
		/home)
			[ "$DISKLABEL" = 'gpt' ] &&
				parttype="home" ||
				parttype="linux"
			;;
		*)
			parttype="linux"
			;;
	esac

	if [ -z "${mntpoint##/*}" ] && ks_is_mountpoint_exists "$mntpoint"; then
		message "partition for \`$mntpoint' mountpoint already exists. Ignore."
		return
	fi

	case "$fstype" in
		'')
			;;
		efi)
			parttype="uefi"
			size="${size:-200Mi}"
			ks_set_makefs_args --fstype "vfat"
			;;
		biosboot)
			parttype="ef02"
			size="${size:-1Mi}"
			;;
		swap)
			parttype="swap"
			if [ -z "$size" ] && [ -n "$hibernation" ]; then
				size="$(ks_guess_swap_size)"

				if [ "$size" -eq 0 ]; then
					message "no need to create a \`$fstype' partition. Ignore."
					return
				fi

				size="${size}B"
			fi
			ks_set_makefs_args --fstype "swap"
			;;
		*)
			ks_set_makefs_args --fstype "$fstype"
			;;
	esac

	for dev in $disks; do
		if [ -n "$size" ] && [ -n "${size%\%}" ]; then
			local totalsize_bytes=0 totalsize_sectors=0 needbytes=0 sectors='' sector_size=0

			ks_get_dev_sector_size "$dev"

			if [ -z "${size##*%}" ]; then
				local totalfree_percent freesize_sectors

				ks_get_dev_size "$dev" --list-free
				freesize_sectors="$totalsize_sectors"

				ks_get_dev_size "$dev" --list
				totalfree_percent=$(( $freesize_sectors * 1000 / $totalsize_sectors ))

				[ ${totalfree_percent:$(( ${#totalfree_percent} - 1 ))} -ge 5 ] &&
					totalfree_percent=$(( ${totalfree_percent:0:$(( ${#totalfree_percent} - 1 ))} + 1 )) ||
					totalfree_percent=$(( ${totalfree_percent:0:$(( ${#totalfree_percent} - 1 ))} ))

				if [ "${size%\%}" -eq "$totalfree_percent" ]; then
					sectors="$freesize_sectors"
				else
					sectors="$(( $totalsize_sectors * ${size%\%} / 100 ))"
				fi
				needbytes=$(( $sectors * $sector_size ))
			elif [ -z "${size##*[!0-9]*}" ]; then
				case "$size" in
					*[Bb]) size="${size%[Bb]}" ;;
					*i)    size=$(LANG=C numfmt --from=iec-i "$size") ;;
					*)     size=$(LANG=C numfmt --from=iec   "$size") ;;
				esac

				sectors="$(( $size / $sector_size ))"
				needbytes="$size"
			else
				# sectors
				sectors="$size"
				needbytes=$(( $sectors * $sector_size ))
			fi

			ks_get_dev_size "$dev" --list-free

			if [ "$needbytes" -gt "$totalsize_bytes" ]; then
				verbose "/dev/$dev has too little free space, ignoring."
				continue
			fi

			[ -z "$sectors" ] ||
				set -- "size=$sectors"
		fi

		[ ! -d "$ks_datadir/part/$dev/stop" ] ||
			find "$ks_datadir/part/$dev/stop" -type f -executable -exec '{}' ';'

		set -- ${1:+"$@" ,} "type=$parttype"

		local prev_parttable curr_parttable sz i
		prev_parttable=()
		curr_parttable=()

		readarray -t prev_parttable <<< "$(sfdisk -d "/dev/$dev" | grep ^/dev/)"

		if [ -z "$asprimary" ] && [ "$DISKLABEL" = dos ] && [ "${#prev_parttable[@]}" = 3 ]; then
			verbose "creating new extended partition on /dev/$dev"

			printf 'type=extended\n' |
				sfdisk -q -W always --append "/dev/$dev" ||
				break

			readarray -t prev_parttable <<< "$(sfdisk -d "/dev/$dev" | grep ^/dev/)"
		fi

		verbose "creating new partition on /dev/$dev"

		ret=0
		printf '%s\n' "$*" |
			sfdisk -q -W always --append "/dev/$dev" ||
			ret=1

		if [ "$ret" = 1 ]; then
			message "unable to create partition"
			return 1
		fi

		readarray -t curr_parttable <<< "$(sfdisk -d "/dev/$dev" | grep ^/dev/)"

		sz=${#curr_parttable[@]}

		for (( i=0; $i < $sz; i++ )); do
			local prev="${prev_parttable[$i]-}"
			local curr="${curr_parttable[$i]}"

			if [ "${prev#* : }" != "${curr#* : }" ]; then
				partdev="${curr%% : *}"
				partdev="${partdev#/dev/}"
				break
			fi
		done
		break
	done

	if [ -z "$partdev" ]; then
		if [ -n "$size" ]; then
			message "unable to create partition with $size size"
			return 1
		fi
		return 0
	fi

	if [ -n "$resize" ]; then
		[ ! -d "$ks_datadir/part/$dev/stop" ] ||
			find "$ks_datadir/part/$dev/stop" -type f -executable -exec '{}' ';'

		# https://karelzak.blogspot.com/2015/05/resize-by-sfdisk.html

		if [ -n "$grow" ]; then
			size='+'
		elif [ -z "$size" ]; then
			message "you need to specify the size by which you want to increase the partition."
			return 1
		else
			size="+$size"
		fi

		local partnum
		read -r partnum < "/sys/class/block/$partdev/partition"

		dev="$(readlink-e "/sys/class/block/$partdev")"
		dev="${dev%/*}"
		dev="${dev##*/}"

		verbose "increasing partition /dev/$partdev"

		printf ', %s\n' "$size" |
			sfdisk -q -N "$partnum" "/dev/$dev"
		ks_growfs "/dev/$partdev"
	fi

	if [ -n "$encrypted" ]; then
		eval "set -- $crypto_args"

		local crypt_name="${partdev}_crypt"

		crypto --name="$crypt_name" "$@" "/dev/$partdev" ||
			return

		mkdir -p -- \
			"$ks_datadir/part/$dev/start" \
			"$ks_datadir/part/$dev/stop"

		ks_cat_exec "$ks_datadir/part/$dev/start/$partdev" <<-EOF
		#!/bin/sh -efu
		[ ! -b "/dev/mapper/$crypt_name" ] ||
			exit 0
		cryptsetup --key-file "$ks_datadir/crypto/$crypt_name/pass" open --type luks "/dev/$partdev" "$crypt_name"
		EOF

		ks_cat_exec "$ks_datadir/part/$dev/stop/$partdev" <<-EOF
		#!/bin/sh -efu
		[ -b "/dev/mapper/$crypt_name" ] ||
			exit 0
		cryptsetup close "$crypt_name"
		EOF

		partdev="mapper/$crypt_name"
	fi

	[ ! -d "$ks_datadir/part/$dev/start" ] ||
		find "$ks_datadir/part/$dev/start" -type f -executable -exec '{}' ';'

	quote_shell_args makefs_args "$makefs_args"
	eval "set -- $makefs_args"

	case "$mntpoint" in
		/|/*)
			[ -n "$useexisting" ] ||
				makefs "$partdev" "$@"
			ks_fstab "/dev/${partdev#/dev/}" "$mntpoint" "$fsoptions"
			;;
		swap)
			makefs "$partdev" "$@" --fstype=swap
			ks_fstab "/dev/${partdev#/dev/}" "swap" "$fsoptions"
			;;
		none)
			[ -n "$useexisting" ] ||
				makefs "$partdev" "$@"
			;;
		btrfs.*|pv.*|raid.*)
			[ -d "$ks_datadir/partid" ] ||
				mkdir -- "$ks_datadir/partid"
			printf '%s\n' "$partdev" >> "$ks_datadir/partid/$mntpoint"
			;;
		zfs.*)
			[ -d "$ks_datadir/partid" ] ||
				mkdir -- "$ks_datadir/partid"
			# From upstream docs:
			# Always use the long /dev/disk/by-id/* aliases with
			# ZFS. Using the /dev/sd* device nodes directly can
			# cause sporadic import failures, especially on systems
			# that have more than one storage pool.
			ks_get_dev_id "$partdev" >> "$ks_datadir/partid/$mntpoint"
			;;
	esac
}

ks_requires_partition=("${ks_requires_part[@]}")
partition() { part "$@"; }

fi #__kickstart_sh_storage
