setup-disk.in 32.5 KB
Newer Older
Natanael Copa's avatar
Natanael Copa committed
1 2 3 4
#!/bin/sh

PREFIX=
. "$PREFIX/lib/libalpine.sh"
5
. "$PREFIX/lib/dasd-functions.sh"
Natanael Copa's avatar
Natanael Copa committed
6

7
MBR=${MBR:-"/usr/share/syslinux/mbr.bin"}
8 9
ROOTFS=${ROOTFS:-ext4}
BOOTFS=${BOOTFS:-ext4}
10
VARFS=${VARFS:-ext4}
11 12
BOOTLOADER=${BOOTLOADER:-syslinux}
DISKLABEL=${DISKLABEL:-dos}
Natanael Copa's avatar
Natanael Copa committed
13

14 15 16
# default location for mounted root
SYSROOT=${SYSROOT:-/mnt}

17 18 19
# machine arch
ARCH=$(apk --print-arch)

Natanael Copa's avatar
Natanael Copa committed
20 21 22 23 24 25 26 27 28 29
in_list() {
	local i="$1"
	shift
	while [ $# -gt 0 ]; do
		[ "$i" = "$1" ] && return 0
		shift
	done
	return 1
}

30 31 32 33 34 35 36 37 38 39 40
all_in_list() {
	local needle="$1"
	local i
	[ -z "$needle" ] && return 1
	shift
	for i in $needle; do
		in_list "$i" $@ || return 1
	done
	return 0
}

41 42 43 44 45 46 47 48
# wrapper to only show given device
_blkid() {
	blkid | grep "^$1:"
}

# if given device have an UUID display it, otherwise return the device
uuid_or_device() {
	local i=
49 50 51
	case "$1" in
		/dev/md*) echo "$1" && return 0;;
	esac
52 53 54 55 56 57 58 59 60 61 62 63 64
	for i in $(_blkid "$1"); do
		case "$i" in
			UUID=*) eval $i;;
		esac
	done
	if [ -n "$UUID" ]; then
		echo "UUID=$UUID"
	else
		echo "$1"
	fi
}

# generate an fstab from a given mountpoint. Convert to UUID if possible
65
enumerate_fstab() {
66
	local mnt="$1"
67
	local fs_spec= fs_file= fs_vfstype= fs_mntops= fs_freq= fs_passno=
68
	[ -z "$mnt" ] && return
69 70
	local escaped_mnt=$(echo $mnt | sed -e 's:/*$::' -e 's:/:\\/:g')
	awk "\$2 ~ /^$escaped_mnt(\/|\$)/ {print \$0}" /proc/mounts | \
71
		sed "s:$mnt:/:g; s: :\t:g" | sed -E 's:/+:/:g' | \
72
		while read fs_spec fs_file fs_vfstype fs_mntops fs_freq fs_passno; do
73
			if [ "$fs_file" = / ]; then
74 75 76 77
				fs_passno=1
			else
				fs_passno=2
			fi
78 79
			echo -e "$(uuid_or_device $fs_spec)\t${fs_file}\t${fs_vfstype}\t${fs_mntops} ${fs_freq} ${fs_passno}"
		done
80 81
}

82 83 84 85 86
is_vmware() {
	grep -q VMware /proc/scsi/scsi 2>/dev/null \
		|| grep -q VMware /proc/ide/hd*/model 2>/dev/null
}

87 88 89 90 91
# return true (0) if given device is lvm
is_lvm() {
	lvs "$1" >/dev/null 2>&1
}

92 93 94 95 96
is_efi() {
	[ -d "/sys/firmware/efi" ] && return 0
	return 1
}

97 98
# Find the disk device from given partition
disk_from_part() {
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	local path=${1%/*}
	local dev=${1##*/}
	echo $path/$(basename "$(readlink -f "/sys/class/block/$dev/..")")
}

# $1 partition type (swap,linux,raid,lvm,prep,esp)
# return partition type id based on table type
partition_id() {
	local id
	if [ "$DISKLABEL" = "gpt" ]; then
		case "$1" in
			swap)	id=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F ;;
			linux)	id=0FC63DAF-8483-4772-8E79-3D69D8477DE4 ;;
			raid)	id=A19D880F-05FC-4D3B-A006-743F0F84911E ;;
			lvm)	id=E6D6D379-F507-44C2-A23C-238F2A3DF928 ;;
			prep)	id=9E1A2d38-C612-4316-AA26-8B49521E5A8B ;;
			esp)	id=C12A7328-F81F-11D2-BA4B-00A0C93EC93B ;;
			*)	die "Partition id \"$1\" is not supported!" ;;
		esac
	elif [ "$DISKLABEL" = "dos" ]; then
		case "$1" in
			swap)	id=82 ;;
			linux)	id=83 ;;
			raid)	id=fd ;;
			lvm)	id=8e ;;
			prep)	id=41 ;;
			esp)	id=EF ;;
			*)	die "Partition id \"$1\" is not supported!" ;;
		esac
128 129 130 131
	elif [ "$DISKLABEL" = "eckd" ]; then
		case "$1" in
			native|lvm|swap|raid|gpfs)	id="$1" ;;
		esac
132 133 134 135 136 137 138 139 140 141
	else
		die "Partition label \"$DISKLABEL\" is not supported!"
	fi
	echo $id
}

# find partitions based on partition type from specified disk
# type can be any type from partition_id or the literal string "boot"
find_partitions() {
	local dev="$1" type="$2" search=
142 143 144 145 146 147 148
	if is_dasd "$dev" eckd; then
		case "$type" in
			boot) echo "$dev"1 ;;
			*) fdasd -p "$dev" | grep "Linux $(partition_id "$type")" | awk '{print $1}' | tail -n 1 ;;
		esac
		return 0
	fi
149 150 151 152 153 154 155 156 157 158 159
	case "$type" in
		boot)
			search=bootable
			[ -n "$USE_EFI" ] && search=$(partition_id esp)
			sfdisk -d "$dev" | awk '/'$search'/ {print $1}'
			;;
		*)
			search=$(partition_id "$type")
			sfdisk -d "$dev" | awk '/type='$search'/ {print $1}'
			;;
	esac
