From: Jon Turney <jon.turney@dronecode.org.uk>
To: cygwin-apps@cygwin.com
Cc: Jon Turney <jon.turney@dronecode.org.uk>
Subject: [PATCH setup 02/11] Add support for creating native symlinks
Date: Tue, 10 Aug 2021 18:02:19 +0100 [thread overview]
Message-ID: <20210810170228.1690-3-jon.turney@dronecode.org.uk> (raw)
In-Reply-To: <20210810170228.1690-1-jon.turney@dronecode.org.uk>
---
filemanip.cc | 27 ++++++++++++
filemanip.h | 1 +
mklink2.cc | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 141 insertions(+)
diff --git a/filemanip.cc b/filemanip.cc
index 48f5117..ca5e4ac 100644
--- a/filemanip.cc
+++ b/filemanip.cc
@@ -247,6 +247,33 @@ mklongpath (wchar_t *tgt, const char *src, size_t len)
return 0;
}
+int
+mklongrelpath (wchar_t *tgt, const char *src, size_t len)
+{
+ wchar_t *tp;
+ size_t n;
+ mbstate_t mb;
+
+ tp = tgt;
+ memset (&mb, 0, sizeof mb);
+
+ while (len > 0)
+ {
+ n = mbrtowc (tp, src, 6, &mb);
+ if (n == (size_t) -1 || n == (size_t) -2)
+ return -1;
+ if (n == 0)
+ break;
+ src += n;
+ /* Transform char according to Cygwin rules. */
+ if (*tp < 128)
+ *tp = tfx_chars[*tp];
+ ++tp;
+ --len;
+ }
+ return 0;
+}
+
/* Replacement functions for Win32 API functions. The joke here is that the
replacement functions always use the FILE_OPEN_FOR_BACKUP_INTENT flag. */
diff --git a/filemanip.h b/filemanip.h
index 451211f..e83b8e9 100644
--- a/filemanip.h
+++ b/filemanip.h
@@ -34,6 +34,7 @@ size_t get_file_size (const std::string& );
std::string backslash (const std::string& s);
const char * trail (const char *, const char *);
int mklongpath (wchar_t *tgt, const char *src, size_t len);
+int mklongrelpath (wchar_t *tgt, const char *src, size_t len);
FILE *nt_wfopen (const wchar_t *wpath, const char *mode, mode_t perms);
FILE *nt_fopen (const char *path, const char *mode, mode_t perms = 0644);
diff --git a/mklink2.cc b/mklink2.cc
index 3fe5b15..6e7a002 100644
--- a/mklink2.cc
+++ b/mklink2.cc
@@ -196,6 +196,113 @@ mkwslsymlink (const char *from, const char *to)
return NT_SUCCESS (status) ? 0 : 1;
}
+static int
+mknativesymlink (const char *from, const char *to)
+{
+ /* Construct the absolute Windows path of 'to' ... */
+ std::string absto;
+ if (to[0] == '/')
+ {
+ absto = get_root_dir();
+ absto.append(to);
+ }
+ else
+ {
+ /* 'from' is already absolute */
+ absto.append(from);
+ /* remove the last pathname component */
+ size_t i = absto.rfind('/');
+ if (i != std::string::npos)
+ absto.resize(i);
+ /* ... and add relative path 'to'. */
+ absto.append("/");
+ absto.append(to);
+ }
+
+ /* ... so we can discover if it's a file or directory (if it already exists) */
+ size_t abstlen = strlen (absto.c_str()) + 7;
+ wchar_t wabsto[abstlen];
+ mklongpath (wabsto, absto.c_str(), abstlen);
+ wabsto[1] = '?';
+
+ bool isdir = FALSE;
+ bool isdir_known = FALSE;
+ HANDLE fh;
+ NTSTATUS status;
+ UNICODE_STRING uto;
+ OBJECT_ATTRIBUTES attr;
+ IO_STATUS_BLOCK io;
+ RtlInitUnicodeString (&uto, wabsto);
+ InitializeObjectAttributes (&attr, &uto, OBJ_CASE_INSENSITIVE, NULL, NULL);
+ status = NtOpenFile (&fh, FILE_READ_ATTRIBUTES, &attr, &io, FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
+ if (NT_SUCCESS (status))
+ {
+ FILE_BASIC_INFORMATION fi;
+ status = NtQueryInformationFile(fh, &io, &fi, sizeof(fi), FileBasicInformation);
+ if (!NT_SUCCESS (status))
+ Log (LOG_BABBLE) << "Querying " << absto << " failed " << std::hex << status << endLog;
+ else
+ {
+ isdir = fi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+ isdir_known = TRUE;
+ Log (LOG_BABBLE) << "Querying " << absto << " isdir is " << isdir << endLog;
+ }
+ NtClose(fh);
+ }
+ else
+ {
+ Log (LOG_BABBLE) << "Opening " << absto << " failed " << std::hex << status << endLog;
+ }
+
+ /*
+ Fail, if we failed to determine if the symlink target is a directory
+ (probably because it doesn't exist (yet))
+
+ (We could guess that it's a file, since that works for Cygwin (and WSL),
+ which don't care if the directory flag in the symlink is wrong (when the
+ target comes into existence), but native tools will fail.
+ */
+
+ if (!isdir_known)
+ return 1;
+
+ /* Try to create the native symlink. */
+ const size_t flen = strlen (from) + 7;
+ WCHAR wfrom[flen];
+ mklongpath (wfrom, from, flen);
+ wfrom[1] = '?';
+
+ size_t tlen = strlen (to) + 7;
+ wchar_t wto[tlen];
+ if (to[0] == '/')
+ {
+ absto = get_root_dir();
+ absto.append(to);
+ mklongpath (wto, to, tlen);
+ wto[1] = '?';
+ }
+ else
+ {
+ mklongrelpath (wto, to, tlen);
+ }
+
+ DWORD flags = isdir ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
+ /* Windows 10 1703 and later allow unprivileged symlink creation when
+ 'Developer Mode' is on.*/
+ VersionInfo v = GetVer();
+ if ((v.major() > 10) ||
+ ((v.major() == 10) && (v.buildNumber() >= 15063)))
+ flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+
+ status = CreateSymbolicLinkW (wfrom, wto, flags);
+
+ if (!status)
+ Log (LOG_PLAIN) << "Linking " << from << " to " << to << " failed " << std::hex << GetLastError() << endLog;
+
+ return !status;
+}
+
int
mkcygsymlink (const char *from, const char *to)
{
@@ -205,6 +312,12 @@ mkcygsymlink (const char *from, const char *to)
return 0;
}
+ if (symlinkType == SymlinkTypeNative)
+ {
+ if (!mknativesymlink (from, to))
+ return 0;
+ }
+
/* fall back to magic symlink, if selected method fails */
return mkmagiccygsymlink(from, to);
}
--
2.32.0
next prev parent reply other threads:[~2021-08-10 17:04 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-08-10 17:02 [PATCH setup 00/11] Add options to choose symlink type (v2) Jon Turney
2021-08-10 17:02 ` [PATCH setup 01/11] Add support for creating WSL symlinks Jon Turney
2021-08-10 17:02 ` Jon Turney [this message]
2021-08-10 17:02 ` [PATCH setup 03/11] Factor out the iteration over archive files to install Jon Turney
2021-08-10 17:02 ` [PATCH setup 04/11] Add seek() method to archive and compress file classes Jon Turney
2021-08-10 17:02 ` [PATCH setup 05/11] Add separate symlink-creation phase when extracting archive Jon Turney
2021-08-10 17:02 ` [PATCH setup 06/11] Enable SeCreateSymbolicLink privilege Jon Turney
2021-08-11 8:46 ` Corinna Vinschen
2021-09-14 11:53 ` Jon Turney
2021-08-10 17:02 ` [PATCH setup 07/11] Add symlink capabilities to user-agent telemetry Jon Turney
2021-08-10 17:02 ` [PATCH setup 08/11] Factor out StringChoiceOption Jon Turney
2021-08-10 17:02 ` [PATCH setup 09/11] Add a command line option to choose symlink type used Jon Turney
2021-08-10 17:02 ` [PATCH setup 10/11] Propagate --symlink-type setting to post-install scripts Jon Turney
2021-08-10 17:02 ` [PATCH setup 11/11] Default symlink mode from CYGWIN env var Jon Turney
2021-09-14 11:53 ` [PATCH setup 00/11] Add options to choose symlink type (v2) Jon Turney
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210810170228.1690-3-jon.turney@dronecode.org.uk \
--to=jon.turney@dronecode.org.uk \
--cc=cygwin-apps@cygwin.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).