setup-disk.in 32.3 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
	# setup GRUB config

	# all_video is needed to remove the video error on boot
	cat > "$mnt"/boot/grub/grub.cfg <<- EOF
313
	set timeout=2
314
	insmod all_video
315
	menuentry "Alpine Linux" {
316
	    linux   /boot/vmlinuz-$KERNEL_FLAVOR modules=$modules root=$root $kernel_opts
317
	    initrd  /boot/initramfs-$KERNEL_FLAVOR
318 319 320 321 322 323
	}
	EOF
}

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

	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
335 336 337 338 339 340 341

	# 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

342 343 344
	extlinux $extlinux_raidopt --install "$mnt"/boot
}

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

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

	if is_lvm "$rootdev"; then
		initfs_features="$initfs_features lvm"
364 365
		local vg=$(find_volume_group "$rootdev")
		pvs=$(find_pvs_in_vg $vg)
366 367
	fi

368

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

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

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

396 397 398
		[ -e "/sys/block/${dev#/dev/}/md" ] || continue

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


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

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

427 428 429
	# we should not try start modloop on sys install
	rm -f "$mnt"/etc/runlevels/*/modloop

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

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

449 450 451 452 453
	# generate the fstab
	if [ -f "$mnt"/etc/fstab ]; then
		mv "$mnt"/etc/fstab "$mnt"/etc/fstab.old
	fi
	enumerate_fstab "$mnt" >> "$mnt"/etc/fstab
454 455 456 457 458 459
	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
460
	fi
461 462 463 464
	cat >>"$mnt"/etc/fstab <<-__EOF__
		/dev/cdrom	/media/cdrom	iso9660	noauto,ro 0 0
		/dev/usbdisk	/media/usb	vfat	noauto	0 0
	__EOF__
465
	# remove the installed db in case its there so we force re-install
466
	rm -f "$mnt"/var/lib/apk/installed "$mnt"/lib/apk/db/installed
467
	echo "Installing system on $rootdev:"
468 469 470
	case "$BOOTLOADER" in
		grub) setup_grub "$mnt" "$root" "$modules" "$kernel_opts" "$bootdev" $disks ;;
		syslinux) setup_syslinux "$mnt" "$root" "$modules" "$kernel_opts" "$bootdev" ;;
471
		zipl) setup_zipl "$mnt" "$root" "$modules" "$kernel_opts" ;;
472 473
		*) die "Bootloader \"$BOOTLOADER\" not supported!" ;;
	esac
474

475 476 477 478
	# 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/

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

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

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

unmount_partitions() {
	local mnt="$1"
505 506

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

510 511 512
# figure out decent default swap size in mega bytes
find_swap_size() {
	local memtotal_kb=$(awk '$1 == "MemTotal:" {print $2}' /proc/meminfo)
513 514 515 516
	# 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
517 518
		local sysfsdev=$(echo ${disk#/dev/} | sed 's:/:!:g')
		local sysfspath=/sys/block/$sysfsdev/size
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
		# 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
534 535 536 537
}

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

Natanael Copa's avatar
Natanael Copa committed
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
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
563

Natanael Copa's avatar
Natanael Copa committed
564 565 566 567
	# check so it does not have mounted partitions
	has_mounted_part $dev && return 1

	# check so its not part of an md setup
568 569 570 571
	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
572 573 574 575 576 577 578

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

	return 0
}

579
find_disks() {
Natanael Copa's avatar
Natanael Copa committed
580
	local p=
581 582
	# 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
583
		is_available_disk $p && echo -n " $p"
584 585 586
	done
}

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

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

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

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

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

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

657 658
	read answer
	case "$answer" in
659
		y*|Y*) return 0;;
660
	esac
661
	return 1
662 663
}

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

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

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

	# create device nodes if not exist
	mdev -s
}

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

	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

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

# $1 = index
728 729 730
# $2 = partition type
# $3... = disk devices
find_nth_non_boot_parts() {
731 732 733 734 735
	local idx=$1 id=$2 disk= type=bootable
	shift 2
	local disks="$@"
	[ -n "$USE_EFI" ] && type=$(partition_id esp)
	for disk in $disks; do
736 737 738 739 740 741
		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
742 743 744 745 746 747
	done
}

setup_non_boot_raid_dev() {
	local md_dev=$1
	local idx=${md_dev#/dev/md}
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 909 910 911 912 913
	done

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

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

	setup_mdadm_conf
940
	install_mounted_root "$SYSROOT" "$disks" || return 1
941
	unmount_partitions "$SYSROOT"
942 943 944
	swapoff -a

	echo ""
945
	echo "Installation is complete. Please reboot."
946 947
}

948
native_disk_install_lvm() {
949
	local diskdev= vgname=vg0
950 951
	local lvm_part_type=$(partition_id lvm)
	local boot_part_type=$(partition_id linux)
952 953 954 955
	local boot_size=${BOOT_SIZE:-100}
	local lvm_size=
	local root_dev=/dev/$vgname/lv_root

956
	init_progs $(select_bootloader) || return 1
957
	confirm_erase $@ || return 1
958 959

	if [ -n "$USE_RAID" ]; then
960 961
		boot_part_type=$(partition_id raid)
		lvm_part_type=$(partition_id raid)
962
		stop_all_raid
963
	fi
964 965 966 967 968

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

969
	for diskdev in "$@"; do
970 971 972 973 974 975 976 977 978
		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
979 980 981 982 983 984 985 986
	done

	# will find BOOT_DEV for us
	setup_boot_dev $@

	setup_lvm_volume_group $vgname $@ || return 1
	setup_lvm_swap $vgname
	lvcreate --quiet -n ${root_dev##*/} -l 100%FREE $vgname
