diff --git a/docs/manual/atomic-upgrades.md b/docs/manual/atomic-upgrades.md index 341372d0..890617cf 100644 --- a/docs/manual/atomic-upgrades.md +++ b/docs/manual/atomic-upgrades.md @@ -95,7 +95,7 @@ collected at any point. ## The /ostree/boot directory However, we want to optimize for the case where the set of -kernel/initramfs pairs is the same between both the old and new +kernel/initramfs/devicetree sets is the same between both the old and new deployment lists. This happens when doing an upgrade that does not include the kernel; think of a simple translation update. OSTree optimizes for this case because on some systems `/boot` may be on a diff --git a/docs/manual/deployment.md b/docs/manual/deployment.md index 013a2d0e..957044db 100644 --- a/docs/manual/deployment.md +++ b/docs/manual/deployment.md @@ -53,10 +53,11 @@ paths, which are `vmlinuz(-.*)?-$checksum` in either `/boot` or `/usr/lib/ostree The checksum should be a SHA256 hash of the kernel contents; it must be pre-computed before storing the kernel in the repository. Optionally, the directory can also contain an initramfs, stored as -`initramfs(-.*)?-$checksum`. If this exists, the checksum must include -both the kernel and initramfs contents. OSTree will use this to -determine which kernels are shared. The rationale for this is to avoid -computing checksums on the client by default. +`initramfs(-.*)?-$checksum` and/or a device tree, stored as +`devicetree(-.*)?-$checksum`. If an initramfs or devicetree exist, +the checksum must include all of the kernel, initramfs and devicetree contents. +OSTree will use this to determine which kernels are shared. The rationale for +this is to avoid computing checksums on the client by default. The deployment should not have a traditional UNIX `/etc`; instead, it should include `/usr/etc`. This is the "default configuration". When diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 5dc5bde0..d477d2c4 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -93,7 +93,7 @@ sysroot_flags_to_copy_flags (GLnxFileCopyFlags defaults, } /* Try a hardlink if we can, otherwise fall back to copying. Used - * right now for kernels/initramfs in /boot, where we can just + * right now for kernels/initramfs/device trees in /boot, where we can just * hardlink if we're on the same partition. */ static gboolean @@ -894,6 +894,8 @@ typedef struct { char *kernel_namever; char *initramfs_srcpath; char *initramfs_namever; + char *devicetree_srcpath; + char *devicetree_namever; char *bootcsum; } OstreeKernelLayout; static void @@ -904,6 +906,8 @@ _ostree_kernel_layout_free (OstreeKernelLayout *layout) g_free (layout->kernel_namever); g_free (layout->initramfs_srcpath); g_free (layout->initramfs_namever); + g_free (layout->devicetree_srcpath); + g_free (layout->devicetree_namever); g_free (layout->bootcsum); g_free (layout); } @@ -1025,6 +1029,22 @@ get_kernel_from_tree_usrlib_modules (int deployment_dfd, if (!ot_gio_splice_update_checksum (NULL, in, &checksum, cancellable, error)) return FALSE; } + g_clear_object (&in); + glnx_close_fd (&fd); + + if (!ot_openat_ignore_enoent (ret_layout->boot_dfd, "devicetree", &fd, error)) + return FALSE; + if (fd != -1) + { + ret_layout->devicetree_srcpath = g_strdup ("devicetree"); + ret_layout->devicetree_namever = g_strdup_printf ("devicetree-%s", kver); + in = g_unix_input_stream_new (fd, FALSE); + if (!ot_gio_splice_update_checksum (NULL, in, &checksum, cancellable, error)) + return FALSE; + } + + g_clear_object (&in); + glnx_close_fd (&fd); char hexdigest[OSTREE_SHA256_STRING_LEN+1]; ot_checksum_get_hexdigest (&checksum, hexdigest, sizeof (hexdigest)); @@ -1044,6 +1064,7 @@ get_kernel_from_tree_legacy_layouts (int deployment_dfd, const char *legacy_paths[] = {"usr/lib/ostree-boot", "boot"}; g_autofree char *kernel_checksum = NULL; g_autofree char *initramfs_checksum = NULL; + g_autofree char *devicetree_checksum = NULL; g_autoptr(OstreeKernelLayout) ret_layout = _ostree_kernel_layout_new (); for (guint i = 0; i < G_N_ELEMENTS (legacy_paths); i++) @@ -1110,9 +1131,23 @@ get_kernel_from_tree_legacy_layouts (int deployment_dfd, ret_layout->initramfs_namever = g_strndup (name, dash - name); } } + /* See if this is the devicetree */ + else if (ret_layout->devicetree_srcpath == NULL && g_str_has_prefix (name, "devicetree-")) + { + const char *dash = strrchr (name, '-'); + g_assert (dash); + if (ostree_validate_structureof_checksum_string (dash + 1, NULL)) + { + devicetree_checksum = g_strdup (dash + 1); + ret_layout->devicetree_srcpath = g_strdup (name); + ret_layout->devicetree_namever = g_strndup (name, dash - name); + } + } - /* If we found both a kernel and initramfs, break out of the loop */ - if (ret_layout->kernel_srcpath != NULL && ret_layout->initramfs_srcpath != NULL) + /* If we found a kernel, an initramfs and a devicetree, break out of the loop */ + if (ret_layout->kernel_srcpath != NULL && + ret_layout->initramfs_srcpath != NULL && + ret_layout->devicetree_srcpath != NULL) break; } @@ -1132,6 +1167,19 @@ get_kernel_from_tree_legacy_layouts (int deployment_dfd, return glnx_throw (error, "Mismatched kernel checksum vs initrd"); } + /* The kernel/devicetree checksums must be the same */ + if (ret_layout->devicetree_srcpath != NULL) + { + g_assert (kernel_checksum != NULL); + g_assert (devicetree_checksum != NULL); + if (strcmp (kernel_checksum, devicetree_checksum) != 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Mismatched kernel checksum vs device tree in tree"); + return FALSE; + } + } + ret_layout->bootcsum = g_steal_pointer (&kernel_checksum); *out_layout = g_steal_pointer (&ret_layout); @@ -1567,7 +1615,7 @@ install_deployment_kernel (OstreeSysroot *sysroot, &deployment_dfd, error)) return FALSE; - /* Find the kernel/initramfs in the tree */ + /* Find the kernel/initramfs/devicetree in the tree */ g_autoptr(OstreeKernelLayout) kernel_layout = NULL; if (!get_kernel_from_tree (deployment_dfd, &kernel_layout, cancellable, error)) @@ -1628,6 +1676,21 @@ install_deployment_kernel (OstreeSysroot *sysroot, } } + if (kernel_layout->devicetree_srcpath) + { + g_assert (kernel_layout->devicetree_namever); + if (!glnx_fstatat_allow_noent (bootcsum_dfd, kernel_layout->devicetree_namever, &stbuf, 0, error)) + return FALSE; + if (errno == ENOENT) + { + if (!hardlink_or_copy_at (kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath, + bootcsum_dfd, kernel_layout->devicetree_namever, + sysroot->debug_flags, + cancellable, error)) + return FALSE; + } + } + g_autofree char *contents = NULL; if (!glnx_fstatat_allow_noent (deployment_dfd, "usr/lib/os-release", &stbuf, 0, error)) return FALSE; @@ -1717,6 +1780,12 @@ install_deployment_kernel (OstreeSysroot *sysroot, _ostree_kernel_args_replace_take (kargs, g_steal_pointer (&prepare_root_arg)); } + if (kernel_layout->devicetree_namever) + { + g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->devicetree_namever, NULL); + ostree_bootconfig_parser_set (bootconfig, "devicetree", boot_relpath); + } + /* Note this is parsed in ostree-impl-system-generator.c */ g_autofree char *ostree_kernel_arg = g_strdup_printf ("ostree=/ostree/boot.%d/%s/%s/%d", new_bootversion, osname, bootcsum, diff --git a/tests/test-no-initramfs.sh b/tests/test-no-initramfs.sh index 7c23a3e7..52bb9d98 100755 --- a/tests/test-no-initramfs.sh +++ b/tests/test-no-initramfs.sh @@ -2,7 +2,7 @@ . $(dirname $0)/libtest.sh -echo "1..7" +echo "1..10" setup_os_repository "archive-z2" "uboot" @@ -20,11 +20,12 @@ echo "ok deployment with initramfs" pull_test_tree() { kernel_contents=$1 initramfs_contents=$2 + devicetree_contents=$3 - printf "TEST SETUP:\n kernel: %s\n initramfs: %s\n layout: %s\n" \ - "$kernel_contents" "$initramfs_contents" "$layout" + printf "TEST SETUP:\n kernel: %s\n initramfs: %s\n devicetree: %s\n layout: %s\n" \ + "$kernel_contents" "$initramfs_contents" "$devicetree_contents" "$layout" - rm -rf ${test_tmpdir}/osdata/usr/lib/modules/3.6.0/{initramfs.img,vmlinuz} \ + rm -rf ${test_tmpdir}/osdata/usr/lib/modules/3.6.0/{initramfs.img,vmlinuz,devicetree} \ ${test_tmpdir}/osdata/usr/lib/ostree-boot \ ${test_tmpdir}/osdata/boot if [ "$layout" = "/usr/lib/modules" ]; then @@ -32,14 +33,16 @@ pull_test_tree() { cd ${test_tmpdir}/osdata/usr/lib/modules/3.6.0 echo -n "$kernel_contents" > vmlinuz [ -n "$initramfs_contents" ] && echo -n "$initramfs_contents" > initramfs.img + [ -n "$devicetree_contents" ] && echo -n "$devicetree_contents" > devicetree elif [ "$layout" = "/usr/lib/ostree-boot" ] || [ "$layout" = "/boot" ]; then # "Legacy" layout mkdir -p "${test_tmpdir}/osdata/$layout" cd "${test_tmpdir}/osdata/$layout" - bootcsum=$(echo -n "$kernel_contents$initramfs_contents" \ + bootcsum=$(echo -n "$kernel_contents$initramfs_contents$devicetree_contents" \ | sha256sum | cut -f 1 -d ' ') echo -n "$kernel_contents" > vmlinuz-${bootcsum} [ -n "$initramfs_contents" ] && echo -n "$initramfs_contents" > initramfs-${bootcsum} + [ -n "$devicetree_contents" ] && echo -n "$devicetree_contents" > devicetree-${bootcsum} else exit 1 fi @@ -75,4 +78,13 @@ do assert_not_file_has_content sysroot/boot/loader/entries/ostree-testos-0.conf 'init=' echo "ok switching from no initramfs to initramfs enabled sysroot layout=$layout" + + pull_test_tree "the kernel" "" "my .dtb file" + ${CMD_PREFIX} ostree admin deploy --os=testos testos:testos/buildmaster/x86_64-runtime + + assert_file_has_content sysroot/boot/loader/entries/ostree-testos-0.conf 'init=' + assert_file_has_content sysroot/boot/"$(get_key_from_bootloader_conf sysroot/boot/loader/entries/ostree-testos-0.conf 'devicetree')" "my .dtb file" + assert_not_file_has_content sysroot/boot/loader/entries/ostree-testos-0.conf 'initrd' + + echo "ok switching from initramfs to no initramfs sysroot with devicetree layout=$layout" done