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

PREFIX=
. "$PREFIX/lib/libalpine.sh"

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

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

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

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

29 30 31 32 33 34 35 36 37 38 39
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
}

40 41 42 43 44 45 46 47
# 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=
48 49 50
	case "$1" in
		/dev/md*) echo "$1" && return 0;;
	esac
51 52 53 54 55 56 57 58 59 60 61 62 63
	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
64
enumerate_fstab() {
65
	local mnt="$1"
66
	local fs_spec= fs_file= fs_vfstype= fs_mntops= fs_freq= fs_passno=
67
	[ -z "$mnt" ] && return
68 69
	local escaped_mnt=$(echo $mnt | sed -e 's:/*$::' -e 's:/:\\/:g')
	awk "\$2 ~ /^$escaped_mnt(\/|\$)/ {print \$0}" /proc/mounts | \
70
		sed "s:$mnt:/:g; s: :\t:g" | sed -E 's:/+:/:g' | \
71
		while read fs_spec fs_file fs_vfstype fs_mntops fs_freq fs_passno; do
72
			if [ "$fs_file" = / ]; then
73 74 75 76
				fs_passno=1
			else
				fs_passno=2
			fi
77 78
			echo -e "$(uuid_or_device $fs_spec)\t${fs_file}\t${fs_vfstype}\t${fs_mntops} ${fs_freq} ${fs_passno}"
		done
79 80
}

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

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

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

96 97
# Find the disk device from given partition
disk_from_part() {
98 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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
	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
	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=
	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
148 149
}

150 151 152 153 154 155 156 157 158 159 160 161 162 163
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
164
		return 0
165 166
	fi

167
	apk add --quiet openssl
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

	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
}

185 186 187 188 189 190 191 192
# 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() {
193
	local mnt="$1"
194 195 196 197
	awk "\$2 == \"$mnt\" { print \$1 }" /proc/mounts | tail -n 1
}

supported_boot_fs() {
198
	local supported="ext2 ext3 ext4 btrfs xfs vfat"
199 200 201 202 203 204 205 206
	local fs=
	for fs in $supported; do
		[ "$fs" = "$1" ] && return 0
	done
	echo "$1 is not supported. Only supported are: $supported" >&2
	return 1
}

207 208 209 210 211 212 213
supported_part_label() {
	case "$1" in
		dos|gpt) return 0 ;;
		*) die "Partition label \"$DISKLABEL\" is not supported!" ;;
	esac
}

214 215
find_volume_group() {
	local lv=${1##*/}
216
	lvs --noheadings "$1" | awk '{print $2}'
217 218 219 220 221 222 223
}

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

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
# 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
}

249 250 251 252 253 254 255 256 257
has_bootopt() {
	local opt="$1"
	set -- $(cat /proc/cmdline)
	for i; do
		[ "$i" = "$opt" ] && return 0
	done
	return 1
}

258
# setup GRUB bootloader
259
setup_grub() {
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
	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
295

296 297 298 299 300 301 302 303 304
	# setup GRUB config
	local kernel
	case $KERNEL_FLAVOR in
		vanilla) kernel=vmlinuz ;;
		*) kernel=vmlinuz-$KERNEL_FLAVOR ;;
	esac

	# all_video is needed to remove the video error on boot
	cat > "$mnt"/boot/grub/grub.cfg <<- EOF
305
	set timeout=2
306
	insmod all_video
307
	menuentry "Alpine Linux" {
308 309
	    linux   /boot/$kernel modules=$modules root=$root $kernel_opts
	    initrd  /boot/initramfs-$KERNEL_FLAVOR
310 311 312 313 314 315
	}
	EOF
}

# setup syslinux bootloader
setup_syslinux() {
316 317
	local mnt="$1" root="$2" modules="$3" kernel_opts="$4" bootdev="$5"
	local exlinux_raidopt=
318 319 320 321 322 323 324 325 326

	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
327 328 329 330 331 332 333

	# 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

334 335 336
	extlinux $extlinux_raidopt --install "$mnt"/boot
}

