[v2] initramfs-framework: Add overlayroot module

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

Commit Message

Alejandro Enedino Hernandez Samaniego Jan. 29, 2022, 7:48 p.m. UTC
When installed, this module mounts a read-write (RW) overlay on
top of a root filesystem, which is kept read-only (RO), free
from modifications by the user, this might prove to be useful
if we want to access or restore the original unmodified rootfs.

The existing overlay-etc.bbclass does something similar, it
mounts an overlay on top of the /etc directory, however doing
the same for root causes the original root to be inaccessible
once the system is booted, hence why this module is added to
the initramfs boot flow, allowing us to mount the RW overlay,
while keeping the original rootfs mounted at /rofs once the
system finishes booting. This script is loosely based on that
class.

This module 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.

The module 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.
After overlayroot is executed the usual boot flow continues from
the real init process.

If something goes wrong while running this module, the rootfs
is still mounted RO (with no overlay) and the finish module is
executed to continue booting normally.

Its worth noting that, on purpose, this isnt installed by default
on any images that use initramfs-framework to keep the boot flow
unmodified, only when a user manually requests to install it,
then it becomes functional.

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

Comments

Vyacheslav Yurkov Jan. 31, 2022, 8:36 p.m. UTC | #1
Hi Alejandro,
Thanks for the v2, but my questions from v1 are still left unanswered.

1. Do you really need the whole rootfs to be in overlay? Perhaps you 
have another use case in mind, but the more scope overlay has, the more 
migration effort you need in order to update upper layer on image upgrade.
2. I was thinking if perhaps that would be better to extend 
files/overlayfs-etc-preinit.sh.in to suit your needs and re-use common 
parts rather then having a good part of it copied to initramfs-framework?

Cheers,
Vyacheslav

