PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
dirmod.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * dirmod.c
4 * directory handling functions
5 *
6 * Portions Copyright (c) 1996-2025, 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 */
51int
52pgrename(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
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*/
103static bool
104lstat_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 */
118int
119pgunlink(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 */
196typedef 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 */
218int
219pgsymlink(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
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 */
308int
309pgreadlink(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
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:420
#define fprintf(file, fmt, msg)
Definition: cubescan.l:21
int errcode_for_file_access(void)
Definition: elog.c:876
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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:72
#define snprintf
Definition: port.h:238
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:275
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:334
int pgsymlink(const char *oldpath, const char *newpath)
PGDLLIMPORT RtlGetLastNtStatus_t pg_RtlGetLastNtStatus
Definition: win32ntdll.c:20