]> granicus.if.org Git - postgresql/blob - src/port/dirmod.c
Revise psql pattern-matching switches as per discussion. The rule is now
[postgresql] / src / port / dirmod.c
1 /*-------------------------------------------------------------------------
2  *
3  * dirmod.c
4  *        rename/unlink()
5  *
6  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
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.
11  *
12  * IDENTIFICATION
13  *        $PostgreSQL: pgsql/src/port/dirmod.c,v 1.43 2006/07/18 22:36:46 tgl Exp $
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 <dirent.h>
32 #include <sys/stat.h>
33
34 #if defined(WIN32) || defined(__CYGWIN__)
35 #ifndef __CYGWIN__
36 #include <winioctl.h>
37 #else
38 #include <windows.h>
39 #include <w32api/winioctl.h>
40 #endif
41 #endif
42
43
44 #ifndef FRONTEND
45
46 /*
47  *      On Windows, call non-macro versions of palloc; we can't reference
48  *      CurrentMemoryContext in this file because of DLLIMPORT conflict.
49  */
50 #if defined(WIN32) || defined(__CYGWIN__)
51 #undef palloc
52 #undef pstrdup
53 #define palloc(sz)              pgport_palloc(sz)
54 #define pstrdup(str)    pgport_pstrdup(str)
55 #endif
56 #else                                                   /* FRONTEND */
57
58 /*
59  *      In frontend, fake palloc behavior with these
60  */
61 #undef palloc
62 #undef pstrdup
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)
67
68 static void *
69 fe_palloc(Size size)
70 {
71         void       *res;
72
73         if ((res = malloc(size)) == NULL)
74         {
75                 fprintf(stderr, _("out of memory\n"));
76                 exit(1);
77         }
78         return res;
79 }
80
81 static char *
82 fe_pstrdup(const char *string)
83 {
84         char       *res;
85
86         if ((res = strdup(string)) == NULL)
87         {
88                 fprintf(stderr, _("out of memory\n"));
89                 exit(1);
90         }
91         return res;
92 }
93
94 static void *
95 fe_repalloc(void *pointer, Size size)
96 {
97         void       *res;
98
99         if ((res = realloc(pointer, size)) == NULL)
100         {
101                 fprintf(stderr, _("out of memory\n"));
102                 exit(1);
103         }
104         return res;
105 }
106 #endif   /* FRONTEND */
107
108
109 #if defined(WIN32) || defined(__CYGWIN__)
110
111 /*
112  *      pgrename
113  */
114 int
115 pgrename(const char *from, const char *to)
116 {
117         int                     loops = 0;
118
119         /*
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.
123          */
124 #if defined(WIN32) && !defined(__CYGWIN__)
125         while (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
126 #endif
127 #ifdef __CYGWIN__
128                 while (rename(from, to) < 0)
129 #endif
130                 {
131 #if defined(WIN32) && !defined(__CYGWIN__)
132                         if (GetLastError() != ERROR_ACCESS_DENIED)
133 #endif
134 #ifdef __CYGWIN__
135                                 if (errno != EACCES)
136 #endif
137                                         /* set errno? */
138                                         return -1;
139                         pg_usleep(100000);      /* us */
140                         if (loops == 30)
141 #ifndef FRONTEND
142                                 elog(LOG, "could not rename file \"%s\" to \"%s\", continuing to try",
143                                          from, to);
144 #else
145                                 fprintf(stderr, _("could not rename file \"%s\" to \"%s\", continuing to try\n"),
146                                                 from, to);
147 #endif
148                         loops++;
149                 }
150
151         if (loops > 30)
152 #ifndef FRONTEND
153                 elog(LOG, "completed rename of file \"%s\" to \"%s\"", from, to);
154 #else
155                 fprintf(stderr, _("completed rename of file \"%s\" to \"%s\"\n"), from, to);
156 #endif
157         return 0;
158 }
159
160
161 /*
162  *      pgunlink
163  */
164 int
165 pgunlink(const char *path)
166 {
167         int                     loops = 0;
168
169         /*
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.
173          */
174         while (unlink(path))
175         {
176                 if (errno != EACCES)
177                         /* set errno? */
178                         return -1;
179                 pg_usleep(100000);              /* us */
180                 if (loops == 30)
181 #ifndef FRONTEND
182                         elog(LOG, "could not remove file \"%s\", continuing to try",
183                                  path);
184 #else
185                         fprintf(stderr, _("could not remove file \"%s\", continuing to try\n"),
186                                         path);
187 #endif
188                 loops++;
189         }
190
191         if (loops > 30)
192 #ifndef FRONTEND
193                 elog(LOG, "completed removal of file \"%s\"", path);
194 #else
195                 fprintf(stderr, _("completed removal of file \"%s\"\n"), path);
196 #endif
197         return 0;
198 }
199
200
201 #ifdef WIN32                                    /* Cygwin has its own symlinks */
202
203 /*
204  *      pgsymlink support:
205  *
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.
209  */
210 typedef struct
211 {
212         DWORD           ReparseTag;
213         WORD            ReparseDataLength;
214         WORD            Reserved;
215         /* SymbolicLinkReparseBuffer */
216         WORD            SubstituteNameOffset;
217         WORD            SubstituteNameLength;
218         WORD            PrintNameOffset;
219         WORD            PrintNameLength;
220         WCHAR           PathBuffer[1];
221 }       REPARSE_JUNCTION_DATA_BUFFER;
222
223 #define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE   \
224                 FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset)
225
226
227 /*
228  *      pgsymlink - uses Win32 junction points
229  *
230  *      For reference:  http://www.codeproject.com/w2k/junctionpoints.asp
231  */
232 int
233 pgsymlink(const char *oldpath, const char *newpath)
234 {
235         HANDLE          dirhandle;
236         DWORD           len;
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;
241
242         CreateDirectory(newpath, 0);
243         dirhandle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE,
244                                                    0, 0, OPEN_EXISTING,
245                            FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
246
247         if (dirhandle == INVALID_HANDLE_VALUE)
248                 return -1;
249
250         /* make sure we have an unparsed native win32 path */
251         if (memcmp("\\??\\", oldpath, 4))
252                 sprintf(nativeTarget, "\\??\\%s", oldpath);
253         else
254                 strcpy(nativeTarget, oldpath);
255
256         while ((p = strchr(p, '/')) != 0)
257                 *p++ = '\\';
258
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);
269
270         /*
271          * FSCTL_SET_REPARSE_POINT is coded differently depending on SDK version;
272          * we use our own definition
273          */
274         if (!DeviceIoControl(dirhandle,
275          CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_ANY_ACCESS),
276                                                  reparseBuf,
277         reparseBuf->ReparseDataLength + REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE,
278                                                  0, 0, &len, 0))
279         {
280                 LPSTR           msg;
281
282                 errno = 0;
283                 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
284                                           NULL, GetLastError(),
285                                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
286                                           (LPSTR) & msg, 0, NULL);
287 #ifndef FRONTEND
288                 ereport(ERROR,
289                                 (errcode_for_file_access(),
290                                  errmsg("could not set junction for \"%s\": %s",
291                                                 nativeTarget, msg)));
292 #else
293                 fprintf(stderr, _("could not set junction for \"%s\": %s\n"),
294                                 nativeTarget, msg);
295 #endif
296                 LocalFree(msg);
297
298                 CloseHandle(dirhandle);
299                 RemoveDirectory(newpath);
300                 return -1;
301         }
302
303         CloseHandle(dirhandle);
304
305         return 0;
306 }
307 #endif   /* WIN32 */
308 #endif   /* defined(WIN32) || defined(__CYGWIN__) */
309
310
311 /* We undefined this above, so we redefine it */
312 #if defined(WIN32) || defined(__CYGWIN__)
313 #define unlink(path)    pgunlink(path)
314 #endif
315
316
317 /*
318  * fnames
319  *
320  * return a list of the names of objects in the argument directory
321  */
322 static char **
323 fnames(char *path)
324 {
325         DIR                *dir;
326         struct dirent *file;
327         char      **filenames;
328         int                     numnames = 0;
329         int                     fnsize = 200;   /* enough for many small dbs */
330
331         dir = opendir(path);
332         if (dir == NULL)
333         {
334 #ifndef FRONTEND
335                 elog(WARNING, "could not open directory \"%s\": %m", path);
336 #else
337                 fprintf(stderr, _("could not open directory \"%s\": %s\n"),
338                                 path, strerror(errno));
339 #endif
340                 return NULL;
341         }
342
343         filenames = (char **) palloc(fnsize * sizeof(char *));
344
345         errno = 0;
346         while ((file = readdir(dir)) != NULL)
347         {
348                 if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0)
349                 {
350                         if (numnames + 1 >= fnsize)
351                         {
352                                 fnsize *= 2;
353                                 filenames = (char **) repalloc(filenames,
354                                                                                            fnsize * sizeof(char *));
355                         }
356                         filenames[numnames++] = pstrdup(file->d_name);
357                 }
358                 errno = 0;
359         }
360 #ifdef WIN32
361
362         /*
363          * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
364          * released version
365          */
366         if (GetLastError() == ERROR_NO_MORE_FILES)
367                 errno = 0;
368 #endif
369         if (errno)
370         {
371 #ifndef FRONTEND
372                 elog(WARNING, "could not read directory \"%s\": %m", path);
373 #else
374                 fprintf(stderr, _("could not read directory \"%s\": %s\n"),
375                                 path, strerror(errno));
376 #endif
377         }
378
379         filenames[numnames] = NULL;
380
381         closedir(dir);
382
383         return filenames;
384 }
385
386
387 /*
388  *      fnames_cleanup
389  *
390  *      deallocate memory used for filenames
391  */
392 static void
393 fnames_cleanup(char **filenames)
394 {
395         char      **fn;
396
397         for (fn = filenames; *fn; fn++)
398                 pfree(*fn);
399
400         pfree(filenames);
401 }
402
403
404 /*
405  *      rmtree
406  *
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.
411  */
412 bool
413 rmtree(char *path, bool rmtopdir)
414 {
415         char            pathbuf[MAXPGPATH];
416         char       *filepath;
417         char      **filenames;
418         char      **filename;
419         struct stat statbuf;
420
421         /*
422          * we copy all the names out of the directory before we start modifying
423          * it.
424          */
425         filenames = fnames(path);
426
427         if (filenames == NULL)
428                 return false;
429
430         /* now we have the names we can start removing things */
431         filepath = pathbuf;
432
433         for (filename = filenames; *filename; filename++)
434         {
435                 snprintf(filepath, MAXPGPATH, "%s/%s", path, *filename);
436
437                 if (lstat(filepath, &statbuf) != 0)
438                         goto report_and_fail;
439
440                 if (S_ISDIR(statbuf.st_mode))
441                 {
442                         /* call ourselves recursively for a directory */
443                         if (!rmtree(filepath, true))
444                         {
445                                 /* we already reported the error */
446                                 fnames_cleanup(filenames);
447                                 return false;
448                         }
449                 }
450                 else
451                 {
452                         if (unlink(filepath) != 0)
453                                 goto report_and_fail;
454                 }
455         }
456
457         if (rmtopdir)
458         {
459                 filepath = path;
460                 if (rmdir(filepath) != 0)
461                         goto report_and_fail;
462         }
463
464         fnames_cleanup(filenames);
465         return true;
466
467 report_and_fail:
468
469 #ifndef FRONTEND
470         elog(WARNING, "could not remove file or directory \"%s\": %m", filepath);
471 #else
472         fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
473                         filepath, strerror(errno));
474 #endif
475         fnames_cleanup(filenames);
476         return false;
477 }