160 161
}

162 163 164 165 166 167 168 169 170 171 172 173 174 175
unpack_apkovl() {
	local ovl="$1"
	local dest="$2"
	local suffix=${ovl##*.}
	local i
	ovlfiles=/tmp/ovlfiles
	if [ "$suffix" = "gz" ]; then
		if ! tar -C "$dest" --numeric-owner -zxvf "$ovl" > $ovlfiles; then
			echo -n "Continue anyway? [Y/n]: "
			read i
			case "$i" in
				n*|N*) return 1;;
			esac
		fi
176
		return 0
177 178
	fi

179
	apk add --quiet openssl
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196

	if ! openssl list-cipher-commands | grep "^$suffix$" > /dev/null; then
		errstr="Cipher $suffix is not supported"
		return 1
	fi
	local count=0
	# beep
	echo -e "\007"
	while [ $count -lt 3 ]; do
		openssl enc -d -$suffix -in "$ovl" | tar --numeric-owner \
			-C "$dest" -zxv >$ovlfiles 2>/dev/null && return 0
		count=$(( $count + 1 ))
	done
	ovlfiles=
	return 1
}

197 198 199 200 201 202 203 204
# find filesystem of given mounted dir
find_mount_fs() {
	local mount_point="$1"
	awk "\$2 == \"$mount_point\" {print \$3}" /proc/mounts | tail -n 1
}

# find device for given mounted dir
find_mount_dev() {
205
	local mnt="$1"
206 207 208 209
	awk "\$2 == \"$mnt\" { print \$1 }" /proc/mounts | tail -n 1
}

supported_boot_fs() {
210
	local supported="ext2 ext3 ext4 btrfs xfs vfat"
211 212 213 214 215 216 217 218
	local fs=
	for fs in $supported; do
		[ "$fs" = "$1" ] && return 0
	done
	echo "$1 is not supported. Only supported are: $supported" >&2
	return 1
}

219 220
supported_part_label() {
	case "$1" in
221
		dos|gpt|eckd) return 0 ;;
222 223 224 225
		*) die "Partition label \"$DISKLABEL\" is not supported!" ;;
	esac
}

226 227
find_volume_group() {
	local lv=${1##*/}
228
	lvs --noheadings "$1" | awk '{print $2}'
229 230 231 232 233 234 235
}

find_pvs_in_vg() {
	local vg="$1"
	pvs --noheadings | awk "\$2 == \"$vg\" {print \$1}"
}

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
# echo current grsecurity option and set new
set_grsec() {
	local key="$1" value="$2"
	if ! [ -e /proc/sys/kernel/grsecurity/$key ]; then
		return 0
	fi
	cat /proc/sys/kernel/grsecurity/$key
	echo $value > /proc/sys/kernel/grsecurity/$key
}

init_chroot_mounts() {
	local mnt="$1" i=
	for i in proc dev; do
		mkdir -p "$mnt"/$i
		mount --bind /$i "$mnt"/$i
	done
}

cleanup_chroot_mounts() {
	local mnt="$1" i=
	for i in proc dev; do
		umount "$mnt"/$i
	done
}

261
get_bootopt() {
262 263 264
	local opt="$1"
	set -- $(cat /proc/cmdline)
	for i; do
265 266 267
		case "$i" in
			"$opt"|"$opt"=*) echo "${i#*=}"; break;;
		esac
268 269 270
	done
}

271
# setup GRUB bootloader
272
setup_grub() {
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
	local mnt="$1" root="$2" modules="$3" kernel_opts="$4" bootdev="$5"
	# install GRUB efi mode
	if [ -n "$USE_EFI" ]; then
		local target fwa
		case "$ARCH" in
			x86_64)		target=x86_64-efi ; fwa=x64 ;;
			x86)		target=i386-efi ; fwa=ia32 ;;
			arm*)		target=arm-efi ; fwa=arm ;;
			aarch64)	target=arm64-efi ; fwa=aa64 ;;
		esac
		# currently disabling nvram so grub doesnt call efibootmgr
		# installing to alpine directory so other distros dont overwrite it
		grub-install --target=$target --efi-directory="$mnt"/boot/efi \
			--bootloader-id=alpine --boot-directory="$mnt"/boot --no-nvram
		# fallback mode will use boot/boot${fw arch}.efi
		install -D "$mnt"/boot/efi/EFI/alpine/grub$fwa.efi \
			"$mnt"/boot/efi/EFI/boot/boot$fwa.efi
	# install GRUB for ppc64le
	elif [ "$ARCH" = "ppc64le" ]; then
		shift 5
		local disks="${@}"
		for disk in $disks; do
			prep=$(find_partitions "$disk" "prep")
			echo "Installing grub on $prep"
			grub-install --boot-directory="$mnt"/boot $prep
		done
	# install GRUB in bios mode
	else
		local bootdisk=$(disk_from_part $bootdev)
		case "$ARCH" in
			x86|x86_64) grub-install --boot-directory="$mnt"/boot \
				--target=i386-pc $bootdisk ;;
			*) die "Cannot install GRUB in BIOS mode for $ARCH" ;;
		esac
	fi
308

309 310 311 312 313 314 315
	# setup GRUB config. trigger will generate final grub.cfg
	install -d "$mnt"/etc/default/
	cat > "$mnt"/etc/default/grub <<- EOF
	GRUB_TIMEOUT=2
	GRUB_DISABLE_SUBMENU=y
	GRUB_DISABLE_RECOVERY=true
	GRUB_CMDLINE_LINUX_DEFAULT="modules=$modules $kernel_opts"
316 317 318 319 320
	EOF
}

