Skip to content

Commit

Permalink
WIP/DNM / Support overlayfs whiteout character files
Browse files Browse the repository at this point in the history
Related-Issue: #2712
  • Loading branch information
mangelajo committed Sep 16, 2022
1 parent c6c3c5a commit ecd0a0f
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 13 deletions.
8 changes: 4 additions & 4 deletions src/libostree/ostree-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2014,7 +2014,7 @@ file_header_parse (GVariant *metadata,
mode = GUINT32_FROM_BE (mode);
g_autoptr(GFileInfo) ret_file_info = _ostree_mode_uidgid_to_gfileinfo (mode, uid, gid);

if (S_ISREG (mode))
if (S_ISREG (mode) || S_ISCHR(mode))
{
;
}
Expand Down Expand Up @@ -2065,7 +2065,7 @@ zlib_file_header_parse (GVariant *metadata,
g_autoptr(GFileInfo) ret_file_info = _ostree_mode_uidgid_to_gfileinfo (mode, uid, gid);
g_file_info_set_size (ret_file_info, GUINT64_FROM_BE (size));

if (S_ISREG (mode))
if (S_ISREG (mode) || S_ISCHR (mode))
{
;
}
Expand Down Expand Up @@ -2368,7 +2368,7 @@ _ostree_validate_bareuseronly_mode (guint32 content_mode,
return glnx_throw (error, "Content object %s: invalid mode 0%04o with bits 0%04o",
checksum, content_mode, invalid_modebits);
}
else if (S_ISLNK (content_mode))
else if (S_ISLNK (content_mode) || S_ISCHR(content_mode))
; /* Nothing */
else
g_assert_not_reached ();
Expand Down Expand Up @@ -2400,7 +2400,7 @@ gboolean
ostree_validate_structureof_file_mode (guint32 mode,
GError **error)
{
if (!(S_ISREG (mode) || S_ISLNK (mode)))
if (!(S_ISREG (mode) || S_ISLNK (mode) || S_ISCHR(mode)))
return glnx_throw (error, "Invalid file metadata mode %u; not a valid file type", mode);

if (!validate_stat_mode_perms (mode, error))
Expand Down
21 changes: 21 additions & 0 deletions src/libostree/ostree-repo-checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,27 @@ create_file_copy_from_input_at (OstreeRepo *repo,
error))
return FALSE;
}
else if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_SPECIAL)
{
guint32 file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
g_assert(S_ISCHR(file_mode));

if (mknodat(destination_dfd, destination_name, file_mode, (dev_t)0) < 0)
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");
}
}
else
g_assert_not_reached ();

Expand Down
72 changes: 67 additions & 5 deletions src/libostree/ostree-repo-commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,37 @@ _ostree_repo_commit_tmpf_final (OstreeRepo *self,
return TRUE;
}

gboolean
_ostree_repo_commit_bare_whiteout (OstreeRepo *self,
const char *checksum,
guint32 uid,
guint32 gid,
GVariant *xattrs,
GCancellable *cancellable,
GError **error)
{
g_assert(self->mode == OSTREE_REPO_MODE_BARE);

char dest_filename[_OSTREE_LOOSE_PATH_MAX];
_ostree_loose_path (dest_filename, checksum, OSTREE_OBJECT_TYPE_FILE, self->mode);

int dest_dfd = commit_dest_dfd (self);
if (!_ostree_repo_ensure_loose_objdir_at (dest_dfd, dest_filename, cancellable, error))
return FALSE;

if (mknodat(dest_dfd, dest_filename, S_IFCHR, (dev_t)0) < 0)
return glnx_throw_errno_prefix (error, "Creating whiteout char device");

if (xattrs != NULL &&
!glnx_dfd_name_set_all_xattrs(dest_dfd, dest_filename, xattrs, cancellable, error))
return glnx_throw_errno_prefix (error, "Setting xattrs for whiteout char device");

if (TEMP_FAILURE_RETRY(fchownat(dest_dfd, dest_filename, uid, gid, AT_SYMLINK_NOFOLLOW) < 0))
return glnx_throw_errno_prefix (error, "fchownat");

return TRUE;
}

