diff options
author | Andrew Cady <d@jerkface.net> | 2016-04-19 14:39:53 -0400 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2016-04-19 14:39:53 -0400 |
commit | e98c4babea202d692a5dac8c05efc64a44e8aedc (patch) | |
tree | 033df22923a6b09b3395006ed4e2056afc281ff9 /old-school/lvm-create.sh | |
parent | 3884276c13d142483b1a018b31f5723e3961965f (diff) |
cause the grok-block hooks to run
Diffstat (limited to 'old-school/lvm-create.sh')
-rw-r--r-- | old-school/lvm-create.sh | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/old-school/lvm-create.sh b/old-school/lvm-create.sh new file mode 100644 index 0000000..22016f5 --- /dev/null +++ b/old-school/lvm-create.sh | |||
@@ -0,0 +1,393 @@ | |||
1 | #!/bin/sh | ||
2 | : ${ROOT_MKFS_CMD:=mkfs.ext4 -q} | ||
3 | : ${ROOT_FS_TYPE:=ext4} | ||
4 | |||
5 | losetup() { /sbin/losetup "$@"; } | ||
6 | |||
7 | lvm() | ||
8 | { | ||
9 | # get rid of warnings from lvm because we are holding open these fds | ||
10 | command lvm "$@" 3>&- 4>&- | ||
11 | } | ||
12 | |||
13 | luks_secret() | ||
14 | { | ||
15 | local parms=$-; # this junk keeps set -x from being too annoying | ||
16 | set +x | ||
17 | [ -n "$luks_secret" ] || luks_secret="$(head -c256 /dev/urandom)" | ||
18 | printf %s "$luks_secret" | ||
19 | case $parms in *x*) set -x; set -x ;; esac | ||
20 | } | ||
21 | |||
22 | mount_squashfs_images() | ||
23 | { | ||
24 | modprobe squashfs | ||
25 | find_squashfs_root | while read dirname basename; do | ||
26 | [ -n "$dirname" -a -n "$basename" ] || continue | ||
27 | local f="$dirname/$basename" | ||
28 | [ -f "$f" ] || return | ||
29 | local name=${basename%.squashfs} | ||
30 | mkdir -p /squashes/$name | ||
31 | mount -r -o loop "$f" /squashes/$name | ||
32 | done | ||
33 | bootdone squashfs-root | ||
34 | } | ||
35 | |||
36 | lv_exists() | ||
37 | { | ||
38 | [ -n "$1" ] && lvm lvs "$1" >/dev/null 2>&1 | ||
39 | } | ||
40 | |||
41 | floor4() | ||
42 | { | ||
43 | # Negatives round up, but aren't used. | ||
44 | echo $(($1 / 4 * 4)) | ||
45 | } | ||
46 | |||
47 | ceil4() | ||
48 | { | ||
49 | local x="$1" | ||
50 | [ $((x % 4)) -eq 0 ] || x=$((x + 4 - x % 4)) | ||
51 | printf '%d\n' "$x" | ||
52 | } | ||
53 | |||
54 | vgfree_megs() | ||
55 | { | ||
56 | local vg="$1" out | ||
57 | out=$(lvm vgs -o pv_free --noheadings --nosuffix --units m "$vg") || return | ||
58 | echo ${out%.*} | ||
59 | } | ||
60 | |||
61 | init_samizdat_lvs() | ||
62 | { | ||
63 | local megs_free cdrom_dev cdrom_sectors cdrom_megs root_megs | ||
64 | megs_free=$(vgfree_megs samizdat) || return | ||
65 | cdrom_dev="$(mountpoint_to_dev /cdrom)" || return | ||
66 | cdrom_sectors=$(blockdev --getsz "$cdrom_dev") || return | ||
67 | cdrom_megs=$(ceil4 $(( cdrom_sectors / 2048 ))) | ||
68 | root_megs=$(floor4 $(( megs_free - cdrom_megs ))) | ||
69 | |||
70 | if [ $root_megs -le 0 ]; then # No room for cdrom mirror. Oh well, charge forward. | ||
71 | root_megs=$megs_free | ||
72 | fi | ||
73 | |||
74 | lvm lvcreate -Z n -L ${root_megs}m -n root samizdat || return | ||
75 | $ROOT_MKFS_CMD /dev/mapper/samizdat-root || return | ||
76 | } | ||
77 | |||
78 | mount_aufs_branches() | ||
79 | { | ||
80 | local new="$1" | ||
81 | |||
82 | mkdir /overlay | ||
83 | mount -t${ROOT_FS_TYPE} /dev/mapper/samizdat-root /overlay || return | ||
84 | |||
85 | if [ "$new" ]; then | ||
86 | cp -a /gpg /overlay || return | ||
87 | touch /overlay/samizdat-filesystem-is-new | ||
88 | fi | ||
89 | bootdone rw-overlay | ||
90 | mirror_cdrom || return | ||
91 | mount_squashfs_images || return | ||
92 | } | ||
93 | |||
94 | init_samizdat() | ||
95 | { | ||
96 | local imgfile="$1" megs="$2" keyfile="$3" dev | ||
97 | |||
98 | init_samizdat_vg "$imgfile" "$megs" "$keyfile" || return | ||
99 | init_samizdat_lvs || return | ||
100 | |||
101 | mount_aufs_branches new | ||
102 | } | ||
103 | |||
104 | open_samizdat() | ||
105 | { | ||
106 | open_samizdat_vg "$@" || return | ||
107 | |||
108 | lvs=$(lvm lvs --separator / samizdat -o vg_name,lv_name --noheadings) || return | ||
109 | lvm lvchange -ay $lvs || return | ||
110 | |||
111 | mount_aufs_branches | ||
112 | } | ||
113 | |||
114 | init_samizdat_lodev() | ||
115 | { | ||
116 | local imgfile="$1" megs=$(ceil4 "$2") | ||
117 | truncate -s ${megs}M "$imgfile" || return | ||
118 | dev=$(losetup -f) && losetup "$dev" "$imgfile" || return | ||
119 | echo "$dev" | ||
120 | } | ||
121 | |||
122 | open_samizdat_vg() | ||
123 | { | ||
124 | local imgfile="$1" keyfile="$2" dev | ||
125 | local cryptname=samizdatcrypt | ||
126 | dev=$(losetup -f) && losetup "$dev" "$imgfile" || return | ||
127 | |||
128 | gpg2 --verify "$keyfile" || return | ||
129 | # The first --decrypt merely strips the signature. The option is | ||
130 | # poorly named for that case. | ||
131 | gpg2 --decrypt "$keyfile" | gpg2 --decrypt | cryptsetup --key-file - luksOpen "$dev" "$cryptname" || return | ||
132 | |||
133 | [ -b /dev/mapper/"$cryptname" ] || return | ||
134 | |||
135 | } | ||
136 | |||
137 | init_samizdat_vg() | ||
138 | { | ||
139 | local imgfile="$1" megs="$2" keyfile="$3" dev | ||
140 | local cryptname=samizdatcrypt | ||
141 | |||
142 | dev=$(init_samizdat_lodev "$imgfile" "$megs") || return | ||
143 | |||
144 | [ ! -b /dev/mapper/"$cryptname" ] || return | ||
145 | |||
146 | luks_secret >/dev/null | ||
147 | luks_secret | gpg2 --default-recipient-self --encrypt --armor | gpg2 --clearsign --output "$keyfile" || return | ||
148 | |||
149 | luks_secret | cryptsetup luksFormat "$dev" - || return | ||
150 | cryptsetup luksDump "$dev" >&2 | ||
151 | luks_secret | cryptsetup --key-file - luksOpen "$dev" "$cryptname" || return | ||
152 | |||
153 | [ -b /dev/mapper/"$cryptname" ] || return | ||
154 | |||
155 | lvm pvcreate /dev/mapper/"$cryptname" || return | ||
156 | lvm vgcreate samizdat /dev/mapper/"$cryptname" | ||
157 | } | ||
158 | |||
159 | grow_samizdat_vg_free() | ||
160 | { | ||
161 | # Grow the samizdat VG sufficiently to ensure it has at least $want_free_megs free. | ||
162 | |||
163 | local want_free_megs=$(ceil4 "$1") free_megs | ||
164 | free_megs=$(vgfree_megs samizdat) || return | ||
165 | |||
166 | if [ "$free_megs" -lt "$want_free_megs" ]; then | ||
167 | grow_samizdat_vg $((want_free_megs - free_megs)) || return | ||
168 | fi | ||
169 | } | ||
170 | |||
171 | majmin() | ||
172 | { | ||
173 | local dev="$1" major minor | ||
174 | eval $(stat -c 'major=%t minor=%T' "$dev") || return | ||
175 | [ "$major" -a "$minor" ] || return | ||
176 | printf '%d:%d\n' 0x$major 0x$minor | ||
177 | } | ||
178 | |||
179 | vg_to_pv() | ||
180 | { | ||
181 | lvm vgs "$1" -o devices --noheadings | | ||
182 | ( | ||
183 | found= multidev= | ||
184 | while read dev; do | ||
185 | dev=${dev%(*} | ||
186 | if [ "$found" -a "$found" != "$dev" ]; then | ||
187 | exit 1 | ||
188 | fi | ||
189 | found=$dev | ||
190 | done | ||
191 | readlink -f "$found" | ||
192 | ) | ||
193 | } | ||
194 | |||
195 | cryptdev_to_dev() | ||
196 | { | ||
197 | local dev="$1" majmin | ||
198 | majmin=$(majmin "$dev") || return | ||
199 | set -- /sys/dev/block/$majmin/slaves/* | ||
200 | [ $# = 1 ] || return | ||
201 | |||
202 | cryptsetup status "$dev" |while read k v; do if [ "$k" = device: ]; then echo $v; break; fi; done | ||
203 | } | ||
204 | |||
205 | cryptdev_to_backing_file() | ||
206 | { | ||
207 | local dev="$1" majmin result | ||
208 | majmin="$(majmin "$dev")" || return | ||
209 | set -- /sys/dev/block/$majmin/slaves/* | ||
210 | [ $# = 1 ] || return | ||
211 | read result < "$1"/loop/backing_file || return | ||
212 | printf '%s\n' "$result" | ||
213 | } | ||
214 | |||
215 | samizdat_backing_file() | ||
216 | { | ||
217 | local pv | ||
218 | pv=$(vg_to_pv samizdat) && [ "$pv" ] || return | ||
219 | cryptdev_to_backing_file "$pv" | ||
220 | } | ||
221 | |||
222 | grow_samizdat_lv() | ||
223 | { | ||
224 | # Increase the size of the specified LV by $megs MB, creating the LV and resizing the VG as necessary. | ||
225 | |||
226 | local lv_name="$1" megs="$2" stat imgfile freemegs | ||
227 | imgfile=$(samizdat_backing_file) || return | ||
228 | |||
229 | if lv_exists samizdat/"$lv_name"; then | ||
230 | grow_samizdat_vg_free "$megs" || return | ||
231 | lvm lvresize -r -L +${megs}m samizdat/"$lv_name" || return | ||
232 | else | ||
233 | grow_samizdat_vg_free "$megs" || return | ||
234 | lvm lvcreate -Z n -L ${megs}m -n "$lv_name" samizdat || return | ||
235 | fi | ||
236 | } | ||
237 | |||
238 | lodev_to_file() | ||
239 | { | ||
240 | local result majmin dev="$1" | ||
241 | majmin="$(majmin "$dev")" || return | ||
242 | read result < /sys/dev/block/$majmin/loop/backing_file || return | ||
243 | printf '%s' "$result" | ||
244 | } | ||
245 | |||
246 | grow_samizdat_vg() | ||
247 | { | ||
248 | # Increase the size of the samizdat VG by $megs MB, resizing the backing file as necessary. | ||
249 | |||
250 | local megs="$1" cryptdev dev imgfile stat | ||
251 | cryptdev=$(vg_to_pv samizdat) | ||
252 | dev=$(cryptdev_to_dev "$cryptdev") || return | ||
253 | [ -b "$dev" ] || return | ||
254 | |||
255 | if [ "$(stat -c '%t' "$dev")" = 7 ]; then # this is a loop device | ||
256 | |||
257 | imgfile=$(lodev_to_file "$dev") || return | ||
258 | stat="$(stat -c 'local du=$((%B*%b)) sz=%s' "$imgfile")" || return | ||
259 | eval "$stat" | ||
260 | stat=$(stat -f -c 'local df=$((%f*%S))' "$imgfile") || return | ||
261 | eval "$stat" | ||
262 | |||
263 | local min_free_space=30 | ||
264 | if [ $(( df - megs*1024*1024 - sz + du )) -le $((min_free_space * 1024 * 1024)) ]; then | ||
265 | echo 'grow_samizdat_vg: Not enough disk space!' >&2 | ||
266 | return -1 | ||
267 | fi | ||
268 | |||
269 | truncate -cs +${megs}M "$imgfile" || return | ||
270 | losetup -c "$dev" || return | ||
271 | cryptsetup resize "$cryptdev" || return | ||
272 | lvm pvresize "$cryptdev" || return | ||
273 | |||
274 | else | ||
275 | echo 'grow_samizdat_vg: Unimplemented!' >&2 | ||
276 | return 1 | ||
277 | |||
278 | fi | ||
279 | } | ||
280 | |||
281 | mountpoint_to_dev() | ||
282 | { | ||
283 | local wantmp="$1" dev mp rest | ||
284 | mountpoint -q "$wantmp" || return | ||
285 | while read dev mp rest; do if [ "$mp" = "$wantmp" ]; then echo "$dev"; return; fi; done < /proc/mounts | ||
286 | return 1 | ||
287 | } | ||
288 | |||
289 | get_cdrom_sizelimit() | ||
290 | { | ||
291 | # returns bytes | ||
292 | local dev="$1" sectors | ||
293 | sectors=$(blockdev --getsz "$dev") || return | ||
294 | if dd count=2 if="$dev" bs=2048 skip=$((sectors/4 - 2)) of=/dev/null 2>/dev/null; then | ||
295 | return | ||
296 | else | ||
297 | echo $(((sectors-8)*512)) | ||
298 | fi | ||
299 | } | ||
300 | |||
301 | mirror_cdrom() | ||
302 | { | ||
303 | local md_num=55 dev mp rest cdrom_dev sectors | ||
304 | cdrom_dev="$(mountpoint_to_dev /cdrom)" || return | ||
305 | |||
306 | local lv_name=samizdat/cdrom | ||
307 | local lv_dev=/dev/mapper/samizdat-cdrom | ||
308 | local md_name=/dev/md$md_num | ||
309 | |||
310 | if [ -b $md_name ]; then | ||
311 | echo "RAID device already exists: '$md_name'; try removing (mdadm -S $md_name) and retry" >&2 | ||
312 | return 1 | ||
313 | fi | ||
314 | |||
315 | if lv_exists $lv_name.tmp; then | ||
316 | lvm lvchange --available n $lv_name.tmp && lvm lvremove $lv_name.tmp || return | ||
317 | fi | ||
318 | |||
319 | if lv_exists $lv_name; then | ||
320 | umount /cdrom || return | ||
321 | mount -r "$lv_dev" /cdrom | ||
322 | return | ||
323 | fi | ||
324 | |||
325 | umount /cdrom || return | ||
326 | |||
327 | sectors=$(blockdev --getsz "$cdrom_dev") || return | ||
328 | grow_samizdat_lv ${lv_name#samizdat/}.tmp $((sectors / 2 / 1024 + 1)) || return | ||
329 | |||
330 | # In order to trick mdadm into accepting a read-only device, we need | ||
331 | # to create a (read-write) loopback device. | ||
332 | |||
333 | # Furthermore, in order to deal with block device errors caused by | ||
334 | # TAO "run-out blocks" we may need to discard the last two 2048-byte | ||
335 | # sectors. | ||
336 | |||
337 | local cdrom_loopdev sizelimit | ||
338 | cdrom_loopdev=$(losetup -f) || return | ||
339 | sizelimit=$(get_cdrom_sizelimit "$cdrom_dev") | ||
340 | |||
341 | # Apparently loopdev sizelimit is not respected by linux md. Nor | ||
342 | # blockdev --getsz. Does it even work? Anyway, although this is used | ||
343 | # here redundantly, 'mdadm --size' is used as well. | ||
344 | /sbin/losetup ${sizelimit:+--sizelimit=$sizelimit} "$cdrom_loopdev" "$cdrom_dev" || return | ||
345 | |||
346 | mdadm --build $md_name ${sizelimit:+--size=$((sizelimit / 1024))} \ | ||
347 | --level=1 --raid-devices=1 --force --write-mostly "$cdrom_loopdev" || return | ||
348 | mdadm -D $md_name >&2 | ||
349 | mdadm --add $md_name $lv_dev.tmp | ||
350 | mdadm -D $md_name >&2 | ||
351 | mdadm --grow $md_name -n 2 | ||
352 | mdadm -D $md_name >&2 | ||
353 | |||
354 | mount -r $md_name /cdrom || { mount -r "$cdrom_dev" /cdrom; return 1; } | ||
355 | |||
356 | chpst -P samizdat-cdrom-copy "$md_name" "$lv_name" "$lv_dev" "$cdrom_loopdev" "$cdrom_dev" & | ||
357 | echo "[$$] Launched RAID monitor with pid $!." >&2 | ||
358 | } | ||
359 | |||
360 | init_gpg() | ||
361 | { | ||
362 | bootwait samizdat-cdrom | ||
363 | export GNUPGHOME=/gpg/gnupghome | ||
364 | (umask 077; rsync --exclude '/luks-key*' --ignore-existing -rpP /cdrom/samizdat/gpg/ /gpg/) | ||
365 | |||
366 | if samizdat-password-agent >/var/log/samizdat-password-agent.log 2>&1; then | ||
367 | clear | ||
368 | true | ||
369 | else | ||
370 | false | ||
371 | fi | ||
372 | } | ||
373 | |||
374 | start_meter() | ||
375 | { | ||
376 | local startmsg="$*" | ||
377 | (exec >&4 | ||
378 | clear | ||
379 | echo -n $startmsg | ||
380 | set +x | ||
381 | while sleep 2; do | ||
382 | echo -n . | ||
383 | done) & | ||
384 | meterpid=$! | ||
385 | } | ||
386 | |||
387 | stop_meter() | ||
388 | { | ||
389 | local endmsg="$*" | ||
390 | kill $meterpid | ||
391 | echo " $endmsg" >&4 | ||
392 | } | ||
393 | |||