[PATCH 2/3] util: Introduce virDirOpenSorted()

Michal Privoznik mprivozn at redhat.com
Fri Dec 23 08:35:29 UTC 2022


In some cases (e.g. test suite) it may be desirable to read
directory contents in a sorted fashion. Introduce
virDirOpenSorted() and modify virDirRead() so that directory
entries can be returned in alphabetical order (as defined by
strcmp()).

Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
---
 src/libvirt_private.syms |   1 +
 src/util/virfile.c       | 110 ++++++++++++++++++++++++++++++++++-----
 src/util/virfile.h       |   2 +
 3 files changed, 101 insertions(+), 12 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index ae746a2d51..cdf027f285 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2246,6 +2246,7 @@ virDirCreate;
 virDirOpen;
 virDirOpenIfExists;
 virDirOpenQuiet;
+virDirOpenSorted;
 virDirRead;
 virFileAccessibleAs;
 virFileActivateDirOverrideForLib;
diff --git a/src/util/virfile.c b/src/util/virfile.c
index ae60be2189..d570603cbd 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -2899,13 +2899,20 @@ virFileRemove(const char *path,
 
 struct _virDir {
     DIR *dir;
+    bool sorted;
+    size_t pos;
+    size_t len;
+    GSList *ents;
 };
 
+
 static int
 virDirOpenInternal(virDir **dirp, const char *name, bool ignoreENOENT, bool quiet)
 {
     DIR *dir = opendir(name); /* exempt from syntax-check */
 
+    *dirp = NULL;
+
     if (!dir) {
         if (quiet)
             return -1;
@@ -2916,7 +2923,7 @@ virDirOpenInternal(virDir **dirp, const char *name, bool ignoreENOENT, bool quie
         return -1;
     }
 
-    *dirp = g_new(virDir, 1);
+    *dirp = g_new0(virDir, 1);
     (*dirp)->dir = g_steal_pointer(&dir);
     return 1;
 }
@@ -2966,6 +2973,56 @@ virDirOpenQuiet(virDir **dirp, const char *name)
     return virDirOpenInternal(dirp, name, false, true);
 }
 
+/**
+ * virDirOpenSorted:
+ * @dirp: directory stream
+ * @name: path of the directory:
+ *
+ * Like virDirOpen() except subsequent virDirRead() returns
+ * directory content sorted alphabetically (as defined by
+ * strcmp()).
+ *
+ * Returns 1 on success.
+ *        -1 on failure.
+ */
+int virDirOpenSorted(virDir **dirp, const char *dirname)
+{
+    int rc = virDirOpen(dirp, dirname);
+
+    if (rc <= 0)
+        return rc;
+
+    (*dirp)->sorted = true;
+    return rc;
+}
+
+static int
+virDirReadImpl(DIR *dir, struct dirent **ent, const char *name)
+{
+    do {
+        errno = 0;
+        *ent = readdir(dir); /* exempt from syntax-check */
+        if (!*ent && errno) {
+            if (name)
+                virReportSystemError(errno, _("Unable to read directory '%s'"),
+                                     name);
+            return -1;
+        }
+    } while (*ent && (STREQ((*ent)->d_name, ".") ||
+                      STREQ((*ent)->d_name, "..")));
+
+    return !!*ent;
+}
+
+static int
+virDirEntsSorter(const void *a, const void *b)
+{
+    const struct dirent *da = a;
+    const struct dirent *db = b;
+
+    return strcmp(da->d_name, db->d_name);
+}
+
 /**
  * virDirRead:
  * @dirp: directory to read
@@ -2988,17 +3045,42 @@ virDirOpenQuiet(virDir **dirp, const char *name)
  */
 int virDirRead(virDir *dirp, struct dirent **ent, const char *name)
 {
-    do {
-        errno = 0;
-        *ent = readdir(dirp->dir); /* exempt from syntax-check */
-        if (!*ent && errno) {
-            if (name)
-                virReportSystemError(errno, _("Unable to read directory '%s'"),
-                                     name);
-            return -1;
-        }
-    } while (*ent && (STREQ((*ent)->d_name, ".") ||
-                      STREQ((*ent)->d_name, "..")));
+    if (dirp->sorted && !dirp->ents) {
+        int rc;
+
+        do {
+            struct dirent *tmp = NULL;
+
+            rc = virDirReadImpl(dirp->dir, &tmp, name);
+            if (rc < 0)
+                return rc;
+
+            if (rc > 0) {
+                struct dirent *copy;
+                size_t len;
+
+                /* Thing is, struct dirent is not of a static size. It also
+                 * contains the d_name as a variable sized array. */
+                len = offsetof(struct dirent, d_name) + strlen(tmp->d_name) + 1;
+                copy = g_memdup(tmp, len);
+
+                dirp->ents = g_slist_prepend(dirp->ents, copy);
+            }
+        } while (rc > 0);
+
+        dirp->len = g_slist_length(dirp->ents);
+        dirp->ents = g_slist_sort(dirp->ents, virDirEntsSorter);
+    }
+
+    if (dirp->sorted) {
+        if (dirp->pos < dirp->len)
+            *ent = g_slist_nth_data(dirp->ents, dirp->pos++);
+        else
+            *ent = NULL;
+    } else {
+        return virDirReadImpl(dirp->dir, ent, name);
+    }
+
     return !!*ent;
 }
 
@@ -3007,7 +3089,11 @@ void virDirClose(virDir *dirp)
     if (!dirp || !dirp->dir)
         return;
 
+    if (dirp->sorted)
+        g_slist_free_full(dirp->ents, free);
+
     closedir(dirp->dir); /* exempt from syntax-check */
+    VIR_FREE(dirp);
 }
 
 
diff --git a/src/util/virfile.h b/src/util/virfile.h
index d4b6b9a15e..cb746501fd 100644
--- a/src/util/virfile.h
+++ b/src/util/virfile.h
@@ -278,6 +278,8 @@ int virDirOpenIfExists(virDir **dirp, const char *dirname)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
 int virDirOpenQuiet(virDir **dirp, const char *dirname)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
+int virDirOpenSorted(virDir **dirp, const char *dirname)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
 int virDirRead(virDir *dirp, struct dirent **ent, const char *dirname)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
 void virDirClose(virDir *dirp);
-- 
2.38.2



More information about the libvir-list mailing list