blob: 8ec302d385193737c699a5faf281a780637c1843 (
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
|
LoSetup()
{
local losetup_binary="$(which LoSetup)"
if [ "$losetup_binary" ]; then
"$losetup_binary" "$@"
else
losetup "$@"
fi
}
dm_snapshot()
{
# TODO: eliminate duplication; this function exists elsewhere in a less generalized form
local ro_file rw_file cutoff_size
ro_file=$1
rw_file=$2
cutoff_size=$3
local ro_dev rw_dev size new_dev_name persist chunksize
if [ -b "$ro_file" ];
then ro_dev=$ro_file
else ro_dev=$(LoSetup -r -f --show "$ro_file") || return
fi
if [ -b "$rw_file" ];
then rw_dev=$rw_file
else rw_dev=$(LoSetup -f --show "$rw_file") || return
fi
if [ "$cutoff_size" ] && [ "$cutoff_size" -gt 0 ]; then
size=$cutoff_size
else
size=$(blockdev --getsz "$ro_dev") || return
fi
new_dev_name=${ro_dev##*/}
persist=p
chunksize=16
dmsetup create "$new_dev_name" --table "0 $size snapshot $ro_dev $rw_dev $persist $chunksize" || return
wait_for_dm_device /dev/mapper/"$new_dev_name"
echo /dev/mapper/"$new_dev_name"
}
dm_snapshot_teardown()
{
local dev="$1"
case "$dev" in
/dev/dm-*)
dmsetup table "$dev" | (
read _ _ snapshot ro_dev rw_dev _ crypt_dev _
case "$snapshot" in
snapshot)
dmsetup remove "$dev" || exit 1
# errors ignored because the loop dev can be configured to be
# automatically removed upon disuse
losetup -d /dev/block/"$rw_dev" || true
eject /dev/block/"$ro_dev" || true
;;
crypt)
cryptsetup remove "$dev" || exit 1
losetup -d /dev/block/"$crypt_dev" || true
;;
esac
) || return
;;
*) return 1 ;;
esac
}
wait_for_dm_device()
{
# TODO: improve
while ! [ -e "$1" ]; do
sleep 1
done
}
dup_mount_cdrom()
{
local cdrom_dev="$1" mountpoint="$2"
local sectors md_dev=/dev/md55 cdrom_rw_file=/"${cdrom_dev##*/}".rw
sectors=$(get_cdrom_sizelimit "$cdrom_dev") || return
# TODO: do we even need this backing file? We do need to trick mdadm into
# thinking that this is a RW device, but previously we got away with just
# creating a loopback device.
dd if=/dev/zero of="$cdrom_rw_file" bs=1K count=32 || return
cdrom_rw_dev=$(dm_snapshot "$cdrom_dev" "$cdrom_rw_file" "$sectors") || return
mdadm_dup "$cdrom_rw_dev" "$md_dev" "$sectors" || return
mount -t iso9660 -r $md_dev "$mountpoint"
}
get_cdrom_sizelimit()
{
# returns 512-byte sectors
local dev="$1" sectors
sectors=$(blockdev --getsz "$dev") || return
# Check if we can read the last 8 sectors. With a TAO CDROM, we can't --
# these sectors are faux, and not part of the ISO fs. If mdadm is allowed to
# read them, it will mark the device failed.
if dd count=2 if="$dev" bs=2048 skip=$((sectors/4 - 2)) of=/dev/null 2>/dev/null; then
echo $sectors
else
echo $((sectors - 8))
fi
}
mdadm_dup()
{
local input_dev="$1" md_name="$2" sectors="$3"
mdadm --build "$md_name" "${sectors:+--size=$((sectors / 2))}" \
--level=1 --raid-devices=1 --force --write-mostly "$input_dev" || return
}
mdadm_subdevices()
{
local md_dev="$1"
mdadm -D "$md_dev" -Y | sed -ne 's/^MD_DEVICE_.*_DEV=//p'
}
cryptsetup_temp()
{
local sectors="$1" cryptname="$2" temp_file="$3" parms=$- secret
set +x
# Add 4096 sectors for LUKS header
truncate -s $(((sectors + 4096) * 512)) "$temp_file" || return
cleartext_dev=$(LoSetup -f --show "$temp_file") || return
secret="$(head -c256 /dev/urandom)" || return
printf %s "$secret" |
cryptsetup luksFormat "$cleartext_dev" - || return
printf %s "$secret" |
cryptsetup --key-file - luksOpen "$cleartext_dev" "$cryptname" || return
unset secret
set "$parms"
wait_for_dm_device /dev/mapper/"$cryptname"
rm "$temp_file"
echo /dev/mapper/"$cryptname"
}
mdadm_copy_eject_crypt()
{
local md_dev="$1" temp_file="$2"
[ -b "$md_dev" ] || return
local output_dev sectors
old_subdev=$(mdadm_subdevices "$md_dev"|head -n1) || return
[ -b "$old_subdev" ] || return
# TODO: truncate to the ISO fs size if the device is larger
sectors=$(blockdev --getsz "$md_dev") || return
output_dev=$(cryptsetup_temp "$sectors" samizdatiso "$temp_file") || return
mdadm "$md_dev" --add "$output_dev" || return
mdadm "$md_dev" --grow -n2 || return
mdadm_wait_remove "$md_dev" "$old_subdev" || return
mdadm "$md_dev" --grow -n1 --force || return
dm_snapshot_teardown "$old_subdev"
}
mdadm_copy_eject()
{
local md_dev="$1" output_file="$2"
[ -b "$md_dev" ] || return
[ ! -e "$output_file" ] || return
local output_dev sectors
old_subdev=$(mdadm_subdevices "$md_dev"|head -n1) || return
[ -b "$old_subdev" ] || return
sectors=$(blockdev --getsz "$md_dev") || return
truncate -s $((sectors * 512)) "$output_file" || return
output_dev=$(LoSetup -f --show "$output_file") || return
mdadm "$md_dev" --add "$output_dev" || return
mdadm "$md_dev" --grow -n2 || return
mdadm_wait_remove "$md_dev" "$old_subdev" || return
mdadm "$md_dev" --grow -n1 --force || return
dm_snapshot_teardown "$old_subdev"
}
mdadm_wait_remove()
{
# We should perhaps use mdadm --monitor's RebuildFinished event.
local dev="$1" disk="$2" tries
if ! mdadm --wait "$dev"; then
tries=1000
while ! mdadm --detail --test "$dev"; do
[ $tries -gt 0 ] || return 1
sleep 1
tries=$((tries-1))
done
fi
mdadm "$dev" --fail "$disk" || return 1
tries=100
while ! mdadm "$dev" --remove "$disk"; do
[ $tries -gt 0 ] || return 1
sleep 1
tries=$((tries-1))
done
return 0
}
|