1# 2# Copyright OpenEmbedded Contributors 3# 4# SPDX-License-Identifier: MIT 5# 6 7from oeqa.selftest.case import OESelftestTestCase 8from oeqa.utils.commands import bitbake, runqemu 9from oeqa.core.decorator import OETestTag 10from oeqa.core.decorator.data import skipIfNotMachine 11 12def getline_qemu(out, line): 13 for l in out.split('\n'): 14 if line in l: 15 return l 16 17def getline(res, line): 18 return getline_qemu(res.output, line) 19 20class OverlayFSTests(OESelftestTestCase): 21 """Overlayfs class usage tests""" 22 23 def add_overlay_conf_to_machine(self): 24 machine_inc = """ 25OVERLAYFS_MOUNT_POINT[mnt-overlay] = "/mnt/overlay" 26""" 27 self.set_machine_config(machine_inc) 28 29 def test_distro_features_missing(self): 30 """ 31 Summary: Check that required DISTRO_FEATURES are set 32 Expected: Fail when either systemd or overlayfs are not in DISTRO_FEATURES 33 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 34 """ 35 36 config = """ 37IMAGE_INSTALL:append = " overlayfs-user" 38""" 39 overlayfs_recipe_append = """ 40inherit overlayfs 41""" 42 self.write_config(config) 43 self.add_overlay_conf_to_machine() 44 self.write_recipeinc('overlayfs-user', overlayfs_recipe_append) 45 46 res = bitbake('core-image-minimal', ignore_status=True) 47 line = getline(res, "overlayfs-user was skipped: missing required distro features") 48 self.assertTrue("overlayfs" in res.output, msg=res.output) 49 self.assertTrue("systemd" in res.output, msg=res.output) 50 self.assertTrue("ERROR: Required build target 'core-image-minimal' has no buildable providers." in res.output, msg=res.output) 51 52 def test_not_all_units_installed(self): 53 """ 54 Summary: Test QA check that we have required mount units in the image 55 Expected: Fail because mount unit for overlay partition is not installed 56 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 57 """ 58 59 config = """ 60IMAGE_INSTALL:append = " overlayfs-user" 61DISTRO_FEATURES:append = " systemd overlayfs usrmerge" 62""" 63 64 self.write_config(config) 65 self.add_overlay_conf_to_machine() 66 67 res = bitbake('core-image-minimal', ignore_status=True) 68 line = getline(res, " Mount path /mnt/overlay not found in fstab and unit mnt-overlay.mount not found in systemd unit directories") 69 self.assertTrue(line and line.startswith("WARNING:"), msg=res.output) 70 line = getline(res, "Not all mount paths and units are installed in the image") 71 self.assertTrue(line and line.startswith("ERROR:"), msg=res.output) 72 73 def test_not_all_units_installed_but_qa_skipped(self): 74 """ 75 Summary: Test skipping the QA check 76 Expected: Image is created successfully 77 Author: Claudius Heine <ch@denx.de> 78 """ 79 80 config = """ 81IMAGE_INSTALL:append = " overlayfs-user" 82DISTRO_FEATURES:append = " systemd overlayfs usrmerge" 83OVERLAYFS_QA_SKIP[mnt-overlay] = "mount-configured" 84""" 85 86 self.write_config(config) 87 self.add_overlay_conf_to_machine() 88 89 bitbake('core-image-minimal') 90 91 def test_mount_unit_not_set(self): 92 """ 93 Summary: Test whether mount unit was set properly 94 Expected: Fail because mount unit was not set 95 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 96 """ 97 98 config = """ 99IMAGE_INSTALL:append = " overlayfs-user" 100DISTRO_FEATURES:append = " systemd overlayfs usrmerge" 101""" 102 103 self.write_config(config) 104 105 res = bitbake('core-image-minimal', ignore_status=True) 106 line = getline(res, "A recipe uses overlayfs class but there is no OVERLAYFS_MOUNT_POINT set in your MACHINE configuration") 107 self.assertTrue(line and line.startswith("Parsing recipes...ERROR:"), msg=res.output) 108 109 def test_wrong_mount_unit_set(self): 110 """ 111 Summary: Test whether mount unit was set properly 112 Expected: Fail because not the correct flag used for mount unit 113 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 114 """ 115 116 config = """ 117IMAGE_INSTALL:append = " overlayfs-user" 118DISTRO_FEATURES:append = " systemd overlayfs usrmerge" 119""" 120 121 wrong_machine_config = """ 122OVERLAYFS_MOUNT_POINT[usr-share-overlay] = "/usr/share/overlay" 123""" 124 125 self.write_config(config) 126 self.set_machine_config(wrong_machine_config) 127 128 res = bitbake('core-image-minimal', ignore_status=True) 129 line = getline(res, "Missing required mount point for OVERLAYFS_MOUNT_POINT[mnt-overlay] in your MACHINE configuration") 130 self.assertTrue(line and line.startswith("Parsing recipes...ERROR:"), msg=res.output) 131 132 def _test_correct_image(self, recipe, data): 133 """ 134 Summary: Check that we can create an image when all parameters are 135 set correctly 136 Expected: Image is created successfully 137 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 138 """ 139 140 config = """ 141IMAGE_INSTALL:append = " overlayfs-user systemd-machine-units" 142DISTRO_FEATURES:append = " overlayfs" 143 144# Use systemd as init manager 145INIT_MANAGER = "systemd" 146 147# enable overlayfs in the kernel 148KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc" 149""" 150 151 overlayfs_recipe_append = """ 152OVERLAYFS_WRITABLE_PATHS[mnt-overlay] += "/usr/share/another-overlay-mount" 153 154SYSTEMD_SERVICE:${PN} += " \ 155 my-application.service \ 156" 157 158do_install:append() { 159 install -d ${D}${systemd_system_unitdir} 160 cat <<EOT > ${D}${systemd_system_unitdir}/my-application.service 161[Unit] 162Description=Sample application start-up unit 163After=overlayfs-user-overlays.service 164Requires=overlayfs-user-overlays.service 165 166[Service] 167Type=oneshot 168ExecStart=/bin/true 169RemainAfterExit=true 170 171[Install] 172WantedBy=multi-user.target 173EOT 174} 175""" 176 177 self.write_config(config) 178 self.add_overlay_conf_to_machine() 179 self.write_recipeinc(recipe, data) 180 self.write_recipeinc('overlayfs-user', overlayfs_recipe_append) 181 182 bitbake('core-image-minimal') 183 184 with runqemu('core-image-minimal') as qemu: 185 # Check that application service started 186 status, output = qemu.run_serial("systemctl status my-application") 187 self.assertTrue("active (exited)" in output, msg=output) 188 189 # Check that overlay mounts are dependencies of our application unit 190 status, output = qemu.run_serial("systemctl list-dependencies my-application") 191 self.assertTrue("overlayfs-user-overlays.service" in output, msg=output) 192 193 status, output = qemu.run_serial("systemctl list-dependencies overlayfs-user-overlays") 194 self.assertTrue("usr-share-another\\x2doverlay\\x2dmount.mount" in output, msg=output) 195 self.assertTrue("usr-share-my\\x2dapplication.mount" in output, msg=output) 196 197 # Check that we have /mnt/overlay fs mounted as tmpfs and 198 # /usr/share/my-application as an overlay (see overlayfs-user recipe) 199 status, output = qemu.run_serial("/bin/mount -t tmpfs,overlay") 200 201 line = getline_qemu(output, "on /mnt/overlay") 202 self.assertTrue(line and line.startswith("tmpfs"), msg=output) 203 204 line = getline_qemu(output, "upperdir=/mnt/overlay/upper/usr/share/my-application") 205 self.assertTrue(line and line.startswith("overlay"), msg=output) 206 207 line = getline_qemu(output, "upperdir=/mnt/overlay/upper/usr/share/another-overlay-mount") 208 self.assertTrue(line and line.startswith("overlay"), msg=output) 209 210 @OETestTag("runqemu") 211 def test_correct_image_fstab(self): 212 """ 213 Summary: Check that we can create an image when all parameters are 214 set correctly via fstab 215 Expected: Image is created successfully 216 Author: Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com> 217 """ 218 219 base_files_append = """ 220do_install:append() { 221 cat <<EOT >> ${D}${sysconfdir}/fstab 222tmpfs /mnt/overlay tmpfs mode=1777,strictatime,nosuid,nodev 0 0 223EOT 224} 225""" 226 227 self._test_correct_image('base-files', base_files_append) 228 229 @OETestTag("runqemu") 230 def test_correct_image_unit(self): 231 """ 232 Summary: Check that we can create an image when all parameters are 233 set correctly via mount unit 234 Expected: Image is created successfully 235 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 236 """ 237 238 systemd_machine_unit_append = """ 239SYSTEMD_SERVICE:${PN} += " \ 240 mnt-overlay.mount \ 241" 242 243do_install:append() { 244 install -d ${D}${systemd_system_unitdir} 245 cat <<EOT > ${D}${systemd_system_unitdir}/mnt-overlay.mount 246[Unit] 247Description=Tmpfs directory 248DefaultDependencies=no 249 250[Mount] 251What=tmpfs 252Where=/mnt/overlay 253Type=tmpfs 254Options=mode=1777,strictatime,nosuid,nodev 255 256[Install] 257WantedBy=multi-user.target 258EOT 259} 260 261""" 262 263 self._test_correct_image('systemd-machine-units', systemd_machine_unit_append) 264 265@OETestTag("runqemu") 266class OverlayFSEtcRunTimeTests(OESelftestTestCase): 267 """overlayfs-etc class tests""" 268 269 def test_all_required_variables_set(self): 270 """ 271 Summary: Check that required variables are set 272 Expected: Fail when any of required variables is missing 273 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 274 """ 275 276 configBase = """ 277# Use systemd as init manager 278INIT_MANAGER = "systemd" 279 280# enable overlayfs in the kernel 281KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc" 282 283# Image configuration for overlayfs-etc 284EXTRA_IMAGE_FEATURES += "overlayfs-etc" 285IMAGE_FEATURES:remove = "package-management" 286""" 287 configMountPoint = """ 288OVERLAYFS_ETC_MOUNT_POINT = "/data" 289""" 290 configDevice = """ 291OVERLAYFS_ETC_DEVICE = "/dev/mmcblk0p1" 292""" 293 294 self.write_config(configBase) 295 res = bitbake('core-image-minimal', ignore_status=True) 296 line = getline(res, "OVERLAYFS_ETC_MOUNT_POINT must be set in your MACHINE configuration") 297 self.assertTrue(line, msg=res.output) 298 299 self.append_config(configMountPoint) 300 res = bitbake('core-image-minimal', ignore_status=True) 301 line = getline(res, "OVERLAYFS_ETC_DEVICE must be set in your MACHINE configuration") 302 self.assertTrue(line, msg=res.output) 303 304 self.append_config(configDevice) 305 res = bitbake('core-image-minimal', ignore_status=True) 306 line = getline(res, "OVERLAYFS_ETC_FSTYPE should contain a valid file system type on /dev/mmcblk0p1") 307 self.assertTrue(line, msg=res.output) 308 309 def test_image_feature_conflict(self): 310 """ 311 Summary: Overlayfs-etc is not allowed to be used with package-management 312 Expected: Feature conflict 313 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 314 """ 315 316 config = """ 317# Use systemd as init manager 318INIT_MANAGER = "systemd" 319 320# enable overlayfs in the kernel 321KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc" 322EXTRA_IMAGE_FEATURES += "overlayfs-etc" 323EXTRA_IMAGE_FEATURES += "package-management" 324""" 325 326 self.write_config(config) 327 328 res = bitbake('core-image-minimal', ignore_status=True) 329 line = getline(res, "contains conflicting IMAGE_FEATURES") 330 self.assertTrue("overlayfs-etc" in res.output, msg=res.output) 331 self.assertTrue("package-management" in res.output, msg=res.output) 332 333 # https://bugzilla.yoctoproject.org/show_bug.cgi?id=14963 334 @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently") 335 def test_image_feature_is_missing(self): 336 """ 337 Summary: Overlayfs-etc class is not applied when image feature is not set 338 Expected: Image is created successfully but /etc is not an overlay 339 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 340 """ 341 342 config = """ 343# Use systemd as init manager 344INIT_MANAGER = "systemd" 345 346# enable overlayfs in the kernel 347KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc" 348 349IMAGE_FSTYPES += "wic" 350WKS_FILE = "overlayfs_etc.wks.in" 351 352EXTRA_IMAGE_FEATURES += "read-only-rootfs" 353# Image configuration for overlayfs-etc 354OVERLAYFS_ETC_MOUNT_POINT = "/data" 355OVERLAYFS_ETC_DEVICE = "/dev/sda3" 356OVERLAYFS_ROOTFS_TYPE = "ext4" 357""" 358 359 self.write_config(config) 360 361 bitbake('core-image-minimal') 362 363 with runqemu('core-image-minimal', image_fstype='wic') as qemu: 364 status, output = qemu.run_serial("/bin/mount") 365 366 line = getline_qemu(output, "upperdir=/data/overlay-etc/upper") 367 self.assertFalse(line, msg=output) 368 369 @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently") 370 def test_sbin_init_preinit(self): 371 self.run_sbin_init(False, "ext4") 372 373 @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently") 374 def test_sbin_init_original(self): 375 self.run_sbin_init(True, "ext4") 376 377 @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently") 378 def test_sbin_init_read_only(self): 379 self.run_sbin_init(True, "squashfs") 380 381 def run_sbin_init(self, origInit, rootfsType): 382 """ 383 Summary: Confirm we can replace original init and mount overlay on top of /etc 384 Expected: Image is created successfully and /etc is mounted as an overlay 385 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 386 """ 387 388 config = self.get_working_config() 389 390 args = { 391 'OVERLAYFS_INIT_OPTION': "" if origInit else "init=/sbin/preinit", 392 'OVERLAYFS_ETC_USE_ORIG_INIT_NAME': int(origInit == True), 393 'OVERLAYFS_ROOTFS_TYPE': rootfsType, 394 'OVERLAYFS_ETC_CREATE_MOUNT_DIRS': int(rootfsType == "ext4") 395 } 396 397 self.write_config(config.format(**args)) 398 399 bitbake('core-image-minimal') 400 testFile = "/etc/my-test-data" 401 402 with runqemu('core-image-minimal', image_fstype='wic', discard_writes=False) as qemu: 403 status, output = qemu.run_serial("/bin/mount") 404 405 line = getline_qemu(output, "/dev/sda3") 406 self.assertTrue("/data" in output, msg=output) 407 408 line = getline_qemu(output, "upperdir=/data/overlay-etc/upper") 409 self.assertTrue(line and line.startswith("/data/overlay-etc/upper on /etc type overlay"), msg=output) 410 411 # check that lower layer is not available 412 status, output = qemu.run_serial("ls -1 /data/overlay-etc/lower") 413 line = getline_qemu(output, "No such file or directory") 414 self.assertTrue(line, msg=output) 415 416 status, output = qemu.run_serial("touch " + testFile) 417 status, output = qemu.run_serial("sync") 418 status, output = qemu.run_serial("ls -1 " + testFile) 419 line = getline_qemu(output, testFile) 420 self.assertTrue(line and line.startswith(testFile), msg=output) 421 422 # Check that file exists in /etc after reboot 423 with runqemu('core-image-minimal', image_fstype='wic') as qemu: 424 status, output = qemu.run_serial("ls -1 " + testFile) 425 line = getline_qemu(output, testFile) 426 self.assertTrue(line and line.startswith(testFile), msg=output) 427 428 @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently") 429 def test_lower_layer_access(self): 430 """ 431 Summary: Test that lower layer of /etc is available read-only when configured 432 Expected: Can't write to lower layer. The files on lower and upper different after 433 modification 434 Author: Vyacheslav Yurkov <uvv.mail@gmail.com> 435 """ 436 437 config = self.get_working_config() 438 439 configLower = """ 440OVERLAYFS_ETC_EXPOSE_LOWER = "1" 441IMAGE_INSTALL:append = " overlayfs-user" 442""" 443 testFile = "lower-layer-test.txt" 444 445 args = { 446 'OVERLAYFS_INIT_OPTION': "", 447 'OVERLAYFS_ETC_USE_ORIG_INIT_NAME': 1, 448 'OVERLAYFS_ROOTFS_TYPE': "ext4", 449 'OVERLAYFS_ETC_CREATE_MOUNT_DIRS': 1 450 } 451 452 self.write_config(config.format(**args)) 453 454 self.append_config(configLower) 455 bitbake('core-image-minimal') 456 457 with runqemu('core-image-minimal', image_fstype='wic') as qemu: 458 status, output = qemu.run_serial("echo \"Modified in upper\" > /etc/" + testFile) 459 status, output = qemu.run_serial("diff /etc/" + testFile + " /data/overlay-etc/lower/" + testFile) 460 line = getline_qemu(output, "Modified in upper") 461 self.assertTrue(line, msg=output) 462 line = getline_qemu(output, "Original file") 463 self.assertTrue(line, msg=output) 464 465 status, output = qemu.run_serial("touch /data/overlay-etc/lower/ro-test.txt") 466 line = getline_qemu(output, "Read-only file system") 467 self.assertTrue(line, msg=output) 468 469 def get_working_config(self): 470 return """ 471# Use systemd as init manager 472INIT_MANAGER = "systemd" 473 474# enable overlayfs in the kernel 475KERNEL_EXTRA_FEATURES:append = " \ 476 features/overlayfs/overlayfs.scc \ 477 cfg/fs/squashfs.scc" 478 479IMAGE_FSTYPES += "wic" 480OVERLAYFS_INIT_OPTION = "{OVERLAYFS_INIT_OPTION}" 481OVERLAYFS_ROOTFS_TYPE = "{OVERLAYFS_ROOTFS_TYPE}" 482OVERLAYFS_ETC_CREATE_MOUNT_DIRS = "{OVERLAYFS_ETC_CREATE_MOUNT_DIRS}" 483WKS_FILE = "overlayfs_etc.wks.in" 484 485EXTRA_IMAGE_FEATURES += "read-only-rootfs" 486# Image configuration for overlayfs-etc 487EXTRA_IMAGE_FEATURES += "overlayfs-etc" 488IMAGE_FEATURES:remove = "package-management" 489OVERLAYFS_ETC_MOUNT_POINT = "/data" 490OVERLAYFS_ETC_FSTYPE = "ext4" 491OVERLAYFS_ETC_DEVICE = "/dev/sda3" 492OVERLAYFS_ETC_USE_ORIG_INIT_NAME = "{OVERLAYFS_ETC_USE_ORIG_INIT_NAME}" 493 494ROOTFS_POSTPROCESS_COMMAND += "{OVERLAYFS_ROOTFS_TYPE}_rootfs" 495 496ext4_rootfs() {{ 497}} 498 499squashfs_rootfs() {{ 500 mkdir -p ${{IMAGE_ROOTFS}}/data 501}} 502""" 503