summaryrefslogtreecommitdiff
path: root/old-school/lvm-create.sh
diff options
context:
space:
mode:
Diffstat (limited to 'old-school/lvm-create.sh')
-rw-r--r--old-school/lvm-create.sh393
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
5losetup() { /sbin/losetup "$@"; }
6
7lvm()
8{
9 # get rid of warnings from lvm because we are holding open these fds
10 command lvm "$@" 3>&- 4>&-
11}
12
13luks_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
22mount_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
36lv_exists()
37{
38 [ -n "$1" ] && lvm lvs "$1" >/dev/null 2>&1
39}
40
41floor4()
42{
43 # Negatives round up, but aren't used.
44 echo $(($1 / 4 * 4))
45}
46
47ceil4()
48{
49 local x="$1"
50 [ $((x % 4)) -eq 0 ] || x=$((x + 4 - x % 4))
51 printf '%d\n' "$x"
52}
53
54vgfree_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
61init_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
78mount_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
94init_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
104open_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
114init_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
122open_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
137init_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
159grow_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
171majmin()
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
179vg_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
195cryptdev_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
205cryptdev_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
215samizdat_backing_file()
216{
217 local pv
218 pv=$(vg_to_pv samizdat) && [ "$pv" ] || return
219 cryptdev_to_backing_file "$pv"
220}
221
222grow_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
238lodev_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
246grow_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
281mountpoint_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
289get_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
301mirror_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
360init_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
374start_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
387stop_meter()
388{
389 local endmsg="$*"
390 kill $meterpid
391 echo " $endmsg" >&4
392}
393