summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2017-10-19 11:18:00 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2017-10-20 13:20:27 +0000
commitb8251d26bd22338ca65a9ea52c6ca5107e572738 (patch)
treeb0f017edb07b4f2dc8085a4b393b91815dd15a1c
parent9166605e5acbffd2c4db4e8a374770830f900fbc (diff)
downloadostree-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.c42
-rw-r--r--src/libostree/ostree-repo.h2
-rw-r--r--tests/basic-test.sh14
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