initramfs-framework: Add overlayroot module

Message ID 20220126072231.1749903-1-alhe@linux.microsoft.com
State Accepted, archived
Commit 4f876982a856c54a8074c85346632e33caa7ef53
Headers show
Series initramfs-framework: Add overlayroot module | expand

Commit Message

Alejandro Enedino Hernandez Samaniego Jan. 26, 2022, 7:22 a.m. UTC
When installed, this module mounts a read-write (RW) overlay on
top of a root filesystem, which is kept read-only (RO).

It needs to be executed after the initramfs-module-rootfs since
it relies on it to mount the filesystem at initramfs startup but
before the finish module which normally switches root.

It requires rootrw=<foo> to be passed as a kernel parameter to
specify the device/partition to be used as RW by the overlay and
has a dependency on overlayfs support being present in the
running kernel.

It does not require the read-only IMAGE_FEATURE to be enabled.

Signed-off-by: Alejandro Enedino Hernandez Samaniego <alhe@linux.microsoft.com>
---
 .../initramfs-framework/overlayroot           | 93 +++++++++++++++++++
 .../initrdscripts/initramfs-framework_1.0.bb  |  9 ++
 2 files changed, 102 insertions(+)
 create mode 100644 meta/recipes-core/initrdscripts/initramfs-framework/overlayroot

Comments

Bruce Ashfield Jan. 26, 2022, 2:11 p.m. UTC | #1
On Wed, Jan 26, 2022 at 2:22 AM Alejandro Hernandez Samaniego
<alhe@linux.microsoft.com> wrote:
>
> When installed, this module mounts a read-write (RW) overlay on
> top of a root filesystem, which is kept read-only (RO).
>
> It needs to be executed after the initramfs-module-rootfs since
> it relies on it to mount the filesystem at initramfs startup but
> before the finish module which normally switches root.
>
> It requires rootrw=<foo> to be passed as a kernel parameter to
> specify the device/partition to be used as RW by the overlay and
> has a dependency on overlayfs support being present in the
> running kernel.
>
> It does not require the read-only IMAGE_FEATURE to be enabled.

Alejandro,

What's the higher level use case for this ? It would be worth capturing
it in the commit log.  Is this filling a gap in existing functionality ? Is it
for spinning up initramfs only configurations ? A security thing ?

>
> Signed-off-by: Alejandro Enedino Hernandez Samaniego <alhe@linux.microsoft.com>
> ---
>  .../initramfs-framework/overlayroot           | 93 +++++++++++++++++++
>  .../initrdscripts/initramfs-framework_1.0.bb  |  9 ++
>  2 files changed, 102 insertions(+)
>  create mode 100644 meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
>
> diff --git a/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
> new file mode 100644
> index 0000000000..ec5700e8fc
> --- /dev/null
> +++ b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
> @@ -0,0 +1,93 @@
> +#!/bin/sh
> +

The new script should have a copyright and SPDX identifier .. Don't
take this as great wisdom, it is something thatI always forget to to
as well!

On that note, is this completely from scratch or is it the assembly of
other bits and pieces found elsewhere ? It would be good to document
that as well.

