Esempio n. 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);
}
Esempio n. 2
0
gboolean
ostree_content_file_parse (gboolean                compressed,
                           GFile                  *content_path,
                           gboolean                trusted,
                           GInputStream          **out_input,
                           GFileInfo             **out_file_info,
                           GVariant              **out_xattrs,
                           GCancellable           *cancellable,
                           GError                **error)
{
  gboolean ret = FALSE;
  guint64 length;
  struct stat stbuf;
  ot_lobj GInputStream *file_input = NULL;
  ot_lobj GInputStream *ret_input = NULL;
  ot_lobj GFileInfo *content_file_info = NULL;
  ot_lobj GFileInfo *ret_file_info = NULL;
  ot_lvariant GVariant *ret_xattrs = NULL;

  file_input = (GInputStream*)gs_file_read_noatime (content_path, cancellable, error);
  if (!file_input)
    goto out;

  if (fstat (g_file_descriptor_based_get_fd ((GFileDescriptorBased*)file_input), &stbuf) < 0)
    {
      ot_util_set_error_from_errno (error, errno);
      goto out;
    }

  length = stbuf.st_size;

  if (!ostree_content_stream_parse (compressed, file_input, length, trusted,
                                    out_input ? &ret_input : NULL,
                                    &ret_file_info, &ret_xattrs,
                                    cancellable, error))
    goto out;

  ret = TRUE;
  ot_transfer_out_value (out_input, &ret_input);
  ot_transfer_out_value (out_file_info, &ret_file_info);
  ot_transfer_out_value (out_xattrs, &ret_xattrs);
 out:
  return ret;
}
Esempio n. 3
0
gboolean
ot_util_variant_map_fd (GFileDescriptorBased  *stream,
                        goffset                start,
                        const GVariantType    *type,
                        gboolean               trusted,
                        GVariant             **out_variant,
                        GError               **error)
{
  gboolean ret = FALSE;
  gpointer map;
  struct stat stbuf;
  VariantMapData *mdata = NULL;
  gsize len;

  if (!gs_stream_fstat (stream, &stbuf, NULL, error))
    goto out;

  len = stbuf.st_size - start;
  map = mmap (NULL, len, PROT_READ, MAP_PRIVATE, 
              g_file_descriptor_based_get_fd (stream), start);
  if (!map)
    {
      ot_util_set_error_from_errno (error, errno);
      goto out;
    }

  mdata = g_new (VariantMapData, 1);
  mdata->addr = map;
  mdata->len = len;

  ret = TRUE;
  *out_variant = g_variant_new_from_data (type, map, len, trusted,
                                          variant_map_data_destroy, mdata);
 out:
  return ret;
}
Esempio n. 4
0
/**
 * gs_stream_fstat:
 * @stream: A stream containing a Unix file descriptor
 * @stbuf: Memory location to write stat buffer
 * @cancellable:
 * @error:
 *
 * Some streams created via libgsystem are #GUnixInputStream; these do
 * not support e.g. g_file_input_stream_query_info().  This function
 * allows dropping to the raw unix fstat() call for these types of
 * streams, while still conveniently wrapped with the normal GLib
 * handling of @cancellable and @error.
 */
