/
ubuntu-core-rootfs
323 lines (273 loc) · 10.7 KB
/
ubuntu-core-rootfs
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# -*- shell-script -*- vim:ft=sh:
#---------------------------------------------------------------------
# Description: Mount appropriate Ubuntu Core root filesystem read-only
# and writable partition writable.
# Entry point: mountroot().
#---------------------------------------------------------------------
. /scripts/ubuntu-core-functions
sync_dirs()
{
base="$1"
source="$2"
target="$3"
OLD_PWD="$PWD"
cd "$base"
for file in "$source"/*
do
# Skip empty directories
[ ! -e "$base/$file" -a ! -L "$base/$file" ] && continue
# If the target already exists as a file or link, there's nothing we can do
[ -e "$target/$file" -o -L "$target/$file" ] && [ ! -d "$target/$file" ] && continue
# If the target doesn't exist, just copy it over
if [ ! -e "$target/$file" -a ! -L "$target/$file" ]; then
cp -Ra "$base/$file" "$target/$file"
continue
fi
# That leaves us with directories and a recursive call
[ -d "$file" ] && sync_dirs "$base" "$file" "$target"
done
cd "$OLD_PWD"
}
# Process the list of bind-mounts (but don't mount them - systemd will handle that)
# File format is documented in writable-paths(5).
handle_writable_paths()
{
writable_paths="$1"
fstab="$2"
[ -n "$writable_paths" ] || panic "need writeable paths"
[ -e "$writable_paths" ] || panic "writeable paths does not exist"
[ -n "$fstab" ] || panic "need fstab"
cat "$writable_paths" | while read line; do
# tokenise
set -- $line
# skip invalid/commented entries
([ -z "$1" ] || \
[ -z "$2" ] || \
[ -z "$3" ] || \
[ -z "$4" ] || \
[ -z "$5" ]) && continue
# ignore anything that isn't an absolute path (including comments)
case "$1" in
/*) ;;
*) continue ;;
esac
# skip invalid mount points
dstpath="${rootmnt}$1"
[ ! -e "$dstpath" ] && continue
if [ "$3" = "temporary" ]; then
# Temporary entries are simple, just mount a tmpfs
echo "tmpfs $1 tmpfs $5 0 0" >> "$fstab"
elif [ "$3" = "persistent" ] || \
[ "$3" = "synced" ]; then
# Figure out the source path
if [ "$2" = "auto" ]; then
srcpath="${rootmnt}/writable/system-data${1}"
path="/writable/system-data${1}"
else
srcpath="${rootmnt}/writable/$2"
path="/writable/$2"
fi
if [ ! -e "$srcpath" ]; then
# Process new persistent or synced paths
dstown=$(stat -c "%u:%g" "$dstpath")
dstmode=$(stat -c "%a" "$dstpath")
mkdir -p ${srcpath%/*}
if [ ! -d "$dstpath" ]; then
# Deal with redirected files
if [ "$4" = "transition" ]; then
cp -a "$dstpath" "$srcpath"
else
touch "$srcpath"
chown "$dstown" "$srcpath"
chmod "$dstmode" "$srcpath"
fi
else
# Deal with redirected directories
if [ "$4" = "transition" ] || [ "$3" = "synced" ]; then
cp -aR "$dstpath" "$srcpath"
else
mkdir "$srcpath"
chown "$dstown" "$srcpath"
chmod "$dstmode" "$srcpath"
fi
fi
elif [ "$3" = "synced" ]; then
# Process existing synced paths
sync_dirs "$dstpath" . "$srcpath"
fi
# mount all /etc dirs right now, not later when fstab is
# processed, as it will cause races.
case $1 in
/etc*)
[ -e "${rootmnt}/writable/system-data/$1" ] || mkdir -p "${rootmnt}/writable/system-data/$1"
mount -o bind "${rootmnt}/writable/system-data/$1" "${rootmnt}/$1"
;;
*)
# Write the fstab entry
if [ "$5" = "none" ]; then
echo "$path $1 none bind 0 0" >> "$fstab"
else
echo "$path $1 none bind,$5 0 0" >> "$fstab"
fi
;;
esac
else
continue
fi
done
}
fsck_writable()
{
local writable_label="$1"
local writable_mnt="$2"
path=$(get_partition_from_label "$writable_label")
[ -n "$path" ] || panic "cannot find '$writable_label' partition"
# Mount the writable partition to a temporary mount point
# (to allow it to be move-mounted on top of the read-only root).
logfile="/run/initramfs/fsck-${writable_label}"
echo "initrd: checking filesystem for ${writable_label} partition" >/dev/kmsg || true
echo "$(date '+%s'): start" >> "$logfile" || true
# XXX: The following commands must not fail (to ensure the system boots!)
# Mount and umount first to let the kernel handle
# the journal and orphaned inodes (much faster than e2fsck).
mount -o errors=remount-ro "$path" "$writable_mnt" || true
umount "$writable_mnt" || true
# Automatically fix errors
/sbin/e2fsck -va "$path" >> "$logfile" 2>&1 || true
echo "$(date '+%s'): end" >> "$logfile" || true
}
# Mount core and kernel snaps
mount_snaps()
{
# mount OS snap
mount -o ro "${writable_mnt}/system-data/var/lib/snapd/snaps/${snap_core}" "$rootmnt"
# now add a kernel bind mounts to it
local kernel_mnt="/tmpmnt_kernel"
mkdir -p "$kernel_mnt"
mount -o ro "${writable_mnt}/system-data/var/lib/snapd/snaps/${snap_kernel}" "$kernel_mnt"
for d in modules firmware; do
if [ -d "${kernel_mnt}/$d" ]; then
mount -o bind "${kernel_mnt}/$d" "$rootmnt/lib/$d"
fi
done
# now we can umount the kernel and the bind mounts are still intact
# (magic!)
umount "$kernel_mnt"
}
#---------------------------------------------------------------------
# XXX: Entry point - called by the initramfs "/init" script.
#---------------------------------------------------------------------
mountroot()
{
pre_mountroot
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-premount"
run_scripts /scripts/local-premount
[ "$quiet" != "y" ] && log_end_msg
# find what snappy-os version to use
for x in $(cat /proc/cmdline); do
case "${x}" in
# new kernel/os snap vars
snap_core=*)
snap_core="${x#*=}"
;;
snap_kernel=*)
snap_kernel="${x#*=}"
;;
esac
done
# basic validation
if [ -z "$snap_core" ]; then
panic "the required kernel commandline snap_core is not set"
fi
if [ -z "$snap_kernel" ]; then
panic "the required kernel commandline snap_kernel is not set"
fi
# always ensure writable is in a good state
writable_label="writable"
writable_mnt="/tmpmnt_${writable_label}"
mkdir -p "$writable_mnt"
fsck_writable "$writable_label" "$writable_mnt"
# mount the root fs
do_root_mounting
# mount core and kernel snaps
mount_snaps
# mount /run
echo "initrd: mounting /run" >/dev/kmsg || true
mount -o rw,nosuid,noexec,relatime,mode=755 -t tmpfs tmpfs "${rootmnt}/run"
# move /writable to its final destination
mount --move "$writable_mnt" "${rootmnt}/writable"
# Prepare the fstab
fstab="${rootmnt}/etc/fstab"
writable_paths="${rootmnt}/etc/system-image/writable-paths"
# Add writable overlays
if [ -e "$writable_paths" ]; then
touch "${rootmnt}/run/image.fstab"
mount -o bind "${rootmnt}/run/image.fstab" "$fstab" || panic "Cannot bind mount fstab"
echo "# Auto-generated by $0" >> "$fstab"
echo "# DO NOT EDIT THIS FILE BY HAND - YOUR CHANGES WILL BE OVERWRITTEN" >> "$fstab"
echo "# (See writable-paths(5) for details)" >> "$fstab"
echo "/dev/root / rootfs defaults,ro 0 0" >> "$fstab"
# FIXME: ideally we would mount /writable RO here and
# let systemd do a "remount,rw" for us. unfortunately
# this is not supported by systemd so we need to do
# the RW mount and fsck dance etc here :/
echo "LABEL=writable /writable auto defaults 0 0" >> "$fstab"
handle_writable_paths "$writable_paths" "$fstab"
fi
# IMPORTANT: ensure we synced everything back to disk
sync
# Request bootloader partition be mounted
boot_partition=$(findfs LABEL="system-boot" 2>/dev/null || :)
if [ -n "$boot_partition" ]; then
# determine bootloader type, we need to inspect the boot
# partition for this
grubdir="/boot/grub"
ubootdir="/boot/uboot"
tmpboot_mnt="/tmpmnt_system-boot"
mkdir -p $tmpboot_mnt
mount -o ro "$boot_partition" "$tmpboot_mnt"
# Since the boot partition is not required to actually boot
# the image, request the init system handle it (fsck+mount).
if [ -f "${tmpboot_mnt}/EFI/ubuntu/grub/grub.cfg" ]; then
# OLD style (ubuntu-device-flash) base dir
efidir="/boot/efi"
efibinddir="${efidir}/EFI/ubuntu/grub"
if [ -d "${rootmnt}/${efidir}" ]; then
echo "$boot_partition $efidir auto defaults 0 2" >> "$fstab"
echo "$efibinddir $grubdir none bind 0 0" >> "$fstab"
fi
elif [ -f "${tmpboot_mnt}/EFI/ubuntu/grub.cfg" ]; then
# NEW style (u-i base dir)
efidir="/boot/efi"
efibinddir="${efidir}/EFI/ubuntu/"
if [ -d "${rootmnt}/${efidir}" ]; then
echo "$boot_partition $efidir auto defaults 0 2" >> "$fstab"
echo "$efibinddir $grubdir none bind 0 0" >> "$fstab"
fi
else
echo "$boot_partition $ubootdir auto defaults 0 2" >> "$fstab"
fi
# let systemd do the actual mounting
umount "$tmpboot_mnt"
elif abootimg -i "$(findfs PARTLABEL=recovery)" >/dev/null 2>&1; then
echo "android style bootloader detected" >/dev/kmsg || true
androiddir="writable/androidboot"
mkdir -p "${rootmnt}/${androiddir}"
if [ ! -e "${rootmnt}/${androiddir}/androidboot.env" ]; then
echo -e "snap_mode=\nsnap_core=$snap_core\nsnap_kernel=$snap_kernel\nsnap_try_core=\nsnap_try_kernel=" > \
"${rootmnt}/${androiddir}/androidboot.env"
fi
mount -o bind "${rootmnt}/${androiddir}" "${rootmnt}/boot/androidboot"
fi
# Mount the systemd overlay so that we have a complete root partition during boot
mkdir -p "${rootmnt}/writable/system-data/etc/systemd/system"
mount -o bind "${rootmnt}/writable/system-data/etc/systemd/system" "${rootmnt}/etc/systemd/system"
# create "machine-id" if it does not exist and bind mount it (LP: #1619721)
if [ ! -e ${rootmnt}/writable/system-data/etc/machine-id ]; then
cat /proc/sys/kernel/random/uuid|tr -d - >${rootmnt}/writable/system-data/etc/machine-id
fi
mount -o bind "${rootmnt}/writable/system-data/etc/machine-id" "${rootmnt}/etc/machine-id"
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-bottom"
run_scripts /scripts/local-bottom
[ "$quiet" != "y" ] && log_end_msg
}