1/*-------------------------------------------------------------------------
4 * Replacements for <sys/stat.h> functions using GetFileInformationByHandle
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/port/win32stat.c
13 *-------------------------------------------------------------------------
22 * Convert a FILETIME struct into a 64 bit time_t.
27 ULARGE_INTEGER unified_ft = {0};
30 unified_ft.LowPart = ft->dwLowDateTime;
31 unified_ft.HighPart = ft->dwHighDateTime;
33 if (unified_ft.QuadPart < EpochShift)
36 unified_ft.QuadPart -= EpochShift;
37 unified_ft.QuadPart /= 10 * 1000 * 1000;
39 return unified_ft.QuadPart;
43 * Convert WIN32 file attributes to a Unix-style mode.
45 * Only owner permissions are set.
50 unsigned short uxmode = 0;
52 uxmode |= (
unsigned short) ((attr & FILE_ATTRIBUTE_DIRECTORY) ?
53 (_S_IFDIR) : (_S_IFREG));
55 uxmode |= (
unsigned short) ((attr & FILE_ATTRIBUTE_READONLY) ?
56 (_S_IREAD) : (_S_IREAD | _S_IWRITE));
58 /* there is no need to simulate _S_IEXEC using CMD's PATHEXT extensions */
65 * Convert WIN32 file information (from a HANDLE) to a struct stat.
70 BY_HANDLE_FILE_INFORMATION fiData;
72 memset(
buf, 0,
sizeof(*
buf));
75 * GetFileInformationByHandle minimum supported version: Windows XP and
76 * Windows Server 2003, so it exists everywhere we care about.
78 if (!GetFileInformationByHandle(hFile, &fiData))
84 if (fiData.ftLastWriteTime.dwLowDateTime ||
85 fiData.ftLastWriteTime.dwHighDateTime)
88 if (fiData.ftLastAccessTime.dwLowDateTime ||
89 fiData.ftLastAccessTime.dwHighDateTime)
92 buf->st_atime =
buf->st_mtime;
94 if (fiData.ftCreationTime.dwLowDateTime ||
95 fiData.ftCreationTime.dwHighDateTime)
98 buf->st_ctime =
buf->st_mtime;
101 buf->st_nlink = fiData.nNumberOfLinks;
103 buf->st_size = ((((
uint64) fiData.nFileSizeHigh) << 32) |
104 fiData.nFileSizeLow);
110 * Windows implementation of lstat().
116 * Our open wrapper will report STATUS_DELETE_PENDING as ENOENT. We
117 * request FILE_FLAG_BACKUP_SEMANTICS so that we can open directories too,
118 * for limited purposes. We use the private handle-based version, so we
119 * don't risk running out of fds.
124 hFile = pgwin32_open_handle(
name, O_RDONLY,
true);
125 if (hFile == INVALID_HANDLE_VALUE)
130 * If it's a junction point pointing to a non-existent path, we'll
131 * have ENOENT here (because pgwin32_open_handle does not use
132 * FILE_FLAG_OPEN_REPARSE_POINT). In that case, we'll try again
133 * with readlink() below, which will distinguish true ENOENT from
136 memset(
buf, 0,
sizeof(*
buf));
146 * Junction points appear as directories to fileinfo_to_stat(), so we'll
147 * need to do a bit more work to distinguish them.
149 if ((ret == 0 &&
S_ISDIR(
buf->st_mode)) || hFile == INVALID_HANDLE_VALUE)
155 * POSIX says we need to put the length of the target path into
156 * st_size. Use readlink() to get it, or learn that this is not a
162 if (errno == EACCES &&
165 /* Unlinked underneath us. */
169 else if (errno == EINVAL)
171 /* It's not a junction point, nothing to do. */
175 /* Some other failure. */
181 /* It's a junction point, so report it as a symlink. */
182 buf->st_mode &= ~S_IFDIR;
189 if (hFile != INVALID_HANDLE_VALUE)
195 * Windows implementation of stat().
208 /* Do we need to follow a symlink (junction point)? */
221 * _pglstat64() already called readlink() once to be able to fill in
222 * st_size, and now we need to do it again to get the path to follow.
223 * That could be optimized, but stat() on symlinks is probably rare
224 * and this way is simple.
229 if (errno == EACCES &&
232 /* Unlinked underneath us. */
237 if (size >=
sizeof(
next))
239 errno = ENAMETOOLONG;
252 * Windows implementation of fstat().
257 HANDLE hFile = (HANDLE) _get_osfhandle(fileno);
258 DWORD fileType = FILE_TYPE_UNKNOWN;
259 unsigned short st_mode;
273 /* The specified file is a disk file */
278 * The specified file is a socket, a named pipe, or an anonymous
284 /* The specified file is a character file */
288 /* Unused flag and unknown file type */
289 case FILE_TYPE_REMOTE:
290 case FILE_TYPE_UNKNOWN:
296 memset(
buf, 0,
sizeof(*
buf));
297 buf->st_mode = st_mode;
298 buf->st_dev = fileno;
299 buf->st_rdev = fileno;
size_t strlcpy(char *dst, const char *src, size_t siz)
void _dosmaperr(unsigned long)
DWORD pgwin32_get_file_type(HANDLE hFile)
#define readlink(path, buf, size)
PGDLLIMPORT RtlGetLastNtStatus_t pg_RtlGetLastNtStatus
static __time64_t filetime_to_time(const FILETIME *ft)
static unsigned short fileattr_to_unixmode(int attr)
int _pgfstat64(int fileno, struct stat *buf)
int _pgstat64(const char *name, struct stat *buf)
static int fileinfo_to_stat(HANDLE hFile, struct stat *buf)
int _pglstat64(const char *name, struct stat *buf)