diff options
author | Andrew Cady <d@jerkface.net> | 2020-10-12 14:59:59 -0400 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2020-10-12 14:59:59 -0400 |
commit | f6e7c471f5a6d9fb1c9defc514d3b668a9cb9f8f (patch) | |
tree | 82d418de47b4e2059a5fa658f5445798dcd56acc | |
parent | b19d7bb1f5257cfd07c3389bc9b8bf728e00a9f5 (diff) |
btrfs-shrink
-rwxr-xr-x | btrfs-shrink | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/btrfs-shrink b/btrfs-shrink new file mode 100755 index 0000000..1d059ff --- /dev/null +++ b/btrfs-shrink | |||
@@ -0,0 +1,101 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | die() { printf "%s: Error: %s\n" "$0" "$*" >&2; exit 1; } | ||
4 | |||
5 | shrink() | ||
6 | { | ||
7 | local shrinkmegs=100 mountpoint="$1" | ||
8 | while true; do | ||
9 | while ! btrfs filesystem resize -${shrinkmegs}M "$mountpoint"/; do | ||
10 | shrinkmegs=$((shrinkmegs - 10 )) | ||
11 | if [ $shrinkmegs -lt 10 ]; then | ||
12 | return | ||
13 | fi | ||
14 | done | ||
15 | done | ||
16 | } | ||
17 | |||
18 | smart_shrink() | ||
19 | { | ||
20 | local min_dev_size mountpoint="$1" | ||
21 | if min_dev_size=$(btrfs inspect-internal min-dev-size --id 1 "$mountpoint") && [ "$min_dev_size" ] | ||
22 | then | ||
23 | set -- $min_dev_size | ||
24 | [ "$2" = bytes ] || die "Unexpected output from btrfs inspect-internal min-dev-size: '$min_dev_size'" | ||
25 | btrfs filesystem resize "$1" "$mountpoint" && return | ||
26 | fi | ||
27 | shrink "$mountpoint" | ||
28 | } | ||
29 | |||
30 | btrfs_truncate() | ||
31 | { | ||
32 | local img="$1" bytes | ||
33 | # 548044800/1176715264 bytes used | ||
34 | bytes=$(file "$img" | sed -ne 's?.*/\([0-9]*\) bytes used.*?\1?p') | ||
35 | if [ "$bytes" ]; then | ||
36 | truncate -s "$bytes" "$img" | ||
37 | fi | ||
38 | } | ||
39 | |||
40 | parse_btrfs_dev_size() | ||
41 | { | ||
42 | local dev="$1" | ||
43 | sed -n 's?.* size \([0-9]*\) .* path \([^ ]*\)$?\1 \2? p' | | ||
44 | while read size name | ||
45 | do [ "$name" = "$dev" ] || continue | ||
46 | echo $size | ||
47 | break | ||
48 | done | ||
49 | } | ||
50 | |||
51 | get_truncate_size() | ||
52 | { | ||
53 | local imgfile="$1" mountpoint="$2" loop_dev dev_size | ||
54 | loop_dev=$(losetup -ONAME --raw -nj "$imgfile") | ||
55 | btrfs fi sh --raw "$mountpoint" | parse_btrfs_dev_size "$loop_dev" | ||
56 | } | ||
57 | |||
58 | main() | ||
59 | { | ||
60 | [ "$(id -u)" = 0 ] || die 'you are not root' | ||
61 | if [ -d "$1" ]; then | ||
62 | mountpoint=$1 | ||
63 | mountpoint -q "$mountpoint" || die "not a mountpoint: $1" | ||
64 | smart_shrink "$mountpoint" | ||
65 | elif [ -f "$1" ]; then | ||
66 | truncate= | ||
67 | mountpoint="$1".mnt.tmp | ||
68 | mkdir -p "$mountpoint" | ||
69 | mount -t btrfs "$1" "$mountpoint" | ||
70 | result=$? | ||
71 | |||
72 | if [ $result = 0 ]; then | ||
73 | smart_shrink "$mountpoint" | ||
74 | result=$? | ||
75 | if [ $result = 0 ] | ||
76 | then | ||
77 | truncate=$(get_truncate_size "$1" "$mountpoint") | ||
78 | fi | ||
79 | umount "$mountpoint" | ||
80 | fi | ||
81 | rmdir "$mountpoint" | ||
82 | if [ "$truncate" ] | ||
83 | then | ||
84 | truncate -s "$truncate" "$1" | ||
85 | fi | ||
86 | return $result | ||
87 | else | ||
88 | die "not a file or directory: $1" | ||
89 | fi | ||
90 | } | ||
91 | |||
92 | usage() | ||
93 | { | ||
94 | echo "Usage: $0 <mountpoint>" >&2 | ||
95 | } | ||
96 | |||
97 | case $# in | ||
98 | 0) usage ;; | ||
99 | 1) main "$1" ;; | ||
100 | *) usage; exit 1 ;; | ||
101 | esac | ||