337
install_mounted_root() {
338 339 340
	local mnt="$1"
	shift 1
	local disks="${@}" mnt_boot= boot_fs= root_fs=
341
	local initfs_features="ata base ide scsi usb virtio"
342
	local pvs= dev= rootdev= bootdev= extlinux_raidopt= root= modules=
343
	local kernel_opts="quiet"
344

345
	rootdev=$(find_mount_dev "$mnt")
346 347 348 349
	if [ -z "$rootdev" ]; then
		echo "$mnt does not seem to be a mount point" >&2
		return 1
	fi
350
	root_fs=$(find_mount_fs "$mnt")
351 352 353 354
	initfs_features="$initfs_features $root_fs"

	if is_lvm "$rootdev"; then
		initfs_features="$initfs_features lvm"
355 356
		local vg=$(find_volume_group "$rootdev")
		pvs=$(find_pvs_in_vg $vg)
357 358
	fi

359

360 361 362
	bootdev=$(find_mount_dev "$mnt"/boot)
	if [ -z "$bootdev" ]; then
		bootdev=$rootdev
363
		mnt_boot="$mnt"
364 365 366 367 368 369
	else
		mnt_boot="$mnt"/boot
	fi
	boot_fs=$(find_mount_fs "$mnt_boot")
	supported_boot_fs "$boot_fs" || return 1

370
	# check if our root is on raid so we can feed mkinitfs and
371
	# bootloader conf with the proper kernel module params
372
	for dev in $rootdev $pvs; do
373 374 375 376 377 378

		# check if we need hardware raid drivers
		case $dev in
		/dev/cciss/*)
			initfs_features="${initfs_features% raid} raid"
			;;
379 380 381 382 383 384
		/dev/nvme*)
			initfs_features="${initfs_features% nvme} nvme"
			;;
		/dev/mmc*)
			initfs_features="${initfs_features% mmc} mmc"
			;;
385 386
		esac

387 388 389
		[ -e "/sys/block/${dev#/dev/}/md" ] || continue

		local md=${dev#/dev/}
390
		initfs_features="${initfs_features% raid} raid"
391 392
		local level=$(cat /sys/block/$md/md/level)
		case "$level" in
393 394
			raid1) raidmod="${raidmod%,raid1},raid1";;
			raid[456]) raidmod="${raidmod%,raid456},raid456";;
395 396
		esac
	done
397 398


399 400 401 402 403 404
	if [ -n "$VERBOSE" ]; then
		echo "Root device:     $rootdev"
		echo "Root filesystem: $root_fs"
		echo "Boot device:     $bootdev"
		echo "Boot filesystem: $boot_fs"
	fi
405

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

418 419 420
	# we should not try start modloop on sys install
	rm -f "$mnt"/etc/runlevels/*/modloop

421
	# generate mkinitfs.conf
422
	mkdir -p "$mnt"/etc/mkinitfs/features.d
423
	echo "features=\"$initfs_features\"" > "$mnt"/etc/mkinitfs/mkinitfs.conf
424
	if [ -n "$raidmod" ]; then
425 426
		echo "/sbin/mdadm" > "$mnt"/etc/mkinitfs/features.d/raid.files
		echo "/etc/mdadm.conf" >> "$mnt"/etc/mkinitfs/features.d/raid.files
427
	fi
428

429 430
	# generate update-extlinux.conf
	root=$(uuid_or_device $rootdev)
431
	kernel_opts="$kernel_opts rootfstype=$root_fs"
432 433 434
	if is_vmware; then
		kernel_opts="pax_nouderef $kernel_opts"
	fi
435 436 437
	if has_bootopt nomodeset; then
		kernel_opts="nomodeset $kernel_opts"
	fi
438 439
	modules="sd-mod,usb-storage,${root_fs}${raidmod}"

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

465 466 467 468
	# 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/

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

473
	pkgs="$pkgs acct linux-$KERNEL_FLAVOR alpine-base"
474 475 476
	if [ "$(rc --sys)" = "XEN0" ]; then
		pkgs="$pkgs xen-hypervisor"
	fi
477 478 479 480 481
	local repos=$(sed -e 's/\#.*//' /etc/apk/repositories)
	local repoflags=
	for i in $repos; do
		repoflags="$repoflags --repository $i"
	done
482

483 484
	chroot_caps=$(set_grsec chroot_caps 0)
	init_chroot_mounts "$mnt"
485
	apk add --root "$mnt" $apkflags --overlay-from-stdin \
Natanael Copa's avatar
Natanael Copa committed
486
		$repoflags $pkgs <$ovlfiles
487 488 489 490
	local ret=$?
	cleanup_chroot_mounts "$mnt"
	set_grsec chroot_caps $chroot_caps > /dev/null
	return $ret
491 492 493 494
}

