#!/bin/sh die() { printf "%s: Error: %s\n" "$0" "$*" >&2; exit 1; } [ "$(id -u)" = 0 ] || die 'you are not root' TEMP="$(getopt -o 'f:vcxz' --long size:,file:,create,extract,gzip,gunzip,ungzip -n "$0" -- "$@")" || die 'getopt error' eval set -- "$TEMP" MIN_BYTES=$((128 * 1024 * 1024)) FILE= MODE= ZIP=y BYTES=$((256 * 1024 * 1024)) while [ $# -gt 0 ]; do case "$1" in --size) BYTES=$(( $2 * 1024 * 1024)) [ "$BYTES" -ge "$MIN_BYTES" ] || die "invalid size (too small or not a number)" SIZE="$2" shift 2 ;; -f|--file) [ "$FILE" ] && die "only one archive file can be specified" FILE="$2" shift 2 ;; -c|--create) [ "$MODE" ] && die "incompatible modes" MODE=create shift ;; -x|--extract) [ "$MODE" ] && die "incompatible modes" MODE=extract shift ;; -v) VERBOSE=y shift ;; -z|--gzip|--gunzip|--ungzip) ZIP=y shift ;; --) shift; break;; *) die 'getopt error';; esac done [ "$FILE" ] || die "you must specify a file (this is not tar, there is no stdout)" create() { TMPFILE=$(dirname "$FILE")/.~.$(basename "$FILE") # [ ! -e "$FILE" ] || die "refusing to overwrite '$FILE'" # [ ! -e "$TMPFILE" ] || die "refusing to overwrite '.~.$FILE'" rm -f "$TMPFILE" fallocate -l "$BYTES" "$TMPFILE" mountpoint=$(mktemp -d) || die trap 'umount "$mountpoint"; rmdir "$mountpoint"; rm "$TMPFILE"' EXIT mkfs.btrfs "$TMPFILE" || die mount -o ${ZIP:+compress,}loop "$TMPFILE" "$mountpoint" || die rsync -a ${VERBOSE:+ -i} "$@" "$mountpoint" || die shrink "$mountpoint" umount "$mountpoint" || die btrfs_truncate "$TMPFILE" rmdir "$mountpoint" btrfstune -S1 "$TMPFILE" || die mv "$TMPFILE" "$FILE" trap '' EXIT } btrfs_truncate() { local img="$1" bytes bytes=$(file "$img" | sed -ne 's?.*/\([0-9]*\) bytes used.*?\1?p') if [ "$bytes" ]; then truncate -s "$bytes" "$img" fi } get_k() { local mountpoint="$1" kilos kilos=$(btrfs fi usage -k "$mountpoint"|sed -ne 's/[\t ]*Device size:[\t ]*//p') case "$kilos" in *.00KiB) echo "${kilos%.00KiB}";; *) return 1;; esac } shrink() { shrinkmegs=100 while true; do while ! btrfs filesystem resize -${shrinkmegs}M "$mountpoint"/; do shrinkmegs=$((shrinkmegs - 10 )) if [ $shrinkmegs -lt 10 ]; then return fi done done } $MODE "$@"