Skip to content

Commit

Permalink
Support unprivileged symlink creation in Windows
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
chris-morgan committed Jan 9, 2017
1 parent 7ac9d33 commit 02ae1e1
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/libstd/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
22 changes: 19 additions & 3 deletions src/libstd/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}

Expand Down

0 comments on commit 02ae1e1

Please sign in to comment.