#!/bin/sh prog=${0##*/} version=@VERSION@ cleanup() { local i= cd / if [ -n "$uninstalls" ]; then apk del --quiet syslinux fi sync sleep 1 for i in $read_only_mounts; do mount -o remount,ro "$i" || echo "Warning: Failed to remount as read-only. Is modloop mounted?" done if [ -n "$umounts" ]; then umount $umounts fi } die() { echo "$@" >&2 cleanup exit 1 } # find device for mountpoint find_dev() { local mnt="${1%/}" # strip trailing / awk "\$2 == \"$mnt\" {print \$1}" /proc/mounts } # check if given device is on usb bus on_usb_bus() { local dev="$1" [ -e /sys/block/$dev ] || return 1 local sysdev=$(readlink -f /sys/block/$dev/device) test "${sysdev##*/usb[0-9]}" != "$sysdev" } # mount source as loopback and set srcdir mount_srcdir() { local srcmnt=${MNT:-/mnt} mount -o loop -t iso9660 "$src" $srcmnt \ || die "Failed to mount loopback $src" umounts="$srcmnt" srcdir="$srcmnt" if ! [ -f "$srcdir"/.alpine-release ]; then die "No .alpine-release found on image $src" fi } vecho() { [ -z "$verbose" ] && return 0 echo "$@" } # check if given dir is read-only is_read_only() { local tmpfile=$(mktemp -p "$1" 2>/dev/null) [ -z "$tmpfile" ] && return 0 rm -f "$tmpfile" return 1 } # find what disk this partition belongs to find_disk_dev() { local i= sysfsname=${1#/dev/} sysfsname=${sysfsname//\/!} # cciss/c0d0 -> cciss!c0d0 if [ -e /sys/block/$sysfsname ]; then echo "/dev/${sysfsname//!/'/'}" return 0 fi for i in /sys/block/*/$sysfsname; do [ -e "$i" ] || continue echo "$i" | cut -d/ -f4 | sed -e 's:!:/:g' -e 's:^:/dev/:' return 0 done return 1 } usage() { cat <<__EOF__ $prog $version usage: $prog [-hu] SOURCE [DEST] Copy the contents of SOURCE to DEST and make DEST bootable. SOURCE can be a directory or a ISO image. DEST can be a mounted directory or a device. If DEST is ommitted /media/usb will be used. Options: -f Force overwrite existing files. Will overwrite syslinux.cfg if upgrade. -h Show this help. -k Keep current alpine_dev in syslinux.cfg. Without this it will be replaced with the UUID. -u Upgrade mode. Keep existing syslinux.cfg and don't run syslinux. -s Force run syslinux, even if upgrade mode. -v Verbose mode. Display whats going on. __EOF__ exit 1 } while getopts "fhkusv" opt; do case "$opt" in f) force=1;; h) usage;; k) keep_alpine_dev=1;; u) upgrade=1;; s) syslinux=1;; v) verbose=1;; esac done shift $(($OPTIND - 1)) src=${1} dest=${2:-/media/usb} [ -z "$src" ] && usage srcdir= # Find the srcdir or srcurl. mount loopback if needed if [ -f "$src"/.alpine-release ]; then srcdir="`echo $src | sed -r 's,/$,,'`" else case "$src" in http://*|ftp://*) srcurl="$src";; *) mount_srcdir;; esac fi if [ -n "$srcdir" ]; then to_version=$(cat "$srcdir"/.alpine-release) fi # find target device if [ -d "$dest" ]; then dest=${dest%/} # strip trailing / if ! awk '{print $2}' /proc/mounts | grep -q "^$dest\$"; then mount "$dest" || die "Failed to mount $dest" umounts="$umounts $dest" fi destdir="$dest" dest=$(find_dev "$destdir") elif [ -b "$dest" ]; then destdir="/media/${dest##*/}" mkdir -p "$destdir" mount "$dest" "$destdir" || die "Failed to mount $dest on $destdir" umounts="$umounts $destdir" fi # remount as rw if needed if is_read_only "$destdir"; then vecho "Remounting $destdir as read/write" mount -o remount,rw "$dest" || die "Failed to remount $destdir as rw" read_only_mounts="$read_only_mounts $destdir" fi # fish out label, uuid and type eval $(blkid $dest | cut -d: -f2-) vecho "Using $dest as target (mounted on $destdir)" # find parent device (i.e sda) dev="$dest" while [ -L "$dev" ]; do dev=$(readlink -f $dev) done parent_dev=$(find_disk_dev $dev) # check if this files exist and not in upgrade mode if [ -z "$upgrade" ] && [ -z "$force" ]; then for i in boot apks syslinux.cfg .alpine-release; do [ -e "$destdir"/$i ] && die "$destdir/$i already exists. Aborting" done fi # check if its same version if [ -n "$upgrade" ] && [ -e "$destdir"/.alpine-release ]; then from_version=$(cat "$destdir"/.alpine-release) if [ -z "$force" ] && [ -n "$to_version" ] && [ "$from_version" = "$to_version" ]; then die "Source and target seems to have same version ($from_version). Aborting." fi fi # Display what versions we are installing/upgrading if [ -n "$from_version" ]; then echo "Upgrading $dest from $from_version to $to_version" else echo "Copying $to_version to $dest (mounted on $destdir)" fi # remove partial upgrades if any. rm -rf "$destdir"/.new "$destdir"/.old mkdir -p "$destdir"/.new || die "Failed to create $destdir/.new" # check that we have the space we need # we calculate on MB since shell arthimetic gets problems with big disks # and bytes. free_blocks=$(stat -f -c "%f" "$destdir") block_size=$(stat -f -c "%s" "$destdir") blocks_per_mb=$(( 1024 * 1024 / $block_size)) available_space=$(( $free_blocks / $blocks_per_mb )) vecho "Available space: $available_space MiB" if [ -n "$srcdir" ]; then needed_space=$(cd "$srcdir" && du -m -s -c boot apks syslinux.cfg .alpine-release | awk '$2 == "total" {print $1}') vecho "Needed space: $needed_space MiB" [ $available_space -lt $needed_space ] \ && die "Not enough space on $destdir. Aborting." # copy the files to .new for i in boot apks syslinux.cfg .alpine-release; do vecho "Copying $srcdir/$i to $destdir/.new/" cp -a "$srcdir"/$i "$destdir"/.new/ done elif [ -n "$srcurl" ]; then cd "$destdir"/.new ${WGET:-wget} -O - "$srcurl" | uniso \ || die "Failed to download or extract $srcurl" echo "" fi # make sure files are really there before we replace existing vecho "Flushing cache..." sync vecho "Replacing existing files..." mkdir -p "$destdir"/.old || die "Failed to create $destdir/.old" # do we want keep existing syslinux.cfg? tomove="boot apks .alpine-release" if [ -n "$force" ] || ! [ -e "$destdir"/syslinux.cfg ]; then tomove="$tomove syslinux.cfg" # update syslinux.cfg unless device is on usb bus # this is so we can boot from CF's and harddisk if ! on_usb_bus $parent_dev; then vecho "Updating syslinux.cfg to use $dest" sed -i -e "s/usbdisk/${dest##*/}/g" \ "$destdir"/.new/syslinux.cfg fi fi # move current files to .old for i in $tomove; do if [ -e "$destdir"/$i ]; then mv "$destdir"/$i "$destdir"/.old/ || die "Failed to move $destdir/$i to $destdir/.old/" fi done # move .new to current for i in $tomove; do mv "$destdir"/.new/$i "$destdir"/ || die "Failed to move $destdir/.new/ to $destdir" done if [ -z "$keep_alpine_dev" ] && [ -n "$UUID" ]; then sed -i -e "s/alpine_dev=[^ \t:]\+/alpine_dev=UUID=$UUID/" \ "$destdir"/syslinux.cfg fi # cleanup [ -z "$keep_old" ] && rm -rf "$destdir"/.old "$destdir"/.new # If we only copy then we are done. if [ -n "$upgrade" ] && [ -z "$syslinux" ]; then cleanup exit 0 fi echo "Making $dest bootable..." if ! [ -x "$(which syslinux)" ]; then apk add --quiet syslinux || die "Failed to install syslinux" uninstalls="syslinux" fi syslinux $dest if [ -b $parent_dev ]; then dd if=/usr/share/syslinux/mbr.bin of=$parent_dev else echo "Warning: Could not find the parent device for $dest" fi cleanup