blob: 26c1365599588d4cb5326f47fc7b2e55d814c881 (
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
|
#!/bin/sh
die() { printf "%s: Error: %s\n" "$0" "$*" >&2; exit 1; }
shrink()
{
local shrinkmegs=100 mountpoint="$1"
while true; do
while ! btrfs filesystem resize "${DEVICE_ID}:-${shrinkmegs}M" "$mountpoint"/; do
shrinkmegs=$((shrinkmegs - 10 ))
if [ $shrinkmegs -lt 10 ]; then
return
fi
done
done
}
smart_shrink()
{
local min_dev_size mountpoint="$1"
if min_dev_size=$(btrfs inspect-internal min-dev-size --id "$DEVICE_ID" "$mountpoint") && [ "$min_dev_size" ]
then
set -- $min_dev_size
[ "$2" = bytes ] || die "Unexpected output from btrfs inspect-internal min-dev-size: '$min_dev_size'"
btrfs filesystem resize "$DEVICE_ID:$1" "$mountpoint" && return
fi
shrink "$mountpoint"
}
parse_btrfs_dev()
{
local dev="$1"
sed -n 's?^ devid *\([0-9]*\) size \([0-9]*\) .* path \([^ ]*\)$?\1 \2 \3? p' |
while read devid size name
do [ "$name" = "$dev" ] || continue
echo "$devid $size"
break
done
}
get_btrfs_dev()
{
local imgfile="$1" mountpoint="$2" loop_dev dev_size
losetup -nj "$imgfile" >&2
loop_dev=$(losetup -ONAME --raw -nj "$imgfile")
ds=$(btrfs fi sh --raw "$mountpoint" | parse_btrfs_dev "$loop_dev")
[ "$ds" ] || return
DEVICE_ID=${ds% *}
DEVICE_SIZE=${ds#* }
}
main()
{
[ "$(id -u)" = 0 ] || die 'you are not root'
if [ -d "$1" ]; then
exit 1
mountpoint=$1
mountpoint -q "$mountpoint" || die "not a mountpoint: $1"
smart_shrink "$mountpoint"
elif [ -f "$1" ]; then
truncate=
mountpoint="$1".mnt.tmp
mkdir -p "$mountpoint"
btrfs dev scan -u
mount -t btrfs "$1" "$mountpoint"
result=$?
if [ $result = 0 ]; then
get_btrfs_dev "$1" "$mountpoint" && smart_shrink "$mountpoint"
result=$?
if [ $result = 0 ]
then
get_btrfs_dev "$1" "$mountpoint" && truncate=$DEVICE_SIZE
fi
umount "$mountpoint"
fi
rmdir "$mountpoint"
if [ "$truncate" ]
then
r=$((truncate % 4096))
if [ $r -ne 0 ]
then
truncate=$((truncate + 4096 - r))
fi
truncate -s "$truncate" "$1"
fi
return $result
else
die "not a file or directory: $1"
fi
}
usage()
{
echo "Usage: $0 <mountpoint>" >&2
}
case $# in
0) usage ;;
1) main "$1" ;;
*) usage; exit 1 ;;
esac
|