return g_variant_ref_sink (g_variant_builder_end (builder));
}
+typedef struct {
+ gboolean initialized;
+ GLnxTmpfile tmpf;
+ char *expected_checksum;
+ OtChecksum checksum;
+ guint64 content_len;
+ guint64 bytes_written;
+ guint uid;
+ guint gid;
+ guint mode;
+ GVariant *xattrs;
+} OstreeRealRepoBareContent;
+G_STATIC_ASSERT (sizeof (OstreeRepoBareContent) >= sizeof (OstreeRealRepoBareContent));
+
/* Create a tmpfile for writing a bare file. Currently just used
* by the static delta code, but will likely later be extended
* to be used also by the dfd_iter commit path.
*/
gboolean
-_ostree_repo_open_content_bare (OstreeRepo *self,
- const char *checksum,
- guint64 content_len,
- GLnxTmpfile *out_tmpf,
- GCancellable *cancellable,
- GError **error)
-{
- return glnx_open_tmpfile_linkable_at (self->tmp_dir_fd, ".", O_WRONLY|O_CLOEXEC,
- out_tmpf, error);
+_ostree_repo_bare_content_open (OstreeRepo *self,
+ const char *expected_checksum,
+ guint64 content_len,
+ guint uid,
+ guint gid,
+ guint mode,
+ GVariant *xattrs,
+ OstreeRepoBareContent *out_regwrite,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRealRepoBareContent *real = (OstreeRealRepoBareContent*) out_regwrite;
+ g_assert (!real->initialized);
+ real->initialized = TRUE;
+ g_assert (S_ISREG (mode));
+ if (!glnx_open_tmpfile_linkable_at (self->tmp_dir_fd, ".", O_WRONLY|O_CLOEXEC,
+ &real->tmpf, error))
+ return FALSE;
+ ot_checksum_init (&real->checksum);
+ real->expected_checksum = g_strdup (expected_checksum);
+ real->content_len = content_len;
+ real->bytes_written = 0;
+ real->uid = uid;
+ real->gid = gid;
+ real->mode = mode;
+ real->xattrs = xattrs ? g_variant_ref (xattrs) : NULL;
+
+ /* Initialize the checksum with the header info */
+ g_autoptr(GFileInfo) finfo = _ostree_mode_uidgid_to_gfileinfo (mode, uid, gid);
+ g_autoptr(GBytes) header = _ostree_file_header_new (finfo, xattrs);
+ ot_checksum_update_bytes (&real->checksum, header);
+
+ return TRUE;
}
-/* Used by static deltas, which have a separate "push" flow for
- * regfile objects distinct from the "pull" model used by
- * write_content_object().
- */
gboolean
-_ostree_repo_commit_trusted_content_bare (OstreeRepo *self,
- const char *checksum,
- GLnxTmpfile *tmpf,
- guint32 uid,
- guint32 gid,
- guint32 mode,
- GVariant *xattrs,
- GCancellable *cancellable,
- GError **error)
-{
- /* I don't think this is necessary, but a similar check was here previously,
- * keeping it for extra redundancy.
- */
- if (!tmpf->initialized || tmpf->fd == -1)
- return TRUE;
+_ostree_repo_bare_content_write (OstreeRepo *repo,
+ OstreeRepoBareContent *barewrite,
+ const guint8 *buf,
+ size_t len,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRealRepoBareContent *real = (OstreeRealRepoBareContent*) barewrite;
+ g_assert (real->initialized);
+ ot_checksum_update (&real->checksum, buf, len);
+ if (glnx_loop_write (real->tmpf.fd, buf, len) < 0)
+ return glnx_throw_errno_prefix (error, "write");
+ return TRUE;
+}
+
+gboolean
+_ostree_repo_bare_content_commit (OstreeRepo *self,
+ OstreeRepoBareContent *barewrite,
+ char *checksum_buf,
+ size_t buflen,
+ GCancellable *cancellable,
+ GError **error)
+{
+ OstreeRealRepoBareContent *real = (OstreeRealRepoBareContent*) barewrite;
+ g_assert (real->initialized);
+ ot_checksum_get_hexdigest (&real->checksum, checksum_buf, buflen);
+
+ if (real->expected_checksum &&
+ !_ostree_compare_object_checksum (OSTREE_OBJECT_TYPE_FILE,
+ real->expected_checksum, checksum_buf,
+ error))
+ return FALSE;
+
+ if (!commit_loose_regfile_object (self, checksum_buf,
+ &real->tmpf, real->uid, real->gid,
+ real->mode, real->xattrs,
+ cancellable, error))
+ return FALSE;
- return commit_loose_regfile_object (self, checksum,
- tmpf, uid, gid, mode, xattrs,
- cancellable, error);
+ /* Let's have a guarantee that after commit the object is cleaned up */
+ _ostree_repo_bare_content_cleanup (barewrite);
+ return TRUE;
+}
+
+void
+_ostree_repo_bare_content_cleanup (OstreeRepoBareContent *regwrite)
+{
+ OstreeRealRepoBareContent *real = (OstreeRealRepoBareContent*) regwrite;
+ if (!real->initialized)
+ return;
+ glnx_tmpfile_clear (&real->tmpf);
+ ot_checksum_clear (&real->checksum);
+ g_clear_pointer (&real->expected_checksum, (GDestroyNotify)g_free);
+ g_clear_pointer (&real->xattrs, (GDestroyNotify)g_variant_unref);
+ real->initialized = FALSE;
}
/* Allocate an O_TMPFILE, write everything from @input to it, but
GError **async_error;
OstreeObjectType output_objtype;
- GLnxTmpfile tmpf;
guint64 content_size;
- GOutputStream *content_out;
- OtChecksum content_checksum;
char checksum[OSTREE_SHA256_STRING_LEN+1];
+ OstreeRepoBareContent content_out;
char *read_source_object;
int read_source_fd;
gboolean have_obj;
ret = TRUE;
out:
- glnx_tmpfile_clear (&state->tmpf);
- g_clear_object (&state->content_out);
- ot_checksum_clear (&state->content_checksum);
+ _ostree_repo_bare_content_cleanup (&state->content_out);
return ret;
}
return TRUE;
}
-static gboolean
-content_out_write (OstreeRepo *repo,
- StaticDeltaExecutionState *state,
- const guint8* buf,
- gsize len,
- GCancellable *cancellable,
- GError **error)
-{
- gsize bytes_written;
-
- if (state->content_checksum.initialized)
- ot_checksum_update (&state->content_checksum, buf, len);
-
- /* Ignore bytes_written since we discard partial content */
- if (!g_output_stream_write_all (state->content_out,
- buf, len,
- &bytes_written,
- cancellable, error))
- return FALSE;
-
- return TRUE;
-}
-
static gboolean
do_content_open_generic (OstreeRepo *repo,
StaticDeltaExecutionState *state,
&stream) < 0)
return FALSE;
- if (!content_out_write (repo, state, buf, state->content_size,
- cancellable, error))
+ if (!_ostree_repo_bare_content_write (repo, &state->content_out,
+ buf, state->content_size,
+ cancellable, error))
return FALSE;
}
return TRUE;
}
-/* Before, we had a distinction between "trusted" and "untrusted" deltas
- * which we've decided wasn't a good idea. Now, we always checksum the content.
- * Compare with what ostree_checksum_file_from_input() is doing too.
- */
-static gboolean
-handle_untrusted_content_checksum (OstreeRepo *repo,
- StaticDeltaExecutionState *state,
- GCancellable *cancellable,
- GError **error)
-{
- g_autoptr(GFileInfo) finfo = _ostree_mode_uidgid_to_gfileinfo (state->mode, state->uid, state->gid);
- g_autoptr(GBytes) header = _ostree_file_header_new (finfo, state->xattrs);
-
- ot_checksum_init (&state->content_checksum);
- ot_checksum_update_bytes (&state->content_checksum, header);
-
- return TRUE;
-}
-
static gboolean
dispatch_open_splice_and_close (OstreeRepo *repo,
StaticDeltaExecutionState *state,
if (!state->have_obj)
{
- if (!_ostree_repo_open_content_bare (repo, state->checksum,
+ if (!_ostree_repo_bare_content_open (repo, state->checksum,
state->content_size,
- &state->tmpf,
+ state->uid, state->gid, state->mode,
+ state->xattrs,
+ &state->content_out,
cancellable, error))
goto out;
- state->content_out = g_unix_output_stream_new (state->tmpf.fd, FALSE);
- if (!handle_untrusted_content_checksum (repo, state, cancellable, error))
- goto out;
-
- if (!content_out_write (repo, state,
- state->payload_data + content_offset,
- state->content_size,
- cancellable, error))
+ if (!_ostree_repo_bare_content_write (repo, &state->content_out,
+ state->payload_data + content_offset,
+ state->content_size,
+ cancellable, error))
goto out;
}
}
if (!state->have_obj)
{
- if (!_ostree_repo_open_content_bare (repo, state->checksum,
+ if (!_ostree_repo_bare_content_open (repo, state->checksum,
state->content_size,
- &state->tmpf,
+ state->uid, state->gid, state->mode,
+ state->xattrs,
+ &state->content_out,
cancellable, error))
return FALSE;
- state->content_out = g_unix_output_stream_new (state->tmpf.fd, FALSE);
}
- if (!handle_untrusted_content_checksum (repo, state, cancellable, error))
- return FALSE;
-
return TRUE;
}
if (G_UNLIKELY (bytes_read == 0))
return glnx_throw (error, "Unexpected EOF reading object %s", state->read_source_object);
- if (!content_out_write (repo, state, (guint8*)buf, bytes_read,
- cancellable, error))
+ if (!_ostree_repo_bare_content_write (repo, &state->content_out,
+ (guint8*)buf, bytes_read,
+ cancellable, error))
return FALSE;
content_size -= bytes_read;
if (!validate_ofs (state, content_offset, content_size, error))
return FALSE;
- if (!content_out_write (repo, state, state->payload_data + content_offset, content_size,
- cancellable, error))
+ if (!_ostree_repo_bare_content_write (repo, &state->content_out,
+ state->payload_data + content_offset, content_size,
+ cancellable, error))
return FALSE;
}
}
{
GLNX_AUTO_PREFIX_ERROR("opcode close", error);
- if (state->content_out)
+ if (state->content_out.initialized)
{
- if (!g_output_stream_flush (state->content_out, cancellable, error))
+ char actual_checksum[OSTREE_SHA256_STRING_LEN+1];
+ if (!_ostree_repo_bare_content_commit (repo, &state->content_out, actual_checksum,
+ sizeof (actual_checksum),
+ cancellable, error))
return FALSE;
- if (state->content_checksum.initialized)
- {
- char actual_checksum[OSTREE_SHA256_STRING_LEN+1];
- ot_checksum_get_hexdigest (&state->content_checksum, actual_checksum, sizeof (actual_checksum));
-
- if (strcmp (actual_checksum, state->checksum) != 0)
- return glnx_throw (error, "Corrupted object %s (actual checksum is %s)",
- state->checksum, actual_checksum);
- }
-
- if (!_ostree_repo_commit_trusted_content_bare (repo, state->checksum, &state->tmpf,
- state->uid, state->gid, state->mode,
- state->xattrs,
- cancellable, error))
- return FALSE;
- g_clear_object (&state->content_out);
+ g_assert_cmpstr (state->checksum, ==, actual_checksum);
}
if (!dispatch_unset_read_source (repo, state, cancellable, error))
return FALSE;
g_clear_pointer (&state->xattrs, g_variant_unref);
- ot_checksum_clear (&state->content_checksum);
+ _ostree_repo_bare_content_cleanup (&state->content_out);
state->checksum_index++;
state->output_target = NULL;