summaryrefslogtreecommitdiffstats
path: root/recipes-core/initrdscripts/files/init-readonly-rootfs-overlay-boot.sh
blob: 37a1635bc466f9580ebbf35dd82870daa667e47b (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
#!/bin/sh

# Enable strict shell mode
set -euo pipefail

PATH=/sbin:/bin:/usr/sbin:/usr/bin

MOUNT="/bin/mount"
UMOUNT="/bin/umount"

INIT="/sbin/init"

ROOT_MOUNT="/mnt"
ROOT_RODEVICE=""
ROOT_RWDEVICE=""
ROOT_ROMOUNT="/media/rfs/ro"
ROOT_RWMOUNT="/media/rfs/rw"
ROOT_RWRESET="no"

# Copied from initramfs-framework. The core of this script probably should be
# turned into initramfs-framework modules to reduce duplication.
udev_daemon() {
	OPTIONS="/sbin/udev/udevd /sbin/udevd /lib/udev/udevd /lib/systemd/systemd-udevd"

	for o in $OPTIONS; do
		if [ -x "$o" ]; then
			echo $o
			return 0
		fi
	done

	return 1
}

_UDEV_DAEMON=`udev_daemon`

early_setup() {
    mkdir -p /proc
    mkdir -p /sys
    $MOUNT -t proc proc /proc
    $MOUNT -t sysfs sysfs /sys
    $MOUNT -t devtmpfs none /dev

    mkdir -p /run
    mkdir -p /var/run

    $_UDEV_DAEMON --daemon
    udevadm trigger --action=add
}

read_args() {
    [ -z "${CMDLINE+x}" ] && CMDLINE=`cat /proc/cmdline`
    for arg in $CMDLINE; do
        optarg=`expr "x$arg" : 'x[^=]*=\(.*\)'`
        case $arg in
            root=*)
                ROOT_RODEVICE=$optarg ;;
            rootfstype=*)
                modprobe $optarg 2> /dev/null ;;
            rootrw=*)
                ROOT_RWDEVICE=$optarg ;;
            rootrwfstype=*)
                modprobe $optarg 2> /dev/null ;;
            rootrwreset=*)
                ROOT_RWRESET=$optarg ;;
            video=*)
                video_mode=$arg ;;
            vga=*)
                vga_mode=$arg ;;
            init=*)
                INIT=$optarg ;;
            console=*)
                if [ -z "${console_params+x}" ]; then
                    console_params=$arg
                else
                    console_params="$console_params $arg"
                fi ;;
        esac
    done
}

fatal() {
    echo $1 >$CONSOLE
    echo >$CONSOLE
    exec sh
}

early_setup

[ -z "${CONSOLE+x}" ] && CONSOLE="/dev/console"

read_args

mount_and_boot() {
    mkdir -p $ROOT_MOUNT $ROOT_ROMOUNT $ROOT_RWMOUNT

    # Build mount options for read only root filesystem.
    # If no read-only device was specified via kernel commandline, use current
    # rootfs.
    if [ -z "${ROOT_RODEVICE}" ]; then
	ROOT_ROMOUNTOPTIONS="--bind,ro /"
    else
	ROOT_ROMOUNTOPTIONS="-o ro,noatime,nodiratime $ROOT_RODEVICE"
    fi

    # Mount rootfs as read-only to mount-point
    if ! $MOUNT $ROOT_ROMOUNTOPTIONS $ROOT_ROMOUNT ; then
        fatal "Could not mount read-only rootfs"
    fi

    # Build mount options for read write root filesystem.
    # If no read-write device was specified via kernel commandline, use tmpfs.
    if [ -z "${ROOT_RWDEVICE}" ]; then
	ROOT_RWMOUNTOPTIONS="-t tmpfs -o rw,noatime,mode=755 tmpfs"
    else
	ROOT_RWMOUNTOPTIONS="-o rw,noatime,mode=755 $ROOT_RWDEVICE"
    fi

    # Mount read-write filesystem into initram rootfs
    if ! $MOUNT $ROOT_RWMOUNTOPTIONS $ROOT_RWMOUNT ; then
	fatal "Could not mount read-write rootfs"
    fi

    # Reset read-write filesystem if specified
    if [ "yes" == "$ROOT_RWRESET" -a -n "${ROOT_RWMOUNT}" ]; then
	rm -rf $ROOT_RWMOUNT/*
    fi

    # Determine which unification filesystem to use
    union_fs_type=""
    if grep -w "overlay" /proc/filesystems; then
	union_fs_type="overlay"
    elif grep -w "aufs" /proc/filesystems; then
	union_fs_type="aufs"
    else
	union_fs_type=""
    fi

    # Create/Mount overlay root filesystem 
    case $union_fs_type in
	"overlay")
	    mkdir -p $ROOT_RWMOUNT/upperdir $ROOT_RWMOUNT/work
	    $MOUNT -t overlay overlay -o "lowerdir=$ROOT_ROMOUNT,upperdir=$ROOT_RWMOUNT/upperdir,workdir=$ROOT_RWMOUNT/work" $ROOT_MOUNT
	    ;;
	"aufs")
	    $MOUNT -t aufs -o "dirs=$ROOT_RWMOUNT=rw:$ROOT_ROMOUNT=ro" aufs $ROOT_MOUNT
	    ;;
	"")
	    fatal "No overlay filesystem type available"
	    ;;
    esac

    # Move read-only and read-write root filesystem into the overlay filesystem
    mkdir -p $ROOT_MOUNT/$ROOT_ROMOUNT $ROOT_MOUNT/$ROOT_RWMOUNT
    $MOUNT -n --move $ROOT_ROMOUNT ${ROOT_MOUNT}/$ROOT_ROMOUNT
    $MOUNT -n --move $ROOT_RWMOUNT ${ROOT_MOUNT}/$ROOT_RWMOUNT

    # Watches the udev event queue, and exits if all current events are handled
    udevadm settle --timeout=3
    # Kills the current udev running processes, which survived after
    # device node creation events were handled, to avoid unexpected behavior
    killall -9 "${_UDEV_DAEMON##*/}" 2>/dev/null

    # Remove /run /var/run that are created in early_setup
    rm -rf /run /var/run

    # Move the mount points of some filesystems over to
    # the corresponding directories under the real root filesystem.
    for dir in `awk '/\/dev.* \/run\/media/{print $2}' /proc/mounts`; do
        mkdir -p ${ROOT_MOUNT}/media/${dir##*/}
        $MOUNT -n --move $dir ${ROOT_MOUNT}/media/${dir##*/}
    done
    $MOUNT -n --move /proc ${ROOT_MOUNT}/proc
    $MOUNT -n --move /sys ${ROOT_MOUNT}/sys
    $MOUNT -n --move /dev ${ROOT_MOUNT}/dev

    cd $ROOT_MOUNT

    # busybox switch_root supports -c option
    exec chroot $ROOT_MOUNT $INIT ||
        fatal "Couldn't chroot, dropping to shell"
}

mount_and_boot