On 29.01.2022 20:48, 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), free
> from modifications by the user, this might prove to be useful
> if we want to access or restore the original unmodified rootfs.
>
> The existing overlay-etc.bbclass does something similar, it
> mounts an overlay on top of the /etc directory, however doing
> the same for root causes the original root to be inaccessible
> once the system is booted, hence why this module is added to
> the initramfs boot flow, allowing us to mount the RW overlay,
> while keeping the original rootfs mounted at /rofs once the
> system finishes booting. This script is loosely based on that
> class.
>
> This module 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.
>
> The module 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.
> After overlayroot is executed the usual boot flow continues from
> the real init process.
>
> If something goes wrong while running this module, the rootfs
> is still mounted RO (with no overlay) and the finish module is
> executed to continue booting normally.
>
> Its worth noting that, on purpose, this isnt installed by default
> on any images that use initramfs-framework to keep the boot flow
> unmodified, only when a user manually requests to install it,
> then it becomes functional.
>
> Signed-off-by: Alejandro Enedino Hernandez Samaniego <alhe@linux.microsoft.com>
> ---
>   .../initramfs-framework/overlayroot           | 112 ++++++++++++++++++
>   .../initrdscripts/initramfs-framework_1.0.bb  |   9 ++
>   2 files changed, 121 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..d40342dc59
> --- /dev/null
> +++ b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
> @@ -0,0 +1,112 @@
> +#!/bin/sh
> +
> +# SPDX-License-Identifier: MIT
> +#
> +# Copyright 2022 (C), Microsoft Corporation
> +
> +# Simple initramfs module intended to mount a read-write (RW)
> +# overlayfs on top of /, keeping the original root filesystem
> +# as read-only (RO), free from modifications by the user.
> +#
> +# NOTE: The read-only IMAGE_FEATURE is not required for this to work
> +#
> +# This script is based on the overlay-etc.bbclass, which sets up
> +# an overlay on top of the /etc directory, but in this case allows
> +# accessing the original, unmodified rootfs at /rofs after boot.
> +#
> +# 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.
> +#
> +# This module 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.
> +# After overlayroot is executed the usual boot flow continues from
> +# the real init process.
> +#
> +# If something goes wrong while running this module, the rootfs
> +# is still mounted RO (with no overlay) and the finish module is
> +# executed to continue booting normally.
> +#
> +# It also has a dependency on overlayfs being enabled in the
> +# running kernel via KERNEL_FEATURES (kmeta) or any other means.
> +
> +
> +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
> +
> +# Something went wrong, make sure / is mounted as read only anyway.
> +exit_gracefully() {
> +    echo $1 >/dev/console
> +    echo >/dev/console
> +    echo "OverlayRoot mounting failed, starting system as read-only" >/dev/console
> +    echo >/dev/console
> +
> +    # The following is borrowed from rootfs-postcommands.bbclass
> +    # This basically looks at the real rootfs mounting options and
> +    # replaces them with "ro"
> +
> +    # 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 to continue booting
> +    . $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"
Alejandro Enedino Hernandez Samaniego Feb. 2, 2022, 6:58 a.m. UTC | #2
Hey Vyacheslav,

Yes, in this case it is different and its specifically required if we 
want to overlay the entire rootfs, also like it says in the commit 
message and in the comments giving credit to the overlay-etc class I 
definitely tried extending the overlay-etc class but it simply does not 
work for this, since the rootfs becomes inaccessible once the system has 
booted, hence why this has to be done inside initramfs, this isn't meant 
to replace the overlay-etc class in any way, just to provide an 
alternative if it fits the user needs better.

Also, my apologies, your reply to my v1 somehow slipped through my 
filter and I hadn't seen it.

I think they are simply two different scenarios, I can see the potential 
the overlay-etc class has to be extended for other directories for 
example, but it is a different use case and boot flow so I believe both 
can co-exist for different workflows depending on the user needs.

Cheers,

Alejandro

On 1/31/22 20:36, Vyacheslav Yurkov wrote:
> Hi Alejandro,
> Thanks for the v2, but my questions from v1 are still left unanswered.
>
> 1. Do you really need the whole rootfs to be in overlay? Perhaps you 
> have another use case in mind, but the more scope overlay has, the 
> more migration effort you need in order to update upper layer on image 
> upgrade.
> 2. I was thinking if perhaps that would be better to extend 
> files/overlayfs-etc-preinit.sh.in to suit your needs and re-use common 
> parts rather then having a good part of it copied to initramfs-framework?
>
> Cheers,
> Vyacheslav
>
> On 29.01.2022 20:48, 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), free
>> from modifications by the user, this might prove to be useful
>> if we want to access or restore the original unmodified rootfs.
>>
>> The existing overlay-etc.bbclass does something similar, it
>> mounts an overlay on top of the /etc directory, however doing
>> the same for root causes the original root to be inaccessible
>> once the system is booted, hence why this module is added to
>> the initramfs boot flow, allowing us to mount the RW overlay,
>> while keeping the original rootfs mounted at /rofs once the
>> system finishes booting. This script is loosely based on that
>> class.
>>
>> This module 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.
>>
>> The module 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.
>> After overlayroot is executed the usual boot flow continues from
>> the real init process.
>>
>> If something goes wrong while running this module, the rootfs
>> is still mounted RO (with no overlay) and the finish module is
>> executed to continue booting normally.
>>
>> Its worth noting that, on purpose, this isnt installed by default
>> on any images that use initramfs-framework to keep the boot flow
>> unmodified, only when a user manually requests to install it,
>> then it becomes functional.
>>
>> Signed-off-by: Alejandro Enedino Hernandez Samaniego 
>> <alhe@linux.microsoft.com>
>> ---
>>   .../initramfs-framework/overlayroot           | 112 ++++++++++++++++++
>>   .../initrdscripts/initramfs-framework_1.0.bb  |   9 ++
>>   2 files changed, 121 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..d40342dc59
>> --- /dev/null
>> +++ b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
>> @@ -0,0 +1,112 @@
>> +#!/bin/sh
>> +
>> +# SPDX-License-Identifier: MIT
>> +#
>> +# Copyright 2022 (C), Microsoft Corporation
>> +
>> +# Simple initramfs module intended to mount a read-write (RW)
>> +# overlayfs on top of /, keeping the original root filesystem
>> +# as read-only (RO), free from modifications by the user.
>> +#
>> +# NOTE: The read-only IMAGE_FEATURE is not required for this to work
>> +#
>> +# This script is based on the overlay-etc.bbclass, which sets up
>> +# an overlay on top of the /etc directory, but in this case allows
>> +# accessing the original, unmodified rootfs at /rofs after boot.
>> +#
>> +# 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.
>> +#
>> +# This module 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.
>> +# After overlayroot is executed the usual boot flow continues from
>> +# the real init process.
>> +#
>> +# If something goes wrong while running this module, the rootfs
>> +# is still mounted RO (with no overlay) and the finish module is
>> +# executed to continue booting normally.
>> +#
>> +# It also has a dependency on overlayfs being enabled in the
>> +# running kernel via KERNEL_FEATURES (kmeta) or any other means.
>> +
>> +
>> +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
>> +
>> +# Something went wrong, make sure / is mounted as read only anyway.
>> +exit_gracefully() {
>> +    echo $1 >/dev/console
>> +    echo >/dev/console
>> +    echo "OverlayRoot mounting failed, starting system as read-only" 
>> >/dev/console
>> +    echo >/dev/console
>> +
>> +    # The following is borrowed from rootfs-postcommands.bbclass
>> +    # This basically looks at the real rootfs mounting options and
>> +    # replaces them with "ro"
>> +
>> +    # 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 to continue 
>> booting
>> +    . $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"
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#161137):https://lists.openembedded.org/g/openembedded-core/message/161137
> Mute This Topic:https://lists.openembedded.org/mt/88773634/4354175
> Group Owner:openembedded-core+owner@lists.openembedded.org
> Unsubscribe:https://lists.openembedded.org/g/openembedded-core/unsub  [alhe@linux.microsoft.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
Vyacheslav Yurkov Feb. 2, 2022, 8:15 a.m. UTC | #3
Hi Alejandro

On 02.02.2022 07:58, Alejandro Hernandez wrote:
>
> Hey Vyacheslav,
>
> ...I definitely tried extending the overlay-etc class but it simply 
> does not work for this, since the rootfs becomes inaccessible once the 
> system has booted, ...
>

That's what I meant. What if we added another parameter, let's say 
OVERLAY_OLDROOT_MOUNT. If it's set, then lower layer can bind mounted to 
that mount point. In that case -etc part of overlayfs-etc has to be 
changed for something else, of course, to make it more general purpose 
class. Perhaps overlayfs.bbclass can be renamed to 
overlayfs-systemd.bbclass then?
What do you think?

> ... but it is a different use case and boot flow so I believe both can 
> co-exist for different workflows depending on the user needs...

My concern here is code duplication :) If we find an issue in one place, 
we would need to change it in two places.

