From 9b4e28e1557c58fa625906a47bd57f42c53fd7dd Mon Sep 17 00:00:00 2001 From: Diego Fronza Date: Wed, 23 Oct 2019 16:25:06 -0300 Subject: [PATCH] Added a isc_glob() function that wraps glob() calls for POSIX systems and implement a custom glob() function on Windows systems. --- config.h.in | 3 + configure.ac | 2 + lib/isc/Makefile.in | 4 +- lib/isc/glob.c | 248 ++++++++++++++++++++++++++++++++ lib/isc/include/isc/glob.h | 43 ++++++ lib/isc/win32/libisc.def.in | 2 + lib/isc/win32/libisc.vcxproj.in | 2 + lib/isccfg/parser.c | 33 +---- 8 files changed, 306 insertions(+), 31 deletions(-) create mode 100644 lib/isc/glob.c create mode 100644 lib/isc/include/isc/glob.h diff --git a/config.h.in b/config.h.in index a280e6dafc..ffe84333b7 100644 --- a/config.h.in +++ b/config.h.in @@ -177,6 +177,9 @@ /* Define to 1 if you have the `getrandom' function. */ #undef HAVE_GETRANDOM +/* Define to 1 if you have the header file. */ +#undef HAVE_GLOB_H + /* Define to use gperftools CPU profiler. */ #undef HAVE_GPERFTOOLS_PROFILER diff --git a/configure.ac b/configure.ac index 5ecdcac429..6e9aa40759 100644 --- a/configure.ac +++ b/configure.ac @@ -2602,6 +2602,8 @@ AC_SUBST(DLZ_DRIVER_MYSQL_INCLUDES) AC_SUBST(DLZ_DRIVER_MYSQL_LIBS) AC_SUBST_FILE(DLZ_DRIVER_RULES) +AC_CHECK_HEADERS([glob.h]) + if test "yes" = "$cross_compiling"; then if test -z "$BUILD_CC"; then AC_MSG_ERROR([BUILD_CC not set]) diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in index a8986c5a45..7876c511e2 100644 --- a/lib/isc/Makefile.in +++ b/lib/isc/Makefile.in @@ -50,7 +50,7 @@ OBJS = pk11.@O@ pk11_result.@O@ \ backtrace.@O@ base32.@O@ base64.@O@ \ bind9.@O@ buffer.@O@ bufferlist.@O@ \ commandline.@O@ counter.@O@ crc64.@O@ error.@O@ entropy.@O@ \ - event.@O@ hash.@O@ ht.@O@ heap.@O@ hex.@O@ \ + event.@O@ glob.@O@ hash.@O@ ht.@O@ heap.@O@ hex.@O@ \ hmac.@O@ hp.@O@ httpd.@O@ iterated_hash.@O@ \ lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \ md.@O@ mem.@O@ mutexblock.@O@ \ @@ -71,7 +71,7 @@ SRCS = pk11.c pk11_result.c \ aes.c app.c assertions.c astack.c \ backtrace.c base32.c base64.c bind9.c \ buffer.c bufferlist.c commandline.c counter.c crc64.c \ - entropy.c error.c event.c hash.c ht.c heap.c \ + entropy.c error.c event.c glob.c hash.c ht.c heap.c \ hex.c hmac.c hp.c httpd.c iterated_hash.c \ lex.c lfsr.c lib.c log.c \ md.c mem.c mutexblock.c \ diff --git a/lib/isc/glob.c b/lib/isc/glob.c new file mode 100644 index 0000000000..34cb7db712 --- /dev/null +++ b/lib/isc/glob.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if HAVE_GLOB_H +#include +#elif defined(_WIN32) || defined(_WIN64) +#include +#define WIN32_LEAN_AND_MEAN 1 +#include +#include +#define GLOB_WIN_IMPL 1 +#define GLOB_ERR 0x0004 /* Return on error. */ +#define GLOB_NOSPACE (-1) +#define GLOB_NOMATCH (-3) + +/* custom glob implementation for windows */ +static int +glob(const char *pattern, int flags, void *unused, glob_t *pglob); + +static void +globfree(glob_t *pglob); + +#else +#error "Required header missing: glob.h" +#endif + +isc_result_t +isc_glob(const char *pattern, glob_t *pglob) { + REQUIRE(pattern != NULL); + REQUIRE(*pattern != '\0'); + REQUIRE(pglob != NULL); + + int rc = glob(pattern, GLOB_ERR, NULL, pglob); + + switch (rc) { + case 0: + return (ISC_R_SUCCESS); + + case GLOB_NOMATCH: + return (ISC_R_FILENOTFOUND); + + case GLOB_NOSPACE: + return (ISC_R_NOMEMORY); + + default: + return (errno != 0 ? isc_errno_toresult(errno) : ISC_R_IOERROR); + } + +} + +void +isc_globfree(glob_t *pglob) { + REQUIRE(pglob != NULL); + globfree(pglob); +} + +#ifdef GLOB_WIN_IMPL + +typedef struct file_path file_path_t; + +struct file_path { + char *path; + ISC_LINK(file_path_t) link; +}; + +typedef ISC_LIST(file_path_t) file_list_t; + +/* map a winapi error to a convenient errno code */ +static int +map_error(DWORD win_err_code) { + switch (win_err_code) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + return (GLOB_NOMATCH); + case ERROR_ACCESS_DENIED: + return (EACCES); + case ERROR_NOT_ENOUGH_MEMORY: + return (GLOB_NOSPACE); + default: + return (EIO); + } +} + +/* add file in directory dir, that matches glob expression + * provided in function glob(), to the linked list fl */ +static int +append_file(isc_mem_t *mctx, + file_list_t *fl, + const char *dir, + const char *file, + size_t full_path_len) +{ + file_path_t *fp = isc_mem_get(mctx, sizeof(file_path_t)); + fp->path = isc_mem_get(mctx, full_path_len + 1); + _snprintf(fp->path, full_path_len + 1, "%s%s", dir, file); + + ISC_LINK_INIT(fp, link); + ISC_LIST_PREPEND(*fl, fp, link); +} + +/* sort files alphabetically case insensitive on windows */ +static int +path_cmp(const void *path1, const void *path2) { + return _stricmp((const char *)path1, (const char *)path2); +} + +static int +glob(const char *pattern, int flags, void *unused, glob_t *pglob) { + char path[MAX_PATH]; + WIN32_FIND_DATAA find_data;; + HANDLE hnd; + + REQUIRE(pattern != NULL); + REQUIRE(pglob != NULL); + + UNUSED(flags); + UNUSED(unused); + + pglob->mctx = NULL; + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + + hnd = FindFirstFileA(pattern, &find_data); + if (hnd == INVALID_HANDLE_VALUE) { + return (map_error(GetLastError())); + } + + path[MAX_PATH - 1] = 0; + strncpy(path, pattern, MAX_PATH); + if (path[MAX_PATH - 1] != 0) { + errno = ENAMETOOLONG; + goto fail; + } + + // strip filename from path. + size_t dir_len = strlen(path); + while (dir_len > 0 && path[dir_len - 1] != '/' && path[dir_len - 1] != '\\') { + dir_len--; + } + + path[dir_len] = '\0'; + + isc_mem_create(&pglob->mctx); + pglob->reserved = isc_mem_get(pglob->mctx, sizeof(file_list_t)); + + size_t entries = 0; + + do { + size_t file_len = strlen(find_data.cFileName); + size_t full_path_len = dir_len + file_len; + + if (full_path_len > MAX_PATH) { + errno = ENAMETOOLONG; + goto fail; + } + + append_file(pglob->mctx, + (file_list_t *)pglob->reserved, + path, + find_data.cFileName, + full_path_len); + + entries++; + } while (FindNextFileA(hnd, &find_data)); + + FindClose(hnd); + + pglob->gl_pathv = isc_mem_get(pglob->mctx, (entries + 1) * sizeof(char*)); + pglob->gl_pathv[entries] = NULL; + pglob->gl_pathc = entries; + + file_list_t *fl = (file_list_t *)pglob->reserved; + + size_t e = 0; + file_path_t *fp; + for (fp = ISC_LIST_HEAD(*fl); fp != NULL; fp = ISC_LIST_NEXT(fp, link)) { + pglob->gl_pathv[e++] = fp->path; + } + + qsort(pglob->gl_pathv, pglob->gl_pathc, sizeof(char*), path_cmp); + + return (0); + +fail: + int ec = errno; + + FindClose(hnd); + + if (pglob->mctx) { + globfree(pglob); + } + + return ec; +} + +void +globfree(glob_t* pglob) { + REQUIRE(pglob != NULL); + REQUIRE(pglob->mctx != NULL); + + /* first free memory used by char ** gl_pathv */ + if (pglob->gl_pathv) { + isc_mem_put(pglob->mctx, + pglob->gl_pathv, + (pglob->gl_pathc + 1) * sizeof(char *)); + pglob->gl_pathv = NULL; + } + + file_list_t *fl = (file_list_t *)pglob->reserved; + file_path_t *p, *next; + + /* next free each individual file path string + nodes in list */ + for (p = ISC_LIST_HEAD(*fl); p != NULL; p = next) { + next = ISC_LIST_NEXT(p, link); + isc_mem_put(pglob->mctx, p->path, strlen(p->path) + 1); + isc_mem_put(pglob->mctx, p, sizeof(file_path_t)); + } + + /* free linked list of files */ + isc_mem_put(pglob->mctx, pglob->reserved, sizeof(file_list_t)); + pglob->reserved = NULL; + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + + isc_mem_destroy(&pglob->mctx); + pglob->mctx = NULL; +} + +#endif /* GLOB_WIN_IMPL */ diff --git a/lib/isc/include/isc/glob.h b/lib/isc/include/isc/glob.h new file mode 100644 index 0000000000..314dd5a657 --- /dev/null +++ b/lib/isc/include/isc/glob.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_GLOB_H +#define ISC_GLOB_H + +#include +#include + +#if HAVE_GLOB_H +#include +#else +#include +#include + +typedef struct { + size_t gl_pathc; + char **gl_pathv; + isc_mem_t *mctx; + void *reserved; +} glob_t; + +#endif + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_glob(const char *pattern, glob_t *pglob); + +void +isc_globfree(glob_t *pglob); + +ISC_LANG_ENDDECLS + +#endif /* ISC_GLOB_H */ diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index fcf8ad779f..2b590f8a02 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -232,6 +232,8 @@ isc_fsaccess_add isc_fsaccess_changeowner isc_fsaccess_remove isc_fsaccess_set +isc_glob +isc_globfree isc_hash_function isc_hash_get_initializer isc_hash_set_initializer diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in index 9fd2d149fe..c45883a5f3 100644 --- a/lib/isc/win32/libisc.vcxproj.in +++ b/lib/isc/win32/libisc.vcxproj.in @@ -314,6 +314,7 @@ copy InstallFiles ..\Build\Release\ + @@ -426,6 +427,7 @@ copy InstallFiles ..\Build\Release\ + diff --git a/lib/isccfg/parser.c b/lib/isccfg/parser.c index f538ce7abd..26fc2a5dd9 100644 --- a/lib/isccfg/parser.c +++ b/lib/isccfg/parser.c @@ -17,12 +17,12 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -110,9 +110,6 @@ static void parser_complain(cfg_parser_t *pctx, bool is_warning, unsigned int flags, const char *format, va_list args); -static isc_result_t -glob_include(const char * restrict pattern, glob_t * restrict pglob); - #if defined(HAVE_GEOIP2) static isc_result_t parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); @@ -2398,13 +2395,15 @@ cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { /* Allow include to specify a pattern that follows * the same rules as the shell e.g "/path/zone*.conf" */ glob_t glob_obj; - CHECK(glob_include(includename->value.string.base, &glob_obj)); + CHECK(isc_glob(includename->value.string.base, &glob_obj)); cfg_obj_destroy(pctx, &includename); for (size_t i = 0; i < glob_obj.gl_pathc; ++i) { CHECK(parser_openfile(pctx, glob_obj.gl_pathv[i])); } + isc_globfree(&glob_obj); + goto redo; } @@ -4095,27 +4094,3 @@ cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list, return (result); } - - -static isc_result_t -glob_include(const char * restrict pattern, glob_t * restrict pglob) -{ - int rc = glob(pattern, GLOB_ERR, NULL, pglob); - - switch (rc) { - case 0: - return (ISC_R_SUCCESS); - case GLOB_NOMATCH: - /* if a magic char (*?[]) was in pattern - * and no path matched we report error early, - * otherwise proceed as normal */ - return (strpbrk(pattern, "[]*?")) ? - (ISC_R_FILENOTFOUND) : (ISC_R_SUCCESS); - - case GLOB_NOSPACE: - return (ISC_R_NOMEMORY); - default: - return (errno != 0 ? isc_errno_toresult(errno) : ISC_R_IOERROR); - } - -}