From d52e14c4f77216bd1ae5eec7195e26554f1952e3 Mon Sep 17 00:00:00 2001 From: zshuang0316 Date: Thu, 5 Feb 2026 11:12:44 +0800 Subject: [PATCH 1/3] win32: add reusable glob utility for Windows Extract glob implementation into src/win32/flb_glob.c as a shared utility that can be used across different components on Windows. Signed-off-by: zshuang0316 --- include/fluent-bit/flb_glob_win32.h | 95 +++++ src/CMakeLists.txt | 1 + src/win32/flb_glob.c | 549 ++++++++++++++++++++++++++++ 3 files changed, 645 insertions(+) create mode 100644 include/fluent-bit/flb_glob_win32.h create mode 100644 src/win32/flb_glob.c diff --git a/include/fluent-bit/flb_glob_win32.h b/include/fluent-bit/flb_glob_win32.h new file mode 100644 index 00000000000..692755a660b --- /dev/null +++ b/include/fluent-bit/flb_glob_win32.h @@ -0,0 +1,95 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2024 The Fluent Bit Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLB_GLOB_WIN32_H +#define FLB_GLOB_WIN32_H + +#include +#include +#include + +#ifdef FLB_SYSTEM_WINDOWS + +#include + +#define FLB_FILE_GLOB_ABORT_ON_ERROR (((uint64_t) 1) << 0) +#define FLB_FILE_GLOB_MARK_DIRECTORIES (((uint64_t) 1) << 1) +#define FLB_FILE_GLOB_DO_NOT_SORT (((uint64_t) 1) << 2) +#define FLB_FILE_GLOB_EXPAND_TILDE (((uint64_t) 1) << 3) + +#define FLB_FILE_GLOB_ERROR_SUCCESS 0 +#define FLB_FILE_GLOB_ERROR_ABORTED 1 +#define FLB_FILE_GLOB_ERROR_NO_MEMORY 2 +#define FLB_FILE_GLOB_ERROR_NO_FILE 3 +#define FLB_FILE_GLOB_ERROR_NO_ACCESS 4 +#define FLB_FILE_GLOB_ERROR_NO_MATCHES 5 +#define FLB_FILE_GLOB_ERROR_NO_MORE_RESULTS 6 +#define FLB_FILE_GLOB_ERROR_OVERSIZED_PATH 7 +#define FLB_FILE_GLOB_ERROR_INVALID_ARGUMENT 8 + +#ifndef GLOB_NOSPACE +#define GLOB_NOSPACE FLB_FILE_GLOB_ERROR_NO_MEMORY +#endif + +#ifndef GLOB_ABORTED +#define GLOB_ABORTED FLB_FILE_GLOB_ERROR_ABORTED +#endif + +#ifndef GLOB_NOMATCH +#define GLOB_NOMATCH FLB_FILE_GLOB_ERROR_NO_MATCHES +#endif + +#ifndef GLOB_ERR +#define GLOB_ERR FLB_FILE_GLOB_ABORT_ON_ERROR +#endif + +#define FLB_FILE_MAX_PATH_LENGTH PATH_MAX + +struct flb_file_glob_inner_entry { + char *path; + struct cfl_list _head; +}; + +struct flb_file_glob_inner_context { + struct flb_file_glob_inner_entry *current_entry; + struct cfl_list results; + size_t entries; + size_t index; + uint64_t flags; +}; + +struct flb_file_glob_context { + struct flb_file_glob_inner_context *inner_context; + uint64_t flags; + char *path; +}; + +typedef struct { + struct flb_file_glob_context inner_context; + char **gl_pathv; + size_t gl_pathc; +} glob_t; + +int glob(const char *path, uint64_t flags, void *unused, glob_t *context); +void globfree(glob_t *context); +int is_directory(char *path, struct stat *fs_entry_metadata); + + +#endif /* FLB_SYSTEM_WINDOWS */ +#endif /* FLB_GLOB_WIN32_H */ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ba8440ce0c..77fb11f69f1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -131,6 +131,7 @@ if(FLB_SYSTEM_WINDOWS) set(src ${src} flb_dlfcn_win32.c + win32/flb_glob.c ) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W2") endif() diff --git a/src/win32/flb_glob.c b/src/win32/flb_glob.c new file mode 100644 index 00000000000..8bb0582158c --- /dev/null +++ b/src/win32/flb_glob.c @@ -0,0 +1,549 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2019-2021 The Fluent Bit Authors + * Copyright (C) 2015-2018 Treasure Data Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(FLB_SYSTEM_WINDOWS) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define FLB_FILE_ISTYPE(m, t) (((m) & 0170000) == t) + +static int flb_file_glob_start(struct flb_file_glob_context *context, + const char *path, + uint64_t flags); + +static void flb_file_glob_clean(struct flb_file_glob_context *context); + +static int flb_file_glob_fetch(struct flb_file_glob_context *context, + char **result); + +void globfree(glob_t *context) +{ + size_t index; + + if (context->gl_pathv != NULL) { + flb_free(context->gl_pathv); + context->gl_pathv = NULL; + } + + flb_file_glob_clean(&context->inner_context); +} + +int glob(const char *path, + uint64_t flags, + void *unused, + glob_t *context) +{ + size_t entries; + int result; + size_t index; + + (void) unused; + + result = flb_file_glob_start(&context->inner_context, path, flags); + + if (result == FLB_FILE_GLOB_ERROR_SUCCESS) { + entries = cfl_list_size(&context->inner_context.inner_context->results); + + context->gl_pathv = flb_calloc(entries, sizeof(char *)); + + if (context->gl_pathv == NULL) { + globfree(context); + + return FLB_FILE_GLOB_ERROR_NO_MEMORY; + } + + for (index = 0 ; index < entries ; index++) { + result = flb_file_glob_fetch(&context->inner_context, + &context->gl_pathv[index]); + + if (result != FLB_FILE_GLOB_ERROR_SUCCESS) { + globfree(context); + + return result; + } + } + context->gl_pathc = entries; + } + + return result; +} + +int is_directory(char *path, struct stat *fs_entry_metadata) +{ + return ((fs_entry_metadata->st_mode & S_IFDIR) != 0); +} + +static void reset_errno() +{ + errno = 0; +} + +static void propagate_last_error_to_errno() +{ + DWORD error_code; + + error_code = GetLastError(); + + switch (error_code) { + case ERROR_INVALID_TARGET_HANDLE: + case ERROR_INVALID_HANDLE: + errno = EBADF; + break; + + case ERROR_TOO_MANY_OPEN_FILES: + errno = EMFILE; + break; + + case ERROR_INVALID_FLAG_NUMBER: + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + errno = ENOMEM; + break; + + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + case ERROR_PATH_BUSY: + case ERROR_BUSY: + errno = EBUSY; + break; + + case ERROR_HANDLE_DISK_FULL: + case ERROR_DISK_FULL: + errno = ENOSPC; + break; + + case ERROR_INVALID_ADDRESS: + errno = EFAULT; + break; + + case ERROR_FILE_TOO_LARGE: + errno = EFBIG; + break; + + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + errno = EEXIST; + break; + + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: + case ERROR_BAD_PATHNAME: + case ERROR_INVALID_NAME: + case ERROR_BAD_UNIT: + errno = ENOENT; + break; + + case ERROR_SEEK_ON_DEVICE: + errno = ESPIPE; + break; + + case ERROR_NEGATIVE_SEEK: + errno = EINVAL; + break; + + case ERROR_ACCESS_DENIED: + errno = EACCES; + break; + + case ERROR_DIR_NOT_EMPTY: + errno = ENOTEMPTY; + break; + + case ERROR_BROKEN_PIPE: + errno = EPIPE; + break; + + case ERROR_GEN_FAILURE: + errno = EIO; + break; + + case ERROR_OPEN_FAILED: + errno = EIO; + break; + + case ERROR_SUCCESS: + errno = 0; + break; + + default: + /* This is just a canary, if you find this + * error then it means we need to expand the + * translation list. + */ + + errno = EOWNERDEAD; + break; + } +} + +static int limited_win32_glob_append_entry( + struct flb_file_glob_inner_context *context, + char *path, + uint16_t mode_filter) +{ + char entry_path_buffer[FLB_FILE_MAX_PATH_LENGTH]; + char *entry_path; + struct stat entry_info; + int result; + struct flb_file_glob_inner_entry *entry; + + result = stat(path, &entry_info); + + if (result != 0) { + result = FLB_FILE_GLOB_ERROR_NO_FILE; + } + else { + result = FLB_FILE_GLOB_ERROR_SUCCESS; + + if (mode_filter != 0) { + if (!FLB_FILE_ISTYPE(entry_info.st_mode, mode_filter)) { + result = FLB_FILE_GLOB_ERROR_NO_MATCHES; + } + } + } + + if (result == FLB_FILE_GLOB_ERROR_SUCCESS) { + entry_path = _fullpath(entry_path_buffer, + path, + FLB_FILE_MAX_PATH_LENGTH); + + if (entry_path == NULL) { + result = FLB_FILE_GLOB_ERROR_OVERSIZED_PATH; + } + } + + if (result == FLB_FILE_GLOB_ERROR_SUCCESS) { + entry = flb_calloc(1, sizeof(struct flb_file_glob_inner_entry)); + + if (entry == NULL) { + return FLB_FILE_GLOB_ERROR_NO_MEMORY; + } + + entry->path = flb_strdup(entry_path); + + if (entry->path == NULL) { + flb_free(entry); + + return FLB_FILE_GLOB_ERROR_NO_MEMORY; + } + + cfl_list_append(&entry->_head, &context->results); + + context->entries++; + } + + return result; +} + +/* + * Perform patern match on the given path string. This function + * supports patterns with "nested" wildcards like below. + * + * tail_scan_pattern("C:\fluent-bit\*\*.txt", ctx); + * + * On success, the number of files found is returned (zero indicates + * "no file found"). On error, -1 is returned. + */ +static int limited_win32_glob(struct flb_file_glob_inner_context *context, + char *path) +{ + char *star, *p0, *p1; + char pattern[FLB_FILE_MAX_PATH_LENGTH]; + char buf[FLB_FILE_MAX_PATH_LENGTH]; + int ret; + int n_added = 0; + time_t now; + int64_t mtime; + HANDLE h; + WIN32_FIND_DATA data; + struct flb_file_glob_inner_entry *entry; + int transverse_directory; + struct stat entry_info; + + if (strlen(path) >= FLB_FILE_MAX_PATH_LENGTH) { + return FLB_FILE_GLOB_ERROR_OVERSIZED_PATH; + } + + star = strchr(path, '*'); + + if (star == NULL) { + return limited_win32_glob_append_entry(context, path, 0); + } + + /* + * C:\data\tmp\input_*.conf + * 0<-----| + */ + p0 = star; + while (path <= p0 && *p0 != '\\') { + p0--; + } + + /* + * C:\data\tmp\input_*.conf + * |---->1 + */ + p1 = star; + while (*p1 && *p1 != '\\') { + p1++; + } + + memcpy(pattern, path, (p1 - path)); + pattern[p1 - path] = '\0'; + + h = FindFirstFileA(pattern, &data); + + if (h == INVALID_HANDLE_VALUE) { + return FLB_FILE_GLOB_ERROR_NO_MATCHES; + } + + ret = FLB_FILE_GLOB_ERROR_SUCCESS; + + do { + /* Ignore the current and parent dirs */ + if (!strcmp(".", data.cFileName) || + !strcmp("..", data.cFileName)) { + continue; + } + + /* Avoid an infinite loop */ + if (strchr(data.cFileName, '*')) { + continue; + } + + /* Create a path (prefix + filename + suffix) */ + memcpy(buf, path, p0 - path + 1); + buf[p0 - path + 1] = '\0'; + + if ((strlen(buf) + + strlen(data.cFileName) + + strlen(p1)) >= FLB_FILE_MAX_PATH_LENGTH) { + if (context->flags & + FLB_FILE_GLOB_ABORT_ON_ERROR) { + ret = FLB_FILE_GLOB_ERROR_OVERSIZED_PATH; + + break; + } + else { + continue; + } + } + + strcat(buf, data.cFileName); + + if (strchr(p1, '*')) { + transverse_directory = FLB_FALSE; + + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + transverse_directory = FLB_TRUE; + } + else if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + ret = stat(data.cFileName, &entry_info); + + if (ret != 0) { + if (context->flags & + FLB_FILE_GLOB_ABORT_ON_ERROR) { + ret = FLB_FILE_GLOB_ERROR_NO_FILE; + + break; + } + } + } + + if (transverse_directory) { + strcat(buf, p1); + + ret = limited_win32_glob(context, buf); + + if (ret != FLB_FILE_GLOB_ERROR_SUCCESS && + ret != FLB_FILE_GLOB_ERROR_NO_FILE && + ret != FLB_FILE_GLOB_ERROR_NO_MATCHES) { + if (context->flags & + FLB_FILE_GLOB_ABORT_ON_ERROR) { + break; + } + } + + continue; + } + } + + strcat(buf, p1); + + ret = limited_win32_glob_append_entry(context, buf, 0); + + if (ret != FLB_FILE_GLOB_ERROR_SUCCESS && + ret != FLB_FILE_GLOB_ERROR_NO_FILE) { + if (context->flags & + FLB_FILE_GLOB_ABORT_ON_ERROR) { + break; + } + } + + ret = FLB_FILE_GLOB_ERROR_SUCCESS; + } while (FindNextFileA(h, &data) != 0); + + FindClose(h); + + if (!(context->flags & + FLB_FILE_GLOB_ABORT_ON_ERROR)) { + ret = FLB_FILE_GLOB_ERROR_SUCCESS; + } + + return ret; +} + +static int flb_file_glob_start(struct flb_file_glob_context *context, + const char *path, + uint64_t flags) +{ + + int tilde_expansion_attempted; + struct stat path_stat; + int result; + + if (context == NULL) { + return -1; + } + + memset(context, 0, sizeof(struct flb_file_glob_context)); + + context->inner_context = + flb_calloc(1, sizeof(struct flb_file_glob_inner_context)); + + if (context->inner_context == NULL) { + return -2; + } + + cfl_list_init(&context->inner_context->results); + + context->inner_context->flags = 0; + context->flags = flags; + + if (flags & FLB_FILE_GLOB_ABORT_ON_ERROR) { + context->inner_context->flags |= FLB_FILE_GLOB_ABORT_ON_ERROR; + } + + context->path = flb_strdup(path); + + if (context->path == NULL) { + flb_file_glob_clean(context); + + return -3; + } + + return limited_win32_glob(context->inner_context, + context->path); +} + +static void flb_file_glob_clean(struct flb_file_glob_context *context) +{ + struct cfl_list *iterator_backup; + struct cfl_list *iterator; + struct flb_file_glob_inner_entry *entry; + + if (context != NULL) { + if (context->path != NULL) { + flb_free(context->path); + } + + if (context->inner_context != NULL) { + cfl_list_foreach_safe(iterator, + iterator_backup, + &context->inner_context->results) { + entry = cfl_list_entry(iterator, + struct flb_file_glob_inner_entry, + _head); + + if (entry->path != NULL) { + flb_free(entry->path); + } + + cfl_list_del(&entry->_head); + + flb_free(entry); + } + + flb_free(context->inner_context); + } + + memset(context, 0, sizeof(struct flb_file_glob_context)); + } + +} + +static int flb_file_glob_fetch(struct flb_file_glob_context *context, + char **result) +{ + + if (context == NULL) { + return FLB_FILE_GLOB_ERROR_NO_MEMORY; + } + + if (result == NULL) { + return FLB_FILE_GLOB_ERROR_NO_MEMORY; + } + + *result = NULL; + + if (context->inner_context->index >= + context->inner_context->entries) { + return FLB_FILE_GLOB_ERROR_NO_MORE_RESULTS; + } + + if (context->inner_context->current_entry == NULL) { + context->inner_context->current_entry = + cfl_list_entry_first(&context->inner_context->results, + struct flb_file_glob_inner_entry, + _head); + } + else { + context->inner_context->current_entry = + cfl_list_entry_next(&context->inner_context->current_entry->_head, + struct flb_file_glob_inner_entry, + _head, + &context->inner_context->results); + } + + *result = context->inner_context->current_entry->path; + + context->inner_context->index++; + + return FLB_FILE_GLOB_ERROR_SUCCESS; +} + +#endif \ No newline at end of file From e6db43dc2fcc846c0e090c27d5b6a1d716d9c365 Mon Sep 17 00:00:00 2001 From: zshuang0316 Date: Thu, 5 Feb 2026 11:13:03 +0800 Subject: [PATCH 2/3] in_blob: use shared win32 glob utility Replace in_blob's local glob implementation with the shared win32 glob utility. Signed-off-by: zshuang0316 --- plugins/in_blob/blob.c | 4 +- plugins/in_blob/win32_glob.c | 607 ----------------------------------- 2 files changed, 3 insertions(+), 608 deletions(-) delete mode 100644 plugins/in_blob/win32_glob.c diff --git a/plugins/in_blob/blob.c b/plugins/in_blob/blob.c index 5281386e4fe..a479c026dc2 100644 --- a/plugins/in_blob/blob.c +++ b/plugins/in_blob/blob.c @@ -43,7 +43,9 @@ #include "blob_db.h" #include "blob_file.h" -#include "win32_glob.c" +#ifdef FLB_SYSTEM_WINDOWS +#include +#endif /* Define missing GLOB_TILDE if not exists */ #ifndef GLOB_TILDE diff --git a/plugins/in_blob/win32_glob.c b/plugins/in_blob/win32_glob.c deleted file mode 100644 index 9bba4ca1e1c..00000000000 --- a/plugins/in_blob/win32_glob.c +++ /dev/null @@ -1,607 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* Fluent Bit - * ========== - * Copyright (C) 2019-2021 The Fluent Bit Authors - * Copyright (C) 2015-2018 Treasure Data Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if defined(FLB_SYSTEM_WINDOWS) && !defined(FLB_FILE_GLOB_ERROR_SUCCESS) - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define FLB_FILE_GLOB_ABORT_ON_ERROR (((uint64_t) 1) << 0) -#define FLB_FILE_GLOB_MARK_DIRECTORIES (((uint64_t) 1) << 1) -#define FLB_FILE_GLOB_DO_NOT_SORT (((uint64_t) 1) << 2) -#define FLB_FILE_GLOB_EXPAND_TILDE (((uint64_t) 1) << 3) - -#define FLB_FILE_GLOB_ERROR_SUCCESS 0 -#define FLB_FILE_GLOB_ERROR_ABORTED 1 -#define FLB_FILE_GLOB_ERROR_NO_MEMORY 2 -#define FLB_FILE_GLOB_ERROR_NO_FILE 3 -#define FLB_FILE_GLOB_ERROR_NO_ACCESS 4 -#define FLB_FILE_GLOB_ERROR_NO_MATCHES 5 -#define FLB_FILE_GLOB_ERROR_NO_MORE_RESULTS 6 -#define FLB_FILE_GLOB_ERROR_OVERSIZED_PATH 7 -#define FLB_FILE_GLOB_ERROR_INVALID_ARGUMENT 8 - -#ifndef GLOB_NOSPACE -#define GLOB_NOSPACE FLB_FILE_GLOB_ERROR_NO_MEMORY -#endif - -#ifndef GLOB_ABORTED -#define GLOB_ABORTED FLB_FILE_GLOB_ERROR_ABORTED -#endif - -#ifndef GLOB_NOMATCH -#define GLOB_NOMATCH FLB_FILE_GLOB_ERROR_NO_MATCHES -#endif - -#ifndef GLOB_ERR -#define GLOB_ERR FLB_FILE_GLOB_ABORT_ON_ERROR -#endif - -#define FLB_FILE_MAX_PATH_LENGTH PATH_MAX - -#define FLB_FILE_ISTYPE(m, t) (((m) & 0170000) == t) - -struct flb_file_glob_inner_entry { - char *path; - struct cfl_list _head; -}; - -struct flb_file_glob_inner_context { - struct flb_file_glob_inner_entry *current_entry; - struct cfl_list results; - size_t entries; - size_t index; - uint64_t flags; -}; - -struct flb_file_glob_context { - struct flb_file_glob_inner_context *inner_context; - uint64_t flags; - char *path; -}; - -struct glob_t { - struct flb_file_glob_context inner_context; - char **gl_pathv; - size_t gl_pathc; -}; - -typedef struct glob_t glob_t; - -static int flb_file_glob_start(struct flb_file_glob_context *context, - const char *path, - uint64_t flags); - -static void flb_file_glob_clean(struct flb_file_glob_context *context); - -static int flb_file_glob_fetch(struct flb_file_glob_context *context, - char **result); - -static void globfree(glob_t *context) -{ - size_t index; - - if (context->gl_pathv != NULL) { - flb_free(context->gl_pathv); - context->gl_pathv = NULL; - } - - flb_file_glob_clean(&context->inner_context); -} - -static int glob(const char *path, - uint64_t flags, - void *unused, - glob_t *context) -{ - size_t entries; - int result; - size_t index; - - (void) unused; - - result = flb_file_glob_start(context, path, flags); - - if (result == FLB_FILE_GLOB_ERROR_SUCCESS) { - entries = cfl_list_size(&context->inner_context.inner_context->results); - - context->gl_pathv = flb_calloc(entries, sizeof(char *)); - - if (context->gl_pathv == NULL) { - globfree(context); - - return FLB_FILE_GLOB_ERROR_NO_MEMORY; - } - - for (index = 0 ; index < entries ; index++) { - result = flb_file_glob_fetch(&context->inner_context, - &context->gl_pathv[index]); - - if (result != FLB_FILE_GLOB_ERROR_SUCCESS) { - globfree(context); - - return result; - } - } - } - - return result; -} - -static int is_directory(char *path, struct stat *fs_entry_metadata) -{ - return (fs_entry_metadata->st_mode & S_IFDIR != 0); -} - -static void reset_errno() -{ - errno = 0; -} - -static void propagate_last_error_to_errno() -{ - DWORD error_code; - - error_code = GetLastError(); - - switch (error_code) { - case ERROR_INVALID_TARGET_HANDLE: - case ERROR_INVALID_HANDLE: - errno = EBADF; - break; - - case ERROR_TOO_MANY_OPEN_FILES: - errno = EMFILE; - break; - - case ERROR_INVALID_FLAG_NUMBER: - case ERROR_INVALID_PARAMETER: - errno = EINVAL; - break; - - case ERROR_NOT_ENOUGH_MEMORY: - case ERROR_OUTOFMEMORY: - errno = ENOMEM; - break; - - case ERROR_SHARING_VIOLATION: - case ERROR_LOCK_VIOLATION: - case ERROR_PATH_BUSY: - case ERROR_BUSY: - errno = EBUSY; - break; - - case ERROR_HANDLE_DISK_FULL: - case ERROR_DISK_FULL: - errno = ENOSPC; - break; - - case ERROR_INVALID_ADDRESS: - errno = EFAULT; - break; - - case ERROR_FILE_TOO_LARGE: - errno = EFBIG; - break; - - case ERROR_ALREADY_EXISTS: - case ERROR_FILE_EXISTS: - errno = EEXIST; - break; - - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - case ERROR_INVALID_DRIVE: - case ERROR_BAD_PATHNAME: - case ERROR_INVALID_NAME: - case ERROR_BAD_UNIT: - errno = ENOENT; - break; - - case ERROR_SEEK_ON_DEVICE: - errno = ESPIPE; - break; - - case ERROR_NEGATIVE_SEEK: - errno = EINVAL; - break; - - case ERROR_ACCESS_DENIED: - errno = EACCES; - break; - - case ERROR_DIR_NOT_EMPTY: - errno = ENOTEMPTY; - break; - - case ERROR_BROKEN_PIPE: - errno = EPIPE; - break; - - case ERROR_GEN_FAILURE: - errno = EIO; - break; - - case ERROR_OPEN_FAILED: - errno = EIO; - break; - - case ERROR_SUCCESS: - errno = 0; - break; - - default: - /* This is just a canary, if you find this - * error then it means we need to expand the - * translation list. - */ - - errno = EOWNERDEAD; - break; - } -} - -static int limited_win32_glob_append_entry( - struct flb_file_glob_inner_context *context, - char *path, - uint16_t mode_filter) -{ - char entry_path_buffer[FLB_FILE_MAX_PATH_LENGTH]; - char *entry_path; - struct stat entry_info; - int result; - struct flb_file_glob_inner_entry *entry; - - result = stat(path, &entry_info); - - if (result != 0) { - result = FLB_FILE_GLOB_ERROR_NO_FILE; - } - else { - result = FLB_FILE_GLOB_ERROR_SUCCESS; - - if (mode_filter != 0) { - if (!FLB_FILE_ISTYPE(entry_info.st_mode, mode_filter)) { - result = FLB_FILE_GLOB_ERROR_NO_MATCHES; - } - } - } - - if (result == FLB_FILE_GLOB_ERROR_SUCCESS) { - entry_path = _fullpath(entry_path_buffer, - path, - FLB_FILE_MAX_PATH_LENGTH); - - if (entry_path == NULL) { - result = FLB_FILE_GLOB_ERROR_OVERSIZED_PATH; - } - } - - if (result == FLB_FILE_GLOB_ERROR_SUCCESS) { - entry = flb_calloc(1, sizeof(struct flb_file_glob_inner_entry)); - - if (entry == NULL) { - return FLB_FILE_GLOB_ERROR_NO_MEMORY; - } - - entry->path = flb_strdup(entry_path); - - if (entry->path == NULL) { - flb_free(entry); - - return FLB_FILE_GLOB_ERROR_NO_MEMORY; - } - - cfl_list_append(&entry->_head, &context->results); - - context->entries++; - } - - return result; -} - -/* - * Perform patern match on the given path string. This function - * supports patterns with "nested" wildcards like below. - * - * tail_scan_pattern("C:\fluent-bit\*\*.txt", ctx); - * - * On success, the number of files found is returned (zero indicates - * "no file found"). On error, -1 is returned. - */ -static int limited_win32_glob(struct flb_file_glob_inner_context *context, - char *path) -{ - char *star, *p0, *p1; - char pattern[FLB_FILE_MAX_PATH_LENGTH]; - char buf[FLB_FILE_MAX_PATH_LENGTH]; - int ret; - int n_added = 0; - time_t now; - int64_t mtime; - HANDLE h; - WIN32_FIND_DATA data; - struct flb_file_glob_inner_entry *entry; - int transverse_directory; - struct stat entry_info; - - if (strlen(path) >= FLB_FILE_MAX_PATH_LENGTH) { - return FLB_FILE_GLOB_ERROR_OVERSIZED_PATH; - } - - star = strchr(path, '*'); - - if (star == NULL) { - return limited_win32_glob_append_entry(context, path, 0); - } - - /* - * C:\data\tmp\input_*.conf - * 0<-----| - */ - p0 = star; - while (path <= p0 && *p0 != '\\') { - p0--; - } - - /* - * C:\data\tmp\input_*.conf - * |---->1 - */ - p1 = star; - while (*p1 && *p1 != '\\') { - p1++; - } - - memcpy(pattern, path, (p1 - path)); - pattern[p1 - path] = '\0'; - - h = FindFirstFileA(pattern, &data); - - if (h == INVALID_HANDLE_VALUE) { - return FLB_FILE_GLOB_ERROR_NO_MATCHES; - } - - ret = FLB_FILE_GLOB_ERROR_SUCCESS; - - do { - /* Ignore the current and parent dirs */ - if (!strcmp(".", data.cFileName) || - !strcmp("..", data.cFileName)) { - continue; - } - - /* Avoid an infinite loop */ - if (strchr(data.cFileName, '*')) { - continue; - } - - /* Create a path (prefix + filename + suffix) */ - memcpy(buf, path, p0 - path + 1); - buf[p0 - path + 1] = '\0'; - - if ((strlen(buf) + - strlen(data.cFileName) + - strlen(p1)) >= FLB_FILE_MAX_PATH_LENGTH) { - if (context->flags & - FLB_FILE_GLOB_ABORT_ON_ERROR) { - ret = FLB_FILE_GLOB_ERROR_OVERSIZED_PATH; - - break; - } - else { - continue; - } - } - - strcat(buf, data.cFileName); - - if (strchr(p1, '*')) { - transverse_directory = FLB_FALSE; - - if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - transverse_directory = FLB_TRUE; - } - else if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - ret = stat(data.cFileName, &entry_info); - - if (ret != 0) { - if (context->flags & - FLB_FILE_GLOB_ABORT_ON_ERROR) { - ret = FLB_FILE_GLOB_ERROR_NO_FILE; - - break; - } - } - } - - if (transverse_directory) { - strcat(buf, p1); - - ret = limited_win32_glob(context, buf); - - if (ret != FLB_FILE_GLOB_ERROR_SUCCESS && - ret != FLB_FILE_GLOB_ERROR_NO_FILE && - ret != FLB_FILE_GLOB_ERROR_NO_MATCHES) { - if (context->flags & - FLB_FILE_GLOB_ABORT_ON_ERROR) { - break; - } - } - - continue; - } - } - - strcat(buf, p1); - - ret = limited_win32_glob_append_entry(context, buf, 0); - - if (ret != FLB_FILE_GLOB_ERROR_SUCCESS && - ret != FLB_FILE_GLOB_ERROR_NO_FILE) { - if (context->flags & - FLB_FILE_GLOB_ABORT_ON_ERROR) { - break; - } - } - - ret = FLB_FILE_GLOB_ERROR_SUCCESS; - } while (FindNextFileA(h, &data) != 0); - - FindClose(h); - - if (!(context->flags & - FLB_FILE_GLOB_ABORT_ON_ERROR)) { - ret = FLB_FILE_GLOB_ERROR_SUCCESS; - } - - return ret; -} - -int flb_file_glob_start(struct flb_file_glob_context *context, - const char *path, - uint64_t flags) -{ - - int tilde_expansion_attempted; - struct stat path_stat; - int result; - - if (context == NULL) { - return -1; - } - - memset(context, 0, sizeof(struct flb_file_glob_context)); - - context->inner_context = - flb_calloc(1, sizeof(struct flb_file_glob_inner_context)); - - if (context->inner_context == NULL) { - return -2; - } - - cfl_list_init(&context->inner_context->results); - - context->inner_context->flags = 0; - context->flags = flags; - - if (flags & FLB_FILE_GLOB_ABORT_ON_ERROR) { - context->inner_context->flags |= FLB_FILE_GLOB_ABORT_ON_ERROR; - } - - context->path = flb_strdup(path); - - if (context->path == NULL) { - flb_file_glob_clean(context); - - return -3; - } - - return limited_win32_glob(context->inner_context, - context->path); -} - -void flb_file_glob_clean(struct flb_file_glob_context *context) -{ - struct cfl_list *iterator_backup; - struct cfl_list *iterator; - struct flb_file_glob_inner_entry *entry; - - if (context != NULL) { - if (context->path != NULL) { - flb_free(context->path); - } - - if (context->inner_context != NULL) { - cfl_list_foreach_safe(iterator, - iterator_backup, - &context->inner_context->results) { - entry = cfl_list_entry(iterator, - struct flb_file_glob_inner_entry, - _head); - - if (entry->path != NULL) { - flb_free(entry->path); - } - - cfl_list_del(&entry->_head); - - flb_free(entry); - } - - flb_free(context->inner_context); - } - - memset(context, 0, sizeof(struct flb_file_glob_context)); - } - -} - -int flb_file_glob_fetch(struct flb_file_glob_context *context, - char **result) -{ - - if (context == NULL) { - return FLB_FILE_GLOB_ERROR_NO_MEMORY; - } - - if (result == NULL) { - return FLB_FILE_GLOB_ERROR_NO_MEMORY; - } - - *result = NULL; - - if (context->inner_context->index >= - context->inner_context->entries) { - return FLB_FILE_GLOB_ERROR_NO_MORE_RESULTS; - } - - if (context->inner_context->current_entry == NULL) { - context->inner_context->current_entry = - cfl_list_entry_first(&context->inner_context->results, - struct flb_file_glob_inner_entry, - _head); - } - else { - context->inner_context->current_entry = - cfl_list_entry_next(&context->inner_context->current_entry->_head, - struct flb_file_glob_inner_entry, - _head, - &context->inner_context->results); - } - - *result = context->inner_context->current_entry->path; - - context->inner_context->index++; - - return FLB_FILE_GLOB_ERROR_SUCCESS; -} - -#endif \ No newline at end of file From f7bc00d31d8d07d4f85209329bcac4b85937a691 Mon Sep 17 00:00:00 2001 From: zshuang0316 Date: Thu, 5 Feb 2026 17:50:17 +0800 Subject: [PATCH 3/3] tests: add unit tests for win32 glob utility Signed-off-by: zshuang0316 --- tests/internal/CMakeLists.txt | 7 ++++ tests/internal/win32_glob.c | 74 +++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tests/internal/win32_glob.c diff --git a/tests/internal/CMakeLists.txt b/tests/internal/CMakeLists.txt index 6984f69d80f..0793a3f59c7 100644 --- a/tests/internal/CMakeLists.txt +++ b/tests/internal/CMakeLists.txt @@ -83,6 +83,13 @@ if(FLB_HAVE_LIBYAML) ) endif() +if (WIN32) + set(UNIT_TESTS_FILES + ${UNIT_TESTS_FILES} + win32_glob.c + ) +endif() + if (NOT WIN32) set(UNIT_TESTS_FILES ${UNIT_TESTS_FILES} diff --git a/tests/internal/win32_glob.c b/tests/internal/win32_glob.c new file mode 100644 index 00000000000..0beae798250 --- /dev/null +++ b/tests/internal/win32_glob.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include "flb_tests_internal.h" + +#ifdef FLB_SYSTEM_WINDOWS +#include + +void test_glob_basic() +{ + glob_t glob_data; + int ret; + FILE *fp; + + /* Create some dummy files */ + fp = fopen("test_glob_1.txt", "w"); + if (fp) fclose(fp); + fp = fopen("test_glob_2.txt", "w"); + if (fp) fclose(fp); + + ret = glob("test_glob_*.txt", 0, NULL, &glob_data); + TEST_CHECK(ret == 0); + TEST_CHECK(glob_data.gl_pathc == 2); + + globfree(&glob_data); + + /* Cleanup */ + unlink("test_glob_1.txt"); + unlink("test_glob_2.txt"); +} + +void test_glob_nomatch() +{ + glob_t glob_data = {0}; + int ret; + + ret = glob("non_existent_*.txt", 0, NULL, &glob_data); + TEST_CHECK(ret == GLOB_NOMATCH); + + globfree(&glob_data); +} + +void test_glob_wildcard() +{ + glob_t glob_data; + int ret; + FILE *fp; + + /* Create dummy file */ + fp = fopen("test_wildcard.txt", "w"); + if (fp) fclose(fp); + + ret = glob("test_wild*.txt", 0, NULL, &glob_data); + TEST_CHECK(ret == 0); + TEST_CHECK(glob_data.gl_pathc == 1); + if (glob_data.gl_pathc > 0) { + TEST_CHECK(strstr(glob_data.gl_pathv[0], "test_wildcard.txt") != NULL); + } + + globfree(&glob_data); + unlink("test_wildcard.txt"); +} + +TEST_LIST = { + { "basic", test_glob_basic }, + { "nomatch", test_glob_nomatch }, + { "wildcard", test_glob_wildcard }, + { 0 } +}; +#else +TEST_LIST = { + { 0 } +}; +#endif \ No newline at end of file