diff options
Diffstat (limited to 'fluent-bit/plugins/in_tail/win32/stat.c')
-rw-r--r-- | fluent-bit/plugins/in_tail/win32/stat.c | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/fluent-bit/plugins/in_tail/win32/stat.c b/fluent-bit/plugins/in_tail/win32/stat.c new file mode 100644 index 00000000..bce80274 --- /dev/null +++ b/fluent-bit/plugins/in_tail/win32/stat.c @@ -0,0 +1,332 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Fluent Bit + * ========== + * Copyright (C) 2015-2022 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. + */ + +#include <Windows.h> +#include <stdlib.h> +#include <stdint.h> +#include <io.h> +#include "interface.h" + +/* + * NTFS stat(2) emulation tailored for in_tail's usage. + * + * (1) Support st_ino (inode) for Windows NTFS. + * (2) Support NTFS symlinks. + * (3) Support large files >= 2GB. + * + * To use it, include "win32.h" and it will transparently + * replace stat(), lstat() and fstat(). + */ + +#define UINT64(high, low) ((uint64_t) (high) << 32 | (low)) +#define WINDOWS_TICKS_TO_SECONDS_RATIO 10000000 +#define WINDOWS_EPOCH_TO_UNIX_EPOCH_DELTA 11644473600 + +/* + * FILETIME timestamps are represented in 100-nanosecond intervals, + * because of this, that's why we need to divide the number by 10000000 + * in order to convert it to seconds. + * + * While UNIX timestamps use January 1, 1970 as epoch Windows FILETIME + * timestamps use January 1, 1601. Because of this we need to subtract + * 11644473600 seconds to account for it. + * + * Note: Even though this does not account for leap seconds it should be + * accurate enough. + */ + +static uint64_t filetime_to_epoch(FILETIME *ft) +{ + ULARGE_INTEGER timestamp; + + if (ft == NULL) { + return 0; + } + + timestamp.HighPart = ft->dwHighDateTime; + timestamp.LowPart = ft->dwLowDateTime; + + timestamp.QuadPart /= WINDOWS_TICKS_TO_SECONDS_RATIO; + timestamp.QuadPart -= WINDOWS_EPOCH_TO_UNIX_EPOCH_DELTA; + + return timestamp.QuadPart; +} + +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: + case ERROR_NEGATIVE_SEEK: + errno = ESPIPE; + 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 get_mode(unsigned int attr) +{ + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + return WIN32_S_IFDIR; + } + return WIN32_S_IFREG; +} + + + +static int is_symlink(const char *path) +{ + WIN32_FIND_DATA data; + HANDLE h; + + SetLastError(0); + reset_errno(); + + h = FindFirstFileA(path, &data); + + if (h == INVALID_HANDLE_VALUE) { + propagate_last_error_to_errno(); + + return 0; + } + + FindClose(h); + + /* + * A NTFS symlink is a file with a bit of metadata ("reparse point"), + * So (1) check if the file has metadata and then (2) confirm that + * it is indeed a symlink. + */ + if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) { + return 1; + } + } + + return 0; +} + +static int hstat(HANDLE h, struct win32_stat *wst) +{ + BY_HANDLE_FILE_INFORMATION info; + FILE_STANDARD_INFO std; + + SetLastError(0); + reset_errno(); + + if (!GetFileInformationByHandle(h, &info)) { + propagate_last_error_to_errno(); + + return -1; + } + + if (!GetFileInformationByHandleEx(h, FileStandardInfo, + &std, sizeof(std))) { + propagate_last_error_to_errno(); + + return -1; + } + + wst->st_nlink = std.NumberOfLinks; + if (std.DeletePending) { + wst->st_nlink = 0; + } + + wst->st_mode = get_mode(info.dwFileAttributes); + wst->st_size = UINT64(info.nFileSizeHigh, info.nFileSizeLow); + wst->st_ino = UINT64(info.nFileIndexHigh, info.nFileIndexLow); + wst->st_mtime = filetime_to_epoch(&info.ftLastWriteTime); + + return 0; +} + +int win32_stat(const char *path, struct win32_stat *wst) +{ + HANDLE h; + + SetLastError(0); + reset_errno(); + + h = CreateFileA(path, + GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + NULL, /* lpSecurityAttributes */ + OPEN_EXISTING, /* dwCreationDisposition */ + 0, /* dwFlagsAndAttributes */ + NULL); /* hTemplateFile */ + + if (h == INVALID_HANDLE_VALUE) { + propagate_last_error_to_errno(); + + return -1; + } + + if (hstat(h, wst)) { + CloseHandle(h); + return -1; + } + + CloseHandle(h); + return 0; +} + +int win32_lstat(const char *path, struct win32_stat *wst) +{ + HANDLE h; + + SetLastError(0); + reset_errno(); + + h = CreateFileA(path, + GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + NULL, /* lpSecurityAttributes */ + OPEN_EXISTING, /* dwCreationDisposition */ + FILE_FLAG_OPEN_REPARSE_POINT, + NULL); /* hTemplateFile */ + + if (h == INVALID_HANDLE_VALUE) { + propagate_last_error_to_errno(); + + return -1; + } + + if (hstat(h, wst)) { + CloseHandle(h); + return -1; + } + + if (is_symlink(path)) { + wst->st_mode = WIN32_S_IFLNK; + } + + CloseHandle(h); + return 0; +} + +int win32_fstat(int fd, struct win32_stat *wst) +{ + HANDLE h; + + SetLastError(0); + reset_errno(); + + h = (HANDLE) _get_osfhandle(fd); + + if (h == INVALID_HANDLE_VALUE) { + propagate_last_error_to_errno(); + + return -1; + } + + return hstat(h, wst); +} |