summaryrefslogtreecommitdiffstats
path: root/src/fluent-bit/plugins/in_tail/win32/stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fluent-bit/plugins/in_tail/win32/stat.c')
-rw-r--r--src/fluent-bit/plugins/in_tail/win32/stat.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/src/fluent-bit/plugins/in_tail/win32/stat.c b/src/fluent-bit/plugins/in_tail/win32/stat.c
new file mode 100644
index 000000000..bce802749
--- /dev/null
+++ b/src/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);
+}