/** * gs_file_open_in_tmpdir: * @tmpdir: Directory to place temporary file * @mode: Default mode (will be affected by umask) * @out_file: (out) (transfer full): Newly created file path * @out_stream: (out) (transfer full) (allow-none): Newly created output stream * @cancellable: * @error: * * Like g_file_open_tmp(), except the file will be created in the * provided @tmpdir, and allows specification of the Unix @mode, which * means private files may be created. Return values will be stored * in @out_file, and optionally @out_stream. */ gboolean gs_file_open_in_tmpdir (GFile *tmpdir, int mode, GFile **out_file, GOutputStream **out_stream, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; DIR *d = NULL; int dfd = -1; char *tmp_name = NULL; GOutputStream *ret_stream = NULL; d = opendir (gs_file_get_path_cached (tmpdir)); if (!d) { gs_set_prefix_error_from_errno (error, errno, "opendir"); goto out; } dfd = dirfd (d); if (!gs_file_open_in_tmpdir_at (dfd, mode, &tmp_name, out_stream ? &ret_stream : NULL, cancellable, error)) goto out; ret = TRUE; *out_file = g_file_get_child (tmpdir, tmp_name); gs_transfer_out_value (out_stream, &ret_stream); out: if (d) (void) closedir (d); g_clear_object (&ret_stream); g_free (tmp_name); 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; }
/* * This function creates a file under a temporary name, then rename()s * it into place. This implements union-like behavior. */ static gboolean checkout_file_unioning_from_input_at (OstreeRepo *repo, OstreeRepoCheckoutOptions *options, GFileInfo *file_info, GVariant *xattrs, GInputStream *input, int destination_dfd, const char *destination_name, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autofree char *temp_filename = NULL; if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_SYMBOLIC_LINK) { if (!_ostree_make_temporary_symlink_at (destination_dfd, g_file_info_get_symlink_target (file_info), &temp_filename, cancellable, error)) goto out; if (xattrs) { if (!glnx_dfd_name_set_all_xattrs (destination_dfd, temp_filename, xattrs, cancellable, error)) goto out; } } else if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR) { g_autoptr(GOutputStream) temp_out = NULL; guint32 file_mode; file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode"); /* Don't make setuid files on checkout when we're doing --user */ if (options->mode == OSTREE_REPO_CHECKOUT_MODE_USER) file_mode &= ~(S_ISUID|S_ISGID); if (!gs_file_open_in_tmpdir_at (destination_dfd, file_mode, &temp_filename, &temp_out, cancellable, error)) goto out; if (!write_regular_file_content (repo, options, temp_out, file_info, xattrs, input, cancellable, error)) goto out; } else g_assert_not_reached (); if (G_UNLIKELY (renameat (destination_dfd, temp_filename, destination_dfd, destination_name) == -1)) { glnx_set_error_from_errno (error); goto out; } ret = TRUE; out: return ret; }
/** * 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; }