diff --git a/Makefile-rpm-ostree.am b/Makefile-rpm-ostree.am index 5a21a634ed..656e8f5877 100644 --- a/Makefile-rpm-ostree.am +++ b/Makefile-rpm-ostree.am @@ -30,6 +30,7 @@ rpm_ostree_SOURCES = src/app/main.c \ src/app/rpmostree-builtin-rebase.c \ src/app/rpmostree-builtin-cleanup.c \ src/app/rpmostree-builtin-initramfs.c \ + src/app/rpmostree-builtin-livefs.c \ src/app/rpmostree-pkg-builtins.c \ src/app/rpmostree-builtin-status.c \ src/app/rpmostree-builtin-internals.c \ diff --git a/libglnx b/libglnx index abd37a4790..f25ed933f0 160000 --- a/libglnx +++ b/libglnx @@ -1 +1 @@ -Subproject commit abd37a4790f86f53bfb442e6d80e1710f50bff92 +Subproject commit f25ed933f02455d160d74c4db478452e5e9ada02 diff --git a/src/app/main.c b/src/app/main.c index 86d6da0c38..db091894fc 100644 --- a/src/app/main.c +++ b/src/app/main.c @@ -64,6 +64,7 @@ static RpmOstreeCommand legacy_alias_commands[] = { static RpmOstreeCommand experimental_commands[] = { { "internals", rpmostree_builtin_internals }, { "container", rpmostree_builtin_container }, + { "livefs", rpmostree_builtin_livefs }, { NULL } }; diff --git a/src/app/rpmostree-builtin-livefs.c b/src/app/rpmostree-builtin-livefs.c new file mode 100644 index 0000000000..845cd17279 --- /dev/null +++ b/src/app/rpmostree-builtin-livefs.c @@ -0,0 +1,100 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2017 Colin Walters + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include "rpmostree-builtins.h" +#include "rpmostree-libbuiltin.h" +#include "rpmostree-dbus-helpers.h" + +#include + +static gboolean opt_dry_run; +static gboolean opt_allow_replace; +static gboolean opt_accept_partial; + +static GOptionEntry option_entries[] = { + { "dry-run", 'n', 0, G_OPTION_ARG_NONE, &opt_dry_run, "Only perform analysis, do not make changes", NULL }, + { "allow-replace", 0, 0, G_OPTION_ARG_NONE, &opt_allow_replace, "Allow replacing existing files", NULL }, + { "accept-partial", 0, 0, G_OPTION_ARG_NONE, &opt_accept_partial, "Continue even on partial updates", NULL }, + { NULL } +}; + +static GVariant * +get_args_variant (void) +{ + GVariantDict dict; + + g_variant_dict_init (&dict, NULL); + g_variant_dict_insert (&dict, "dry-run", "b", opt_dry_run); + g_variant_dict_insert (&dict, "replace", "b", opt_allow_replace); + g_variant_dict_insert (&dict, "partial", "b", opt_accept_partial); + + return g_variant_dict_end (&dict); +} + +int +rpmostree_builtin_livefs (int argc, + char **argv, + GCancellable *cancellable, + GError **error) +{ + int exit_status = EXIT_FAILURE; + g_autoptr(GOptionContext) context = g_option_context_new ("- Apply pending deployment changes to booted deployment"); + glnx_unref_object RPMOSTreeOS *os_proxy = NULL; + glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL; + g_autofree char *transaction_address = NULL; + + if (!rpmostree_option_context_parse (context, + option_entries, + &argc, &argv, + RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT, + cancellable, + &sysroot_proxy, + error)) + goto out; + + if (!rpmostree_load_os_proxy (sysroot_proxy, NULL, + cancellable, &os_proxy, error)) + goto out; + + if (!rpmostree_os_call_live_fs_sync (os_proxy, + get_args_variant (), + &transaction_address, + cancellable, + error)) + goto out; + + if (!rpmostree_transaction_get_response_sync (sysroot_proxy, + transaction_address, + cancellable, + error)) + goto out; + + exit_status = EXIT_SUCCESS; +out: + /* Does nothing if using the message bus. */ + rpmostree_cleanup_peer (); + + return exit_status; +} diff --git a/src/app/rpmostree-builtins.h b/src/app/rpmostree-builtins.h index 8283246345..51364a6da0 100644 --- a/src/app/rpmostree-builtins.h +++ b/src/app/rpmostree-builtins.h @@ -50,6 +50,7 @@ BUILTINPROTO(rebase); BUILTINPROTO(cleanup); BUILTINPROTO(rollback); BUILTINPROTO(initramfs); +BUILTINPROTO(livefs); BUILTINPROTO(status); BUILTINPROTO(db); BUILTINPROTO(internals); diff --git a/src/daemon/rpmostreed-os.c b/src/daemon/rpmostreed-os.c index 3fc407c379..acaf114785 100644 --- a/src/daemon/rpmostreed-os.c +++ b/src/daemon/rpmostreed-os.c @@ -878,6 +878,26 @@ os_handle_cleanup (RPMOSTreeOS *interface, return TRUE; } +static RpmOstreeTransactionLiveFsFlags +livefs_flags_from_options (GVariant *options) +{ + RpmOstreeTransactionLiveFsFlags ret = 0; + GVariantDict options_dict; + gboolean opt = FALSE; + + g_variant_dict_init (&options_dict, options); + if (g_variant_dict_lookup (&options_dict, "dry-run", "b", &opt) && opt) + ret |= RPMOSTREE_TRANSACTION_LIVEFS_FLAG_DRY_RUN; + if (g_variant_dict_lookup (&options_dict, "replace", "b", &opt) && opt) + ret |= RPMOSTREE_TRANSACTION_LIVEFS_FLAG_ALLOW_REPLACE; + if (g_variant_dict_lookup (&options_dict, "partial", "b", &opt) && opt) + ret |= RPMOSTREE_TRANSACTION_LIVEFS_FLAG_IGNORE_NON_USR; + + g_variant_dict_clear (&options_dict); + + return ret; +} + static gboolean os_handle_live_fs (RPMOSTreeOS *interface, GDBusMethodInvocation *invocation, @@ -888,8 +908,6 @@ os_handle_live_fs (RPMOSTreeOS *interface, glnx_unref_object OstreeSysroot *ot_sysroot = NULL; g_autoptr(GCancellable) cancellable = g_cancellable_new (); g_autoptr(GVariantDict) dict = NULL; - const char *osname; - gboolean reboot = FALSE; GError *local_error = NULL; transaction = merge_compatible_txn (self, invocation); @@ -903,19 +921,11 @@ os_handle_live_fs (RPMOSTreeOS *interface, &local_error)) goto out; - osname = rpmostree_os_get_name (interface); - - dict = g_variant_dict_new (arg_options); - g_variant_dict_lookup (dict, "reboot", "b", &reboot); - - transaction = rpmostreed_transaction_new_initramfs_state (invocation, - ot_sysroot, - osname, - regenerate, - (char**)args, - reboot, - cancellable, - &local_error); + transaction = rpmostreed_transaction_new_livefs (invocation, + ot_sysroot, + livefs_flags_from_options (arg_options), + cancellable, + &local_error); if (transaction == NULL) goto out; @@ -1322,6 +1332,7 @@ rpmostreed_os_iface_init (RPMOSTreeOSIface *iface) iface->handle_pkg_change = os_handle_pkg_change; iface->handle_set_initramfs_state = os_handle_set_initramfs_state; iface->handle_cleanup = os_handle_cleanup; + iface->handle_live_fs = os_handle_live_fs; iface->handle_get_cached_rebase_rpm_diff = os_handle_get_cached_rebase_rpm_diff; iface->handle_download_rebase_rpm_diff = os_handle_download_rebase_rpm_diff; iface->handle_get_cached_deploy_rpm_diff = os_handle_get_cached_deploy_rpm_diff; diff --git a/src/daemon/rpmostreed-transaction-types.c b/src/daemon/rpmostreed-transaction-types.c index 7b5b54d001..12d12a427c 100644 --- a/src/daemon/rpmostreed-transaction-types.c +++ b/src/daemon/rpmostreed-transaction-types.c @@ -19,6 +19,9 @@ #include "config.h" #include "ostree.h" +#include +#include +#include #include #include "rpmostreed-transaction-types.h" @@ -27,6 +30,7 @@ #include "rpmostreed-sysroot.h" #include "rpmostree-sysroot-upgrader.h" #include "rpmostree-util.h" +#include "rpmostree-output.h" #include "rpmostree-core.h" #include "rpmostreed-utils.h" @@ -1132,7 +1136,7 @@ rpmostreed_transaction_new_cleanup (GDBusMethodInvocation *invocation, typedef struct { RpmostreedTransaction parent; - gboolean allow_replacement; + RpmOstreeTransactionLiveFsFlags flags; } LiveFsTransaction; typedef RpmostreedTransactionClass LiveFsTransactionClass; @@ -1146,7 +1150,7 @@ G_DEFINE_TYPE (LiveFsTransaction, static void livefs_transaction_finalize (GObject *object) { - LiveFsTransaction *self; + G_GNUC_UNUSED LiveFsTransaction *self; self = (LiveFsTransaction *) object; @@ -1185,22 +1189,23 @@ path_is_rootfs (const char *path) return !g_str_has_prefix (path, "/usr/"); } -static gboolean -update_flags_for_path (const char *path, - gboolean is_addition, - CommitDiffFlags *inout_flags) +static void +update_diff_for_path (const char *path, + gboolean is_addition, + CommitDiffFlags *inout_flags, + guint *inout_changed) { /* We expect the rpmdb to change */ if (path_is_rpmdb (path)) ; else if (path_is_usretc (path)) - ret_flags |= COMMIT_DIFF_FLAGS_ETC; + (*inout_flags) |= COMMIT_DIFF_FLAGS_ETC; else if (path_is_boot (path)) - ret_flags |= COMMIT_DIFF_FLAGS_BOOT; + (*inout_flags) |= COMMIT_DIFF_FLAGS_BOOT; else if (path_is_rootfs (path)) - ret_flags |= COMMIT_DIFF_FLAGS_ROOTFS; - else - ret_flags |= COMMIT_DIFF_FLAGS_REPLACEMENT; + (*inout_flags) |= COMMIT_DIFF_FLAGS_ROOTFS; + else if (inout_changed) + (*inout_changed)++; } static gboolean @@ -1208,6 +1213,7 @@ analyze_commit_diff (OstreeRepo *repo, const char *from_rev, const char *to_rev, CommitDiffFlags *out_flags, + guint *out_n_replaced, GCancellable *cancellable, GError **error) { @@ -1217,12 +1223,11 @@ analyze_commit_diff (OstreeRepo *repo, g_autoptr(GPtrArray) removed = NULL; g_autoptr(GPtrArray) added = NULL; CommitDiffFlags ret_flags = 0; - - *out_flags = 0; + guint ret_replaced = 0; /* Shouldn't happen, but might as well fast path it in case */ if (strcmp (from_rev, to_rev) == 0) - return TRUE; + goto done; if (!ostree_repo_read_commit (repo, from_rev, &from_tree, NULL, cancellable, error)) @@ -1231,7 +1236,12 @@ analyze_commit_diff (OstreeRepo *repo, cancellable, error)) return FALSE; - if (!ostree_diff_dirs (0, from_tree, to_tree, &modified, &removed, &added, + + modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref); + removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + if (!ostree_diff_dirs (0, from_tree, to_tree, + modified, removed, added, cancellable, error)) return FALSE; @@ -1240,7 +1250,7 @@ analyze_commit_diff (OstreeRepo *repo, GFile *gfpath = removed->pdata[i]; const char *path = gs_file_get_path_cached (gfpath); - update_flags_for_path (path, FALSE, &ret_flags); + update_diff_for_path (path, FALSE, &ret_flags, &ret_replaced); } for (guint i = 0; i < modified->len; i++) @@ -1248,7 +1258,7 @@ analyze_commit_diff (OstreeRepo *repo, OstreeDiffItem *diffitem = modified->pdata[i]; const char *path = gs_file_get_path_cached (diffitem->src); - update_flags_for_path (path, FALSE, &ret_flags); + update_diff_for_path (path, FALSE, &ret_flags, &ret_replaced); } for (guint i = 0; i < added->len; i++) @@ -1256,11 +1266,16 @@ analyze_commit_diff (OstreeRepo *repo, GFile *added_f = added->pdata[i]; const char *path = gs_file_get_path_cached (added_f); - update_flags_for_path (path, TRUE, &ret_flags); + update_diff_for_path (path, TRUE, &ret_flags, NULL); } + + done: + *out_flags = ret_flags; + *out_n_replaced = ret_replaced; + return TRUE; } -OstreeDeployment * +static OstreeDeployment * get_rollback_deployment (OstreeSysroot *self, OstreeDeployment *booted) { @@ -1335,8 +1350,8 @@ prepare_rollback_deployment (OstreeSysroot *sysroot, static gboolean livefs_transaction_execute (RpmostreedTransaction *transaction, - GCancellable *cancellable, - GError **error) + GCancellable *cancellable, + GError **error) { LiveFsTransaction *self = (LiveFsTransaction *) transaction; OstreeSysroot *sysroot; @@ -1346,9 +1361,13 @@ livefs_transaction_execute (RpmostreedTransaction *transaction, glnx_unref_object OstreeRepo *repo = NULL; g_autofree char *deployment_path = NULL; glnx_fd_close int deployment_dfd = -1; - const char *src_csum; - OstreeRepoCheckoutAtOptions opts = { OSTREE_REPO_CHECKOUT_MODE_USER, - OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES, }; + const char *booted_csum; + const char *target_csum; + CommitDiffFlags diff_analysis_flags = 0; + guint n_replaced; + char current_xattr_csum[OSTREE_SHA256_STRING_LEN]; + gboolean have_current_replacement = FALSE; + static const char orig_rpmdb_path[] = "usr/share/rpm.rpmostree-orig"; sysroot = rpmostreed_transaction_get_sysroot (transaction); if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) @@ -1359,7 +1378,7 @@ livefs_transaction_execute (RpmostreedTransaction *transaction, { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not currently booted into an OSTree system"); - goto out; + return FALSE; } origin_merge_deployment = rpmostreed_get_origin_merge_deployment (sysroot, @@ -1370,40 +1389,191 @@ livefs_transaction_execute (RpmostreedTransaction *transaction, { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No pending deployment"); - goto out; + return FALSE; + } + + deployment_path = ostree_sysroot_get_deployment_dirpath (sysroot, booted_deployment); + if (!glnx_opendirat (ostree_sysroot_get_fd (sysroot), deployment_path, TRUE, + &deployment_dfd, error)) + return FALSE; + + booted_csum = ostree_deployment_get_csum (booted_deployment); + target_csum = ostree_deployment_get_csum (origin_merge_deployment); + + if (fgetxattr (deployment_dfd, LIVE_REPLACED_XATTR, current_xattr_csum, sizeof (current_xattr_csum)) < 0) + { + if (errno != ENODATA) + { + glnx_set_error_from_errno (error); + return FALSE; + } + } + else + have_current_replacement = TRUE; + + if (have_current_replacement) + { + if (strcmp (current_xattr_csum, booted_csum) == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Current overlay is already %s", booted_csum); + return FALSE; + } + g_print ("Note: Previous overlay was %s\n", current_xattr_csum); + } + + if (!analyze_commit_diff (repo, booted_csum, + target_csum, + &diff_analysis_flags, + &n_replaced, + cancellable, error)) + return FALSE; + + if (diff_analysis_flags == 0) + { + if ((self->flags & RPMOSTREE_TRANSACTION_LIVEFS_FLAG_DRY_RUN) > 0) + { + g_print ("livefs would be safe!\n"); + return TRUE; + } + } + else + { + if (diff_analysis_flags & COMMIT_DIFF_FLAGS_ETC) + { + g_print ("warning: livefs would change configuration in /etc\n"); + } + if (diff_analysis_flags & COMMIT_DIFF_FLAGS_REPLACEMENT) + { + g_print ("warning: livefs would replace %u files\n", n_replaced); + } + if (diff_analysis_flags & COMMIT_DIFF_FLAGS_ROOTFS) + { + g_print ("warning: livefs would add or replace content in /\n"); + } + if (diff_analysis_flags & COMMIT_DIFF_FLAGS_BOOT) + { + g_print ("warning: livefs would add or replace kernel/initramfs\n"); + } + + if ((self->flags & RPMOSTREE_TRANSACTION_LIVEFS_FLAG_DRY_RUN) > 0) + return TRUE; + else if ((diff_analysis_flags & COMMIT_DIFF_FLAGS_REPLACEMENT) > 0 + && (self->flags & RPMOSTREE_TRANSACTION_LIVEFS_FLAG_ALLOW_REPLACE) == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "livefs update would replace files in /usr, and replacement not enabled"); + return FALSE; + } + else if ((diff_analysis_flags & ~COMMIT_DIFF_FLAGS_REPLACEMENT) > 0 + && (self->flags & RPMOSTREE_TRANSACTION_LIVEFS_FLAG_IGNORE_NON_USR) == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "livefs update would perform changes beyond new files in /usr, and partial updates not enabled"); + return FALSE; + } } rollback_deployment = get_rollback_deployment (sysroot, booted_deployment); if (!rollback_deployment) { if (!prepare_rollback_deployment (sysroot, booted_deployment, cancellable, error)) - return FALSE; + { + g_prefix_error (error, "Preparing rollback: "); + return FALSE; + } } - if (!ostree_sysroot_deployment_set_mutable (self, booted_deployment, TRUE, + if (!ostree_sysroot_deployment_set_mutable (sysroot, booted_deployment, TRUE, cancellable, error)) - goto out; + { + g_prefix_error (error, "Setting deployment mutable: "); + return FALSE; + } + /* This xattr says which commit we're in the process of overlaying */ + if (fsetxattr (deployment_dfd, LIVE_INPROGRESS_XATTR, target_csum, OSTREE_SHA256_STRING_LEN, 0) < 0) + { + /* FIXME */ + if (errno == EPERM) + { + sd_journal_print (LOG_WARNING, "Got EPERM setting xattr '%s'?", LIVE_INPROGRESS_XATTR); + } + else + { + glnx_set_prefix_error_from_errno (error, "Setting %s", LIVE_INPROGRESS_XATTR); + return FALSE; + } + } + + rpmostree_output_task_begin ("Overlaying /usr"); + + /* Note we only overlay /usr. Note this should implicitly skip the rpmdb since + * we won't overlay new files right now, but we should *explicitly* skip it + * which would require some sort of skip support in checkout. + */ + { OstreeRepoCheckoutAtOptions usr_checkout_opts = { .mode = OSTREE_REPO_CHECKOUT_MODE_NONE, + .overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES, + .no_copy_fallback = TRUE, + .subpath = "/usr" }; + if (!ostree_repo_checkout_at (repo, &usr_checkout_opts, deployment_dfd, "usr", + target_csum, cancellable, error)) + return FALSE; + } - deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment); + /* Now, replace the rpmdb. First, ensure the temporary dir for the new version doesn't exist */ + if (!glnx_shutil_rm_rf_at (deployment_dfd, orig_rpmdb_path, cancellable, error)) + return FALSE; + /* Check out the new rpmdb */ + { OstreeRepoCheckoutAtOptions rpmdb_checkout_opts = { .mode = OSTREE_REPO_CHECKOUT_MODE_NONE, + .overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES, + .no_copy_fallback = TRUE, + .subpath = "/usr/share/rpm" }; - if (!glnx_opendirat (ostree_sysroot_get_fd (sysroot), deployment_path, TRUE, &deployment_dfd, error)) - goto out; - /* This xattr says which commit we're in the process of overlaying */ - if (fsetxattr (deployment_dfd, LIVE_INPROGRESS_XATTR, src_csum, 1, 0) < 0) + if (!ostree_repo_checkout_at (repo, &rpmdb_checkout_opts, deployment_dfd, orig_rpmdb_path, + target_csum, cancellable, error)) + return FALSE; + } + /* Now, RENAME_EXCHANGE the two */ + if (glnx_renameat2_exchange (deployment_dfd, "usr/share/rpm", deployment_dfd, orig_rpmdb_path) < 0) { - glnx_set_error_from_errno (error); - goto out; + glnx_set_prefix_error_from_errno (error, "%s", "rename(..., RENAME_EXCHANGE) for rpmdb"); + return FALSE; } + /* And nuke the old one */ + if (!glnx_shutil_rm_rf_at (deployment_dfd, orig_rpmdb_path, cancellable, error)) + return FALSE; - if (!ostree_repo_checkout_at (repo, &checkout_opts, deployment_dfd, )) + if (fsetxattr (deployment_dfd, LIVE_REPLACED_XATTR, target_csum, OSTREE_SHA256_STRING_LEN, 0) < 0) + { + if (errno == EPERM) + { + sd_journal_print (LOG_WARNING, "Got EPERM setting xattr '%s'?", LIVE_REPLACED_XATTR); + } + else + { + glnx_set_prefix_error_from_errno (error, "Setting %s", LIVE_REPLACED_XATTR); + return FALSE; + } + } - if (fsetxattr (deployment_dfd, LIVE_REPLACED_XATTR, src_csum, 1, 0) < 0) + if (fremovexattr (deployment_dfd, LIVE_INPROGRESS_XATTR) < 0) { - glnx_set_error_from_errno (error); - goto out; + /* If this somehow happens...hm, well, let's ignore it */ + if (errno == ENODATA) + sd_journal_print (LOG_WARNING, "Got ENODATA removing xattr '%s'?", LIVE_INPROGRESS_XATTR); + else if (errno == EPERM) + { + sd_journal_print (LOG_WARNING, "Got EPERM removing xattr '%s'?", LIVE_INPROGRESS_XATTR); + } + else + { + glnx_set_error_from_errno (error); + return FALSE; + } } + rpmostree_output_task_end ("done"); return TRUE; } @@ -1427,7 +1597,7 @@ livefs_transaction_init (LiveFsTransaction *self) RpmostreedTransaction * rpmostreed_transaction_new_livefs (GDBusMethodInvocation *invocation, OstreeSysroot *sysroot, - gboolean allow_replacement, + RpmOstreeTransactionLiveFsFlags flags, GCancellable *cancellable, GError **error) { @@ -1444,7 +1614,7 @@ rpmostreed_transaction_new_livefs (GDBusMethodInvocation *invocation, if (self != NULL) { - self->allow_replacement = allow_replacement; + self->flags = flags; } return (RpmostreedTransaction *) self; diff --git a/src/daemon/rpmostreed-transaction-types.h b/src/daemon/rpmostreed-transaction-types.h index ade16911ad..6322adfbc3 100644 --- a/src/daemon/rpmostreed-transaction-types.h +++ b/src/daemon/rpmostreed-transaction-types.h @@ -92,10 +92,15 @@ rpmostreed_transaction_new_cleanup (GDBusMethodInvocation *invocation, GCancellable *cancellable, GError **error); +typedef enum { + RPMOSTREE_TRANSACTION_LIVEFS_FLAG_DRY_RUN = (1 << 0), + RPMOSTREE_TRANSACTION_LIVEFS_FLAG_ALLOW_REPLACE = (1 << 1), + RPMOSTREE_TRANSACTION_LIVEFS_FLAG_IGNORE_NON_USR = (1 << 2) +} RpmOstreeTransactionLiveFsFlags; RpmostreedTransaction * -rpmostreed_transaction_live_fs (GDBusMethodInvocation *invocation, - OstreeSysroot *sysroot, - gboolean allow_replacement, - GCancellable *cancellable, - GError **error); +rpmostreed_transaction_new_livefs (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + RpmOstreeTransactionLiveFsFlags flags, + GCancellable *cancellable, + GError **error);