1 /*-------------------------------------------------------------------------
2 *
3 * dirmod.c
4 * directory handling functions
5 *
6 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * This includes replacement versions of functions that work on
10 * Win32 (NT4 and newer).
11 *
12 * IDENTIFICATION
13 * src/port/dirmod.c
14 *
15 *-------------------------------------------------------------------------
16 */
17
18 #ifndef FRONTEND
19 #include "postgres.h"
20 #else
21 #include "postgres_fe.h"
22 #endif
23
24 /* Don't modify declarations in system headers */
25 #if defined(WIN32) || defined(__CYGWIN__)
26 #undef rename
27 #undef unlink
28 #endif
29
30 #include <unistd.h>
31 #include <sys/stat.h>
32
33 #if defined(WIN32) || defined(__CYGWIN__)
34 #ifndef __CYGWIN__
35 #include <winioctl.h>
36 #else
37 #include <windows.h>
38 #include <w32api/winioctl.h>
39 #endif
40 #endif
41
42 #if defined(WIN32) || defined(__CYGWIN__)
43
44 /*
45 * pgrename
46 */
47 int
pgrename(const char * from,const char * to)48 pgrename(const char *from, const char *to)
49 {
50 int loops = 0;
51
52 /*
53 * We need to loop because even though PostgreSQL uses flags that allow
54 * rename while the file is open, other applications might have the file
55 * open without those flags. However, we won't wait indefinitely for
56 * someone else to close the file, as the caller might be holding locks
57 * and blocking other backends.
58 */
59 #if defined(WIN32) && !defined(__CYGWIN__)
60 while (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
61 #else
62 while (rename(from, to) < 0)
63 #endif
64 {
65 #if defined(WIN32) && !defined(__CYGWIN__)
66 DWORD err = GetLastError();
67
68 _dosmaperr(err);
69
70 /*
71 * Modern NT-based Windows versions return ERROR_SHARING_VIOLATION if
72 * another process has the file open without FILE_SHARE_DELETE.
73 * ERROR_LOCK_VIOLATION has also been seen with some anti-virus
74 * software. This used to check for just ERROR_ACCESS_DENIED, so
75 * presumably you can get that too with some OS versions. We don't
76 * expect real permission errors where we currently use rename().
77 */
78 if (err != ERROR_ACCESS_DENIED &&
79 err != ERROR_SHARING_VIOLATION &&
80 err != ERROR_LOCK_VIOLATION)
81 return -1;
82 #else
83 if (errno != EACCES)
84 return -1;
85 #endif
86
87 if (++loops > 100) /* time out after 10 sec */
88 return -1;
89 pg_usleep(100000); /* us */
90 }
91 return 0;
92 }
93
94
95 /*
96 * pgunlink
97 */
98 int
pgunlink(const char * path)99 pgunlink(const char *path)
100 {
101 int loops = 0;
102
103 /*
104 * We need to loop because even though PostgreSQL uses flags that allow
105 * unlink while the file is open, other applications might have the file
106 * open without those flags. However, we won't wait indefinitely for
107 * someone else to close the file, as the caller might be holding locks
108 * and blocking other backends.
109 */
110 while (unlink(path))
111 {
112 if (errno != EACCES)
113 return -1;
114 if (++loops > 100) /* time out after 10 sec */
115 return -1;
116 pg_usleep(100000); /* us */
117 }
118 return 0;
119 }
120
121 /* We undefined these above; now redefine for possible use below */
122 #define rename(from, to) pgrename(from, to)
123 #define unlink(path) pgunlink(path)
124 #endif /* defined(WIN32) || defined(__CYGWIN__) */
125
126
127 #if defined(WIN32) && !defined(__CYGWIN__) /* Cygwin has its own symlinks */
128
129 /*
130 * pgsymlink support:
131 *
132 * This struct is a replacement for REPARSE_DATA_BUFFER which is defined in VC6 winnt.h
133 * but omitted in later SDK functions.
134 * We only need the SymbolicLinkReparseBuffer part of the original struct's union.
135 */
136 typedef struct
137 {
138 DWORD ReparseTag;
139 WORD ReparseDataLength;
140 WORD Reserved;
141 /* SymbolicLinkReparseBuffer */
142 WORD SubstituteNameOffset;
143 WORD SubstituteNameLength;
144 WORD PrintNameOffset;
145 WORD PrintNameLength;
146 WCHAR PathBuffer[FLEXIBLE_ARRAY_MEMBER];
147 } REPARSE_JUNCTION_DATA_BUFFER;
148
149 #define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE \
150 FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset)
151
152
153 /*
154 * pgsymlink - uses Win32 junction points
155 *
156 * For reference: http://www.codeproject.com/KB/winsdk/junctionpoints.aspx
157 */
158 int
pgsymlink(const char * oldpath,const char * newpath)159 pgsymlink(const char *oldpath, const char *newpath)
160 {
161 HANDLE dirhandle;
162 DWORD len;
163 char buffer[MAX_PATH * sizeof(WCHAR) + offsetof(REPARSE_JUNCTION_DATA_BUFFER, PathBuffer)];
164 char nativeTarget[MAX_PATH];
165 char *p = nativeTarget;
166 REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
167
168 CreateDirectory(newpath, 0);
169 dirhandle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE,
170 0, 0, OPEN_EXISTING,
171 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
172
173 if (dirhandle == INVALID_HANDLE_VALUE)
174 return -1;
175
176 /* make sure we have an unparsed native win32 path */
177 if (memcmp("\\??\\", oldpath, 4) != 0)
178 snprintf(nativeTarget, sizeof(nativeTarget), "\\??\\%s", oldpath);
179 else
180 strlcpy(nativeTarget, oldpath, sizeof(nativeTarget));
181
182 while ((p = strchr(p, '/')) != NULL)
183 *p++ = '\\';
184
185 len = strlen(nativeTarget) * sizeof(WCHAR);
186 reparseBuf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
187 reparseBuf->ReparseDataLength = len + 12;
188 reparseBuf->Reserved = 0;
189 reparseBuf->SubstituteNameOffset = 0;
190 reparseBuf->SubstituteNameLength = len;
191 reparseBuf->PrintNameOffset = len + sizeof(WCHAR);
192 reparseBuf->PrintNameLength = 0;
193 MultiByteToWideChar(CP_ACP, 0, nativeTarget, -1,
194 reparseBuf->PathBuffer, MAX_PATH);
195
196 /*
197 * FSCTL_SET_REPARSE_POINT is coded differently depending on SDK version;
198 * we use our own definition
199 */
200 if (!DeviceIoControl(dirhandle,
201 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_ANY_ACCESS),
202 reparseBuf,
203 reparseBuf->ReparseDataLength + REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE,
204 0, 0, &len, 0))
205 {
206 LPSTR msg;
207
208 errno = 0;
209 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
210 FORMAT_MESSAGE_IGNORE_INSERTS |
211 FORMAT_MESSAGE_FROM_SYSTEM,
212 NULL, GetLastError(),
213 MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
214 (LPSTR) &msg, 0, NULL);
215 #ifndef FRONTEND
216 ereport(ERROR,
217 (errcode_for_file_access(),
218 errmsg("could not set junction for \"%s\": %s",
219 nativeTarget, msg)));
220 #else
221 fprintf(stderr, _("could not set junction for \"%s\": %s\n"),
222 nativeTarget, msg);
223 #endif
224 LocalFree(msg);
225
226 CloseHandle(dirhandle);
227 RemoveDirectory(newpath);
228 return -1;
229 }
230
231 CloseHandle(dirhandle);
232
233 return 0;
234 }
235
236 /*
237 * pgreadlink - uses Win32 junction points
238 */
239 int
pgreadlink(const char * path,char * buf,size_t size)240 pgreadlink(const char *path, char *buf, size_t size)
241 {
242 DWORD attr;
243 HANDLE h;
244 char buffer[MAX_PATH * sizeof(WCHAR) + offsetof(REPARSE_JUNCTION_DATA_BUFFER, PathBuffer)];
245 REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
246 DWORD len;
247 int r;
248
249 attr = GetFileAttributes(path);
250 if (attr == INVALID_FILE_ATTRIBUTES)
251 {
252 _dosmaperr(GetLastError());
253 return -1;
254 }
255 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
256 {
257 errno = EINVAL;
258 return -1;
259 }
260
261 h = CreateFile(path,
262 GENERIC_READ,
263 FILE_SHARE_READ | FILE_SHARE_WRITE,
264 NULL,
265 OPEN_EXISTING,
266 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
267 0);
268 if (h == INVALID_HANDLE_VALUE)
269 {
270 _dosmaperr(GetLastError());
271 return -1;
272 }
273
274 if (!DeviceIoControl(h,
275 FSCTL_GET_REPARSE_POINT,
276 NULL,
277 0,
278 (LPVOID) reparseBuf,
279 sizeof(buffer),
280 &len,
281 NULL))
282 {
283 LPSTR msg;
284
285 errno = 0;
286 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
287 FORMAT_MESSAGE_IGNORE_INSERTS |
288 FORMAT_MESSAGE_FROM_SYSTEM,
289 NULL, GetLastError(),
290 MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
291 (LPSTR) &msg, 0, NULL);
292 #ifndef FRONTEND
293 ereport(ERROR,
294 (errcode_for_file_access(),
295 errmsg("could not get junction for \"%s\": %s",
296 path, msg)));
297 #else
298 fprintf(stderr, _("could not get junction for \"%s\": %s\n"),
299 path, msg);
300 #endif
301 LocalFree(msg);
302 CloseHandle(h);
303 errno = EINVAL;
304 return -1;
305 }
306 CloseHandle(h);
307
308 /* Got it, let's get some results from this */
309 if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
310 {
311 errno = EINVAL;
312 return -1;
313 }
314
315 r = WideCharToMultiByte(CP_ACP, 0,
316 reparseBuf->PathBuffer, -1,
317 buf,
318 size,
319 NULL, NULL);
320
321 if (r <= 0)
322 {
323 errno = EINVAL;
324 return -1;
325 }
326
327 /*
328 * If the path starts with "\??\", which it will do in most (all?) cases,
329 * strip those out.
330 */
331 if (r > 4 && strncmp(buf, "\\??\\", 4) == 0)
332 {
333 memmove(buf, buf + 4, strlen(buf + 4) + 1);
334 r -= 4;
335 }
336 return r;
337 }
338
339 /*
340 * Assumes the file exists, so will return false if it doesn't
341 * (since a nonexistent file is not a junction)
342 */
343 bool
pgwin32_is_junction(const char * path)344 pgwin32_is_junction(const char *path)
345 {
346 DWORD attr = GetFileAttributes(path);
347
348 if (attr == INVALID_FILE_ATTRIBUTES)
349 {
350 _dosmaperr(GetLastError());
351 return false;
352 }
353 return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT);
354 }
355 #endif /* defined(WIN32) && !defined(__CYGWIN__) */
356
357
358 #if defined(WIN32) && !defined(__CYGWIN__)
359
360 #undef stat
361
362 /*
363 * The stat() function in win32 is not guaranteed to update the st_size
364 * field when run. So we define our own version that uses the Win32 API
365 * to update this field.
366 */
367 int
pgwin32_safestat(const char * path,struct stat * buf)368 pgwin32_safestat(const char *path, struct stat *buf)
369 {
370 int r;
371 WIN32_FILE_ATTRIBUTE_DATA attr;
372
373 r = stat(path, buf);
374 if (r < 0)
375 {
376 if (GetLastError() == ERROR_DELETE_PENDING)
377 {
378 /*
379 * File has been deleted, but is not gone from the filesystem yet.
380 * This can happen when some process with FILE_SHARE_DELETE has it
381 * open and it will be fully removed once that handle is closed.
382 * Meanwhile, we can't open it, so indicate that the file just
383 * doesn't exist.
384 */
385 errno = ENOENT;
386 return -1;
387 }
388
389 return r;
390 }
391
392 if (!GetFileAttributesEx(path, GetFileExInfoStandard, &attr))
393 {
394 _dosmaperr(GetLastError());
395 return -1;
396 }
397
398 /*
399 * XXX no support for large files here, but we don't do that in general on
400 * Win32 yet.
401 */
402 buf->st_size = attr.nFileSizeLow;
403
404 return 0;
405 }
406
407 #endif
408