Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Backport "Add pgreadlink() on Windows to read junction points".
authorRobert Haas <rhaas@postgresql.org>
Tue, 5 May 2015 19:54:26 +0000 (15:54 -0400)
committerRobert Haas <rhaas@postgresql.org>
Tue, 5 May 2015 19:54:26 +0000 (15:54 -0400)
The patch to recurseively fsync pgdata needs this, but it was only
introduced in 9.1.

src/include/port.h
src/port/dirmod.c

index 35a50ccc39561af5cff0dbdddd4943378241e843..74da77ca39e0c389a3829add145b1cea3e31d2bc 100644 (file)
@@ -291,8 +291,11 @@ extern int pgunlink(const char *path);
  */
 #if defined(WIN32) && !defined(__CYGWIN__)
 extern int pgsymlink(const char *oldpath, const char *newpath);
+extern int pgreadlink(const char *path, char *buf, size_t size);
+extern bool pgwin32_is_junction(char *path);
 
 #define symlink(oldpath, newpath)  pgsymlink(oldpath, newpath)
+#define readlink(path, buf, size)  pgreadlink(path, buf, size)
 #endif
 
 extern void copydir(char *fromdir, char *todir, bool recurse);
index d7754418a88a5c169a9ca0e5ab83114accfeb237..72576cd84f51971337bbd1363626e6760622cd2f 100644 (file)
@@ -297,6 +297,124 @@ pgsymlink(const char *oldpath, const char *newpath)
 
    return 0;
 }
+
+/*
+ * pgreadlink - uses Win32 junction points
+ */
+int
+pgreadlink(const char *path, char *buf, size_t size)
+{
+   DWORD       attr;
+   HANDLE      h;
+   char        buffer[MAX_PATH * sizeof(WCHAR) + sizeof(REPARSE_JUNCTION_DATA_BUFFER)];
+   REPARSE_JUNCTION_DATA_BUFFER *reparseBuf = (REPARSE_JUNCTION_DATA_BUFFER *) buffer;
+   DWORD       len;
+   int         r;
+
+   attr = GetFileAttributes(path);
+   if (attr == INVALID_FILE_ATTRIBUTES)
+   {
+       _dosmaperr(GetLastError());
+       return -1;
+   }
+   if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
+   {
+       errno = EINVAL;
+       return -1;
+   }
+
+   h = CreateFile(path,
+                  GENERIC_READ,
+                  FILE_SHARE_READ | FILE_SHARE_WRITE,
+                  NULL,
+                  OPEN_EXISTING,
+                  FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+                  0);
+   if (h == INVALID_HANDLE_VALUE)
+   {
+       _dosmaperr(GetLastError());
+       return -1;
+   }
+
+   if (!DeviceIoControl(h,
+                        FSCTL_GET_REPARSE_POINT,
+                        NULL,
+                        0,
+                        (LPVOID) reparseBuf,
+                        sizeof(buffer),
+                        &len,
+                        NULL))
+   {
+       LPSTR       msg;
+
+       errno = 0;
+       FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                     NULL, GetLastError(),
+                     MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+                     (LPSTR) &msg, 0, NULL);
+#ifndef FRONTEND
+       ereport(ERROR,
+               (errcode_for_file_access(),
+                errmsg("could not get junction for \"%s\": %s",
+                       path, msg)));
+#else
+       fprintf(stderr, _("could not get junction for \"%s\": %s\n"),
+               path, msg);
+#endif
+       LocalFree(msg);
+       CloseHandle(h);
+       errno = EINVAL;
+       return -1;
+   }
+   CloseHandle(h);
+
+   /* Got it, let's get some results from this */
+   if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
+   {
+       errno = EINVAL;
+       return -1;
+   }
+
+   r = WideCharToMultiByte(CP_ACP, 0,
+                           reparseBuf->PathBuffer, -1,
+                           buf,
+                           size,
+                           NULL, NULL);
+
+   if (r <= 0)
+   {
+       errno = EINVAL;
+       return -1;
+   }
+
+   /*
+    * If the path starts with "\??\", which it will do in most (all?) cases,
+    * strip those out.
+    */
+   if (r > 4 && strncmp(buf, "\\??\\", 4) == 0)
+   {
+       memmove(buf, buf + 4, strlen(buf + 4) + 1);
+       r -= 4;
+   }
+   return r;
+}
+
+/*
+ * Assumes the file exists, so will return false if it doesn't
+ * (since a nonexistant file is not a junction)
+ */
+bool
+pgwin32_is_junction(char *path)
+{
+   DWORD       attr = GetFileAttributes(path);
+
+   if (attr == INVALID_FILE_ATTRIBUTES)
+   {
+       _dosmaperr(GetLastError());
+       return false;
+   }
+   return ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT);
+}
 #endif   /* defined(WIN32) && !defined(__CYGWIN__) */