# setup syslinux bootloader
setup_syslinux() {
321 322
	local mnt="$1" root="$2" modules="$3" kernel_opts="$4" bootdev="$5"
	local exlinux_raidopt=
323 324 325 326 327 328 329 330 331

	sed -e "s:^root=.*:root=$root:" \
		-e "s:^default_kernel_opts=.*:default_kernel_opts=\"$kernel_opts\":" \
		-e "s:^modules=.*:modules=$modules:" \
		/etc/update-extlinux.conf > "$mnt"/etc/update-extlinux.conf
	if [ "$(rc --sys)" = "XEN0" ]; then
		sed -i -e "s:^default=.*:default=xen-grsec:" \
			"$mnt"/etc/update-extlinux.conf
	fi
332 333 334 335 336 337 338

	# Check if we boot from raid so we can pass proper option to
	# extlinux later.
	if [ -e "/sys/block/${bootdev#/dev/}/md" ]; then
		extlinux_raidopt="--raid"
	fi

339 340 341
	extlinux $extlinux_raidopt --install "$mnt"/boot
}

342
install_mounted_root() {
343 344 345
	local mnt="$1"
	shift 1
	local disks="${@}" mnt_boot= boot_fs= root_fs=
346
	local initfs_features="ata base ide scsi usb virtio"
347
	local pvs= dev= rootdev= bootdev= extlinux_raidopt= root= modules=
348
	local kernel_opts="quiet"
349
	[ "$ARCH" = "s390x" ] && initfs_features="$initfs_features qeth dasd_mod"
350

351
	rootdev=$(find_mount_dev "$mnt")
352 353 354 355
	if [ -z "$rootdev" ]; then
		echo "$mnt does not seem to be a mount point" >&2
		return 1
	fi
356
	root_fs=$(find_mount_fs "$mnt")
357 358 359 360
	initfs_features="$initfs_features $root_fs"

	if is_lvm "$rootdev"; then
		initfs_features="$initfs_features lvm"
361 362
		local vg=$(find_volume_group "$rootdev")
		pvs=$(find_pvs_in_vg $vg)
363 364
	fi

365

366 367 368
	bootdev=$(find_mount_dev "$mnt"/boot)
	if [ -z "$bootdev" ]; then
		bootdev=$rootdev
369
		mnt_boot="$mnt"
370 371 372 373 374 375
	else
		mnt_boot="$mnt"/boot
	fi
	boot_fs=$(find_mount_fs "$mnt_boot")
	supported_boot_fs "$boot_fs" || return 1

376
	# check if our root is on raid so we can feed mkinitfs and
377
	# bootloader conf with the proper kernel module params
378
	for dev in $rootdev $pvs; do
379 380 381 382 383 384

		# check if we need hardware raid drivers
		case $dev in
		/dev/cciss/*)
			initfs_features="${initfs_features% raid} raid"
			;;
385 386 387 388 389 390
		/dev/nvme*)
			initfs_features="${initfs_features% nvme} nvme"
			;;
		/dev/mmc*)
			initfs_features="${initfs_features% mmc} mmc"
			;;
391 392
		esac

393 394 395
		[ -e "/sys/block/${dev#/dev/}/md" ] || continue

		local md=${dev#/dev/}
396
		initfs_features="${initfs_features% raid} raid"
397 398
		local level=$(cat /sys/block/$md/md/level)
		case "$level" in
399 400
			raid1) raidmod="${raidmod%,raid1},raid1";;
			raid[456]) raidmod="${raidmod%,raid456},raid456";;
401 402
		esac
	done
403 404


405 406 407 408 409 410
	if [ -n "$VERBOSE" ]; then
		echo "Root device:     $rootdev"
		echo "Root filesystem: $root_fs"
		echo "Boot device:     $bootdev"
		echo "Boot filesystem: $boot_fs"
	fi
411

412
	if [ -z "$APKOVL" ]; then
413 414
		ovlfiles=/tmp/ovlfiles
		lbu package - | tar -C "$mnt" -zxv > "$ovlfiles"
415 416 417 418
		# comment out local repositories
		if [ -f "$mnt"/etc/apk/repositories ]; then
			sed -i -e 's:^/:#/:' "$mnt"/etc/apk/repositories
		fi
419 420
	else
		echo "Restoring backup from $APKOVL to $rootdev..."
421
		unpack_apkovl "$APKOVL" "$mnt" || return 1
422
	fi
423

424 425 426
	# we should not try start modloop on sys install
	rm -f "$mnt"/etc/runlevels/*/modloop

427
	# generate mkinitfs.conf
428
	mkdir -p "$mnt"/etc/mkinitfs/features.d
429
	echo "features=\"$initfs_features\"" > "$mnt"/etc/mkinitfs/mkinitfs.conf
430
	if [ -n "$raidmod" ]; then
431 432
		echo "/sbin/mdadm" > "$mnt"/etc/mkinitfs/features.d/raid.files
		echo "/etc/mdadm.conf" >> "$mnt"/etc/mkinitfs/features.d/raid.files
433
	fi
434

435 436
	# generate update-extlinux.conf
	root=$(uuid_or_device $rootdev)
437
	kernel_opts="$kernel_opts rootfstype=$root_fs"
438 439 440
	if is_vmware; then
		kernel_opts="pax_nouderef $kernel_opts"
	fi
441
	if [ -n "$(get_bootopt nomodeset)" ]; then
442 443
		kernel_opts="nomodeset $kernel_opts"
	fi
444 445
	modules="sd-mod,usb-storage,${root_fs}${raidmod}"

446 447 448 449 450
	# generate the fstab
	if [ -f "$mnt"/etc/fstab ]; then
		mv "$mnt"/etc/fstab "$mnt"/etc/fstab.old
	fi
	enumerate_fstab "$mnt" >> "$mnt"/etc/fstab
451 452 453 454 455 456
	if [ -n "$SWAP_DEVICES" ]; then
		local swap_dev
		for swap_dev in $SWAP_DEVICES; do
			echo -e "$(uuid_or_device ${swap_dev})\tswap\tswap\tdefaults\t0 0" \
				>> "$mnt"/etc/fstab
		done
457
	fi
458 459 460 461
	cat >>"$mnt"/etc/fstab <<-__EOF__
		/dev/cdrom	/media/cdrom	iso9660	noauto,ro 0 0
		/dev/usbdisk	/media/usb	vfat	noauto	0 0
	__EOF__
462
	# remove the installed db in case its there so we force re-install
463
	rm -f "$mnt"/var/lib/apk/installed "$mnt"/lib/apk/db/installed
464
	echo "Installing system on $rootdev:"
465 466 467
	case "$BOOTLOADER" in
		grub) setup_grub "$mnt" "$root" "$modules" "$kernel_opts" "$bootdev" $disks ;;
		syslinux) setup_syslinux "$mnt" "$root" "$modules" "$kernel_opts" "$bootdev" ;;