Thanks,
Vyacheslav

> Cheers,
>
> Alejandro
>
Alejandro Enedino Hernandez Samaniego Feb. 2, 2022, 6:35 p.m. UTC | #4
On 2/2/22 08:15, Vyacheslav Yurkov wrote:
> Hi Alejandro
>
> On 02.02.2022 07:58, Alejandro Hernandez wrote:
>>
>> Hey Vyacheslav,
>>
>> ...I definitely tried extending the overlay-etc class but it simply 
>> does not work for this, since the rootfs becomes inaccessible once 
>> the system has booted, ...
>>
>
> That's what I meant. What if we added another parameter, let's say 
> OVERLAY_OLDROOT_MOUNT. If it's set, then lower layer can bind mounted 
> to that mount point. In that case -etc part of overlayfs-etc has to be 
> changed for something else, of course, to make it more general purpose 
> class. Perhaps overlayfs.bbclass can be renamed to 
> overlayfs-systemd.bbclass then?
> What do you think?
>
This is precisely the way I implemented it first, I wanted to use the 
overlay-etc class as general purpose, but sadly that doesn't work for 
this flow as its explained in the script comments, using it in that way, 
the / overlay can be mounted, however its impossible to access its 
original contents once we chroot in the overlay, hence why this belongs 
in initramfs-framework as a separate feature from the class, the 
intention is not to replace the class which provides different 
functionality.


>> ... but it is a different use case and boot flow so I believe both 
>> can co-exist for different workflows depending on the user needs...
>
> My concern here is code duplication :) If we find an issue in one 
> place, we would need to change it in two places.
>
> Thanks,
> Vyacheslav
>
>> Cheers,
>>
>> Alejandro
>>
>

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..d40342dc59
--- /dev/null
+++ b/meta/recipes-core/initrdscripts/initramfs-framework/overlayroot
@@ -0,0 +1,112 @@ 
+#!/bin/sh
+
+# SPDX-License-Identifier: MIT
+#
+# Copyright 2022 (C), Microsoft Corporation
+
+# Simple initramfs module intended to mount a read-write (RW)
+# overlayfs on top of /, keeping the original root filesystem
+# as read-only (RO), free from modifications by the user.
+#
+# NOTE: The read-only IMAGE_FEATURE is not required for this to work
+#
+# This script is based on the overlay-etc.bbclass, which sets up
+# an overlay on top of the /etc directory, but in this case allows
+# accessing the original, unmodified rootfs at /rofs after boot.
+#
+# 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.
+#
+# This module 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.
+# After overlayroot is executed the usual boot flow continues from
+# the real init process.
+#
+# If something goes wrong while running this module, the rootfs
+# is still mounted RO (with no overlay) and the finish module is
+# executed to continue booting normally.
+#
+# It also has a dependency on overlayfs being enabled in the
+# running kernel via KERNEL_FEATURES (kmeta) or any other means.
+
+
+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
+
+# Something went wrong, make sure / is mounted as read only anyway.
+exit_gracefully() {
+    echo $1 >/dev/console
+    echo >/dev/console
+    echo "OverlayRoot mounting failed, starting system as read-only" >/dev/console
+    echo >/dev/console
+
+    # The following is borrowed from rootfs-postcommands.bbclass
+    # This basically looks at the real rootfs mounting options and
+    # replaces them with "ro"
+
+    # 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 to continue booting
+    . $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"