lib/repo: Split archive/bare file parsing

Prep for future cleanup patches (in particular I want an internal-only
version at first that uses a fd+`struct stat`) to avoid allocations.

The new version avoids lots of deep nesting of conditionals as well
by hoisting the "not found" handling to an early return.

There's a bit of code duplication between the two cases but it's
quite worth the result.

Closes: #951
Approved by: jlebon
This commit is contained in:
Colin Walters 2017-06-22 13:03:14 -04:00 committed by Atomic Bot
parent aafda9073a
commit 63ad289a9c

View File

@ -2614,6 +2614,204 @@ _ostree_repo_read_bare_fd (OstreeRepo *self,
return TRUE;
}
static gboolean
repo_load_file_archive (OstreeRepo *self,
const char *checksum,
GInputStream **out_input,
GFileInfo **out_file_info,
GVariant **out_xattrs,
GCancellable *cancellable,
GError **error)
{
struct stat stbuf;
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
_ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, self->mode);
glnx_fd_close int fd = -1;
if (!ot_openat_ignore_enoent (self->objects_dir_fd, loose_path_buf, &fd,
error))
return FALSE;
if (fd < 0 && self->commit_stagedir_fd != -1)
{
if (!ot_openat_ignore_enoent (self->commit_stagedir_fd, loose_path_buf, &fd,
error))
return FALSE;
}
if (fd != -1)
{
if (!glnx_fstat (fd, &stbuf, error))
return FALSE;
g_autoptr(GInputStream) tmp_stream = g_unix_input_stream_new (glnx_steal_fd (&fd), TRUE);
/* Note return here */
return ostree_content_stream_parse (TRUE, tmp_stream, stbuf.st_size, TRUE,
out_input, out_file_info, out_xattrs,
cancellable, error);
}
else if (self->parent_repo)
{
return ostree_repo_load_file (self->parent_repo, checksum,
out_input, out_file_info, out_xattrs,
cancellable, error);
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Couldn't find file object '%s'", checksum);
return FALSE;
}
}
static gboolean
repo_load_file_bare (OstreeRepo *self,
const char *checksum,
GInputStream **out_input,
GFileInfo **out_file_info,
GVariant **out_xattrs,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GInputStream) ret_input = NULL;
g_autoptr(GFileInfo) ret_file_info = NULL;
g_autoptr(GVariant) ret_xattrs = NULL;
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
_ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, self->mode);
int objdir_fd = -1; /* referenced */
if (!stat_bare_content_object (self, loose_path_buf,
&objdir_fd,
&ret_file_info,
cancellable, error))
return FALSE;
if (!ret_file_info)
{
if (self->parent_repo)
{
return ostree_repo_load_file (self->parent_repo, checksum,
out_input, out_file_info, out_xattrs,
cancellable, error);
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Couldn't find file object '%s'", checksum);
return FALSE;
}
}
if (self->mode == OSTREE_REPO_MODE_BARE_USER)
{
/* In bare-user, symlinks are stored as regular files, so we just
* always do an open, then query the user.ostreemeta xattr for
* more information.
*/
glnx_fd_close int fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return glnx_throw_errno (error);
g_autoptr(GBytes) bytes = glnx_fgetxattr_bytes (fd, "user.ostreemeta", error);
if (bytes == NULL)
return FALSE;
g_autoptr(GVariant) metadata = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_FILEMETA_GVARIANT_FORMAT,
bytes, FALSE));
ret_xattrs = set_info_from_filemeta (ret_file_info, metadata);
guint32 mode = g_file_info_get_attribute_uint32 (ret_file_info, "unix::mode");
if (S_ISREG (mode) && out_input)
{
g_assert (fd != -1);
ret_input = g_unix_input_stream_new (glnx_steal_fd (&fd), TRUE);
}
else if (S_ISLNK (mode))
{
g_file_info_set_file_type (ret_file_info, G_FILE_TYPE_SYMBOLIC_LINK);
g_file_info_set_size (ret_file_info, 0);
char targetbuf[PATH_MAX+1];
gsize target_size;
g_autoptr(GInputStream) target_input = g_unix_input_stream_new (fd, FALSE);
if (!g_input_stream_read_all (target_input, targetbuf, sizeof (targetbuf),
&target_size, cancellable, error))
return FALSE;
g_file_info_set_symlink_target (ret_file_info, targetbuf);
}
}
else if (self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
{
/* Canonical info is: uid/gid is 0 and no xattrs, which
might be wrong and thus not validate correctly, but
at least we report something consistent. */
g_file_info_set_attribute_uint32 (ret_file_info, "unix::uid", 0);
g_file_info_set_attribute_uint32 (ret_file_info, "unix::gid", 0);
if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR &&
out_input)
{
glnx_fd_close int fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return glnx_throw_errno (error);
ret_input = g_unix_input_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */
}
if (out_xattrs)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
ret_xattrs = g_variant_ref_sink (g_variant_builder_end (&builder));
}
}
else
{
g_assert (self->mode == OSTREE_REPO_MODE_BARE);
if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR
&& (out_input || out_xattrs))
{
glnx_fd_close int fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return glnx_throw_errno (error);
if (out_xattrs)
{
if (self->disable_xattrs)
ret_xattrs = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("(ayay)"), NULL, 0));
else if (!glnx_fd_get_all_xattrs (fd, &ret_xattrs,
cancellable, error))
return FALSE;
}
if (out_input)
{
ret_input = g_unix_input_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */
}
}
else if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_SYMBOLIC_LINK
&& out_xattrs)
{
if (self->disable_xattrs)
ret_xattrs = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("(ayay)"), NULL, 0));
else if (!glnx_dfd_name_get_all_xattrs (objdir_fd, loose_path_buf,
&ret_xattrs,
cancellable, error))
return FALSE;
}
}
ot_transfer_out_value (out_input, &ret_input);
ot_transfer_out_value (out_file_info, &ret_file_info);
ot_transfer_out_value (out_xattrs, &ret_xattrs);
return TRUE;
}
/**
* ostree_repo_load_file:
* @self: Repo
@ -2636,209 +2834,12 @@ ostree_repo_load_file (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
gboolean found = FALSE;
g_autoptr(GInputStream) ret_input = NULL;
g_autoptr(GFileInfo) ret_file_info = NULL;
g_autoptr(GVariant) ret_xattrs = NULL;
OstreeRepoMode repo_mode = ostree_repo_get_mode (self);
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
_ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, repo_mode);
if (repo_mode == OSTREE_REPO_MODE_ARCHIVE_Z2)
{
int fd = -1;
struct stat stbuf;
g_autoptr(GInputStream) tmp_stream = NULL;
if (!ot_openat_ignore_enoent (self->objects_dir_fd, loose_path_buf, &fd,
error))
return FALSE;
if (fd < 0 && self->commit_stagedir_fd != -1)
{
if (!ot_openat_ignore_enoent (self->commit_stagedir_fd, loose_path_buf, &fd,
error))
return FALSE;
}
if (fd != -1)
{
tmp_stream = g_unix_input_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */
if (!glnx_stream_fstat ((GFileDescriptorBased*) tmp_stream, &stbuf,
error))
return FALSE;
if (!ostree_content_stream_parse (TRUE, tmp_stream, stbuf.st_size, TRUE,
out_input ? &ret_input : NULL,
&ret_file_info, &ret_xattrs,
cancellable, error))
return FALSE;
found = TRUE;
}
}
if (self->mode == OSTREE_REPO_MODE_ARCHIVE_Z2)
return repo_load_file_archive (self, checksum, out_input, out_file_info, out_xattrs,
cancellable, error);
else
{
int objdir_fd = -1; /* referenced */
if (!stat_bare_content_object (self, loose_path_buf,
&objdir_fd,
&ret_file_info,
cancellable, error))
return FALSE;
if (ret_file_info)
{
found = TRUE;
if (repo_mode == OSTREE_REPO_MODE_BARE_USER)
{
guint32 mode;
g_autoptr(GVariant) metadata = NULL;
g_autoptr(GBytes) bytes = NULL;
glnx_fd_close int fd = -1;
/* In bare-user, symlinks are stored as regular files, so we just
* always do an open, then query the user.ostreemeta xattr for
* more information.
*/
fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return glnx_throw_errno (error);
bytes = glnx_fgetxattr_bytes (fd, "user.ostreemeta", error);
if (bytes == NULL)
return FALSE;
metadata = g_variant_new_from_bytes (OSTREE_FILEMETA_GVARIANT_FORMAT,
bytes, FALSE);
g_variant_ref_sink (metadata);
ret_xattrs = set_info_from_filemeta (ret_file_info, metadata);
mode = g_file_info_get_attribute_uint32 (ret_file_info, "unix::mode");
if (S_ISREG (mode) && out_input)
{
g_assert (fd != -1);
ret_input = g_unix_input_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */
}
else if (S_ISLNK (mode))
{
g_autoptr(GInputStream) target_input = NULL;
char targetbuf[PATH_MAX+1];
gsize target_size;
g_file_info_set_file_type (ret_file_info, G_FILE_TYPE_SYMBOLIC_LINK);
g_file_info_set_size (ret_file_info, 0);
target_input = g_unix_input_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */
if (!g_input_stream_read_all (target_input, targetbuf, sizeof (targetbuf),
&target_size, cancellable, error))
return FALSE;
g_file_info_set_symlink_target (ret_file_info, targetbuf);
}
}
else if (repo_mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
{
glnx_fd_close int fd = -1;
/* Canonical info is: uid/gid is 0 and no xattrs, which
might be wrong and thus not validate correctly, but
at least we report something consistent. */
g_file_info_set_attribute_uint32 (ret_file_info, "unix::uid", 0);
g_file_info_set_attribute_uint32 (ret_file_info, "unix::gid", 0);
if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR &&
out_input)
{
fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return glnx_throw_errno (error);
ret_input = g_unix_input_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */
}
if (out_xattrs)
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
ret_xattrs = g_variant_ref_sink (g_variant_builder_end (&builder));
}
}
else
{
g_assert (repo_mode == OSTREE_REPO_MODE_BARE);
if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR
&& (out_input || out_xattrs))
{
glnx_fd_close int fd = -1;
fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return glnx_throw_errno (error);
if (out_xattrs)
{
if (self->disable_xattrs)
ret_xattrs = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("(ayay)"), NULL, 0));
else if (!glnx_fd_get_all_xattrs (fd, &ret_xattrs,
cancellable, error))
return FALSE;
}
if (out_input)
{
ret_input = g_unix_input_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */
}
}
else if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_SYMBOLIC_LINK
&& out_xattrs)
{
if (self->disable_xattrs)
ret_xattrs = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("(ayay)"), NULL, 0));
else if (!glnx_dfd_name_get_all_xattrs (objdir_fd, loose_path_buf,
&ret_xattrs,
cancellable, error))
return FALSE;
}
}
}
}
if (!found)
{
if (self->parent_repo)
{
if (!ostree_repo_load_file (self->parent_repo, checksum,
out_input ? &ret_input : NULL,
out_file_info ? &ret_file_info : NULL,
out_xattrs ? &ret_xattrs : NULL,
cancellable, error))
return FALSE;
}
else
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"Couldn't find file object '%s'", checksum);
return FALSE;
}
}
ot_transfer_out_value (out_input, &ret_input);
ot_transfer_out_value (out_file_info, &ret_file_info);
ot_transfer_out_value (out_xattrs, &ret_xattrs);
return TRUE;
return repo_load_file_bare (self, checksum, out_input, out_file_info, out_xattrs,
cancellable, error);
}
/**