Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+17-1
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

+106-4
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)