PostgreSQL Source Code git master
win32stat.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * win32stat.c
4 * Replacements for <sys/stat.h> functions using GetFileInformationByHandle
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/port/win32stat.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16#include "c.h"
17#include "port/win32ntdll.h"
18
19#include <windows.h>
20
21/*
22 * Convert a FILETIME struct into a 64 bit time_t.
23 */
24static __time64_t
25filetime_to_time(const FILETIME *ft)
26{
27 ULARGE_INTEGER unified_ft = {0};
28 static const uint64 EpochShift = UINT64CONST(116444736000000000);
29
30 unified_ft.LowPart = ft->dwLowDateTime;
31 unified_ft.HighPart = ft->dwHighDateTime;
32
33 if (unified_ft.QuadPart < EpochShift)
34 return -1;
35
36 unified_ft.QuadPart -= EpochShift;
37 unified_ft.QuadPart /= 10 * 1000 * 1000;
38
39 return unified_ft.QuadPart;
40}
41
42/*
43 * Convert WIN32 file attributes to a Unix-style mode.
44 *
45 * Only owner permissions are set.
46 */
47static unsigned short
49{
50 unsigned short uxmode = 0;
51
52 uxmode |= (unsigned short) ((attr & FILE_ATTRIBUTE_DIRECTORY) ?
53 (_S_IFDIR) : (_S_IFREG));
54
55 uxmode |= (unsigned short) ((attr & FILE_ATTRIBUTE_READONLY) ?
56 (_S_IREAD) : (_S_IREAD | _S_IWRITE));
57
58 /* there is no need to simulate _S_IEXEC using CMD's PATHEXT extensions */
59 uxmode |= _S_IEXEC;
60
61 return uxmode;
62}
63
64/*
65 * Convert WIN32 file information (from a HANDLE) to a struct stat.
66 */
67static int
68fileinfo_to_stat(HANDLE hFile, struct stat *buf)
69{
70 BY_HANDLE_FILE_INFORMATION fiData;
71
72 memset(buf, 0, sizeof(*buf));
73
74 /*
75 * GetFileInformationByHandle minimum supported version: Windows XP and
76 * Windows Server 2003, so it exists everywhere we care about.
77 */
78 if (!GetFileInformationByHandle(hFile, &fiData))
79 {
80 _dosmaperr(GetLastError());
81 return -1;
82 }
83
84 if (fiData.ftLastWriteTime.dwLowDateTime ||
85 fiData.ftLastWriteTime.dwHighDateTime)
86 buf->st_mtime = filetime_to_time(&fiData.ftLastWriteTime);
87
88 if (fiData.ftLastAccessTime.dwLowDateTime ||
89 fiData.ftLastAccessTime.dwHighDateTime)
90 buf->st_atime = filetime_to_time(&fiData.ftLastAccessTime);
91 else
92 buf->st_atime = buf->st_mtime;
93
94 if (fiData.ftCreationTime.dwLowDateTime ||
95 fiData.ftCreationTime.dwHighDateTime)
96 buf->st_ctime = filetime_to_time(&fiData.ftCreationTime);
97 else
98 buf->st_ctime = buf->st_mtime;
99
100 buf->st_mode = fileattr_to_unixmode(fiData.dwFileAttributes);
101 buf->st_nlink = fiData.nNumberOfLinks;
102
103 buf->st_size = ((((uint64) fiData.nFileSizeHigh) << 32) |
104 fiData.nFileSizeLow);
105
106 return 0;
107}
108
109/*
110 * Windows implementation of lstat().
111 */
112int
113_pglstat64(const char *name, struct stat *buf)
114{
115 /*
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.
120 */
121 HANDLE hFile;
122 int ret;
123
124 hFile = pgwin32_open_handle(name, O_RDONLY, true);
125 if (hFile == INVALID_HANDLE_VALUE)
126 {
127 if (errno == ENOENT)
128 {
129 /*
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
134 * pseudo-symlink.
135 */
136 memset(buf, 0, sizeof(*buf));
137 ret = 0;
138 }
139 else
140 return -1;
141 }
142 else
143 ret = fileinfo_to_stat(hFile, buf);
144
145 /*
146 * Junction points appear as directories to fileinfo_to_stat(), so we'll
147 * need to do a bit more work to distinguish them.
148 */
149 if ((ret == 0 && S_ISDIR(buf->st_mode)) || hFile == INVALID_HANDLE_VALUE)
150 {
151 char next[MAXPGPATH];
152 ssize_t size;
153
154 /*
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
157 * junction point.
158 */
159 size = readlink(name, next, sizeof(next));
160 if (size < 0)
161 {
162 if (errno == EACCES &&
163 pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING)
164 {
165 /* Unlinked underneath us. */
166 errno = ENOENT;
167 ret = -1;
168 }
169 else if (errno == EINVAL)
170 {
171 /* It's not a junction point, nothing to do. */
172 }
173 else
174 {
175 /* Some other failure. */
176 ret = -1;
177 }
178 }
179 else
180 {
181 /* It's a junction point, so report it as a symlink. */
182 buf->st_mode &= ~S_IFDIR;
183 buf->st_mode |= S_IFLNK;
184 buf->st_size = size;
185 ret = 0;
186 }
187 }
188
189 if (hFile != INVALID_HANDLE_VALUE)
190 CloseHandle(hFile);
191 return ret;
192}
193
194/*
195 * Windows implementation of stat().
196 */
197int
198_pgstat64(const char *name, struct stat *buf)
199{
200 int loops = 0;
201 int ret;
202 char curr[MAXPGPATH];
203
204 ret = _pglstat64(name, buf);
205
206 strlcpy(curr, name, MAXPGPATH);
207
208 /* Do we need to follow a symlink (junction point)? */
209 while (ret == 0 && S_ISLNK(buf->st_mode))
210 {
211 char next[MAXPGPATH];
212 ssize_t size;
213
214 if (++loops > 8)
215 {
216 errno = ELOOP;
217 return -1;
218 }
219
220 /*
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.
225 */
226 size = readlink(curr, next, sizeof(next));
227 if (size < 0)
228 {
229 if (errno == EACCES &&
230 pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING)
231 {
232 /* Unlinked underneath us. */
233 errno = ENOENT;
234 }
235 return -1;
236 }
237 if (size >= sizeof(next))
238 {
239 errno = ENAMETOOLONG;
240 return -1;
241 }
242 next[size] = 0;
243
244 ret = _pglstat64(next, buf);
245 strcpy(curr, next);
246 }
247
248 return ret;
249}
250
251/*
252 * Windows implementation of fstat().
253 */
254int
255_pgfstat64(int fileno, struct stat *buf)
256{
257 HANDLE hFile = (HANDLE) _get_osfhandle(fileno);
258 DWORD fileType = FILE_TYPE_UNKNOWN;
259 unsigned short st_mode;
260
261 if (buf == NULL)
262 {
263 errno = EINVAL;
264 return -1;
265 }
266
267 fileType = pgwin32_get_file_type(hFile);
268 if (errno != 0)
269 return -1;
270
271 switch (fileType)
272 {
273 /* The specified file is a disk file */
274 case FILE_TYPE_DISK:
275 return fileinfo_to_stat(hFile, buf);
276
277 /*
278 * The specified file is a socket, a named pipe, or an anonymous
279 * pipe.
280 */
281 case FILE_TYPE_PIPE:
282 st_mode = _S_IFIFO;
283 break;
284 /* The specified file is a character file */
285 case FILE_TYPE_CHAR:
286 st_mode = _S_IFCHR;
287 break;
288 /* Unused flag and unknown file type */
289 case FILE_TYPE_REMOTE:
290 case FILE_TYPE_UNKNOWN:
291 default:
292 errno = EINVAL;
293 return -1;
294 }
295
296 memset(buf, 0, sizeof(*buf));
297 buf->st_mode = st_mode;
298 buf->st_dev = fileno;
299 buf->st_rdev = fileno;
300 buf->st_nlink = 1;
301 return 0;
302}
static int32 next
Definition: blutils.c:219
uint64_t uint64
Definition: c.h:489
#define UINT64CONST(x)
Definition: c.h:503
#define MAXPGPATH
static char * buf
Definition: pg_test_fsync.c:72
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
static pg_noinline void Size size
Definition: slab.c:607
const char * name
#define S_ISDIR(m)
Definition: win32_port.h:315
void _dosmaperr(unsigned long)
Definition: win32error.c:177
#define S_ISLNK(m)
Definition: win32_port.h:334
DWORD pgwin32_get_file_type(HANDLE hFile)
Definition: win32common.c:31
#define readlink(path, buf, size)
Definition: win32_port.h:226
#define S_IFLNK
Definition: win32_port.h:333
PGDLLIMPORT RtlGetLastNtStatus_t pg_RtlGetLastNtStatus
Definition: win32ntdll.c:20
static __time64_t filetime_to_time(const FILETIME *ft)
Definition: win32stat.c:25
static unsigned short fileattr_to_unixmode(int attr)
Definition: win32stat.c:48
int _pgfstat64(int fileno, struct stat *buf)
Definition: win32stat.c:255
int _pgstat64(const char *name, struct stat *buf)
Definition: win32stat.c:198
static int fileinfo_to_stat(HANDLE hFile, struct stat *buf)
Definition: win32stat.c:68
int _pglstat64(const char *name, struct stat *buf)
Definition: win32stat.c:113