Natanael Copa's avatar
Natanael Copa committed
987
	rc-update add lvm boot
988
	setup_root $root_dev $BOOT_DEV
Natanael Copa's avatar
Natanael Copa committed
989 990
}

991
native_disk_install() {
992 993 994 995
	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)
996
	local prep_size=8
997 998 999 1000
	local boot_size=${BOOT_SIZE:-100}
	local swap_size=${SWAP_SIZE}
	local root_size=
	local root_dev= boot_dev= swap_dev=
1001
	init_progs $(select_bootloader) || return 1
1002 1003 1004
	confirm_erase $@ || return 1

	if [ -n "$USE_RAID" ]; then
1005 1006 1007
		boot_part_type=$(partition_id raid)
		root_part_type=$(partition_id raid)
		swap_part_type=$(partition_id raid)
1008 1009
		stop_all_raid
	fi
1010 1011 1012 1013 1014

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

1015
	for diskdev in "$@"; do
1016 1017 1018 1019 1020 1021 1022
		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
1023 1024 1025 1026 1027
		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
1028 1029 1030 1031 1032 1033 1034
		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
1035 1036 1037 1038
	done

	# will find BOOT_DEV for us
	setup_boot_dev $@
1039

1040 1041 1042 1043 1044 1045
	if [ "$USE_RAID" ]; then
		[ $SWAP_SIZE -gt 0 ] && setup_non_boot_raid_dev /dev/md1 $@
		setup_non_boot_raid_dev /dev/md2 $@ || return 1
		swap_dev=/dev/md1
		root_dev=/dev/md2
	else
1046
		swap_dev=$(find_nth_non_boot_parts 1 "$swap_part_type" $@)
1047 1048 1049 1050 1051 1052 1053
		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
1054
		root_dev=$(find_nth_non_boot_parts $index "$root_part_type" $@)
1055
	fi
1056

1057
	[ $SWAP_SIZE -gt 0 ] && setup_swap_dev $swap_dev
1058
	setup_root $root_dev $BOOT_DEV $@
1059 1060
}

