PostgreSQL Source Code  git master
dirmod.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * dirmod.c
4  * directory handling functions
5  *
6  * Portions Copyright (c) 1996-2024, 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  * Windows.
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 #include "port/win32ntdll.h"
44 #endif
45 
46 #if defined(WIN32) || defined(__CYGWIN__)
47 
48 /*
49  * pgrename
50  */
51 int
52 pgrename(const char *from, const char *to)
53 {
54  int loops = 0;
55 
56  /*
57  * We need to loop because even though PostgreSQL uses flags that allow
58  * rename while the file is open, other applications might have the file
59  * open without those flags. However, we won't wait indefinitely for
60  * someone else to close the file, as the caller might be holding locks
61  * and blocking other backends.
62  */
63 #if defined(WIN32) && !defined(__CYGWIN__)
64  while (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
65 #else
66  while (rename(from, to) < 0)
67 #endif
68  {
69 #if defined(WIN32) && !defined(__CYGWIN__)
70  DWORD err = GetLastError();
71 
72  _dosmaperr(err);
73 
74  /*
75  * Modern NT-based Windows versions return ERROR_SHARING_VIOLATION if
76  * another process has the file open without FILE_SHARE_DELETE.
77  * ERROR_LOCK_VIOLATION has also been seen with some anti-virus
78  * software. This used to check for just ERROR_ACCESS_DENIED, so
79  * presumably you can get that too with some OS versions. We don't
80  * expect real permission errors where we currently use rename().
81  */
82  if (err != ERROR_ACCESS_DENIED &&
83  err != ERROR_SHARING_VIOLATION &&
84  err != ERROR_LOCK_VIOLATION)
85  return -1;
86 #else
87  if (errno != EACCES)
88  return -1;
89 #endif
90 
91  if (++loops > 100) /* time out after 10 sec */
92  return -1;
93  pg_usleep(100000); /* us */
94  }
95  return 0;
96 }
97 
98 /*
99  * Check if _pglstat64()'s reason for failure was STATUS_DELETE_PENDING.
100  * This doesn't apply to Cygwin, which has its own lstat() that would report
101  * the case as EACCES.
102 */
103 static bool
104 lstat_error_was_status_delete_pending(void)
105 {
106  if (errno != ENOENT)
107  return false;
108 #if defined(WIN32) && !defined(__CYGWIN__)
109  if (pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING)
110  return true;
111 #endif
112  return false;
113 }
114 
115 /*
116  * pgunlink
117  */
118 int
119 pgunlink(const char *path)
120 {
121  bool is_lnk;
122  int loops = 0;
123  struct stat st;
124 
125  /*
126  * This function might be called for a regular file or for a junction
127  * point (which we use to emulate symlinks). The latter must be unlinked
128  * with rmdir() on Windows. Before we worry about any of that, let's see
129  * if we can unlink directly, since that's expected to be the most common
130  * case.
131  */
132  if (unlink(path) == 0)
133  return 0;
134  if (errno != EACCES)
135  return -1;
136 
137  /*
138  * EACCES is reported for many reasons including unlink() of a junction
139  * point. Check if that's the case so we can redirect to rmdir().
140  *
141  * Note that by checking only once, we can't cope with a path that changes
142  * from regular file to junction point underneath us while we're retrying
143  * due to sharing violations, but that seems unlikely. We could perhaps
144  * prevent that by holding a file handle ourselves across the lstat() and
145  * the retry loop, but that seems like over-engineering for now.
146  *
147  * In the special case of a STATUS_DELETE_PENDING error (file already
148  * unlinked, but someone still has it open), we don't want to report
149  * ENOENT to the caller immediately, because rmdir(parent) would probably
150  * fail. We want to wait until the file truly goes away so that simple
151  * recursive directory unlink algorithms work.
152  */
153  if (lstat(path, &st) < 0)
154  {
155  if (lstat_error_was_status_delete_pending())
156  is_lnk = false;
157  else
158  return -1;
159  }
160  else
161  is_lnk = S_ISLNK(st.st_mode);
162 
163  /*
164  * We need to loop because even though PostgreSQL uses flags that allow
165  * unlink while the file is open, other applications might have the file
166  * open without those flags. However, we won't wait indefinitely for
167  * someone else to close the file, as the caller might be holding locks
168  * and blocking other backends.
169  */
170  while ((is_lnk ? rmdir(path) : unlink(path)) < 0)
171  {
172  if (errno != EACCES)
173  return -1;
174  if (++loops > 100) /* time out after 10 sec */
175  return -1;
176  pg_usleep(100000); /* us */
177  }
178  return 0;
179 }
180 
181 /* We undefined these above; now redefine for possible use below */
182 #define rename(from, to) pgrename(from, to)
183 #define unlink(path) pgunlink(path)
184 #endif /* defined(WIN32) || defined(__CYGWIN__) */
185 
186 
187 #if defined(WIN32) && !defined(__CYGWIN__) /* Cygwin has its own symlinks */
188 
189 /*
190  * pgsymlink support:
191  *
192  * This struct is a replacement for REPARSE_DATA_BUFFER which is defined in VC6 winnt.h
193  * but omitted in later SDK functions.
194  * We only need the SymbolicLinkReparseBuffer part of the original struct's union.
195  */
196 typedef struct
197 {
198  DWORD ReparseTag;
199  WORD ReparseDataLength;
200  WORD Reserved;
201  /* SymbolicLinkReparseBuffer */
202  WORD SubstituteNameOffset;
203  WORD SubstituteNameLength;
204  WORD PrintNameOffset;
205  WORD PrintNameLength;
206  WCHAR PathBuffer[FLEXIBLE_ARRAY_MEMBER];
207 } REPARSE_JUNCTION_DATA_BUFFER;
208 
209 #define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE \
210  FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset)
211 
212 
213 /*
214  * pgsymlink - uses Win32 junction points
215  *
216  * For reference: http://www.codeproject.com/KB/winsdk/junctionpoints.aspx
217  */
218 int
219 pgsymlink(const char *oldpath, const char *newpath)
220 {
221  HANDLE dirhandle;
222  DWORD len;
223  char buffer[MAX_PATH * sizeof(WCHAR) + offsetof(REPARSE_JUNCTION_DATA_BUFFER, PathBuffer)];
224  char nativeTarget[MAX_PATH];
225  char *p = nativeTarget;
226  REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
227 
228  CreateDirectory(newpath, 0);
229  dirhandle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE,
230  0, 0, OPEN_EXISTING,
231  FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
232 
233  if (dirhandle == INVALID_HANDLE_VALUE)
234  {
235  _dosmaperr(GetLastError());
236  return -1;
237  }
238 
239  /* make sure we have an unparsed native win32 path */
240  if (memcmp("\\??\\", oldpath, 4) != 0)
241  snprintf(nativeTarget, sizeof(nativeTarget), "\\??\\%s", oldpath);
242  else
243  strlcpy(nativeTarget, oldpath, sizeof(nativeTarget));
244 
245  while ((p = strchr(p, '/')) != NULL)
246  *p++ = '\\';
247 
248  len = strlen(nativeTarget) * sizeof(WCHAR);
249  reparseBuf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
250  reparseBuf->ReparseDataLength = len + 12;
251  reparseBuf->Reserved = 0;
252  reparseBuf->SubstituteNameOffset = 0;
253  reparseBuf->SubstituteNameLength = len;
254  reparseBuf->PrintNameOffset = len + sizeof(WCHAR);
255  reparseBuf->PrintNameLength = 0;
256  MultiByteToWideChar(CP_ACP, 0, nativeTarget, -1,
257  reparseBuf->PathBuffer, MAX_PATH);
258 
259  /*
260  * FSCTL_SET_REPARSE_POINT is coded differently depending on SDK version;
261  * we use our own definition
262  */
263  if (!DeviceIoControl(dirhandle,
264  CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_ANY_ACCESS),
265  reparseBuf,
266  reparseBuf->ReparseDataLength + REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE,
267  0, 0, &len, 0))
268  {
269  LPSTR msg;
270  int save_errno;
271 
272  _dosmaperr(GetLastError());
273  save_errno = errno;
274 
275  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
276  FORMAT_MESSAGE_IGNORE_INSERTS |
277  FORMAT_MESSAGE_FROM_SYSTEM,
278  NULL, GetLastError(),
279  MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
280  (LPSTR) &msg, 0, NULL);
281 #ifndef FRONTEND
282  ereport(ERROR,
284  errmsg("could not set junction for \"%s\": %s",
285  nativeTarget, msg)));
286 #else
287  fprintf(stderr, _("could not set junction for \"%s\": %s\n"),
288  nativeTarget, msg);
289 #endif
290  LocalFree(msg);
291 
292  CloseHandle(dirhandle);
293  RemoveDirectory(newpath);
294 
295  errno = save_errno;
296 
297  return -1;
298  }
299 
300  CloseHandle(dirhandle);
301 
302  return 0;
303 }
304 
305 /*
306  * pgreadlink - uses Win32 junction points
307  */
308 int
309 pgreadlink(const char *path, char *buf, size_t size)
310 {
311  DWORD attr;
312  HANDLE h;
313  char buffer[MAX_PATH * sizeof(WCHAR) + offsetof(REPARSE_JUNCTION_DATA_BUFFER, PathBuffer)];
314  REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
315  DWORD len;
316  int r;
317 
318  attr = GetFileAttributes(path);
319  if (attr == INVALID_FILE_ATTRIBUTES)
320  {
321  _dosmaperr(GetLastError());
322  return -1;
323  }
324  if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
325  {
326  errno = EINVAL;
327  return -1;
328  }
329 
330  h = CreateFile(path,
331  GENERIC_READ,
332  FILE_SHARE_READ | FILE_SHARE_WRITE,
333  NULL,
334  OPEN_EXISTING,
335  FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
336  0);
337  if (h == INVALID_HANDLE_VALUE)
338  {
339  _dosmaperr(GetLastError());
340  return -1;
341  }
342 
343  if (!DeviceIoControl(h,
344  FSCTL_GET_REPARSE_POINT,
345  NULL,
346  0,
347  (LPVOID) reparseBuf,
348  sizeof(buffer),
349  &len,
350  NULL))
351  {
352  LPSTR msg;
353 
354  errno = 0;
355  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
356  FORMAT_MESSAGE_IGNORE_INSERTS |
357  FORMAT_MESSAGE_FROM_SYSTEM,
358  NULL, GetLastError(),
359  MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
360  (LPSTR) &msg, 0, NULL);
361 #ifndef FRONTEND
362  ereport(ERROR,
364  errmsg("could not get junction for \"%s\": %s",
365  path, msg)));
366 #else
367  fprintf(stderr, _("could not get junction for \"%s\": %s\n"),
368  path, msg);
369 #endif
370  LocalFree(msg);
371  CloseHandle(h);
372  errno = EINVAL;
373  return -1;
374  }
375  CloseHandle(h);
376 
377  /* Got it, let's get some results from this */
378  if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
379  {
380  errno = EINVAL;
381  return -1;
382  }
383 
384  r = WideCharToMultiByte(CP_ACP, 0,
385  reparseBuf->PathBuffer, -1,
386  buf,
387  size,
388  NULL, NULL);
389 
390  if (r <= 0)
391  {
392  errno = EINVAL;
393  return -1;
394  }
395 
396  /* r includes the null terminator */
397  r -= 1;
398 
399  /*
400  * If the path starts with "\??\" followed by a "drive absolute" path
401  * (known to Windows APIs as RtlPathTypeDriveAbsolute), then strip that
402  * prefix. This undoes some of the transformation performed by
403  * pgsymlink(), to get back to a format that users are used to seeing. We
404  * don't know how to transform other path types that might be encountered
405  * outside PGDATA, so we just return them directly.
406  */
407  if (r >= 7 &&
408  buf[0] == '\\' &&
409  buf[1] == '?' &&
410  buf[2] == '?' &&
411  buf[3] == '\\' &&
412  isalpha(buf[4]) &&
413  buf[5] == ':' &&
414  buf[6] == '\\')
415  {
416  memmove(buf, buf + 4, strlen(buf + 4) + 1);
417  r -= 4;
418  }
419  return r;
420 }
421 
422 #endif /* defined(WIN32) && !defined(__CYGWIN__) */
#define FLEXIBLE_ARRAY_MEMBER
Definition: c.h:398
int errcode_for_file_access(void)
Definition: elog.c:882
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define _(x)
Definition: elog.c:90
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
void err(int eval, const char *fmt,...)
Definition: err.c:43
const void size_t len
static char * buf
Definition: pg_test_fsync.c:73
#define snprintf
Definition: port.h:238
#define fprintf
Definition: port.h:242
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
void pg_usleep(long microsec)
Definition: signal.c:53
static pg_noinline void Size size
Definition: slab.c:607
#define lstat(path, sb)
Definition: win32_port.h:285
void _dosmaperr(unsigned long)
Definition: win32error.c:177
int pgreadlink(const char *path, char *buf, size_t size)
#define S_ISLNK(m)
Definition: win32_port.h:344
int pgsymlink(const char *oldpath, const char *newpath)
PGDLLIMPORT RtlGetLastNtStatus_t pg_RtlGetLastNtStatus
Definition: win32ntdll.c:20