1 /*-------------------------------------------------------------------------
2  *
3  * dirmod.c
4  *	  directory handling functions
5  *
6  * Portions Copyright (c) 1996-2021, 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  *	Win32 (NT4 and newer).
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 
44 /*
45  *	pgrename
46  */
47 int
pgrename(const char * from,const char * to)48 pgrename(const char *from, const char *to)
49 {
50 	int			loops = 0;
51 
52 	/*
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.
58 	 */
59 #if defined(WIN32) && !defined(__CYGWIN__)
60 	while (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
61 #else
62 	while (rename(from, to) < 0)
63 #endif
64 	{
65 #if defined(WIN32) && !defined(__CYGWIN__)
66 		DWORD		err = GetLastError();
67 
68 		_dosmaperr(err);
69 
70 		/*
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().
77 		 */
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
86 
87 		if (++loops > 100)		/* time out after 10 sec */
88 			return -1;
89 		pg_usleep(100000);		/* us */
90 	}
91 	return 0;
92 }
93 
94 
95 /*
96  *	pgunlink
97  */
98 int
pgunlink(const char * path)99 pgunlink(const char *path)
100 {
101 	int			loops = 0;
102 
103 	/*
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.
109 	 */
110 	while (unlink(path))
111 	{
112 		if (errno != EACCES)
113 			return -1;
114 		if (++loops > 100)		/* time out after 10 sec */
115 			return -1;
116 		pg_usleep(100000);		/* us */
117 	}
118 	return 0;
119 }
120 
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__) */
125 
126 
127 #if defined(WIN32) && !defined(__CYGWIN__)	/* Cygwin has its own symlinks */
128 
129 /*
130  *	pgsymlink support:
131  *
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.
135  */
136 typedef struct
137 {
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;
148 
149 #define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE   \
150 		FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset)
151 
152 
153 /*
154  *	pgsymlink - uses Win32 junction points
155  *
156  *	For reference:	http://www.codeproject.com/KB/winsdk/junctionpoints.aspx
157  */
158 int
pgsymlink(const char * oldpath,const char * newpath)159 pgsymlink(const char *oldpath, const char *newpath)
160 {
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;
167 
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);
172 
173 	if (dirhandle == INVALID_HANDLE_VALUE)
174 		return -1;
175 
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));
181 
182 	while ((p = strchr(p, '/')) != NULL)
183 		*p++ = '\\';
184 
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);
195 
196 	/*
197 	 * FSCTL_SET_REPARSE_POINT is coded differently depending on SDK version;
198 	 * we use our own definition
199 	 */
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))
205 	{
206 		LPSTR		msg;
207 
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);
225 
226 		CloseHandle(dirhandle);
227 		RemoveDirectory(newpath);
228 		return -1;
229 	}
230 
231 	CloseHandle(dirhandle);
232 
233 	return 0;
234 }
235 
236 /*
237  *	pgreadlink - uses Win32 junction points
238  */
239 int
pgreadlink(const char * path,char * buf,size_t size)240 pgreadlink(const char *path, char *buf, size_t size)
241 {
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;
248 
249 	attr = GetFileAttributes(path);
250 	if (attr == INVALID_FILE_ATTRIBUTES)
251 	{
252 		_dosmaperr(GetLastError());
253 		return -1;
254 	}
255 	if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
256 	{
257 		errno = EINVAL;
258 		return -1;
259 	}
260 
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,
267 				   0);
268 	if (h == INVALID_HANDLE_VALUE)
269 	{
270 		_dosmaperr(GetLastError());
271 		return -1;
272 	}
273 
274 	if (!DeviceIoControl(h,
275 						 FSCTL_GET_REPARSE_POINT,
276 						 NULL,
277 						 0,
278 						 (LPVOID) reparseBuf,
279 						 sizeof(buffer),
280 						 &len,
281 						 NULL))
282 	{
283 		LPSTR		msg;
284 
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;
305 	}
306 	CloseHandle(h);
307 
308 	/* Got it, let's get some results from this */
309 	if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
310 	{
311 		errno = EINVAL;
312 		return -1;
313 	}
314 
315 	r = WideCharToMultiByte(CP_ACP, 0,
316 							reparseBuf->PathBuffer, -1,
317 							buf,
318 							size,
319 							NULL, NULL);
320 
321 	if (r <= 0)
322 	{
323 		errno = EINVAL;
324 		return -1;
325 	}
326 
327 	/*
328 	 * If the path starts with "\??\", which it will do in most (all?) cases,
329 	 * strip those out.
330 	 */
331 	if (r > 4 && strncmp(buf, "\\??\\", 4) == 0)
332 	{
333 		memmove(buf, buf + 4, strlen(buf + 4) + 1);
334 		r -= 4;
335 	}
336 	return r;
337 }
338 
339 /*
340  * Assumes the file exists, so will return false if it doesn't
341  * (since a nonexistent file is not a junction)
342  */
343 bool
pgwin32_is_junction(const char * path)344 pgwin32_is_junction(const char *path)
345 {
346 	DWORD		attr = GetFileAttributes(path);
347 
348 	if (attr == INVALID_FILE_ATTRIBUTES)
349 	{
350 		_dosmaperr(GetLastError());
351 		return false;
352 	}
353 	return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT);
354 }
355 #endif							/* defined(WIN32) && !defined(__CYGWIN__) */
356