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); }
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; }
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; }
/** * 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; }
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; }
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; }
/** * 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; }