unmount_partitions() {
	local mnt="$1"
495 496

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

500 501 502
# figure out decent default swap size in mega bytes
find_swap_size() {
	local memtotal_kb=$(awk '$1 == "MemTotal:" {print $2}' /proc/meminfo)
503 504 505 506
	# 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
507 508
		local sysfsdev=$(echo ${disk#/dev/} | sed 's:/:!:g')
		local sysfspath=/sys/block/$sysfsdev/size
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
		# 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
524 525 526 527
}

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

Natanael Copa's avatar
Natanael Copa committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
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
553

Natanael Copa's avatar
Natanael Copa committed
554 555 556 557
	# check so it does not have mounted partitions
	has_mounted_part $dev && return 1

	# check so its not part of an md setup
558 559 560 561
	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
562 563 564 565 566 567 568

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

	return 0
}

569
find_disks() {
Natanael Copa's avatar
Natanael Copa committed
570
	local p=
571 572
	# 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
573
		is_available_disk $p && echo -n " $p"
574 575 576
	done
}

577 578 579 580 581 582 583
stop_all_raid() {
	local rd
	for rd in /dev/md*; do
		[ -b $rd ] && mdadm --stop $rd
	done
}

584 585 586 587 588 589 590 591 592 593 594 595
select_bootloader() {
	local bootloader=syslinux
	if [ "$ARCH" = "ppc64le" ]; then
		bootloader=grub-ieee1275
	elif [ -n "$USE_EFI" ]; then
		bootloader=grub-efi
	elif [ "$BOOTLOADER" = "grub" ]; then
		bootloader=grub-bios
	fi
	echo "$bootloader"
}

596 597
# install needed programs
init_progs() {
598
	local raidpkg= lvmpkg= fs= fstools= grub=
599
	[ -n "$USE_RAID" ] && raidpkg="mdadm"
600
	[ -n "$USE_LVM" ] && lvmpkg="lvm2"
601 602 603
	for fs in $BOOTFS $ROOTFS $VARFS; do
		# we need load btrfs module early to avoid the error message:
		# 'failed to open /dev/btrfs-control'
604 605 606
		if ! grep -q -w "$fs" /proc/filesystems; then
			modprobe $fs
		fi
607 608 609 610 611

		case $fs in
		xfs) fstools="$fstools xfsprogs";;
		ext*) fstools="$fstools e2fsprogs";;
		btrfs) fstools="$fstools btrfs-progs";;
612
		vfat) fstools="$fstools dosfstools";;
613 614
		esac
	done
615
	apk add --quiet sfdisk $lvmpkg $raidpkg $fstools $@
616 617
}

618
show_disk_info() {
Natanael Copa's avatar
Natanael Copa committed
619
	local disk= vendor= model= d= size=
620
	for disk in $@; do
621 622
		local dev=${disk#/dev/}
		d=$(echo $dev | sed 's:/:!:g')
623 624 625
		vendor=$(cat /sys/block/$d/device/vendor 2>/dev/null)
		model=$(cat /sys/block/$d/device/model 2>/dev/null)
		size=$(awk '{gb = ($1 * 512)/1000000000; printf "%.1f GB\n", gb}' /sys/block/$d/size 2>/dev/null)
626
		echo "  $dev	($size $vendor $model)"
627 628
	done
}
629

630 631
confirm_erase() {
	local answer=
632 633
	local erasedisks="$@"
	if [ "$ERASE_DISKS" = "$erasedisks" ]; then
Natanael Copa's avatar
Natanael Copa committed
634
		return 0
635
	fi
636
	echo "WARNING: The following disk(s) will be erased:"
637
	show_disk_info $@
638
	echo -n "WARNING: Erase the above disk(s) and continue? [y/N]: "
639

640 641
	read answer
	case "$answer" in
642
		y*|Y*) return 0;;
643
	esac
644
	return 1
645 646
}