1061
diskselect_help() {
1062
	cat <<-__EOF__
1063

1064 1065
		The disk you select can be used for a traditional disk install or for a
		data-only install.
1066

1067
		The disk will be erased.
1068

1069
		Enter 'none' if you want to run diskless.
1070

1071
	__EOF__
1072 1073
}

1074
diskmode_help() {
1075
	cat <<-__EOF__
1076

1077
		You can select between 'sys', 'data', 'lvm', 'lvmsys' or 'lvmdata'.
1078

1079 1080 1081
		sys:
		  This mode is a traditional disk install. The following partitions will be
		  created on the disk: /boot, / (filesystem root) and swap.
1082

1083
		  This mode may be used for development boxes, desktops, virtual servers, etc.
1084

1085 1086 1087
		data:
		  This mode uses your disk(s) for data storage, not for the operating system.
		  The system itself will run from tmpfs (RAM).
1088

1089 1090
		  Use this mode if you only want to use the disk(s) for a mailspool, databases,
		  logs, etc.
1091

1092 1093
		lvm:
		  Enable logical volume manager and ask again for 'sys' or 'data'.
1094

1095 1096
		lvmsys:
		  Same as 'sys' but use logical volume manager for partitioning.
1097

1098 1099
		lvmdata:
		  Same as 'data' but use logical volume manager for partitioning.
1100

1101
	__EOF__
1102 1103 1104 1105 1106 1107 1108
}

# ask for a root or data disk
# returns answer in global variable $answer
ask_disk() {
	local prompt="$1"
	local help_func="$2"
1109
	local i=
1110 1111
	shift 2
	answer=
1112
	local default_disk=${DEFAULT_DISK:-$1}
1113

1114 1115 1116
	while ! all_in_list "$answer" $@ "none" "abort"; do
		echo "Available disks are:"
		show_disk_info "$@"
1117 1118
		echon "$prompt [$default_disk] "
		default_read answer $default_disk
1119 1120 1121 1122
		case "$answer" in
			'abort') exit 0;;
			'none') return 0;;
			'?') $help_func;;
1123 1124 1125 1126
			*) for i in $answer; do
				if ! [ -b "/dev/$i" ]; then
					echo "/dev/$i is not a block device" >&2
					answer=
1127
				fi
1128
			done;;
1129 1130 1131 1132
		esac
	done
}

1133
usage() {
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
	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

1158 1159 1160 1161 1162 1163 1164 1165 1166 1167
		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

1168 1169 1170
		If BOOTFS, ROOTFS, VARFS are specified, then format a partition with specified
		filesystem. If not specified, the default filesystem is ext4.
		Supported filesystems for
1171
		  boot: ext2, ext3, ext4, btrfs, xfs, vfat(EFI)
1172 1173 1174
		  root: ext2, ext3, ext4, btrfs, xfs
		   var: ext2, ext3, ext4, btrfs, xfs
	__EOF__
1175 1176 1177
	exit 1
}

Francesco Colista's avatar
Francesco Colista committed
1178 1179
kver=$(uname -r)
case $kver in
1180 1181 1182
	*-rc[0-9]*) KERNEL_FLAVOR=vanilla;;
	*-[a-z]*) KERNEL_FLAVOR=${kver##*-};;
	*) KERNEL_FLAVOR=vanilla;;
1183 1184
esac

1185
DISK_MODE=
1186
USE_LVM=
1187
# Parse args
1188
while getopts "hk:Lm:o:qrs:v" opt; do
1189
	case $opt in
1190
		m) DISK_MODE="$OPTARG";;
1191
		k) KERNEL_FLAVOR="$OPTARG";;
1192
		L) USE_LVM="_lvm";;
1193
		o) APKOVL="$OPTARG";;
1194 1195
		q) QUIET=1;;
		r) USE_RAID=1;;
1196
		s) SWAP_SIZE="$OPTARG";;
1197
		v) VERBOSE=1;;
Natanael Copa's avatar
Natanael Copa committed
1198
		*) usage;;
1199 1200
	esac
done
1201
shift $(( $OPTIND - 1))