From 8bcb2c9e0e224f68aae750c2ece5791987a16ce8 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Sun, 2 Apr 2017 15:15:18 +0100 Subject: [PATCH] Add a formatting container This is based on the code we used for Docker Editions, and will format an external drive, to be used for example for `/var` for Docker image persistence. It does not `mount` the drive yet, as splitting format and mount gives better modularity. Example yaml fragment: ``` - name: format image: "mobylinux/format:097d4f22b20f976b1f89d8f0b8a5d074d35b856c" binds: - /dev:/dev capabilities: - CAP_SYS_ADMIN - CAP_MKNOD ``` Signed-off-by: Justin Cormack --- pkg/format/Dockerfile | 13 +++++ pkg/format/Makefile | 29 +++++++++++ pkg/format/format.sh | 113 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 pkg/format/Dockerfile create mode 100644 pkg/format/Makefile create mode 100755 pkg/format/format.sh diff --git a/pkg/format/Dockerfile b/pkg/format/Dockerfile new file mode 100644 index 000000000..124e4daec --- /dev/null +++ b/pkg/format/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:3.5 + +RUN \ + apk update && apk upgrade -a && \ + apk add --no-cache \ + e2fsprogs \ + e2fsprogs-extra \ + jq \ + sfdisk \ + && true + +COPY . ./ +CMD ["/bin/sh", "/format.sh"] diff --git a/pkg/format/Makefile b/pkg/format/Makefile new file mode 100644 index 000000000..b11e09f67 --- /dev/null +++ b/pkg/format/Makefile @@ -0,0 +1,29 @@ +.PHONY: tag push + +BASE=alpine:3.5 +IMAGE=format + +default: push + +hash: Dockerfile format.sh + DOCKER_CONTENT_TRUST=1 docker pull $(BASE) + tar cf - $^ | docker build --no-cache -t $(IMAGE):build - + docker run --rm --entrypoint /bin/sh $(IMAGE):build -c "cat $^ /lib/apk/db/installed | sha1sum" | sed 's/ .*//' > $@ + +push: hash + docker pull mobylinux/$(IMAGE):$(shell cat hash) || \ + (docker tag $(IMAGE):build mobylinux/$(IMAGE):$(shell cat hash) && \ + docker push mobylinux/$(IMAGE):$(shell cat hash)) + docker rmi $(IMAGE):build + rm -f hash + +tag: hash + docker pull mobylinux/$(IMAGE):$(shell cat hash) || \ + docker tag $(IMAGE):build mobylinux/$(IMAGE):$(shell cat hash) + docker rmi $(IMAGE):build + rm -f hash + +clean: + rm -f hash + +.DELETE_ON_ERROR: diff --git a/pkg/format/format.sh b/pkg/format/format.sh new file mode 100755 index 000000000..bd5bcd89e --- /dev/null +++ b/pkg/format/format.sh @@ -0,0 +1,113 @@ +#!/bin/sh + +# this script assumes anything on the disk can be removed if corrupted +# other use cases may need different scripts. + +# currently only supports ext4 but should be expanded + +do_fsck() +{ + # preen + /sbin/e2fsck -p $* + EXIT_CODE=$? + # exit code 1 is errors corrected + [ "${EXIT_CODE}" -eq 1 ] && EXIT_CODE=0 + # exit code 2 or 3 means need to reboot + [ "${EXIT_CODE}" -eq 2 -o "${EXIT_CODE}" -eq 3 ] && /sbin/reboot + # exit code 4 or over is fatal + [ "${EXIT_CODE}" -lt 4 ] && return "${EXIT_CODE}" + + # try harder + /sbin/e2fsck -y $* + # exit code 1 is errors corrected + [ "${EXIT_CODE}" -eq 1 ] && EXIT_CODE=0 + # exit code 2 or 3 means need to reboot + [ "${EXIT_CODE}" -eq 2 -o "${EXIT_CODE}" -eq 3 ] && /sbin/reboot + # exit code 4 or over is fatal + [ "${EXIT_CODE}" -ge 4 ] && printf "Filesystem unrecoverably corrupted, will reformat\n" + + return "${EXIT_CODE}" +} + +do_fsck_extend_mount() +{ + DRIVE="$1" + DATA="$2" + + do_fsck "$DATA" || return 1 + + # only try to extend if there is a single partition and free space + PARTITIONS=$(sfdisk -J "$DRIVE" | jq '.partitiontable.partitions | length') + + if [ "$PARTITIONS" -eq 1 ] && \ + sfdisk -F "$DRIVE" | grep -q 'Unpartitioned space' && + ! sfdisk -F "$DRIVE" | grep -q '0 B, 0 bytes, 0 sectors' + then + SPACE=$(sfdisk -F "$DRIVE" | grep 'Unpartitioned space') + printf "Resizing disk partition: $SPACE\n" + + START=$(sfdisk -J "$DRIVE" | jq -e '.partitiontable.partitions | map(select(.type=="83")) | .[0].start') + + sfdisk -q --delete "$DRIVE" 2> /dev/null + echo "${START},,83;" | sfdisk -q "$DRIVE" + + # set bootable flag + sfdisk -A "$DRIVE" 1 + + # update status + blockdev --rereadpt $diskdev 2> /dev/null + mdev -s + + # wait for device + for i in $(seq 1 50); do test -b "$DATA" && break || sleep .1; mdev -s; done + + # resize2fs fails unless we use -f here + do_fsck -f "$DATA" || return 1 + resize2fs "$DATA" + + do_fsck "$DATA" || return 1 + fi +} + +do_mkfs() +{ + diskdev="$1" + + # new disks does not have an DOS signature in sector 0 + # this makes sfdisk complain. We can workaround this by letting + # fdisk create that DOS signature, by just do a "w", a write. + # http://bugs.alpinelinux.org/issues/145 + echo "w" | fdisk $diskdev >/dev/null + + # format one large partition + echo ";" | sfdisk --quiet $diskdev + + # update status + blockdev --rereadpt $diskdev 2> /dev/null + mdev -s + + FSOPTS="-O resize_inode,has_journal,extent,huge_file,flex_bg,uninit_bg,64bit,dir_nlink,extra_isize" + + mkfs.ext4 -q -F $FSOPTS ${diskdev}1 +} + +# TODO fix for multiple disks, cdroms etc +DEV="$(find /dev -maxdepth 1 -type b ! -name 'loop*' | grep -v '[0-9]$' | sed 's@.*/dev/@@' | sort | head -1 )" + +[ -z "${DEV}" ] && exit 1 + +DRIVE="/dev/${DEV}" + +# see if it has a partition table already +if sfdisk -d "${DRIVE}" >/dev/null 2>/dev/null +then + DATA=$(sfdisk -J "$DRIVE" | jq -e -r '.partitiontable.partitions | map(select(.type=="83")) | .[0].node') + if [ $? -eq 0 ] + then + do_fsck_extend_mount "$DRIVE" "$DATA" || do_mkfs "$DRIVE" + else + do_mkfs "$DRIVE" + fi +else + do_mkfs "$DRIVE" +fi