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");
}
/* 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;
/* 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;
/**
* 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)
*/
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