aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2017-02-05 00:30:32 -0800
committerJakob Odersky <jakob@odersky.com>2017-02-05 00:30:32 -0800
commitae43325d1ce55bbce43bd0a996cfab00657be838 (patch)
treeea38ea5bd7e6c9674bd03260638e817e513a6225
parent3687eb638dfcdc641b77e46f44814638d5b9bf81 (diff)
downloadrpi2-mkroot-master.tar.gz
rpi2-mkroot-master.tar.bz2
rpi2-mkroot-master.zip
Implement mkrootfs and mkrootimg for Raspberry Pi 2HEADmaster
-rw-r--r--README.md51
-rwxr-xr-xchrootfs76
-rwxr-xr-xmkrootfs372
-rw-r--r--mkrootfs.d/01-apt.sh5
-rw-r--r--mkrootfs.d/02-kernel.sh12
-rw-r--r--mkrootfs.d/03-fstab.sh5
-rw-r--r--mkrootfs.d/04-network.sh14
-rw-r--r--mkrootfs.d/05-locales.sh2
-rw-r--r--mkrootfs.d/06-login.sh18
-rwxr-xr-xmkrootimg145
10 files changed, 388 insertions, 312 deletions
diff --git a/README.md b/README.md
index 044928f..f7120d7 100644
--- a/README.md
+++ b/README.md
@@ -1,41 +1,46 @@
-A suite of utilities to build debian root filesystems and bootable images for the Cubieboard 5 (Cubietruck Plus).
+A suite of utilities to build a debian root filesystem and bootable image for the Raspberry Pi 2, model B.
## Utilities
-- `mkrootfs <dir>` Build a debian root filesystem in the given directory
-- `chrootfs <dir>` Start a shell in the given root filesystem.
-- `mkrootimg <dir> <img>` Bundle a root filesystem into a binary image that can be flashed to an sd card (bootloader still needs to be flashed manually).
+- `mkrootfs` Build a debian root filesystem in the given directory
+- `mkrootimg` Bundle a root filesystem into a bootable, binary image that can be flashed to an sd card.
-Run any of the above utilities with the `-h` or `--help` options for additional information.
+Run any of the above utilities without arguments for additional information.
## Usage Example
-The typical use-case, creating a bootable image for the Cubieboard 5 involves the following steps:
+The typical use-case, creating a bootable image involves the following steps:
1. Create a root filesystem in a temporary directory "tmprootfs".
- mkrootfs --hostname=cb5 --release=stretch --ssh-key=/home/user/.ssh/id_rsa.pub tmprootfs
-
-2. (optional) Start a shell in the directory to install any additional packages.
+ mkrootfs --hostname=pi --release=stretch --ssh-key=/home/user/.ssh/id_rsa.pub tmprootfs
- chrootfs tmprootfs
-
- root@inside_rootfs:~# apt install ...
- root@inside_rootfs:~# ...
- root@inside_rootfs:~# exit
-
-3. Pack the root filesystem into a binary image (note that the first 1MB of the image will be empty, reserved for a bootloader).
+2. Pack the root filesystem into a binary image
- mkrootimg tmprootfs cb5-stretch.img
+ mkrootimg tmprootfs pi2-stretch.img
-4. Add bootloader to start of image (see http://linux-sunxi.org/Mainline_Debian_HowTo for instructions on how to build u-boot).
+3. Copy the image to an sd card.
+
+ dd if=pi2-stretch.img of=/dev/mmc0
+
+4. Boot a Raspberry Pi from the card.
+
+ Note that several initialization tasks are performed during the first boot:
+
+ - the root filesystem is expanded to fill the whole sd card
+ - ssh keys and machine ids are regenerated
+
+ Progress will be signaled through the ACT and PWR LEDs:
- dd if=<u-boot>/u-boot-sunxi-with-spl.bin of=cb5-stretch.img bs=1024 seek=8 conv=notrunc
-
-5. Copy the image to an sd card.
+ - both leds will flash rapidly for a couple of seconds, indicating
+ that first-boot tasks are about to start
+ - PWR will blink in a "heartbeat" pattern and ACT will indicate sd card activity
+ - first-boot tasks are run
+ - after completion, the ACT led will blink in a heartbeat pattern an PWR will be turned off
- dd if=cb5-stretch.img of=/dev/mmc0
+## Requirements
-Note that you will need root privileges to run most of these commands.
+- qemu-user-static
+- qemu-debootstrap
## Copying
This program is free software, released under the GNU General Public License version 2.
diff --git a/chrootfs b/chrootfs
deleted file mode 100755
index 0b42070..0000000
--- a/chrootfs
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/bin/bash
-set -e
-
-# Directory in which the root filesystem is contained
-#
-ROOTFS=$(pwd)/cb5-rootfs
-
-print_usage() {
- cat 1>&2 <<EOF
-Usage: $0 rootfs-directory
-Start a shell in a root filesystem. Mounting and unmounting temporary driver filesystems
-is handled by this script.
-EOF
-}
-
-# Process options
-#
-while [ $# -gt 1 ]
-do
- case "$1" in
- --help|-h)
- print_usage
- exit 0
- ;;
- *)
- echo "Unknown option '$1'" 1>&2
- exit 1
- esac
- shift
-done
-
-# Process last argument, the root file system location
-if [ -z "$1" ] || [ "$1" = -h ] || [ "$1" = --help ]; then
- print_usage
- exit 1
-fi
-if [ "$EUID" -ne 0 ]; then
- echo "This must be run as root." 1>&2
- exit 1
-fi
-export ROOTFS="$1"
-
-# Unmount and remove any temporary files
-#
-cleanup() {
- set +e
-
- # Identify and kill all processes still using files
- echo "Killing processes using mount point" 1>&2
- fuser -k "${ROOTFS}"
- sleep 3
- fuser -9 -k -v "${ROOTFS}"
-
- # Clean up all temporary mount points
- echo "Removing temporary mount points" 1>&2
- umount -l "${ROOTFS}/proc" 2> /dev/null
- umount -l "${ROOTFS}/sys" 2> /dev/null
- umount -l "${ROOTFS}/dev/pts" 2> /dev/null
- umount -l "${ROOTFS}/dev" 2> /dev/null
- trap - 0 1 2 3 6
-}
-trap cleanup 0 1 2 3 6
-
-# Prepare filesystem to be chrooted
-#
-echo "Creating temporary system mount points in rootfs" 1>&2
-mount -t proc chproc "$ROOTFS/proc"
-mount -t sysfs chsys "$ROOTFS/sys"
-mount -t devtmpfs chdev "$ROOTFS/dev"
-mount -t devpts chpts "$ROOTFS/dev/pts"
-
-echo "Starting shell in rootfs" 1>&2
-LANG=C LC_ALL=C DEBIAN_FRONTEND=noninteractive chroot ${ROOTFS}
-
-cleanup
-echo "Cleaned up"
diff --git a/mkrootfs b/mkrootfs
index e8941b0..09047cb 100755
--- a/mkrootfs
+++ b/mkrootfs
@@ -1,136 +1,312 @@
#!/bin/bash
set -e
-# Directory in which the file system will be built
-#
-ROOTFS=$(pwd)/cb5-rootfs
-
-# Debian release
-#
-RELEASE=stretch
-
-# Host name to be used by machine
-#
-HOSTNAME=cb5
-
-# SSH to log in as root
-#
-SSH_KEY="$HOME/.ssh/id_rsa.pub"
+hostname=pi
+ssh_key="$HOME/.ssh/id_rsa.pub"
+release=stretch
+rootfs=
print_usage() {
- cat 1>&2 <<EOF
-Usage: $0 [options] rootfs-directory
-Create a debian root filesystem for the cubieboard5.
-The password for the root account will be set to 'guest', unless an ssh key is
-specified, in which case the account will be locked.
+ cat <<EOF
+Usage: $0 [options] <rootfs_directory>
+
+Create a bare debian root filesystem for the Raspberry Pi 2 B. The
+password for the root account will be set to 'guest' by default.
Options:
- --hostname=<$HOSTNAME> Use a given hostname for the device.
- --ssh-key=<$SSH_KEY> Add the given SSH key to root's authorized_keys file. When this option is provided, root's account will be locked.
- --release=<$RELEASE> Create a root filesystem for the given debian release.
+ --hostname <hostname> Use a given hostname for the device. Defaults to '$hostname'.
+ --ssh-key <key file> Add the given SSH key to root's authorized_keys file. Defaults to '$ssh_key'.
+ --release <release> Create a root filesystem for the given debian release. Defaults to '$release'.
+ --no-debootstrap Don't create an initial file system with debootstrap. This may be useful for debugging purposes
EOF
}
-# Process options
-#
-while [ $# -gt 1 ]
-do
+fail() {
+ echo "$1" >&2
+ exit 1
+}
+
+log() {
+ echo "mkrootfs: $1" >&2
+}
+
+# Process arguments
+while [ $# -gt 0 ]; do
case "$1" in
--help|-h)
print_usage
exit 0
;;
- --hostname=*)
- HOSTNAME="${1#*=}"
+ --hostname)
+ shift
+ [[ -z "$1" ]] && fail "no hostname specified"
+ hostname="$1"
+ ;;
+ --release)
+ shift
+ [[ -z "$1" ]] && fail "no release specified"
+ release="$1"
;;
- --release=*)
- RELEASE="${1#*=}"
+ --ssh-key)
+ shift
+ [[ -z "$1" ]] && fail "no ssh-key specified"
+ ssh_key="$1"
;;
- --ssh-key=*)
- SSH_KEY="${1#*=}"
+ --no-debootstrap)
+ nodeboostrap=true
;;
*)
- echo "Unknown option '$1'" 1>&2
- exit 1
+ [[ -n "$rootfs" ]] && fail "rootfs already defined"
+ rootfs="$1"
+ ;;
esac
shift
done
-# Process last argument, the root file system location
-if [ -z "$1" ] || [ "$1" = -h ] || [ "$1" = --help ]; then
- print_usage
- exit 1
-fi
-if [ "$EUID" -ne 0 ]; then
- echo "This must be run as root." 1>&2
- exit 1
-fi
-export ROOTFS="$1"
+[ -n "$rootfs" ] || fail "$(print_usage)"
+[ ! -e "$rootfs" ] || fail "$rootfs already exists"
+[ "$EUID" -eq 0 ] || fail "$0 must be run as root"
+
+if [ -n "$nodebootstrap" ]; then
+ echo "skipping deboostrap" >&2
+else
+ log "debootstrapping"
+ if [ $(dpkg -l | grep -c apt-cacher-ng) -gt 0 ]; then
+ export http_proxy="http://localhost:3142"
+ else
+ echo "WARNING: running debootstrap will download a lot of data. It is recommended to use apt-cacher-ng." >&2
+ fi
-export RELEASE
-export HOSTNAME
-export SSH_KEY
+ qemu-debootstrap \
+ --verbose \
+ --include=wget,curl,openssl,ca-certificates \
+ --arch=armhf \
+ "$release" \
+ "$rootfs" \
+ http://httpredir.debian.org/debian
+
+ log "debootstrap complete"
+ # http_proxy conflicts with direct calls to apt-get
+ #unset http_proxy
+fi
# Run a command in the root file system
-#
chroot_exec() {
- # Exec command in chroot
- LANG=C LC_ALL=C DEBIAN_FRONTEND=noninteractive chroot ${ROOTFS} /bin/sh -c "$*"
+ LANG=C LC_ALL=C DEBIAN_FRONTEND=noninteractive chroot "$rootfs" "$@"
}
-export -f chroot_exec
# Unmount and remove any temporary files
-#
cleanup() {
+ log "cleaning"
set +e
-
- # Identify and kill all processes still using files
- echo "Killing processes using mount point" 1>&2
- fuser -k "${ROOTFS}"
- sleep 3
- fuser -9 -k -v "${ROOTFS}"
-
- # Clean up all temporary mount points
- echo "Removing temporary mount points" 1>&2
- umount -l "${ROOTFS}/proc" 2> /dev/null
- umount -l "${ROOTFS}/sys" 2> /dev/null
- umount -l "${ROOTFS}/dev/pts" 2> /dev/null
- umount -l "${ROOTFS}/dev" 2> /dev/null
+ fuser --kill -SIGTERM "$rootfs"
+ sleep 2
+ fuser --kill -SIGKILL --verbose "$rootfs"
+ umount -l "$rootfs/proc" 2> /dev/null
+ umount -l "$rootfs/sys" 2> /dev/null
+ umount -l "$rootfs/dev/pts" 2> /dev/null
+ umount -l "$rootfs/dev" 2> /dev/null
trap - 0 1 2 3 6
}
trap cleanup 0 1 2 3 6
-# Bootstrap initial file system
-#
-echo "Creating rootfs" 1>&2
-# proxy is set to use apt-cacher-ng
-http_proxy="localhost:3142" qemu-debootstrap \
- --verbose \
- --include=linux-image-armmp-lpae,locales,flash-kernel,sunxi-tools,firmware-linux-free,u-boot-tools \
- --arch=armhf \
- "$RELEASE" \
- "$ROOTFS" \
- http://httpredir.debian.org/debian
-
-# Prepare filesystem to be chrooted
-#
-echo "Creating temporary system mount points in rootfs" 1>&2
-mount -t proc chproc "$ROOTFS/proc"
-mount -t sysfs chsys "$ROOTFS/sys"
-mount -t devtmpfs chdev "$ROOTFS/dev"
-mount -t devpts chpts "$ROOTFS/dev/pts"
-echo "Configuring rootfs" 1>&2
-chroot_exec dpkg --configure -a
-
-# Run setup scripts
-#
-echo "Running setup scripts" 1>&2
-dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-for script in "$dir"/mkrootfs.d/*.sh; do
- echo "Running $script" 1>&2
- bash "$script"
-done
-echo "Completed setup scripts" 1>&2
+# Prepare filesystem to be chrooted into
+log "creating temporary system mount points"
+mount -t proc chproc "$rootfs/proc"
+mount -t sysfs chsys "$rootfs/sys"
+mount -t devtmpfs chdev "$rootfs/dev"
+mount -t devpts chpts "$rootfs/dev/pts"
+
+# Set up package archive
+log "configuring apt archive"
+cat > "$rootfs/etc/apt/sources.list" <<EOF
+deb http://httpredir.debian.org/debian $release main contrib non-free
+deb http://httpredir.debian.org/debian $release-updates main contrib non-free
+deb http://security.debian.org $release/updates main contrib non-free
+EOF
+chroot_exec apt-get update
+
+# Configure fstab
+log "configuring fstab"
+cat > "$rootfs/etc/fstab" <<EOF
+/dev/mmcblk0p1 /boot/firmware vfat noatime 0 2
+/dev/mmcblk0p2 / ext4 noatime 0 1
+tmpfs /tmp tmpfs defaults 0 0
+EOF
+
+# Install proprietary 2nd-stage bootloader firmware
+log "installing proprietary 2nd-stage bootloader"
+mkdir -p "$rootfs/boot/firmware"
+wget https://github.com/raspberrypi/firmware/raw/master/boot/bootcode.bin \
+ -O "$rootfs/boot/firmware/bootcode.bin"
+wget https://github.com/raspberrypi/firmware/raw/master/boot/start.elf \
+ -O "$rootfs/boot/firmware/start.elf"
+
+# Install u-boot 3rd-stage bootloader
+# https://blog.night-shade.org.uk/2015/05/booting-a-raspberry-pi2-with-u-boot-and-hyp-enabled/
+log "installing u-boot 3rd stage bootloader"
+chroot_exec apt-get install -y u-boot-rpi
+cp "$rootfs/usr/lib/u-boot/rpi_2/u-boot.bin" "$rootfs/boot/firmware/u-boot.bin"
+cat <<EOF > "$rootfs/boot/firmware/config.txt"
+gpu_mem=64
+kernel=u-boot.bin
+EOF
+
+# Install and configure kernel flashing utility
+# http://sjoerd.luon.net/posts/2015/02/debian-jessie-on-rpi2/
+chroot_exec apt-get install -y flash-kernel u-boot-tools
+echo "Raspberry Pi 2 Model B" > "$rootfs/etc/flash-kernel/machine"
+cat <<EOF > "$rootfs/etc/flash-kernel/ubootenv.d/10-partition"
+setenv distro_bootpart 2
+setenv prefix /boot/
+EOF
+echo "Machine: Raspberry Pi 2 Model B" >> "$rootfs/etc/flash-kernel/db"
+echo "Boot-Script-Path: /boot/firmware/boot.scr" >> "$rootfs/etc/flash-kernel/db"
+
+# Install kernel
+log "installing kernel"
+chroot_exec apt-get install -y linux-image-armmp-lpae
+
+# Configure kernel boot args
+log "setting kernel boot arguments"
+cat <<EOF > "$rootfs/etc/default/flash-kernel"
+LINUX_KERNEL_CMDLINE="quiet"
+LINUX_KERNEL_CMDLINE_DEFAULTS="console=ttyAMA0 root=/dev/mmcblk0p2"
+EOF
+chroot_exec flash-kernel
+
+# Set up hostname configuration
+log "configuring network host names"
+echo "$hostname" > "$rootfs/etc/hostname"
+sed -i "/^127.0.0.1/ s/\$/ $hostname/" "$rootfs/etc/hosts"
+sed -i "/^::1/ s/\$/ $hostname/" "$rootfs/etc/hosts"
+
+# Set up network interfaces
+log "configuring network interfaces"
+cat > "$rootfs/etc/network/interfaces.d/lo" <<EOF
+auto lo
+iface lo inet loopback
+EOF
+cat > "$rootfs/etc/network/interfaces.d/eth0" <<EOF
+allow-hotplug eth0
+iface eth0 inet dhcp
+EOF
+
+# Set up root account
+log "creating root account"
+chroot_exec sh -c "echo root:guest | chpasswd"
+
+# Install OpenSSH
+log "setting up openssh-server"
+chroot_exec apt-get -y install openssh-server
+if [ -f "$ssh_key" ]; then
+ mkdir -p "$rootfs/root/.ssh"
+ chmod 600 "$rootfs/root/.ssh"
+ cat "$ssh_key" > "$rootfs/root/.ssh/authorized_keys"
+ chmod 600 "$rootfs/root/.ssh/authorized_keys"
+ sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' "$rootfs/etc/ssh/sshd_config"
+ echo "SSH password login disabled. Use the key in $ssh_key to login." 1>&2
+else
+ sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/g' "$rootfs/etc/ssh/sshd_config"
+ echo "Warning: SSH password login is enabled! This can be a security risk." 1>&2
+fi
+
+# Prepare first boot
+log "setting up first boot script"
+chroot_exec apt-get -y install parted
+cat <<'EOM' > "$rootfs/etc/rc.firstboot"
+#!/bin/bash
+set -e
+logger -t "rc.firstboot" "Starting first boot"
+
+led_blink() {
+ local led=/sys/class/leds/"$1"
+
+ local time_on="$2"
+ local time_off="$time_on"
+ local blinks="$3"
+
+ local trigger=$(cat "$led/trigger" | sed 's/.*\[\(.*\)\].*/\1/g' -)
+ local brightness=$(cat "$led/brightness")
+
+ echo "none" > "$led/trigger"
+ for i in $(seq 1 $blinks); do
+ echo 255 > "$led/brightness"
+ sleep "$time_on"
+ echo 0 > "$led/brightness"
+ sleep "$time_off"
+ done
+ echo "$brightness" > "$led/brightness"
+ echo "$trigger" > "$led/trigger"
+}
+
+led_trigger() {
+ local led=/sys/class/leds/"$1"
+
+ if [ "$2" == "heartbeat" ]; then
+ modprobe ledtrig_heartbeat || true
+ fi
+ echo "$2" > "$led/trigger"
+}
+
+led_blink ACT 0.1 20 &
+led_blink PWR 0.1 20
+led_trigger PWR heartbeat
+led_trigger ACT mmc0
+
+logger -t "rc.firstboot" "Generating ssh host keys"
+rm -f /etc/ssh/ssh_host_*
+systemctl stop sshd
+ssh-keygen -q -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key
+ssh-keygen -q -t dsa -N "" -f /etc/ssh/ssh_host_dsa_key
+ssh-keygen -q -t ecdsa -N "" -f /etc/ssh/ssh_host_ecdsa_key
+ssh-keygen -q -t ed25519 -N "" -f /etc/ssh/ssh_host_ed25519_key
+systemctl start sshd
+
+logger -t "rc.firstboot" "Expanding root file system"
+fdisk /dev/mmcblk0 <<EOF || true
+p
+d
+2
+n
+p
+2
+
+
+n
+w
+EOF
+partprobe
+resize2fs /dev/mmcblk0p2
+
+logger -t "rc.firstboot" "Generating D-Bus machine-id"
+rm -f /var/lib/dbus/machine-id
+dbus-uuidgen --ensure
+
+logger -t "rc.firstboot" "Regenerating initramfs"
+update-initramfs -u
+
+logger -t "rc.firstboot" "Restarting core services"
+systemctl daemon-reload
+systemctl restart networking.service
+systemctl restart systemd-networkd.service
+
+logger -t "rc.firstboot" "First boot actions finished"
+rm -f /etc/rc.firstboot
+sed -i '/.*rc.firstboot/d' /etc/rc.local
+
+led_trigger ACT heartbeat
+led_trigger PWR none
+EOM
+chmod +x "$rootfs/etc/rc.firstboot"
+
+cat <<EOF > "$rootfs/etc/rc.local"
+#!/bin/sh -e
+/etc/rc.firstboot
+exit 0
+EOF
+chmod +x "$rootfs/etc/rc.local"
cleanup
-echo "Successfully created rootfs" 1>&2
+
+log "done"
diff --git a/mkrootfs.d/01-apt.sh b/mkrootfs.d/01-apt.sh
deleted file mode 100644
index 9bda665..0000000
--- a/mkrootfs.d/01-apt.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-cat > "$ROOTFS/etc/apt/sources.list" <<EOF
-deb http://httpredir.debian.org/debian ${RELEASE} main contrib non-free
-deb http://httpredir.debian.org/debian ${RELEASE}-updates main contrib non-free
-deb http://security.debian.org ${RELEASE}/updates main contrib non-free
-EOF
diff --git a/mkrootfs.d/02-kernel.sh b/mkrootfs.d/02-kernel.sh
deleted file mode 100644
index be000b6..0000000
--- a/mkrootfs.d/02-kernel.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-# Set up automatic kernel installation an upgrades
-echo "Cubietech Cubietruck Plus" > "$ROOTFS/etc/flash-kernel/machine"
-echo 'LINUX_KERNEL_CMDLINE="console=ttyS0,115200 hdmi.audio=EDID:0 disp.screen0_output_mode=EDID:1280x1024p60 rootfstype=ext4 root=/dev/mmcblk0p1 rootwait panic=10 ${extra}"' >> "$ROOTFS/etc/default/flash-kernel"
-
-# Enable specific modules at startup
-echo "rtc_sunxi" >> "$ROOTFS/etc/initramfs-tools/modules"
-echo "mmc_core" >> "$ROOTFS/etc/initramfs-tools/modules"
-echo "mmc_block" >> "$ROOTFS/etc/initramfs-tools/modules"
-echo "sdhci" >> "$ROOTFS/etc/initramfs-tools/modules"
-echo "sdhci-pci" >> "$ROOTFS/etc/initramfs-tools/modules"
-
-chroot_exec flash-kernel
diff --git a/mkrootfs.d/03-fstab.sh b/mkrootfs.d/03-fstab.sh
deleted file mode 100644
index 835afc6..0000000
--- a/mkrootfs.d/03-fstab.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-# Configure fstab
-cat > "$ROOTFS/etc/fstab" <<EOF
-/dev/mmcblk0p1 / ext4 relatime,errors=remount-ro 0 1
-tmpfs /tmp tmpfs defaults 0 0
-EOF
diff --git a/mkrootfs.d/04-network.sh b/mkrootfs.d/04-network.sh
deleted file mode 100644
index 31b5f1a..0000000
--- a/mkrootfs.d/04-network.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-# Set host configuration
-echo "$HOSTNAME" > "$ROOTFS/etc/hostname"
-sed -i "/^127.0.0.1/ s/\$/ $HOSTNAME/" "$ROOTFS/etc/hosts"
-sed -i "/^::1/ s/\$/ $HOSTNAME/" "$ROOTFS/etc/hosts"
-
-# Set up networking
-cat > "$ROOTFS/etc/network/interfaces.d/lo" <<EOF
-auto lo
-iface lo inet loopback
-EOF
-cat > "$ROOTFS/etc/network/interfaces.d/eth0" <<EOF
-allow-hotplug eth0
-iface eth0 inet dhcp
-EOF
diff --git a/mkrootfs.d/05-locales.sh b/mkrootfs.d/05-locales.sh
deleted file mode 100644
index e57930b..0000000
--- a/mkrootfs.d/05-locales.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-chroot_exec dpkg-reconfigure locales
-chroot_exec dpkg-reconfigure tzdata
diff --git a/mkrootfs.d/06-login.sh b/mkrootfs.d/06-login.sh
deleted file mode 100644
index 67811e9..0000000
--- a/mkrootfs.d/06-login.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-# Enable serial console login
-echo "T0:23:respawn:/sbin/getty -L ttyS0 115200 vt100" >> "$ROOTFS/etc/inittab"
-
-if [ -e "$SSH_KEY" ]; then
- chroot_exec apt-get -y install openssh-server
- mkdir -p "$ROOTFS/root/.ssh"
- chmod 600 "$ROOTFS/root/.ssh"
- cat "$SSH_KEY" > "$ROOTFS/root/.ssh/authorized_keys"
- chmod 600 "$ROOTFS/root/.ssh/authorized_keys"
- sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' "$ROOTFS/etc/ssh/sshd_config"
- # Lock root account password
- chroot_exec passwd -l root
- echo "Success: root's account was locked and SSH password login disabled. Use the key in $SSH_KEY to login." 1>&2
-else
- sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/g' "$ROOTFS"/etc/ssh/sshd_config
- chroot_exec echo "root:guest" | chpasswd
- echo "Warning: no SSH key found, root's password has been set to 'guest' and SSH password login has been enabled! This can be a security risk if the device is exposed to a public network." 1>&2
-fi
diff --git a/mkrootimg b/mkrootimg
index 01d509b..22a492f 100755
--- a/mkrootimg
+++ b/mkrootimg
@@ -3,36 +3,19 @@ set -e
print_usage() {
cat 1>&2 <<EOF
-Usage: $0 rootfs-directory image
-Package the contents of a directory into an ext4-based binary image file.
+Usage: $0 <rootfs_directory> <image_file>
+Package the contents of a debian root filesystem into a binary image file, ready to be burned onto an sd card.
EOF
}
-# Process options
-#
-while [ $# -gt 2 ]
-do
- case "$1" in
- --help|-h)
- print_usage
- exit 0
- ;;
- *)
- echo "Unknown option '$1'" 1>&2
- exit 1
- esac
- shift
-done
-
-# Process last argument, the root file system location
-if [ -z "$1" ] || [ -z "$2" ] || [ "$1" = -h ] || [ "$1" = --help ]; then
- print_usage
- exit 1
-fi
-if [ "$EUID" -ne 0 ]; then
- echo "This must be run as root." 1>&2
+fail() {
+ echo "$1" >&2
exit 1
-fi
+}
+
+log() {
+ echo "mkrootimg: $1" >&2
+}
# Directory to package
rootfs="$1"
@@ -40,46 +23,90 @@ rootfs="$1"
# Binary image file
image="$2"
-# Number of 512-byte sectors reserved at the beginning of the image
-# (usually 1M for e.g. a bootloader)
-table_sectors=$(expr 1 \* 1024 \* 1024 \/ 512)
-
-# Calculate size of the rootfs directory in KB
-rootfs_size=$(expr `du -s "${rootfs}" | awk '{ print $1 }'`)
-
-# The root partition is EXT4
-# This means more space than the actual used space of the rootfs is used.
-# As overhead for journaling and reserved blocks 25% are added.
-rootfs_sectors=$(expr $(expr ${rootfs_size} + ${rootfs_size} \/ 100 \* 25) \* 1024 \/ 512)
+# Contains temporary build files
+builddir=$(mktemp -d)
-# Calculate required image size in 512 Byte sectors
-image_sectors=$(expr ${table_sectors} + ${rootfs_sectors})
+([ -n "$rootfs" ] && [ -n "$image" ]) || fail "$(print_usage)"
+[ -d "$rootfs" ] || fail "$rootfs does not exist or is not a directory"
+[ ! -e "$image" ] || fail "$image already exists"
-# Initialize image file
-dd if=/dev/zero of="$image" bs=512 count=${table_sectors}
-dd if=/dev/zero of="$image" bs=512 count=0 seek=${image_sectors}
-
-# Write partition table
-sfdisk "$image" <<EOF
-${table_sectors},${rootfs_sectors},L,*
-EOF
-
-# Setup temporary loop devices
-image_loop="$(losetup -o 1M -f --show $image)"
-
-mkfs.ext4 -O^64bit "$image_loop"
+[ "$EUID" -eq 0 ] || fail "$0 must be run as root"
cleanup() {
- losetup -d "$image_loop" 2> /dev/null
+ set +e
+ umount --lazy "$builddir/mount/boot/firmware" 2> /dev/null
+ umount --lazy "$builddir/mount" 2> /dev/null
+ sync
+ losetup --detach "$bootfs_loop" 2> /dev/null
+ losetup --detach "$rootfs_loop" 2> /dev/null
+ rm -rf "$builddir"
trap - 0 1 2 3 6
}
trap cleanup 0 1 2 3 6
-# Mount the temporary loop devices
-mount_dir=$(mktemp -d)
-mount "$image_loop" "$mount_dir"
+# Partition layout
+#
+# Content Size
+# ---------------------------------------------
+# reserved 1024k (1M, for e.g. a bootloader)
+# bootfs 100*1024k (100M)
+# rootfs as required
+
+# size in bytes
+reserved_size=1024*1024
+bootfs_size=$((100*1024*1024))
+rootfs_size=$(du --block-size=1 -s "$rootfs" | awk '{ print $1 }')
+
+# converted to 512-byte sectors
+reserved_sectors=$(( $reserved_size / 512 ))
+bootfs_sectors=$(( $bootfs_size / 512 ))
+# as overhead for journaling and reserved blocks 25% are added.
+rootfs_sectors=$(( ($rootfs_size + $rootfs_size * 25 / 100) / 512 ))
+image_sectors=$(( $reserved_sectors + $bootfs_sectors + $rootfs_sectors ))
+
+# create empty image file
+log "creating empty image"
+dd if=/dev/zero of="$image" bs=512 count="$image_sectors" status=none
+
+# write partition table to image
+log "partitioning image"
+sfdisk --label dos -q -uS "$image" <<EOF > /dev/null
+$reserved_sectors,$bootfs_sectors,c,*
+,$rootfs_sectors,L,
+EOF
-# Copy all files from the chroot to the loop device mount point directory
-rsync -a "$rootfs/" "$mount_dir"
-umount "$mount_dir"
+# set up temporary loop devices
+log "setting up image loop devices"
+bootfs_loop=$(losetup \
+ --offset $(( $reserved_sectors * 512 )) \
+ --sizelimit $(( $bootfs_sectors * 512 )) \
+ --find \
+ --show \
+ "$image")
+rootfs_loop=$(losetup \
+ --offset $(( ($reserved_sectors + $bootfs_sectors) * 512 )) \
+ --sizelimit $(( $rootfs_sectors * 512 )) \
+ --find \
+ --show \
+ "$image")
+
+# format partitions
+log "formatting partitions"
+mkfs.vfat -F 32 "$bootfs_loop" &> /dev/null
+mkfs.ext4 -O^64bit "$rootfs_loop" &> /dev/null
+
+# mount partitions
+log "mounting partitions"
+mkdir -p "$builddir/mount"
+mount "$rootfs_loop" "$builddir/mount"
+mkdir -p "$builddir/mount/boot/firmware"
+mount "$bootfs_loop" "$builddir/mount/boot/firmware"
+
+# copy root filesystem to image
+log "copying root filesystem"
+rsync -a "$rootfs/" "$builddir/mount/"
+
+log "cleaning up"
cleanup
+
+log "done"