PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
dirmod.c
Go to the documentation of this file.
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
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 |
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,
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
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,
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
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
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
380  * yet. This can happen when some process with FILE_SHARE_DELETE
381  * has it open and it will be fully removed once that handle
382  * is closed. Meanwhile, we can't open it, so indicate that
383  * the file just 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
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
void pg_usleep(long microsec)
Definition: signal.c:53
#define ERROR
Definition: elog.h:43
static char * buf
Definition: pg_test_fsync.c:66
#define memmove(d, s, c)
Definition: c.h:1058
int errcode_for_file_access(void)
Definition: elog.c:598
int unlink(const char *filename)
#define ereport(elevel, rest)
Definition: elog.h:122
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
#define NULL
Definition: c.h:229
void _dosmaperr(unsigned long)
Definition: win32error.c:171
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:207
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define _(x)
Definition: elog.c:84
#define offsetof(type, field)
Definition: c.h:555