[libgsystem/wip/diriter: 2/3] fileutil: Add new API to iterate over a directory with *at system calls



commit e8d509e1d26b51bc01d77934d1b1c07ef78cb5ec
Author: Colin Walters <walters verbum org>
Date:   Fri Dec 12 08:49:36 2014 -0500

    fileutil: Add new API to iterate over a directory with *at system calls
    
    Using the GFileEnumerator doesn't allow using the *at system calls
    such as unlinkat() very easily.  These system calls are both more
    performant (no need to traverse paths repeatedly) and more secure
    (assuming an attacker has write access to an intervening path).
    
    Currently, this exposes the Unix "struct dirent*" which means it's not
    available to introspection, but a future API addition could add a
    binding to create a GFileInfo*.

 src/gsystem-file-utils.c |   94 ++++++++++++++++++++++++++++++++++++++++++++++
 src/gsystem-file-utils.h |   25 ++++++++++++
 2 files changed, 119 insertions(+), 0 deletions(-)
---
diff --git a/src/gsystem-file-utils.c b/src/gsystem-file-utils.c
index 30ccc9d..0528a13 100644
--- a/src/gsystem-file-utils.c
+++ b/src/gsystem-file-utils.c
@@ -1761,3 +1761,97 @@ gs_file_set_all_xattrs (GFile         *file,
 {
   return set_all_xattrs_for_path (gs_file_get_path_cached (file), xattrs, cancellable, error);
 }
+
+struct GsRealDirfdIterator
+{
+  gboolean initialized;
+  int fd;
+  DIR *d;
+};
+typedef struct GsRealDirfdIterator GsRealDirfdIterator;
+
+gboolean
+gs_dirfd_iterator_init_at (int              dfd,
+                           const char      *path,
+                           gboolean         follow,
+                           GSDirFdIterator *dfd_iter,
+                           GError         **error)
+{
+  gboolean ret = FALSE;
+  int fd = -1;
+  
+  if (!gs_opendirat (dfd, path, follow, &fd, error))
+    goto out;
+
+  if (!gs_dirfd_iterator_init_take_fd (fd, dfd_iter, error))
+    goto out;
+  /* Transfer value */
+  fd = -1;
+
+  ret = TRUE;
+ out:
+  if (fd != -1) (void) close (fd);
+  return ret;
+}
+
+gboolean
+gs_dirfd_iterator_init_take_fd (int dfd,
+                                GSDirFdIterator *dfd_iter,
+                                GError **error)
+{
+  gboolean ret = FALSE;
+  GsRealDirfdIterator *real_dfd_iter = (GsRealDirfdIterator*) dfd_iter;
+  DIR *d = NULL;
+
+  d = fdopendir (dfd);
+  if (!d)
+    {
+      _set_error_from_errno ("fdopendir", error);
+      goto out;
+    }
+
+  real_dfd_iter->fd = dfd;
+  real_dfd_iter->d = d;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+gboolean
+gs_dirfd_iterator_next_dent (GSDirFdIterator  *dfd_iter,
+                             struct dirent   **out_dent,
+                             GCancellable     *cancellable,
+                             GError          **error)
+{
+  gboolean ret = FALSE;
+  GsRealDirfdIterator *real_dfd_iter = (GsRealDirfdIterator*) dfd_iter;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    goto out;
+
+  do
+    {
+      errno = 0;
+      *out_dent = readdir (real_dfd_iter->d);
+      if (*out_dent == NULL && errno != 0)
+        {
+          _set_error_from_errno ("fdopendir", error);
+          goto out;
+        }
+    } while (*out_dent &&
+             (strcmp ((*out_dent)->d_name, ".") == 0 ||
+              strcmp ((*out_dent)->d_name, "..") == 0));
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+void
+gs_dirfd_iterator_clear (GSDirFdIterator *dfd_iter)
+{
+  GsRealDirfdIterator *real_dfd_iter = (GsRealDirfdIterator*) dfd_iter;
+  /* fd is owned by dfd_iter */
+  (void) closedir (real_dfd_iter->d);
+}
diff --git a/src/gsystem-file-utils.h b/src/gsystem-file-utils.h
index 82b7070..3895a85 100644
--- a/src/gsystem-file-utils.h
+++ b/src/gsystem-file-utils.h
@@ -30,6 +30,31 @@ const char *gs_file_get_path_cached (GFile *file);
 
 const char *gs_file_get_basename_cached (GFile *file);
 
+struct GSDirFdIterator {
+  gboolean initialized;
+  int fd;
+  gpointer padding_data[4];
+};
+
+typedef struct GSDirFdIterator GSDirFdIterator;
+gboolean gs_dirfd_iterator_init_at (int dfd, const char *path,
+                                    gboolean follow,
+                                    GSDirFdIterator *dfd_iter, GError **error);
+gboolean gs_dirfd_iterator_init_take_fd (int dfd, GSDirFdIterator *dfd_iter, GError **error);
+#ifndef __GI_SCANNER__
+gboolean gs_dirfd_iterator_next_dent (GSDirFdIterator  *dfd_iter,
+                                      struct dirent   **out_dent,
+                                      GCancellable     *cancellable,
+                                      GError          **error);
+#endif
+gboolean gs_dirfd_iterator_next_finfo (GSDirFdIterator  *dfd_iter,
+                                       GFileInfo       **out_finfo,
+                                       GError          **error);
+void gs_dirfd_iterator_clear (GSDirFdIterator *dfd_iter);
+
+#define gs_dirfd_iterator_cleanup __attribute__((cleanup(gs_dirfd_iterator_clear)))
+
+
 gboolean gs_file_enumerator_iterate (GFileEnumerator  *direnum,
                                      GFileInfo       **out_info,
                                      GFile           **out_child,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]