summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2013-08-06 23:49:11 +0200
committerColin Walters <walters@verbum.org>2013-08-06 23:57:39 +0200
commit3dee2f23ac34d1d5ce2ed8cbad14cb0596d2b57f (patch)
tree427bdc6adbb850eb24b4d8edf766f69866274dee
parenta53219add5cbf9a9026422ea72b2e37dc3febe52 (diff)
downloadlibgsystem-3dee2f23ac34d1d5ce2ed8cbad14cb0596d2b57f.tar.gz
fileutil: Add new API to open a temporary file in a particular directory
This is useful for ostree to write to repo/tmp.
-rw-r--r--gsystem-file-utils.c70
-rw-r--r--gsystem-file-utils.h7
2 files changed, 77 insertions, 0 deletions
diff --git a/gsystem-file-utils.c b/gsystem-file-utils.c
index 5510948..c25b309 100644
--- a/gsystem-file-utils.c
+++ b/gsystem-file-utils.c
@@ -35,6 +35,7 @@
#include <gio/gunixoutputstream.h>
#include <glib-unix.h>
#include <limits.h>
+#include <dirent.h>
static int
close_nointr (int fd)
@@ -428,6 +429,75 @@ gsystem_fileutil_gen_tmp_name (const char *prefix,
return g_string_free (str, FALSE);
}
+/**
+ * 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;
+ const int max_attempts = 128;
+ guint i;
+ DIR *d;
+ int dfd;
+ char *tmp_name = NULL;
+ int fd;
+
+ d = opendir (gs_file_get_path_cached (tmpdir));
+ if (!d)
+ {
+ _set_error_from_errno (error);
+ goto out;
+ }
+ dfd = dirfd (d);
+
+ /* 128 attempts seems reasonable... */
+ for (i = 0; i < max_attempts; i++)
+ {
+ g_free (tmp_name);
+ tmp_name = gsystem_fileutil_gen_tmp_name (NULL, NULL);
+
+ do
+ fd = openat (dfd, tmp_name, O_WRONLY | O_CREAT | O_EXCL, mode);
+ while (fd == -1 && errno == EINTR);
+ if (fd < 0 && errno != EEXIST)
+ {
+ _set_error_from_errno (error);
+ goto out;
+ }
+ break;
+ }
+ if (i == max_attempts)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Exhausted attempts to open temporary file");
+ goto out;
+ }
+
+ ret = TRUE;
+ *out_file = g_file_get_child (tmpdir, tmp_name);
+ if (out_stream)
+ *out_stream = g_unix_output_stream_new (fd, TRUE);
+ out:
+ return ret;
+}
+
static gboolean
linkcopy_internal_attempt (GFile *src,
GFile *dest,
diff --git a/gsystem-file-utils.h b/gsystem-file-utils.h
index d8cd73d..0d9994d 100644
--- a/gsystem-file-utils.h
+++ b/gsystem-file-utils.h
@@ -55,6 +55,13 @@ gboolean gs_file_sync_data (GFile *file,
char * gsystem_fileutil_gen_tmp_name (const char *prefix,
const char *suffix);
+gboolean gs_file_open_in_tmpdir (GFile *tmpdir,
+ int mode,
+ GFile **out_file,
+ GOutputStream **out_stream,
+ GCancellable *cancellable,
+ GError **error);
+
gboolean gs_file_create (GFile *file,
int mode,
GOutputStream **out_stream,