468
		zipl) setup_zipl "$mnt" "$root" "$modules" "$kernel_opts" ;;
469 470
		*) die "Bootloader \"$BOOTLOADER\" not supported!" ;;
	esac
471

472 473 474 475
	# apk reads config from target root so we need to copy the config
	mkdir -p "$mnt"/etc/apk/keys/
	cp /etc/apk/keys/* "$mnt"/etc/apk/keys/

476
	local apkflags="--initdb --quiet --progress --update-cache --clean-protected"
477 478 479
	local pkgs=$(grep -h -v -w sfdisk "$mnt"/etc/apk/world \
		"$mnt"/var/lib/apk/world 2>/dev/null)

480
	pkgs="$pkgs acct linux-$KERNEL_FLAVOR alpine-base"
481 482 483
	if [ "$(rc --sys)" = "XEN0" ]; then
		pkgs="$pkgs xen-hypervisor"
	fi
484 485 486 487 488
	local repos=$(sed -e 's/\#.*//' /etc/apk/repositories)
	local repoflags=
	for i in $repos; do
		repoflags="$repoflags --repository $i"
	done
489

490 491
	chroot_caps=$(set_grsec chroot_caps 0)
	init_chroot_mounts "$mnt"
492
	apk add --root "$mnt" $apkflags --overlay-from-stdin \
Natanael Copa's avatar
Natanael Copa committed
493
		$repoflags $pkgs <$ovlfiles
494 495 496 497
	local ret=$?
	cleanup_chroot_mounts "$mnt"
	set_grsec chroot_caps $chroot_caps > /dev/null
	return $ret
498 499 500 501
}

unmount_partitions() {
	local mnt="$1"
502 503

	# unmount the partitions
504
	umount $(awk '{print $2}' /proc/mounts | egrep "^$mnt(/|\$)" | sort -r)
505
}
506

507 508 509
# figure out decent default swap size in mega bytes
find_swap_size() {
	local memtotal_kb=$(awk '$1 == "MemTotal:" {print $2}' /proc/meminfo)
510 511 512 513
	# use 2 * avaiable ram or no more than 1/3 of smallest disk space
	local size=$(( $memtotal_kb * 2 / 1024 ))
	local disk= disksize=
	for disk in $@; do
514 515
		local sysfsdev=$(echo ${disk#/dev/} | sed 's:/:!:g')
		local sysfspath=/sys/block/$sysfsdev/size
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
		# disksize = x * 512 / (1024 * 1024) = x / 2048
		# maxsize = $disksize / 4 = x / (2048 * 4) = x / 8192
		maxsize=$(awk '{ printf "%i", $0 / 8192 }' $sysfspath )
		if [ $size -gt $maxsize ]; then
			size=$maxsize
		fi
	done
	if [ $size -gt 4096 ]; then
		# dont ever use more than 4G
		size=4096
	elif [ $size -lt 64 ]; then
		# dont bother create swap smaller than 64MB
		size=0
	fi
	echo $size
531 532 533 534
}

has_mounted_part() {
	local p
535
	local sysfsdev=$(echo ${1#/dev/} | sed 's:/:!:g')
Natanael Copa's avatar
Natanael Copa committed
536
	# parse /proc/mounts for mounted devices
537
	for p in $(awk '$1 ~ /^\/dev\// {gsub("/dev/", "", $1); gsub("/", "!", $1); print $1}' \
538
			/proc/mounts); do
539 540
		[ "$p" = "$sysfsdev" ] && return 0
		[ -e /sys/block/$sysfsdev/$p ] && return 0
541 542 543 544
	done
	return 1
}

Natanael Copa's avatar
Natanael Copa committed
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
has_holders() {
	local i
	# check if device is used by any md devices
	for i in $1/holders/* $1/*/holders/*; do
		[ -e "$i" ] && return 0
	done
	return 1
}

is_available_disk() {
	local dev=$1
	local b=$(echo $p | sed 's:/:!:g')

	# check if its a "root" block device and not a partition
	[ -e /sys/block/$b ] || return 1
560

Natanael Copa's avatar
Natanael Copa committed
561 562 563 564
	# check so it does not have mounted partitions
	has_mounted_part $dev && return 1

	# check so its not part of an md setup
565 566 567 568
	if has_holders /sys/block/$b; then
		[ -n "$USE_RAID" ] && echo "Warning: $dev is part of a running raid" >&2
		return 1
	fi
Natanael Copa's avatar
Natanael Copa committed
569 570 571 572 573 574 575

	# check so its not an md device
	[ -e /sys/block/$b/md ] && return 1

	return 0
}

576
find_disks() {
Natanael Copa's avatar
Natanael Copa committed
577
	local p=
578 579
	# filter out ramdisks (major=1)
	for p in $(awk '$1 != 1 && $1 ~ /[0-9]+/ {print $4}' /proc/partitions); do
Natanael Copa's avatar
Natanael Copa committed
580
		is_available_disk $p && echo -n " $p"
581 582 583
	done
}

584 585 586 587 588 589 590
stop_all_raid() {
	local rd
	for rd in /dev/md*; do
		[ -b $rd ] && mdadm --stop $rd
	done
}

591 592 593 594
select_bootloader() {
	local bootloader=syslinux
	if [ "$ARCH" = "ppc64le" ]; then
		bootloader=grub-ieee1275
595 596
	elif [ "$ARCH" = "s390x" ]; then
		bootloader=s390-tools
597 598 599 600 601 602 603 604
	elif [ -n "$USE_EFI" ]; then
		bootloader=grub-efi
	elif [ "$BOOTLOADER" = "grub" ]; then
		bootloader=grub-bios
	fi
	echo "$bootloader"
}

605 606
# install needed programs
init_progs() {
607
	local raidpkg= lvmpkg= fs= fstools= grub=
608
	[ -n "$USE_RAID" ] && raidpkg="mdadm"
609
	[ -n "$USE_LVM" ] && lvmpkg="lvm2"
610 611 612
	for fs in $BOOTFS $ROOTFS $VARFS; do
		# we need load btrfs module early to avoid the error message:
		# 'failed to open /dev/btrfs-control'
613 614 615
		if ! grep -q -w "$fs" /proc/filesystems; then
			modprobe $fs
		fi
616 617 618 619 620

		case $fs in
		xfs) fstools="$fstools xfsprogs";;
		ext*) fstools="$fstools e2fsprogs";;
		btrfs) fstools="$fstools btrfs-progs";;
621
		vfat) fstools="$fstools dosfstools";;
622 623
		esac
	done
624
	apk add --quiet sfdisk $lvmpkg $raidpkg $fstools $@
625 626
}

627
show_disk_info() {
628
	local disk= vendor= model= d= size= busid=
629
	for disk in $@; do
630 631
		local dev=${disk#/dev/}
		d=$(echo $dev | sed 's:/:!:g')
632 633
		vendor=$(cat /sys/block/$d/device/vendor 2>/dev/null)
		model=$(cat /sys/block/$d/device/model 2>/dev/null)
634
		busid=$(readlink -f /sys/block/$d/device 2>/dev/null)
635
		size=$(awk '{gb = ($1 * 512)/1000000000; printf "%.1f GB\n", gb}' /sys/block/$d/size 2>/dev/null)
636 637 638 639 640
		if is_dasd $dev eckd; then
			echo "  $dev	($size $vendor ${busid##*/})"
		else
			echo "  $dev	($size $vendor $model)"
		fi
641 642
	done
}
643

644 645
confirm_erase() {
	local answer=
646 647
	local erasedisks="$@"
	if [ "$ERASE_DISKS" = "$erasedisks" ]; then
Natanael Copa's avatar
Natanael Copa committed
648
		return 0
649
	fi
650
	echo "WARNING: The following disk(s) will be erased:"
651
	show_disk_info $@
652
	echo -n "WARNING: Erase the above disk(s) and continue? [y/N]: "
653

654 655
	read answer
	case "$answer" in
656
		y*|Y*) return 0;;
657
	esac
658
	return 1
659 660
}

661
# setup partitions on given disk dev in $1.
662
# usage: setup_partitions <diskdev> size1,type1 [size2,type2 ...]
663
setup_partitions() {
664
	local diskdev="$1" start=1M line=
665
	shift
666
	supported_part_label "$DISKLABEL" || return 1
667

668 669
	# initialize MBR for syslinux only
	if [ "$BOOTLOADER" = "syslinux" ] && [ -f "$MBR" ]; then
670 671
		cat "$MBR" > $diskdev
	fi
672

673 674
	# create new partitions
	(
675
		for line in "$@"; do
676 677 678 679
			case "$line" in
			0M*) ;;
			*) echo "$start,$line"; start= ;;
			esac
680
		done
681
	) | sfdisk --quiet --label $DISKLABEL $diskdev