> +# Simple initramfs module intended to mount a read-write (RW)
> +# overlayfs on top of /, keeping the original root filesystem
> +# as read-only (RO).
> +#
> +# NOTE: The read-only IMAGE_FEATURE is not required for this to work
> +#
> +# It relies on the initramfs-module-rootfs to mount the original
> +# root filesystem, and requires 'rootrw=<foo>' to be passed as a
> +# kernel parameter, specifying the device/partition intended to
> +# use as RW.
> +#
> +# It also has a dependency on overlayfs being enabled in the
> +# running kernel via KERNEL_FEATURES (kmeta) or any other means.
> +#
> +# The RO root filesystem remains accessible by the system, mounted
> +# at /rofs
> +
> +PATH=/sbin:/bin:/usr/sbin:/usr/bin
> +
> +# We get OLDROOT from the rootfs module
> +OLDROOT="/rootfs"
> +
> +NEWROOT="${RWMOUNT}/root"
> +RWMOUNT="/overlay"
> +ROMOUNT="${RWMOUNT}/rofs"
> +UPPER_DIR="${RWMOUNT}/upper"
> +WORK_DIR="${RWMOUNT}/work"
> +
> +MODULES_DIR=/init.d
> +
> +exit_gracefully() {
> +    echo $1 >/dev/console
> +    echo >/dev/console
> +    echo "OverlayRoot mounting failed, starting system as read-only" >/dev/console
> +    echo >/dev/console
> +
> +    # Make sure / is mounted as read only anyway.
> +    # Borrowed from rootfs-postcommands.bbclass
> +    # Tweak the mount option and fs_passno for rootfs in fstab

For maintenance reasons, it would be a good idea to expand on (in
words) what the tweak is, what the old value is, and the new.

> +    if [ -f ${OLDROOT}/etc/fstab ]; then
> +        sed -i -e '/^[#[:space:]]*\/dev\/root/{s/defaults/ro/;s/\([[:space:]]*[[:digit:]]\)\([[:space:]]*\)[[:digit:]]$/\1\20/}' ${OLDROOT}/etc/fstab
> +    fi
> +
> +    # Tweak the "mount -o remount,rw /" command in busybox-inittab inittab
> +    if [ -f ${OLDROOT}/etc/inittab ]; then
> +        sed -i 's|/bin/mount -o remount,rw /|/bin/mount -o remount,ro /|' ${OLDROOT}/etc/inittab
> +    fi
> +
> +    # Continue as if the overlayroot module didn't exist
> +    . $MODULES_DIR/99-finish
> +    eval "finish_run"
> +}
> +
> +
> +if [ -z "$bootparam_rootrw" ]; then
> +    exit_gracefully "rootrw= kernel parameter doesn't exist and its required to mount the overlayfs"
> +fi
> +
> +mkdir -p ${RWMOUNT}
> +
> +# Mount RW device
> +if mount -n -t ${bootparam_rootfstype:-ext4} -o ${bootparam_rootflags:-defaults} ${bootparam_rootrw} ${RWMOUNT}
> +then
> +    # Set up overlay directories
> +    mkdir -p ${UPPER_DIR}
> +    mkdir -p ${WORK_DIR}
> +    mkdir -p ${NEWROOT}
> +    mkdir -p ${ROMOUNT}
> +
> +    # Remount OLDROOT as read-only
> +    mount -o bind ${OLDROOT} ${ROMOUNT}
> +    mount -o remount,ro ${ROMOUNT}
> +
> +    # Mount RW overlay
> +    mount -t overlay overlay -o lowerdir=${ROMOUNT},upperdir=${UPPER_DIR},workdir=${WORK_DIR} ${NEWROOT} || exit_gracefully "initramfs-overlayroot: Mounting overlay failed"
> +else
> +    exit_gracefully "initramfs-overlayroot: Mounting RW device failed"
> +fi
> +
> +# Set up filesystems on overlay
> +mkdir -p ${NEWROOT}/proc
> +mkdir -p ${NEWROOT}/dev
> +mkdir -p ${NEWROOT}/sys
> +mkdir -p ${NEWROOT}/rofs
> +
> +mount -n --move ${ROMOUNT} ${NEWROOT}/rofs
> +mount -n --move /proc ${NEWROOT}/proc
> +mount -n --move /sys ${NEWROOT}/sys
> +mount -n --move /dev ${NEWROOT}/dev
> +
> +exec chroot ${NEWROOT}/ ${bootparam_init:-/sbin/init} || exit_gracefully "Couldn't chroot into overlay"

What's the boot flow in the end ? The r/w overlay is setup, and the rest of the
initrams -> full boot happens as normal ?

Bruce

