Merge pull request #2717 from mangelajo/support-whiteouts
Support overlayfs whiteouts on checkout
This commit is contained in:
13 files changed
+292
-9
No files matched your search
@@ -35,6 +35,8 @@
|
||||
#define WHITEOUT_PREFIX ".wh."
|
||||
#define OPAQUE_WHITEOUT_NAME ".wh..wh..opq"
|
||||
|
||||
#define OVERLAYFS_WHITEOUT_PREFIX ".ostree-wh."
|
||||
|
||||
/* Per-checkout call state/caching */
|
||||
typedef struct {
|
||||
GString *path_buf; /* buffer for real path if filtering enabled */
|
||||
@@ -582,6 +584,117 @@ checkout_file_hardlink (OstreeRepo *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_checkout_overlayfs_whiteout_at_no_overwrite (OstreeRepoCheckoutAtOptions *options,
|
||||
int destination_dfd,
|
||||
const char *destination_name,
|
||||
GFileInfo *file_info,
|
||||
GVariant *xattrs,
|
||||
gboolean *found_exant_file,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
if (found_exant_file != NULL)
|
||||
*found_exant_file = FALSE;
|
||||
guint32 file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
|
||||
if (mknodat(destination_dfd, destination_name, (file_mode & ~S_IFMT) | S_IFCHR, (dev_t)0) < 0)
|
||||
{
|
||||
if (errno == EEXIST && found_exant_file != NULL)
|
||||
{
|
||||
*found_exant_file = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
return glnx_throw_errno_prefix (error, "Creating whiteout char device");
|
||||
}
|
||||
if (options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
|
||||
{
|
||||
if (xattrs != NULL &&
|
||||
!glnx_dfd_name_set_all_xattrs(destination_dfd, destination_name, xattrs,
|
||||
cancellable, error))
|
||||
return glnx_throw_errno_prefix (error, "Setting xattrs for whiteout char device");
|
||||
|
||||
if (TEMP_FAILURE_RETRY(fchownat(destination_dfd, destination_name,
|
||||
g_file_info_get_attribute_uint32 (file_info, "unix::uid"),
|
||||
g_file_info_get_attribute_uint32 (file_info, "unix::gid"),
|
||||
AT_SYMLINK_NOFOLLOW) < 0))
|
||||
return glnx_throw_errno_prefix (error, "fchownat");
|
||||
if (TEMP_FAILURE_RETRY (fchmodat (destination_dfd, destination_name, file_mode & ~S_IFMT, 0)) < 0)
|
||||
return glnx_throw_errno_prefix (error, "fchmodat %s to 0%o", destination_name, file_mode & ~S_IFMT);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_checkout_overlayfs_whiteout_at (OstreeRepo *repo,
|
||||
OstreeRepoCheckoutAtOptions *options,
|
||||
int destination_dfd,
|
||||
const char *destination_name,
|
||||
GFileInfo *file_info,
|
||||
GVariant *xattrs,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean found_exant_file = FALSE;
|
||||
if (!_checkout_overlayfs_whiteout_at_no_overwrite(options, destination_dfd, destination_name,
|
||||
file_info, xattrs,&found_exant_file,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!found_exant_file)
|
||||
return TRUE;
|
||||
|
||||
guint32 uid = g_file_info_get_attribute_uint32 (file_info, "unix::uid");
|
||||
guint32 gid = g_file_info_get_attribute_uint32 (file_info, "unix::gid");
|
||||
guint32 file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
|
||||
|
||||
struct stat dest_stbuf;
|
||||
|
||||
switch(options->overwrite_mode)
|
||||
{
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_NONE:
|
||||
return FALSE;
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES:
|
||||
if (!ot_ensure_unlinked_at (destination_dfd, destination_name, error))
|
||||
return FALSE;
|
||||
return _checkout_overlayfs_whiteout_at_no_overwrite(options, destination_dfd, destination_name,
|
||||
file_info, xattrs, NULL, cancellable, error);
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES:
|
||||
return TRUE;
|
||||
|
||||
case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL:
|
||||
if (!glnx_fstatat(destination_dfd, destination_name, &dest_stbuf, AT_SYMLINK_NOFOLLOW,
|
||||
error))
|
||||
return FALSE;
|
||||
if (!(repo->disable_xattrs || repo->mode == OSTREE_REPO_MODE_BARE_USER_ONLY))
|
||||
{
|
||||
g_autoptr(GVariant) fs_xattrs;
|
||||
if (!glnx_dfd_name_get_all_xattrs (destination_dfd, destination_name,
|
||||
&fs_xattrs, cancellable, error))
|
||||
return FALSE;
|
||||
if (!g_variant_equal(fs_xattrs, xattrs))
|
||||
return glnx_throw(error, "existing destination file %s xattrs don't match",
|
||||
destination_name);
|
||||
}
|
||||
if (options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
|
||||
{
|
||||
if (gid != dest_stbuf.st_gid)
|
||||
return glnx_throw(error, "existing destination file %s does not match gid %d",
|
||||
destination_name, gid);
|
||||
|
||||
if (uid != dest_stbuf.st_uid)
|
||||
return glnx_throw(error, "existing destination file %s does not match uid %d",
|
||||
destination_name, gid);
|
||||
|
||||
if ((file_mode & ALLPERMS) != (dest_stbuf.st_mode & ALLPERMS))
|
||||
return glnx_throw(error, "existing destination file %s does not match mode %o",
|
||||
destination_name, file_mode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
checkout_one_file_at (OstreeRepo *repo,
|
||||
OstreeRepoCheckoutAtOptions *options,
|
||||
@@ -603,7 +716,8 @@ checkout_one_file_at (OstreeRepo *repo,
|
||||
|
||||
/* FIXME - avoid the GFileInfo here */
|
||||
g_autoptr(GFileInfo) source_info = NULL;
|
||||
if (!ostree_repo_load_file (repo, checksum, NULL, &source_info, NULL,
|
||||
g_autoptr(GVariant) source_xattrs = NULL;
|
||||
if (!ostree_repo_load_file (repo, checksum, NULL, &source_info, &source_xattrs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -623,6 +737,7 @@ checkout_one_file_at (OstreeRepo *repo,
|
||||
const gboolean is_unreadable = (!is_symlink && (source_mode & S_IRUSR) == 0);
|
||||
const gboolean is_whiteout = (!is_symlink && options->process_whiteouts &&
|
||||
g_str_has_prefix (destination_name, WHITEOUT_PREFIX));
|
||||
const gboolean is_overlayfs_whiteout = (!is_symlink && g_str_has_prefix (destination_name, OVERLAYFS_WHITEOUT_PREFIX));
|
||||
const gboolean is_reg_zerosized = (!is_symlink && g_file_info_get_size (source_info) == 0);
|
||||
const gboolean override_user_unreadable = (options->mode == OSTREE_REPO_CHECKOUT_MODE_USER && is_unreadable);
|
||||
|
||||
@@ -643,6 +758,18 @@ checkout_one_file_at (OstreeRepo *repo,
|
||||
|
||||
need_copy = FALSE;
|
||||
}
|
||||
else if (is_overlayfs_whiteout && options->process_passthrough_whiteouts)
|
||||
{
|
||||
const char *name = destination_name + (sizeof (OVERLAYFS_WHITEOUT_PREFIX) - 1);
|
||||
|
||||
if (!name[0])
|
||||
return glnx_throw (error, "Invalid empty overlayfs whiteout '%s'", name);
|
||||
|
||||
g_assert (name[0] != '/'); /* Sanity */
|
||||
|
||||
return _checkout_overlayfs_whiteout_at(repo, options, destination_dfd, name,
|
||||
source_info, source_xattrs, cancellable, error);
|
||||
}
|
||||
else if (is_reg_zerosized || override_user_unreadable)
|
||||
{
|
||||
/* In https://github.com/ostreedev/ostree/commit/673cacd633f9d6b653cdea530657d3e780a41bbd we
|
||||
|
||||
@@ -989,8 +989,9 @@ typedef struct {
|
||||
gboolean force_copy; /* Since: 2017.6 */
|
||||
gboolean bareuseronly_dirs; /* Since: 2017.7 */
|
||||
gboolean force_copy_zerosized; /* Since: 2018.9 */
|
||||
gboolean unused_bools[4];
|
||||
/* 4 byte hole on 64 bit */
|
||||
gboolean process_passthrough_whiteouts;
|
||||
gboolean unused_bools[3];
|
||||
/* 3 byte hole on 64 bit */
|
||||
|
||||
const char *subpath;
|
||||
|
||||
|
||||
@@ -641,7 +641,7 @@ checkout_deployment_tree (OstreeSysroot *sysroot,
|
||||
return FALSE;
|
||||
|
||||
/* Generate hardlink farm, then opendir it */
|
||||
OstreeRepoCheckoutAtOptions checkout_opts = { 0, };
|
||||
OstreeRepoCheckoutAtOptions checkout_opts = { .process_passthrough_whiteouts = TRUE };
|
||||
if (!ostree_repo_checkout_at (repo, &checkout_opts, osdeploy_dfd,
|
||||
checkout_target_name, csum,
|
||||
cancellable, error))
|
||||
|
||||
@@ -37,6 +37,7 @@ static gboolean opt_union;
|
||||
static gboolean opt_union_add;
|
||||
static gboolean opt_union_identical;
|
||||
static gboolean opt_whiteouts;
|
||||
static gboolean opt_process_passthrough_whiteouts;
|
||||
static gboolean opt_from_stdin;
|
||||
static char *opt_from_file;
|
||||
static gboolean opt_disable_fsync;
|
||||
@@ -77,6 +78,7 @@ static GOptionEntry options[] = {
|
||||
{ "union-add", 0, 0, G_OPTION_ARG_NONE, &opt_union_add, "Keep existing files/directories, only add new", NULL },
|
||||
{ "union-identical", 0, 0, G_OPTION_ARG_NONE, &opt_union_identical, "When layering checkouts, error out if a file would be replaced with a different version, but add new files and directories", NULL },
|
||||
{ "whiteouts", 0, 0, G_OPTION_ARG_NONE, &opt_whiteouts, "Process 'whiteout' (Docker style) entries", NULL },
|
||||
{ "process-passthrough-whiteouts", 0, 0, G_OPTION_ARG_NONE, &opt_process_passthrough_whiteouts, "Enable overlayfs whiteout extraction into char 0:0 devices", NULL },
|
||||
{ "allow-noent", 0, 0, G_OPTION_ARG_NONE, &opt_allow_noent, "Do nothing if specified path does not exist", NULL },
|
||||
{ "from-stdin", 0, 0, G_OPTION_ARG_NONE, &opt_from_stdin, "Process many checkouts from standard input", NULL },
|
||||
{ "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", "FILE" },
|
||||
@@ -129,7 +131,8 @@ process_one_checkout (OstreeRepo *repo,
|
||||
if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks ||
|
||||
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)
|
||||
opt_skiplist_file || opt_selinux_policy || opt_selinux_prefix ||
|
||||
opt_process_passthrough_whiteouts)
|
||||
{
|
||||
OstreeRepoCheckoutAtOptions checkout_options = { 0, };
|
||||
|
||||
@@ -162,6 +165,8 @@ process_one_checkout (OstreeRepo *repo,
|
||||
}
|
||||
if (opt_whiteouts)
|
||||
checkout_options.process_whiteouts = TRUE;
|
||||
if (opt_process_passthrough_whiteouts)
|
||||
checkout_options.process_passthrough_whiteouts = TRUE;
|
||||
if (subpath)
|
||||
checkout_options.subpath = subpath;
|
||||
|
||||
|
||||
Reference in new issue
Block a user