Skip to content

Commit

Permalink
checkout: Provide useful error with checkout -H and incompat mode
Browse files Browse the repository at this point in the history
Previously we'd assert and dump core if one used `checkout -H` without
`-U` on a bare-user repo, because we'd hit the bare-user symlink case.

Rework the code to handle this, and add tests. I hit this when I was going to
suggest to someone to use `-H` to ensure they were getting hardlinks.

Closes: #779
Approved by: jlebon
  • Loading branch information
cgwalters authored and rh-atomic-bot committed Apr 12, 2017
1 parent 6a7ee48 commit d3385a3
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 16 deletions.
48 changes: 38 additions & 10 deletions src/libostree/ostree-repo-checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ checkout_one_file_at (OstreeRepo *repo,
gboolean ret = FALSE;
const char *checksum;
gboolean is_symlink;
gboolean is_bare_user_symlink = FALSE;
gboolean can_cache;
gboolean need_copy = TRUE;
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
Expand All @@ -431,7 +432,6 @@ checkout_one_file_at (OstreeRepo *repo,
gboolean is_whiteout;

is_symlink = g_file_info_get_file_type (source_info) == G_FILE_TYPE_SYMBOLIC_LINK;

checksum = ostree_repo_file_get_checksum ((OstreeRepoFile*)source);

is_whiteout = !is_symlink && options->process_whiteouts &&
Expand Down Expand Up @@ -468,20 +468,42 @@ checkout_one_file_at (OstreeRepo *repo,

while (current_repo)
{
gboolean is_bare = ((current_repo->mode == OSTREE_REPO_MODE_BARE
&& options->mode == OSTREE_REPO_CHECKOUT_MODE_NONE) ||
(current_repo->mode == OSTREE_REPO_MODE_BARE_USER
&& options->mode == OSTREE_REPO_CHECKOUT_MODE_USER
/* NOTE: bare-user symlinks are not stored as symlinks */
&& !is_symlink) ||
(current_repo->mode == OSTREE_REPO_MODE_BARE_USER_ONLY
&& options->mode == OSTREE_REPO_CHECKOUT_MODE_USER));
/* TODO - Hoist this up to the toplevel at least for checking out from
* !parent; don't need to compute it for each file.
*/
gboolean repo_is_usermode =
current_repo->mode == OSTREE_REPO_MODE_BARE_USER ||
current_repo->mode == OSTREE_REPO_MODE_BARE_USER_ONLY;
/* We're hardlinkable if the checkout mode matches the repo mode */
gboolean is_hardlinkable =
(current_repo->mode == OSTREE_REPO_MODE_BARE
&& options->mode == OSTREE_REPO_CHECKOUT_MODE_NONE) ||
(repo_is_usermode && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER);
gboolean is_bare = is_hardlinkable && !is_bare_user_symlink;
gboolean current_can_cache = (options->enable_uncompressed_cache
&& current_repo->enable_uncompressed_cache);
gboolean is_archive_z2_with_cache = (current_repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2
&& options->mode == OSTREE_REPO_CHECKOUT_MODE_USER
&& current_can_cache);

/* NOTE: bare-user symlinks are not stored as symlinks; see
* https://github.com/ostreedev/ostree/commit/47c612e5a0688c3452a125655a245e8f4f01b2b0
* as well as write_object().
*/
is_bare_user_symlink = (repo_is_usermode && is_symlink);

/* Verify if no_copy_fallback is set that we can hardlink, with a
* special exception for bare-user symlinks.
*/
if (options->no_copy_fallback && !is_hardlinkable && !is_bare_user_symlink)
{
glnx_throw (error,
repo_is_usermode ?
"User repository mode requires user checkout mode to hardlink" :
"Bare repository mode cannot hardlink in user checkout mode");
goto out;
}

/* But only under these conditions */
if (is_bare || is_archive_z2_with_cache)
{
Expand Down Expand Up @@ -596,7 +618,13 @@ checkout_one_file_at (OstreeRepo *repo,
/* Fall back to copy if we couldn't hardlink */
if (need_copy)
{
g_assert (!options->no_copy_fallback);
/* Bare user mode can't hardlink symlinks, so we need to do a copy for
* those. (Although in the future we could hardlink inside checkouts) This
* assertion is intended to ensure that for regular files at least, we
* succeeded at hardlinking above.
*/
if (options->no_copy_fallback)
g_assert (is_bare_user_symlink);
if (!ostree_repo_load_file (repo, checksum, &input, NULL, &xattrs,
cancellable, error))
goto out;
Expand Down
44 changes: 38 additions & 6 deletions tests/basic-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,45 @@ if grep bare-user-only repo/config; then
CHECKOUT_U_ARG="-U"
fi

validate_checkout_basic() {
(cd $1;
assert_has_file firstfile
assert_has_file baz/cow
assert_file_has_content baz/cow moo
assert_has_file baz/deeper/ohyeah
)
}

$OSTREE checkout test2 checkout-test2
validate_checkout_basic checkout-test2
echo "ok checkout"

# Note this tests bare-user *and* bare-user-only
rm checkout-test2 -rf
if grep bare-user repo/config; then
$OSTREE checkout -U -H test2 checkout-test2
else
$OSTREE checkout -H test2 checkout-test2
fi
validate_checkout_basic checkout-test2
rm checkout-test2 -rf
# Only do these tests on bare-user/bare, not bare-user-only
# since the latter automatically synthesizes -U if it's not passed.
if ! grep -q bare-user-only repo/config; then
if grep -q bare-user repo/config; then
if $OSTREE checkout -H test2 checkout-test2 2>err.txt; then
assert_not_reached "checkout -H worked?"
fi
assert_file_has_content err.txt "User repository.*requires.*user"
else
if $OSTREE checkout -U -H test2 checkout-test2 2>err.txt; then
assert_not_reached "checkout -H worked?"
fi
assert_file_has_content err.txt "Bare repository mode cannot hardlink in user"
fi
fi
echo "ok checkout -H"

$OSTREE rev-parse test2
$OSTREE rev-parse 'test2^'
$OSTREE rev-parse 'test2^^' 2>/dev/null && fatal "rev-parse test2^^ unexpectedly succeeded!"
Expand All @@ -64,13 +100,9 @@ ostree_repo_init test-repo --mode=bare-user
rm test-repo -rf
echo "ok repo-init on existing repo"

rm checkout-test2 -rf
$OSTREE checkout test2 checkout-test2
cd checkout-test2
assert_has_file firstfile
assert_has_file baz/cow
assert_file_has_content baz/cow moo
assert_has_file baz/deeper/ohyeah
echo "ok content"

rm firstfile
$OSTREE commit ${COMMIT_ARGS} -b test2 -s delete

Expand Down

0 comments on commit d3385a3

Please sign in to comment.