1 /*-------------------------------------------------------------------------
4 * directory handling functions
6 * Portions Copyright (c) 1996-2021, 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
10 * Win32 (NT4 and newer).
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__)
48 pgrename(const char *from
, const char *to
)
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.
59 #if defined(WIN32) && !defined(__CYGWIN__)
60 while (!MoveFileEx(from
, to
, MOVEFILE_REPLACE_EXISTING
))
62 while (rename(from
, to
) < 0)
65 #if defined(WIN32) && !defined(__CYGWIN__)
66 DWORD err
= GetLastError();
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().
78 if (err
!= ERROR_ACCESS_DENIED
&&
79 err
!= ERROR_SHARING_VIOLATION
&&
80 err
!= ERROR_LOCK_VIOLATION
)
87 if (++loops
> 100) /* time out after 10 sec */
89 pg_usleep(100000); /* us */
99 pgunlink(const char *path
)
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.
114 if (++loops
> 100) /* time out after 10 sec */
116 pg_usleep(100000); /* us */
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__) */
127 #if defined(WIN32) && !defined(__CYGWIN__) /* Cygwin has its own symlinks */
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.
139 WORD ReparseDataLength
;
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
;
149 #define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE \
150 FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset)
154 * pgsymlink - uses Win32 junction points
156 * For reference: http://www.codeproject.com/KB/winsdk/junctionpoints.aspx
159 pgsymlink(const char *oldpath
, const char *newpath
)
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
;
168 CreateDirectory(newpath
, 0);
169 dirhandle
= CreateFile(newpath
, GENERIC_READ
| GENERIC_WRITE
,
171 FILE_FLAG_OPEN_REPARSE_POINT
| FILE_FLAG_BACKUP_SEMANTICS
, 0);
173 if (dirhandle
== INVALID_HANDLE_VALUE
)
176 /* make sure we have an unparsed native win32 path */
177 if (memcmp("\\??\\", oldpath
, 4) != 0)
178 snprintf(nativeTarget
, sizeof(nativeTarget
), "\\??\\%s", oldpath
);
180 strlcpy(nativeTarget
, oldpath
, sizeof(nativeTarget
));
182 while ((p
= strchr(p
, '/')) != NULL
)
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
);
197 * FSCTL_SET_REPARSE_POINT is coded differently depending on SDK version;
198 * we use our own definition
200 if (!DeviceIoControl(dirhandle
,
201 CTL_CODE(FILE_DEVICE_FILE_SYSTEM
, 41, METHOD_BUFFERED
, FILE_ANY_ACCESS
),
203 reparseBuf
->ReparseDataLength
+ REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE
,
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
);
217 (errcode_for_file_access(),
218 errmsg("could not set junction for \"%s\": %s",
219 nativeTarget
, msg
)));
221 fprintf(stderr
, _("could not set junction for \"%s\": %s\n"),
226 CloseHandle(dirhandle
);
227 RemoveDirectory(newpath
);
231 CloseHandle(dirhandle
);
237 * pgreadlink - uses Win32 junction points
240 pgreadlink(const char *path
, char *buf
, size_t size
)
244 char buffer
[MAX_PATH
* sizeof(WCHAR
) + offsetof(REPARSE_JUNCTION_DATA_BUFFER
, PathBuffer
)];
245 REPARSE_JUNCTION_DATA_BUFFER
*reparseBuf
= (REPARSE_JUNCTION_DATA_BUFFER
*) buffer
;
249 attr
= GetFileAttributes(path
);
250 if (attr
== INVALID_FILE_ATTRIBUTES
)
252 _dosmaperr(GetLastError());
255 if ((attr
& FILE_ATTRIBUTE_REPARSE_POINT
) == 0)
263 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
266 FILE_FLAG_OPEN_REPARSE_POINT
| FILE_FLAG_BACKUP_SEMANTICS
,
268 if (h
== INVALID_HANDLE_VALUE
)
270 _dosmaperr(GetLastError());
274 if (!DeviceIoControl(h
,
275 FSCTL_GET_REPARSE_POINT
,
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
);
294 (errcode_for_file_access(),
295 errmsg("could not get junction for \"%s\": %s",
298 fprintf(stderr
, _("could not get junction for \"%s\": %s\n"),
308 /* Got it, let's get some results from this */
309 if (reparseBuf
->ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
315 r
= WideCharToMultiByte(CP_ACP
, 0,
316 reparseBuf
->PathBuffer
, -1,
328 * If the path starts with "\??\", which it will do in most (all?) cases,
331 if (r
> 4 && strncmp(buf
, "\\??\\", 4) == 0)
333 memmove(buf
, buf
+ 4, strlen(buf
+ 4) + 1);
340 * Assumes the file exists, so will return false if it doesn't
341 * (since a nonexistent file is not a junction)
344 pgwin32_is_junction(const char *path
)
346 DWORD attr
= GetFileAttributes(path
);
348 if (attr
== INVALID_FILE_ATTRIBUTES
)
350 _dosmaperr(GetLastError());
353 return ((attr
& FILE_ATTRIBUTE_REPARSE_POINT
) == FILE_ATTRIBUTE_REPARSE_POINT
);
355 #endif /* defined(WIN32) && !defined(__CYGWIN__) */