gboolean _ostree_sysroot_read_current_subbootversion (OstreeSysroot *self, int bootversion, int *out_subbootversion, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; struct stat stbuf; g_autofree char *ostree_bootdir_name = g_strdup_printf ("ostree/boot.%d", bootversion); if (!ensure_sysroot_fd (self, error)) goto out; if (fstatat (self->sysroot_fd, ostree_bootdir_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0) { if (errno == ENOENT) *out_subbootversion = 0; else { glnx_set_error_from_errno (error); goto out; } } else { g_autofree char *current_subbootdir_name = NULL; current_subbootdir_name = glnx_readlinkat_malloc (self->sysroot_fd, ostree_bootdir_name, cancellable, error); if (!current_subbootdir_name) goto out; if (g_str_has_suffix (current_subbootdir_name, ".0")) *out_subbootversion = 0; else if (g_str_has_suffix (current_subbootdir_name, ".1")) *out_subbootversion = 1; else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid target '%s' in %s", current_subbootdir_name, ostree_bootdir_name); goto out; } } ret = TRUE; out: return ret; }
static gboolean read_current_bootversion (OstreeSysroot *self, int *out_bootversion, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int ret_bootversion; struct stat stbuf; if (fstatat (self->sysroot_fd, "boot/loader", &stbuf, AT_SYMLINK_NOFOLLOW) != 0) { if (errno != ENOENT) { glnx_set_error_from_errno (error); goto out; } ret_bootversion = 0; } else { g_autofree char *target = NULL; if (!S_ISLNK (stbuf.st_mode)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not a symbolic link: boot/loader"); goto out; } target = glnx_readlinkat_malloc (self->sysroot_fd, "boot/loader", cancellable, error); if (!target) goto out; if (g_strcmp0 (target, "loader.0") == 0) ret_bootversion = 0; else if (g_strcmp0 (target, "loader.1") == 0) ret_bootversion = 1; else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid target '%s' in boot/loader", target); goto out; } } ret = TRUE; *out_bootversion = ret_bootversion; out: return ret; }
static gboolean convert_var_to_tmpfiles_d_recurse (GOutputStream *tmpfiles_out, int dfd, GString *prefix, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; gsize bytes_written; if (!glnx_dirfd_iterator_init_at (dfd, prefix->str + 1, TRUE, &dfd_iter, error)) goto out; while (TRUE) { struct dirent *dent = NULL; GString *tmpfiles_d_buf; gs_free char *tmpfiles_d_line = NULL; char filetype_c; gs_free char *relpath = NULL; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) goto out; if (!dent) break; switch (dent->d_type) { case DT_DIR: filetype_c = 'd'; break; case DT_LNK: filetype_c = 'L'; break; default: g_print ("Ignoring non-directory/non-symlink '%s'\n", dent->d_name); continue; } tmpfiles_d_buf = g_string_new (""); g_string_append_c (tmpfiles_d_buf, filetype_c); g_string_append_c (tmpfiles_d_buf, ' '); g_string_append (tmpfiles_d_buf, prefix->str); g_string_append_c (tmpfiles_d_buf, '/'); g_string_append (tmpfiles_d_buf, dent->d_name); if (filetype_c == 'd') { struct stat stbuf; if (TEMP_FAILURE_RETRY (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0) { glnx_set_error_from_errno (error); goto out; } g_string_append_printf (tmpfiles_d_buf, " 0%02o", stbuf.st_mode & ~S_IFMT); g_string_append_printf (tmpfiles_d_buf, " %d %d - -", stbuf.st_uid, stbuf.st_gid); /* Push prefix */ g_string_append_c (prefix, '/'); g_string_append (prefix, dent->d_name); if (!convert_var_to_tmpfiles_d_recurse (tmpfiles_out, dfd, prefix, cancellable, error)) goto out; /* Pop prefix */ { char *r = memrchr (prefix->str, '/', prefix->len); g_assert (r != NULL); g_string_truncate (prefix, r - prefix->str); } } else { g_autofree char *link = glnx_readlinkat_malloc (dfd_iter.fd, dent->d_name, cancellable, error); if (!link) goto out; g_string_append (tmpfiles_d_buf, " - - - - "); g_string_append (tmpfiles_d_buf, link); } g_string_append_c (tmpfiles_d_buf, '\n'); tmpfiles_d_line = g_string_free (tmpfiles_d_buf, FALSE); if (!g_output_stream_write_all (tmpfiles_out, tmpfiles_d_line, strlen (tmpfiles_d_line), &bytes_written, cancellable, error)) goto out; } ret = TRUE; out: return ret; }
gboolean rpmostree_container_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GError **error) { int exit_status = EXIT_FAILURE; GOptionContext *context = g_option_context_new ("NAME"); g_auto(ROContainerContext) rocctx_data = RO_CONTAINER_CONTEXT_INIT; ROContainerContext *rocctx = &rocctx_data; g_autoptr(RpmOstreeInstall) install = NULL; const char *name; g_autofree char *commit_checksum = NULL; g_autofree char *new_commit_checksum = NULL; g_autoptr(GVariant) commit = NULL; g_autoptr(GVariant) metadata = NULL; g_autoptr(GVariant) input_packages_v = NULL; g_autoptr(RpmOstreeTreespec) treespec = NULL; guint current_version; guint new_version; g_autofree char *previous_state_sha512 = NULL; const char *target_current_root; const char *target_new_root; if (!rpmostree_option_context_parse (context, assemble_option_entries, &argc, &argv, RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD, cancellable, NULL, error)) goto out; if (argc < 1) { rpmostree_usage_error (context, "NAME must be specified", error); goto out; } name = argv[1]; if (!roc_context_init (rocctx, error)) goto out; target_current_root = glnx_readlinkat_malloc (rocctx->roots_dfd, name, cancellable, error); if (!target_current_root) { g_prefix_error (error, "Reading app link %s: ", name); goto out; } if (!parse_app_version (target_current_root, ¤t_version, error)) goto out; { g_autoptr(GVariantDict) metadata_dict = NULL; g_autoptr(GVariant) spec_v = NULL; g_autoptr(GVariant) previous_sha512_v = NULL; if (!ostree_repo_resolve_rev (rocctx->repo, name, FALSE, &commit_checksum, error)) goto out; if (!ostree_repo_load_variant (rocctx->repo, OSTREE_OBJECT_TYPE_COMMIT, commit_checksum, &commit, error)) goto out; metadata = g_variant_get_child_value (commit, 0); metadata_dict = g_variant_dict_new (metadata); spec_v = _rpmostree_vardict_lookup_value_required (metadata_dict, "rpmostree.spec", (GVariantType*)"a{sv}", error); if (!spec_v) goto out; treespec = rpmostree_treespec_new (spec_v); previous_sha512_v = _rpmostree_vardict_lookup_value_required (metadata_dict, "rpmostree.state-sha512", (GVariantType*)"s", error); if (!previous_sha512_v) goto out; previous_state_sha512 = g_variant_dup_string (previous_sha512_v, NULL); } new_version = current_version == 0 ? 1 : 0; if (new_version == 0) target_new_root = glnx_strjoina (name, ".0"); else target_new_root = glnx_strjoina (name, ".1"); if (!roc_context_prepare_for_root (rocctx, name, treespec, cancellable, error)) goto out; /* --- Downloading metadata --- */ if (!rpmostree_context_download_metadata (rocctx->ctx, cancellable, error)) goto out; /* --- Resolving dependencies --- */ if (!rpmostree_context_prepare_install (rocctx->ctx, &install, cancellable, error)) goto out; { g_autofree char *new_state_sha512 = rpmostree_context_get_state_sha512 (rocctx->ctx); if (strcmp (new_state_sha512, previous_state_sha512) == 0) { g_print ("No changes in inputs to %s (%s)\n", name, commit_checksum); exit_status = EXIT_SUCCESS; goto out; } } /* --- Download and import as necessary --- */ if (!rpmostree_context_download_import (rocctx->ctx, install, cancellable, error)) goto out; { glnx_fd_close int tmpdir_dfd = -1; if (!glnx_opendirat (rocctx->userroot_dfd, "tmp", TRUE, &tmpdir_dfd, error)) goto out; if (!rpmostree_context_assemble_commit (rocctx->ctx, tmpdir_dfd, name, install, &new_commit_checksum, cancellable, error)) goto out; } g_print ("Checking out %s @ %s...\n", name, new_commit_checksum); { OstreeRepoCheckoutOptions opts = { OSTREE_REPO_CHECKOUT_MODE_USER, OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES, }; /* For now... to be crash safe we'd need to duplicate some of the * boot-uuid/fsync gating at a higher level. */ opts.disable_fsync = TRUE; if (!ostree_repo_checkout_tree_at (rocctx->repo, &opts, rocctx->roots_dfd, target_new_root, new_commit_checksum, cancellable, error)) goto out; } g_print ("Checking out %s @ %s...done\n", name, new_commit_checksum); if (!symlink_at_replace (target_new_root, rocctx->roots_dfd, name, cancellable, error)) goto out; g_print ("Creating current symlink...done\n"); exit_status = EXIT_SUCCESS; out: return exit_status; }
static gboolean parse_deployment (OstreeSysroot *self, const char *boot_link, OstreeDeployment **out_deployment, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *relative_boot_link; glnx_unref_object OstreeDeployment *ret_deployment = NULL; int entry_boot_version; int treebootserial = -1; int deployserial = -1; g_autofree char *osname = NULL; g_autofree char *bootcsum = NULL; g_autofree char *treecsum = NULL; glnx_fd_close int deployment_dfd = -1; const char *deploy_basename; g_autofree char *treebootserial_target = NULL; g_autofree char *deploy_dir = NULL; GKeyFile *origin = NULL; if (!ensure_sysroot_fd (self, error)) goto out; if (!parse_bootlink (boot_link, &entry_boot_version, &osname, &bootcsum, &treebootserial, error)) goto out; relative_boot_link = boot_link; if (*relative_boot_link == '/') relative_boot_link++; treebootserial_target = glnx_readlinkat_malloc (self->sysroot_fd, relative_boot_link, cancellable, error); if (!treebootserial_target) goto out; deploy_basename = glnx_basename (treebootserial_target); if (!_ostree_sysroot_parse_deploy_path_name (deploy_basename, &treecsum, &deployserial, error)) goto out; if (!glnx_opendirat (self->sysroot_fd, relative_boot_link, TRUE, &deployment_dfd, error)) goto out; if (!parse_origin (self, deployment_dfd, deploy_basename, &origin, cancellable, error)) goto out; ret_deployment = ostree_deployment_new (-1, osname, treecsum, deployserial, bootcsum, treebootserial); if (origin) ostree_deployment_set_origin (ret_deployment, origin); ret = TRUE; gs_transfer_out_value (out_deployment, &ret_deployment); out: if (origin) g_key_file_unref (origin); return ret; }