⇤ ← Revision 1 as of 2012-12-27 23:05:36
17965
Comment: Initial description
|
17287
Remove /etc mounting
|
Deletions are marked like this. | Additions are marked like this. |
Line 28: | Line 28: |
=== Mounting /etc in the initramfs === One use case identified in previous discussion was the need to support encrypted filesystems at boot. It's common to keep /usr separate and just encrypt the rootfs for performance reasons. However, the real need is for /etc to be encrypted. With a separate /usr mount no longer making sense, it would therefore be useful to be able to mount /etc encrypted directly in the initramfs, leaving the rootfs and /usr unencrypted. |
|
Line 47: | Line 43: |
== Additional changes == 1. Update initramfs to mount /etc 1. Update util-linux to skip /etc with the -R option 1. Update initscripts to fsck /etc in checkroot and remount r/o when shutting down |
Mount /usr in the initramfs
Introduction
Traditionally, /usr has been mounted in mountall, as for any filesystem in /etc/fstab other than the root filesystem (or special filesystems like /proc and /dev). This has several implications:
- Programs, libraries and data under /usr are not available in the early stages of booting
- Programs in /bin and /sbin can not assume /usr is available
- init scripts need to cope with /usr being unavailable in early boot
- This causes particular problems with shared libraries, PAM modules, NSS module dependencies, etc. as well as locale availability.
If /usr could be guaranteed to be available by mounting it in the initramfs, these problems can also be overcome. This permits all shared libraries, PAM modules, NSS modules etc. to function at once. It also allows locales to be used at once as well. Additionally, it is possible to use C++ (libstdc++ is under /usr), and interpreters such as perl and python, etc. In short, it makes a large number of things possible which were previously not possible, and this will both allow greater flexibility in what can run in the early stages of booting, as well as removing a large number of special cases and hairy logic which existed solely to cope with /usr not being available.
Initial implementation
The change to the initramfs is fairly simple. Once the root filesystem is mounted, the /etc/fstab on the root filesystem can be read, and then if it contains an entry for /usr, we mount it.
The -R option of /sbin/fsck, used to check all filesystems except the root filesystem needs updating so that it will also ignore /usr, or else at boot the checkfs script will try to fsck a mounted /usr.
The initscripts themselves need updating to treat /usr like the root filesystem
- Remount r/o
- Check
- Remount r/w
- Remount r/o at shutdown and don't umount
Caveats
The main caveat is that if you use a separately mounted /usr, you must use an initramfs to mount it. While not using an initramfs will continue to be supported, this will only work if you have /usr on the root filesystem (i.e. it's a single mount). This is because mounting /usr in the traditional way will cause startup to fail since it's mounted too late (this will only happen once init scripts start relying on /usr to be available immediately).
|
initramfs |
No initramfs |
root only |
Supported |
Supported |
root and /usr |
Supported |
Broken |
The initramfs will support / and /usr mounts on local/local and nfs/nfs. However, it it not yet clear if this will extend to local/nfs and/or nfs/local.
Required changes
- Update initramfs to mount /usr
- Update util-linux to skip /usr with the -R option
- Update initscripts to fsck /usr in checkroot and remount r/o when shutting down
Patches
initramfs-tools
1 commit afe1bf2fcbb6e8ef56b99a224b44533811cc6c5a
2 Author: Roger Leigh <rleigh@debian.org>
3 Date: Tue Dec 25 13:25:48 2012 +0000
4
5 Mount /usr in the initramfs
6
7 diff --git a/init b/init
8 index cb832ff..7a0fc0f 100755
9 --- a/init
10 +++ b/init
11 @@ -217,12 +217,18 @@ run_scripts /scripts/init-premount
12
13 maybe_break mount
14 log_begin_msg "Mounting root file system"
15 -. /scripts/${BOOT}
16 +. /scripts/local
17 +. /scripts/nfs
18 parse_numeric ${ROOT}
19 maybe_break mountroot
20 mountroot
21 log_end_msg
22
23 +if read_fstab_entry /usr; then
24 + log_begin_msg "Mounting /usr file system"
25 + domount unknown /usr
26 +fi
27 +
28 maybe_break bottom
29 [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-bottom"
30 run_scripts /scripts/init-bottom
31 diff --git a/scripts/functions b/scripts/functions
32 index 6e74ade..79506e7 100644
33 --- a/scripts/functions
34 +++ b/scripts/functions
35 @@ -423,3 +423,55 @@ wait_for_udev()
36 command -v udevadm >/dev/null 2>&1 || return 0
37 udevadm settle ${1:+--timeout=$1}
38 }
39 +
40 +# Find a specific fstab entry
41 +# $1=mountpoint
42 +# $2=fstype (optional)
43 +# returns 0 on success, 1 on failure (not found or no fstab)
44 +read_fstab_entry () {
45 + # Not found by default.
46 + found=1
47 +
48 + for file in ${rootmnt}/etc/fstab; do
49 + if [ -f "$file" ]; then
50 + while read MNT_FSNAME MNT_DIR MNT_TYPE MNT_OPTS MNT_FREQ MNT_PASS MNT_JUNK; do
51 + case "$MNT_FSNAME" in
52 + ""|\#*)
53 + continue;
54 + ;;
55 + esac
56 + if [ "$MNT_DIR" = "$1" ]; then
57 + if [ -n "$2" ]; then
58 + [ "$MNT_TYPE" = "$2" ] || continue;
59 + fi
60 + found=0
61 + break 2
62 + fi
63 + done < "$file"
64 + fi
65 + done
66 +
67 + return $found
68 +}
69 +
70 +# Mount a file system. We parse the information from the fstab.
71 +# $1=type local|nfs|unknown $2=mountpoint mount location
72 +domount()
73 +{
74 + read_fstab_entry "$2"
75 + mount "-t${MNT_TYPE}" "-o${MNT_OPTS}" "$MNT_FSNAME" "${rootmnt}${MNT_DIR}"
76 +}
77 +
78 +# Mount the root file system. This is delegated to the
79 +# mount(local|nfs)root functions.
80 +# $1=type local|nfs|unknown
81 +mountroot()
82 +{
83 + case $boot in
84 + nfs) : ;;
85 + *) boot=local ;;
86 + esac
87 +
88 + echo "Mounting ${boot} root filesystem"
89 + mount${boot}root
90 +}
91 \ No newline at end of file
92 diff --git a/scripts/local b/scripts/local
93 index 521e69a..f6f023f 100644
94 --- a/scripts/local
95 +++ b/scripts/local
96 @@ -74,7 +74,7 @@ pre_mountroot()
97 done
98 }
99
100 -mountroot()
101 +mountlocalroot()
102 {
103 pre_mountroot
104
105 diff --git a/scripts/nfs b/scripts/nfs
106 index 6fa0c43..a0764a3 100644
107 --- a/scripts/nfs
108 +++ b/scripts/nfs
109 @@ -49,7 +49,7 @@ do_nfsmount()
110 }
111
112 # NFS root mounting
113 -mountroot()
114 +mountnfsroot()
115 {
116 [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/nfs-top"
117 run_scripts /scripts/nfs-top
Mount /etc (work in progress):
1 diff --git a/init b/init
2 index 7a0fc0f..d3ee2b9 100755
3 --- a/init
4 +++ b/init
5 @@ -40,10 +40,18 @@ export ROOT=
6 export ROOTDELAY=
7 export ROOTFLAGS=
8 export ROOTFSTYPE=
9 +export ETC=
10 +export ETCDELAY=
11 +export ETCFLAGS=
12 +export ETCFSTYPE=
13 export IP=
14 export BOOT=
15 export BOOTIF=
16 export UBIMTD=
17 +export localtopused=no
18 +export localpremountused=no
19 +export nfstopused=no
20 +export nfspremountused=no
21 export break=
22 export init=/sbin/init
23 export quiet=n
24 @@ -119,6 +127,57 @@ for x in $(cat /proc/cmdline); do
25 ;;
26 esac
27 ;;
28 + etc=*)
29 + ETC=${x#etc=}
30 + case $ETC in
31 + LABEL=*)
32 + ETC="${ETC#LABEL=}"
33 +
34 + # support any / in LABEL= path (escape to \x2f)
35 + case "${ETC}" in
36 + */*)
37 + if command -v sed >/dev/null 2>&1; then
38 + ETC="$(echo ${ETC} | sed 's,/,\\x2f,g')"
39 + else
40 + if [ "${ETC}" != "${ETC#/}" ]; then
41 + ETC="\x2f${ETC#/}"
42 + fi
43 + if [ "${ETC}" != "${ETC%/}" ]; then
44 + ETC="${ETC%/}\x2f"
45 + fi
46 + IFS='/'
47 + newetc=
48 + for s in $ETC; do
49 + newetc="${newetc:+${newetc}\\x2f}${s}"
50 + done
51 + unset IFS
52 + ETC="${newetc}"
53 + fi
54 + esac
55 + ETC="/dev/disk/by-label/${ETC}"
56 + ;;
57 + UUID=*)
58 + ETC="/dev/disk/by-uuid/${ETC#UUID=}"
59 + ;;
60 + /dev/nfs)
61 + [ -z "${BOOT}" ] && BOOT=nfs
62 + ;;
63 + esac
64 + ;;
65 + etcflags=*)
66 + ETCFLAGS="-o ${x#etcflags=}"
67 + ;;
68 + etcfstype=*)
69 + ETCFSTYPE="${x#etcfstype=}"
70 + ;;
71 + etcdelay=*)
72 + ETCDELAY="${x#etcdelay=}"
73 + case ${ETCDELAY} in
74 + *[![:digit:].]*)
75 + ETCDELAY=
76 + ;;
77 + esac
78 + ;;
79 nfsroot=*)
80 NFSROOT="${x#nfsroot=}"
81 ;;
82 @@ -224,9 +283,23 @@ maybe_break mountroot
83 mountroot
84 log_end_msg
85
86 +if [ -n "${ETC}" ]; then
87 + log_begin_msg "Mounting /usr file system"
88 + mountetc
89 + log_end_msg
90 +fi
91 +
92 if read_fstab_entry /usr; then
93 - log_begin_msg "Mounting /usr file system"
94 - domount unknown /usr
95 + log_begin_msg "Mounting /usr file system"
96 + domount unknown /usr
97 + log_end_msg
98 +fi
99 +
100 +if [ "${localtopused}" = "yes" ] || [ "${localpremountused}" = "yes" ]; then
101 + mountlocalend
102 +fi
103 +if [ "${nfstopused}" = "yes" ] || [ "${nfspremountused}" = "yes" ]; then
104 + mountnfsend
105 fi
106
107 maybe_break bottom
108 @@ -308,10 +381,18 @@ unset ROOTFLAGS
109 unset ROOTFSTYPE
110 unset ROOTDELAY
111 unset ROOT
112 +unset ETCFLAGS
113 +unset ETCFSTYPE
114 +unset ETCDELAY
115 +unset ETC
116 unset IP
117 unset BOOT
118 unset BOOTIF
119 unset UBIMTD
120 +unset localtopused
121 +unset localpremountused
122 +unset nfstopused
123 +unset nfspremountused
124 unset blacklist
125 unset break
126 unset noresume
127 diff --git a/initramfs-tools.8 b/initramfs-tools.8
128 index bc6a030..5728146 100644
129 --- a/initramfs-tools.8
130 +++ b/initramfs-tools.8
131 @@ -42,6 +42,24 @@ The default is 180 seconds.
132 set the file system mount option string.
133
134 .TP
135 +\fB\fI etc= "<path to blockdevice>"
136 +the device node to mount as the etc file system.
137 +The recommended usage is to specify the UUID as followed "etc=UUID=xxx".
138 +
139 +.TP
140 +\fB\fI etcfstype
141 +set the etc file system type.
142 +
143 +.TP
144 +\fB\fI etcdelay
145 +set delay in seconds. Determines how long mountetc waits for etc to appear.
146 +The default is 180 seconds.
147 +
148 +.TP
149 +\fB\fI etcflags
150 +set the file system mount option string.
151 +
152 +.TP
153 \fB\fI nfsroot
154 can be either "auto" to try to get the relevant information from DHCP or a
155 string of the form NFSSERVER:NFSPATH or NFSSERVER:NFSPATH:NFSOPTS.
156 diff --git a/scripts/functions b/scripts/functions
157 index 79506e7..3c4f7af 100644
158 --- a/scripts/functions
159 +++ b/scripts/functions
160 @@ -472,6 +472,18 @@ mountroot()
161 *) boot=local ;;
162 esac
163
164 - echo "Mounting ${boot} root filesystem"
165 mount${boot}root
166 +}
167 +
168 +# Mount the etc file system. This is delegated to the
169 +# mountlocaletc function; not currently supported by nfs.
170 +# $1=type local|nfs|unknown
171 +mountetc()
172 +{
173 + case $boot in
174 + nfs) return ;;
175 + *) boot=local ;;
176 + esac
177 +
178 + mount${boot}etc
179 }
180 \ No newline at end of file
181 diff --git a/scripts/local b/scripts/local
182 index f6f023f..2df27ab 100644
183 --- a/scripts/local
184 +++ b/scripts/local
185 @@ -1,11 +1,9 @@
186 # Local filesystem mounting -*- shell-script -*-
187
188 -pre_mountroot()
189 +# $1=device to mount
190 +# $2=optionname (for root and etc)
191 +pre_localmount()
192 {
193 - [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-top"
194 - run_scripts /scripts/local-top
195 - [ "$quiet" != "y" ] && log_end_msg
196 -
197 wait_for_udev 10
198
199 # Load ubi with the correct MTD partition and return since fstype
200 @@ -17,21 +15,21 @@ pre_mountroot()
201
202 # Don't wait for a root device that doesn't have a corresponding
203 # device in /dev (ie, mtd0)
204 - if [ "${ROOT#/dev}" = "${ROOT}" ]; then
205 + if [ "${ROOT#/dev}" = "$1" ]; then
206 return
207 fi
208
209 # If the root device hasn't shown up yet, give it a little while
210 # to deal with removable devices
211 - if [ ! -e "${ROOT}" ] || ! $(get_fstype "${ROOT}" >/dev/null); then
212 + if [ ! -e "$1" ] || ! $(get_fstype "$1" >/dev/null); then
213 log_begin_msg "Waiting for root file system"
214
215 # Default delay is 30s
216 slumber=${ROOTDELAY:-30}
217
218 slumber=$(( ${slumber} * 10 ))
219 - while [ ! -e "${ROOT}" ] \
220 - || ! $(get_fstype "${ROOT}" >/dev/null); do
221 + while [ ! -e "$1" ] \
222 + || ! $(get_fstype "$1" >/dev/null); do
223 /bin/sleep 0.1
224 slumber=$(( ${slumber} - 1 ))
225 [ ${slumber} -gt 0 ] || break
226 @@ -45,15 +43,15 @@ pre_mountroot()
227 fi
228
229 # We've given up, but we'll let the user fix matters if they can
230 - while [ ! -e "${ROOT}" ]; do
231 + while [ ! -e "$1" ]; do
232 # give hint about renamed root
233 - case "${ROOT}" in
234 + case "$1" in
235 /dev/hd*)
236 suffix="${ROOT#/dev/hd}"
237 major="${suffix%[[:digit:]]}"
238 major="${major%[[:digit:]]}"
239 if [ -d "/sys/block/sd${major}" ]; then
240 - echo "WARNING bootdevice may be renamed. Try root=/dev/sd${suffix}"
241 + echo "WARNING bootdevice may be renamed. Try $2=/dev/sd${suffix}"
242 fi
243 ;;
244 /dev/sd*)
245 @@ -61,22 +59,43 @@ pre_mountroot()
246 major="${suffix%[[:digit:]]}"
247 major="${major%[[:digit:]]}"
248 if [ -d "/sys/block/hd${major}" ]; then
249 - echo "WARNING bootdevice may be renamed. Try root=/dev/hd${suffix}"
250 + echo "WARNING bootdevice may be renamed. Try $2=/dev/hd${suffix}"
251 fi
252 ;;
253 esac
254 - echo "Gave up waiting for root device. Common problems:"
255 + echo "Gave up waiting for $2 device. Common problems:"
256 echo " - Boot args (cat /proc/cmdline)"
257 echo " - Check rootdelay= (did the system wait long enough?)"
258 - echo " - Check root= (did the system wait for the right device?)"
259 + echo " - Check $2= (did the system wait for the right device?)"
260 echo " - Missing modules (cat /proc/modules; ls /dev)"
261 - panic "ALERT! ${ROOT} does not exist. Dropping to a shell!"
262 + panic "ALERT! $1 does not exist. Dropping to a shell!"
263 done
264 }
265
266 +mountlocaltop()
267 +{
268 + if [ "${localtopused}" != "yes" ]; then
269 + [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-top"
270 + run_scripts /scripts/local-top
271 + [ "$quiet" != "y" ] && log_end_msg
272 + fi
273 + localtopused=yes
274 +}
275 +
276 +mountlocalpremount()
277 +{
278 + if [ "${localpremountused}" != "yes" ]; then
279 + [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-premount"
280 + run_scripts /scripts/local-premount
281 + [ "$quiet" != "y" ] && log_end_msg
282 + fi
283 + localpremountused=yes
284 +}
285 +
286 mountlocalroot()
287 {
288 - pre_mountroot
289 + mountlocaltop
290 + pre_localmount "${ROOT}" root
291
292 # Get the root filesystem type if not set
293 if [ -z "${ROOTFSTYPE}" ]; then
294 @@ -85,9 +104,7 @@ mountlocalroot()
295 FSTYPE=${ROOTFSTYPE}
296 fi
297
298 - [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-premount"
299 - run_scripts /scripts/local-premount
300 - [ "$quiet" != "y" ] && log_end_msg
301 + mountlocalpremount
302
303 if [ "${readonly}" = "y" ]; then
304 roflag=-r
305 @@ -105,7 +122,41 @@ mountlocalroot()
306 else
307 mount ${roflag} ${ROOTFLAGS} ${ROOT} ${rootmnt}
308 fi
309 +}
310 +
311 +mountlocaletc()
312 +{
313 + pre_localmount "${ETC}" etc
314 +
315 + # Get the etc filesystem type if not set
316 + if [ -z "${ETCFSTYPE}" ]; then
317 + FSTYPE=$(get_fstype "${ETC}")
318 + else
319 + FSTYPE=${ETCFSTYPE}
320 + fi
321
322 + mountlocalstart
323 +
324 + if [ "${readonly}" = "y" ]; then
325 + roflag=-r
326 + else
327 + roflag=-w
328 + fi
329 +
330 + # FIXME This has no error checking
331 + modprobe ${FSTYPE}
332 +
333 + # FIXME This has no error checking
334 + # Mount etc
335 + if [ "${FSTYPE}" != "unknown" ]; then
336 + mount ${roflag} -t ${FSTYPE} ${ETCFLAGS} ${ETC} ${rootmnt}/etc
337 + else
338 + mount ${roflag} ${ETCFLAGS} ${ETC} ${rootmnt}/etc
339 + fi
340 +}
341 +
342 +mountlocalend()
343 +{
344 [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-bottom"
345 run_scripts /scripts/local-bottom
346 [ "$quiet" != "y" ] && log_end_msg
347 diff --git a/scripts/nfs b/scripts/nfs
348 index a0764a3..d93f5fb 100644
349 --- a/scripts/nfs
350 +++ b/scripts/nfs
351 @@ -35,9 +35,7 @@ do_nfsmount()
352 NFSOPTS="-o retrans=10"
353 fi
354
355 - [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/nfs-premount"
356 - run_scripts /scripts/nfs-premount
357 - [ "$quiet" != "y" ] && log_end_msg
358 + mountnfspremount
359
360 if [ ${readonly} = y ]; then
361 roflag="-o ro"
362 @@ -48,12 +46,31 @@ do_nfsmount()
363 nfsmount -o nolock ${roflag} ${NFSOPTS} ${NFSROOT} ${rootmnt}
364 }
365
366 +mountnfstop()
367 +{
368 + if [ "${nfstopused}" != "yes" ]; then
369 + [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/nfs-top"
370 + run_scripts /scripts/nfs-top
371 + [ "$quiet" != "y" ] && log_end_msg
372 + fi
373 + nfstopused=yes
374 +}
375 +
376 +mountnfspremount()
377 +{
378 + if [ "${nfspremountused}" != "yes" ]; then
379 + [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/nfs-premount"
380 + run_scripts /scripts/nfs-premount
381 + [ "$quiet" != "y" ] && log_end_msg
382 + fi
383 + nfspremountused=yes
384 +}
385 +
386 +
387 # NFS root mounting
388 mountnfsroot()
389 {
390 - [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/nfs-top"
391 - run_scripts /scripts/nfs-top
392 - [ "$quiet" != "y" ] && log_end_msg
393 + mountnfstop
394
395 modprobe nfs
396 # For DHCP
397 @@ -73,7 +90,10 @@ mountnfsroot()
398 retry_nr=$(( ${retry_nr} + 1 ))
399 [ "$quiet" != "y" ] && log_end_msg
400 done
401 +}
402
403 +mountnfsend()
404 +{
405 [ "$quiet" != "y" ] && log_begin_msg "Running /scripts/nfs-bottom"
406 run_scripts /scripts/nfs-bottom
407 [ "$quiet" != "y" ] && log_end_msg
util-linux
1 --- util-linux-2.20.1.original/fsck/fsck.8 2012-12-25 15:14:40.000000000 +0000
2 +++ util-linux-2.20.1/fsck/fsck.8 2012-12-27 22:19:18.000000000 +0000
3 @@ -273,8 +273,8 @@
4 .B \-R
5 When checking all filesystems with the
6 .B \-A
7 -flag, skip the root filesystem. (This is useful in case the root
8 -filesystem has already been mounted read-write.)
9 +flag, skip the root, /etc and /usr filesystems. (This is useful in case the root,
10 +/etc and/or /usr filesystems have already been mounted read-write.)
11 .TP
12 .B \-T
13 Don't show the title on startup.
14 --- util-linux-2.20.1.original/fsck/fsck.c 2012-12-25 15:14:40.000000000 +0000
15 +++ util-linux-2.20.1/fsck/fsck.c 2012-12-27 22:18:20.000000000 +0000
16 @@ -1132,11 +1132,11 @@
17 }
18 /*
19 * This is for the bone-headed user who enters the root
20 - * filesystem twice. Skip root will skep all root entries.
21 + * filesystem twice. Skip root will skep all root, /etc and /usr entries.
22 */
23 if (skip_root)
24 for (fs = filesys_info; fs; fs = fs->next)
25 - if (!strcmp(fs->mountpt, "/"))
26 + if (!strcmp(fs->mountpt, "/") || !strcmp(fs->mountpt, "/etc") || !strcmp(fs->mountpt, "/usr"))
27 fs->flags |= FLAG_DONE;
28
29 while (not_done_yet) {
initscripts (sysvinit)
TODO