diff mbox series

wic: do not ignore ROOTFS_SIZE if the rootfs is modified

Message ID 20250408205709.7013-1-twoerner@gmail.com
State New
Headers show
Series wic: do not ignore ROOTFS_SIZE if the rootfs is modified | expand

Commit Message

Trevor Woerner April 8, 2025, 8:57 p.m. UTC
If the *.wks file contains a "--source rootfs" then
lib/wic/plugins/source/rootfs.py will be invoked to generate (what is assumed
to be) the rootfs partition. If the rootfs partition needs to be tweaked or
modified, the "rootfs.py" plugin will make a copy of the filesystem and then
perform the changes on that copy. In other words, if the "--source rootfs"
line of the *.wks file also contains any of:

	--exclude-path
	--include-path
	--change-directory
	--use-label (i.e. modify etc/fstab)

then the rootfs will be copied first, then the copy is modified.

If, for example, the unmodified IMAGE_ROOTFS is:

	.../tmp/work/qemuarm64_secureboot-oe-linux/core-image-base/1.0/rootfs

then the copy would be made at:

	.../tmp/work/qemuarm64_secureboot-oe-linux/core-image-base/1.0/tmp-wic/rootfs${LINENO}

where ${LINENO} is the line number where this "--source rootfs" line appears
in the *wks file.

When it comes time to make an actual partition of a specific filesystem type,
lib/wic/partition.py::prepare_rootfs() is called. It is in this function that
wic figures out if any extra size needs to be added. The bitbake variable used
to specify the ultimate rootfs size is ROOTFS_SIZE, and since this variable is
only valid for the rootfs (and not any other partitions), the code also
verifies that the partition being created is ${IMAGE_ROOTFS}:

	rsize_bb = get_bitbake_var('ROOTFS_SIZE')
	rdir = get_bitbake_var('IMAGE_ROOTFS')
	if rsize_bb and rdir == rootfs_dir:
		<use rsize_bb>
	else:
		<calculate the partition size using "du -ks $p">

As noted above, if lib/wic/plugins/source/rootfs.py has made a copy, then the
"rdir == rootfs_dir" clause will fail and the code will assume this partition
is not a rootfs since the strings do not compare equal.

Therefore, in order to determine if this is a rootfs, retain the existing
"rdir == rootfs_dir" comparison, but also add another one to check whether or
not this is a wic-generated copy of the rootfs.

STEPS TO REPRODUCE:
	- start with the following *wks file:
		bootloader --ptable gpt
		part /boot --ondisk=vda --align 64 --size=100M --active --source bootimg-partition --fstype=ext4 --label boot --sourceparams="loader=u-boot"
		part /     --ondisk=vda                                 --source rootfs            --fstype=ext4 --label root
	- and the following extra variable in conf/local.conf:
		IMAGE_ROOTFS_EXTRA_SPACE = "500000"
	- build an image
	- run it in qemu
		$ runqemu slirp nographic serial
	- verify the root partition has extra space:
		root@qemuarm64-secureboot:~# df -h
		Filesystem                Size      Used Available Use% Mounted on
		/dev/root               721.5M     67.4M    600.6M  10% /
		devtmpfs                477.7M         0    477.7M   0% /dev
		tmpfs                    40.0K         0     40.0K   0% /mnt
		tmpfs                   489.3M     92.0K    489.2M   0% /run
		tmpfs                   489.3M     68.0K    489.2M   0% /var/volatile
		/dev/vda1               120.4M     19.9M     91.4M  18% /boot
	- modify the "/" line of the *wks file to be:
		part /     --ondisk=vda --exclude-path boot/            --source rootfs            --fstype=ext4 --label root
	- build image

	when it fails:
		root@qemuarm64-secureboot:~# df -h
		Filesystem                Size      Used Available Use% Mounted on
		/dev/root                73.4M     41.9M     25.8M  62% /
		devtmpfs                477.7M         0    477.7M   0% /dev
		tmpfs                    40.0K         0     40.0K   0% /mnt
		tmpfs                   489.3M     92.0K    489.2M   0% /run
		tmpfs                   489.3M     68.0K    489.2M   0% /var/volatile
		/dev/vda1               120.4M     19.9M     91.4M  18% /boot

	after this fix:
		root@qemuarm64-secureboot:~# df -h
		Filesystem                Size      Used Available Use% Mounted on
		/dev/root               721.5M     47.4M    620.6M   7% /
		devtmpfs                477.7M         0    477.7M   0% /dev
		tmpfs                    40.0K         0     40.0K   0% /mnt
		tmpfs                   489.3M     92.0K    489.2M   0% /run
		tmpfs                   489.3M     68.0K    489.2M   0% /var/volatile
		/dev/vda1               120.4M     19.9M     91.4M  18% /boot