647
# setup partitions on given disk dev in $1.
648
# usage: setup_partitions <diskdev> size1,type1 [size2,type2 ...]
649
setup_partitions() {
650
	local diskdev="$1" start=1M line=
651
	shift
652
	supported_part_label "$DISKLABEL" || return 1
653

654 655
	# initialize MBR for syslinux only
	if [ "$BOOTLOADER" = "syslinux" ] && [ -f "$MBR" ]; then
656 657
		cat "$MBR" > $diskdev
	fi
658

659 660
	# create new partitions
	(
661 662 663 664
		for line in "$@"; do
			echo "$start,$line"
			start=
		done
665
	) | sfdisk --quiet --label $DISKLABEL $diskdev
666 667 668 669 670

	# create device nodes if not exist
	mdev -s
}

671
# set up optional raid and create filesystem on boot device.
672
setup_boot_dev() {
673 674 675
	local disks="$@" disk= bootdev= mkfs_args=
	[ "$BOOTFS" != "vfat" ] && mkfs_args="-q"
	local part=$(for disk in $disks; do find_partitions "$disk" "boot"; done)
676 677
	set -- $part
	bootdev=$1
678
	[ -z "$bootdev" ] && return 1
679 680 681 682 683 684 685 686 687 688

	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

689
	echo "Creating file systems..."
690
	if [ -n "$USE_RAID" ]; then
691 692 693 694 695 696
		local missing=
		local num=$#
		if [ $# -eq 1 ]; then
			missing="missing"
			num=2
		fi
697
		# we only use raid level 1 for boot devices
698 699
		mdadm --create /dev/md0 --level=1 --raid-devices=$num \
			--metadata=0.90 --quiet --run $@ $missing || return 1
700 701
		bootdev=/dev/md0
	fi
702 703 704 705
	case "$BOOTFS" in
	btrfs) mkfs_args="";;
	ext4) mkfs_args="$mkfs_args -O ^64bit";; # pv-grub does not support 64bit
	esac
706
	mkfs.$BOOTFS $MKFS_OPTS_BOOT $mkfs_args $bootdev
707 708 709 710
	BOOT_DEV="$bootdev"
}

# $1 = index
711 712 713
# $2 = partition type
# $3... = disk devices
find_nth_non_boot_parts() {
714 715 716 717 718 719
	local idx=$1 id=$2 disk= type=bootable
	shift 2
	local disks="$@"
	[ -n "$USE_EFI" ] && type=$(partition_id esp)
	for disk in $disks; do
		sfdisk -d $disk | grep -v $type \
720
			| awk "/(Id|type)=$id/ { i++; if (i==$idx) print \$1 }"
721 722 723 724 725 726
	done
}

setup_non_boot_raid_dev() {
	local md_dev=$1
	local idx=${md_dev#/dev/md}
727 728 729 730 731
	if [ "$ARCH" = "ppc64le" ]; then
		# increment idx as PReP partition is
		# the bootable partition in ppc64le
		idx=$((idx+1))
	fi
732 733 734
	shift
	local level=1
	local missing=
735 736
	local pid=$(partition_id raid)
	local raid_parts=$(find_nth_non_boot_parts $idx $pid $@)
737 738 739 740 741 742 743 744
	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
745
	mdadm --create $md_dev --level=$level --raid-devices=$num \
746
		--quiet --run $@ $missing || return 1
747 748 749 750
}

# setup device for lvm, create raid array if needed
setup_lvm_volume_group() {
751 752 753
	local vgname="$1"
	shift
	local lvmdev=
754 755

	if [ -n "$USE_RAID" ]; then
756 757
		setup_non_boot_raid_dev /dev/md1 $@ || return 1
		lvmdev=/dev/md1
758
	else
759
		lvmdev=$(find_partitions "$1" "lvm")
760 761 762 763 764
	fi

	# be quiet on success
	local errmsg=$(dd if=/dev/zero of=$lvmdev bs=1k count=1 2>&1) \
		|| echo "$errmsg"
765 766 767 768 769 770 771 772
	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
773
	SWAP_DEVICES=
774 775
	for swap_dev in "$@"; do
		mkswap $swap_dev >/dev/null
776 777
		echo -e "$(uuid_or_device $swap_dev)\tswap\t\tswap\tdefaults 0 0" >> /etc/fstab
		SWAP_DEVICES="$SWAP_DEVICES $swap_dev"
778 779 780
	done
	swapon -a
	rc-update --quiet add swap boot
781 782 783
}

# setup and enable swap on given volumegroup if needed
784
setup_lvm_swap() {
785
	local vgname="$1"
786
	local swapname=lv_swap
787 788 789
	if [ -z "$SWAP_SIZE" ] || [ "$SWAP_SIZE" -eq 0 ]; then
		return
	fi
790 791
	lvcreate --quiet -n $swapname -L ${SWAP_SIZE}MB $vgname
	setup_swap_dev /dev/$vgname/$swapname
792 793 794 795 796 797 798 799 800 801
}

# 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
}

802 803 804
# set up /var on given device
setup_var() {
	local var_dev="$1"
805
	local varfs=${VARFS}
806
	echo "Creating file systems..."
807
	mkfs.$varfs $MKFS_OPTS_VAR $var_dev >/dev/null || return 1
808
	sed -i -e '/[[:space:]]\/var[[:space:]]/d' /etc/fstab
809
	echo -e "$(uuid_or_device ${var_dev})\t/var\t\t${varfs}\tdefaults 1 2" >> /etc/fstab
810 811 812 813 814 815 816

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

817
	service syslog --quiet condrestart
818
	setup_mdadm_conf
819 820
}

821
setup_mdadm_conf() {
822
	local mods= mod=
823 824 825
	if [ -n "$USE_RAID" ]; then
		mdadm --detail --scan > /etc/mdadm.conf
		rc-update --quiet add mdadm-raid boot
826 827 828 829 830 831
		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
832 833 834
	fi
}

835
data_only_disk_install_lvm() {
836
	local diskdev=
837 838
	local vgname=vg0
	local var_dev=/dev/$vgname/lv_var
839
	local lvm_part_type=$(partition_id lvm)
840
	local size=
841
	unset BOOTLOADER
842 843

	init_progs || return 1
844
	confirm_erase $@ || return 1
845

846
	if [ "$USE_RAID" ]; then
847
		lvm_part_type=$(partition_id raid)
848 849
		stop_all_raid
	fi
850

851
	for diskdev in "$@"; do
852
		setup_partitions $diskdev "${size}${size:+M},$lvm_part_type" || return 1
853
	done
854

855 856
	setup_lvm_volume_group $vgname $@ || return 1
	setup_lvm_swap $vgname
857
	lvcreate --quiet -n ${var_dev##*/} -l 100%FREE $vgname
858
	setup_mdadm_conf
Natanael Copa's avatar
Natanael Copa committed
859
	rc-update add lvm boot
860
	setup_var $var_dev
861 862
}

863 864
data_only_disk_install() {
	local diskdev=
865
	local var_dev=
866 867
	local var_part_type=$(partition_id linux)
	local swap_part_type=$(partition_id swap)
868 869
	local size=
	local swap_dev= var_dev=
870
	unset BOOTLOADER
871 872 873 874 875

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

	if [ "$USE_RAID" ]; then
876 877
		var_part_type=$(partition_id raid)
		swap_part_type=$(partition_id raid)
878 879 880 881 882
		stop_all_raid
	fi

	for diskdev in "$@"; do
		setup_partitions $diskdev \
883 884
			"${SWAP_SIZE}M,$swap_part_type" \
			"${size}${size:+M},$var_part_type" || return 1
885 886 887 888 889 890 891 892
	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
893 894
		swap_dev=$(find_nth_non_boot_parts 1 "$swap_part_type" $@)
		var_dev=$(find_nth_non_boot_parts 1 "$var_part_type" $@)
895 896 897 898 899
	fi
	[ $SWAP_SIZE -gt 0 ] && setup_swap_dev $swap_dev
	setup_var $var_dev
}

900
# setup
901
setup_root() {
902 903 904
	local root_dev="$1" boot_dev="$2"
	shift 2
	local disks="$@" mkfs_args="-q"
Natanael Copa's avatar
Natanael Copa committed
905
	[ "$ROOTFS" = "btrfs" ] && mkfs_args=""
906
	mkfs.$ROOTFS $MKFS_OPTS_ROOT $mkfs_args "$root_dev"
907 908
	mkdir -p "$SYSROOT"
	mount -t $ROOTFS $root_dev "$SYSROOT" || return 1
909
	if [ -n "$boot_dev" ] && [ -z "$USE_EFI" ]; then
910 911
		mkdir -p "$SYSROOT"/boot
		mount -t $BOOTFS $boot_dev "$SYSROOT"/boot || return 1
912
	fi
913 914 915 916
	if [ -n "$boot_dev" ] && [ -n "$USE_EFI" ]; then
		mkdir -p "$SYSROOT"/boot/efi
		mount -t $BOOTFS $boot_dev "$SYSROOT"/boot/efi || return 1
	fi
917 918

	setup_mdadm_conf
919
	install_mounted_root "$SYSROOT" "$disks" || return 1
920
	unmount_partitions "$SYSROOT"
921 922 923
	swapoff -a

	echo ""
924
	echo "Installation is complete. Please reboot."
925 926
}

927
native_disk_install_lvm() {
928
	local diskdev= vgname=vg0
929 930
	local lvm_part_type=$(partition_id lvm)
	local boot_part_type=$(partition_id linux)
931 932 933 934
	local boot_size=${BOOT_SIZE:-100}
	local lvm_size=
	local root_dev=/dev/$vgname/lv_root

935
	init_progs $(select_bootloader) || return 1
936
	confirm_erase $@ || return 1
937 938

	if [ -n "$USE_RAID" ]; then
939 940
		boot_part_type=$(partition_id raid)
		lvm_part_type=$(partition_id raid)
941
		stop_all_raid
942
	fi
943 944 945 946 947

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

948 949
	for diskdev in "$@"; do
		setup_partitions $diskdev \
950 951
			"${boot_size}M,$boot_part_type,*" \
			"${lvm_size}${lvm_size:+M},$lvm_part_type" || return 1
952 953 954 955 956 957 958 959
	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
960
	rc-update add lvm boot
961
	setup_root $root_dev $BOOT_DEV
Natanael Copa's avatar
Natanael Copa committed
962 963
}

964
native_disk_install() {
965 966 967 968
	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)
969
	local prep_size=8
970 971 972 973
	local boot_size=${BOOT_SIZE:-100}
	local swap_size=${SWAP_SIZE}
	local root_size=
	local root_dev= boot_dev= swap_dev=
974
	init_progs $(select_bootloader) || return 1
975 976 977
	confirm_erase $@ || return 1

	if [ -n "$USE_RAID" ]; then
978 979 980
		boot_part_type=$(partition_id raid)
		root_part_type=$(partition_id raid)
		swap_part_type=$(partition_id raid)
981 982
		stop_all_raid
	fi
983 984 985 986 987

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

988
	for diskdev in "$@"; do
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
		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

		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
1004 1005 1006 1007
	done

	# will find BOOT_DEV for us
	setup_boot_dev $@
1008

1009 1010 1011 1012 1013 1014
	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
1015
		swap_dev=$(find_nth_non_boot_parts 1 "$swap_part_type" $@)
1016 1017 1018 1019 1020 1021 1022
		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
1023
		root_dev=$(find_nth_non_boot_parts $index "$root_part_type" $@)
1024
	fi
1025

1026
	[ $SWAP_SIZE -gt 0 ] && setup_swap_dev $swap_dev
1027
	setup_root $root_dev $BOOT_DEV $@
1028 1029
}

