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(a)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