> diff --git a/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb b/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
> index 9e8c1dc3ab..4e76e20026 100644
> --- a/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
> +++ b/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
> @@ -18,6 +18,7 @@ SRC_URI = "file://init \
>             file://e2fs \
>             file://debug \
>             file://lvm \
> +           file://overlayroot \
>            "
>
>  S = "${WORKDIR}"
> @@ -49,6 +50,9 @@ do_install() {
>      # lvm
>      install -m 0755 ${WORKDIR}/lvm ${D}/init.d/09-lvm
>
> +    # overlayroot needs to run after rootfs module but before finish
> +    install -m 0755 ${WORKDIR}/overlayroot ${D}/init.d/91-overlayroot
> +
>      # Create device nodes expected by some kernels in initramfs
>      # before even executing /init.
>      install -d ${D}/dev
> @@ -64,6 +68,7 @@ PACKAGES = "${PN}-base \
>              initramfs-module-rootfs \
>              initramfs-module-debug \
>              initramfs-module-lvm \
> +            initramfs-module-overlayroot \
>             "
>
>  FILES:${PN}-base = "/init /init.d/99-finish /dev"
> @@ -107,3 +112,7 @@ FILES:initramfs-module-debug = "/init.d/00-debug"
>  SUMMARY:initramfs-module-lvm = "initramfs lvm rootfs support"
>  RDEPENDS:initramfs-module-lvm = "${PN}-base"
>  FILES:initramfs-module-lvm = "/init.d/09-lvm"
> +
> +SUMMARY:initramfs-module-overlayroot = "initramfs support for mounting a RW overlay on top of a RO root filesystem"
> +RDEPENDS:initramfs-module-overlayroot = "${PN}-base initramfs-module-rootfs"
> +FILES:initramfs-module-overlayroot = "/init.d/91-overlayroot"
> --
> 2.25.1
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#160961): https://lists.openembedded.org/g/openembedded-core/message/160961
> Mute This Topic: https://lists.openembedded.org/mt/88691930/1050810
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [bruce.ashfield@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>


--
- Thou shalt not follow the NULL pointer, for chaos and madness await
thee at its end
- "Use the force Harry" - Gandalf, Star Trek II
Alejandro Enedino Hernandez Samaniego Jan. 26, 2022, 6:09 p.m. UTC | #2
On 1/26/22 7:11 AM, Bruce Ashfield wrote:
> On Wed, Jan 26, 2022 at 2:22 AM Alejandro Hernandez Samaniego
> <alhe@linux.microsoft.com> wrote:
>> When installed, this module mounts a read-write (RW) overlay on
>> top of a root filesystem, which is kept read-only (RO).
>>
>> It needs to be executed after the initramfs-module-rootfs since
>> it relies on it to mount the filesystem at initramfs startup but
>> before the finish module which normally switches root.
>>
>> It requires rootrw=<foo> to be passed as a kernel parameter to
>> specify the device/partition to be used as RW by the overlay and
>> has a dependency on overlayfs support being present in the
>> running kernel.
>>
>> It does not require the read-only IMAGE_FEATURE to be enabled.
> Alejandro,
>
> What's the higher level use case for this ? It would be worth capturing
> it in the commit log.  Is this filling a gap in existing functionality ? Is it
> for spinning up initramfs only configurations ? A security thing ?

Hi Bruce, thanks for reviewing this and pointing out what I missed, I'll be sending a v2 later today.

There may be several reasons why we'd want to have the possibility of keeping the original
rootfs unmodified, and keeping user customizations on a separate device, for example it
would make it easier to perform a factory reset on a device.

>> Signed-off-by: Alejandro Enedino Hernandez Samaniego <alhe@linux.microsoft.com>
>> ---
>>   .../initramfs-framework/overlayroot           | 93 +++++++++++++++++++
>>   .../initrdscripts/initramfs-framework_1.0.bb  |  9 ++
>>   2 files changed, 102 insertions(+)
>>   create mode 100644 meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
>>
>> diff --git a/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
>> new file mode 100644
>> index 0000000000..ec5700e8fc
>> --- /dev/null
>> +++ b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
>> @@ -0,0 +1,93 @@
>> +#!/bin/sh
>> +
> The new script should have a copyright and SPDX identifier .. Don't
> take this as great wisdom, it is something thatI always forget to to
> as well!
>
> On that note, is this completely from scratch or is it the assembly of
> other bits and pieces found elsewhere ? It would be good to document
> that as well.

Oops! completely forgot!, I'll add it.

This is loosely based on the overlay-etc.bbclass the difference is that
the class only works for overlaying /etc and it doesnt require an
initramfs, but, while its possible to tinker it in such a way that it
overlays /, its not possible to access the original (RO) / after booting the
system, this is the reason  why this has to be done from initramfs and why
this doesnt patch the overlay-etc.bbclass instead.

I believe Ubuntu has this feature, coming from the cloud-initramfs
package, functionally it should be similar although no code was borrowed
from there.

I'll add a note about the overlay-etc class though.

>> +# Simple initramfs module intended to mount a read-write (RW)
>> +# overlayfs on top of /, keeping the original root filesystem
>> +# as read-only (RO).
>> +#
>> +# NOTE: The read-only IMAGE_FEATURE is not required for this to work
>> +#
>> +# It relies on the initramfs-module-rootfs to mount the original
>> +# root filesystem, and requires 'rootrw=<foo>' to be passed as a
>> +# kernel parameter, specifying the device/partition intended to
>> +# use as RW.
>> +#
>> +# It also has a dependency on overlayfs being enabled in the
>> +# running kernel via KERNEL_FEATURES (kmeta) or any other means.
>> +#
>> +# The RO root filesystem remains accessible by the system, mounted
>> +# at /rofs
>> +
>> +PATH=/sbin:/bin:/usr/sbin:/usr/bin
>> +
>> +# We get OLDROOT from the rootfs module
>> +OLDROOT="/rootfs"
>> +
>> +NEWROOT="${RWMOUNT}/root"
>> +RWMOUNT="/overlay"
>> +ROMOUNT="${RWMOUNT}/rofs"
>> +UPPER_DIR="${RWMOUNT}/upper"
>> +WORK_DIR="${RWMOUNT}/work"
>> +
>> +MODULES_DIR=/init.d
>> +
>> +exit_gracefully() {
>> +    echo $1 >/dev/console
>> +    echo >/dev/console
>> +    echo "OverlayRoot mounting failed, starting system as read-only" >/dev/console
>> +    echo >/dev/console
>> +
>> +    # Make sure / is mounted as read only anyway.
>> +    # Borrowed from rootfs-postcommands.bbclass
>> +    # Tweak the mount option and fs_passno for rootfs in fstab
> For maintenance reasons, it would be a good idea to expand on (in
> words) what the tweak is, what the old value is, and the new.

Actually that comment (along with the next couple of lines) is coming
from the rootfs-postcommands.bbclass to mount the root filesystem RO.

>
>> +    if [ -f ${OLDROOT}/etc/fstab ]; then
>> +        sed -i -e '/^[#[:space:]]*\/dev\/root/{s/defaults/ro/;s/\([[:space:]]*[[:digit:]]\)\([[:space:]]*\)[[:digit:]]$/\1\20/}' ${OLDROOT}/etc/fstab
>> +    fi
>> +
>> +    # Tweak the "mount -o remount,rw /" command in busybox-inittab inittab
>> +    if [ -f ${OLDROOT}/etc/inittab ]; then
>> +        sed -i 's|/bin/mount -o remount,rw /|/bin/mount -o remount,ro /|' ${OLDROOT}/etc/inittab
>> +    fi
>> +
>> +    # Continue as if the overlayroot module didn't exist
>> +    . $MODULES_DIR/99-finish
>> +    eval "finish_run"
>> +}
>> +
>> +
>> +if [ -z "$bootparam_rootrw" ]; then
>> +    exit_gracefully "rootrw= kernel parameter doesn't exist and its required to mount the overlayfs"
>> +fi
>> +
>> +mkdir -p ${RWMOUNT}
>> +
>> +# Mount RW device
>> +if mount -n -t ${bootparam_rootfstype:-ext4} -o ${bootparam_rootflags:-defaults} ${bootparam_rootrw} ${RWMOUNT}
>> +then
>> +    # Set up overlay directories
>> +    mkdir -p ${UPPER_DIR}
>> +    mkdir -p ${WORK_DIR}
>> +    mkdir -p ${NEWROOT}
>> +    mkdir -p ${ROMOUNT}
>> +
>> +    # Remount OLDROOT as read-only
>> +    mount -o bind ${OLDROOT} ${ROMOUNT}
>> +    mount -o remount,ro ${ROMOUNT}
>> +
>> +    # Mount RW overlay
>> +    mount -t overlay overlay -o lowerdir=${ROMOUNT},upperdir=${UPPER_DIR},workdir=${WORK_DIR} ${NEWROOT} || exit_gracefully "initramfs-overlayroot: Mounting overlay failed"
>> +else
>> +    exit_gracefully "initramfs-overlayroot: Mounting RW device failed"
>> +fi
>> +
>> +# Set up filesystems on overlay
>> +mkdir -p ${NEWROOT}/proc
>> +mkdir -p ${NEWROOT}/dev
>> +mkdir -p ${NEWROOT}/sys
>> +mkdir -p ${NEWROOT}/rofs
>> +
>> +mount -n --move ${ROMOUNT} ${NEWROOT}/rofs
>> +mount -n --move /proc ${NEWROOT}/proc
>> +mount -n --move /sys ${NEWROOT}/sys
>> +mount -n --move /dev ${NEWROOT}/dev
>> +
>> +exec chroot ${NEWROOT}/ ${bootparam_init:-/sbin/init} || exit_gracefully "Couldn't chroot into overlay"
> What's the boot flow in the end ? The r/w overlay is setup, and the rest of the
> initrams -> full boot happens as normal ?
>
> Bruce

In the initramfs-framework flow, this  would be the last module that is run, and "replaces" the finish module,
so the rest of the initramfs-modules that were installed should have been run already keeping the same boot
flow, although, instead of the finish module executing switch_root to the now mounted real root, this chroots
into the now mounted real RW root overlay.

This patch does not install (or create any rdepends to) the initramfs-module-overlayroot package on purpose,
the original flow using initramfs-framework is kept, it would only change to the above flow when a user manually
installs this module on their initramfs image.

Alejandro

>
>> diff --git a/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb b/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
>> index 9e8c1dc3ab..4e76e20026 100644
>> --- a/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
>> +++ b/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
>> @@ -18,6 +18,7 @@ SRC_URI = "file://init \
>>              file://e2fs \
>>              file://debug \
>>              file://lvm \
>> +           file://overlayroot \
>>             "
>>
>>   S = "${WORKDIR}"
>> @@ -49,6 +50,9 @@ do_install() {
>>       # lvm
>>       install -m 0755 ${WORKDIR}/lvm ${D}/init.d/09-lvm
>>
>> +    # overlayroot needs to run after rootfs module but before finish
>> +    install -m 0755 ${WORKDIR}/overlayroot ${D}/init.d/91-overlayroot
>> +
>>       # Create device nodes expected by some kernels in initramfs
>>       # before even executing /init.
>>       install -d ${D}/dev
>> @@ -64,6 +68,7 @@ PACKAGES = "${PN}-base \
>>               initramfs-module-rootfs \
>>               initramfs-module-debug \
>>               initramfs-module-lvm \
>> +            initramfs-module-overlayroot \
>>              "
>>
>>   FILES:${PN}-base = "/init /init.d/99-finish /dev"
>> @@ -107,3 +112,7 @@ FILES:initramfs-module-debug = "/init.d/00-debug"
>>   SUMMARY:initramfs-module-lvm = "initramfs lvm rootfs support"
>>   RDEPENDS:initramfs-module-lvm = "${PN}-base"
>>   FILES:initramfs-module-lvm = "/init.d/09-lvm"
>> +
>> +SUMMARY:initramfs-module-overlayroot = "initramfs support for mounting a RW overlay on top of a RO root filesystem"
>> +RDEPENDS:initramfs-module-overlayroot = "${PN}-base initramfs-module-rootfs"
>> +FILES:initramfs-module-overlayroot = "/init.d/91-overlayroot"
>> --
>> 2.25.1
>>
>>
>> -=-=-=-=-=-=-=-=-=-=-=-
>> Links: You receive all messages sent to this group.
>> View/Reply Online (#160961): https://lists.openembedded.org/g/openembedded-core/message/160961
>> Mute This Topic: https://lists.openembedded.org/mt/88691930/1050810
>> Group Owner: openembedded-core+owner@lists.openembedded.org
>> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [bruce.ashfield@gmail.com]
>> -=-=-=-=-=-=-=-=-=-=-=-
>>
>
> --
> - Thou shalt not follow the NULL pointer, for chaos and madness await
> thee at its end
> - "Use the force Harry" - Gandalf, Star Trek II
Vyacheslav Yurkov Jan. 27, 2022, 7:30 a.m. UTC | #3
Hi Alejandro,
Thanks for your patch.

I recently submitted two classes overlayfs and overlayfs-etc to do the 
same thing you want to achieve. Could you please take a look if you can 
use them instead? If not, perhaps we could adapt it to suit your needs?

Regards,
Vyacheslav

On 26.01.2022 08:22, Alejandro Hernandez Samaniego wrote:
> When installed, this module mounts a read-write (RW) overlay on
> top of a root filesystem, which is kept read-only (RO).
>
> It needs to be executed after the initramfs-module-rootfs since
> it relies on it to mount the filesystem at initramfs startup but
> before the finish module which normally switches root.
>
> It requires rootrw=<foo> to be passed as a kernel parameter to
> specify the device/partition to be used as RW by the overlay and
> has a dependency on overlayfs support being present in the
> running kernel.
>
> It does not require the read-only IMAGE_FEATURE to be enabled.
>
> Signed-off-by: Alejandro Enedino Hernandez Samaniego <alhe@linux.microsoft.com>
> ---
>   .../initramfs-framework/overlayroot           | 93 +++++++++++++++++++
>   .../initrdscripts/initramfs-framework_1.0.bb  |  9 ++
>   2 files changed, 102 insertions(+)
>   create mode 100644 meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
>
> diff --git a/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
> new file mode 100644
> index 0000000000..ec5700e8fc
> --- /dev/null
> +++ b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
> @@ -0,0 +1,93 @@
> +#!/bin/sh
> +
> +# Simple initramfs module intended to mount a read-write (RW)
> +# overlayfs on top of /, keeping the original root filesystem
> +# as read-only (RO).
> +#
> +# NOTE: The read-only IMAGE_FEATURE is not required for this to work
> +#
> +# It relies on the initramfs-module-rootfs to mount the original
> +# root filesystem, and requires 'rootrw=<foo>' to be passed as a
> +# kernel parameter, specifying the device/partition intended to
> +# use as RW.
> +#
> +# It also has a dependency on overlayfs being enabled in the
> +# running kernel via KERNEL_FEATURES (kmeta) or any other means.
> +#
> +# The RO root filesystem remains accessible by the system, mounted
> +# at /rofs
> +
> +PATH=/sbin:/bin:/usr/sbin:/usr/bin
> +
> +# We get OLDROOT from the rootfs module
> +OLDROOT="/rootfs"
> +
> +NEWROOT="${RWMOUNT}/root"
> +RWMOUNT="/overlay"
> +ROMOUNT="${RWMOUNT}/rofs"
> +UPPER_DIR="${RWMOUNT}/upper"
> +WORK_DIR="${RWMOUNT}/work"
> +
> +MODULES_DIR=/init.d
> +
> +exit_gracefully() {
> +    echo $1 >/dev/console
> +    echo >/dev/console
> +    echo "OverlayRoot mounting failed, starting system as read-only" >/dev/console
> +    echo >/dev/console
> +
> +    # Make sure / is mounted as read only anyway.
> +    # Borrowed from rootfs-postcommands.bbclass
> +    # Tweak the mount option and fs_passno for rootfs in fstab
> +    if [ -f ${OLDROOT}/etc/fstab ]; then
> +        sed -i -e '/^[#[:space:]]*\/dev\/root/{s/defaults/ro/;s/\([[:space:]]*[[:digit:]]\)\([[:space:]]*\)[[:digit:]]$/\1\20/}' ${OLDROOT}/etc/fstab
> +    fi
> +
> +    # Tweak the "mount -o remount,rw /" command in busybox-inittab inittab
> +    if [ -f ${OLDROOT}/etc/inittab ]; then
> +        sed -i 's|/bin/mount -o remount,rw /|/bin/mount -o remount,ro /|' ${OLDROOT}/etc/inittab
> +    fi
> +
> +    # Continue as if the overlayroot module didn't exist
> +    . $MODULES_DIR/99-finish
> +    eval "finish_run"
> +}
> +
> +
> +if [ -z "$bootparam_rootrw" ]; then
> +    exit_gracefully "rootrw= kernel parameter doesn't exist and its required to mount the overlayfs"
> +fi
> +
> +mkdir -p ${RWMOUNT}
> +
> +# Mount RW device
> +if mount -n -t ${bootparam_rootfstype:-ext4} -o ${bootparam_rootflags:-defaults} ${bootparam_rootrw} ${RWMOUNT}
> +then
> +    # Set up overlay directories
> +    mkdir -p ${UPPER_DIR}
> +    mkdir -p ${WORK_DIR}
> +    mkdir -p ${NEWROOT}
> +    mkdir -p ${ROMOUNT}
> +
> +    # Remount OLDROOT as read-only
> +    mount -o bind ${OLDROOT} ${ROMOUNT}
> +    mount -o remount,ro ${ROMOUNT}
> +
> +    # Mount RW overlay
> +    mount -t overlay overlay -o lowerdir=${ROMOUNT},upperdir=${UPPER_DIR},workdir=${WORK_DIR} ${NEWROOT} || exit_gracefully "initramfs-overlayroot: Mounting overlay failed"
> +else
> +    exit_gracefully "initramfs-overlayroot: Mounting RW device failed"
> +fi
> +
> +# Set up filesystems on overlay
> +mkdir -p ${NEWROOT}/proc
> +mkdir -p ${NEWROOT}/dev
> +mkdir -p ${NEWROOT}/sys
> +mkdir -p ${NEWROOT}/rofs
> +
> +mount -n --move ${ROMOUNT} ${NEWROOT}/rofs
> +mount -n --move /proc ${NEWROOT}/proc
> +mount -n --move /sys ${NEWROOT}/sys
> +mount -n --move /dev ${NEWROOT}/dev
> +
> +exec chroot ${NEWROOT}/ ${bootparam_init:-/sbin/init} || exit_gracefully "Couldn't chroot into overlay"
> diff --git a/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb b/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
> index 9e8c1dc3ab..4e76e20026 100644
> --- a/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
> +++ b/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
> @@ -18,6 +18,7 @@ SRC_URI = "file://init \
>              file://e2fs \
>              file://debug \
>              file://lvm \
> +           file://overlayroot \
>             "
>   
>   S = "${WORKDIR}"
> @@ -49,6 +50,9 @@ do_install() {
>       # lvm
>       install -m 0755 ${WORKDIR}/lvm ${D}/init.d/09-lvm
>   
> +    # overlayroot needs to run after rootfs module but before finish
> +    install -m 0755 ${WORKDIR}/overlayroot ${D}/init.d/91-overlayroot
> +
>       # Create device nodes expected by some kernels in initramfs
>       # before even executing /init.
>       install -d ${D}/dev
> @@ -64,6 +68,7 @@ PACKAGES = "${PN}-base \
>               initramfs-module-rootfs \
>               initramfs-module-debug \
>               initramfs-module-lvm \
> +            initramfs-module-overlayroot \
>              "
>   
>   FILES:${PN}-base = "/init /init.d/99-finish /dev"
> @@ -107,3 +112,7 @@ FILES:initramfs-module-debug = "/init.d/00-debug"
>   SUMMARY:initramfs-module-lvm = "initramfs lvm rootfs support"
>   RDEPENDS:initramfs-module-lvm = "${PN}-base"
>   FILES:initramfs-module-lvm = "/init.d/09-lvm"
> +
> +SUMMARY:initramfs-module-overlayroot = "initramfs support for mounting a RW overlay on top of a RO root filesystem"
> +RDEPENDS:initramfs-module-overlayroot = "${PN}-base initramfs-module-rootfs"
> +FILES:initramfs-module-overlayroot = "/init.d/91-overlayroot"
>
Vyacheslav Yurkov Jan. 27, 2022, 6:31 p.m. UTC | #4
On 26.01.2022 19:09, Alejandro Hernandez Samaniego wrote:
> Oops! completely forgot!, I'll add it.
>
> This is loosely based on the overlay-etc.bbclass the difference is that
> the class only works for overlaying /etc and it doesnt require an
> initramfs, but, while its possible to tinker it in such a way that it
> overlays /, its not possible to access the original (RO) / after booting the
> system, this is the reason  why this has to be done from initramfs and why
> this doesnt patch the overlay-etc.bbclass instead.
>
> I believe Ubuntu has this feature, coming from the cloud-initramfs
> package, functionally it should be similar although no code was borrowed
> from there.
>
> I'll add a note about the overlay-etc class though.

I missed this part when I answered to your first email this morning. I 
generally against expanding the scope of overlayfs (i.e. using it for 
the whole / ), because it creates a bigger migration effort when you 
need to update the files on a persistent partition. I see the only 
difference in your code is that you provide access to original lower 
layer fs. Why would you want to do that?

Thanks,
Vyacheslav

Patch

diff --git a/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
new file mode 100644
index 0000000000..ec5700e8fc
--- /dev/null
+++ b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
@@ -0,0 +1,93 @@ 
+#!/bin/sh
+
+# Simple initramfs module intended to mount a read-write (RW)
+# overlayfs on top of /, keeping the original root filesystem
+# as read-only (RO).
+#
+# NOTE: The read-only IMAGE_FEATURE is not required for this to work
+#
+# It relies on the initramfs-module-rootfs to mount the original
+# root filesystem, and requires 'rootrw=<foo>' to be passed as a
+# kernel parameter, specifying the device/partition intended to
+# use as RW.
+#
+# It also has a dependency on overlayfs being enabled in the
+# running kernel via KERNEL_FEATURES (kmeta) or any other means.
+#
+# The RO root filesystem remains accessible by the system, mounted
+# at /rofs
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin
+
+# We get OLDROOT from the rootfs module
+OLDROOT="/rootfs"
+
+NEWROOT="${RWMOUNT}/root"
+RWMOUNT="/overlay"
+ROMOUNT="${RWMOUNT}/rofs"
+UPPER_DIR="${RWMOUNT}/upper"
+WORK_DIR="${RWMOUNT}/work"
+
+MODULES_DIR=/init.d
+
+exit_gracefully() {
+    echo $1 >/dev/console
+    echo >/dev/console
+    echo "OverlayRoot mounting failed, starting system as read-only" >/dev/console
+    echo >/dev/console
+
+    # Make sure / is mounted as read only anyway.
+    # Borrowed from rootfs-postcommands.bbclass
+    # Tweak the mount option and fs_passno for rootfs in fstab
+    if [ -f ${OLDROOT}/etc/fstab ]; then
+        sed -i -e '/^[#[:space:]]*\/dev\/root/{s/defaults/ro/;s/\([[:space:]]*[[:digit:]]\)\([[:space:]]*\)[[:digit:]]$/\1\20/}' ${OLDROOT}/etc/fstab
+    fi
+
+    # Tweak the "mount -o remount,rw /" command in busybox-inittab inittab
+    if [ -f ${OLDROOT}/etc/inittab ]; then
+        sed -i 's|/bin/mount -o remount,rw /|/bin/mount -o remount,ro /|' ${OLDROOT}/etc/inittab
+    fi
+
+    # Continue as if the overlayroot module didn't exist
+    . $MODULES_DIR/99-finish
+    eval "finish_run"
+}
+
+
+if [ -z "$bootparam_rootrw" ]; then
+    exit_gracefully "rootrw= kernel parameter doesn't exist and its required to mount the overlayfs"
+fi
+
+mkdir -p ${RWMOUNT}
+
+# Mount RW device
+if mount -n -t ${bootparam_rootfstype:-ext4} -o ${bootparam_rootflags:-defaults} ${bootparam_rootrw} ${RWMOUNT}
+then
+    # Set up overlay directories
+    mkdir -p ${UPPER_DIR}
+    mkdir -p ${WORK_DIR}
+    mkdir -p ${NEWROOT}
+    mkdir -p ${ROMOUNT}
+
+    # Remount OLDROOT as read-only
+    mount -o bind ${OLDROOT} ${ROMOUNT}
+    mount -o remount,ro ${ROMOUNT}
+
+    # Mount RW overlay
+    mount -t overlay overlay -o lowerdir=${ROMOUNT},upperdir=${UPPER_DIR},workdir=${WORK_DIR} ${NEWROOT} || exit_gracefully "initramfs-overlayroot: Mounting overlay failed"
+else
+    exit_gracefully "initramfs-overlayroot: Mounting RW device failed"
+fi
+
+# Set up filesystems on overlay
+mkdir -p ${NEWROOT}/proc
+mkdir -p ${NEWROOT}/dev
+mkdir -p ${NEWROOT}/sys
+mkdir -p ${NEWROOT}/rofs
+
+mount -n --move ${ROMOUNT} ${NEWROOT}/rofs
+mount -n --move /proc ${NEWROOT}/proc
+mount -n --move /sys ${NEWROOT}/sys
+mount -n --move /dev ${NEWROOT}/dev
+
+exec chroot ${NEWROOT}/ ${bootparam_init:-/sbin/init} || exit_gracefully "Couldn't chroot into overlay"
diff --git a/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb b/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
index 9e8c1dc3ab..4e76e20026 100644
--- a/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
+++ b/meta/recipes-core/initrdscripts/initramfs-framework_1.0.bb
@@ -18,6 +18,7 @@  SRC_URI = "file://init \
            file://e2fs \
            file://debug \
            file://lvm \
+           file://overlayroot \
           "
 
 S = "${WORKDIR}"
@@ -49,6 +50,9 @@  do_install() {
     # lvm
     install -m 0755 ${WORKDIR}/lvm ${D}/init.d/09-lvm
 
+    # overlayroot needs to run after rootfs module but before finish
+    install -m 0755 ${WORKDIR}/overlayroot ${D}/init.d/91-overlayroot
+
     # Create device nodes expected by some kernels in initramfs
     # before even executing /init.
     install -d ${D}/dev
@@ -64,6 +68,7 @@  PACKAGES = "${PN}-base \
             initramfs-module-rootfs \
             initramfs-module-debug \
             initramfs-module-lvm \
+            initramfs-module-overlayroot \
            "
 
 FILES:${PN}-base = "/init /init.d/99-finish /dev"
@@ -107,3 +112,7 @@  FILES:initramfs-module-debug = "/init.d/00-debug"
 SUMMARY:initramfs-module-lvm = "initramfs lvm rootfs support"
 RDEPENDS:initramfs-module-lvm = "${PN}-base"
 FILES:initramfs-module-lvm = "/init.d/09-lvm"
+
+SUMMARY:initramfs-module-overlayroot = "initramfs support for mounting a RW overlay on top of a RO root filesystem"
+RDEPENDS:initramfs-module-overlayroot = "${PN}-base initramfs-module-rootfs"
+FILES:initramfs-module-overlayroot = "/init.d/91-overlayroot"