deltas: Handle untrusted checksums faster and more robustly

When reworking the ostree core [to use O_TMPFILE](https://github.com/ostreedev/ostree/pull/369),
I hit an issue in the way the untrusted delta codepath ends up trying
to re-open the file to checksum it.  That's not possible with
`O_TMPFILE` since the fd (which we opened `O_WRONLY`) is the only
accessible reference to the content.

Fix this by changing the delta processing code to update a checksum as
we're doing writes, which is also faster, and ends up simplifying the
code as well.

What would be an even larger simplification here is if we e.g. used a
separate thread calling `write_object()` or something like that; the
main issue I see there is somehow bridging the fact that function
wants a `GInputStream*` but the delta code is generating stream of
writes.

Closes: #392
Approved by: jlebon
This commit is contained in:
Colin Walters authored and Atomic Bot committed 2016-07-29 16:03:28 +00:00
1 parent fb0bf27d10
commit 6ffcb24d22
5 files changed
+126 -195

No files matched your search

+8 -103
View File
@@ -446,109 +446,14 @@ fallocate_stream (GFileDescriptorBased *stream,
}
gboolean
_ostree_repo_open_untrusted_content_bare (OstreeRepo *self,
const char *expected_checksum,
guint64 content_len,
OstreeRepoContentBareCommit *out_state,
GOutputStream **out_stream,
gboolean *out_have_object,
GCancellable *cancellable,
GError **error)
{
/* The trusted codepath is fine here */
return _ostree_repo_open_trusted_content_bare (self,
expected_checksum,
content_len,
out_state,
out_stream,
out_have_object,
cancellable,
error);
}
gboolean
_ostree_repo_commit_untrusted_content_bare (OstreeRepo *self,
const char *expected_checksum,
OstreeRepoContentBareCommit *state,
guint32 uid,
guint32 gid,
guint32 mode,
GVariant *xattrs,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
if (state->fd != -1)
{
GMappedFile *mapped;
g_autoptr(GBytes) bytes = NULL;
g_autoptr(GInputStream) raw_in = NULL;
g_autoptr(GInputStream) in = NULL;
g_autoptr(GFileInfo) file_info = NULL;
g_autofree guchar *actual_csum = NULL;
g_autofree char *actual_checksum = NULL;
int fd;
fd = openat (self->tmp_dir_fd, state->temp_filename, O_RDONLY);
if (fd == -1)
{
glnx_set_error_from_errno (error);
goto out;
}
mapped = g_mapped_file_new_from_fd (fd, FALSE, error);
close (fd);
if (mapped == NULL)
goto out;
bytes = g_mapped_file_get_bytes (mapped);
g_mapped_file_unref (mapped);
raw_in = g_memory_input_stream_new_from_bytes (bytes);
file_info = _ostree_header_gfile_info_new (mode, uid, gid);
if (!ostree_raw_file_to_content_stream (raw_in, file_info, xattrs, &in, NULL, cancellable, error))
goto out;
if (!ot_gio_checksum_stream (in, &actual_csum, cancellable, error))
goto out;
actual_checksum = ostree_checksum_from_bytes (actual_csum);
if (strcmp (actual_checksum, expected_checksum) != 0)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Corrupted object %s (actual checksum is %s)",
expected_checksum, actual_checksum);
goto out;
}
if (!commit_loose_object_trusted (self, expected_checksum, OSTREE_OBJECT_TYPE_FILE,
state->temp_filename,
FALSE, uid, gid, mode,
xattrs, state->fd,
cancellable, error))
goto out;
}
ret = TRUE;
out:
g_free (state->temp_filename);
return ret;
}
gboolean
_ostree_repo_open_trusted_content_bare (OstreeRepo *self,
const char *checksum,
guint64 content_len,
OstreeRepoContentBareCommit *out_state,
GOutputStream **out_stream,
gboolean *out_have_object,
GCancellable *cancellable,
GError **error)
_ostree_repo_open_content_bare (OstreeRepo *self,
const char *checksum,
guint64 content_len,
OstreeRepoContentBareCommit *out_state,
GOutputStream **out_stream,
gboolean *out_have_object,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
g_autofree char *temp_filename = NULL;