Doing the math we see that the /boot partition is ~20MB and in the first image
the / partition contains this ~20MB in addition to the rest of the rootfs.
This ~20MB is completely wasted since it is used in the / partition, but then
the /boot partition is mounted on top of it, making the /boot directory of /
inaccessible. After the fix the / partition has an additional ~20MB since the
/boot portion is excluded.

Fixes [YOCTO #15555]

Signed-off-by: Trevor Woerner <twoerner@gmail.com>
---
 meta/lib/oeqa/selftest/cases/wic.py | 32 +++++++++++++++++++++++++++++
 scripts/lib/wic/partition.py        |  2 +-
 2 files changed, 33 insertions(+), 1 deletion(-)

Comments

Mathieu Dubois-Briand April 9, 2025, 2:29 p.m. UTC | #1
On Tue Apr 8, 2025 at 10:57 PM CEST, Trevor Woerner via lists.openembedded.org wrote:
> If the *.wks file contains a "--source rootfs" then
> lib/wic/plugins/source/rootfs.py will be invoked to generate (what is assumed
> to be) the rootfs partition. If the rootfs partition needs to be tweaked or
> modified, the "rootfs.py" plugin will make a copy of the filesystem and then
> perform the changes on that copy. In other words, if the "--source rootfs"
> line of the *.wks file also contains any of:
>
> 	--exclude-path
> 	--include-path
> 	--change-directory
> 	--use-label (i.e. modify etc/fstab)
>
> then the rootfs will be copied first, then the copy is modified.
>
> If, for example, the unmodified IMAGE_ROOTFS is:
>
> 	.../tmp/work/qemuarm64_secureboot-oe-linux/core-image-base/1.0/rootfs
>
> then the copy would be made at:
>
> 	.../tmp/work/qemuarm64_secureboot-oe-linux/core-image-base/1.0/tmp-wic/rootfs${LINENO}
>
> where ${LINENO} is the line number where this "--source rootfs" line appears
> in the *wks file.
>
> When it comes time to make an actual partition of a specific filesystem type,
> lib/wic/partition.py::prepare_rootfs() is called. It is in this function that
> wic figures out if any extra size needs to be added. The bitbake variable used
> to specify the ultimate rootfs size is ROOTFS_SIZE, and since this variable is
> only valid for the rootfs (and not any other partitions), the code also
> verifies that the partition being created is ${IMAGE_ROOTFS}:
>
> 	rsize_bb = get_bitbake_var('ROOTFS_SIZE')
> 	rdir = get_bitbake_var('IMAGE_ROOTFS')
> 	if rsize_bb and rdir == rootfs_dir:
> 		<use rsize_bb>
> 	else:
> 		<calculate the partition size using "du -ks $p">
>
> As noted above, if lib/wic/plugins/source/rootfs.py has made a copy, then the
> "rdir == rootfs_dir" clause will fail and the code will assume this partition
> is not a rootfs since the strings do not compare equal.
>
> Therefore, in order to determine if this is a rootfs, retain the existing
> "rdir == rootfs_dir" comparison, but also add another one to check whether or
> not this is a wic-generated copy of the rootfs.
>
> STEPS TO REPRODUCE:
> 	- start with the following *wks file:
> 		bootloader --ptable gpt
> 		part /boot --ondisk=vda --align 64 --size=100M --active --source bootimg-partition --fstype=ext4 --label boot --sourceparams="loader=u-boot"
> 		part /     --ondisk=vda                                 --source rootfs            --fstype=ext4 --label root
> 	- and the following extra variable in conf/local.conf:
> 		IMAGE_ROOTFS_EXTRA_SPACE = "500000"
> 	- build an image
> 	- run it in qemu
> 		$ runqemu slirp nographic serial
> 	- verify the root partition has extra space:
> 		root@qemuarm64-secureboot:~# df -h
> 		Filesystem                Size      Used Available Use% Mounted on
> 		/dev/root               721.5M     67.4M    600.6M  10% /
> 		devtmpfs                477.7M         0    477.7M   0% /dev
> 		tmpfs                    40.0K         0     40.0K   0% /mnt
> 		tmpfs                   489.3M     92.0K    489.2M   0% /run
> 		tmpfs                   489.3M     68.0K    489.2M   0% /var/volatile
> 		/dev/vda1               120.4M     19.9M     91.4M  18% /boot
> 	- modify the "/" line of the *wks file to be:
> 		part /     --ondisk=vda --exclude-path boot/            --source rootfs            --fstype=ext4 --label root
> 	- build image
>
> 	when it fails:
> 		root@qemuarm64-secureboot:~# df -h
> 		Filesystem                Size      Used Available Use% Mounted on
> 		/dev/root                73.4M     41.9M     25.8M  62% /
> 		devtmpfs                477.7M         0    477.7M   0% /dev
> 		tmpfs                    40.0K         0     40.0K   0% /mnt
> 		tmpfs                   489.3M     92.0K    489.2M   0% /run
> 		tmpfs                   489.3M     68.0K    489.2M   0% /var/volatile
> 		/dev/vda1               120.4M     19.9M     91.4M  18% /boot
>
> 	after this fix:
> 		root@qemuarm64-secureboot:~# df -h
> 		Filesystem                Size      Used Available Use% Mounted on
> 		/dev/root               721.5M     47.4M    620.6M   7% /
> 		devtmpfs                477.7M         0    477.7M   0% /dev
> 		tmpfs                    40.0K         0     40.0K   0% /mnt
> 		tmpfs                   489.3M     92.0K    489.2M   0% /run
> 		tmpfs                   489.3M     68.0K    489.2M   0% /var/volatile
> 		/dev/vda1               120.4M     19.9M     91.4M  18% /boot
>
> Doing the math we see that the /boot partition is ~20MB and in the first image
> the / partition contains this ~20MB in addition to the rest of the rootfs.
> This ~20MB is completely wasted since it is used in the / partition, but then
> the /boot partition is mounted on top of it, making the /boot directory of /
> inaccessible. After the fix the / partition has an additional ~20MB since the
> /boot portion is excluded.
>
> Fixes [YOCTO #15555]
>
> Signed-off-by: Trevor Woerner <twoerner@gmail.com>
> ---

Hi Trevor,

Thanks for your patch.

I do have some build issue on the autobuilder:

2025-04-09 11:14:21,897 - oe-selftest - INFO - wic.Wic.test_exclude_path_with_extra_space (subunit.RemotedTestCase)
2025-04-09 11:14:21,898 - oe-selftest - INFO -  ... FAIL
...
[Wed Apr  9 11:01:08 2025] (1309377/1309395): 192.168.7.4:53040 UA:elfutils/0.192,Linux/x86_64,/ XFF: GET /buildid/da3aea1c8160034e1b0961a7f2b5ce5c5a95553b/debuginfo 200 237848 0+9ms
ERROR: When reparsing /tmp/selftest-fetchw3a6djws/test.bb:do_checkuri, the basehash value changed from 84b9e23a83a764e7a5d8a96ee523199e2b1c604f4280a45030e248de0ff52962 to 4123da244f465feffecb2b4fd2b132b64851981ec2fb70bf9445df8b0ea6a69e. The metadata is not deterministic and this needs to be fixed.
ERROR: The following commands may help:
ERROR: $ bitbake test -cdo_checkuri -Snone
ERROR: Then:
ERROR: $ bitbake test -cdo_checkuri -Sprintdiff

ERROR: When reparsing /tmp/selftest-fetchw3a6djws/test.bb:do_fetch, the basehash value changed from 6d55d6743729b615749dc016857d7e5f9c884a8b92f0d57f68e743f4910333d3 to fa7d6ea0563b60362418d2a6c5e41a6684183d01ec578b65abd0c64a9cfba7cf. The metadata is not deterministic and this needs to be fixed.
ERROR: The following commands may help:
ERROR: $ bitbake test -cdo_fetch -Snone
ERROR: Then:
ERROR: $ bitbake test -cdo_fetch -Sprintdiff

https://autobuilder.yoctoproject.org/valkyrie/#/builders/35/builds/1329

I tried to reproduce this locally, and reproduced a fail with
"oe-selftest -r wic.Wic.test_exclude_path_with_extra_space", but with a
different error:

| INFO: Creating image(s)...
|
| DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/imager/direct.py
| DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py
| DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/empty.py
| DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/bootimg-efi.py
| DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/bootimg-pcbios.py
| DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
| DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/bootimg-partition.py
| DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/rootfs.py
| DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/rawcopy.py
| DEBUG: _exec_cmd: install -d /home/mdubois-briand/swat/oe-sefltest/poky-contrib/build-st/tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0/tmp-wic/boot.1
| DEBUG: ['install', '-d', '/home/mdubois-briand/swat/oe-sefltest/poky-contrib/build-st/tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0/tmp-wic/boot.1']
| DEBUG: _exec_cmd: output for install -d /home/mdubois-briand/swat/oe-sefltest/poky-contrib/build-st/tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0/tmp-wic/boot.1 (rc = 0):
| ERROR: No boot files defined, IMAGE_BOOT_FILES unset for entry #1

So maybe I'm missing something here.

The exact branch I used for this build should be available in
https://web.git.yoctoproject.org/poky-ci-archive/tag/?h=autobuilder.yoctoproject.org/valkyrie/a-full-1372,
but the mirror seems out of sync. I hope it will be there a bit later
today.

Can you have a look at this, please?
Trevor Woerner April 9, 2025, 4:47 p.m. UTC | #2
Hi Mathieu,

Thanks for looking at my patch!

On Wed 2025-04-09 @ 04:29:57 PM, Mathieu Dubois-Briand wrote:
> On Tue Apr 8, 2025 at 10:57 PM CEST, Trevor Woerner via lists.openembedded.org wrote:
> I do have some build issue on the autobuilder:
> 
> 2025-04-09 11:14:21,897 - oe-selftest - INFO - wic.Wic.test_exclude_path_with_extra_space (subunit.RemotedTestCase)
> 2025-04-09 11:14:21,898 - oe-selftest - INFO -  ... FAIL
> ...
> [Wed Apr  9 11:01:08 2025] (1309377/1309395): 192.168.7.4:53040 UA:elfutils/0.192,Linux/x86_64,/ XFF: GET /buildid/da3aea1c8160034e1b0961a7f2b5ce5c5a95553b/debuginfo 200 237848 0+9ms
> ERROR: When reparsing /tmp/selftest-fetchw3a6djws/test.bb:do_checkuri, the basehash value changed from 84b9e23a83a764e7a5d8a96ee523199e2b1c604f4280a45030e248de0ff52962 to 4123da244f465feffecb2b4fd2b132b64851981ec2fb70bf9445df8b0ea6a69e. The metadata is not deterministic and this needs to be fixed.
> ERROR: The following commands may help:
> ERROR: $ bitbake test -cdo_checkuri -Snone
> ERROR: Then:
> ERROR: $ bitbake test -cdo_checkuri -Sprintdiff
> 
> ERROR: When reparsing /tmp/selftest-fetchw3a6djws/test.bb:do_fetch, the basehash value changed from 6d55d6743729b615749dc016857d7e5f9c884a8b92f0d57f68e743f4910333d3 to fa7d6ea0563b60362418d2a6c5e41a6684183d01ec578b65abd0c64a9cfba7cf. The metadata is not deterministic and this needs to be fixed.
> ERROR: The following commands may help:
> ERROR: $ bitbake test -cdo_fetch -Snone
> ERROR: Then:
> ERROR: $ bitbake test -cdo_fetch -Sprintdiff
> 
> https://autobuilder.yoctoproject.org/valkyrie/#/builders/35/builds/1329

Strange. This *sounds* like what happens when I update my layers while a build
is running. I wouldn't expect the AB to do something like that in the middle
of a build?

> I tried to reproduce this locally, and reproduced a fail with
> "oe-selftest -r wic.Wic.test_exclude_path_with_extra_space", but with a
> different error:
> 
> | INFO: Creating image(s)...
> |
> | DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/imager/direct.py
> | DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py
> | DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/empty.py
> | DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/bootimg-efi.py
> | DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/bootimg-pcbios.py
> | DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
> | DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/bootimg-partition.py
> | DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/rootfs.py
> | DEBUG: loading plugin module /home/mdubois-briand/swat/oe-sefltest/poky-contrib/scripts/lib/wic/plugins/source/rawcopy.py
> | DEBUG: _exec_cmd: install -d /home/mdubois-briand/swat/oe-sefltest/poky-contrib/build-st/tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0/tmp-wic/boot.1
> | DEBUG: ['install', '-d', '/home/mdubois-briand/swat/oe-sefltest/poky-contrib/build-st/tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0/tmp-wic/boot.1']
> | DEBUG: _exec_cmd: output for install -d /home/mdubois-briand/swat/oe-sefltest/poky-contrib/build-st/tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0/tmp-wic/boot.1 (rc = 0):
> | ERROR: No boot files defined, IMAGE_BOOT_FILES unset for entry #1
> 
> So maybe I'm missing something here.
> 
> The exact branch I used for this build should be available in
> https://web.git.yoctoproject.org/poky-ci-archive/tag/?h=autobuilder.yoctoproject.org/valkyrie/a-full-1372,
> but the mirror seems out of sync. I hope it will be there a bit later
> today.
> 
> Can you have a look at this, please?

This is my first oe-selftest, so I'm not entirely surprised. It succeeded for
me, I thought I had it figured out, but I've probably done something wrong in
my testing.

Thanks, I'll take another look at this with your branch.

Best regards,
	Trevor
diff mbox series

Patch

diff --git a/meta/lib/oeqa/selftest/cases/wic.py b/meta/lib/oeqa/selftest/cases/wic.py
index 59fd99a78836..67f938351bde 100644
--- a/meta/lib/oeqa/selftest/cases/wic.py
+++ b/meta/lib/oeqa/selftest/cases/wic.py
@@ -535,6 +535,38 @@  part /mnt --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/whoa
         finally:
             os.environ['PATH'] = oldpath
 
+    def test_exclude_path_with_extra_space(self):
+        """Test having --exclude-path with IMAGE_ROOTFS_EXTRA_SPACE. [Yocto #15555]"""
+
+        with NamedTemporaryFile("w", suffix=".wks") as wks:
+            wks.writelines(
+                ['bootloader --ptable gpt\n',
+                 'part /boot --size=100M --active --source bootimg-partition --fstype=ext4 --label boot\n',
+                 'part /     --exclude-path boot/ --source rootfs            --fstype=ext4 --label root\n'])
+            wks.flush()
+            config = 'IMAGE_ROOTFS_EXTRA_SPACE = "500000"\n'\
+                     'IMAGE_FSTYPES += "wic"\n'\
+                     'WKS_FILE = "%s"\n' % wks.name
+            self.append_config(config)
+            bitbake('core-image-minimal')
+
+        """
+        the output of "wic ls <image>.wic" will look something like:
+            Num     Start        End          Size      Fstype
+             1         17408    136332287    136314880  ext4
+             2     136332288    171464703     35132416  ext4
+        we are looking for the size of partition 2
+        i.e. in this case the number 35,132,416
+        without the fix the size will be around 85,403,648
+        with the fix the size should be around 799,960,064
+        """
+        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'MACHINE'], 'core-image-minimal')
+        deploy_dir = bb_vars['DEPLOY_DIR_IMAGE']
+        machine = bb_vars['MACHINE']
+        wicout = glob(os.path.join(deploy_dir, "core-image-minimal-%s.rootfs-*.wic" % machine))[0]
+        size_of_root_partition = int(runCmd("wic ls %s" % wicout).output.split('\n')[2].split()[3])
+        self.assertGreater(size_of_root_partition, 500000000)
+
     def test_include_path(self):
         """Test --include-path wks option."""
 
diff --git a/scripts/lib/wic/partition.py b/scripts/lib/wic/partition.py
index bf2c34d5940f..b18431d8fb84 100644
--- a/scripts/lib/wic/partition.py
+++ b/scripts/lib/wic/partition.py
@@ -244,7 +244,7 @@  class Partition():
             # from bitbake variable
             rsize_bb = get_bitbake_var('ROOTFS_SIZE')
             rdir = get_bitbake_var('IMAGE_ROOTFS')
-            if rsize_bb and rdir == rootfs_dir:
+            if rsize_bb and (rdir == rootfs_dir or (rootfs_dir.split('/')[-2] == "tmp-wic" and rootfs_dir.split('/')[-1][:6] == "rootfs")):
                 # Bitbake variable ROOTFS_SIZE is calculated in
                 # Image._get_rootfs_size method from meta/lib/oe/image.py
                 # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT,