Consistently use "superuser" instead of "super user"
[pgsql.git] / src / port / dirmod.c
blob763bc5f915a670203ab19bbf2976a533d46e38aa
1 /*-------------------------------------------------------------------------
3 * dirmod.c
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).
12 * IDENTIFICATION
13 * src/port/dirmod.c
15 *-------------------------------------------------------------------------
18 #ifndef FRONTEND
19 #include "postgres.h"
20 #else
21 #include "postgres_fe.h"
22 #endif
24 /* Don't modify declarations in system headers */
25 #if defined(WIN32) || defined(__CYGWIN__)
26 #undef rename
27 #undef unlink
28 #endif
30 #include <unistd.h>
31 #include <sys/stat.h>
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
42 #if defined(WIN32) || defined(__CYGWIN__)
45 * pgrename
47 int
48 pgrename(const char *from, const char *to)
50 int loops = 0;
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))
61 #else
62 while (rename(from, to) < 0)
63 #endif
65 #if defined(WIN32) && !defined(__CYGWIN__)
66 DWORD err = GetLastError();
68 _dosmaperr(err);
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)
81 return -1;
82 #else
83 if (errno != EACCES)
84 return -1;
85 #endif
87 if (++loops > 100) /* time out after 10 sec */
88 return -1;
89 pg_usleep(100000); /* us */
91 return 0;
96 * pgunlink
98 int
99 pgunlink(const char *path)
101 int loops = 0;
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.
110 while (unlink(path))
112 if (errno != EACCES)
113 return -1;
114 if (++loops > 100) /* time out after 10 sec */
115 return -1;
116 pg_usleep(100000); /* us */
118 return 0;
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 */
130 * pgsymlink support:
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.
136 typedef struct
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;
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)
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;
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);
173 if (dirhandle == INVALID_HANDLE_VALUE)
174 return -1;
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));
182 while ((p = strchr(p, '/')) != NULL)
183 *p++ = '\\';
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),
202 reparseBuf,
203 reparseBuf->ReparseDataLength + REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE,
204 0, 0, &len, 0))
206 LPSTR msg;
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,
217 (errcode_for_file_access(),
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);
226 CloseHandle(dirhandle);
227 RemoveDirectory(newpath);
228 return -1;
231 CloseHandle(dirhandle);
233 return 0;
237 * pgreadlink - uses Win32 junction points
240 pgreadlink(const char *path, char *buf, size_t size)
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;
249 attr = GetFileAttributes(path);
250 if (attr == INVALID_FILE_ATTRIBUTES)
252 _dosmaperr(GetLastError());
253 return -1;
255 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
257 errno = EINVAL;
258 return -1;
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,
268 if (h == INVALID_HANDLE_VALUE)
270 _dosmaperr(GetLastError());
271 return -1;
274 if (!DeviceIoControl(h,
275 FSCTL_GET_REPARSE_POINT,
276 NULL,
278 (LPVOID) reparseBuf,
279 sizeof(buffer),
280 &len,
281 NULL))
283 LPSTR msg;
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,
294 (errcode_for_file_access(),
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;
306 CloseHandle(h);
308 /* Got it, let's get some results from this */
309 if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
311 errno = EINVAL;
312 return -1;
315 r = WideCharToMultiByte(CP_ACP, 0,
316 reparseBuf->PathBuffer, -1,
317 buf,
318 size,
319 NULL, NULL);
321 if (r <= 0)
323 errno = EINVAL;
324 return -1;
328 * If the path starts with "\??\", which it will do in most (all?) cases,
329 * strip those out.
331 if (r > 4 && strncmp(buf, "\\??\\", 4) == 0)
333 memmove(buf, buf + 4, strlen(buf + 4) + 1);
334 r -= 4;
336 return r;
340 * Assumes the file exists, so will return false if it doesn't
341 * (since a nonexistent file is not a junction)
343 bool
344 pgwin32_is_junction(const char *path)
346 DWORD attr = GetFileAttributes(path);
348 if (attr == INVALID_FILE_ATTRIBUTES)
350 _dosmaperr(GetLastError());
351 return false;
353 return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT);
355 #endif /* defined(WIN32) && !defined(__CYGWIN__) */