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
1 changed files with 203 additions and 202 deletions

View File

@ -2614,21 +2614,8 @@ _ostree_repo_read_bare_fd (OstreeRepo *self,
return TRUE; return TRUE;
} }
/** static gboolean
* ostree_repo_load_file: repo_load_file_archive (OstreeRepo *self,
* @self: Repo
* @checksum: ASCII SHA256 checksum
* @out_input: (out) (optional) (nullable): File content
* @out_file_info: (out) (optional) (nullable): File information
* @out_xattrs: (out) (optional) (nullable): Extended attributes
* @cancellable: Cancellable
* @error: Error
*
* Load content object, decomposing it into three parts: the actual
* content (for regular files), the metadata, and extended attributes.
*/
gboolean
ostree_repo_load_file (OstreeRepo *self,
const char *checksum, const char *checksum,
GInputStream **out_input, GInputStream **out_input,
GFileInfo **out_file_info, GFileInfo **out_file_info,
@ -2636,22 +2623,11 @@ ostree_repo_load_file (OstreeRepo *self,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) 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; struct stat stbuf;
g_autoptr(GInputStream) tmp_stream = NULL; 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, if (!ot_openat_ignore_enoent (self->objects_dir_fd, loose_path_buf, &fd,
error)) error))
return FALSE; return FALSE;
@ -2665,24 +2641,44 @@ ostree_repo_load_file (OstreeRepo *self,
if (fd != -1) if (fd != -1)
{ {
tmp_stream = g_unix_input_stream_new (fd, TRUE); if (!glnx_fstat (fd, &stbuf, error))
fd = -1; /* Transfer ownership */
if (!glnx_stream_fstat ((GFileDescriptorBased*) tmp_stream, &stbuf,
error))
return FALSE; return FALSE;
if (!ostree_content_stream_parse (TRUE, tmp_stream, stbuf.st_size, TRUE, g_autoptr(GInputStream) tmp_stream = g_unix_input_stream_new (glnx_steal_fd (&fd), TRUE);
out_input ? &ret_input : NULL, /* Note return here */
&ret_file_info, &ret_xattrs, return ostree_content_stream_parse (TRUE, tmp_stream, stbuf.st_size, TRUE,
cancellable, error)) out_input, out_file_info, out_xattrs,
return FALSE; cancellable, error);
found = TRUE;
} }
else if (self->parent_repo)
{
return ostree_repo_load_file (self->parent_repo, checksum,
out_input, out_file_info, out_xattrs,
cancellable, error);
} }
else 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 */ int objdir_fd = -1; /* referenced */
if (!stat_bare_content_object (self, loose_path_buf, if (!stat_bare_content_object (self, loose_path_buf,
&objdir_fd, &objdir_fd,
@ -2690,55 +2686,54 @@ ostree_repo_load_file (OstreeRepo *self,
cancellable, error)) cancellable, error))
return FALSE; return FALSE;
if (ret_file_info) if (!ret_file_info)
{ {
found = TRUE; if (self->parent_repo)
if (repo_mode == OSTREE_REPO_MODE_BARE_USER)
{ {
guint32 mode; return ostree_repo_load_file (self->parent_repo, checksum,
g_autoptr(GVariant) metadata = NULL; out_input, out_file_info, out_xattrs,
g_autoptr(GBytes) bytes = NULL; cancellable, error);
glnx_fd_close int fd = -1; }
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 /* In bare-user, symlinks are stored as regular files, so we just
* always do an open, then query the user.ostreemeta xattr for * always do an open, then query the user.ostreemeta xattr for
* more information. * more information.
*/ */
fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC); glnx_fd_close int fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
if (fd < 0) if (fd < 0)
return glnx_throw_errno (error); return glnx_throw_errno (error);
bytes = glnx_fgetxattr_bytes (fd, "user.ostreemeta", error); g_autoptr(GBytes) bytes = glnx_fgetxattr_bytes (fd, "user.ostreemeta", error);
if (bytes == NULL) if (bytes == NULL)
return FALSE; return FALSE;
metadata = g_variant_new_from_bytes (OSTREE_FILEMETA_GVARIANT_FORMAT, g_autoptr(GVariant) metadata = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_FILEMETA_GVARIANT_FORMAT,
bytes, FALSE); bytes, FALSE));
g_variant_ref_sink (metadata);
ret_xattrs = set_info_from_filemeta (ret_file_info, metadata); ret_xattrs = set_info_from_filemeta (ret_file_info, metadata);
mode = g_file_info_get_attribute_uint32 (ret_file_info, "unix::mode"); guint32 mode = g_file_info_get_attribute_uint32 (ret_file_info, "unix::mode");
if (S_ISREG (mode) && out_input) if (S_ISREG (mode) && out_input)
{ {
g_assert (fd != -1); g_assert (fd != -1);
ret_input = g_unix_input_stream_new (fd, TRUE); ret_input = g_unix_input_stream_new (glnx_steal_fd (&fd), TRUE);
fd = -1; /* Transfer ownership */
} }
else if (S_ISLNK (mode)) 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_file_type (ret_file_info, G_FILE_TYPE_SYMBOLIC_LINK);
g_file_info_set_size (ret_file_info, 0); g_file_info_set_size (ret_file_info, 0);
target_input = g_unix_input_stream_new (fd, TRUE); char targetbuf[PATH_MAX+1];
fd = -1; /* Transfer ownership */ 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), if (!g_input_stream_read_all (target_input, targetbuf, sizeof (targetbuf),
&target_size, cancellable, error)) &target_size, cancellable, error))
return FALSE; return FALSE;
@ -2746,9 +2741,8 @@ ostree_repo_load_file (OstreeRepo *self,
g_file_info_set_symlink_target (ret_file_info, targetbuf); g_file_info_set_symlink_target (ret_file_info, targetbuf);
} }
} }
else if (repo_mode == OSTREE_REPO_MODE_BARE_USER_ONLY) else if (self->mode == OSTREE_REPO_MODE_BARE_USER_ONLY)
{ {
glnx_fd_close int fd = -1;
/* Canonical info is: uid/gid is 0 and no xattrs, which /* Canonical info is: uid/gid is 0 and no xattrs, which
might be wrong and thus not validate correctly, but might be wrong and thus not validate correctly, but
@ -2759,7 +2753,7 @@ ostree_repo_load_file (OstreeRepo *self,
if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR && if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR &&
out_input) out_input)
{ {
fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC); glnx_fd_close int fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
if (fd < 0) if (fd < 0)
return glnx_throw_errno (error); return glnx_throw_errno (error);
@ -2776,14 +2770,12 @@ ostree_repo_load_file (OstreeRepo *self,
} }
else else
{ {
g_assert (repo_mode == OSTREE_REPO_MODE_BARE); g_assert (self->mode == OSTREE_REPO_MODE_BARE);
if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR
&& (out_input || out_xattrs)) && (out_input || out_xattrs))
{ {
glnx_fd_close int fd = -1; glnx_fd_close int fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
fd = openat (objdir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
if (fd < 0) if (fd < 0)
return glnx_throw_errno (error); return glnx_throw_errno (error);
@ -2813,27 +2805,6 @@ ostree_repo_load_file (OstreeRepo *self,
return FALSE; 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_input, &ret_input);
ot_transfer_out_value (out_file_info, &ret_file_info); ot_transfer_out_value (out_file_info, &ret_file_info);
@ -2841,6 +2812,36 @@ ostree_repo_load_file (OstreeRepo *self,
return TRUE; return TRUE;
} }
/**
* ostree_repo_load_file:
* @self: Repo
* @checksum: ASCII SHA256 checksum
* @out_input: (out) (optional) (nullable): File content
* @out_file_info: (out) (optional) (nullable): File information
* @out_xattrs: (out) (optional) (nullable): Extended attributes
* @cancellable: Cancellable
* @error: Error
*
* Load content object, decomposing it into three parts: the actual
* content (for regular files), the metadata, and extended attributes.
*/
gboolean
ostree_repo_load_file (OstreeRepo *self,
const char *checksum,
GInputStream **out_input,
GFileInfo **out_file_info,
GVariant **out_xattrs,
GCancellable *cancellable,
GError **error)
{
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
return repo_load_file_bare (self, checksum, out_input, out_file_info, out_xattrs,
cancellable, error);
}
/** /**
* ostree_repo_load_object_stream: * ostree_repo_load_object_stream:
* @self: Repo * @self: Repo