/** * rpmostree_prepare_rootfs_for_commit: * * Walk over the root filesystem and perform some core conversions * from RPM conventions to OSTree conventions. For example: * * * Move /etc to /usr/etc * * Checksum the kernel in /boot * * Migrate content in /var to systemd-tmpfiles */ gboolean rpmostree_prepare_rootfs_for_commit (GFile *rootfs, JsonObject *treefile, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_object GFile *rootfs_tmp = NULL; gs_free char *rootfs_tmp_path = NULL; rootfs_tmp_path = g_strconcat (gs_file_get_path_cached (rootfs), ".tmp", NULL); rootfs_tmp = g_file_new_for_path (rootfs_tmp_path); if (!gs_shutil_rm_rf (rootfs_tmp, cancellable, error)) goto out; if (!create_rootfs_from_yumroot_content (rootfs_tmp, rootfs, treefile, cancellable, error)) goto out; if (!gs_shutil_rm_rf (rootfs, cancellable, error)) goto out; if (!gs_file_rename (rootfs_tmp, rootfs, cancellable, error)) goto out; ret = TRUE; out: return ret; }
static gboolean move_to_dir (GFile *src, GFile *dest_dir, GCancellable *cancellable, GError **error) { gs_unref_object GFile *dest = g_file_get_child (dest_dir, gs_file_get_basename_cached (src)); return gs_file_rename (src, dest, cancellable, error); }
/** * ot_gfile_atomic_symlink_swap: * @path: Replace the contents of this symbolic link * @target: New symbolic link target * @cancellable: * @error * * Create a new temporary symbolic link, then use the Unix rename() * function to atomically replace @path with the new symbolic link. * Do not use this function inside directories such as /tmp as it uses * a predicatable file name. */ gboolean ot_gfile_atomic_symlink_swap (GFile *path, const char *target, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GFile) parent = g_file_get_parent (path); g_autofree char *tmpname = g_strconcat (gs_file_get_basename_cached (path), ".tmp", NULL); g_autoptr(GFile) tmppath = g_file_get_child (parent, tmpname); int parent_dfd = -1; if (!ot_gfile_ensure_unlinked (tmppath, cancellable, error)) goto out; if (!g_file_make_symbolic_link (tmppath, target, cancellable, error)) goto out; if (!gs_file_open_dir_fd (parent, &parent_dfd, cancellable, error)) goto out; /* Ensure the link has hit disk */ if (fsync (parent_dfd) != 0) { gs_set_error_from_errno (error, errno); goto out; } if (!gs_file_rename (tmppath, path, cancellable, error)) goto out; /* And sync again for good measure */ if (fsync (parent_dfd) != 0) { gs_set_error_from_errno (error, errno); goto out; } ret = TRUE; out: if (parent_dfd != -1) (void) close (parent_dfd); return ret; }
/* Prepare a root filesystem, taking mainly the contents of /usr from yumroot */ static gboolean create_rootfs_from_yumroot_content (GFile *targetroot, GFile *yumroot, JsonObject *treefile, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; glnx_fd_close int src_rootfs_fd = -1; glnx_fd_close int target_root_dfd = -1; gs_unref_object GFile *kernel_path = NULL; gs_unref_object GFile *initramfs_path = NULL; gs_unref_hashtable GHashTable *preserve_groups_set = NULL; gboolean container = FALSE; if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (yumroot), TRUE, &src_rootfs_fd, error)) goto out; if (!_rpmostree_jsonutil_object_get_optional_boolean_member (treefile, "container", &container, error)) goto out; g_print ("Preparing kernel\n"); if (!container && !do_kernel_prep (yumroot, treefile, cancellable, error)) goto out; g_print ("Initializing rootfs\n"); if (!init_rootfs (targetroot, cancellable, error)) goto out; if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (targetroot), TRUE, &target_root_dfd, error)) goto out; g_print ("Migrating /etc/passwd to /usr/lib/\n"); if (!rpmostree_passwd_migrate_except_root (yumroot, RPM_OSTREE_PASSWD_MIGRATE_PASSWD, NULL, cancellable, error)) goto out; if (json_object_has_member (treefile, "etc-group-members")) { JsonArray *etc_group_members = json_object_get_array_member (treefile, "etc-group-members"); preserve_groups_set = _rpmostree_jsonutil_jsarray_strings_to_set (etc_group_members); } g_print ("Migrating /etc/group to /usr/lib/\n"); if (!rpmostree_passwd_migrate_except_root (yumroot, RPM_OSTREE_PASSWD_MIGRATE_GROUP, preserve_groups_set, cancellable, error)) goto out; /* NSS configuration to look at the new files */ { gs_unref_object GFile *yumroot_etc = g_file_resolve_relative_path (yumroot, "etc"); if (!replace_nsswitch (yumroot_etc, cancellable, error)) goto out; } /* We take /usr from the yum content */ g_print ("Moving /usr to target\n"); { gs_unref_object GFile *usr = g_file_get_child (yumroot, "usr"); if (!move_to_dir (usr, targetroot, cancellable, error)) goto out; } /* Except /usr/local -> ../var/usrlocal */ g_print ("Linking /usr/local -> ../var/usrlocal\n"); { gs_unref_object GFile *target_usrlocal = g_file_resolve_relative_path (targetroot, "usr/local"); if (!gs_shutil_rm_rf (target_usrlocal, cancellable, error)) goto out; if (!g_file_make_symbolic_link (target_usrlocal, "../var/usrlocal", cancellable, error)) goto out; } /* And now we take the contents of /etc and put them in /usr/etc */ g_print ("Moving /etc to /usr/etc\n"); { gs_unref_object GFile *yumroot_etc = g_file_get_child (yumroot, "etc"); gs_unref_object GFile *target_usretc = g_file_resolve_relative_path (targetroot, "usr/etc"); if (!gs_file_rename (yumroot_etc, target_usretc, cancellable, error)) goto out; } if (!migrate_rpm_and_yumdb (targetroot, yumroot, cancellable, error)) goto out; if (!convert_var_to_tmpfiles_d (src_rootfs_fd, target_root_dfd, cancellable, error)) goto out; /* Move boot, but rename the kernel/initramfs to have a checksum */ if (!container) { gs_unref_object GFile *yumroot_boot = g_file_get_child (yumroot, "boot"); gs_unref_object GFile *target_boot = g_file_get_child (targetroot, "boot"); gs_unref_object GFile *target_usrlib = g_file_resolve_relative_path (targetroot, "usr/lib"); gs_unref_object GFile *target_usrlib_ostree_boot = g_file_resolve_relative_path (target_usrlib, "ostree-boot"); RpmOstreePostprocessBootLocation boot_location = RPMOSTREE_POSTPROCESS_BOOT_LOCATION_BOTH; const char *boot_location_str = NULL; g_print ("Moving /boot\n"); if (!_rpmostree_jsonutil_object_get_optional_string_member (treefile, "boot_location", &boot_location_str, error)) goto out; if (boot_location_str != NULL) { if (strcmp (boot_location_str, "legacy") == 0) boot_location = RPMOSTREE_POSTPROCESS_BOOT_LOCATION_LEGACY; else if (strcmp (boot_location_str, "both") == 0) boot_location = RPMOSTREE_POSTPROCESS_BOOT_LOCATION_BOTH; else if (strcmp (boot_location_str, "new") == 0) boot_location = RPMOSTREE_POSTPROCESS_BOOT_LOCATION_NEW; else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid boot location '%s'", boot_location_str); goto out; } } if (!gs_file_ensure_directory (target_usrlib, TRUE, cancellable, error)) goto out; switch (boot_location) { case RPMOSTREE_POSTPROCESS_BOOT_LOCATION_LEGACY: { g_print ("Using boot location: legacy\n"); if (!gs_file_rename (yumroot_boot, target_boot, cancellable, error)) goto out; } break; case RPMOSTREE_POSTPROCESS_BOOT_LOCATION_BOTH: { g_print ("Using boot location: both\n"); if (!gs_file_rename (yumroot_boot, target_boot, cancellable, error)) goto out; /* Hardlink the existing content, only a little ugly as * we'll end up sha256'ing it twice, but oh well. */ if (!gs_shutil_cp_al_or_fallback (target_boot, target_usrlib_ostree_boot, cancellable, error)) goto out; } break; case RPMOSTREE_POSTPROCESS_BOOT_LOCATION_NEW: { g_print ("Using boot location: new\n"); if (!gs_file_rename (yumroot_boot, target_usrlib_ostree_boot, cancellable, error)) goto out; } break; } } /* Also carry along toplevel compat links */ g_print ("Copying toplevel compat symlinks\n"); { guint i; const char *toplevel_links[] = { "lib", "lib64", "lib32", "bin", "sbin" }; for (i = 0; i < G_N_ELEMENTS (toplevel_links); i++) { gs_unref_object GFile *srcpath = g_file_get_child (yumroot, toplevel_links[i]); if (g_file_query_file_type (srcpath, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_SYMBOLIC_LINK) { if (!move_to_dir (srcpath, targetroot, cancellable, error)) goto out; } } } g_print ("Adding tmpfiles-ostree-integration.conf\n"); { gs_unref_object GFile *src_pkglibdir = g_file_new_for_path (PKGLIBDIR); gs_unref_object GFile *src_tmpfilesd = g_file_get_child (src_pkglibdir, "tmpfiles-ostree-integration.conf"); gs_unref_object GFile *target_tmpfilesd = g_file_resolve_relative_path (targetroot, "usr/lib/tmpfiles.d/tmpfiles-ostree-integration.conf"); gs_unref_object GFile *target_tmpfilesd_parent = g_file_get_parent (target_tmpfilesd); if (!gs_file_ensure_directory (target_tmpfilesd_parent, TRUE, cancellable, error)) goto out; if (!g_file_copy (src_tmpfilesd, target_tmpfilesd, 0, cancellable, NULL, NULL, error)) goto out; } ret = TRUE; out: return ret; }
static gboolean migrate_rpm_and_yumdb (GFile *targetroot, GFile *yumroot, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_object GFile *usrbin_rpm = g_file_resolve_relative_path (targetroot, "usr/bin/rpm"); gs_unref_object GFile *legacyrpm_path = g_file_resolve_relative_path (yumroot, "var/lib/rpm"); gs_unref_object GFile *newrpm_path = g_file_resolve_relative_path (targetroot, "usr/share/rpm"); gs_unref_object GFile *src_yum_rpmdb_indexes = g_file_resolve_relative_path (yumroot, "var/lib/yum"); gs_unref_object GFile *target_yum_rpmdb_indexes = g_file_resolve_relative_path (targetroot, "usr/share/yumdb"); gs_unref_object GFile *yumroot_yumlib = g_file_get_child (yumroot, "var/lib/yum"); gs_unref_object GFileEnumerator *direnum = NULL; direnum = g_file_enumerate_children (legacyrpm_path, "standard::name", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!direnum) goto out; while (TRUE) { const char *name; GFileInfo *file_info; GFile *child; if (!gs_file_enumerator_iterate (direnum, &file_info, &child, cancellable, error)) goto out; if (!file_info) break; name = g_file_info_get_name (file_info); if (g_str_has_prefix (name, "__db.") || strcmp (name, ".dbenv.lock") == 0 || strcmp (name, ".rpm.lock") == 0) { if (!gs_file_unlink (child, cancellable, error)) goto out; } } (void) g_file_enumerator_close (direnum, cancellable, error); g_print ("Placing RPM db in /usr/share/rpm\n"); if (!gs_file_rename (legacyrpm_path, newrpm_path, cancellable, error)) goto out; /* Remove /var/lib/yum; we don't want it here. */ if (!gs_shutil_rm_rf (yumroot_yumlib, cancellable, error)) goto out; ret = TRUE; out: return ret; }
static gboolean do_kernel_prep (GFile *yumroot, JsonObject *treefile, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_object GFile *bootdir = g_file_get_child (yumroot, "boot"); gs_unref_object GFile *kernel_path = NULL; gs_unref_object GFile *initramfs_path = NULL; const char *boot_checksum_str = NULL; GChecksum *boot_checksum = NULL; g_autofree char *kver = NULL; if (!find_kernel_and_initramfs_in_bootdir (bootdir, &kernel_path, &initramfs_path, cancellable, error)) goto out; if (kernel_path == NULL) { gs_unref_object GFile *mod_dir = g_file_resolve_relative_path (yumroot, "usr/lib/modules"); gs_unref_object GFile *modversion_dir = NULL; if (!find_ensure_one_subdirectory (mod_dir, &modversion_dir, cancellable, error)) goto out; if (modversion_dir) { kver = g_file_get_basename (modversion_dir); if (!find_kernel_and_initramfs_in_bootdir (modversion_dir, &kernel_path, &initramfs_path, cancellable, error)) goto out; } } if (kernel_path == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unable to find kernel (vmlinuz) in /boot or /usr/lib/modules"); goto out; } if (initramfs_path) { g_print ("Removing RPM-generated '%s'\n", gs_file_get_path_cached (initramfs_path)); if (!gs_shutil_rm_rf (initramfs_path, cancellable, error)) goto out; } if (!kver) { const char *kname = gs_file_get_basename_cached (kernel_path); const char *kver_p; kver_p = strchr (kname, '-'); g_assert (kver_p); kver = g_strdup (kver_p + 1); } /* OSTree needs to own this */ { gs_unref_object GFile *loaderdir = g_file_get_child (bootdir, "loader"); if (!gs_shutil_rm_rf (loaderdir, cancellable, error)) goto out; } { char *child_argv[] = { "depmod", (char*)kver, NULL }; if (!run_sync_in_root (yumroot, "depmod", child_argv, error)) goto out; } /* Ensure the /etc/machine-id file is present and empty. Apparently systemd doesn't work when the file is missing (as of systemd-219-9.fc22) but it is correctly populated if the file is there. */ g_print ("Creating empty machine-id\n"); { const char *hardcoded_machine_id = ""; gs_unref_object GFile *machineid_path = g_file_resolve_relative_path (yumroot, "etc/machine-id"); if (!g_file_replace_contents (machineid_path, hardcoded_machine_id, strlen (hardcoded_machine_id), NULL, FALSE, 0, NULL, cancellable, error)) goto out; } { gboolean reproducible; gs_unref_ptrarray GPtrArray *dracut_argv = g_ptr_array_new (); if (!dracut_supports_reproducible (yumroot, &reproducible, cancellable, error)) goto out; g_ptr_array_add (dracut_argv, "dracut"); g_ptr_array_add (dracut_argv, "-v"); if (reproducible) { g_ptr_array_add (dracut_argv, "--reproducible"); g_ptr_array_add (dracut_argv, "--gzip"); } g_ptr_array_add (dracut_argv, "--tmpdir=/tmp"); g_ptr_array_add (dracut_argv, "-f"); g_ptr_array_add (dracut_argv, "/var/tmp/initramfs.img"); g_ptr_array_add (dracut_argv, (char*)kver); if (json_object_has_member (treefile, "initramfs-args")) { guint i, len; JsonArray *initramfs_args; initramfs_args = json_object_get_array_member (treefile, "initramfs-args"); len = json_array_get_length (initramfs_args); for (i = 0; i < len; i++) { const char *arg = _rpmostree_jsonutil_array_require_string_element (initramfs_args, i, error); if (!arg) goto out; g_ptr_array_add (dracut_argv, (char*)arg); } } g_ptr_array_add (dracut_argv, NULL); if (!run_sync_in_root (yumroot, "dracut", (char**)dracut_argv->pdata, error)) goto out; } initramfs_path = g_file_resolve_relative_path (yumroot, "var/tmp/initramfs.img"); if (!g_file_query_exists (initramfs_path, NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Dracut failed to generate '%s'", gs_file_get_path_cached (initramfs_path)); goto out; } { gs_free char *initramfs_name = g_strconcat ("initramfs-", kver, ".img", NULL); gs_unref_object GFile *initramfs_dest = g_file_get_child (bootdir, initramfs_name); if (!gs_file_rename (initramfs_path, initramfs_dest, cancellable, error)) goto out; /* Transfer ownership */ g_object_unref (initramfs_path); initramfs_path = initramfs_dest; initramfs_dest = NULL; } boot_checksum = g_checksum_new (G_CHECKSUM_SHA256); if (!_rpmostree_util_update_checksum_from_file (boot_checksum, kernel_path, cancellable, error)) goto out; if (!_rpmostree_util_update_checksum_from_file (boot_checksum, initramfs_path, cancellable, error)) goto out; boot_checksum_str = g_checksum_get_string (boot_checksum); { gs_free char *new_kernel_name = g_strconcat (gs_file_get_basename_cached (kernel_path), "-", boot_checksum_str, NULL); gs_unref_object GFile *new_kernel_path = g_file_get_child (bootdir, new_kernel_name); gs_free char *new_initramfs_name = g_strconcat (gs_file_get_basename_cached (initramfs_path), "-", boot_checksum_str, NULL); gs_unref_object GFile *new_initramfs_path = g_file_get_child (bootdir, new_initramfs_name); if (!gs_file_rename (kernel_path, new_kernel_path, cancellable, error)) goto out; if (!gs_file_rename (initramfs_path, new_initramfs_path, cancellable, error)) goto out; } ret = TRUE; out: if (boot_checksum) g_checksum_free (boot_checksum); return ret; }
static gboolean migrate_rpm_and_yumdb (GFile *targetroot, GFile *yumroot, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_object GFile *usrbin_rpm = g_file_resolve_relative_path (targetroot, "usr/bin/rpm"); gs_unref_object GFile *legacyrpm_path = g_file_resolve_relative_path (yumroot, "var/lib/rpm"); gs_unref_object GFile *newrpm_path = g_file_resolve_relative_path (targetroot, "usr/share/rpm"); gs_unref_object GFile *src_yum_rpmdb_indexes = g_file_resolve_relative_path (yumroot, "var/lib/yum"); gs_unref_object GFile *target_yum_rpmdb_indexes = g_file_resolve_relative_path (targetroot, "usr/share/yumdb"); gs_unref_object GFile *yumroot_yumlib = g_file_get_child (yumroot, "var/lib/yum"); gs_unref_object GFileEnumerator *direnum = NULL; direnum = g_file_enumerate_children (legacyrpm_path, "standard::name", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!direnum) goto out; while (TRUE) { const char *name; GFileInfo *file_info; GFile *child; if (!gs_file_enumerator_iterate (direnum, &file_info, &child, cancellable, error)) goto out; if (!file_info) break; name = g_file_info_get_name (file_info); if (g_str_has_prefix (name, "__db.") || strcmp (name, ".dbenv.lock") == 0 || strcmp (name, ".rpm.lock") == 0) { if (!gs_file_unlink (child, cancellable, error)) goto out; } } (void) g_file_enumerator_close (direnum, cancellable, error); g_print ("Placing RPM db in /usr/share/rpm\n"); if (!gs_file_rename (legacyrpm_path, newrpm_path, cancellable, error)) goto out; /* Move the yum database to usr/share/yumdb; disabled for now due * to bad conflict with OSTree's current * one-http-request-per-file. */ #if 0 if (g_file_query_exists (src_yum_rpmdb_indexes, NULL)) { g_print ("Moving %s to %s\n", gs_file_get_path_cached (src_yum_rpmdb_indexes), gs_file_get_path_cached (target_yum_rpmdb_indexes)); if (!gs_file_rename (src_yum_rpmdb_indexes, target_yum_rpmdb_indexes, cancellable, error)) goto out; if (!clean_yumdb_extraneous_files (target_yum_rpmdb_indexes, cancellable, error)) goto out; } #endif /* Remove /var/lib/yum; we don't want it here. */ if (!gs_shutil_rm_rf (yumroot_yumlib, cancellable, error)) goto out; ret = TRUE; out: return ret; }
static gboolean do_kernel_prep (GFile *yumroot, JsonObject *treefile, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; gs_unref_object GFile *bootdir = g_file_get_child (yumroot, "boot"); gs_unref_object GFile *kernel_path = NULL; gs_unref_object GFile *initramfs_path = NULL; const char *boot_checksum_str = NULL; GChecksum *boot_checksum = NULL; const char *kname; const char *kver; if (!find_kernel_and_initramfs_in_bootdir (bootdir, &kernel_path, &initramfs_path, cancellable, error)) goto out; if (initramfs_path) { g_print ("Removing RPM-generated '%s'\n", gs_file_get_path_cached (initramfs_path)); if (!gs_shutil_rm_rf (initramfs_path, cancellable, error)) goto out; } kname = gs_file_get_basename_cached (kernel_path); kver = strchr (kname, '-'); g_assert (kver); kver += 1; /* OSTree needs to own this */ { gs_unref_object GFile *loaderdir = g_file_get_child (bootdir, "loader"); if (!gs_shutil_rm_rf (loaderdir, cancellable, error)) goto out; } { char *child_argv[] = { "depmod", (char*)kver, NULL }; if (!run_sync_in_root (yumroot, "/usr/sbin/depmod", child_argv, error)) goto out; } /* Copy of code from gnome-continuous; yes, we hardcode the machine id for now, because distributing pre-generated initramfs images with dracut/systemd at the moment effectively requires this. http://lists.freedesktop.org/archives/systemd-devel/2013-July/011770.html */ g_print ("Hardcoding machine-id\n"); { const char *hardcoded_machine_id = "45bb3b96146aa94f299b9eb43646eb35\n"; gs_unref_object GFile *machineid_path = g_file_resolve_relative_path (yumroot, "etc/machine-id"); if (!g_file_replace_contents (machineid_path, hardcoded_machine_id, strlen (hardcoded_machine_id), NULL, FALSE, 0, NULL, cancellable, error)) goto out; } { gs_unref_ptrarray GPtrArray *dracut_argv = g_ptr_array_new (); g_ptr_array_add (dracut_argv, "dracut"); g_ptr_array_add (dracut_argv, "-v"); g_ptr_array_add (dracut_argv, "--tmpdir=/tmp"); g_ptr_array_add (dracut_argv, "-f"); g_ptr_array_add (dracut_argv, "/var/tmp/initramfs.img"); g_ptr_array_add (dracut_argv, (char*)kver); if (json_object_has_member (treefile, "initramfs-args")) { guint i, len; JsonArray *initramfs_args; initramfs_args = json_object_get_array_member (treefile, "initramfs-args"); len = json_array_get_length (initramfs_args); for (i = 0; i < len; i++) { const char *arg = _rpmostree_jsonutil_array_require_string_element (initramfs_args, i, error); if (!arg) goto out; g_ptr_array_add (dracut_argv, (char*)arg); } } g_ptr_array_add (dracut_argv, NULL); if (!run_sync_in_root (yumroot, "/usr/sbin/dracut", (char**)dracut_argv->pdata, error)) goto out; } initramfs_path = g_file_resolve_relative_path (yumroot, "var/tmp/initramfs.img"); if (!g_file_query_exists (initramfs_path, NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Dracut failed to generate '%s'", gs_file_get_path_cached (initramfs_path)); goto out; } { gs_free char *initramfs_name = g_strconcat ("initramfs-", kver, ".img", NULL); gs_unref_object GFile *initramfs_dest = g_file_get_child (bootdir, initramfs_name); if (!gs_file_rename (initramfs_path, initramfs_dest, cancellable, error)) goto out; /* Transfer ownership */ g_object_unref (initramfs_path); initramfs_path = initramfs_dest; initramfs_dest = NULL; } boot_checksum = g_checksum_new (G_CHECKSUM_SHA256); if (!_rpmostree_util_update_checksum_from_file (boot_checksum, kernel_path, cancellable, error)) goto out; if (!_rpmostree_util_update_checksum_from_file (boot_checksum, initramfs_path, cancellable, error)) goto out; boot_checksum_str = g_checksum_get_string (boot_checksum); { gs_free char *new_kernel_name = g_strconcat (gs_file_get_basename_cached (kernel_path), "-", boot_checksum_str, NULL); gs_unref_object GFile *new_kernel_path = g_file_get_child (bootdir, new_kernel_name); gs_free char *new_initramfs_name = g_strconcat (gs_file_get_basename_cached (initramfs_path), "-", boot_checksum_str, NULL); gs_unref_object GFile *new_initramfs_path = g_file_get_child (bootdir, new_initramfs_name); if (!gs_file_rename (kernel_path, new_kernel_path, cancellable, error)) goto out; if (!gs_file_rename (initramfs_path, new_initramfs_path, cancellable, error)) goto out; } ret = TRUE; out: if (boot_checksum) g_checksum_free (boot_checksum); return ret; }
static gboolean _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader, int bootversion, GCancellable *cancellable, GError **error) { OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader); gboolean ret = FALSE; gs_unref_object GFile *efi_new_config_temp = NULL; gs_unref_object GFile *efi_orig_config = NULL; gs_unref_object GFile *new_config_path = NULL; gs_unref_object GSSubprocessContext *procctx = NULL; gs_unref_object GSSubprocess *proc = NULL; gs_strfreev char **child_env = g_get_environ (); gs_free char *bootversion_str = g_strdup_printf ("%u", (guint)bootversion); gs_unref_object GFile *config_path_efi_dir = NULL; if (self->is_efi) { config_path_efi_dir = g_file_get_parent (self->config_path_efi); new_config_path = g_file_get_child (config_path_efi_dir, "grub.cfg.new"); /* We use grub2-mkconfig to write to a temporary file first */ if (!ot_gfile_ensure_unlinked (new_config_path, cancellable, error)) goto out; } else { new_config_path = ot_gfile_resolve_path_printf (self->sysroot->path, "boot/loader.%d/grub.cfg", bootversion); } procctx = gs_subprocess_context_newv ("grub2-mkconfig", "-o", gs_file_get_path_cached (new_config_path), NULL); child_env = g_environ_setenv (child_env, "_OSTREE_GRUB2_BOOTVERSION", bootversion_str, TRUE); /* We have to pass our state to the child */ if (self->is_efi) child_env = g_environ_setenv (child_env, "_OSTREE_GRUB2_IS_EFI", "1", TRUE); gs_subprocess_context_set_environment (procctx, child_env); gs_subprocess_context_set_stdout_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_NULL); if (g_getenv ("OSTREE_DEBUG_GRUB2")) gs_subprocess_context_set_stderr_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT); else gs_subprocess_context_set_stderr_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_NULL); /* In the current Fedora grub2 package, this script doesn't even try to be atomic; it just does: cat ${grub_cfg}.new > ${grub_cfg} rm -f ${grub_cfg}.new Upstream is fixed though. */ proc = gs_subprocess_new (procctx, cancellable, error); if (!proc) goto out; if (!gs_subprocess_wait_sync_check (proc, cancellable, error)) goto out; /* Now let's fdatasync() for the new file */ if (!gs_file_sync_data (new_config_path, cancellable, error)) goto out; if (self->is_efi) { gs_unref_object GFile *config_path_efi_old = g_file_get_child (config_path_efi_dir, "grub.cfg.old"); /* copy current to old */ if (!ot_gfile_ensure_unlinked (config_path_efi_old, cancellable, error)) goto out; if (!g_file_copy (self->config_path_efi, config_path_efi_old, G_FILE_COPY_OVERWRITE, cancellable, NULL, NULL, error)) goto out; if (!ot_gfile_ensure_unlinked (config_path_efi_old, cancellable, error)) goto out; /* NOTE: NON-ATOMIC REPLACEMENT; WE can't do anything else on FAT; * see https://bugzilla.gnome.org/show_bug.cgi?id=724246 */ if (!ot_gfile_ensure_unlinked (self->config_path_efi, cancellable, error)) goto out; if (!gs_file_rename (new_config_path, self->config_path_efi, cancellable, error)) goto out; } ret = TRUE; out: return ret; }
static gboolean linkcopy_internal_attempt (GFile *src, GFile *dest, GFile *dest_parent, GFileCopyFlags flags, gboolean sync_data, gboolean enable_guestfs_fuse_workaround, gboolean *out_try_again, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int res; char *tmp_name = NULL; GFile *tmp_dest = NULL; if (g_cancellable_set_error_if_cancelled (cancellable, error)) goto out; tmp_name = gs_fileutil_gen_tmp_name (NULL, NULL); tmp_dest = g_file_get_child (dest_parent, tmp_name); res = link (gs_file_get_path_cached (src), gs_file_get_path_cached (tmp_dest)); if (res == -1) { if (errno == EEXIST) { /* Nothing, fall through */ *out_try_again = TRUE; ret = TRUE; goto out; } else if (errno == EXDEV || errno == EMLINK || errno == EPERM || (enable_guestfs_fuse_workaround && errno == ENOENT)) { if (!g_file_copy (src, tmp_dest, flags, cancellable, NULL, NULL, error)) goto out; } else { gs_set_prefix_error_from_errno (error, errno, "link"); goto out; } } if (sync_data) { /* Now, we need to fsync */ if (!gs_file_sync_data (tmp_dest, cancellable, error)) goto out; } if (!gs_file_rename (tmp_dest, dest, cancellable, error)) goto out; ret = TRUE; *out_try_again = FALSE; out: g_clear_pointer (&tmp_name, g_free); g_clear_object (&tmp_dest); return ret; }