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
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
|
#!/bin/sh
# Copyright, 2012 Dustin Kirkland <[email protected]>
# Copyright, 2012 Scott Moser <[email protected]>
# Copyright, 2012 Axel Heider
#
# Based on scripts from
# Sebastian P.
# Nicholas A. Schembri State College PA USA
# Axel Heider
# Dustin Kirkland
# Scott Moser
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
case "$1" in
# no pre-reqs
prereqs) echo ""; exit 0;;
esac
. /scripts/functions
PATH=/usr/sbin:/usr/bin:/sbin:/bin
MYTAG="overlayroot"
TEMP_D="${TMPDIR:-/tmp}/${0##*/}.configs"
VARIABLES="overlayroot overlayroot_cfgdisk"
# generic settings
# ${ROOT} and ${rootmnt} are predefined by caller of this script. Note that
# the root fs ${rootmnt} it mounted readonly on the initrams, which fits
# nicely for our purposes.
root_rw=/media/root-rw
root_ro=/media/root-ro
ROOTMNT=${rootmnt} # use global name to indicate created outside this
OVERLAYROOT_DEBUG=0
# PERSIST_DIR will persist after pivot-root. It is used for log file
# and for the mktemp-made password file in crypt.
PERSIST_DIR=/run/initramfs
if [ ! -d "$PERSIST_DIR" -a -d /dev/.initramfs ]; then
PERSIST_DIR="/dev/.initramfs"
fi
if [ ! -d "$PERSIST_DIR" ]; then
mkdir -p "$PERSIST_DIR" ||
echo "WARNING: $MYTAG: failed to create ${PERSIST_DIR}" 1>&2
fi
LOG_FILE="${PERSIST_DIR}/${MYTAG}.log"
log() {
"log_${1}_msg" "$MYTAG: $2";
_debug "[$1]:" "$2"
}
log_fail() { log failure "$*"; }
log_success() { log success "$*"; }
log_warn() { log warning "$*"; }
fail() {
[ $# -eq 0 ] || log_fail "$@";
exit 0; # why do we exit success?
}
cleanup() {
[ -d "${TEMP_D}" ] && rm -Rf "${TEMP_D}"
}
debug() {
_debug "$@"
[ "${OVERLAYROOT_DEBUG:-0}" = "0" ] && return
echo "$MYTAG:" "$@"
}
_debug() {
if [ "${DEBUG_BUSTED:-0}" ]; then
{ echo "$@" >> "$LOG_FILE"; } 2>/dev/null ||
{ DEBUG_BUSTED=1; log_warn "debug is busted"; }
fi
}
safe_string() {
local prev="$1" allowed="$2" cur=""
[ -n "$prev" ] || return 1
while cur="${prev#[${allowed}]}"; do
[ -z "$cur" ] && return 0
[ "$cur" = "$prev" ] && break
prev="$cur"
done
return 1
}
parse_string() {
# parse a key/value string like:
# name=mapper,pass=foo,fstype=ext4,mkfs=1
# set variables under namespace 'ns'.
# _RET_name=mapper
# _RET_pass=foo
# _RET_fstype=ext4
# set _RET to the list of variables found
local input="${1}" delim="${2:-,}" ns="${3:-_RET_}"
local oifs="$IFS" tok="" keys="" key="" val=""
set -f; IFS="$delim"; set -- $input; IFS="$oifs"; set +f;
_RET=""
for tok in "$@"; do
key="${tok%%=*}"
val="${tok#${key}}"
val=${val#=}
safe_string "$key" "0-9a-zA-Z_" ||
{ debug "$key not a safe variable name"; return 1; }
eval "${ns}${key}"='${val}' || return 1
keys="${keys} ${ns}${key}"
done
_RET=${keys# }
return
}
get_varval() { eval _RET='${'$1'}'; }
write_kernel_cmdline_cfg() {
local cfg="$1" desc="${2:-kernel cmdline}"
local cmdline="" var=""
read cmdline < /proc/cmdline || return 1
: > "${cfg}" || return
set -f
{
echo "desc='${desc}'"
for tok in $cmdline; do
for var in $VARIABLES; do
if [ "$tok" = "$var" ]; then
log_warn "kernel param without value '${tok}'";
continue;
elif [ "${tok}" = "${var}=" ]; then
echo "${var}=''";
elif [ "${tok#${var}=}" != "${tok}" ]; then
echo "${var}='${tok#${var}=}'"
fi
done
done
} >> "$cfg"
set +f
}
wait_for_dev() {
local dev="$1" timeout="${2:-0}"
[ -b "$dev" ] && return 0
[ "$timeout" = "0" ] && return 1
# wait-for-root writes fstype to stdout, redirect to null
wait-for-root "$dev" "$timeout" >/dev/null
}
crypto_setup() {
local fstype="ext4" pass="" mapname="secure" mkfs="1" dev=""
local timeout=0
local entropy_sources="/proc/sys/kernel/random/boot_id /proc/sys/kernel/random/uuid /dev/urandom"
local seed="" rootseed="$ROOTMNT/var/lib/urandom/random-seed"
if [ ! -f "$rootseed" -a -f "$ROOTMNT/var/lib/systemd/random-seed" ]; then
rootseed="$ROOTMNT/var/lib/systemd/random-seed"
fi
# Seed the psuedo random number generator with available seeds
for seed in "/.random-seed" "$rootseed"; do
[ -f "${seed}" ] ||
{ debug "missing rng seed [${seed}]"; continue; }
cat "${seed}" > /dev/urandom ||
debug "failed seeding /dev/urandom from $seed"
done
# this does necessary crypto setup and sets _RET
# to the appropriate block device (ie /dev/mapper/secure)
# mkfs (default is 1):
# 0: never create filesystem
# 1: if pass is given and mount fails, create a new one
# if no pass given, create new
# 2: if pass is given and mount fails, fail
# if no pass given, create new
local options="$1"
parse_string "${options}" ||
{ log_fail "failed parsing '${options}'"; return 1; }
fstype=${_RET_fstype:-${fstype}}
pass=${_RET_pass:-${pass}}
mapname=${_RET_mapname:-${mapname}}
mkfs=${_RET_mkfs:-${mkfs}}
dev=${_RET_dev:-${dev}}
timeout=${_RET_timeout:-${timeout}}
[ -n "$dev" ] ||
{ log_fail "dev= argument not provided in '${options}'"; return 1; }
short2dev "$dev" ||
{ log_fail "failed to convert $dev to a device"; return 1; }
dev="${_RET}"
debug "fstype=${fstype} pass=${pass} mapname=${mapname}"
debug "mkfs=${mkfs} dev=${dev} timeout=${timeout}"
wait_for_dev "$dev" "$timeout" || {
log_fail "crypt dev device $dev does not exist after ${timeout}s";
return 1;
}
if [ -n "$pass" ]; then
printf "%s" "$pass" |
cryptsetup luksOpen "$dev" "$mapname" --key-file -
if [ $? -eq 0 ]; then
local tdev="/dev/mapper/$mapname"
log_warn "reusing existing luks device at $dev"
wait_for_dev "$tdev" 20 ||
{ log_fail "$tdev did not appear"; return 1; }
_RET_DEVICE="/dev/mapper/$mapname"
return 0
fi
if [ "$mkfs" != "1" ]; then
log_fail "luksOpen failed on $dev with mkfs=$mkfs";
return 1;
fi
log_warn "re-opening $dev failed with mkfs=$mkfs will create new"
else
[ "$mkfs" = "0" ] &&
{ log_fail "mkfs=0, but no password provided"; return 1; }
entropy_sources="$entropy_sources $dev"
local tmpf="" pass_file="${PERSIST_DIR}/${MYTAG}.passwd"
tmpf=$(mktemp "${PERSIST_DIR}/${MYTAG}.XXXXXX") ||
{ log_fail "failed creation of password file"; return 1; }
stat -L /dev/* /proc/* /sys/* >"$tmpf" 2>&1 ||
{ log_warn "could not seed with stat entropy [$entropy_sources]"; }
head -c 4096 $entropy_sources >> "$tmpf" ||
{ log_fail "failed reading entropy [$entropy_sources]"; return 1; }
pass=$(sha512sum "$tmpf") ||
{ log_fail "failed generation of password"; return 1; }
pass=${pass%% *}
printf "%s" "${pass}" > "$tmpf" ||
{ log_fail "failed to record password to '$tmpf'"; return 1; }
mv "$tmpf" "${pass_file}" ||
{ log_fail "failed rename '$tmpf' to '${pass_file}'"; return 1; }
log_debug "stored password in ${pass_file}"
fi
log_warn "setting up new luks device at $dev"
# clear backing device
wipefs -a "$dev" ||
{ log_fail "failed to wipe $dev"; return 1; }
printf "%s" "$pass" | cryptsetup luksFormat "$dev" --key-file - ||
{ log_fail "luksFormat $dev failed"; return 1; }
printf "%s" "$pass" |
cryptsetup luksOpen "$dev" "$mapname" --key-file - ||
{ log_fail "luksOpen $dev failed"; return 1; }
mke2fs -t "${fstype}" "/dev/mapper/${mapname}" || {
log_fail "failed to mkfs -t $fstype on map $mapname";
return 1;
}
_RET_DEVICE="/dev/mapper/$mapname"
return 0
}
dev_setup() {
local options="$1" dev="" timeout=0 path="/"
# options supported:
# dev=device,timeout=X,path=/
parse_string "${options}" ||
{ log_fail "failed parsing '${options}'"; return 1; }
dev=${_RET_dev:-${dev}}
timeout=${_RET_timeout:-${timeout}}
[ -n "$dev" ] ||
{ log_fail "dev= argument not provided in '${options}'"; return 1; }
short2dev "$dev" ||
{ log_fail "failed to convert $dev to a device"; return 1; }
dev="${_RET}"
debug "dev=${dev} timeout=${timeout}"
wait_for_dev "$dev" "$timeout"
_RET_DEVICE="$dev"
}
clean_path() {
# remove '//' in a path.
local p="$1" tmp=""
while [ "${p#*//}" != "$p" ]; do
tmp=${p#*//}
p="${p%%//*}/${tmp}"
done
_RET="$p"
}
get_workdir() {
local root_rw="$1" dir_prefix="$2" file="$3"
file=${file%/}
if [ "$file" = "/" -o "$file" = "" ]; then
file="_"
fi
clean_path "${root_rw}/${dir_prefix%/}-workdir/${file}"
}
gen_fstab() {
# gen_fstab(mp) - generate a fabricated /etc/fstab for mount point $mp
local mp="$1" m_spec="" m_opts="" m_fstype=""
local spec file vfstype opts pass freq
# just remove any trailing /
[ "$mp" != "/" ] && mp=${mp%/}
while read spec file vfstype opts pass freq; do
[ "$file" = "$mp" ] && {
m_spec="$spec"
m_opts="$opts"
m_fstype="$vfstype"
break
}
done </proc/mounts
[ -z "$m_spec" ] && {
log_warn "did not find root mount point $mp in /proc/mounts"
m_opts="/dev/root"
m_opts="defaults"
m_fstype="auto"
}
echo "# fabricated by overlayroot, rootfs did not contain /etc/fstab"
echo "${m_spec} / ${m_fstype} ${m_opts} 0 0 "
}
overlayrootify_fstab() {
# overlayrootify_fstab(input, root_ro, root_rw, dir_prefix, recurse, swap)
# read input fstab file, write an overlayroot version to stdout
# also returns (_RET) a list of directories that will need to be made
local input="$1" root_ro="${2:-/media/root-ro}"
local root_rw="${3:-/media/root-rw}" dir_prefix="${4:-/}"
local recurse=${5:-1} swap=${6:-0} fstype=${7:-overlayfs}
local hash="#" oline="" ospec="" upper="" dirs="" copy_opts="" copy_opt=""
local spec file vfstype opts pass freq line ro_line
local workdir="" use_orig="" relfile="" needs_workdir=false noauto=""
[ -f "$input" ] || return 1
cat <<EOF
#
# This fstab is for overlayroot. The real one can be found at
# ${root_ro}/etc/fstab
# The original entry for '/' and other mounts have been updated to be placed
# under $root_ro.
# To permanently modify this (or any other file), you should change-root into
# a writable view of the underlying filesystem using:
# sudo overlayroot-chroot
#
EOF
needs_workdir && needs_workdir=true || needs_workdir=false
while read spec file vfstype opts pass freq; do
[ "$file" = "/" ] && noauto="noauto" || noauto=""
line="$spec $file $vfstype $opts $pass $freq"
case ",$opts," in
*,ro,*) ro_opts="$opts";;
*) ro_opts="ro,${opts}";;
esac
ro_line="$spec ${root_ro}$file $vfstype"
ro_line="${ro_line} ${ro_opts}${noauto:+,${noauto}} $pass $freq"
use_orig=""
if [ "${spec#${hash}}" != "$spec" ]; then
use_orig="comment"
elif [ -z "$freq" ]; then
use_orig="malformed-line"
else
case "$vfstype" in
vfat|fat) use_orig="fs-unsupported";;
proc|sys|tmpfs|dev|udev|debugfs) use_orig="fs-virtual";;
esac
fi
if [ -n "$use_orig" ]; then
if [ "$use_orig" != "comment" ]; then
echo "$line # $MYTAG:$use_orig"
else
echo "$line"
fi
elif [ "$vfstype" = "swap" ]; then
if [ "$swap" = "0" ]; then
# comment out swap lines
echo "#$MYTAG:swap=${swap}#${line}"
elif [ "${spec#/}" != "${spec}" ] &&
[ "${spec#/dev/}" = "${spec}" ]; then
# comment out swap files (spec starts with / and not in /dev)
echo "#$MYTAG:swapfile#${line}"
else
echo "${line}"
fi
else
ospec="${root_ro}${file}"
copy_opts=""
for copy_opt in nobootwait noauto nofail; do
case ",$opts," in
*,${copy_opt},*)
copy_opts="${copy_opts},${copy_opt}";;
esac
done
clean_path "${root_rw}/${dir_prefix}${file}"
upper="$_RET"
oline="${ospec} ${file} $fstype "
clean_path "${root_ro}${file}"
oline="${oline}lowerdir=$_RET"
oline="${oline},upperdir=${upper}${copy_opts}"
if [ "$fstype" = "overlayfs" -o "$fstype" = "overlay" ] &&
${needs_workdir}; then
get_workdir "$root_rw" "$dir_prefix" "$file"
workdir="$_RET"
oline="${oline},workdir=$workdir"
dirs="${dirs} $workdir"
fi
oline="${oline} $pass $freq"
if [ "$recurse" != "0" -o "$file" = "/" ]; then
if [ "$file" = "/" ]; then
# this can confuse systemd (LP: #1723183)
echo "#$ro_line"
else
echo "$ro_line"
fi
echo "$oline"
dirs="${dirs} ${upper}"
else
echo "$line"
fi
fi
done < "$input"
_RET=${dirs# }
}
short2dev() {
# turn 'LABEL=' or 'UUID=' into a device path
# also support /dev/* and 'vdb' or 'xvda'
local input="$1" oifs="$IFS" dev newdev s
case "$input" in
LABEL=*)
dev="${input#LABEL=}"
case "${dev}" in
*/*) dev="$(echo "${dev}" | sed 's,/,\\x2f,g')";;
esac
dev="/dev/disk/by-label/${dev}"
;;
UUID=*) dev="/dev/disk/by-uuid/${input#UUID=}" ;;
/dev/*) dev="${input}";;
*) dev="/dev/${input}";;
esac
_RET=$dev
}
get_cfg() {
# get_cfg(device, file, cfg, timeout=0)
# copy the file $cfg off $device to $file, waiting $timeout for
# $device to appear
local dev="$1" file="$2" cfg="${3:-overlayroot.conf}" timeout="${4:-0}"
local cfgdev="" success=0 didmnt=false mp="" pre="get_cfg($dev):"
[ -z "$dev" ] && return 1
if [ "$timeout" != "0" ]; then
wait_for_dev "$dev" "$timeout" ||
{ debug "$pre did not appear in $timeout"; return 1; }
else
udevadm settle
fi
short2dev "$dev" && cfgdev="$_RET" && [ -b "$cfgdev" ] ||
{ debug "$pre not present"; return 1; }
if mp="${TEMP_D}/mp" && mkdir "$mp" &&
mount -o ro "$cfgdev" "$mp"; then
if [ -f "$mp/$cfg" ]; then
cp "$mp/$cfg" "$file" && success=1 ||
debug "$pre copy failed"
else
debug "$pre '$file' file not found"
fi
umount "$mp"
else
debug "$pre mount failed"
fi
[ -d "$mp" ] || rmdir "$mp"
[ $success -eq 1 ] || return 1
_RET="$dev/$cfg"
}
parse_cfg() {
# parse_cfg($file,$desc,$vars)
# this reasonably safely sources the "config" file $file
# and then declares the variables listed to stdout
#
# known issues:
# * sourced file could re-define 'echo'
# * just fails if a value has a ' in it
[ -f "$1" ] || return 0
tick="'" sh -c '
__tick=$tick
__file=$1; __desc=$2;
__unset__="__unset__"
shift 2
__vars="$*"
readonly __tick __file __desc __vars
. "${__file}"
if [ $? -ne 0 ]; then
echo "failed source \"${__file}\" ($__desc)" 1>&2
return 1
fi
set -e
echo "desc=${__tick}${__desc}${__tick}"
for var in ${__vars}; do
eval val=\${${var}-${__unset__}} ||
{ echo "eval of $var failed"; exit 1; }
[ "${val#*${__tick}}" = "${val}" ] || {
echo "variable \"$var\" had single tick in it. fail";
exit 1;
}
[ "${val}" = "${__unset__}" ] ||
echo "$var=${__tick}${val}${__tick}"
done
' -- "$@"
}
readcfgd() {
local cfg_d="$1"
# this kind of stinks, each VARIABLE goes into global scope
local or="" or_cfgdisk=""
_RET_desc_oroot=""
_RET_desc_cfgdisk=""
set +f
for f in "${cfg_d}"/*; do
. "$f" || fail "failed reading $f"
[ "$or" != "${overlayroot}" ] &&
_RET_desc_oroot="$desc"
[ "${or_cfgdisk}" != "${overlayroot_cfgdisk}" ] &&
_RET_desc_cfgdisk="$desc"
or=${overlayroot}
or_cfgdisk=${overlayroot_cfgdisk}
done
set -f
}
fix_upstart_overlayfs() {
# inotify events on an overlayfs underlay do not propogate through
# a newly created overlay. This causes job creation and deletion issues
# for upstart, which uses inotify on /etc/init. So ensure that the overlay
# explicitly has a /etc/init (LP: #1213925)
local root="$1"
local initctl="$root/sbin/initctl"
local eifile="/etc/init/.overlayfs-upstart-helper"
[ -e "$initctl" -o -L "$initctl" ] || return 0
[ -d "$root/etc/init" ] || return 0
echo "#upstart needs help for overlayfs (LP: #1213925)." \
> "$root/$eifile" ||
{ log_fail "failed to write $root/$eifile"; return 1; }
debug "created $eifile under $root (LP: #1213925)"
}
needs_workdir() {
local uname_r="${UNAME_R}"
if [ -z "$uname_r" ]; then
uname_r=$(uname -r) && UNAME_R="$uname_r" ||
{ log_warn "failed to read uname!"; return 2; }
fi
# 3.18+ require 'workdir=' option.
case "${uname_r}" in
2*|3.1[01234567]*|3.[0-9].*) return 1;;
*) return 0;;
esac
}
search_fs_driver() {
# search_fs_driver(driver1, driver2...)
# return the first supported filesystem driver in the list.
# if no drivers are provided, search default list.
# assumes that name in /proc/filesystems and module name are the same.
local m="" driver="" tab=' '
for m in "$@"; do
grep -q "${tab}$m$" /proc/filesystems && driver="$m" &&
{ debug "/proc/filesystems support for '$m'"; break; }
done
if [ -z "$driver" ]; then
for m in "$@"; do
if modprobe -qb "$m"; then
if grep -q "${tab}$m$" /proc/filesystems; then
debug "loaded module '$m'";
driver="$m"
break;
else
log_warn "loaded '$m' module but no '$m' in /proc/filesystems"
fi
fi
done
fi
[ -n "$driver" ] ||
{ debug "Unable to find driver/module. searched: $*"; return 1; }
_RET="$driver"
}
cfgd="${TEMP_D}/configs"
mkdir -p "${cfgd}" || fail "failed to create tempdir"
trap cleanup EXIT
# collect the different config locations into a file
# write individual config files in $cfgd that contain
{
echo "desc='builtin'"
echo "overlayroot=''"
echo "overlayroot_cfgdisk='disabled'"
} > "$cfgd/00-builtin"
write_kernel_cmdline_cfg "${cfgd}/90-kernel" ||
fail "failed to read kernel command line!"
parse_cfg "/conf/conf.d/overlayroot" "initramfs config" \
"$VARIABLES" > "${cfgd}/10-initramfs" ||
fail "failed parsing initramfs config"
parse_cfg "${ROOTMNT}/etc/overlayroot.conf" "$ROOT/etc/overlayroot.conf" \
"$VARIABLES" > "${cfgd}/20-root-config" ||
fail "failed parsing root config"
parse_cfg "${ROOTMNT}/etc/overlayroot.local.conf" \
"$ROOT/etc/overlayroot.local.conf" \
"$VARIABLES" > "${cfgd}/30-root-local-config" ||
fail "failed parsing root config"
# now read the trusted configs, to see if overlayroot_cfgdisk is set
readcfgd "$cfgd" ||
fail "reading configs failed"
used_desc="${_RET_desc_oroot}"
[ -n "${_RET_desc_cfgdisk}" ] &&
debug "${_RET_desc_cfgdisk} set cfgdisk='${overlayroot_cfgdisk}'"
# if one of the trusted configs above gave us set the overlayroot_cfgdisk
# variable, then look for such a device and read a config from it.
cfgdisk=0
if [ "${overlayroot_cfgdisk:-disabled}" != "disabled" ] &&
get_cfg "${overlayroot_cfgdisk}" "${TEMP_D}/cfgdisk"; then
desc=${_RET}
parse_cfg "${TEMP_D}/cfgdisk" "${desc}" "$VARIABLES" \
> "${cfgd}/80-cfgdisk" ||
fail "reading config from ${desc} failed"
used_desc="${_RET_desc_oroot}"
# now read the parsed configs again.
readcfgd "$cfgd" ||
fail "reading configs failed"
[ -n "${_RET_desc_cfgdisk}" ] &&
debug "${_RET_desc_cfgdisk} set cfgdisk=${overlayroot_cfgdisk}"
[ -n "${_RET_desc_oroot}" ] &&
debug "${_RET_desc_oroot} set overlayroot=${overlayroot}"
fi
opts=""
cfgmsg="${used_desc:+ (per ${used_desc})}"
case "${overlayroot:-disabled}" in
tmpfs|tmpfs:*)
mode="tmpfs"
opts="${overlayroot#tmpfs}";
opts=${opts#:}
;;
/dev/*|device:*)
case "$overlayroot" in
/dev/*) opts="dev=${overlayroot}";;
*) opts="${overlayroot#device:}";;
esac
dev_setup "${opts}" ||
fail "failed setup overlay for ${overlayroot} [$opts]${cfgmsg}"
mode="device"
device="$_RET_DEVICE"
;;
crypt:*)
mode="crypt"
opts=${overlayroot#crypt:}
crypto_setup "${opts}" ||
fail "failed setup crypt for ${overlayroot}${cfgmsg}"
device="$_RET_DEVICE"
;;
disabled)
debug "overlayroot disabled${cfgmsg}"
exit 0;;
*)
fail "invalid value for overlayroot: ${overlayroot}${cfgmsg}"
exit 0;;
esac
parse_string "$opts" "," _RET_common_
swap=${_RET_common_swap:-0}
recurse=${_RET_common_recurse:-1}
OVERLAYROOT_DEBUG=${_RET_common_debug:-${OVERLAYROOT_DEBUG}}
dir_prefix=${_RET_common_dir:-"/overlay"}
overlayroot_driver=${_RET_common_driver}
read cmdline </proc/cmdline
cmdline=" $cmdline "
[ "${cmdline#* ro }" != "$cmdline" ] && cmdline_ro=true || cmdline_ro=false
if [ "${overlayroot_driver:-auto}" = "auto" ]; then
search_fs_driver overlay overlayfs ||
fail "Unable to find a driver. searched: overlay overlayfs"
overlayroot_driver="$_RET"
else
search_fs_driver ${overlayroot_driver} ||
fail "Unable to find a driver named '${overlayroot_driver}'"
overlayroot_driver="$_RET"
fi
debug "swap=$swap recurse=$recurse debug=$OVERLAYROOT_DEBUG dir=$dir_prefix"
debug "device=$device mode=$mode driver=${overlayroot_driver}"
[ "$swap" = "0" -o "$swap" = "1" ] ||
fail "invalid setting for swap: $swap. must be '0' or '1'"
[ "$recurse" = "0" -o "$recurse" = "1" ] ||
fail "invalid setting for recurse: $recurse. must be '0' or '1'"
log_warn "configuring overlayroot with driver=${overlayroot_driver} mode=$mode opts='$opts' per $used_desc"
# settings based on overlayroot_driver
workdir=""
case "${overlayroot_driver}" in
overlayfs|overlay)
mount_type="${overlayroot_driver}"
mount_opts="-o lowerdir=${root_ro},upperdir=${root_rw}/${dir_prefix}"
if needs_workdir; then
get_workdir "$root_rw" "$dir_prefix" "/"
workdir="$_RET"
mount_opts="${mount_opts},workdir=$workdir"
fi
clean_path "${mount_opts} overlayroot ${ROOTMNT}"
mount_opts="$_RET"
;;
aufs)
mount_type="aufs"
mount_opts="-o dirs=${root_rw}/${dir_prefix}:${root_ro}=ro aufs-root ${ROOTMNT}"
;;
*)
log_fail "invalid overlayroot driver: ${overlayroot_driver}"
panic "$MYTAG"
;;
esac
# make the mount point on the init root fs ${root_rw}
mkdir -p "${root_rw}" ||
fail "failed to create ${root_rw}"
# make the mount point on the init root fs ${root_ro}
mkdir -p "${root_ro}" ||
fail "failed to create ${root_ro}"
# mount the backing device to $root_rw
if [ "$mode" = "tmpfs" ]; then
# mount a tmpfs using the device name tmpfs-root
mount -t tmpfs tmpfs-root "${root_rw}" ||
fail "failed to create tmpfs"
else
# dev or crypto
mount "$device" "${root_rw}" ||
fail "failed mount backing device $device"
fi
mkdir -p "${root_rw}/${dir_prefix}" ||
fail "failed to create ${dir_prefix} on ${device}"
[ -z "$workdir" ] || mkdir -p "$workdir" ||
fail "failed to create workdir '$workdir' on ${device}"
# root is mounted on ${ROOTMNT}, move it to ${ROOT_RO}.
mount --move "${ROOTMNT}" "${root_ro}" ||
fail "failed to move root away from ${ROOTMNT} to ${root_ro}"
# there is nothing left at ${ROOTMNT} now. So for any error we get we should
# either do recovery to restore ${ROOTMNT} for drop to a initramfs shell using
# "panic". Otherwise the boot process is very likely to fail with even more
# errors and leave the system in a wired state.
# mount virtual fs ${ROOTMNT} with rw-fs ${root_rw} on top or ro-fs ${root_ro}.
debug mount -t "$mount_type" $mount_opts
mount -t "$mount_type" $mount_opts
if [ $? -ne 0 ]; then
log_fail "failed to create new ro/rw layered ${ROOTMNT}"
log_fail "mount command was: mount -t $mount_type $mount_opts"
# do recovery and try restoring the mount for ${ROOTMNT}
mount --move ${root_ro} ${ROOTMNT}
if [ $? -ne 0 ]; then
# thats bad, drop to shell to let the user try fixing this
log_fail "RECOVERY_ERROR: failed to move $root_ro back to ${ROOTMNT}"
panic "$MYTAG"
fi
exit 0
fi
# now the real root fs is on ${root_ro} of the init file system, our
# layered root fs is set up at ${ROOTMNT}. So we can write anywhere in
# {ROOTMNT} and the changes will end up in ${root_rw} while ${root_ro} it
# not touched. However ${root_ro} and ${root_rw} are on the initramfs root
# fs, which will be removed an replaced by ${ROOTMNT}. Thus we must move
# ${root_ro} and ${root_rw} to the rootfs visible later, ie.
# ${ROOTMNT}${root_ro} and ${ROOTMNT}${root_ro}. Since the layered ro/rw
# is already up, these changes also end up on ${root_rw} while ${root_ro}
# is not touched.
# move mount from ${root_ro} to ${ROOTMNT}${root_ro}
mkdir -p "${ROOTMNT}/${root_ro}"
mount --move ${root_ro} "${ROOTMNT}${root_ro}" ||
fail "failed to move ${root_ro} to ${ROOTMNT}${root_ro}"
# move mount from ${root_rw} to ${ROOTMNT}${root_rw}
[ -d ${ROOTMNT}${root_rw} ] || mkdir -p ${ROOTMNT}${root_rw}
mount --move "${root_rw}" "${ROOTMNT}${root_rw}" ||
fail "failed to move ${root_rw} to ${ROOTMNT}${root_rw}"
# technically, everything is set up nicely now. Since ${ROOTMNT} had beend
# mounted read-only on the initfamfs already, ${ROOTMNT}${root_ro} is it,
# too. Now we init process could run - but unfortunately, we may have to
# prepare some more things here.
# Basically, there are two ways to deal with the read-only root fs. If the
# system is made aware of this, things can be simplified a lot. If it is
# not, things need to be done to our best knowledge.
#
# So we assume here, the system does not really know about our read-only
# root fs.
#
# Let's deal with /etc/fstab first. It usually contains an entry for the
# root fs, which is no longer valid now. We have to remove it and add our
# new ${root_ro} entry.
# Remember we are still on the initramfs root fs here, so we have to work
# on ${ROOTMNT}/etc/fstab. The original fstab is
# ${ROOTMNT}${root_ro}/etc/fstab.
source_fstab="$ROOTMNT/${root_ro}/etc/fstab"
if [ ! -f "$source_fstab" ] && [ "$cmdline_ro" = "true" ]; then
debug "rootfs did not contain /etc/fstab, creating" \
"based on kernel cmdline"
source_fstab="${TEMP_D}/fstab"
gen_fstab "${ROOTMNT}${root_ro}" > "$source_fstab" ||
log_fail "gen_fstab failed for $ROOTMNT/$root_ro"
fi
if [ -f "$source_fstab" ]; then
overlayrootify_fstab "$source_fstab" "$root_ro" \
"$root_rw" "$dir_prefix" "$recurse" "$swap" "${overlayroot_driver}" \
> "${ROOTMNT}/etc/fstab" ||
log_fail "failed to modify /etc/fstab"
else
debug "rootfs did not contain /etc/fstab."
fi
# we have to make the directories in ${root_rw} because if they do not
# exist, then the 'upper=' argument to overlayfs will fail.
for d in ${_RET}; do
mkdir -p "${ROOTMNT}/$d"
done
if [ "${overlayroot_driver}" = "overlayfs" ]; then
fix_upstart_overlayfs "$ROOTMNT" ||
log_fail "failed to fix upstart for overlayfs"
fi
# if / is supposed to be mounted read-only (cmdline with 'ro')
# then mount our overlayfs as read-only just to be more normal
if [ "${cmdline_ro}" = "true" ]; then
mount -o remount,ro "$ROOTMNT" ||
log_fail "failed to remount overlayroot read-only"
debug "mounted $ROOTMNT read-only per kernel cmdline"
fi
msg="configured root with '$overlayroot' using ${overlayroot_driver} per"
msg="$msg ${used_desc}"
log_success "$msg"
exit 0
# vi: ts=4 noexpandtab
|