overlayfs.py 17 KB

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