1 /*-------------------------------------------------------------------------
6 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * These are replacement versions of unlink and rename that work on
10 * Win32 (NT, Win2k, XP). replace() doesn't work on Win95/98/Me.
13 * $PostgreSQL: pgsql/src/port/dirmod.c,v 1.43 2006/07/18 22:36:46 tgl Exp $
15 *-------------------------------------------------------------------------
21 #include "postgres_fe.h"
24 /* Don't modify declarations in system headers */
25 #if defined(WIN32) || defined(__CYGWIN__)
34 #if defined(WIN32) || defined(__CYGWIN__)
39 #include <w32api/winioctl.h>
47 * On Windows, call non-macro versions of palloc; we can't reference
48 * CurrentMemoryContext in this file because of DLLIMPORT conflict.
50 #if defined(WIN32) || defined(__CYGWIN__)
53 #define palloc(sz) pgport_palloc(sz)
54 #define pstrdup(str) pgport_pstrdup(str)
59 * In frontend, fake palloc behavior with these
63 #define palloc(sz) fe_palloc(sz)
64 #define pstrdup(str) fe_pstrdup(str)
65 #define repalloc(pointer,sz) fe_repalloc(pointer,sz)
66 #define pfree(pointer) free(pointer)
73 if ((res = malloc(size)) == NULL)
75 fprintf(stderr, _("out of memory\n"));
82 fe_pstrdup(const char *string)
86 if ((res = strdup(string)) == NULL)
88 fprintf(stderr, _("out of memory\n"));
95 fe_repalloc(void *pointer, Size size)
99 if ((res = realloc(pointer, size)) == NULL)
101 fprintf(stderr, _("out of memory\n"));
106 #endif /* FRONTEND */
109 #if defined(WIN32) || defined(__CYGWIN__)
115 pgrename(const char *from, const char *to)
120 * We need these loops because even though PostgreSQL uses flags that
121 * allow rename while the file is open, other applications might have
122 * these files open without those flags.
124 #if defined(WIN32) && !defined(__CYGWIN__)
125 while (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
128 while (rename(from, to) < 0)
131 #if defined(WIN32) && !defined(__CYGWIN__)
132 if (GetLastError() != ERROR_ACCESS_DENIED)
139 pg_usleep(100000); /* us */
142 elog(LOG, "could not rename file \"%s\" to \"%s\", continuing to try",
145 fprintf(stderr, _("could not rename file \"%s\" to \"%s\", continuing to try\n"),
153 elog(LOG, "completed rename of file \"%s\" to \"%s\"", from, to);
155 fprintf(stderr, _("completed rename of file \"%s\" to \"%s\"\n"), from, to);
165 pgunlink(const char *path)
170 * We need these loops because even though PostgreSQL uses flags that
171 * allow unlink while the file is open, other applications might have
172 * these files open without those flags.
179 pg_usleep(100000); /* us */
182 elog(LOG, "could not remove file \"%s\", continuing to try",
185 fprintf(stderr, _("could not remove file \"%s\", continuing to try\n"),
193 elog(LOG, "completed removal of file \"%s\"", path);
195 fprintf(stderr, _("completed removal of file \"%s\"\n"), path);
201 #ifdef WIN32 /* Cygwin has its own symlinks */
206 * This struct is a replacement for REPARSE_DATA_BUFFER which is defined in VC6 winnt.h
207 * but omitted in later SDK functions.
208 * We only need the SymbolicLinkReparseBuffer part of the original struct's union.
213 WORD ReparseDataLength;
215 /* SymbolicLinkReparseBuffer */
216 WORD SubstituteNameOffset;
217 WORD SubstituteNameLength;
218 WORD PrintNameOffset;
219 WORD PrintNameLength;
221 } REPARSE_JUNCTION_DATA_BUFFER;
223 #define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE \
224 FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset)
228 * pgsymlink - uses Win32 junction points
230 * For reference: http://www.codeproject.com/w2k/junctionpoints.asp
233 pgsymlink(const char *oldpath, const char *newpath)
237 char buffer[MAX_PATH * sizeof(WCHAR) + sizeof(REPARSE_JUNCTION_DATA_BUFFER)];
238 char nativeTarget[MAX_PATH];
239 char *p = nativeTarget;
240 REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
242 CreateDirectory(newpath, 0);
243 dirhandle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE,
245 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
247 if (dirhandle == INVALID_HANDLE_VALUE)
250 /* make sure we have an unparsed native win32 path */
251 if (memcmp("\\??\\", oldpath, 4))
252 sprintf(nativeTarget, "\\??\\%s", oldpath);
254 strcpy(nativeTarget, oldpath);
256 while ((p = strchr(p, '/')) != 0)
259 len = strlen(nativeTarget) * sizeof(WCHAR);
260 reparseBuf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
261 reparseBuf->ReparseDataLength = len + 12;
262 reparseBuf->Reserved = 0;
263 reparseBuf->SubstituteNameOffset = 0;
264 reparseBuf->SubstituteNameLength = len;
265 reparseBuf->PrintNameOffset = len + sizeof(WCHAR);
266 reparseBuf->PrintNameLength = 0;
267 MultiByteToWideChar(CP_ACP, 0, nativeTarget, -1,
268 reparseBuf->PathBuffer, MAX_PATH);
271 * FSCTL_SET_REPARSE_POINT is coded differently depending on SDK version;
272 * we use our own definition
274 if (!DeviceIoControl(dirhandle,
275 CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_ANY_ACCESS),
277 reparseBuf->ReparseDataLength + REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE,
283 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
284 NULL, GetLastError(),
285 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
286 (LPSTR) & msg, 0, NULL);
289 (errcode_for_file_access(),
290 errmsg("could not set junction for \"%s\": %s",
291 nativeTarget, msg)));
293 fprintf(stderr, _("could not set junction for \"%s\": %s\n"),
298 CloseHandle(dirhandle);
299 RemoveDirectory(newpath);
303 CloseHandle(dirhandle);
308 #endif /* defined(WIN32) || defined(__CYGWIN__) */
311 /* We undefined this above, so we redefine it */
312 #if defined(WIN32) || defined(__CYGWIN__)
313 #define unlink(path) pgunlink(path)
320 * return a list of the names of objects in the argument directory
329 int fnsize = 200; /* enough for many small dbs */
335 elog(WARNING, "could not open directory \"%s\": %m", path);
337 fprintf(stderr, _("could not open directory \"%s\": %s\n"),
338 path, strerror(errno));
343 filenames = (char **) palloc(fnsize * sizeof(char *));
346 while ((file = readdir(dir)) != NULL)
348 if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0)
350 if (numnames + 1 >= fnsize)
353 filenames = (char **) repalloc(filenames,
354 fnsize * sizeof(char *));
356 filenames[numnames++] = pstrdup(file->d_name);
363 * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
366 if (GetLastError() == ERROR_NO_MORE_FILES)
372 elog(WARNING, "could not read directory \"%s\": %m", path);
374 fprintf(stderr, _("could not read directory \"%s\": %s\n"),
375 path, strerror(errno));
379 filenames[numnames] = NULL;
390 * deallocate memory used for filenames
393 fnames_cleanup(char **filenames)
397 for (fn = filenames; *fn; fn++)
407 * Delete a directory tree recursively.
408 * Assumes path points to a valid directory.
409 * Deletes everything under path.
410 * If rmtopdir is true deletes the directory too.
413 rmtree(char *path, bool rmtopdir)
415 char pathbuf[MAXPGPATH];
422 * we copy all the names out of the directory before we start modifying
425 filenames = fnames(path);
427 if (filenames == NULL)
430 /* now we have the names we can start removing things */
433 for (filename = filenames; *filename; filename++)
435 snprintf(filepath, MAXPGPATH, "%s/%s", path, *filename);
437 if (lstat(filepath, &statbuf) != 0)
438 goto report_and_fail;
440 if (S_ISDIR(statbuf.st_mode))
442 /* call ourselves recursively for a directory */
443 if (!rmtree(filepath, true))
445 /* we already reported the error */
446 fnames_cleanup(filenames);
452 if (unlink(filepath) != 0)
453 goto report_and_fail;
460 if (rmdir(filepath) != 0)
461 goto report_and_fail;
464 fnames_cleanup(filenames);
470 elog(WARNING, "could not remove file or directory \"%s\": %m", filepath);
472 fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
473 filepath, strerror(errno));
475 fnames_cleanup(filenames);