From 6060abbb4bd8751cc69a320b4e8e5ff058a1226b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 21 Apr 2017 15:43:17 -0400 Subject: [PATCH] repo: Add a "force copy" flag to checkout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is intended to be used for copying `/usr/etc` → `/etc` for deployments. A TODO here is to use `glnx_file_copy_at()` if the repo mode allows it - then we'd use reflinks if available. Closes: #804 Approved by: jlebon --- src/libostree/ostree-repo-checkout.c | 4 +++- src/libostree/ostree-repo.h | 3 ++- src/ostree/ot-builtin-checkout.c | 10 +++++++++- tests/basic-test.sh | 17 ++++++++++++++--- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/libostree/ostree-repo-checkout.c b/src/libostree/ostree-repo-checkout.c index f48aff9e..5b87c7ec 100644 --- a/src/libostree/ostree-repo-checkout.c +++ b/src/libostree/ostree-repo-checkout.c @@ -403,7 +403,7 @@ checkout_one_file_at (OstreeRepo *repo, need_copy = FALSE; } - else + else if (!options->force_copy) { HardlinkResult hardlink_res = HARDLINK_RESULT_NOT_SUPPORTED; /* Try to do a hardlink first, if it's a regular file. This also @@ -895,6 +895,8 @@ ostree_repo_checkout_at (OstreeRepo *self, if (ostree_repo_get_mode (self) == OSTREE_REPO_MODE_BARE_USER_ONLY) options->mode = OSTREE_REPO_CHECKOUT_MODE_USER; + g_return_val_if_fail (!(options->force_copy && options->no_copy_fallback), FALSE); + g_autoptr(GFile) commit_root = (GFile*) _ostree_repo_file_new_for_commit (self, commit, error); if (!commit_root) return FALSE; diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 1664d65d..2e34c21c 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -767,7 +767,8 @@ typedef struct { gboolean enable_fsync; /* Deprecated */ gboolean process_whiteouts; gboolean no_copy_fallback; - gboolean unused_bools[7]; + gboolean force_copy; /* Since: 2017.6 */ + gboolean unused_bools[6]; const char *subpath; diff --git a/src/ostree/ot-builtin-checkout.c b/src/ostree/ot-builtin-checkout.c index 74e27cfb..9ba804f8 100644 --- a/src/ostree/ot-builtin-checkout.c +++ b/src/ostree/ot-builtin-checkout.c @@ -42,6 +42,7 @@ static gboolean opt_from_stdin; static char *opt_from_file; static gboolean opt_disable_fsync; static gboolean opt_require_hardlinks; +static gboolean opt_force_copy; static gboolean parse_fsync_cb (const char *option_name, @@ -71,6 +72,7 @@ static GOptionEntry options[] = { { "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", "FILE" }, { "fsync", 0, 0, G_OPTION_ARG_CALLBACK, parse_fsync_cb, "Specify how to invoke fsync()", "POLICY" }, { "require-hardlinks", 'H', 0, G_OPTION_ARG_NONE, &opt_require_hardlinks, "Do not fall back to full copies if hardlinking fails", NULL }, + { "force-copy", 'C', 0, G_OPTION_ARG_NONE, &opt_force_copy, "Never hardlink (but may reflink if available)", NULL }, { NULL } }; @@ -89,7 +91,7 @@ process_one_checkout (OstreeRepo *repo, * `ostree_repo_checkout_at` until such time as we have a more * convenient infrastructure for testing C APIs with data. */ - if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks || opt_union_add) + if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks || opt_union_add || opt_force_copy) { OstreeRepoCheckoutAtOptions options = { 0, }; @@ -102,6 +104,11 @@ process_one_checkout (OstreeRepo *repo, "Cannot specify both --union and --union-add"); goto out; } + if (opt_require_hardlinks && opt_force_copy) + { + glnx_throw (error, "Cannot specify both --require-hardlinks and --force-copy"); + goto out; + } else if (opt_union) options.overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES; else if (opt_union_add) @@ -111,6 +118,7 @@ process_one_checkout (OstreeRepo *repo, if (subpath) options.subpath = subpath; options.no_copy_fallback = opt_require_hardlinks; + options.force_copy = opt_force_copy; if (!ostree_repo_checkout_at (repo, &options, AT_FDCWD, destination, diff --git a/tests/basic-test.sh b/tests/basic-test.sh index f4b2b118..6ddf7b2e 100644 --- a/tests/basic-test.sh +++ b/tests/basic-test.sh @@ -19,7 +19,7 @@ set -euo pipefail -echo "1..65" +echo "1..66" $CMD_PREFIX ostree --version > version.yaml python -c 'import yaml; yaml.safe_load(open("version.yaml"))' @@ -28,7 +28,7 @@ echo "ok yaml version" CHECKOUT_U_ARG="" COMMIT_ARGS="" DIFF_ARGS="" -if grep bare-user-only repo/config; then +if grep -q bare-user-only repo/config; then # In bare-user-only repos we can only represent files with uid/gid 0, no # xattrs and canonical permissions, so we need to commit them as such, or # we end up with repos that don't pass fsck @@ -50,11 +50,14 @@ validate_checkout_basic() { $OSTREE checkout test2 checkout-test2 validate_checkout_basic checkout-test2 +if grep -q 'mode=bare$' repo/config; then + assert_not_streq $(stat -c '%h' checkout-test2/firstfile) 1 +fi echo "ok checkout" # Note this tests bare-user *and* bare-user-only rm checkout-test2 -rf -if grep bare-user repo/config; then +if grep -q bare-user repo/config; then $OSTREE checkout -U -H test2 checkout-test2 else $OSTREE checkout -H test2 checkout-test2 @@ -78,6 +81,14 @@ fi fi echo "ok checkout -H" +rm checkout-test2 -rf +$OSTREE checkout -C test2 checkout-test2 +for file in firstfile baz/cow baz/alink; do + assert_streq $(stat -c '%h' checkout-test2/$file) 1 +done + +echo "ok checkout -C" + $OSTREE rev-parse test2 $OSTREE rev-parse 'test2^' $OSTREE rev-parse 'test2^^' 2>/dev/null && fatal "rev-parse test2^^ unexpectedly succeeded!"