/* Given a dfd+path combination (may be regular file or symlink),
* rename it into place.
*/
Expand Down Expand Up @@ -301,7 +332,7 @@ commit_loose_regfile_object (OstreeRepo *self,
return FALSE;
}
else
g_assert (S_ISLNK (mode));
g_assert (S_ISLNK (mode) || S_ISCHR(mode));
}
else if (self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
{
Expand Down Expand Up @@ -966,7 +997,9 @@ write_content_object (OstreeRepo *self,
else
file_input = input;

const guint32 mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
gboolean phys_object_is_symlink = FALSE;
gboolean phys_object_is_whiteout = FALSE;
switch (object_file_type)
{
case G_FILE_TYPE_REGULAR:
Expand All @@ -975,6 +1008,19 @@ write_content_object (OstreeRepo *self,
if (self->mode == OSTREE_REPO_MODE_BARE || self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
phys_object_is_symlink = TRUE;
break;
case G_FILE_TYPE_SPECIAL:
/* Only overlayfs whiteout char 0:0 files are supported for G_FILE_TYPE_SPECIAL */
if (S_ISCHR(mode))
{
/* For bare mode repositories where no side file metadata is stored we want to
* avoid the creation of an empty tmp file and later setup of permissions because
* this won't result in a char device.
*/
if (self->mode == OSTREE_REPO_MODE_BARE)
phys_object_is_whiteout = TRUE;
break;
}
return glnx_throw (error, "Unsupported file type %u with mode 0%o", object_file_type, mode);
default:
return glnx_throw (error, "Unsupported file type %u", object_file_type);
}
Expand Down Expand Up @@ -1021,7 +1067,8 @@ write_content_object (OstreeRepo *self,
* binary with trailing garbage, creating a window on the local
* system where a malicious setuid binary exists.
*
* We use GLnxTmpfile for regular files, and OtCleanupUnlinkat for symlinks.
* We use GLnxTmpfile for regular files, OtCleanupUnlinkat for symlinks,
* we use no temporary for whiteout char devices in bare mode.
*/
g_auto(OtCleanupUnlinkat) tmp_unlinker = { commit_tmp_dfd (self), NULL };
g_auto(GLnxTmpfile) tmpf = { 0, };
Expand All @@ -1037,6 +1084,8 @@ write_content_object (OstreeRepo *self,
cancellable, error))
return FALSE;
}
else if (phys_object_is_whiteout)
;
else if (repo_mode != OSTREE_REPO_MODE_ARCHIVE)
{
if (!create_regular_tmpfile_linkable_with_content (self, size, file_input,
Expand Down Expand Up @@ -1079,11 +1128,16 @@ write_content_object (OstreeRepo *self,

unpacked_size = g_file_info_get_size (file_info);
}
else
else if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_SYMBOLIC_LINK)
{
/* For a symlink, the size is the length of the target */
unpacked_size = strlen (g_file_info_get_symlink_target (file_info));
}
else
{
/* For char devices 0:0 whiteouts the content size is 0 */
unpacked_size = 0;
}

if (!g_output_stream_flush (temp_out, cancellable, error))
return FALSE;
Expand Down Expand Up @@ -1151,7 +1205,6 @@ write_content_object (OstreeRepo *self,

const guint32 uid = g_file_info_get_attribute_uint32 (file_info, "unix::uid");
const guint32 gid = g_file_info_get_attribute_uint32 (file_info, "unix::gid");
const guint32 mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
/* Is it "physically" a symlink? */
if (phys_object_is_symlink)
{
Expand Down Expand Up @@ -1189,6 +1242,13 @@ write_content_object (OstreeRepo *self,
&tmp_unlinker, cancellable, error))
return FALSE;
}
else if (phys_object_is_whiteout)
{
if (!_ostree_repo_commit_bare_whiteout (self, actual_checksum,
uid, gid, xattrs,
cancellable, error))
return FALSE;
}
else
{
/* Check if a file with the same payload is present in the repository,
Expand Down Expand Up @@ -3739,6 +3799,7 @@ write_content_to_mtree_internal (OstreeRepo *self,
{
case G_FILE_TYPE_SYMBOLIC_LINK:
case G_FILE_TYPE_REGULAR:
case G_FILE_TYPE_SPECIAL:
break;
default:
return glnx_throw (error, "Unsupported file type for file: '%s'", child_relpath);
Expand Down Expand Up @@ -4090,7 +4151,8 @@ write_dfd_iter_to_mtree_internal (OstreeRepo *self,
continue;
}

if (S_ISREG (stbuf.st_mode))
/* For regular files and whiteout char devices we continue */
if (S_ISREG (stbuf.st_mode) || (S_ISCHR(stbuf.st_mode) && stbuf.st_rdev == 0))
;
else if (S_ISLNK (stbuf.st_mode))
{
Expand Down
9 changes: 9 additions & 0 deletions src/libostree/ostree-repo-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,15 @@ _ostree_repo_commit_tmpf_final (OstreeRepo *self,
GCancellable *cancellable,
GError **error);

gboolean
_ostree_repo_commit_bare_whiteout (OstreeRepo *self,
const char *checksum,
guint32 uid,
guint32 gid,
GVariant *xattrs,
GCancellable *cancellable,
GError **error);

typedef struct {
gboolean initialized;
gpointer opaque0[10];
Expand Down
2 changes: 1 addition & 1 deletion src/libostree/ostree-repo-static-delta-compilation.c
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ process_one_object (OstreeRepo *repo,
}
else
{
g_assert (S_ISREG (mode));
g_assert (S_ISREG (mode) || S_ISCHR(mode));
}

content_offset = current_part->payload->len;
Expand Down
7 changes: 4 additions & 3 deletions src/libostree/ostree-repo.c
Original file line number Diff line number Diff line change
Expand Up @@ -4322,8 +4322,9 @@ _ostree_repo_load_file_bare (OstreeRepo *self,
return glnx_throw_errno_prefix (error, "openat");
}

if (!(S_ISREG (stbuf.st_mode) || S_ISLNK (stbuf.st_mode)))
return glnx_throw (error, "Not a regular file or symlink");
if (!(S_ISREG (stbuf.st_mode) || S_ISLNK (stbuf.st_mode) ||
(S_ISCHR (stbuf.st_mode) && stbuf.st_rdev == 0)))
return glnx_throw (error, "Not a regular file, symlink or whiteout");

/* In the non-bare-user case, gather symlink info if requested */
if (self->mode != OSTREE_REPO_MODE_BARE_USER
Expand Down Expand Up @@ -4474,7 +4475,7 @@ ostree_repo_load_file (OstreeRepo *self,
if (S_ISLNK (stbuf.st_mode))
g_file_info_set_symlink_target (*out_file_info, symlink_target);
else
g_assert (S_ISREG (stbuf.st_mode));
g_assert (S_ISREG (stbuf.st_mode) || (S_ISCHR(stbuf.st_mode) && stbuf.st_rdev == 0));
}

ot_transfer_out_value (out_xattrs, &ret_xattrs);
Expand Down

0 comments on commit ecd0a0f

Please sign in to comment.