GFileIOStream * _g_local_file_io_stream_new (GLocalFileOutputStream *output_stream) { GLocalFileIOStream *stream; int fd; stream = g_object_new (G_TYPE_LOCAL_FILE_IO_STREAM, NULL); stream->output_stream = g_object_ref (output_stream); _g_local_file_output_stream_set_do_close (output_stream, FALSE); fd = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (output_stream)); stream->input_stream = (GInputStream *)_g_local_file_input_stream_new (fd); _g_local_file_input_stream_set_do_close (G_LOCAL_FILE_INPUT_STREAM (stream->input_stream), FALSE); return G_FILE_IO_STREAM (stream); }
/** * eos_shard_writer_v2_add_blob: * @self: an #EosShardWriterV2 * @name: the name of the blob to store. * @file: a file of contents to write into the shard * @content_type: (allow-none): The MIME type of the blob. Pass %NULL to * autodetect using Gio. * @flags: flags about how the data should be stored in the file * * Adds the blob at the specified file path to the shard. * * Returns some opaque identifier for the blob, to be passed to * eos_shard_writer_v2_add_blob_to_record(). */ uint64_t eos_shard_writer_v2_add_blob (EosShardWriterV2 *self, char *name, GFile *file, char *content_type, EosShardBlobFlags flags) { struct eos_shard_writer_v2_blob_entry b = {}; g_autoptr(GFileInfo) info = NULL; b.name = g_strdup (name); if (content_type == NULL) { info = g_file_query_info (file, "standard::size,standard::content-type", 0, NULL, NULL); content_type = (char *) g_file_info_get_content_type (info); } else { info = g_file_query_info (file, "standard::size", 0, NULL, NULL); } g_return_val_if_fail (strlen (name) <= EOS_SHARD_V2_BLOB_MAX_NAME_SIZE, 0); g_return_val_if_fail (strlen (content_type) <= EOS_SHARD_V2_BLOB_MAX_CONTENT_TYPE_SIZE, 0); b.sblob.flags = flags; b.sblob.uncompressed_size = g_file_info_get_size (info); /* Lock around the cpool. */ g_mutex_lock (&self->lock); b.sblob.name_offs = constant_pool_add (&self->cpool, name); b.sblob.content_type_offs = constant_pool_add (&self->cpool, content_type); g_mutex_unlock (&self->lock); /* Now deal with blob contents. */ g_autoptr(GError) error = NULL; GFileInputStream *file_stream = g_file_read (file, NULL, &error); if (!file_stream) { g_error ("Could not read from %s: %s", g_file_get_path (file), error->message); return -1; } int blob_fd; if (b.sblob.flags & EOS_SHARD_BLOB_FLAG_COMPRESSED_ZLIB) { blob_fd = compress_blob_to_tmp (G_INPUT_STREAM (file_stream)); } else { blob_fd = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (file_stream)); } /* Find the size by seeking... */ uint64_t blob_size = lseek (blob_fd, 0, SEEK_END); g_assert (blob_size >= 0); g_assert (lseek (blob_fd, 0, SEEK_SET) >= 0); /* Checksum the blob. */ g_autoptr(GChecksum) checksum = g_checksum_new (G_CHECKSUM_SHA256); uint8_t buf[4096*4]; int size; while ((size = read (blob_fd, buf, sizeof (buf))) != 0) g_checksum_update (checksum, buf, size); size_t checksum_buf_len = sizeof (b.sblob.csum); g_checksum_get_digest (checksum, b.sblob.csum, &checksum_buf_len); g_assert (checksum_buf_len == sizeof (b.sblob.csum)); /* Add the blob entry to the file. */ g_mutex_lock (&self->lock); /* Look for a checksum in our table to return early if we have it... */ off_t data_start = GPOINTER_TO_UINT (g_hash_table_lookup (self->csum_to_data_start, &b.sblob.csum)); struct eos_shard_writer_v2_blob_entry *blob = g_memdup (&b, sizeof (b)); g_ptr_array_add (self->blobs, blob); uint64_t index = self->blobs->len - 1; /* If the blob data isn't already in the file, write it in. */ if (data_start == 0) { /* Position the blob in the file, and add it to the csum table. */ int shard_fd = self->ctx.fd; data_start = self->ctx.offset; self->ctx.offset = ALIGN (self->ctx.offset + blob_size); g_hash_table_insert (self->csum_to_data_start, &blob->sblob.csum, GUINT_TO_POINTER (data_start)); /* Unlock before writing data to the file. */ g_mutex_unlock (&self->lock); off_t offset = data_start; lseek (blob_fd, 0, SEEK_SET); while ((size = read (blob_fd, buf, sizeof (buf))) != 0) { g_assert (pwrite (shard_fd, buf, size, offset) >= 0); offset += size; } } else { g_mutex_unlock (&self->lock); } blob->sblob.data_start = data_start; blob->sblob.size = blob_size; g_assert (close (blob_fd) == 0 || errno == EINTR); return index; }
/** * ot_file_replace_contents_at: * * Like g_file_replace_contents(), except using a fd-relative * directory, and optionally enforces use of fdatasync(). */ gboolean ot_file_replace_contents_at (int dfd, const char *path, GBytes *contents, gboolean datasync, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int fd; g_autofree char *tmpname = NULL; g_autoptr(GOutputStream) stream = NULL; g_autoptr(GInputStream) instream = NULL; if (!gs_file_open_in_tmpdir_at (dfd, 0644, &tmpname, &stream, cancellable, error)) goto out; g_assert (G_IS_FILE_DESCRIPTOR_BASED (stream)); fd = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (stream)); instream = g_memory_input_stream_new_from_bytes (contents); if (g_bytes_get_size (contents) > 0) { int r = posix_fallocate (fd, 0, g_bytes_get_size (contents)); if (r != 0) { /* posix_fallocate is a weird deviation from errno standards */ errno = r; glnx_set_error_from_errno (error); goto out; } } if (g_output_stream_splice (stream, instream, 0, cancellable, error) < 0) goto out; if (datasync && fdatasync (fd) != 0) { glnx_set_error_from_errno (error); goto out; } if (!g_output_stream_close (stream, cancellable, error)) goto out; if (renameat (dfd, tmpname, dfd, path) == -1) { glnx_set_error_from_errno (error); goto out; } g_clear_pointer (&tmpname, g_free); ret = TRUE; out: if (tmpname) (void) unlinkat (dfd, tmpname, 0); return ret; }