1030
diskselect_help() {
1031
	cat <<-__EOF__
1032

1033 1034
		The disk you select can be used for a traditional disk install or for a
		data-only install.
1035

1036
		The disk will be erased.
1037

1038
		Enter 'none' if you want to run diskless.
1039

1040
	__EOF__
1041 1042
}

1043
diskmode_help() {
1044
	cat <<-__EOF__
1045

1046
		You can select between 'sys', 'data', 'lvm', 'lvmsys' or 'lvmdata'.
1047

1048 1049 1050
		sys:
		  This mode is a traditional disk install. The following partitions will be
		  created on the disk: /boot, / (filesystem root) and swap.
1051

1052
		  This mode may be used for development boxes, desktops, virtual servers, etc.
1053

1054 1055 1056
		data:
		  This mode uses your disk(s) for data storage, not for the operating system.
		  The system itself will run from tmpfs (RAM).
1057

1058 1059
		  Use this mode if you only want to use the disk(s) for a mailspool, databases,
		  logs, etc.
1060

1061 1062
		lvm:
		  Enable logical volume manager and ask again for 'sys' or 'data'.
1063

1064 1065
		lvmsys:
		  Same as 'sys' but use logical volume manager for partitioning.
1066

1067 1068
		lvmdata:
		  Same as 'data' but use logical volume manager for partitioning.
1069

1070
	__EOF__
1071 1072 1073 1074 1075 1076 1077
}

