summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2020-10-12 14:59:59 -0400
committerAndrew Cady <d@jerkface.net>2020-10-12 14:59:59 -0400
commitf6e7c471f5a6d9fb1c9defc514d3b668a9cb9f8f (patch)
tree82d418de47b4e2059a5fa658f5445798dcd56acc
parentb19d7bb1f5257cfd07c3389bc9b8bf728e00a9f5 (diff)
btrfs-shrink
-rwxr-xr-xbtrfs-shrink101
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
3die() { printf "%s: Error: %s\n" "$0" "$*" >&2; exit 1; }
4
5shrink()
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
18smart_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
30btrfs_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
40parse_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
51get_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
58main()
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
92usage()
93{
94 echo "Usage: $0 <mountpoint>" >&2
95}
96
97case $# in
98 0) usage ;;
99 1) main "$1" ;;
100 *) usage; exit 1 ;;
101esac