diff --git a/src/libostree/ostree-repo-checkout.c b/src/libostree/ostree-repo-checkout.c index 6317c9e85e..fcae6a7f77 100644 --- a/src/libostree/ostree-repo-checkout.c +++ b/src/libostree/ostree-repo-checkout.c @@ -586,6 +586,7 @@ checkout_one_file_at (OstreeRepo *repo, const gboolean is_symlink = (g_file_info_get_file_type (source_info) == G_FILE_TYPE_SYMBOLIC_LINK); const gboolean is_whiteout = (!is_symlink && options->process_whiteouts && g_str_has_prefix (destination_name, WHITEOUT_PREFIX)); + const gboolean is_reg_zerosized = (!is_symlink && g_file_info_get_size (source_info) == 0); /* First, see if it's a Docker whiteout, * https://github.com/docker/docker/blob/1a714e76a2cb9008cd19609059e9988ff1660b78/pkg/archive/whiteouts.go @@ -604,6 +605,10 @@ checkout_one_file_at (OstreeRepo *repo, need_copy = FALSE; } + else if (options->force_copy_zerosized && is_reg_zerosized) + { + need_copy = TRUE; + } else if (!options->force_copy) { HardlinkResult hardlink_res = HARDLINK_RESULT_NOT_SUPPORTED; @@ -699,6 +704,7 @@ checkout_one_file_at (OstreeRepo *repo, if (can_cache && !is_whiteout && !is_symlink + && !is_reg_zerosized && need_copy && repo->mode == OSTREE_REPO_MODE_ARCHIVE && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER) @@ -762,7 +768,7 @@ checkout_one_file_at (OstreeRepo *repo, * succeeded at hardlinking above. */ if (options->no_copy_fallback) - g_assert (is_bare_user_symlink); + g_assert (is_bare_user_symlink || is_reg_zerosized); if (!ostree_repo_load_file (repo, checksum, &input, NULL, &xattrs, cancellable, error)) return FALSE; diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index a65447615e..eddbbf8701 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -933,7 +933,8 @@ typedef struct { gboolean no_copy_fallback; gboolean force_copy; /* Since: 2017.6 */ gboolean bareuseronly_dirs; /* Since: 2017.7 */ - gboolean unused_bools[5]; + gboolean force_copy_zerosized; /* Since: 2018.9 */ + gboolean unused_bools[4]; /* 4 byte hole on 64 bit */ const char *subpath; diff --git a/src/ostree/ot-builtin-checkout.c b/src/ostree/ot-builtin-checkout.c index e7d6a63464..1519e34efc 100644 --- a/src/ostree/ot-builtin-checkout.c +++ b/src/ostree/ot-builtin-checkout.c @@ -45,6 +45,7 @@ static char *opt_from_file; static gboolean opt_disable_fsync; static gboolean opt_require_hardlinks; static gboolean opt_force_copy; +static gboolean opt_force_copy_zerosized; static gboolean opt_bareuseronly_dirs; static char *opt_skiplist_file; static char *opt_selinux_policy; @@ -84,6 +85,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-zerosized", 'z', 0, G_OPTION_ARG_NONE, &opt_force_copy_zerosized, "Do not hardlink zero-sized files", NULL }, { "force-copy", 'C', 0, G_OPTION_ARG_NONE, &opt_force_copy, "Never hardlink (but may reflink if available)", NULL }, { "bareuseronly-dirs", 'M', 0, G_OPTION_ARG_NONE, &opt_bareuseronly_dirs, "Suppress mode bits outside of 0775 for directories (suid, world writable, etc.)", NULL }, { "skip-list", 0, 0, G_OPTION_ARG_FILENAME, &opt_skiplist_file, "File containing list of files to skip", "PATH" }, @@ -130,7 +132,8 @@ process_one_checkout (OstreeRepo *repo, * convenient infrastructure for testing C APIs with data. */ if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks || - opt_union_add || opt_force_copy || opt_bareuseronly_dirs || opt_union_identical || + opt_union_add || opt_force_copy || opt_force_copy_zerosized || + opt_bareuseronly_dirs || opt_union_identical || opt_skiplist_file || opt_selinux_policy || opt_selinux_prefix) { OstreeRepoCheckoutAtOptions options = { 0, }; @@ -218,6 +221,7 @@ process_one_checkout (OstreeRepo *repo, options.no_copy_fallback = opt_require_hardlinks; options.force_copy = opt_force_copy; + options.force_copy_zerosized = opt_force_copy_zerosized; options.bareuseronly_dirs = opt_bareuseronly_dirs; if (!ostree_repo_checkout_at (repo, &options, diff --git a/tests/basic-test.sh b/tests/basic-test.sh index a0c2f1f763..a817b9d152 100644 --- a/tests/basic-test.sh +++ b/tests/basic-test.sh @@ -21,7 +21,7 @@ set -euo pipefail -echo "1..$((83 + ${extra_basic_tests:-0}))" +echo "1..$((84 + ${extra_basic_tests:-0}))" CHECKOUT_U_ARG="" CHECKOUT_H_ARGS="-H" @@ -694,6 +694,21 @@ for v in bin link; do done echo "ok checkout union identical conflicts" +cd ${test_tmpdir} +rm files -rf && mkdir files +touch files/anemptyfile +touch files/anotheremptyfile +$CMD_PREFIX ostree --repo=repo commit --consume -b tree-with-empty-files --tree=dir=files +$CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} -z tree-with-empty-files tree-with-empty-files +if files_are_hardlinked tree-with-empty-files/an{,other}emptyfile; then + fatal "--force-copy-zerosized failed" +fi +rm tree-with-empty-files -rf +$CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} tree-with-empty-files tree-with-empty-files +assert_files_hardlinked tree-with-empty-files/an{,other}emptyfile +rm tree-with-empty-files -rf +echo "ok checkout --force-copy-zerosized" + cd ${test_tmpdir} rm files -rf && mkdir files mkdir files/worldwritable-dir