# ask for a root or data disk
# returns answer in global variable $answer
ask_disk() {
	local prompt="$1"
	local help_func="$2"
1078
	local i=
1079 1080
	shift 2
	answer=
1081
	local default_disk=${DEFAULT_DISK:-$1}
1082

1083 1084 1085
	while ! all_in_list "$answer" $@ "none" "abort"; do
		echo "Available disks are:"
		show_disk_info "$@"
1086 1087
		echon "$prompt [$default_disk] "
		default_read answer $default_disk
1088 1089 1090 1091
		case "$answer" in
			'abort') exit 0;;
			'none') return 0;;
			'?') $help_func;;
1092 1093 1094 1095
			*) for i in $answer; do
				if ! [ -b "/dev/$i" ]; then
					echo "/dev/$i is not a block device" >&2
					answer=
1096
				fi
1097
			done;;
1098 1099 1100 1101
		esac
	done
}

1102
usage() {
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
	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

1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
		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

1137 1138 1139
		If BOOTFS, ROOTFS, VARFS are specified, then format a partition with specified
		filesystem. If not specified, the default filesystem is ext4.
		Supported filesystems for
1140
		  boot: ext2, ext3, ext4, btrfs, xfs, vfat(EFI)
1141 1142 1143
		  root: ext2, ext3, ext4, btrfs, xfs
		   var: ext2, ext3, ext4, btrfs, xfs
	__EOF__
1144 1145 1146
	exit 1
}

Francesco Colista's avatar
Francesco Colista committed
1147 1148
kver=$(uname -r)
case $kver in
1149 1150 1151
	*-rc[0-9]*) KERNEL_FLAVOR=vanilla;;
	*-[a-z]*) KERNEL_FLAVOR=${kver##*-};;
	*) KERNEL_FLAVOR=vanilla;;
1152 1153
esac

1154
DISK_MODE=
1155
USE_LVM=
1156
# Parse args
1157
while getopts "hk:Lm:o:qrs:v" opt; do
1158
	case $opt in
1159
		m) DISK_MODE="$OPTARG";;
1160
		k) KERNEL_FLAVOR="$OPTARG";;
1161
		L) USE_LVM="_lvm";;
1162
		o) APKOVL="$OPTARG";;
1163 1164
		q) QUIET=1;;
		r) USE_RAID=1;;
1165
		s) SWAP_SIZE="$OPTARG";;
1166
		v) VERBOSE=1;;
Natanael Copa's avatar
Natanael Copa committed
1167
		*) usage;;
1168 1169
	esac
done
1170
shift $(( $OPTIND - 1))
1171

1172 1173
if [ -d "$1" ]; then
	# install to given mounted root
1174
	apk add --quiet syslinux
1175 1176
	install_mounted_root "${1%/}" \
		&& echo "You might need fix the MBR to be able to boot" >&2
1177 1178 1179
	exit $?
fi

1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
reset_var
swapoff -a

# stop all volume groups in use
vgchange --ignorelockingfailure -a n >/dev/null 2>&1

if [ -n "$USE_RAID" ]; then
	stop_all_raid
fi

1190
disks=$(find_disks)
1191
diskdevs=
Natanael Copa's avatar
Natanael Copa committed
1192 1193

# no disks so lets exit quietly.
1194 1195 1196 1197
if [ -z "$disks" ]; then
	[ -z "$QUIET" ] && echo "No disks found." >&2
	exit 0
fi
Natanael Copa's avatar
Natanael Copa committed
1198

1199
if [ $# -gt 0 ]; then
1200
	# check that they are
1201 1202 1203
	for i in "$@"; do
		j=$(readlink -f "$i" | sed 's:^/dev/::; s:/:!:g')
		if ! [ -e "/sys/block/$j/device" ]; then
1204
			die "$i is not a suitable for partitioning"
1205
		fi
1206
		diskdevs="$diskdevs /dev/${j//!//}"
1207 1208
	done
else
1209
	ask_disk "Which disk(s) would you like to use? (or '?' for help or 'none')" \
1210
		diskselect_help $disks
1211
	if [ "$answer" != none ]; then