682 683 684 685 686

	# create device nodes if not exist
	mdev -s
}

687
# set up optional raid and create filesystem on boot device.
688
setup_boot_dev() {
689 690 691
	local disks="$@" disk= bootdev= mkfs_args=
	[ "$BOOTFS" != "vfat" ] && mkfs_args="-q"
	local part=$(for disk in $disks; do find_partitions "$disk" "boot"; done)
692 693
	set -- $part
	bootdev=$1
694
	[ -z "$bootdev" ] && return 1
695 696 697 698 699 700 701 702 703 704

	if [ "$ARCH" = "ppc64le" ]; then
		# Change bootable partition to PReP partition
		for disk in $disks; do
			echo ',,,*' | sfdisk --quiet $disk -N1
			echo ',,,-' | sfdisk --quiet $disk -N2
			mdev -s
		done
	fi

705
	echo "Creating file systems..."
706
	if [ -n "$USE_RAID" ]; then
707 708 709 710 711 712
		local missing=
		local num=$#
		if [ $# -eq 1 ]; then
			missing="missing"
			num=2
		fi
713
		# we only use raid level 1 for boot devices
714 715
		mdadm --create /dev/md0 --level=1 --raid-devices=$num \
			--metadata=0.90 --quiet --run $@ $missing || return 1
716 717
		bootdev=/dev/md0
	fi
718 719 720 721
	case "$BOOTFS" in
	btrfs) mkfs_args="";;
	ext4) mkfs_args="$mkfs_args -O ^64bit";; # pv-grub does not support 64bit
	esac
722
	mkfs.$BOOTFS $MKFS_OPTS_BOOT $mkfs_args $bootdev
723 724 725 726
	BOOT_DEV="$bootdev"
}

# $1 = index
727 728 729
# $2 = partition type
# $3... = disk devices
find_nth_non_boot_parts() {
730 731 732 733 734
	local idx=$1 id=$2 disk= type=bootable
	shift 2
	local disks="$@"
	[ -n "$USE_EFI" ] && type=$(partition_id esp)
	for disk in $disks; do
735 736 737 738 739 740
		if is_dasd $disk eckd; then
			fdasd -p $disk | grep "Linux $id" | awk '{print $1}' | tail -n 1
		else
			sfdisk -d $disk | grep -v $type \
				| awk "/(Id|type)=$id/ { i++; if (i==$idx) print \$1 }"
		fi
741 742 743 744 745 746
	done
}

setup_non_boot_raid_dev() {
	local md_dev=$1
	local idx=${md_dev#/dev/md}
747
	[ -z "$md_dev" ] && return 0
748 749 750 751 752
	if [ "$ARCH" = "ppc64le" ]; then
		# increment idx as PReP partition is
		# the bootable partition in ppc64le
		idx=$((idx+1))
	fi
753 754 755
	shift
	local level=1
	local missing=
756 757
	local pid=$(partition_id raid)
	local raid_parts=$(find_nth_non_boot_parts $idx $pid $@)
758 759 760 761 762 763 764 765
	set -- $raid_parts
	# how many disks do we have?
	case $# in
		0) echo "No Raid partitions found" >&2; return 1;;
		1) level=1; missing="missing"; num=2;;
		2) level=1; missing=  ; num=2;;
		*) level=5; missing=  ; num=$#;;
	esac
