v3-0002-Provide-lstat-for-Windows.patch
text/x-patch
Filename: v3-0002-Provide-lstat-for-Windows.patch
Type: text/x-patch
Part: 1
Patch
Format: format-patch
Series: patch v3-0002
Subject: Provide lstat() for Windows.
| File | + | − |
|---|---|---|
| src/include/port/win32_port.h | 17 | 1 |
| src/port/win32stat.c | 75 | 4 |
From 5e64700facc43fd937349bc8d843b15abb8da5cc Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Thu, 28 Jul 2022 15:19:38 +1200
Subject: [PATCH v3 2/4] Provide lstat() for Windows.
Junction points will be reported with S_ISLNK(x.st_mode), like in POSIX.
stat() will follow symlinks, like in POSIX (but only one level before it
gives up, unlike in POSIX).
Discussion: https://postgr.es/m/CA%2BhUKGLfOOeyZpm5ByVcAt7x5Pn-%3DxGRNCvgiUPVVzjFLtnY0w%40mail.gmail.com
---
src/include/port/win32_port.h | 18 +++++++-
src/port/win32stat.c | 79 +++++++++++++++++++++++++++++++++--
2 files changed, 92 insertions(+), 5 deletions(-)
diff --git a/src/include/port/win32_port.h b/src/include/port/win32_port.h
index 4de5bf3bf6..b8cf2e1480 100644
--- a/src/include/port/win32_port.h
+++ b/src/include/port/win32_port.h
@@ -286,10 +286,11 @@ struct stat /* This should match struct __stat64 */
extern int _pgfstat64(int fileno, struct stat *buf);
extern int _pgstat64(const char *name, struct stat *buf);
+extern int _pglstat64(const char *name, struct stat *buf);
#define fstat(fileno, sb) _pgfstat64(fileno, sb)
#define stat(path, sb) _pgstat64(path, sb)
-#define lstat(path, sb) _pgstat64(path, sb)
+#define lstat(path, sb) _pglstat64(path, sb)
/* These macros are not provided by older MinGW, nor by MSVC */
#ifndef S_IRUSR
@@ -335,6 +336,21 @@ extern int _pgstat64(const char *name, struct stat *buf);
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
+/*
+ * In order for lstat() to be able to report junction points as symlinks, we
+ * need to hijack a bit in st_mode, since neither MSVC nor MinGW provides
+ * S_ISLNK and there aren't any spare bits. We'll steal the one for character
+ * devices, because we don't otherwise make use of those.
+ */
+#ifdef S_ISLNK
+#error "S_ISLNK is already defined"
+#endif
+#ifdef S_IFLNK
+#error "S_IFLNK is already defined"
+#endif
+#define S_IFLNK S_IFCHR
+#define S_ISLNK(m) (((m) & S_IFLNK) == S_IFLNK)
+
/*
* Supplement to <fcntl.h>.
* This is the same value as _O_NOINHERIT in the MS header file. This is
diff --git a/src/port/win32stat.c b/src/port/win32stat.c
index e03ed5f35c..686c510fed 100644
--- a/src/port/win32stat.c
+++ b/src/port/win32stat.c
@@ -15,7 +15,11 @@
#ifdef WIN32
+#define UMDF_USING_NTSTATUS
+
#include "c.h"
+#include "port/win32ntdll.h"
+
#include <windows.h>
/*
@@ -107,12 +111,10 @@ fileinfo_to_stat(HANDLE hFile, struct stat *buf)
}
/*
- * Windows implementation of stat().
- *
- * This currently also implements lstat(), though perhaps that should change.
+ * Windows implementation of lstat().
*/
int
-_pgstat64(const char *name, struct stat *buf)
+_pglstat64(const char *name, struct stat *buf)
{
/*
* Our open wrapper will report STATUS_DELETE_PENDING as ENOENT. We
@@ -129,10 +131,79 @@ _pgstat64(const char *name, struct stat *buf)
ret = fileinfo_to_stat(hFile, buf);
+ /*
+ * Unfortunately it's not possible for fileinfo_to_stat() to see a
+ * FILE_ATTRIBUTE_REPARSE_POINT flag with just a handle, so at this point
+ * junction points appear as directories. Ask for the full attributes
+ * while we still have the handle open. Someone else can unlink the file
+ * while we have it open, but they can't create another file with the same
+ * name.
+ */
+ if (ret == 0 && S_ISDIR(buf->st_mode))
+ {
+ DWORD attr;
+
+ attr = GetFileAttributes(name);
+ if (attr == INVALID_FILE_ATTRIBUTES)
+ {
+ DWORD err;
+
+ /* If it's been unlinked since we opened it, report as ENOENT. */
+ err = GetLastError();
+ if (err == ERROR_ACCESS_DENIED &&
+ pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING)
+ errno = ENOENT;
+ else
+ _dosmaperr(err);
+
+ ret = -1;
+ }
+ else if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ buf->st_mode &= ~S_IFDIR;
+ buf->st_mode |= S_IFLNK;
+ }
+ }
+
CloseHandle(hFile);
return ret;
}
+/*
+ * Windows implementation of stat().
+ */
+int
+_pgstat64(const char *name, struct stat *buf)
+{
+ int ret;
+
+ ret = _pglstat64(name, buf);
+
+ /* Do we need to follow a symlink (junction point)? */
+ if (ret == 0 && S_ISLNK(buf->st_mode))
+ {
+ char next[MAXPGPATH];
+
+ if (readlink(name, next, sizeof(next)) < 0)
+ return -1;
+
+ ret = _pglstat64(next, buf);
+ if (ret == 0 && S_ISLNK(buf->st_mode))
+ {
+ /*
+ * We're only prepared to go one hop, because we only expect to
+ * deal with the simple cases that we create. The error for too
+ * many symlinks is supposed to be ELOOP, but Windows hasn't got
+ * it.
+ */
+ errno = EIO;
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
/*
* Windows implementation of fstat().
*/
--
2.37.1