aboutsummaryrefslogtreecommitdiff
path: root/src/port/win32stat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/port/win32stat.c')
-rw-r--r--src/port/win32stat.c110
1 files changed, 106 insertions, 4 deletions
diff --git a/src/port/win32stat.c b/src/port/win32stat.c
index e03ed5f35ca..26443293d78 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,11 +131,111 @@ _pgstat64(const char *name, struct stat *buf)
ret = fileinfo_to_stat(hFile, buf);
+ /*
+ * Junction points appear as directories to fileinfo_to_stat(), so we'll
+ * need to do a bit more work to distinguish them.
+ */
+ if (ret == 0 && S_ISDIR(buf->st_mode))
+ {
+ char next[MAXPGPATH];
+ ssize_t size;
+
+ /*
+ * POSIX says we need to put the length of the target path into
+ * st_size. Use readlink() to get it, or learn that this is not a
+ * junction point.
+ */
+ size = readlink(name, next, sizeof(next));
+ if (size < 0)
+ {
+ if (errno == EACCES &&
+ pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING)
+ {
+ /* Unlinked underneath us. */
+ errno = ENOENT;
+ ret = -1;
+ }
+ else if (errno == EINVAL)
+ {
+ /* It's not a junction point, nothing to do. */
+ }
+ else
+ {
+ /* Some other failure. */
+ ret = -1;
+ }
+ }
+ else
+ {
+ /* It's a junction point, so report it as a symlink. */
+ buf->st_mode &= ~S_IFDIR;
+ buf->st_mode |= S_IFLNK;
+ buf->st_size = size;
+ }
+ }
+
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];
+ ssize_t size;
+
+ /*
+ * _pglstat64() already called readlink() once to be able to fill in
+ * st_size, and now we need to do it again to get the path to follow.
+ * That could be optimized, but stat() on symlinks is probably rare
+ * and this way is simple.
+ */
+ size = readlink(name, next, sizeof(next));
+ if (size < 0)
+ {
+ if (errno == EACCES &&
+ pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING)
+ {
+ /* Unlinked underneath us. */
+ errno = ENOENT;
+ }
+ return -1;
+ }
+ if (size >= sizeof(next))
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ next[size] = 0;
+
+ 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().
*/
int