diff options
author | Colin Walters <walters@verbum.org> | 2017-10-19 11:18:00 -0400 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2017-10-20 13:20:27 +0000 |
commit | b8251d26bd22338ca65a9ea52c6ca5107e572738 (patch) | |
tree | b0f017edb07b4f2dc8085a4b393b91815dd15a1c | |
parent | 9166605e5acbffd2c4db4e8a374770830f900fbc (diff) | |
download | ostree-b8251d26bd22338ca65a9ea52c6ca5107e572738.tar.gz |
lib/checkout: For "process whiteouts" mode, replace directories too
I'm playing around with some ostree ⇔ OCI/Docker bits, and ran
into this while importing an OCI image that built from the Fedora
base image where `/home` is a regular directory, and I added a layer
that did the ostree bits of moving it to `/var` and leaving a symlink.
OCI/Docker supports this. Now since "process whiteouts" is really the
"enable OCI/Docker" mode, let's only replace dirs if that's enabled.
This leaves the `UNION_FILES` targeted for its original use case
which is unioning components/packages. (Although that use case itself
is now a bit superceded by `UNION_IDENTICAL`, but eh).
Closes: #1294
Approved by: jlebon
-rw-r--r-- | src/libostree/ostree-repo-checkout.c | 42 | ||||
-rw-r--r-- | src/libostree/ostree-repo.h | 2 | ||||
-rw-r--r-- | tests/basic-test.sh | 14 |
3 files changed, 53 insertions, 5 deletions
diff --git a/src/libostree/ostree-repo-checkout.c b/src/libostree/ostree-repo-checkout.c index d144d03e..c2639a22 100644 --- a/src/libostree/ostree-repo-checkout.c +++ b/src/libostree/ostree-repo-checkout.c @@ -228,8 +228,24 @@ create_file_copy_from_input_at (OstreeRepo *repo, return glnx_throw_errno_prefix (error, "symlinkat"); case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES: { - /* Unioning? Let's unlink and try again */ - (void) unlinkat (destination_dfd, destination_name, 0); + /* For unioning, we further bifurcate a bit; for the "process whiteouts" + * mode which is really "Docker/OCI", we need to match their semantics + * and handle replacing a directory with a symlink. See also equivalent + * bits for regular files in checkout_file_hardlink(). + */ + if (options->process_whiteouts) + { + if (!glnx_shutil_rm_rf_at (destination_dfd, destination_name, NULL, error)) + return FALSE; + } + else + { + if (unlinkat (destination_dfd, destination_name, 0) < 0) + { + if (G_UNLIKELY (errno != ENOENT)) + return glnx_throw_errno_prefix (error, "unlinkat(%s)", destination_name); + } + } if (symlinkat (target, destination_dfd, destination_name) < 0) return glnx_throw_errno_prefix (error, "symlinkat"); } @@ -309,7 +325,17 @@ create_file_copy_from_input_at (OstreeRepo *repo, /* Handled above */ break; case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES: - replace_mode = GLNX_LINK_TMPFILE_REPLACE; + /* Special case OCI/Docker - see similar code in checkout_file_hardlink() + * and above for symlinks. + */ + if (options->process_whiteouts) + { + if (!glnx_shutil_rm_rf_at (destination_dfd, destination_name, NULL, error)) + return FALSE; + /* Inherit the NOREPLACE default...we deleted whatever's there */ + } + else + replace_mode = GLNX_LINK_TMPFILE_REPLACE; break; case OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES: replace_mode = GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST; @@ -467,7 +493,15 @@ checkout_file_hardlink (OstreeRepo *self, /* Make a link with a temp name */ if (!hardlink_add_tmp_name (self, srcfd, loose_path, tmpname, cancellable, error)) return FALSE; - /* Rename it into place */ + /* For OCI/Docker mode, we need to handle replacing a directory with a regular + * file. See also the equivalent code for symlinks above. + */ + if (options->process_whiteouts) + { + if (!glnx_shutil_rm_rf_at (destination_dfd, destination_name, NULL, error)) + return FALSE; + } + /* Rename it into place - for non-OCI this will overwrite files but not directories */ if (!glnx_renameat (self->tmp_dir_fd, tmpname, destination_dfd, destination_name, error)) return FALSE; ret_result = HARDLINK_RESULT_LINKED; diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 4f73a051..e1daf08d 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -848,7 +848,7 @@ typedef enum { /** * OstreeRepoCheckoutOverwriteMode: * @OSTREE_REPO_CHECKOUT_OVERWRITE_NONE: No special options - * @OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES: When layering checkouts, unlink() and replace existing files, but do not modify existing directories + * @OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES: When layering checkouts, unlink() and replace existing files, but do not modify existing directories (unless whiteouts are enabled, then directories are replaced) * @OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES: Only add new files/directories * @OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL: Like UNION_FILES, but error if files are not identical (requires hardlink checkouts) */ diff --git a/tests/basic-test.sh b/tests/basic-test.sh index decaf603..d7c5425c 100644 --- a/tests/basic-test.sh +++ b/tests/basic-test.sh @@ -879,6 +879,20 @@ if touch overlay/baz/.wh.cow && touch overlay/.wh.deeper; then assert_has_file overlay-co/anewdir/blah assert_has_file overlay-co/anewfile + # And test replacing a directory wholesale with a symlink as well as a regular file + mkdir overlay + echo baz to file > overlay/baz + ln -s anewfile overlay/anewdir + $OSTREE --repo=repo commit ${COMMIT_ARGS} -b overlay-dir-convert --tree=dir=overlay + rm overlay -rf + + rm overlay-co -rf + for branch in test2 overlay-dir-convert; do + $OSTREE --repo=repo checkout --union --whiteouts ${branch} overlay-co + done + assert_has_file overlay-co/baz + test -L overlay-co/anewdir + echo "ok whiteouts enabled" # Now double check whiteouts are not processed without --whiteouts |