summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2013-09-06 18:14:02 -0400
committerColin Walters <walters@verbum.org>2013-09-06 18:22:19 -0400
commitd63409a3d44b61e40f30cde79ebae879925c716d (patch)
tree2070fc83db6b7b823691820c732f580c987901f2
parent2d24676846e3058d7a03bc127591f9fdcc346fab (diff)
downloadlibgsystem-d63409a3d44b61e40f30cde79ebae879925c716d.tar.gz
fileutil: Add initial directory-relative API
Modern UNIX come with a variety of filesystem API suffixed with "at", like openat(), linkat(), etc. The reason for their existence is multiple. First, if you're doing a lot of file operations in a directory, it's simply more efficient to avoid having the kernel traverse potentially long pathnames constantly. Second, this avoids a problem where if a user does e.g.: rm -rf somedir & mv somedir othername mkdir somedir touch somedir/otherfile We won't end up deleting otherfile, because all of our operations are relative to the original "somedir". The second rationale is unlikely to matter for OSTree since we'll assume the user isn't going to move a repository around while we're committing to it, but anyways, it's just better to use fd-relative pathnames.
-rw-r--r--gsystem-file-utils.c111
-rw-r--r--gsystem-file-utils.h12
2 files changed, 101 insertions, 22 deletions
diff --git a/gsystem-file-utils.c b/gsystem-file-utils.c
index 6291027..0486bf8 100644
--- a/gsystem-file-utils.c
+++ b/gsystem-file-utils.c
@@ -476,10 +476,36 @@ gsystem_fileutil_gen_tmp_name (const char *prefix,
}
/**
- * gs_file_open_in_tmpdir:
- * @tmpdir: Directory to place temporary file
+ * gs_file_open_dir_fd:
+ * @path: Directory name
+ * @out_fd: (out): File descriptor for directory
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * On success, sets @out_fd to a file descriptor for the directory
+ * that can be used with UNIX functions such as openat().
+ */
+gboolean
+gs_file_open_dir_fd (GFile *path,
+ int *out_fd,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* Linux specific probably */
+ *out_fd = open (gs_file_get_path_cached (path), O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC);
+ if (*out_fd == -1)
+ {
+ _set_error_from_errno (error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * gs_file_open_in_tmpdir_at:
+ * @tmpdir_fd: Directory to place temporary file
* @mode: Default mode (will be affected by umask)
- * @out_file: (out) (transfer full): Newly created file path
+ * @out_name: (out) (transfer full): Newly created file name
* @out_stream: (out) (transfer full) (allow-none): Newly created output stream
* @cancellable:
* @error:
@@ -487,32 +513,22 @@ gsystem_fileutil_gen_tmp_name (const char *prefix,
* 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.
+ * in @out_name, and optionally @out_stream.
*/
gboolean
-gs_file_open_in_tmpdir (GFile *tmpdir,
- int mode,
- GFile **out_file,
- GOutputStream **out_stream,
- GCancellable *cancellable,
- GError **error)
+gs_file_open_in_tmpdir_at (int tmpdir_fd,
+ int mode,
+ char **out_name,
+ GOutputStream **out_stream,
+ GCancellable *cancellable,
+ GError **error)
{
gboolean ret = FALSE;
const int max_attempts = 128;
guint i;
- DIR *d = NULL;
- int dfd = -1;
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++)
{
@@ -520,7 +536,7 @@ gs_file_open_in_tmpdir (GFile *tmpdir,
tmp_name = gsystem_fileutil_gen_tmp_name (NULL, NULL);
do
- fd = openat (dfd, tmp_name, O_WRONLY | O_CREAT | O_EXCL, mode);
+ fd = openat (tmpdir_fd, tmp_name, O_WRONLY | O_CREAT | O_EXCL, mode);
while (fd == -1 && errno == EINTR);
if (fd < 0 && errno != EEXIST)
{
@@ -537,11 +553,62 @@ gs_file_open_in_tmpdir (GFile *tmpdir,
}
ret = TRUE;
- *out_file = g_file_get_child (tmpdir, tmp_name);
+ gs_transfer_out_value (out_name, &tmp_name);
if (out_stream)
*out_stream = g_unix_output_stream_new (fd, TRUE);
out:
+ g_free (tmp_name);
+ return ret;
+}
+
+/**
+ * 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)
+ {
+ _set_error_from_errno (error);
+ 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;
}
diff --git a/gsystem-file-utils.h b/gsystem-file-utils.h
index 6075e60..38f4b0a 100644
--- a/gsystem-file-utils.h
+++ b/gsystem-file-utils.h
@@ -63,6 +63,18 @@ gboolean gs_file_sync_data (GFile *file,
char * gsystem_fileutil_gen_tmp_name (const char *prefix,
const char *suffix);
+gboolean gs_file_open_dir_fd (GFile *path,
+ int *out_fd,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean gs_file_open_in_tmpdir_at (int dirfd,
+ int mode,
+ char **out_name,
+ GOutputStream **out_stream,
+ GCancellable *cancellable,
+ GError **error);
+
gboolean gs_file_open_in_tmpdir (GFile *tmpdir,
int mode,
GFile **out_file,