Header And Logo

PostgreSQL
| The world's most advanced open source database.

dirmod.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * dirmod.c
00004  *    directory handling functions
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *  This includes replacement versions of functions that work on
00010  *  Win32 (NT4 and newer).
00011  *
00012  * IDENTIFICATION
00013  *    src/port/dirmod.c
00014  *
00015  *-------------------------------------------------------------------------
00016  */
00017 
00018 #ifndef FRONTEND
00019 #include "postgres.h"
00020 #else
00021 #include "postgres_fe.h"
00022 #endif
00023 
00024 /* Don't modify declarations in system headers */
00025 #if defined(WIN32) || defined(__CYGWIN__)
00026 #undef rename
00027 #undef unlink
00028 #endif
00029 
00030 #include <unistd.h>
00031 #include <dirent.h>
00032 #include <sys/stat.h>
00033 
00034 #if defined(WIN32) || defined(__CYGWIN__)
00035 #ifndef __CYGWIN__
00036 #include <winioctl.h>
00037 #else
00038 #include <windows.h>
00039 #include <w32api/winioctl.h>
00040 #endif
00041 #endif
00042 
00043 #if defined(WIN32) || defined(__CYGWIN__)
00044 
00045 /*
00046  *  pgrename
00047  */
00048 int
00049 pgrename(const char *from, const char *to)
00050 {
00051     int         loops = 0;
00052 
00053     /*
00054      * We need to loop because even though PostgreSQL uses flags that allow
00055      * rename while the file is open, other applications might have the file
00056      * open without those flags.  However, we won't wait indefinitely for
00057      * someone else to close the file, as the caller might be holding locks
00058      * and blocking other backends.
00059      */
00060 #if defined(WIN32) && !defined(__CYGWIN__)
00061     while (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
00062 #else
00063     while (rename(from, to) < 0)
00064 #endif
00065     {
00066 #if defined(WIN32) && !defined(__CYGWIN__)
00067         DWORD       err = GetLastError();
00068 
00069         _dosmaperr(err);
00070 
00071         /*
00072          * Modern NT-based Windows versions return ERROR_SHARING_VIOLATION if
00073          * another process has the file open without FILE_SHARE_DELETE.
00074          * ERROR_LOCK_VIOLATION has also been seen with some anti-virus
00075          * software. This used to check for just ERROR_ACCESS_DENIED, so
00076          * presumably you can get that too with some OS versions. We don't
00077          * expect real permission errors where we currently use rename().
00078          */
00079         if (err != ERROR_ACCESS_DENIED &&
00080             err != ERROR_SHARING_VIOLATION &&
00081             err != ERROR_LOCK_VIOLATION)
00082             return -1;
00083 #else
00084         if (errno != EACCES)
00085             return -1;
00086 #endif
00087 
00088         if (++loops > 100)      /* time out after 10 sec */
00089             return -1;
00090         pg_usleep(100000);      /* us */
00091     }
00092     return 0;
00093 }
00094 
00095 
00096 /*
00097  *  pgunlink
00098  */
00099 int
00100 pgunlink(const char *path)
00101 {
00102     int         loops = 0;
00103 
00104     /*
00105      * We need to loop because even though PostgreSQL uses flags that allow
00106      * unlink while the file is open, other applications might have the file
00107      * open without those flags.  However, we won't wait indefinitely for
00108      * someone else to close the file, as the caller might be holding locks
00109      * and blocking other backends.
00110      */
00111     while (unlink(path))
00112     {
00113         if (errno != EACCES)
00114             return -1;
00115         if (++loops > 100)      /* time out after 10 sec */
00116             return -1;
00117         pg_usleep(100000);      /* us */
00118     }
00119     return 0;
00120 }
00121 
00122 /* We undefined these above; now redefine for possible use below */
00123 #define rename(from, to)        pgrename(from, to)
00124 #define unlink(path)            pgunlink(path)
00125 #endif   /* defined(WIN32) || defined(__CYGWIN__) */
00126 
00127 
00128 #if defined(WIN32) && !defined(__CYGWIN__)      /* Cygwin has its own symlinks */
00129 
00130 /*
00131  *  pgsymlink support:
00132  *
00133  *  This struct is a replacement for REPARSE_DATA_BUFFER which is defined in VC6 winnt.h
00134  *  but omitted in later SDK functions.
00135  *  We only need the SymbolicLinkReparseBuffer part of the original struct's union.
00136  */
00137 typedef struct
00138 {
00139     DWORD       ReparseTag;
00140     WORD        ReparseDataLength;
00141     WORD        Reserved;
00142     /* SymbolicLinkReparseBuffer */
00143     WORD        SubstituteNameOffset;
00144     WORD        SubstituteNameLength;
00145     WORD        PrintNameOffset;
00146     WORD        PrintNameLength;
00147     WCHAR       PathBuffer[1];
00148 } REPARSE_JUNCTION_DATA_BUFFER;
00149 
00150 #define REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE   \
00151         FIELD_OFFSET(REPARSE_JUNCTION_DATA_BUFFER, SubstituteNameOffset)
00152 
00153 
00154 /*
00155  *  pgsymlink - uses Win32 junction points
00156  *
00157  *  For reference:  http://www.codeproject.com/KB/winsdk/junctionpoints.aspx
00158  */
00159 int
00160 pgsymlink(const char *oldpath, const char *newpath)
00161 {
00162     HANDLE      dirhandle;
00163     DWORD       len;
00164     char        buffer[MAX_PATH * sizeof(WCHAR) + sizeof(REPARSE_JUNCTION_DATA_BUFFER)];
00165     char        nativeTarget[MAX_PATH];
00166     char       *p = nativeTarget;
00167     REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
00168 
00169     CreateDirectory(newpath, 0);
00170     dirhandle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE,
00171                            0, 0, OPEN_EXISTING,
00172                FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
00173 
00174     if (dirhandle == INVALID_HANDLE_VALUE)
00175         return -1;
00176 
00177     /* make sure we have an unparsed native win32 path */
00178     if (memcmp("\\??\\", oldpath, 4))
00179         sprintf(nativeTarget, "\\??\\%s", oldpath);
00180     else
00181         strcpy(nativeTarget, oldpath);
00182 
00183     while ((p = strchr(p, '/')) != NULL)
00184         *p++ = '\\';
00185 
00186     len = strlen(nativeTarget) * sizeof(WCHAR);
00187     reparseBuf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
00188     reparseBuf->ReparseDataLength = len + 12;
00189     reparseBuf->Reserved = 0;
00190     reparseBuf->SubstituteNameOffset = 0;
00191     reparseBuf->SubstituteNameLength = len;
00192     reparseBuf->PrintNameOffset = len + sizeof(WCHAR);
00193     reparseBuf->PrintNameLength = 0;
00194     MultiByteToWideChar(CP_ACP, 0, nativeTarget, -1,
00195                         reparseBuf->PathBuffer, MAX_PATH);
00196 
00197     /*
00198      * FSCTL_SET_REPARSE_POINT is coded differently depending on SDK version;
00199      * we use our own definition
00200      */
00201     if (!DeviceIoControl(dirhandle,
00202      CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_ANY_ACCESS),
00203                          reparseBuf,
00204     reparseBuf->ReparseDataLength + REPARSE_JUNCTION_DATA_BUFFER_HEADER_SIZE,
00205                          0, 0, &len, 0))
00206     {
00207         LPSTR       msg;
00208 
00209         errno = 0;
00210         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00211                       NULL, GetLastError(),
00212                       MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
00213                       (LPSTR) &msg, 0, NULL);
00214 #ifndef FRONTEND
00215         ereport(ERROR,
00216                 (errcode_for_file_access(),
00217                  errmsg("could not set junction for \"%s\": %s",
00218                         nativeTarget, msg)));
00219 #else
00220         fprintf(stderr, _("could not set junction for \"%s\": %s\n"),
00221                 nativeTarget, msg);
00222 #endif
00223         LocalFree(msg);
00224 
00225         CloseHandle(dirhandle);
00226         RemoveDirectory(newpath);
00227         return -1;
00228     }
00229 
00230     CloseHandle(dirhandle);
00231 
00232     return 0;
00233 }
00234 
00235 /*
00236  *  pgreadlink - uses Win32 junction points
00237  */
00238 int
00239 pgreadlink(const char *path, char *buf, size_t size)
00240 {
00241     DWORD       attr;
00242     HANDLE      h;
00243     char        buffer[MAX_PATH * sizeof(WCHAR) + sizeof(REPARSE_JUNCTION_DATA_BUFFER)];
00244     REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
00245     DWORD       len;
00246     int         r;
00247 
00248     attr = GetFileAttributes(path);
00249     if (attr == INVALID_FILE_ATTRIBUTES)
00250     {
00251         _dosmaperr(GetLastError());
00252         return -1;
00253     }
00254     if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
00255     {
00256         errno = EINVAL;
00257         return -1;
00258     }
00259 
00260     h = CreateFile(path,
00261                    GENERIC_READ,
00262                    FILE_SHARE_READ | FILE_SHARE_WRITE,
00263                    NULL,
00264                    OPEN_EXISTING,
00265                    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
00266                    0);
00267     if (h == INVALID_HANDLE_VALUE)
00268     {
00269         _dosmaperr(GetLastError());
00270         return -1;
00271     }
00272 
00273     if (!DeviceIoControl(h,
00274                          FSCTL_GET_REPARSE_POINT,
00275                          NULL,
00276                          0,
00277                          (LPVOID) reparseBuf,
00278                          sizeof(buffer),
00279                          &len,
00280                          NULL))
00281     {
00282         LPSTR       msg;
00283 
00284         errno = 0;
00285         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00286                       NULL, GetLastError(),
00287                       MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
00288                       (LPSTR) &msg, 0, NULL);
00289 #ifndef FRONTEND
00290         ereport(ERROR,
00291                 (errcode_for_file_access(),
00292                  errmsg("could not get junction for \"%s\": %s",
00293                         path, msg)));
00294 #else
00295         fprintf(stderr, _("could not get junction for \"%s\": %s\n"),
00296                 path, msg);
00297 #endif
00298         LocalFree(msg);
00299         CloseHandle(h);
00300         errno = EINVAL;
00301         return -1;
00302     }
00303     CloseHandle(h);
00304 
00305     /* Got it, let's get some results from this */
00306     if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
00307     {
00308         errno = EINVAL;
00309         return -1;
00310     }
00311 
00312     r = WideCharToMultiByte(CP_ACP, 0,
00313                             reparseBuf->PathBuffer, -1,
00314                             buf,
00315                             size,
00316                             NULL, NULL);
00317 
00318     if (r <= 0)
00319     {
00320         errno = EINVAL;
00321         return -1;
00322     }
00323 
00324     /*
00325      * If the path starts with "\??\", which it will do in most (all?) cases,
00326      * strip those out.
00327      */
00328     if (r > 4 && strncmp(buf, "\\??\\", 4) == 0)
00329     {
00330         memmove(buf, buf + 4, strlen(buf + 4) + 1);
00331         r -= 4;
00332     }
00333     return r;
00334 }
00335 
00336 /*
00337  * Assumes the file exists, so will return false if it doesn't
00338  * (since a nonexistant file is not a junction)
00339  */
00340 bool
00341 pgwin32_is_junction(char *path)
00342 {
00343     DWORD       attr = GetFileAttributes(path);
00344 
00345     if (attr == INVALID_FILE_ATTRIBUTES)
00346     {
00347         _dosmaperr(GetLastError());
00348         return false;
00349     }
00350     return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT);
00351 }
00352 #endif   /* defined(WIN32) && !defined(__CYGWIN__) */
00353 
00354 
00355 /*
00356  * pgfnames
00357  *
00358  * return a list of the names of objects in the argument directory.  Caller
00359  * must call pgfnames_cleanup later to free the memory allocated by this
00360  * function.
00361  */
00362 char      **
00363 pgfnames(const char *path)
00364 {
00365     DIR        *dir;
00366     struct dirent *file;
00367     char      **filenames;
00368     int         numnames = 0;
00369     int         fnsize = 200;   /* enough for many small dbs */
00370 
00371     dir = opendir(path);
00372     if (dir == NULL)
00373     {
00374 #ifndef FRONTEND
00375         elog(WARNING, "could not open directory \"%s\": %m", path);
00376 #else
00377         fprintf(stderr, _("could not open directory \"%s\": %s\n"),
00378                 path, strerror(errno));
00379 #endif
00380         return NULL;
00381     }
00382 
00383     filenames = (char **) palloc(fnsize * sizeof(char *));
00384 
00385     errno = 0;
00386     while ((file = readdir(dir)) != NULL)
00387     {
00388         if (strcmp(file->d_name, ".") != 0 && strcmp(file->d_name, "..") != 0)
00389         {
00390             if (numnames + 1 >= fnsize)
00391             {
00392                 fnsize *= 2;
00393                 filenames = (char **) repalloc(filenames,
00394                                                fnsize * sizeof(char *));
00395             }
00396             filenames[numnames++] = pstrdup(file->d_name);
00397         }
00398         errno = 0;
00399     }
00400 #ifdef WIN32
00401 
00402     /*
00403      * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in
00404      * released version
00405      */
00406     if (GetLastError() == ERROR_NO_MORE_FILES)
00407         errno = 0;
00408 #endif
00409     if (errno)
00410     {
00411 #ifndef FRONTEND
00412         elog(WARNING, "could not read directory \"%s\": %m", path);
00413 #else
00414         fprintf(stderr, _("could not read directory \"%s\": %s\n"),
00415                 path, strerror(errno));
00416 #endif
00417     }
00418 
00419     filenames[numnames] = NULL;
00420 
00421     closedir(dir);
00422 
00423     return filenames;
00424 }
00425 
00426 
00427 /*
00428  *  pgfnames_cleanup
00429  *
00430  *  deallocate memory used for filenames
00431  */
00432 void
00433 pgfnames_cleanup(char **filenames)
00434 {
00435     char      **fn;
00436 
00437     for (fn = filenames; *fn; fn++)
00438         pfree(*fn);
00439 
00440     pfree(filenames);
00441 }
00442 
00443 
00444 /*
00445  *  rmtree
00446  *
00447  *  Delete a directory tree recursively.
00448  *  Assumes path points to a valid directory.
00449  *  Deletes everything under path.
00450  *  If rmtopdir is true deletes the directory too.
00451  *  Returns true if successful, false if there was any problem.
00452  *  (The details of the problem are reported already, so caller
00453  *  doesn't really have to say anything more, but most do.)
00454  */
00455 bool
00456 rmtree(const char *path, bool rmtopdir)
00457 {
00458     bool        result = true;
00459     char        pathbuf[MAXPGPATH];
00460     char      **filenames;
00461     char      **filename;
00462     struct stat statbuf;
00463 
00464     /*
00465      * we copy all the names out of the directory before we start modifying
00466      * it.
00467      */
00468     filenames = pgfnames(path);
00469 
00470     if (filenames == NULL)
00471         return false;
00472 
00473     /* now we have the names we can start removing things */
00474     for (filename = filenames; *filename; filename++)
00475     {
00476         snprintf(pathbuf, MAXPGPATH, "%s/%s", path, *filename);
00477 
00478         /*
00479          * It's ok if the file is not there anymore; we were just about to
00480          * delete it anyway.
00481          *
00482          * This is not an academic possibility. One scenario where this
00483          * happens is when bgwriter has a pending unlink request for a file in
00484          * a database that's being dropped. In dropdb(), we call
00485          * ForgetDatabaseFsyncRequests() to flush out any such pending unlink
00486          * requests, but because that's asynchronous, it's not guaranteed that
00487          * the bgwriter receives the message in time.
00488          */
00489         if (lstat(pathbuf, &statbuf) != 0)
00490         {
00491             if (errno != ENOENT)
00492             {
00493 #ifndef FRONTEND
00494                 elog(WARNING, "could not stat file or directory \"%s\": %m",
00495                      pathbuf);
00496 #else
00497                 fprintf(stderr, _("could not stat file or directory \"%s\": %s\n"),
00498                         pathbuf, strerror(errno));
00499 #endif
00500                 result = false;
00501             }
00502             continue;
00503         }
00504 
00505         if (S_ISDIR(statbuf.st_mode))
00506         {
00507             /* call ourselves recursively for a directory */
00508             if (!rmtree(pathbuf, true))
00509             {
00510                 /* we already reported the error */
00511                 result = false;
00512             }
00513         }
00514         else
00515         {
00516             if (unlink(pathbuf) != 0)
00517             {
00518                 if (errno != ENOENT)
00519                 {
00520 #ifndef FRONTEND
00521                     elog(WARNING, "could not remove file or directory \"%s\": %m",
00522                          pathbuf);
00523 #else
00524                     fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
00525                             pathbuf, strerror(errno));
00526 #endif
00527                     result = false;
00528                 }
00529             }
00530         }
00531     }
00532 
00533     if (rmtopdir)
00534     {
00535         if (rmdir(path) != 0)
00536         {
00537 #ifndef FRONTEND
00538             elog(WARNING, "could not remove file or directory \"%s\": %m",
00539                  path);
00540 #else
00541             fprintf(stderr, _("could not remove file or directory \"%s\": %s\n"),
00542                     path, strerror(errno));
00543 #endif
00544             result = false;
00545         }
00546     }
00547 
00548     pgfnames_cleanup(filenames);
00549 
00550     return result;
00551 }
00552 
00553 
00554 #if defined(WIN32) && !defined(__CYGWIN__)
00555 
00556 #undef stat
00557 
00558 /*
00559  * The stat() function in win32 is not guaranteed to update the st_size
00560  * field when run. So we define our own version that uses the Win32 API
00561  * to update this field.
00562  */
00563 int
00564 pgwin32_safestat(const char *path, struct stat * buf)
00565 {
00566     int         r;
00567     WIN32_FILE_ATTRIBUTE_DATA attr;
00568 
00569     r = stat(path, buf);
00570     if (r < 0)
00571         return r;
00572 
00573     if (!GetFileAttributesEx(path, GetFileExInfoStandard, &attr))
00574     {
00575         _dosmaperr(GetLastError());
00576         return -1;
00577     }
00578 
00579     /*
00580      * XXX no support for large files here, but we don't do that in general on
00581      * Win32 yet.
00582      */
00583     buf->st_size = attr.nFileSizeLow;
00584 
00585     return 0;
00586 }
00587 
00588 #endif