]> granicus.if.org Git - postgresql/blob - src/port/dirmod.c
Group cluster_name and update_process_title settings together
[postgresql] / src / port / dirmod.c
1 /*-------------------------------------------------------------------------
2  *
3  * dirmod.c
4  *        directory handling functions
5  *
6  * Portions Copyright (c) 1996-2015, 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
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
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
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 | FORMAT_MESSAGE_FROM_SYSTEM,
210                                           NULL, GetLastError(),
211                                           MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
212                                           (LPSTR) &msg, 0, NULL);
213 #ifndef FRONTEND
214                 ereport(ERROR,
215                                 (errcode_for_file_access(),
216                                  errmsg("could not set junction for \"%s\": %s",
217                                                 nativeTarget, msg)));
218 #else
219                 fprintf(stderr, _("could not set junction for \"%s\": %s\n"),
220                                 nativeTarget, msg);
221 #endif
222                 LocalFree(msg);
223
224                 CloseHandle(dirhandle);
225                 RemoveDirectory(newpath);
226                 return -1;
227         }
228
229         CloseHandle(dirhandle);
230
231         return 0;
232 }
233
234 /*
235  *      pgreadlink - uses Win32 junction points
236  */
237 int
238 pgreadlink(const char *path, char *buf, size_t size)
239 {
240         DWORD           attr;
241         HANDLE          h;
242         char            buffer[MAX_PATH * sizeof(WCHAR) + offsetof(REPARSE_JUNCTION_DATA_BUFFER, PathBuffer)];
243         REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
244         DWORD           len;
245         int                     r;
246
247         attr = GetFileAttributes(path);
248         if (attr == INVALID_FILE_ATTRIBUTES)
249         {
250                 _dosmaperr(GetLastError());
251                 return -1;
252         }
253         if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
254         {
255                 errno = EINVAL;
256                 return -1;
257         }
258
259         h = CreateFile(path,
260                                    GENERIC_READ,
261                                    FILE_SHARE_READ | FILE_SHARE_WRITE,
262                                    NULL,
263                                    OPEN_EXISTING,
264                                    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
265                                    0);
266         if (h == INVALID_HANDLE_VALUE)
267         {
268                 _dosmaperr(GetLastError());
269                 return -1;
270         }
271
272         if (!DeviceIoControl(h,
273                                                  FSCTL_GET_REPARSE_POINT,
274                                                  NULL,
275                                                  0,
276                                                  (LPVOID) reparseBuf,
277                                                  sizeof(buffer),
278                                                  &len,
279                                                  NULL))
280         {
281                 LPSTR           msg;
282
283                 errno = 0;
284                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
285                                           NULL, GetLastError(),
286                                           MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
287                                           (LPSTR) &msg, 0, NULL);
288 #ifndef FRONTEND
289                 ereport(ERROR,
290                                 (errcode_for_file_access(),
291                                  errmsg("could not get junction for \"%s\": %s",
292                                                 path, msg)));
293 #else
294                 fprintf(stderr, _("could not get junction for \"%s\": %s\n"),
295                                 path, msg);
296 #endif
297                 LocalFree(msg);
298                 CloseHandle(h);
299                 errno = EINVAL;
300                 return -1;
301         }
302         CloseHandle(h);
303
304         /* Got it, let's get some results from this */
305         if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
306         {
307                 errno = EINVAL;
308                 return -1;
309         }
310
311         r = WideCharToMultiByte(CP_ACP, 0,
312                                                         reparseBuf->PathBuffer, -1,
313                                                         buf,
314                                                         size,
315                                                         NULL, NULL);
316
317         if (r <= 0)
318         {
319                 errno = EINVAL;
320                 return -1;
321         }
322
323         /*
324          * If the path starts with "\??\", which it will do in most (all?) cases,
325          * strip those out.
326          */
327         if (r > 4 && strncmp(buf, "\\??\\", 4) == 0)
328         {
329                 memmove(buf, buf + 4, strlen(buf + 4) + 1);
330                 r -= 4;
331         }
332         return r;
333 }
334
335 /*
336  * Assumes the file exists, so will return false if it doesn't
337  * (since a nonexistant file is not a junction)
338  */
339 bool
340 pgwin32_is_junction(char *path)
341 {
342         DWORD           attr = GetFileAttributes(path);
343
344         if (attr == INVALID_FILE_ATTRIBUTES)
345         {
346                 _dosmaperr(GetLastError());
347                 return false;
348         }
349         return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT);
350 }
351 #endif   /* defined(WIN32) && !defined(__CYGWIN__) */
352
353
354 #if defined(WIN32) && !defined(__CYGWIN__)
355
356 #undef stat
357
358 /*
359  * The stat() function in win32 is not guaranteed to update the st_size
360  * field when run. So we define our own version that uses the Win32 API
361  * to update this field.
362  */
363 int
364 pgwin32_safestat(const char *path, struct stat * buf)
365 {
366         int                     r;
367         WIN32_FILE_ATTRIBUTE_DATA attr;
368
369         r = stat(path, buf);
370         if (r < 0)
371                 return r;
372
373         if (!GetFileAttributesEx(path, GetFileExInfoStandard, &attr))
374         {
375                 _dosmaperr(GetLastError());
376                 return -1;
377         }
378
379         /*
380          * XXX no support for large files here, but we don't do that in general on
381          * Win32 yet.
382          */
383         buf->st_size = attr.nFileSizeLow;
384
385         return 0;
386 }
387
388 #endif