766
	mdadm --create $md_dev --level=$level --raid-devices=$num \
767
		--quiet --run $@ $missing || return 1
768 769 770 771
}

# setup device for lvm, create raid array if needed
setup_lvm_volume_group() {
772 773 774
	local vgname="$1"
	shift
	local lvmdev=
775 776

	if [ -n "$USE_RAID" ]; then
777 778
		setup_non_boot_raid_dev /dev/md1 $@ || return 1
		lvmdev=/dev/md1
779
	else
780
		lvmdev=$(find_partitions "$1" "lvm")
781 782 783 784 785
	fi

	# be quiet on success
	local errmsg=$(dd if=/dev/zero of=$lvmdev bs=1k count=1 2>&1) \
		|| echo "$errmsg"
786 787 788 789 790 791 792 793
	pvcreate --quiet $lvmdev \
		&& vgcreate --quiet $vgname $lvmdev >/dev/null
}

# set up swap on given device(s)
setup_swap_dev() {
	local swap_dev=
	sed -i -e '/swap/d' /etc/fstab
794
	SWAP_DEVICES=
795 796
	for swap_dev in "$@"; do
		mkswap $swap_dev >/dev/null
797 798
		echo -e "$(uuid_or_device $swap_dev)\tswap\t\tswap\tdefaults 0 0" >> /etc/fstab
		SWAP_DEVICES="$SWAP_DEVICES $swap_dev"
799 800 801
	done
	swapon -a
	rc-update --quiet add swap boot
802 803 804
}

# setup and enable swap on given volumegroup if needed
805
setup_lvm_swap() {
806
	local vgname="$1"
807
	local swapname=lv_swap
808 809 810
	if [ -z "$SWAP_SIZE" ] || [ "$SWAP_SIZE" -eq 0 ]; then
		return
	fi
811 812
	lvcreate --quiet -n $swapname -L ${SWAP_SIZE}MB $vgname
	setup_swap_dev /dev/$vgname/$swapname
813 814 815 816 817 818 819 820 821 822
}

# if /var is mounted, move out data and umount it
reset_var() {
	[ -z "$(find_mount_dev /var)" ] && return 0
	mkdir /.var
	mv /var/* /.var/ 2>/dev/null
	umount /var && 	rm -rf /var && mv /.var /var && rm -rf /var/lost+found
}

823 824 825
# set up /var on given device
setup_var() {
	local var_dev="$1"
826
	local varfs=${VARFS}
827
	echo "Creating file systems..."
828
	mkfs.$varfs $MKFS_OPTS_VAR $var_dev >/dev/null || return 1
829
	sed -i -e '/[[:space:]]\/var[[:space:]]/d' /etc/fstab
830
	echo -e "$(uuid_or_device ${var_dev})\t/var\t\t${varfs}\tdefaults 1 2" >> /etc/fstab
831 832 833 834 835 836 837

	mv /var /.var
	mkdir /var
	mount /var
	mv /.var/* /var/
	rmdir /.var

838
	service syslog --quiet condrestart
839
	setup_mdadm_conf
840 841
}

842
setup_mdadm_conf() {
843
	local mods= mod=
844 845 846
	if [ -n "$USE_RAID" ]; then
		mdadm --detail --scan > /etc/mdadm.conf
		rc-update --quiet add mdadm-raid boot
847 848 849 850 851 852
		mods=$(awk '/^raid/ {print $1}' /proc/modules)
		for mod in $mods; do
			if ! grep -q "^$mod" /etc/modules; then
				echo $mod >> /etc/modules
			fi
		done
853 854 855
	fi
}

856
data_only_disk_install_lvm() {
857
	local diskdev=
858 859
	local vgname=vg0
	local var_dev=/dev/$vgname/lv_var
860
	local lvm_part_type=$(partition_id lvm)
861
	local size=
862
	unset BOOTLOADER
863 864

	init_progs || return 1
865
	confirm_erase $@ || return 1
866

867
	if [ "$USE_RAID" ]; then
868
		lvm_part_type=$(partition_id raid)
869 870
		stop_all_raid
	fi
871

872
	for diskdev in "$@"; do
873
		setup_partitions $diskdev "${size}${size:+M},$lvm_part_type" || return 1
874
	done
875

876 877
	setup_lvm_volume_group $vgname $@ || return 1
	setup_lvm_swap $vgname
878
	lvcreate --quiet -n ${var_dev##*/} -l 100%FREE $vgname
879
	setup_mdadm_conf
Natanael Copa's avatar
Natanael Copa committed
880
	rc-update add lvm boot
881
	setup_var $var_dev
882 883
}

884 885
data_only_disk_install() {
	local diskdev=
886
	local var_dev=
887 888
	local var_part_type=$(partition_id linux)
	local swap_part_type=$(partition_id swap)
889 890
	local size=
	local swap_dev= var_dev=
891
	unset BOOTLOADER
892 893 894 895 896

	init_progs || return 1
	confirm_erase $@ || return 1

	if [ "$USE_RAID" ]; then
897 898
		var_part_type=$(partition_id raid)
		swap_part_type=$(partition_id raid)
899 900 901 902 903
		stop_all_raid
	fi

	for diskdev in "$@"; do
		setup_partitions $diskdev \
904 905
			"${SWAP_SIZE}M,$swap_part_type" \
			"${size}${size:+M},$var_part_type" || return 1
906 907 908
	done

	if [ "$USE_RAID" ]; then
909 910 911 912 913 914 915 916 917
		if [ $SWAP_SIZE -gt 0 ]; then
			swap_dev=/dev/md1
			var_dev=/dev/md2
		else
			swap_dev=
			var_dev=/dev/md1
		fi
		setup_non_boot_raid_dev "$swap_dev" $@ || return 1
		setup_non_boot_raid_dev "$var_dev" $@ || return 1
918
	else
919 920
		swap_dev=$(find_nth_non_boot_parts 1 "$swap_part_type" $@)
		var_dev=$(find_nth_non_boot_parts 1 "$var_part_type" $@)
921 922 923 924 925
	fi
	[ $SWAP_SIZE -gt 0 ] && setup_swap_dev $swap_dev
	setup_var $var_dev
}

