confz

git mirror of https://ccx.te2000.cz/bzr/confz
git clone https://ccx.te2000.cz/git/confz
Log | Files | Refs

confz_fs_init (19618B)


      1 # vim: ft=zsh noet ts=4 sts=4 sw=4
      2 
      3 #
      4 # confz functions for dealing with filesystem and mounting
      5 #
      6 
      7 # load the zstat builtin and keep stat external
      8 zmodload -F zsh/stat b:zstat
      9 
     10 # helper for finding disk drives by their metadata
     11 fs_smartctl_probe() {
     12 	(($+smartctl_probed)) && (($smartctl_probed)) && return 0
     13 	local dev item val rest
     14 	typeset -g smartctl_probed
     15 	typeset -ga smartctl_devices
     16 	typeset -gA smartctl_info
     17 	smartctl --scan | while read dev rest; do
     18 		smartctl_devices+=( $dev )
     19 		smartctl -i $dev | while IFS=':' read item val; do smartctl_info[${dev}:${${item:l}// /_}]=${val/# #}; done
     20 	done
     21 	smartctl_probed=1
     22 }
     23 
     24 # helper for probing devices
     25 fs_blkid_probe() {
     26 	unset fs_blkid_output fs_blkid_result
     27 	typeset -gA fs_blkid_output
     28 	typeset -g fs_blkid_result
     29 	local out key val
     30 	fs_blkid_output=( )
     31 
     32 	[[ -b $1 ]] || die "no such block device: ${(qqq)1}"
     33 
     34 	out=$( blkid -o export -p $1 )
     35 	fs_blkid_result=$?
     36 
     37 	case $fs_blkid_result in
     38 		(2);;
     39 		(0) while IFS='=' read key val; do
     40 				[[ $key == *' '* ]] && die "Malformed blkid output for \"export\" format."
     41 				fs_blkid_output[$key]=$val
     42 			done <<<$out;;
     43 		(*) die "blkid probe failed on device: ${(qqq)1}"
     44 	esac
     45 }
     46 
     47 # Check that given device is formatted with given type (return 0) or blank (return 1), die otherwise
     48 fs_check_type() {
     49 	local device fstype kind
     50 	device=$1
     51 	fstype=$2
     52 	kind=${3:-TYPE}  # TYPE = filesystem type, PTTYPE = partition format
     53 	fs_blkid_probe $device
     54 	if (($fs_blkid_result)); then
     55 		# empty label
     56 		return 1
     57 	elif (( $+fs_blkid_output[$kind] )); then
     58 			if [[ ${fs_blkid_output[$kind]:-} == $fstype ]]; then
     59 				return 0
     60 			else
     61 				die "$0: non-$fstype label (${fs_blkid_output[$kind]}) already present on device: ${(qqq)device}"
     62 			fi
     63 	else
     64 		local -a labels
     65 		local k v
     66 		for k v in "${(kv)fs_blkid_output[@]}"; do
     67 			# Filter out the information about partition layout rather than content
     68 			case $k in
     69 				(DEVNAME) continue;;
     70 				(PART_ENTRY_*) continue;;
     71 				(*) labels+=( "$k=${(qqq)v}" )
     72 			esac
     73 		done
     74 		if (( $#labels )); then
     75 			die "$0: unexpected filesystem labels present on device: ${(qqq)device} $labels[*]"
     76 		fi
     77 		# empty label
     78 		return 1
     79 	fi
     80 }
     81 
     82 # helper for listing partition table
     83 fs_parted_list() {
     84 	unset fs_parted_start fs_parted_end fs_parted_size fs_parted_type
     85 	unset fs_parted_filesystem fs_parted_name fs_parted_flags
     86 	typeset -gA fs_parted_start fs_parted_end fs_parted_size fs_parted_type
     87 	typeset -gA fs_parted_filesystem fs_parted_name fs_parted_flags
     88 	local line got_header number out
     89 	local NumberE StartB StartE EndB EndE SizeB SizeE FSB FSE NameB NameE FlagsB
     90 	local TypeB TypeE
     91 	local MATCH MBEGIN MEND
     92 
     93 	[[ -b $1 ]] || die "no such block device: ${(qqq)1}"
     94 
     95 	out=$( parted --script $1 -- unit s print ) || \
     96 		die "parted print of device failed: ${(qqq)1}"
     97 
     98 	got_header=0
     99 	while IFS= read line; do
    100 		if ((got_header)); then
    101 			number=${${line[1,$NumberE]}// }
    102 			fs_parted_start[$number]=${${line[$StartB,$StartE]}// }
    103 			fs_parted_end[$number]=${${line[$EndB,$EndE]}// }
    104 			fs_parted_size[$number]=${${line[$SizeB,$SizeE]}// }
    105 			fs_parted_flags[$number]=${line[$FlagsB,-1]}
    106 
    107 			[[ -n $TypeB ]] && \
    108 				fs_parted_type[$number]=${${line[$TypeB,$TypeE]}// }
    109 
    110 			[[ -n $NameB ]] && \
    111 				fs_parted_name[$number]=${${line[$NameB,$NameE]}// }
    112 
    113 			[[ -n $FSB ]] && \
    114 				fs_parted_filesystem[$number]=${${line[$FSB,$FSE]}// }
    115 
    116 		elif [[ $line == Number* ]]; then
    117 			got_header=1
    118 
    119 			[[ $line =~ 'Number +' ]] || die "malformed header: ${(qqq)line}"
    120 			NumberE=$MEND
    121 
    122 			[[ $line =~ 'Start +' ]] || die "malformed header: ${(qqq)line}"
    123 			StartB=$MBEGIN
    124 			StartE=$MEND
    125 
    126 			[[ $line =~ 'End +' ]] || die "malformed header: ${(qqq)line}"
    127 			EndB=$MBEGIN
    128 			EndE=$MEND
    129 
    130 			[[ $line =~ 'Size +' ]] || die "malformed header: ${(qqq)line}"
    131 			SizeB=$MBEGIN
    132 			SizeE=$MEND
    133 
    134 			[[ $line =~ 'Flags *' ]] || die "malformed header: ${(qqq)line}"
    135 			FlagsB=$MBEGIN
    136 
    137 			if [[ $line =~ 'Type +' ]]; then
    138 				TypeB=$MBEGIN
    139 				TypeE=$MEND
    140 			fi
    141 
    142 			if [[ $line =~ 'Name +' ]]; then
    143 				NameB=$MBEGIN
    144 				NameE=$MEND
    145 			fi
    146 
    147 			if [[ $line =~ 'File system +' ]]; then
    148 				FSB=$MBEGIN
    149 				FSE=$MEND
    150 			fi
    151 		fi
    152 	done <<<"$out"
    153 }
    154 
    155 # Find disks by their IDs
    156 confz_disk_id_check() {
    157 	# arguments: device device_model serial_number
    158 	if ! (($+vars[device] + $+vars[device_model] + $+vars[serial_number])); then
    159 		die "$0: requires one or more arguments: device device_model serial_number"
    160 	fi
    161 
    162 	do_command=( true )
    163 	fs_smartctl_probe
    164 	local device
    165 	local -a found
    166 
    167 	for device in $smartctl_devices; do
    168 		if (($+vars[device])); then
    169 			[[ $vars[device] != $device ]] && continue
    170 		fi
    171 		if (($+vars[device_model])); then
    172 			[[ $vars[device_model] != $smartctl_info[${device}:device_model] ]] && continue
    173 		fi
    174 		if (($+vars[serial_number])); then
    175 			[[ $vars[serial_number] != $smartctl_info[${device}:serial_number] ]] && continue
    176 		fi
    177 		found+=( $device $smartctl_info[${device}:device_model] $smartctl_info[${device}:serial_number] )
    178 	done
    179 
    180 	(( $#found )) || die "$0: found no matching devices"
    181 	(( $#found % 3 )) && die "$0: internal error parsing device information"
    182 	[[ $#found -ne 3 ]] && die "$0: found too many matching devices"
    183 	vars[device]=$found[1]
    184 	vars[device_model]=$found[2]
    185 	vars[serial_number]=$found[3]
    186 }
    187 
    188 # check for DOS-style MBR on device
    189 confz_disklabel_dos_check() {
    190 	checkvars device
    191 
    192 	do_command=( parted --script $vars[device] -- mklabel msdos )
    193 	fs_check_type $vars[device] dos PTTYPE
    194 }
    195 
    196 
    197 # check for GPT partition table on device
    198 confz_disklabel_gpt_check() {
    199 	checkvars device
    200 
    201 	do_command=( parted --script $vars[device] -- mklabel gpt )
    202 	fs_check_type $vars[device] gpt PTTYPE
    203 }
    204 
    205 
    206 # embed file in MBR
    207 confz_mbr_code_check() {
    208 	checkvars device from
    209 	do_command=( dd bs=440 count=1 if=$vars[from] of=$vars[device] )
    210 	dd if=$vars[device] bs=440 count=1 | cmp -s - $vars[from]
    211 }
    212 
    213 
    214 # check for gpt partition
    215 confz_gpt_partition_check() {
    216 	local prev option
    217 	checkvars device number name
    218 	defvar fs ext2
    219 	require disklabel_gpt :device
    220 	fs_parted_list $vars[device]
    221 	(( $+vars[size] || $+vars[end] )) || die "$0: requires either size or end"
    222 	if [[ $vars[number] != 1 ]]; then
    223 		prev=$[ $vars[number] - 1 ]
    224 		(( $+fs_parted_start[$prev] )) || die "$0: missing preceding partition"
    225 		: ${vars[start]:=${fs_parted_start[$prev]}}
    226 	else
    227 		checkvars start
    228 	fi
    229 	if ! (($+vars[end])); then
    230 		vars[end]=$[${${vars[start]}%s} + ${${vars[size]}%s}]s
    231 	fi
    232 	if (( $+fs_parted_start[$vars[number]] )); then
    233 		[[ $fs_parted_name[$vars[number]] == $vars[name] ]] || \
    234 			die "Partition $vars[number] has name ${(qqq)fs_parted_name[$vars[number]}, want ${(qqq)vars[name]}"
    235 		return 0
    236 	else
    237 		do_command=( parted --script )
    238 		(($+vars[align])) && do_command+=( --align $vars[align] )
    239 		do_command+=(
    240 			$vars[device] -- unit s
    241 			mkpart $vars[name] $vars[fs] $vars[start] $vars[end]
    242 		)
    243 		(($+vars[align])) && do_command+=(
    244 			align-check $vars[align] $vars[number]
    245 		)
    246 		(($+vars[options])) && for option in ${=vars[options]}; do
    247 			do_command+=( set $vars[number] $option on )
    248 		done
    249 		return 1
    250 	fi
    251 }
    252 
    253 
    254 # check for primary partition
    255 confz_primary_partition_check() {
    256 	local prev option
    257 	checkvars device number
    258 	defvar fs ext2
    259 	require disklabel_dos :device
    260 	fs_parted_list $vars[device]
    261 	(( $+vars[size] || $+vars[end] )) || die "$0: requires either size or end"
    262 	if [[ $vars[number] != 1 ]]; then
    263 		prev=$[ $vars[number] - 1 ]
    264 		(( $+fs_parted_start[$prev] )) || die "$0: missing preceding partition"
    265 		: ${vars[start]:=${fs_parted_start[$prev]}}
    266 	else
    267 		checkvars start
    268 	fi
    269 	if ! (($+vars[end])); then
    270 		vars[end]=$[${${vars[start]}%s} + ${${vars[size]}%s}]s
    271 	fi
    272 	if (( $+fs_parted_start[$vars[number]] )); then
    273 		return 0
    274 	else
    275 		do_command=( parted --script )
    276 		(($+vars[align])) && do_command+=( --align $vars[align] )
    277 		do_command+=(
    278 			$vars[device] -- unit s
    279 			mkpart primary $vars[fs] $vars[start] $vars[end]
    280 		)
    281 		(($+vars[align])) && do_command+=(
    282 			align-check $vars[align] $vars[number]
    283 		)
    284 		(($+vars[options])) && for option in ${=vars[options]}; do
    285 			do_command+=( set $vars[number] $option on )
    286 		done
    287 		return 1
    288 	fi
    289 }
    290 
    291 
    292 # check for bootable flag on primary partition
    293 confz_bootable_partition_check() {
    294 	checkvars device number
    295 	require disklabel_dos :device
    296 	fs_parted_list $vars[device]
    297 	(( $+fs_parted_flags[$vars[number]] )) || \
    298 		die "$0: device ${(q)vars[device]} partition ${(q)vars[number]} not found"
    299 	if [[ $fs_parted_flags[$vars[number]] = *boot* ]]; then
    300 		return 0
    301 	else
    302 		do_command=( parted --script $vars[device] -- set $vars[number] boot on )
    303 		return 1
    304 	fi
    305 }
    306 
    307 
    308 # create swap partition
    309 confz_swap_check() {
    310 	checkvars device
    311 
    312 	do_command=( mkswap $vars[device] )
    313 
    314 	fs_check_type $vars[device] swap
    315 }
    316 
    317 
    318 # enable swap device
    319 confz_swapon_check() {
    320 	local out device rest
    321 
    322 	checkvars device
    323 	require swap :device
    324 
    325 	out=$(swapon -s) || die "$0: swapon -s comman failed"
    326 	while read device rest; do
    327 		[[ $device == $vars[device] ]] && return 0
    328 	done <<<$out
    329 
    330 	do_command=( swapon $vars[device] )
    331 	return 1
    332 }
    333 
    334 
    335 # set up /dev/md* device
    336 confz_mdraid_check() {
    337 	local line level out seen_level seen_header devices all_empty ret
    338 	local -a seen_devices devices device_numbers
    339 	local -A level_map
    340 
    341 	checkvars md_device raid_devices level
    342 	defvar metadata default
    343 	defvar bitmap none
    344 
    345 	devices=( "${(Q@)${(z)vars[raid_devices]}}" )
    346 
    347 	level_map=(
    348 		linear	linear
    349 		raid0	0
    350 		0	0
    351 		stripe	0
    352 		raid1	1
    353 		1	1
    354 		mirror	1
    355 		raid4	4
    356 		4	4
    357 		raid5	5
    358 		5	5
    359 		raid6	6
    360 		6	6
    361 		raid10	10
    362 		10	10
    363 		multipath	multipath
    364 		mp	multipath
    365 		faulty	faulty
    366 		container	container
    367 	)
    368 	level=${level_map[${vars[level]}]}
    369 
    370 	for device in $devices; do
    371 		[[ -b $device ]] || die "$0: not a block device: ${(qqq)device}"
    372 		device_numbers+=( $(zstat +rdev $device) ) || die "$0: could not stat ${(qqq)device}"
    373 	done
    374 
    375 	if [[ -b $vars[md_device] ]]; then
    376 		out=$( mdadm --detail $vars[md_device] )
    377 		ret=$?
    378 		case $ret in
    379 			(0) ;;
    380 			(1) ;; # return 1;;
    381 			(*) die "$0: mdadm --detail ${(qqq)vars[md_device]} returned $ret";;
    382 		esac
    383 		seen_header=0
    384 		while read line; do
    385 			if ! (($seen_header)); then
    386 				case $line in
    387 					('Raid Level :'*)
    388 						seen_level=$level_map[${line#* : }];;
    389 					(Number*Major*Minor*RaidDevice*State)
    390 						seen_header=1;;
    391 				esac
    392 			else
    393 				seen_devices+=( $(( ${${=line}[2]} << 8 + ${${=line}[3]} )) )
    394 			fi
    395 		done <<<$out
    396 
    397 		if (($seen_header)); then
    398 			[[ $level == $seen_level ]] || \
    399 				die "$0: raid level mismatch." \
    400 				"expected: ${(q)level} got: ${(q)seen_level}"
    401 
    402 			[[ ${(j.:.)${(o)device_numbers}} == ${(j.:.)${(o)seen_devices}} ]] || \
    403 				die $0$': raid device mismatch\nexpected:' \
    404 				${(oqqq)device_numbers}$'\ngot:' \
    405 				${(oqqq)seen_devices}
    406 
    407 			return 0
    408 		fi
    409 	fi
    410 
    411 	all_empty=1
    412 	for device in $devices; do
    413 		# all devices either need to have empty labels or be linux_raid_member
    414 		if fs_check_type $device linux_raid_member; then
    415 			all_empty=0
    416 		fi
    417 	done
    418 
    419 	if (($all_empty)); then
    420 		# empty labels
    421 		do_command=(
    422 			mdadm --create
    423 			-l $level
    424 			--metadata=$vars[metadata]
    425 			--bitmap=$vars[bitmap]
    426 			-n $#devices
    427 			$vars[md_device]
    428 			$devices
    429 		)
    430 	else
    431 		# already created
    432 		do_command=( mdadm --assemble $vars[md_device] $devices )
    433 	fi
    434 	return 1
    435 }
    436 
    437 
    438 # set up LVM2 physical volume
    439 confz_physical_volume_check() {
    440 	checkvars device
    441 	do_command=( lvm pvcreate $vars[device] )
    442 	fs_check_type $vars[device] LVM2_member
    443 }
    444 
    445 
    446 # configure LVM2 volume group
    447 confz_volume_group_check() {
    448 	local -A devices volumes
    449 	local -a vg_devices
    450 	local device
    451 
    452 	[[ -n ${vars[vg_name]:=${DEFAULT_VG}} ]] || \
    453 		die "$0: DEFAULT_VG is unset and no 'vg_name' was passed"
    454 
    455 	checkvars vg_devices
    456 
    457 	vg_devices=( "${(Q@)${(z)vars[vg_devices]}}" )
    458 	for device in $vg_devices; do
    459 		require physical_volume device=$device
    460 	done
    461 
    462 	volumes=( $(lvm vgs --noheadings -o vg_name,pv_count) ) || \
    463 		die "$0: lvm vgs command returned error"
    464 	if ! (($+volumes[${vars[vg_name]}])); then
    465 		do_command=( lvm vgcreate ${vars[vg_name]} $vg_devices )
    466 		return 1
    467 	fi
    468 	devices=( $(lvm pvs --noheadings -o pv_name,vg_name) ) || \
    469 		die "$0: lvm pvs command returned error"
    470 	do_command=( lvm vgextend ${vars[vg_name]} )
    471 	for device in $vg_devices; do
    472 		[[ $+devices[$device] == 1 && $devices[$device] == $vars[vg_name] ]] || \
    473 			do_command+=( $device )
    474 	done
    475 	(( $#do_command == 3 ))
    476 }
    477 
    478 
    479 # configure LVM2 logical volume
    480 confz_logical_volume_check() {
    481 	[[ -n ${vars[vg_name]:=${DEFAULT_VG}} ]] || \
    482 		die "$0: DEFAULT_VG is unset and no 'vg_name' was passed"
    483 
    484 	[[ -n ${vars[size]:=${DEFAULT_VOLUME_SIZE}} ]] || \
    485 		die "$0: DEFAULT_VOLUME_SIZE is unset and no 'size' was passed"
    486 
    487 	checkvars lv_name
    488 
    489 	setvar device /dev/mapper/$vars[vg_name]-$vars[lv_name]
    490 
    491 	do_command=(
    492 		lvcreate
    493 		--name ${vars[lv_name]}
    494 		--size ${vars[size]}
    495 		${vars[vg_name]}
    496 	)
    497 
    498 	fail_reason="not a block device: ${vars[device]}"
    499 	[[ -b ${vars[device]} ]]
    500 }
    501 
    502 # configure LVM2 logical volume
    503 confz_logical_volume_extents_check() {
    504 	[[ -n ${vars[vg_name]:=${DEFAULT_VG}} ]] || \
    505 		die "$0: DEFAULT_VG is unset and no 'vg_name' was passed"
    506 
    507 	checkvars lv_name pv_name extents
    508 
    509 	local vg_name lv_name pv_name pvseg_start pvseg_size segtype
    510 	local -A found
    511 	pvs --segments --noheadings --separator $'\t' \
    512 		-o vg_name,lv_name,pv_name,pvseg_start,pvseg_size,segtype \
    513 		| while IFS=$'\t' \
    514 		read vg_name lv_name pv_name pvseg_start pvseg_size segtype
    515 	do
    516 		vg_name=${vg_name/# #}  # strip preceding space
    517 		if matchvars vg_name $vg_name lv_name $lv_name; then
    518 			(($#found)) && die "$0: more than one segment found"
    519 			if [[ $segtype != linear ]] || ! matchvars pv_name $pv_name offset $pvseg_start extents $pvseg_size; then
    520 				die "$0: non-matching physical layout of the volume"
    521 			fi
    522 			found[offset]=$pvseg_start
    523 		fi
    524 	done
    525 	unify "${(kv@)found}" \
    526 		device /dev/mapper/$vars[vg_name]-$vars[lv_name]
    527 
    528 	do_command=(
    529 		lvcreate
    530 		--name ${vars[lv_name]}
    531 		--extents ${vars[extents]}
    532 		${vars[vg_name]}
    533 		$vars[pv_name]:$vars[offset]-$[ $vars[offset] + $vars[extents] ]
    534 	)
    535 
    536 	if ! (($#found)); then
    537 		fail_reason="not found in pvs output: ${vars[vg_name]}/${vars[lv_name]}"
    538 		return 1
    539 	fi
    540 
    541 	fail_reason="not a block device: ${vars[device]}"
    542 	[[ -b ${vars[device]} ]]
    543 }
    544 
    545 
    546 # create filesystem on block device
    547 confz_filesystem_check() {
    548 	[[ -n ${vars[filesystem]:=${DEFAULT_FS}} ]] || \
    549 		die "$0: DEFAULT_FS is unset and no 'filesystem' was passed"
    550 
    551 	checkvars label device filesystem
    552 	defvar mkfs_opts ''
    553 
    554 	[[ -b ${vars[device]} ]] || \
    555 		die "$0: not a block device: ${(qqq)vars[device]}"
    556 
    557 	do_command=(
    558 		mkfs -t ${vars[filesystem]}
    559 	)
    560 	case $vars[filesystem] in
    561 		(xfs|btrfs|ext[234])
    562 			do_command+=( -L "${vars[label]}" );;
    563 		(reiserfs)
    564 			# Asks questions without -q
    565 			do_command+=( -l "${vars[label]}" -q );;
    566 		(vfat)
    567 			do_command+=( -n "${vars[label]}" );;
    568 		(*)
    569 			if [[ -n $vars[label] ]]; then
    570 				die "$0: I don't know how to set label on ${(qqq)vars[filesystem]}"
    571 			fi
    572 			;;
    573 	esac
    574 	[[ -n $vars[mkfs_opts] ]] && do_command+=( "${(Q@)${(z)vars[mkfs_opts]}}"  )
    575 	do_command+=( ${vars[device]} )
    576 
    577 	local tries # blk_out DEVNAME LABEL UUID TYPE SEC_TYPE PARTLABEL PARTUUID
    578 
    579 	tries=10
    580 	while ((tries)); do
    581 		fs_blkid_probe $vars[device]
    582 		[[ $fs_blkid_result == 0 ]] && break
    583 		tries=$[$tries - 1]
    584 	done
    585 
    586 	fail_reason="no blkid signature found on $vars[device]"
    587 	# Check if anything was detected at all
    588 	(( $#fs_blkid_output )) || return 1
    589 
    590 	# Check if it is a filesystem
    591 	[[ -z ${fs_blkid_output[TYPE]:-} && -z ${fs_blkid_output[LABEL]:-} && -z ${fs_blkid_output[UUID]:-} ]] && return 1
    592 
    593 	# Check if it matches requirements
    594 	[[ ${fs_blkid_output[LABEL]:-} == ${vars[label]:-} && $fs_blkid_output[TYPE] == $vars[filesystem] ]] && return 0
    595 
    596 	die "$0: filesystem already present on ${(qqq)vars[device]}!"
    597 }
    598 
    599 
    600 # put mountpoint for device into /etc/fstab
    601 confz_fstab_check() {
    602 	checkvars device mountpoint filesystem
    603 	defvar opts noatime
    604 	defvar dump 0
    605 	defvar fstab /etc/fstab
    606 	if [[ $vars[filesystem] == xfs ]]; then
    607 		defvar pass 0
    608 	else
    609 		defvar pass 2
    610 	fi
    611 
    612 	local device mountpoint filesystem opts dump pass
    613 	sed '/^[ \t]*#/d;s/#.*//;s/[ \t]\+/ /g' $vars[fstab] | \
    614 	while read device mountpoint filesystem opts dump pass; do
    615 		if [[ $mountpoint == ${vars[mountpoint]} ]]; then
    616 			[[ $device == ${vars[device]} && opts != *bind* ]] || \
    617 				die "$0: $mountpoint already present" \
    618 					"with different device than ${(qqq)vars[device]}"
    619 
    620 			[[ $filesystem == ${vars[filesystem]} ]] || \
    621 				die "$0: $mountpoint already present" \
    622 					"with different filesystem than ${(qqq)vars[filesystem]}"
    623 
    624 			[[ $opts == ${vars[opts]} ]] || \
    625 				die "$0: $mountpoint already present" \
    626 					"with different opts than ${(qqq)vars[opts]}"
    627 
    628 			[[ $dump == ${vars[dump]} ]] || \
    629 				die "$0: $mountpoint already present" \
    630 					"with different dump than ${(qqq)vars[dump]}"
    631 
    632 			[[ $pass == ${vars[pass]} ]] || \
    633 				die "$0: $mountpoint already present" \
    634 					"with different pass than ${(qqq)vars[pass]}"
    635 
    636 			return 0  # found matching entry
    637 		fi
    638 	done
    639 
    640 	fail_reason="no entry for ${(qqq)vars[mountpoint]} in ${(qqq)vars[fstab]}"
    641 	return 1  # did not find matching entry
    642 }
    643 
    644 confz_fstab_do() {
    645 	print -r - >>$vars[fstab] ${vars[device]}$'\t'${vars[mountpoint]}$'\t'${vars[filesystem]}$'\t'${vars[opts]}$'\t'${vars[dump]}' '${vars[pass]}
    646 }
    647 
    648 
    649 # make device mounted on mountpoint
    650 confz_mounted_check() {
    651 	checkvars device mountpoint
    652 
    653 	fail_reason="could not find ${vars[device]} ${vars[mountpoint]} in /proc/mounts"
    654 	grep -q "^${vars[device]} ${vars[mountpoint]} " /proc/mounts
    655 }
    656 
    657 confz_mounted_do() {
    658 	mkdir -p ${vars[mountpoint]} || return $?
    659 	mount "$@" ${vars[device]} ${vars[mountpoint]}
    660 }
    661 
    662 
    663 # make device mounted on mountpoint
    664 confz_bind_mounted_check() {
    665 	checkvars device origin mountpoint
    666 
    667 	fail_reason="could not find ${vars[device]} ${vars[mountpoint]} in /proc/mounts"
    668 	grep -q "^${vars[device]} ${vars[mountpoint]} " /proc/mounts
    669 }
    670 
    671 confz_bind_mounted_do() {
    672 	mkdir -p ${vars[mountpoint]} || return $?
    673 	mount --bind "$@" ${vars[origin]} ${vars[mountpoint]}
    674 }
    675 
    676 
    677 # create LVM2 logical volume, and make sure it's in fstab and mounted
    678 confz_mounted_volume_check() {
    679 	checkvars mountpoint
    680 	defvar lv_name ${${${vars[mountpoint]}##/}//\//_}
    681 	defvar filesystem xfs
    682 	defvar label ${${vars[lv_name]}[1,12]}
    683 	defvar root /
    684 
    685 	[[ $vars[mountpoint] == /* ]] || \
    686 			die "$0: mountpoint must be absolute path, got: ${(qqq)vars[mountpoint]}"
    687 	require logical_volume %device \?vg_name :size :lv_name
    688 	require filesystem :device :label :filesystem \?mkfs_opts
    689 	require fstab fstab=${vars[root]%/}/etc/fstab \
    690 		:device :mountpoint :filesystem \?opts \?dump \?pass
    691 	require mounted :device mountpoint=${vars[root]%/}/${vars[mountpoint]#/}
    692 
    693 	do_command=( true )
    694 }
    695 
    696 
    697 confz_syslinux_modules_check() {
    698 	checkvars directory
    699 	if ! [[ -e $vars[directory]/menu.c32 ]]; then
    700 		do_command=( cp -v /usr/share/syslinux/*.c32 $vars[directory]/ )
    701 		return 1
    702 	else
    703 		return 0
    704 	fi
    705 }
    706 
    707 # install extlinux
    708 confz_extlinux_check() {
    709 	checkvars directory
    710 	defvar extlinux /sbin/extlinux
    711 	defvar install_touch_file $vars[directory]/.extlinux_installed
    712 	[[ -e $vars[directory]/extlinux.conf || -e $vars[directory]/syslinux.cfg ]] || \
    713 		die "no configuration file for extlinux found"
    714 	require syslinux_modules :directory
    715 	[[ -e $vars[install_touch_file] ]]
    716 	return $?
    717 }
    718 
    719 confz_extlinux_do() {
    720 	$vars[extlinux] -i -r $vars[directory] || return $?
    721 	touch $vars[install_touch_file] || return $?
    722 }
    723 
    724 
    725 # install GRUB2
    726 confz_grub2_check() {
    727 	checkvars device
    728 	defvar boot_directory /boot
    729 	defvar install_touch_file vars[boot_directory]/.grub2${${vars[device]}//\//.}
    730 	[[ -e $vars[install_touch_file] ]]
    731 	return $?
    732 }
    733 
    734 confz_grub2_do() {
    735 	grub2-install --boot-directory=$vars[boot_directory] $vars[device] || return $?
    736 	touch $vars[install_touch_file] || return $?
    737 }