Skip to content

Commit c5cb8f3

Browse files
committed
Provide lstat() for Windows.
Junction points will be reported with S_ISLNK(x.st_mode), simulating POSIX lstat(). stat() will follow pseudo-symlinks, like in POSIX (but only one level before giving up, unlike in POSIX). This completes a TODO left by commit bed9075. Tested-by: Andrew Dunstan <andrew@dunslane.net> (earlier version) Discussion: https://postgr.es/m/CA%2BhUKGLfOOeyZpm5ByVcAt7x5Pn-%3DxGRNCvgiUPVVzjFLtnY0w%40mail.gmail.com
1 parent feb5935 commit c5cb8f3

File tree

2 files changed

+123
-5
lines changed

2 files changed

+123
-5
lines changed

src/include/port/win32_port.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,11 @@ struct stat /* This should match struct __stat64 */
278278

279279
extern int _pgfstat64(int fileno, struct stat *buf);
280280
extern int _pgstat64(const char *name, struct stat *buf);
281+
extern int _pglstat64(const char *name, struct stat *buf);
281282

282283
#define fstat(fileno, sb) _pgfstat64(fileno, sb)
283284
#define stat(path, sb) _pgstat64(path, sb)
284-
#define lstat(path, sb) _pgstat64(path, sb)
285+
#define lstat(path, sb) _pglstat64(path, sb)
285286

286287
/* These macros are not provided by older MinGW, nor by MSVC */
287288
#ifndef S_IRUSR
@@ -327,6 +328,21 @@ extern int _pgstat64(const char *name, struct stat *buf);
327328
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
328329
#endif
329330

331+
/*
332+
* In order for lstat() to be able to report junction points as symlinks, we
333+
* need to hijack a bit in st_mode, since neither MSVC nor MinGW provides
334+
* S_ISLNK and there aren't any spare bits. We'll steal the one for character
335+
* devices, because we don't otherwise make use of those.
336+
*/
337+
#ifdef S_ISLNK
338+
#error "S_ISLNK is already defined"
339+
#endif
340+
#ifdef S_IFLNK
341+
#error "S_IFLNK is already defined"
342+
#endif
343+
#define S_IFLNK S_IFCHR
344+
#define S_ISLNK(m) (((m) & S_IFLNK) == S_IFLNK)
345+
330346
/*
331347
* Supplement to <fcntl.h>.
332348
* This is the same value as _O_NOINHERIT in the MS header file. This is

src/port/win32stat.c

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515

1616
#ifdef WIN32
1717

18+
#define UMDF_USING_NTSTATUS
19+
1820
#include "c.h"
21+
#include "port/win32ntdll.h"
22+
1923
#include <windows.h>
2024

2125
/*
@@ -107,12 +111,10 @@ fileinfo_to_stat(HANDLE hFile, struct stat *buf)
107111
}
108112

109113
/*
110-
* Windows implementation of stat().
111-
*
112-
* This currently also implements lstat(), though perhaps that should change.
114+
* Windows implementation of lstat().
113115
*/
114116
int
115-
_pgstat64(const char *name, struct stat *buf)
117+
_pglstat64(const char *name, struct stat *buf)
116118
{
117119
/*
118120
* Our open wrapper will report STATUS_DELETE_PENDING as ENOENT. We
@@ -129,10 +131,110 @@ _pgstat64(const char *name, struct stat *buf)
129131

130132
ret = fileinfo_to_stat(hFile, buf);
131133

134+
/*
135+
* Junction points appear as directories to fileinfo_to_stat(), so we'll
136+
* need to do a bit more work to distinguish them.
137+
*/
138+
if (ret == 0 && S_ISDIR(buf->st_mode))
139+
{
140+
char next[MAXPGPATH];
141+
ssize_t size;
142+
143+
/*
144+
* POSIX says we need to put the length of the target path into
145+
* st_size. Use readlink() to get it, or learn that this is not a
146+
* junction point.
147+
*/
148+
size = readlink(name, next, sizeof(next));
149+
if (size < 0)
150+
{
151+
if (errno == EACCES &&
152+
pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING)
153+
{
154+
/* Unlinked underneath us. */
155+
errno = ENOENT;
156+
ret = -1;
157+
}
158+
else if (errno == EINVAL)
159+
{
160+
/* It's not a junction point, nothing to do. */
161+
}
162+
else
163+
{
164+
/* Some other failure. */
165+
ret = -1;
166+
}
167+
}
168+
else
169+
{
170+
/* It's a junction point, so report it as a symlink. */
171+
buf->st_mode &= ~S_IFDIR;
172+
buf->st_mode |= S_IFLNK;
173+
buf->st_size = size;
174+
}
175+
}
176+
132177
CloseHandle(hFile);
133178
return ret;
134179
}
135180

181+
/*
182+
* Windows implementation of stat().
183+
*/
184+
int
185+
_pgstat64(const char *name, struct stat *buf)
186+
{
187+
int ret;
188+
189+
ret = _pglstat64(name, buf);
190+
191+
/* Do we need to follow a symlink (junction point)? */
192+
if (ret == 0 && S_ISLNK(buf->st_mode))
193+
{
194+
char next[MAXPGPATH];
195+
ssize_t size;
196+
197+
/*
198+
* _pglstat64() already called readlink() once to be able to fill in
199+
* st_size, and now we need to do it again to get the path to follow.
200+
* That could be optimized, but stat() on symlinks is probably rare
201+
* and this way is simple.
202+
*/
203+
size = readlink(name, next, sizeof(next));
204+
if (size < 0)
205+
{
206+
if (errno == EACCES &&
207+
pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING)
208+
{
209+
/* Unlinked underneath us. */
210+
errno = ENOENT;
211+
}
212+
return -1;
213+
}
214+
if (size >= sizeof(next))
215+
{
216+
errno = ENAMETOOLONG;
217+
return -1;
218+
}
219+
next[size] = 0;
220+
221+
ret = _pglstat64(next, buf);
222+
if (ret == 0 && S_ISLNK(buf->st_mode))
223+
{
224+
/*
225+
* We're only prepared to go one hop, because we only expect to
226+
* deal with the simple cases that we create. The error for too
227+
* many symlinks is supposed to be ELOOP, but Windows hasn't got
228+
* it.
229+
*/
230+
errno = EIO;
231+
return -1;
232+
}
233+
}
234+
235+
return ret;
236+
}
237+
136238
/*
137239
* Windows implementation of fstat().
138240
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy