[PATCH setup 1/2] Add support for creating WSL symlinks
Jon Turney
jon.turney@dronecode.org.uk
Sun May 23 18:27:36 GMT 2021
---
inilintmain.cc | 7 +++
mklink2.cc | 139 +++++++++++++++++++++++++++++++++++++++++++++++--
mklink2.h | 10 ++++
3 files changed, 153 insertions(+), 3 deletions(-)
diff --git a/inilintmain.cc b/inilintmain.cc
index f31e5eb..886c152 100644
--- a/inilintmain.cc
+++ b/inilintmain.cc
@@ -56,3 +56,10 @@ main (int argc, char **argv)
return 0;
}
+
+const std::string &
+get_root_dir ()
+{
+ static std::string empty;
+ return empty;
+}
diff --git a/mklink2.cc b/mklink2.cc
index 0b73403..3bff4b3 100644
--- a/mklink2.cc
+++ b/mklink2.cc
@@ -5,6 +5,11 @@
#include "shlobj.h"
#include "mklink2.h"
#include "filemanip.h"
+#include "winioctl.h"
+#include "LogSingleton.h"
+#include "mount.h"
+
+SymlinkTypeEnum symlinkType = SymlinkTypeMagic; // default to historical behaviour
/* This part of the code must be in C because the C++ interface to COM
doesn't work. */
@@ -38,9 +43,8 @@ make_link_2 (char const *exepath, char const *args, char const *icon, char const
#define SYMLINK_COOKIE "!<symlink>"
-extern "C"
-int
-mkcygsymlink (const char *from, const char *to)
+static int
+mkmagiccygsymlink (const char *from, const char *to)
{
char buf[strlen (SYMLINK_COOKIE) + 4096];
unsigned long w;
@@ -72,6 +76,135 @@ mkcygsymlink (const char *from, const char *to)
return 1;
}
+#ifndef IO_REPARSE_TAG_LX_SYMLINK
+#define IO_REPARSE_TAG_LX_SYMLINK (0xa000001d)
+#endif
+
+typedef struct _REPARSE_LX_SYMLINK_BUFFER
+{
+ DWORD ReparseTag;
+ WORD ReparseDataLength;
+ WORD Reserved;
+ struct {
+ DWORD FileType; /* Value is apparently always 2 for symlinks. */
+ char PathBuffer[1];/* UTF-8 encoded POSIX path
+ Isn't \0 terminated.
+ Length is ReparseDataLength - sizeof (FileType).
+ */
+ } LxSymlinkReparseBuffer;
+} REPARSE_LX_SYMLINK_BUFFER,*PREPARSE_LX_SYMLINK_BUFFER;
+
+static int
+mkwslsymlink (const char *from, const char *to)
+{
+ /* Construct the reparse path */
+ std::string lxsymto;
+ if (to[0] == '/')
+ {
+ /* If 'to' is absolute and starts with '/cygdrive' or /proc/cygdrive',
+ this is a problem because: (i) the cygdrive prefix might be different,
+ and (ii) the target drive might not exist, on the install system.
+
+ Because of these problems, we don't expect any install packages to have
+ links like that (they should instead be created by post-install
+ scripts), but fail if they do.
+ */
+ if ((strncmp(to, "/cygdrive", 9) == 0) ||
+ (strncmp(to, "/proc/cygdrive", 14) == 0))
+ {
+ Log (LOG_PLAIN) << "Refusing to create WSL symlink to" << to << " as it starts with /cygdrive" << endLog;
+ return 1;
+ }
+
+ /* Otherwise, we convert the absolute path 'to' into a form a WSL
+ compatible form, constructed from the '/mnt' prefix and the cygwin root
+ directory e.g. /mnt/c/cygwin64/ */
+ lxsymto = "/mnt/";
+ std::string root = get_root_dir();
+ if (root[1] == ':')
+ {
+ lxsymto.append(1, tolower(root.c_str()[0]));
+ lxsymto.append("/");
+ lxsymto.append(&(root[3]));
+ }
+ else
+ {
+ // root dir is UNC path ???
+ lxsymto.append(root.c_str());
+ }
+ lxsymto.append(to);
+ }
+ else
+ {
+ /* Otherwise 'to' is relative to 'from', so leave it alone */
+ lxsymto = to;
+ }
+
+ /* Create reparse point. */
+ SECURITY_DESCRIPTOR sd;
+ acl_t acl;
+ nt_sec.GetPosixPerms (from, NULL, NULL, 0644, sd, acl);
+
+ const size_t flen = strlen (from) + 7;
+ WCHAR wfrom[flen];
+ mklongpath (wfrom, from, flen);
+ wfrom[1] = '?';
+
+ HANDLE fh;
+ UNICODE_STRING ufrom;
+ IO_STATUS_BLOCK io;
+ OBJECT_ATTRIBUTES attr;
+ RtlInitUnicodeString (&ufrom, wfrom);
+ InitializeObjectAttributes (&attr, &ufrom, OBJ_CASE_INSENSITIVE, NULL, &sd);
+ NTSTATUS status = NtCreateFile (&fh,
+ DELETE | FILE_GENERIC_WRITE | READ_CONTROL | WRITE_DAC,
+ &attr,
+ &io,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_CREATE,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
+ | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT,
+ NULL, 0);
+ if (!NT_SUCCESS (status))
+ {
+ Log (LOG_PLAIN) << "NtCreateFile status " << std::hex << status << endLog;
+ return 1;
+ }
+
+ /* Set content of the reparse point */
+ size_t tlen = lxsymto.length();
+ REPARSE_LX_SYMLINK_BUFFER *rpl = (REPARSE_LX_SYMLINK_BUFFER *) new char[sizeof(REPARSE_LX_SYMLINK_BUFFER) + tlen];
+ rpl->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
+ rpl->ReparseDataLength = sizeof (DWORD) + tlen;
+ rpl->Reserved = 0;
+ rpl->LxSymlinkReparseBuffer.FileType = 2;
+ memcpy(rpl->LxSymlinkReparseBuffer.PathBuffer, lxsymto.c_str(), tlen);
+
+ status = NtFsControlFile (fh, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT,
+ (LPVOID) rpl,
+ REPARSE_DATA_BUFFER_HEADER_SIZE + rpl->ReparseDataLength,
+ NULL, 0);
+ if (!NT_SUCCESS (status))
+ {
+ Log (LOG_PLAIN) << "FSCTL_SET_REPARSE_POINT status " << std::hex << status << endLog;
+ }
+
+ delete rpl;
+ NtClose(fh);
+ return NT_SUCCESS (status) ? 0 : 1;
+}
+
+int
+mkcygsymlink (const char *from, const char *to)
+{
+ if (symlinkType == SymlinkTypeWsl)
+ return mkwslsymlink (from, to);
+
+ return mkmagiccygsymlink(from, to);
+}
+
static struct {
FILE_LINK_INFORMATION fli;
WCHAR namebuf[32768];
diff --git a/mklink2.h b/mklink2.h
index 0244748..fda17f6 100644
--- a/mklink2.h
+++ b/mklink2.h
@@ -17,4 +17,14 @@ extern "C"
};
#endif
+typedef enum
+{
+ SymlinkTypeMagic,
+ SymlinkTypeShortcut,
+ SymlinkTypeNative,
+ SymlinkTypeWsl,
+} SymlinkTypeEnum;
+
+extern SymlinkTypeEnum symlinkType;
+
#endif /* SETUP_MKLINK2_H */
--
2.31.1
More information about the Cygwin-apps
mailing list