enumerator: Implement globbing enumerator on Windows
authorTobias Brunner <tobias@strongswan.org>
Thu, 28 Jan 2021 14:10:52 +0000 (15:10 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 3 Feb 2021 16:27:31 +0000 (17:27 +0100)
We don't have glob() available there.  This replacement should work
similarly for simple cases like `include conf.d/*.conf`.

Fixes #3684.

src/libstrongswan/collections/enumerator.c

index 3833d55..a663d52 100644 (file)
@@ -27,6 +27,8 @@
 
 #ifdef HAVE_GLOB_H
 #include <glob.h>
+#elif defined(WIN32)
+#include <fileapi.h>
 #endif /* HAVE_GLOB_H */
 
 #include <utils/debug.h>
@@ -201,8 +203,6 @@ typedef struct {
        glob_t glob;
        /** iteration count */
        u_int pos;
-       /** absolute path of current file */
-       char full[PATH_MAX];
 } glob_enum_t;
 
 METHOD(enumerator_t, destroy_glob_enum, void,
@@ -273,6 +273,143 @@ enumerator_t* enumerator_create_glob(const char *pattern)
        return &this->public;
 }
 
+#elif defined(WIN32) /* HAVE_GLOB_H */
+
+/**
+ * Enumerator implementation for glob enumerator on Windows
+ */
+typedef struct {
+       /** implements enumerator_t */
+       enumerator_t public;
+       /** search handle */
+       HANDLE handle;
+       /** current file path */
+       char path[PATH_MAX];
+       /** base path */
+       char *base;
+} glob_enum_t;
+
+METHOD(enumerator_t, destroy_glob_enum, void,
+       glob_enum_t *this)
+{
+       if (this->handle != INVALID_HANDLE_VALUE)
+       {
+               FindClose(this->handle);
+       }
+       free(this->base);
+       free(this);
+}
+
+/**
+ * Create the combined path from the given file data
+ */
+static bool combine_glob_path(glob_enum_t *this, WIN32_FIND_DATA *data)
+{
+       if (snprintf(this->path, sizeof(this->path), "%s%s%s", this->base,
+                                DIRECTORY_SEPARATOR, data->cFileName) >= sizeof(this->path))
+       {
+               DBG1(DBG_LIB, "path for '%s' too long, ignored", data->cFileName);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/**
+ * Return the path and stat data for the current file
+ */
+static bool enumerate_glob_enum_data(glob_enum_t *this, va_list args)
+{
+       struct stat *st;
+       char **file;
+
+       VA_ARGS_VGET(args, file, st);
+
+       if (file)
+       {
+               *file = this->path;
+       }
+       if (st && stat(this->path, st))
+       {
+               DBG1(DBG_LIB, "stat() on '%s' failed: %s", this->path,
+                        strerror(errno));
+               return FALSE;
+       }
+       return TRUE;
+}
+
+METHOD(enumerator_t, enumerate_glob_enum, bool,
+       glob_enum_t *this, va_list args)
+{
+       WIN32_FIND_DATA data;
+
+       do
+       {
+               if (!FindNextFile(this->handle, &data))
+               {
+                       return FALSE;
+               }
+       }
+       while (!combine_glob_path(this, &data));
+
+       return enumerate_glob_enum_data(this, args);
+}
+
+METHOD(enumerator_t, enumerate_glob_enum_first, bool,
+       glob_enum_t *this, va_list args)
+{
+       if (enumerate_glob_enum_data(this, args))
+       {
+               this->public.venumerate = _enumerate_glob_enum;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+/*
+ * Described in header
+ */
+enumerator_t *enumerator_create_glob(const char *pattern)
+{
+       glob_enum_t *this;
+       WIN32_FIND_DATA data;
+
+       if (!pattern)
+       {
+               return enumerator_create_empty();
+       }
+
+       INIT(this,
+               .public = {
+                       .enumerate = enumerator_enumerate_default,
+                       .venumerate = _enumerate_glob_enum_first,
+                       .destroy = _destroy_glob_enum,
+               },
+               .base = path_dirname(pattern),
+       );
+
+       this->handle = FindFirstFile(pattern, &data);
+       if (this->handle == INVALID_HANDLE_VALUE)
+       {
+               if (GetLastError() == ERROR_FILE_NOT_FOUND ||
+                       GetLastError() == ERROR_PATH_NOT_FOUND)
+               {
+                       DBG1(DBG_LIB, "no files found matching '%s'", pattern);
+               }
+               else
+               {
+                       DBG1(DBG_LIB, "FindFirstFile failed for pattern '%s' (%d)", pattern,
+                                GetLastError());
+               }
+               destroy_glob_enum(this);
+               return enumerator_create_empty();
+       }
+       else if (!combine_glob_path(this, &data))
+       {       /* check the next file if we can't combine the path for the first one */
+               this->public.venumerate = _enumerate_glob_enum;
+       }
+       return &this->public;
+}
+
 #else /* HAVE_GLOB_H */
 
 enumerator_t* enumerator_create_glob(const char *pattern)