From 3de65f8d67057849739de81331b0657aec20d1f5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 28 Jan 2021 15:10:52 +0100 Subject: [PATCH] enumerator: Implement globbing enumerator on Windows 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 | 141 ++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/collections/enumerator.c b/src/libstrongswan/collections/enumerator.c index 3833d553d..a663d5229 100644 --- a/src/libstrongswan/collections/enumerator.c +++ b/src/libstrongswan/collections/enumerator.c @@ -27,6 +27,8 @@ #ifdef HAVE_GLOB_H #include +#elif defined(WIN32) +#include #endif /* HAVE_GLOB_H */ #include @@ -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)