gboolean
gs_stream_fstat (GFileDescriptorBased *stream,
                 struct stat          *stbuf,
                 GCancellable         *cancellable,
                 GError              **error)
{
    gboolean ret = FALSE;
    int fd;

    if (g_cancellable_set_error_if_cancelled (cancellable, error))
        goto out;

    fd = g_file_descriptor_based_get_fd (stream);

    if (fstat (fd, stbuf) == -1)
    {
        gs_set_prefix_error_from_errno (error, errno, "fstat");
        goto out;
    }

    ret = TRUE;
out:
    return ret;
}
gboolean
_ostree_static_delta_part_open (GInputStream   *part_in,
                                GBytes         *inline_part_bytes,
                                OstreeStaticDeltaOpenFlags flags,
                                const char     *expected_checksum,
                                GVariant    **out_part,
                                GCancellable *cancellable,
                                GError      **error)
{
  gboolean ret = FALSE;
  const gboolean trusted = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_VARIANT_TRUSTED) > 0;
  const gboolean skip_checksum = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM) > 0;
  gsize bytes_read;
  guint8 comptype;
  g_autoptr(GChecksum) checksum = NULL;
  g_autoptr(GInputStream) checksum_in = NULL;
  g_autoptr(GVariant) ret_part = NULL;
  GInputStream *source_in;

  /* We either take a fd or a GBytes reference */
  g_return_val_if_fail (G_IS_FILE_DESCRIPTOR_BASED (part_in) || inline_part_bytes != NULL, FALSE);
  g_return_val_if_fail (skip_checksum || expected_checksum != NULL, FALSE);

  if (!skip_checksum)
    {
      checksum = g_checksum_new (G_CHECKSUM_SHA256);
      checksum_in = (GInputStream*)ostree_checksum_input_stream_new (part_in, checksum);
      source_in = checksum_in;
    }
  else
    {
      source_in = part_in;
    }

  { guint8 buf[1];
    /* First byte is compression type */
    if (!g_input_stream_read_all (source_in, buf, sizeof(buf), &bytes_read,
                                  cancellable, error))
      {
        g_prefix_error (error, "Reading initial compression flag byte: ");
      goto out;
      }
    comptype = buf[0];
  }

  switch (comptype)
    {
    case 0:
      if (!inline_part_bytes)
        {
          int part_fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*)part_in);

          /* No compression, no checksums - a fast path */
          if (!ot_util_variant_map_fd (part_fd, 1, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                       trusted, &ret_part, error))
            goto out;
        }
      else
        {
          g_autoptr(GBytes) content_bytes = g_bytes_new_from_bytes (inline_part_bytes, 1,
                                                                    g_bytes_get_size (inline_part_bytes) - 1);
          ret_part = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                               content_bytes, trusted);
          g_variant_ref_sink (ret_part);
        }

      if (!skip_checksum)
        g_checksum_update (checksum, g_variant_get_data (ret_part),
                           g_variant_get_size (ret_part));
      
      break;
    case 'x':
      {
        g_autofree char *tmppath = g_strdup ("/var/tmp/ostree-delta-XXXXXX");
        g_autoptr(GConverter) decomp = (GConverter*) _ostree_lzma_decompressor_new ();
        g_autoptr(GInputStream) convin = g_converter_input_stream_new (source_in, decomp);
        g_autoptr(GOutputStream) unpacked_out = NULL;
        glnx_fd_close int unpacked_fd = -1;
        gssize n_bytes_written;

        unpacked_fd = g_mkstemp_full (tmppath, O_RDWR | O_CLOEXEC, 0640);
        if (unpacked_fd < 0)
          {
            glnx_set_error_from_errno (error);
            goto out;
          }
        
        /* Now make it autocleanup on process exit - in the future, we
         * should consider caching unpacked deltas as well.
         */
        if (unlink (tmppath) < 0)
          {
            glnx_set_error_from_errno (error);
            goto out;
          }
        
        unpacked_out = g_unix_output_stream_new (unpacked_fd, FALSE);

        n_bytes_written = g_output_stream_splice (unpacked_out, convin,
                                                  G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
                                                  G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
                                                  cancellable, error);
        if (n_bytes_written < 0)
          goto out;

        if (!ot_util_variant_map_fd (unpacked_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                     trusted, &ret_part, error))
          goto out;
      }
      break;
    default:
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Invalid compression type '%u'", comptype);
      goto out;
    }

  if (checksum)
    {
      const char *actual_checksum = g_checksum_get_string (checksum);
      g_assert (expected_checksum != NULL);
      if (strcmp (actual_checksum, expected_checksum) != 0)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "Checksum mismatch in static delta part; expected=%s actual=%s",
                       expected_checksum, actual_checksum);
          goto out;
        }
    }
        
  ret = TRUE;
  *out_part = g_steal_pointer (&ret_part);
 out:
  return ret;
}
Esempio n. 6
0
static gboolean
checkout_object_for_uncompressed_cache (OstreeRepo      *self,
                                        const char      *loose_path,
                                        GFileInfo       *src_info,
                                        GInputStream    *content,
                                        GCancellable    *cancellable,
                                        GError         **error)
{
  gboolean ret = FALSE;
  g_autofree char *temp_filename = NULL;
  g_autoptr(GOutputStream) temp_out = NULL;
  int fd;
  int res;
  guint32 file_mode;

  /* Don't make setuid files in uncompressed cache */
  file_mode = g_file_info_get_attribute_uint32 (src_info, "unix::mode");
  file_mode &= ~(S_ISUID|S_ISGID);

  if (!gs_file_open_in_tmpdir_at (self->tmp_dir_fd, file_mode,
                                  &temp_filename, &temp_out,
                                  cancellable, error))
    goto out;

  if (g_output_stream_splice (temp_out, content, 0, cancellable, error) < 0)
    goto out;

  if (!g_output_stream_flush (temp_out, cancellable, error))
    goto out;

  fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*)temp_out);

  if (!self->disable_fsync)
    {
      do
        res = fsync (fd);
      while (G_UNLIKELY (res == -1 && errno == EINTR));
      if (G_UNLIKELY (res == -1))
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }

  if (!g_output_stream_close (temp_out, cancellable, error))
    goto out;

  if (!_ostree_repo_ensure_loose_objdir_at (self->uncompressed_objects_dir_fd,
                                            loose_path,
                                            cancellable, error))
    goto out;

  if (G_UNLIKELY (renameat (self->tmp_dir_fd, temp_filename,
                            self->uncompressed_objects_dir_fd, loose_path) == -1))
    {
      if (errno != EEXIST)
        {
          glnx_set_error_from_errno (error);
          g_prefix_error (error, "Storing file '%s': ", temp_filename);
          goto out;
        }
      else
        (void) unlinkat (self->tmp_dir_fd, temp_filename, 0);
    }

  ret = TRUE;
 out:
  return ret;
}
Esempio n. 7
0
static gboolean
write_regular_file_content (OstreeRepo            *self,
                            OstreeRepoCheckoutOptions *options,
                            GOutputStream         *output,
                            GFileInfo             *file_info,
                            GVariant              *xattrs,
                            GInputStream          *input,
                            GCancellable          *cancellable,
                            GError               **error)
{
  gboolean ret = FALSE;
  const OstreeRepoCheckoutMode mode = options->mode;
  int fd;
  int res;

  if (g_output_stream_splice (output, input, 0,
                              cancellable, error) < 0)
    goto out;

  if (!g_output_stream_flush (output, cancellable, error))
    goto out;

  fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*)output);

  if (mode != OSTREE_REPO_CHECKOUT_MODE_USER)
    {
      do
        res = fchown (fd,
                      g_file_info_get_attribute_uint32 (file_info, "unix::uid"),
                      g_file_info_get_attribute_uint32 (file_info, "unix::gid"));
      while (G_UNLIKELY (res == -1 && errno == EINTR));
      if (G_UNLIKELY (res == -1))
        {
          glnx_set_error_from_errno (error);
          goto out;
        }

      do
        res = fchmod (fd, g_file_info_get_attribute_uint32 (file_info, "unix::mode"));
      while (G_UNLIKELY (res == -1 && errno == EINTR));
      if (G_UNLIKELY (res == -1))
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
              
      if (xattrs)
        {
          if (!glnx_fd_set_all_xattrs (fd, xattrs, cancellable, error))
            goto out;
        }
    }
          
  if (fsync_is_enabled (self, options))
    {
      if (fsync (fd) == -1)
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }
          
  if (!g_output_stream_close (output, cancellable, error))
    goto out;

  ret = TRUE;
 out:
  return ret;
}
static gboolean
handle_install_authorized_keys (MinCloudAgentApp      *self,
                                GInputStream               *instream,
                                GCancellable               *cancellable,
                                GError                    **error)
{
  gboolean ret = FALSE;
  int fd;
  guint i;
  gs_unref_object GOutputStream *outstream = NULL;
  gs_unref_object GDataInputStream *datain = NULL;
  gs_unref_ptrarray GPtrArray *lines = g_ptr_array_new_with_free_func (g_free);

  datain = g_data_input_stream_new (instream);

  while (TRUE)
    {
      gsize len;
      GError *temp_error = NULL;
      char *line = g_data_input_stream_read_line_utf8 (datain, &len, cancellable, &temp_error);
      if (temp_error != NULL)
        {
          g_propagate_error (error, temp_error);
          g_prefix_error (error, "Reading ssh keys: ");
          goto out;
        }
      if (!line)
        break;
      g_ptr_array_add (lines, line);
    }

  (void) g_input_stream_close ((GInputStream*)datain, NULL, NULL);

  outstream = (GOutputStream*)g_file_append_to (self->authorized_keys_path, 0, cancellable, error);
  if (!outstream)
    {
      g_prefix_error (error, "Appending to '%s': ",
                      gs_file_get_path_cached (self->authorized_keys_path));
      goto out;
    }

  fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*)outstream);
  if (fchmod (fd, 0600) != 0)
    {
      int errsv = errno;
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Failed to chmod authorized_keys: %s",
                   g_strerror (errsv));
      goto out;
    }

  for (i = 0; i < lines->len; i++)
    {
      const char *line = lines->pdata[i];
      char nl[] = { '\n' };
      gsize bytes_written;
      if (!g_output_stream_write_all (outstream, line, strlen (line),
                                      &bytes_written,
                                      cancellable, error))
        goto out;
      if (!g_output_stream_write_all (outstream, nl, sizeof (nl),
                                      &bytes_written,
                                      cancellable, error))
        goto out;
    }

  if (!g_output_stream_flush (outstream, cancellable, error))
    goto out;

  gs_log_structured_print_id_v (MCA_KEY_INSTALLED_SUCCESS_ID,
                                "Successfully installed ssh key for '%s'",
                                "root");

  ret = TRUE;
 out:
  return ret;
}
static VALUE
rg_fd(VALUE self)
{
        return FD2RVAL(g_file_descriptor_based_get_fd(_SELF(self)));
}
gboolean
_ostree_static_delta_part_open (GInputStream   *part_in,
                                GBytes         *inline_part_bytes,
                                OstreeStaticDeltaOpenFlags flags,
                                const char     *expected_checksum,
                                GVariant    **out_part,
                                GCancellable *cancellable,
                                GError      **error)
{
  const gboolean trusted = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_VARIANT_TRUSTED) > 0;
  const gboolean skip_checksum = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM) > 0;
  gsize bytes_read;
  guint8 comptype;
  g_autoptr(GChecksum) checksum = NULL;
  g_autoptr(GInputStream) checksum_in = NULL;
  GInputStream *source_in;

  /* We either take a fd or a GBytes reference */
  g_return_val_if_fail (G_IS_FILE_DESCRIPTOR_BASED (part_in) || inline_part_bytes != NULL, FALSE);
  g_return_val_if_fail (skip_checksum || expected_checksum != NULL, FALSE);

  if (!skip_checksum)
    {
      checksum = g_checksum_new (G_CHECKSUM_SHA256);
      checksum_in = (GInputStream*)ostree_checksum_input_stream_new (part_in, checksum);
      source_in = checksum_in;
    }
  else
    {
      source_in = part_in;
    }

  { guint8 buf[1];
    /* First byte is compression type */
    if (!g_input_stream_read_all (source_in, buf, sizeof(buf), &bytes_read,
                                  cancellable, error))
      return glnx_prefix_error (error, "Reading initial compression flag byte");
    comptype = buf[0];
  }

  g_autoptr(GVariant) ret_part = NULL;
  switch (comptype)
    {
    case 0:
      if (!inline_part_bytes)
        {
          int part_fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*)part_in);

          /* No compression, no checksums - a fast path */
          if (!ot_util_variant_map_fd (part_fd, 1, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                       trusted, &ret_part, error))
            return FALSE;
        }
      else
        {
          g_autoptr(GBytes) content_bytes = g_bytes_new_from_bytes (inline_part_bytes, 1,
                                                                    g_bytes_get_size (inline_part_bytes) - 1);
          ret_part = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                               content_bytes, trusted);
          g_variant_ref_sink (ret_part);
        }

      if (!skip_checksum)
        g_checksum_update (checksum, g_variant_get_data (ret_part),
                           g_variant_get_size (ret_part));

      break;
    case 'x':
      {
        g_autoptr(GConverter) decomp = (GConverter*) _ostree_lzma_decompressor_new ();
        g_autoptr(GInputStream) convin = g_converter_input_stream_new (source_in, decomp);
        g_autoptr(GBytes) buf = ot_map_anonymous_tmpfile_from_content (convin, cancellable, error);
        if (!buf)
          return FALSE;

        ret_part = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
                                             buf, FALSE);
      }
      break;
    default:
      return glnx_throw (error, "Invalid compression type '%u'", comptype);
    }

  if (checksum)
    {
      const char *actual_checksum = g_checksum_get_string (checksum);
      g_assert (expected_checksum != NULL);
      if (strcmp (actual_checksum, expected_checksum) != 0)
        return glnx_throw (error, "Checksum mismatch in static delta part; expected=%s actual=%s",
                           expected_checksum, actual_checksum);
    }

  *out_part = g_steal_pointer (&ret_part);
  return TRUE;
}
Esempio n. 11
0
/**
 * 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;
}
Esempio n. 12
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;
}