926
# setup
927
setup_root() {
928 929 930
	local root_dev="$1" boot_dev="$2"
	shift 2
	local disks="$@" mkfs_args="-q"
Natanael Copa's avatar
Natanael Copa committed
931
	[ "$ROOTFS" = "btrfs" ] && mkfs_args=""
932
	mkfs.$ROOTFS $MKFS_OPTS_ROOT $mkfs_args "$root_dev"
933 934
	mkdir -p "$SYSROOT"
	mount -t $ROOTFS $root_dev "$SYSROOT" || return 1
935
	if [ -n "$boot_dev" ] && [ -z "$USE_EFI" ]; then
936 937
		mkdir -p "$SYSROOT"/boot
		mount -t $BOOTFS $boot_dev "$SYSROOT"/boot || return 1
938
	fi
939 940 941 942
	if [ -n "$boot_dev" ] && [ -n "$USE_EFI" ]; then
		mkdir -p "$SYSROOT"/boot/efi
		mount -t $BOOTFS $boot_dev "$SYSROOT"/boot/efi || return 1
	fi
943 944

	setup_mdadm_conf
945
	install_mounted_root "$SYSROOT" "$disks" || return 1
946
	unmount_partitions "$SYSROOT"
947 948 949
	swapoff -a

	echo ""
950
	echo "Installation is complete. Please reboot."
951 952
}

953
native_disk_install_lvm() {
954
	local diskdev= vgname=vg0
955 956
	local lvm_part_type=$(partition_id lvm)
	local boot_part_type=$(partition_id linux)
957 958 959 960
	local boot_size=${BOOT_SIZE:-100}
	local lvm_size=
	local root_dev=/dev/$vgname/lv_root

961
	init_progs $(select_bootloader) || return 1
962
	confirm_erase $@ || return 1
963 964

	if [ -n "$USE_RAID" ]; then
965 966
		boot_part_type=$(partition_id raid)
		lvm_part_type=$(partition_id raid)
967
		stop_all_raid
968
	fi
969 970 971 972 973

	if [ -n "$USE_EFI" ]; then
		boot_part_type=$(partition_id esp)
	fi

974
	for diskdev in "$@"; do
975 976 977 978 979 980 981 982 983
		if is_dasd $diskdev eckd; then
			root_part_type="lvm"
			setup_partitions_eckd $diskdev \
				$boot_size 0 $root_part_type || return 1
		else
			setup_partitions $diskdev \
				"${boot_size}M,$boot_part_type,*" \
				"${lvm_size}${lvm_size:+M},$lvm_part_type" || return 1
		fi
984 985 986 987 988 989 990
	done

	# will find BOOT_DEV for us
	setup_boot_dev $@

	setup_lvm_volume_group $vgname $@ || return 1
	setup_lvm_swap $vgname
991
	lvcreate --quiet -n ${root_dev##*/} -l ${ROOT_SIZE:-100%FREE} $vgname
Natanael Copa's avatar
Natanael Copa committed
992
	rc-update add lvm boot
993
	setup_root $root_dev $BOOT_DEV
Natanael Copa's avatar
Natanael Copa committed
994 995
}

996
native_disk_install() {
997 998 999 1000
	local prep_part_type=$(partition_id prep)
	local root_part_type=$(partition_id linux)
	local swap_part_type=$(partition_id swap)
	local boot_part_type=$(partition_id linux)
1001
	local prep_size=8
1002 1003
	local boot_size=${BOOT_SIZE:-100}
	local swap_size=${SWAP_SIZE}
1004
	local root_size=${ROOT_SIZE}
1005
	local root_dev= boot_dev= swap_dev=
1006
	init_progs $(select_bootloader) || return 1
1007 1008 1009
	confirm_erase $@ || return 1

	if [ -n "$USE_RAID" ]; then
1010 1011 1012
		boot_part_type=$(partition_id raid)
		root_part_type=$(partition_id raid)
		swap_part_type=$(partition_id raid)
1013 1014
		stop_all_raid
	fi
1015 1016 1017 1018 1019

	if [ -n "$USE_EFI" ]; then
		boot_part_type=$(partition_id esp)
	fi

1020
	for diskdev in "$@"; do
1021 1022 1023 1024 1025 1026 1027
		if [ "$ARCH" = "ppc64le" ]; then
			setup_partitions $diskdev \
				"${prep_size}M,$prep_part_type" \
				"${boot_size}M,$boot_part_type,*" \
				"${swap_size}M,$swap_part_type" \
				"${root_size}${root_size:+M},$root_part_type" \
				|| return 1
1028 1029 1030 1031 1032
		elif is_dasd $diskdev eckd; then
			swap_part_type="swap"
			root_part_type="native"
			setup_partitions_eckd $diskdev \
				$boot_size $swap_size $root_part_type || return 1
1033 1034 1035 1036 1037 1038 1039
		else
			setup_partitions $diskdev \
				"${boot_size}M,$boot_part_type,*" \
				"${swap_size}M,$swap_part_type" \
				"${root_size}${root_size:+M},$root_part_type" \
				|| return 1
		fi
1040 1041 1042 1043
	done

	# will find BOOT_DEV for us
	setup_boot_dev $@
1044

1045
	if [ "$USE_RAID" ]; then
1046 1047 1048 1049 1050 1051 1052 1053 1054
		if [ $SWAP_SIZE -gt 0 ]; then
			swap_dev=/dev/md1
			root_dev=/dev/md2
		else
			swap_dev=
			root_dev=/dev/md1
		fi
		setup_non_boot_raid_dev "$swap_dev" $@ || return 1
		setup_non_boot_raid_dev "$root_dev" $@ || return 1
1055
	else
1056
		swap_dev=$(find_nth_non_boot_parts 1 "$swap_part_type" $@)
1057 1058 1059 1060 1061 1062 1063
		local index=
		case "$ARCH" in
			# use the second non botable partition on ppc64le,
			# as PReP partition is the bootable partition
			ppc64le) index=2;;
			*) index=1;;
		esac
