about summary refs log tree commit diff
path: root/src/libstd/sys/windows
diff options
context:
space:
mode:
authorChris Morgan <me@chrismorgan.info>2017-01-08 16:35:13 +0530
committerChris Morgan <me@chrismorgan.info>2017-01-09 08:15:04 +0530
commit02ae1e10605dccf29c77f94dbbf916b28d2b7c54 (patch)
treee6fc7b7e1361411c5f344130d329edad7a5a3d54 /src/libstd/sys/windows
parent7ac9d337dcc544b4b1959997cdd36f1ba0c8d3e1 (diff)
downloadrust-02ae1e10605dccf29c77f94dbbf916b28d2b7c54.tar.gz
rust-02ae1e10605dccf29c77f94dbbf916b28d2b7c54.zip
Support unprivileged symlink creation in Windows
Symlink creation on Windows has in the past basically required admin;
it’s being opened up a bit in the Creators Update, so that at least
people who have put their computers into Developer Mode will be able to
create symlinks without special privileges. (Microsoft are being
cautious about it all; the Developer Mode requirement makes it so that
it this won’t be as helpful as I’d like, but it’s still an improvement
over requiring admin.)

Because of compatibility concerns, they’ve hidden this new functionality
behind a new flag in the CreateSymbolicLink dwFlags:
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE. So we add this flag in
order to join the party.

Older Windows doesn’t like this new flag, though, so if we encounter
ERROR_INVALID_PARAMETER we try again without the new flag.

Sources:

- https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/
  is the official announcement (search for CreateSymbolicLink)

- https://news.ycombinator.com/item?id=13096354 on why the new flag.

- https://twitter.com/richturn_ms/status/818167548269051905 confirming
  that Developer Mode will still be required.
Diffstat (limited to 'src/libstd/sys/windows')
-rw-r--r--src/libstd/sys/windows/c.rs1
-rw-r--r--src/libstd/sys/windows/fs.rs22
2 files changed, 20 insertions, 3 deletions
diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs
index 910d802f059..7b8575f9ec4 100644
--- a/src/libstd/sys/windows/c.rs
+++ b/src/libstd/sys/windows/c.rs
@@ -166,6 +166,7 @@ pub const SYMLINK_FLAG_RELATIVE: DWORD = 0x00000001;
 pub const FSCTL_SET_REPARSE_POINT: DWORD = 0x900a4;
 
 pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1;
+pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2;
 
 // Note that these are not actually HANDLEs, just values to pass to GetStdHandle
 pub const STD_INPUT_HANDLE: DWORD = -10i32 as DWORD;
diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs
index c410fcd1ee0..4efc68afdc4 100644
--- a/src/libstd/sys/windows/fs.rs
+++ b/src/libstd/sys/windows/fs.rs
@@ -646,9 +646,25 @@ pub fn symlink_inner(src: &Path, dst: &Path, dir: bool) -> io::Result<()> {
     let src = to_u16s(src)?;
     let dst = to_u16s(dst)?;
     let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
-    cvt(unsafe {
-        c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as c::BOOL
-    })?;
+    // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
+    // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
+    // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be
+    // added to dwFlags to opt into this behaviour.
+    let result = cvt(unsafe {
+        c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(),
+                               flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) as c::BOOL
+    });
+    if let Err(err) = result {
+        if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) {
+            // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
+            // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag.
+            cvt(unsafe {
+                c::CreateSymbolicLinkW(dst.as_ptr(), src.as_ptr(), flags) as c::BOOL
+            })?;
+        } else {
+            return Err(err);
+        }
+    }
     Ok(())
 }