[libgsystem] fileutil: Add initial directory-relative API



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]