summaryrefslogtreecommitdiff
path: root/src/btrfs-utils/btarfs
blob: a51b2ef8e82a75bd91d8cb3e080d56fd3827cdaf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/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 "$@"