summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2014-12-12 08:49:36 -0500
committerColin Walters <walters@verbum.org>2014-12-12 08:49:36 -0500
commite8d509e1d26b51bc01d77934d1b1c07ef78cb5ec (patch)
tree2983c76feadf1b3373c6d6ef43781c81f1578af8
parent368bf6f452636d6760d17b7aa7c0afa085408d4b (diff)
downloadlibgsystem-e8d509e1d26b51bc01d77934d1b1c07ef78cb5ec.tar.gz
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*.
-rw-r--r--src/gsystem-file-utils.c94
-rw-r--r--src/gsystem-file-utils.h25
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,