OSDN Git Service

Add pgreadlink() on Windows to read junction points
authorMagnus Hagander <magnus@hagander.net>
Sun, 9 Jan 2011 14:06:55 +0000 (15:06 +0100)
committerMagnus Hagander <magnus@hagander.net>
Sun, 9 Jan 2011 14:09:19 +0000 (15:09 +0100)
Add support for reading back information about the symbolic
links we've created with pgsymlink(), which are actually
Junction Points. Just like pgsymlink() can only create directory
symlinks, pgreadlink() can only read directory symlinks.

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

index 2f5abdd..7ad464c 100644 (file)
@@ -284,8 +284,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 bool rmtree(const char *path, bool rmtopdir);
index 0d373a4..b6a5564 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__) */