[libgsystem] fileutil: Add initial directory-relative API
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgsystem] fileutil: Add initial directory-relative API
- Date: Fri, 6 Sep 2013 22:22:46 +0000 (UTC)
commit d63409a3d44b61e40f30cde79ebae879925c716d
Author: Colin Walters <walters verbum org>
Date: Fri Sep 6 18:14:02 2013 -0400
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.
gsystem-file-utils.c | 111 ++++++++++++++++++++++++++++++++++++++++----------
gsystem-file-utils.h | 12 +++++
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,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]