1 /*-------------------------------------------------------------------------
4 * directory handling functions
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * This includes replacement versions of functions that work on
15 *-------------------------------------------------------------------------
21 #include "postgres_fe.h"
24 /* Don't modify declarations in system headers */
25 #if defined(WIN32) || defined(__CYGWIN__)
33 #if defined(WIN32) || defined(__CYGWIN__)
38 #include <w32api/winioctl.h>
42 #if defined(WIN32) && !defined(__CYGWIN__)
43 #include "port/win32ntdll.h"
46 #if defined(WIN32) || defined(__CYGWIN__)
52 pgrename(const char *from
, const char *to
)
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.
63 #if defined(WIN32) && !defined(__CYGWIN__)
64 while (!MoveFileEx(from
, to
, MOVEFILE_REPLACE_EXISTING
))
66 while (rename(from
, to
) < 0)
69 #if defined(WIN32) && !defined(__CYGWIN__)
70 DWORD err
= GetLastError();
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().
82 if (err
!= ERROR_ACCESS_DENIED
&&
83 err
!= ERROR_SHARING_VIOLATION
&&
84 err
!= ERROR_LOCK_VIOLATION
)
91 if (++loops
> 100) /* time out after 10 sec */
93 pg_usleep(100000); /* us */
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.
104 lstat_error_was_status_delete_pending(void)
108 #if defined(WIN32) && !defined(__CYGWIN__)
109 if (pg_RtlGetLastNtStatus() == STATUS_DELETE_PENDING
)
119 pgunlink(const char *path
)
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
132 if (unlink(path
) == 0)
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().
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.
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.
153 if (lstat(path
, &st
) < 0)
155 if (lstat_error_was_status_delete_pending())
161 is_lnk
= S_ISLNK(st
.st_mode
);
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.
170 while ((is_lnk
? rmdir(path
) : unlink(path
)) < 0)
174 if (++loops
> 100) /* time out after 10 sec */
176 pg_usleep(100000); /* us */
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__) */
187 #if defined(WIN32) && !defined(__CYGWIN__) /* Cygwin has its own symlinks */
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.
199 WORD ReparseDataLength
;
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
;
209 #define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE \
210 FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset)
214 * pgsymlink - uses Win32 junction points
216 * For reference: http://www.codeproject.com/KB/winsdk/junctionpoints.aspx
219 pgsymlink(const char *oldpath
, const char *newpath
)
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
;
228 CreateDirectory(newpath
, 0);
229 dirhandle
= CreateFile(newpath
, GENERIC_READ
| GENERIC_WRITE
,
231 FILE_FLAG_OPEN_REPARSE_POINT
| FILE_FLAG_BACKUP_SEMANTICS
, 0);
233 if (dirhandle
== INVALID_HANDLE_VALUE
)
235 _dosmaperr(GetLastError());
239 /* make sure we have an unparsed native win32 path */
240 if (memcmp("\\??\\", oldpath
, 4) != 0)
241 snprintf(nativeTarget
, sizeof(nativeTarget
), "\\??\\%s", oldpath
);
243 strlcpy(nativeTarget
, oldpath
, sizeof(nativeTarget
));
245 while ((p
= strchr(p
, '/')) != NULL
)
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
);
260 * FSCTL_SET_REPARSE_POINT is coded differently depending on SDK version;
261 * we use our own definition
263 if (!DeviceIoControl(dirhandle
,
264 CTL_CODE(FILE_DEVICE_FILE_SYSTEM
, 41, METHOD_BUFFERED
, FILE_ANY_ACCESS
),
266 reparseBuf
->ReparseDataLength
+ REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE
,
272 _dosmaperr(GetLastError());
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
);
283 (errcode_for_file_access(),
284 errmsg("could not set junction for \"%s\": %s",
285 nativeTarget
, msg
)));
287 fprintf(stderr
, _("could not set junction for \"%s\": %s\n"),
292 CloseHandle(dirhandle
);
293 RemoveDirectory(newpath
);
300 CloseHandle(dirhandle
);
306 * pgreadlink - uses Win32 junction points
309 pgreadlink(const char *path
, char *buf
, size_t size
)
313 char buffer
[MAX_PATH
* sizeof(WCHAR
) + offsetof(REPARSE_JUNCTION_DATA_BUFFER
, PathBuffer
)];
314 REPARSE_JUNCTION_DATA_BUFFER
*reparseBuf
= (REPARSE_JUNCTION_DATA_BUFFER
*) buffer
;
318 attr
= GetFileAttributes(path
);
319 if (attr
== INVALID_FILE_ATTRIBUTES
)
321 _dosmaperr(GetLastError());
324 if ((attr
& FILE_ATTRIBUTE_REPARSE_POINT
) == 0)
332 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
335 FILE_FLAG_OPEN_REPARSE_POINT
| FILE_FLAG_BACKUP_SEMANTICS
,
337 if (h
== INVALID_HANDLE_VALUE
)
339 _dosmaperr(GetLastError());
343 if (!DeviceIoControl(h
,
344 FSCTL_GET_REPARSE_POINT
,
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
);
363 (errcode_for_file_access(),
364 errmsg("could not get junction for \"%s\": %s",
367 fprintf(stderr
, _("could not get junction for \"%s\": %s\n"),
377 /* Got it, let's get some results from this */
378 if (reparseBuf
->ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
384 r
= WideCharToMultiByte(CP_ACP
, 0,
385 reparseBuf
->PathBuffer
, -1,
396 /* r includes the null terminator */
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.
416 memmove(buf
, buf
+ 4, strlen(buf
+ 4) + 1);
422 #endif /* defined(WIN32) && !defined(__CYGWIN__) */