1064
		root_dev=$(find_nth_non_boot_parts $index "$root_part_type" $@)
1065
	fi
1066

1067
	[ $SWAP_SIZE -gt 0 ] && setup_swap_dev $swap_dev
1068
	setup_root $root_dev $BOOT_DEV $@
1069 1070
}

1071
diskselect_help() {
1072
	cat <<-__EOF__
1073

1074 1075
		The disk you select can be used for a traditional disk install or for a
		data-only install.
1076

1077
		The disk will be erased.
1078

1079
		Enter 'none' if you want to run diskless.
1080

1081
	__EOF__
1082 1083
}

1084
diskmode_help() {
1085
	cat <<-__EOF__
1086

1087
		You can select between 'sys', 'data', 'lvm', 'lvmsys' or 'lvmdata'.
1088

1089 1090 1091
		sys:
		  This mode is a traditional disk install. The following partitions will be
		  created on the disk: /boot, / (filesystem root) and swap.
1092

1093
		  This mode may be used for development boxes, desktops, virtual servers, etc.
1094

1095 1096 1097
		data:
		  This mode uses your disk(s) for data storage, not for the operating system.
		  The system itself will run from tmpfs (RAM).
1098

1099 1100
		  Use this mode if you only want to use the disk(s) for a mailspool, databases,
		  logs, etc.
1101

1102 1103
		lvm:
		  Enable logical volume manager and ask again for 'sys' or 'data'.
1104

1105 1106
		lvmsys:
		  Same as 'sys' but use logical volume manager for partitioning.
1107

1108 1109
		lvmdata:
		  Same as 'data' but use logical volume manager for partitioning.
1110

1111
	__EOF__
1112 1113 1114 1115 1116 1117 1118
}

# ask for a root or data disk
# returns answer in global variable $answer
ask_disk() {
	local prompt="$1"
	local help_func="$2"
1119
	local i=
1120 1121
	shift 2
	answer=
1122
	local default_disk=${DEFAULT_DISK:-$1}
1123

1124 1125 1126
	while ! all_in_list "$answer" $@ "none" "abort"; do
		echo "Available disks are:"
		show_disk_info "$@"
1127 1128
		echon "$prompt [$default_disk] "
		default_read answer $default_disk
1129 1130 1131 1132
		case "$answer" in
			'abort') exit 0;;
			'none') return 0;;
			'?') $help_func;;
1133 1134 1135 1136
			*) for i in $answer; do
				if ! [ -b "/dev/$i" ]; then
					echo "/dev/$i is not a block device" >&2
					answer=
1137
				fi
1138
			done;;
1139 1140 1141 1142
		esac
	done
}

1143
usage() {
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
	cat <<-__EOF__
		usage: setup-disk [-hLqrv] [-k kernelflavor] [-m MODE] [-o apkovl] [-s SWAPSIZE]
		                  [MOUNTPOINT | DISKDEV...]

		Install alpine on harddisk.

		If MOUNTPOINT is specified, then do a traditional disk install with MOUNTPOINT
		as root.

		If DISKDEV is specified, then use the specified disk(s) without asking. If
		multiple disks are specified then set them up in a RAID array. If there are
		mode than 2 disks, then use raid level 5 instead of raid level 1.

		options:
		 -h  Show this help
		 -m  Use disk for MODE without asking, where MODE is either 'data' or 'sys'
		 -o  Restore system from given apkovl file
		 -k  Use kernelflavor instead of $KERNEL_FLAVOR
		 -L  Use LVM to manage partitions
		 -q  Exit quietly if no disks are found
		 -r  Enable software raid1 with single disk
		 -s  Use SWAPSIZE MB instead of autodetecting swap size (Use 0 to disable swap)
		 -v  Be more verbose about what is happening

1168 1169 1170 1171 1172 1173 1174 1175 1176 1177
		If BOOTLOADER is specified, the specified bootloader will be used.
		If no bootloader is specified, the default bootloader is syslinux(extlinux)
		except when EFI is detected or explicitly set by USE_EFI which will select grub.
		Supported bootloaders are: grub, syslinux

		If DISKLABEL is specified, the specified partition label will be used.
		if no partition label is specified, the default label will be dos
		except when EFI is detected or explicitly set by USE_EFI which will select gpt.
		Supported partition labels are: dos, gpt

1178 1179 1180
		If BOOTFS, ROOTFS, VARFS are specified, then format a partition with specified
		filesystem. If not specified, the default filesystem is ext4.
		Supported filesystems for
1181
		  boot: ext2, ext3, ext4, btrfs, xfs, vfat(EFI)
1182 1183 1184
		  root: ext2, ext3, ext4, btrfs, xfs
		   var: ext2, ext3, ext4, btrfs, xfs
	__EOF__
1185 1186 1187
	exit 1
}

Francesco Colista's avatar
Francesco Colista committed
1188 1189
kver=$(uname -r)
case $kver in
1190 1191 1192
	*-rc[0-9]*) KERNEL_FLAVOR=vanilla;;
	*-[a-z]*) KERNEL_FLAVOR=${kver##*-};;
	*) KERNEL_FLAVOR=vanilla;;
1193 1194
esac

1195
DISK_MODE=
1196
USE_LVM=
1197
# Parse args
1198
while getopts "hk:Lm:o:qrs:v" opt; do
1199
	case $opt in
1200
		m) DISK_MODE="$OPTARG";;
1201
		k) KERNEL_FLAVOR="$OPTARG";;
1202
		L) USE_LVM="_lvm";;
1203
		o) APKOVL="$OPTARG";;
1204 1205
		q) QUIET=1;;
		r) USE_RAID=1;;