bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

symlink.c for Windows


From: Dmitry Selyutin
Subject: symlink.c for Windows
Date: Mon, 9 Sep 2013 02:27:13 +0400

Hello everyone!

Sorry for the long absence, there are lot of other things to be done. Still I'm trying to participate, though, as I can. Recently I've found that there is no symlink function for Windows, though Windows has CreateSymbolicLink function. I've tried to implement my own. I don't know C or C++ really well, but fix seemed to be trivial enough. MinGW doesn't support symlinks, though reports success.


diff --git a/lib/symlink.c b/lib/symlink.c
index d3c9f21..903ae6a 100644
--- a/lib/symlink.c
+++ b/lib/symlink.c
@@ -45,7 +45,12 @@ rpl_symlink (char const *contents, char const *name)
 
 #else /* !HAVE_SYMLINK */
 
-/* The system does not support symlinks.  */
+# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+
+#  include <windows.h>
+
+#  if defined __MINGW32__
+/* MinGW does not support symlinks. */
 int
 symlink (char const *contents _GL_UNUSED,
          char const *name _GL_UNUSED)
@@ -54,4 +59,141 @@ symlink (char const *contents _GL_UNUSED,
   return -1;
 }
 
+#  else /* !defined __MINGW32__ */
+
+#   if !defined SYMBOLIC_LINK_FLAG_DIRECTORY
+#    define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
+#   endif
+
+/* CreateSymbolicLink was introduced only in Windows Vista.
+   Also on Windows it is necessary to get special privileges. */
+typedef BOOL (WINAPI * CreateSymbolicLinkFuncType) (LPCTSTR lpSymlinkFileName,
+                                                    LPCTSTR lpTargetFileName,
+                                                    DWORD dwFlags);
+static CreateSymbolicLinkFuncType CreateSymbolicLinkFunc = NULL;
+static BOOL initialized = FALSE;
+
+static void
+initialize (void)
+{
+  HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
+  if (kernel32 != NULL)
+    {
+      CreateSymbolicLinkFunc =
+        (CreateSymbolicLinkFuncType) GetProcAddress (kernel32, "CreateSymbolicLinkA");
+    }
+  initialized = TRUE;
+}
+
+int
+symlink (const char *file1, const char *file2)
+{
+  /* Enable privileges for symbolic link creation. */
+  HANDLE token;
+  struct stat st;
+  TOKEN_PRIVILEGES tp;
+  if (!OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
+    goto error;
+  if (!LookupPrivilegeValueA (NULL, "SeCreateSymbolicLinkPrivilege", &tp.Privileges[0].Luid))
+    goto error;
+  tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+  tp.PrivilegeCount = 1;
+  if (!AdjustTokenPrivileges (token, false, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
+    goto error;
+  CloseHandle(token);
+
+  /* Initialize symbolic link function. */
+  size_t len1 = strlen (file1);
+  size_t len2 = strlen (file2);
+  if (!initialized)
+    initialize ();
+
+  if (CreateSymbolicLinkFunc == NULL)
+    goto error;
+
+  /* Reject trailing slashes on non-directories. */
+  if ((len1 && (file1[len1 - 1] == '/' || file1[len1 - 1] == '\\'))
+      || (len2 && (file2[len2 - 1] == '/' || file2[len2 - 1] == '\\')))
+    {
+      if (stat (file1, &st) == 0 && S_ISDIR (st.st_mode))
+        errno = EPERM;
+      else
+        errno = ENOTDIR;
+      return -1;
+    }
+
+  DWORD flags;
+  if (stat (file2, &st) == 0 && S_ISDIR (st.st_mode))
+    isdir |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+  /* Create symbolic link. */
+  if (CreateSymbolicLinkFunc (file2, file1, flags) == 0)
+    {
+      /* Convert error codes from Windows to Unix. */
+      DWORD err = GetLastError ();
+      switch (err)
+        {
+        case ERROR_ACCESS_DENIED:
+          errno = EACCES;
+          break;
+
+        case ERROR_INVALID_FUNCTION:
+          errno = EPERM;
+          break;
+
+        case ERROR_NOT_SAME_DEVICE:
+          errno = EXDEV;
+          break;
+
+        case ERROR_PATH_NOT_FOUND:
+        case ERROR_FILE_NOT_FOUND:
+          errno = ENOENT;
+          break;
+
+        case ERROR_INVALID_PARAMETER:
+          errno = ENAMETOOLONG;
+          break;
+
+        case ERROR_TOO_MANY_LINKS:
+          errno = EMLINK;
+          break;
+
+        case ERROR_ALREADY_EXISTS:
+          errno = EEXIST;
+          break;
+
+        default:
+          errno = EIO;
+        }
+      return -1;
+    }
+
+  /* MinGW doesn't report errors, though really fails. */
+  if (stat (file1, &st) != 0)
+  {
+    errno = ENOSYS;
+    return -1;
+  }
+  return 0;
+
+error:
+  CloseHandle(token);
+  errno = EPERM;
+  return -1;
+
+}
+
+#  endif /* !defined __MINGW32__ */
+
+# else
+/* The system does not support symlinks. */
+int
+symlink (char const *contents _GL_UNUSED,
+         char const *name _GL_UNUSED)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+# endif
+
 #endif /* !HAVE_SYMLINK */


--
With best regards,
Dmitry Selyutin

E-mail: address@hidden
Phone: +7(985)334-07-70

reply via email to

[Prev in Thread] Current Thread [Next in Thread]