static gboolean do_checkout (OtAdminDeploy *self, const char *deploy_target, const char *revision, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; ot_lobj GFile *deploy_path = NULL; ot_lobj GFile *deploy_parent = NULL; ot_lfree char *tree_ref = NULL; ot_lptrarray GPtrArray *checkout_args = NULL; deploy_path = ot_gfile_from_build_path ("/ostree", deploy_target, NULL); deploy_parent = g_file_get_parent (deploy_path); if (!ot_gfile_ensure_directory (deploy_parent, TRUE, error)) goto out; checkout_args = g_ptr_array_new (); ot_ptrarray_add_many (checkout_args, "ostree", "--repo=/ostree/repo", "checkout", "--atomic-retarget", revision ? revision : deploy_target, ot_gfile_get_path_cached (deploy_path), NULL); g_ptr_array_add (checkout_args, NULL); if (!ot_spawn_sync_checked ("/ostree", (char**)checkout_args->pdata, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, error)) goto out; ret = TRUE; out: return ret; }
/** * ot_gfile_rename: * @from: Current path * @to: New path * @cancellable: a #GCancellable * @error: a #GError * * This function wraps the raw Unix function rename(). * * Returns: %TRUE on success, %FALSE on error */ gboolean ot_gfile_rename (GFile *from, GFile *to, GCancellable *cancellable, GError **error) { if (g_cancellable_set_error_if_cancelled (cancellable, error)) return FALSE; if (rename (ot_gfile_get_path_cached (from), ot_gfile_get_path_cached (to)) < 0) { ot_util_set_error_from_errno (error, errno); return FALSE; } return TRUE; }
static gboolean update_grub (const char *release, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; ot_lobj GFile *grub_path = g_file_new_for_path ("/boot/grub/grub.conf"); if (g_file_query_exists (grub_path, cancellable)) { gboolean have_grub_entry; if (!grep_literal (grub_path, "OSTree", &have_grub_entry, cancellable, error)) goto out; if (!have_grub_entry) { ot_lptrarray GPtrArray *grubby_args = NULL; ot_lfree char *add_kernel_arg = NULL; ot_lfree char *initramfs_arg = NULL; ot_lobj GFile *kernel_path = NULL; if (!get_kernel_path_from_release (release, &kernel_path, cancellable, error)) goto out; if (kernel_path == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Couldn't find kernel for release %s", release); goto out; } grubby_args = g_ptr_array_new (); add_kernel_arg = g_strconcat ("--add-kernel=", ot_gfile_get_path_cached (kernel_path), NULL); initramfs_arg = g_strconcat ("--initrd=", "/boot/initramfs-ostree-", release, ".img", NULL); ot_ptrarray_add_many (grubby_args, "grubby", "--grub", add_kernel_arg, initramfs_arg, "--copy-default", "--title=OSTree", NULL); g_ptr_array_add (grubby_args, NULL); g_print ("Adding OSTree grub entry...\n"); if (!ot_spawn_sync_checked (NULL, (char**)grubby_args->pdata, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, error)) goto out; } else g_print ("Already have OSTree entry in grub config\n"); } else { g_print ("/boot/grub/grub.conf not found, assuming you have GRUB 2\n"); } ret = TRUE; out: return ret; }
gboolean ostree_get_xattrs_for_file (GFile *f, GVariant **out_xattrs, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *path; ssize_t bytes_read; ot_lvariant GVariant *ret_xattrs = NULL; ot_lfree char *xattr_names = NULL; ot_lfree char *xattr_names_canonical = NULL; GVariantBuilder builder; gboolean builder_initialized = FALSE; path = ot_gfile_get_path_cached (f); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)")); builder_initialized = TRUE; bytes_read = llistxattr (path, NULL, 0); if (bytes_read < 0) { if (errno != ENOTSUP) { ot_util_set_error_from_errno (error, errno); goto out; } } else if (bytes_read > 0) { xattr_names = g_malloc (bytes_read); if (llistxattr (path, xattr_names, bytes_read) < 0) { ot_util_set_error_from_errno (error, errno); goto out; } xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read); if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, &builder, error)) goto out; } ret_xattrs = g_variant_builder_end (&builder); g_variant_ref_sink (ret_xattrs); ret = TRUE; ot_transfer_out_value (out_xattrs, &ret_xattrs); out: if (!builder_initialized) g_variant_builder_clear (&builder); return ret; }
/** * @root: (allow-none): Change to this root; if %NULL, don't chroot * * Triggers are a set of programs to run on a root to regenerate cache * files. This API call will simply run them against the given root. */ gboolean ostree_run_triggers_in_root (GFile *root, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int estatus; ot_lfree char *rel_triggerdir = NULL; ot_lobj GFile *triggerdir = NULL; ot_lptrarray GPtrArray *argv = NULL; rel_triggerdir = g_build_filename ("usr", "libexec", "ostree", "triggers.d", NULL); if (root) triggerdir = g_file_resolve_relative_path (root, rel_triggerdir); else triggerdir = g_file_new_for_path (rel_triggerdir); if (g_file_query_exists (triggerdir, cancellable)) { argv = g_ptr_array_new (); if (root) { g_ptr_array_add (argv, "linux-user-chroot"); g_ptr_array_add (argv, "--unshare-pid"); g_ptr_array_add (argv, "--unshare-ipc"); /* FIXME - unshare net too */ g_ptr_array_add (argv, "--mount-proc"); g_ptr_array_add (argv, "/proc"); g_ptr_array_add (argv, "--mount-bind"); g_ptr_array_add (argv, "/dev"); g_ptr_array_add (argv, "/dev"); g_ptr_array_add (argv, (char*)ot_gfile_get_path_cached (root)); } g_ptr_array_add (argv, "ostree-run-triggers"); g_ptr_array_add (argv, NULL); if (!g_spawn_sync (NULL, (char**)argv->pdata, (char**) ostree_get_sysroot_environ (), G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &estatus, error)) goto out; if (!g_spawn_check_exit_status (estatus, error)) goto out; } ret = TRUE; out: return ret; }
/** * ot_gfile_unlink: * @path: Path to file * @cancellable: a #GCancellable * @error: a #GError * * Like g_file_delete(), except this function does not follow Unix * symbolic links, and will delete a symbolic link even if it's * pointing to a nonexistent file. In other words, this function * merely wraps the raw Unix function unlink(). * * Returns: %TRUE on success, %FALSE on error */ gboolean ot_gfile_unlink (GFile *path, GCancellable *cancellable, GError **error) { if (g_cancellable_set_error_if_cancelled (cancellable, error)) return FALSE; if (unlink (ot_gfile_get_path_cached (path)) < 0) { ot_util_set_error_from_errno (error, errno); return FALSE; } return TRUE; }
gboolean ostree_set_xattrs (GFile *f, GVariant *xattrs, GCancellable *cancellable, GError **error) { const char *path; gboolean ret = FALSE; int i, n; path = ot_gfile_get_path_cached (f); n = g_variant_n_children (xattrs); for (i = 0; i < n; i++) { const guint8* name; GVariant *value; const guint8* value_data; gsize value_len; gboolean loop_err; g_variant_get_child (xattrs, i, "(^&ay@ay)", &name, &value); value_data = g_variant_get_fixed_array (value, &value_len, 1); loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, XATTR_REPLACE) < 0; g_clear_pointer (&value, (GDestroyNotify) g_variant_unref); if (loop_err) { ot_util_set_error_from_errno (error, errno); goto out; } } ret = TRUE; out: return ret; }
/** * Return in @out_variant the result of memory-mapping the entire * contents of file @src. * * Note the returned @out_variant is not floating. */ gboolean ot_util_variant_map (GFile *src, const GVariantType *type, gboolean trusted, GVariant **out_variant, GError **error) { gboolean ret = FALSE; const char *path = NULL; ot_lvariant GVariant *ret_variant = NULL; GMappedFile *mfile = NULL; int fd; path = ot_gfile_get_path_cached (src); if (!ot_unix_open_noatime (path, &fd, error)) goto out; mfile = g_mapped_file_new_from_fd (fd, FALSE, error); if (!mfile) goto out; if (!ot_unix_close (fd, error)) goto out; ret_variant = g_variant_new_from_data (type, g_mapped_file_get_contents (mfile), g_mapped_file_get_length (mfile), trusted, (GDestroyNotify) g_mapped_file_unref, mfile); mfile = NULL; g_variant_ref_sink (ret_variant); ret = TRUE; ot_transfer_out_value(out_variant, &ret_variant); out: if (mfile) g_mapped_file_unref (mfile); return ret; }
static gboolean update_initramfs (const char *release, const char *deploy_target, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; ot_lfree char *initramfs_name = NULL; ot_lobj GFile *initramfs_file = NULL; ot_lfree char *last_deploy_path = NULL; initramfs_name = g_strconcat ("initramfs-ostree-", release, ".img", NULL); initramfs_file = ot_gfile_from_build_path ("/boot", initramfs_name, NULL); if (!g_file_query_exists (initramfs_file, NULL)) { ot_lptrarray GPtrArray *mkinitramfs_args = NULL; ot_lobj GFile *tmpdir = NULL; ot_lfree char *initramfs_tmp_path = NULL; ot_lfree char *ostree_vardir = NULL; ot_lfree char *ostree_moduledir = NULL; ot_lobj GFile *initramfs_tmp_file = NULL; ot_lobj GFileInfo *initramfs_tmp_info = NULL; if (!ostree_create_temp_dir (NULL, "ostree-initramfs", NULL, &tmpdir, cancellable, error)) goto out; ostree_vardir = g_build_filename (opt_ostree_dir, "var", NULL); ostree_moduledir = g_build_filename (opt_ostree_dir, "modules", NULL); last_deploy_path = g_build_filename (opt_ostree_dir, deploy_target, NULL); mkinitramfs_args = g_ptr_array_new (); /* Note: the hardcoded /tmp path below is not actually a * security flaw, because we've bind-mounted dracut's view * of /tmp to the securely-created tmpdir above. */ ot_ptrarray_add_many (mkinitramfs_args, "linux-user-chroot", "--mount-readonly", "/", "--mount-proc", "/proc", "--mount-bind", "/dev", "/dev", "--mount-bind", ostree_vardir, "/var", "--mount-bind", ot_gfile_get_path_cached (tmpdir), "/tmp", "--mount-bind", ostree_moduledir, "/lib/modules", last_deploy_path, "dracut", "-f", "/tmp/initramfs-ostree.img", release, NULL); g_ptr_array_add (mkinitramfs_args, NULL); g_print ("Generating initramfs using %s...\n", last_deploy_path); if (!ot_spawn_sync_checked (NULL, (char**)mkinitramfs_args->pdata, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, error)) goto out; initramfs_tmp_file = g_file_get_child (tmpdir, "initramfs-ostree.img"); initramfs_tmp_info = g_file_query_info (initramfs_tmp_file, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!initramfs_tmp_info) goto out; if (g_file_info_get_size (initramfs_tmp_info) == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Initramfs generation failed, check dracut.log"); goto out; } if (!g_file_copy (initramfs_tmp_file, initramfs_file, 0, cancellable, NULL, NULL, error)) goto out; g_print ("Created: %s\n", ot_gfile_get_path_cached (initramfs_file)); (void) ot_gfile_unlink (initramfs_tmp_file, NULL, NULL); (void) rmdir (ot_gfile_get_path_cached (tmpdir)); } ret = TRUE; out: return ret; }
static gboolean cp_internal (GFile *src, GFile *dest, gboolean use_hardlinks, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; ot_lobj GFileEnumerator *enumerator = NULL; ot_lobj GFileInfo *file_info = NULL; GError *temp_error = NULL; enumerator = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!enumerator) goto out; if (!ot_gfile_ensure_directory (dest, FALSE, error)) goto out; while ((file_info = g_file_enumerator_next_file (enumerator, cancellable, &temp_error)) != NULL) { const char *name = g_file_info_get_name (file_info); ot_lobj GFile *src_child = g_file_get_child (src, name); ot_lobj GFile *dest_child = g_file_get_child (dest, name); if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) { if (!ot_gfile_ensure_directory (dest_child, FALSE, error)) goto out; /* Can't do this even though we'd like to; it fails with an error about * setting standard::type not being supported =/ * if (!g_file_set_attributes_from_info (dest_child, file_info, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error)) goto out; */ if (chmod (ot_gfile_get_path_cached (dest_child), g_file_info_get_attribute_uint32 (file_info, "unix::mode")) == -1) { ot_util_set_error_from_errno (error, errno); goto out; } if (!cp_internal (src_child, dest_child, use_hardlinks, cancellable, error)) goto out; } else { gboolean did_link = FALSE; (void) unlink (ot_gfile_get_path_cached (dest_child)); if (use_hardlinks) { if (link (ot_gfile_get_path_cached (src_child), ot_gfile_get_path_cached (dest_child)) == -1) { if (!(errno == EMLINK || errno == EXDEV)) { ot_util_set_error_from_errno (error, errno); goto out; } use_hardlinks = FALSE; } else did_link = TRUE; } if (!did_link) { if (!g_file_copy (src_child, dest_child, G_FILE_COPY_OVERWRITE | G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS, cancellable, NULL, NULL, error)) goto out; } } g_clear_object (&file_info); } if (temp_error) { g_propagate_error (error, temp_error); goto out; } ret = TRUE; out: return ret; }
gboolean ostree_create_temp_file_from_input (GFile *dir, const char *prefix, const char *suffix, GFileInfo *finfo, GVariant *xattrs, GInputStream *input, GFile **out_file, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GError *temp_error = NULL; int i = 0; ot_lfree char *possible_name = NULL; ot_lobj GFile *possible_file = NULL; ot_lfree guchar *ret_csum = NULL; GString *tmp_name = NULL; tmp_name = create_tmp_string (ot_gfile_get_path_cached (dir), prefix, suffix); /* 128 attempts seems reasonable... */ for (i = 0; i < 128; i++) { if (g_cancellable_set_error_if_cancelled (cancellable, error)) goto out; g_free (possible_name); possible_name = subst_xxxxxx (tmp_name->str); g_clear_object (&possible_file); possible_file = g_file_get_child (dir, possible_name); if (!ostree_create_file_from_input (possible_file, finfo, xattrs, input, cancellable, &temp_error)) { if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { g_clear_error (&temp_error); continue; } else { g_propagate_error (error, temp_error); goto out; } } else { break; } } if (i >= 128) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Exhausted 128 attempts to create a temporary file"); goto out; } ret = TRUE; ot_transfer_out_value(out_file, &possible_file); out: if (tmp_name) g_string_free (tmp_name, TRUE); return ret; }
gboolean ostree_create_file_from_input (GFile *dest_file, GFileInfo *finfo, GVariant *xattrs, GInputStream *input, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *dest_path; guint32 uid, gid, mode; ot_lobj GFileOutputStream *out = NULL; if (g_cancellable_set_error_if_cancelled (cancellable, error)) return FALSE; if (finfo != NULL) { mode = g_file_info_get_attribute_uint32 (finfo, "unix::mode"); } else { mode = S_IFREG | 0664; } dest_path = ot_gfile_get_path_cached (dest_file); if (S_ISDIR (mode)) { if (mkdir (ot_gfile_get_path_cached (dest_file), mode) < 0) { ot_util_set_error_from_errno (error, errno); goto out; } } else if (S_ISREG (mode)) { out = g_file_create (dest_file, 0, cancellable, error); if (!out) goto out; if (input) { if (g_output_stream_splice ((GOutputStream*)out, input, 0, cancellable, error) < 0) goto out; } if (!g_output_stream_close ((GOutputStream*)out, NULL, error)) goto out; } else if (S_ISLNK (mode)) { const char *target = g_file_info_get_attribute_byte_string (finfo, "standard::symlink-target"); if (symlink (target, dest_path) < 0) { ot_util_set_error_from_errno (error, errno); goto out; } } else if (S_ISCHR (mode) || S_ISBLK (mode)) { guint32 dev = g_file_info_get_attribute_uint32 (finfo, "unix::rdev"); if (mknod (dest_path, mode, dev) < 0) { ot_util_set_error_from_errno (error, errno); goto out; } } else if (S_ISFIFO (mode)) { if (mkfifo (dest_path, mode) < 0) { ot_util_set_error_from_errno (error, errno); goto out; } } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid mode %u", mode); goto out; } if (finfo != NULL) { uid = g_file_info_get_attribute_uint32 (finfo, "unix::uid"); gid = g_file_info_get_attribute_uint32 (finfo, "unix::gid"); if (lchown (dest_path, uid, gid) < 0) { ot_util_set_error_from_errno (error, errno); g_prefix_error (error, "lchown(%u, %u) failed: ", uid, gid); goto out; } } if (!S_ISLNK (mode)) { if (chmod (dest_path, mode) < 0) { ot_util_set_error_from_errno (error, errno); g_prefix_error (error, "chmod(%u) failed: ", mode); goto out; } } if (xattrs != NULL) { if (!ostree_set_xattrs (dest_file, xattrs, cancellable, error)) goto out; } ret = TRUE; out: if (!ret && !S_ISDIR(mode)) { (void) unlink (dest_path); } return ret; }