Exemple #1
0
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;
}
Exemple #3
0
/**
 * 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;
}