Skip to content

Commit 8e18726

Browse files
committed
implement IsSymlinkCreationAllowed()
1 parent 6f646c9 commit 8e18726

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

Makefile.PL

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@ my $test_requires = $ExtUtils::MakeMaker::VERSION >= 6.64
3333
: 'PREREQ_PM';
3434

3535
$param{$test_requires}{'Test'} = 0;
36+
$param{$test_requires}{'File::Temp'} = 0;
3637

3738
WriteMakefile(%param);

Win32.pm

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,27 @@ sub _GetOSName {
713713
return ("Win$os", $desc);
714714
}
715715

716+
sub IsSymlinkCreationAllowed {
717+
my(undef, $major, $minor, $build) = GetOSVersion();
718+
719+
# Vista was the first Windows version with symlink support
720+
return !!0 if $major < 6;
721+
722+
# Since Windows 10 1703, enabling the developer mode allows to create
723+
# symlinks regardless of process privileges
724+
if ($major > 10 || ($major == 10 && ($minor > 0 || $build > 15063))) {
725+
return !!1 if IsDeveloperModeEnabled();
726+
}
727+
728+
my $privs = GetProcessPrivileges();
729+
730+
return !!0 unless $privs;
731+
732+
# It doesn't matter if the permission is enabled or not, it just has to
733+
# exist. CreateSymbolicLink() will automatically enable it when needed.
734+
return exists $privs->{SeCreateSymbolicLinkPrivilege};
735+
}
736+
716737
# "no warnings 'redefine';" doesn't work for 5.8.7 and earlier
717738
local $^W = 0;
718739
bootstrap Win32;
@@ -1309,6 +1330,12 @@ returns 1 on Win9X.
13091330
Returns true if the developer mode is currently enabled. It always returns
13101331
false on Windows versions older than Windows 10.
13111332
1333+
=item Win32::IsSymlinkCreationAllowed()
1334+
1335+
Returns true if the current process is allowed to create symbolic links. This
1336+
function is a convenience wrapper around Win32::GetProcessPrivileges() and
1337+
Win32::IsDeveloperModeEnabled().
1338+
13121339
=item Win32::IsWinNT()
13131340
13141341
[CORE] Returns non zero if the Win32 subsystem is Windows NT.

t/Privileges.t

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ use warnings;
33

44
use Test;
55
use Win32;
6+
use Config;
7+
use File::Temp;
68

7-
plan tests => 5;
9+
plan tests => 7;
810

9-
ok(ref(Win32::GetProcessPrivileges) eq 'HASH');
11+
ok(ref(Win32::GetProcessPrivileges()) eq 'HASH');
1012
ok(ref(Win32::GetProcessPrivileges(Win32::GetCurrentProcessId())) eq 'HASH');
1113

1214
# All Windows PIDs are divisible by 4. It's an undocumented implementation
@@ -30,3 +32,24 @@ skip($skip, sub{
3032
# function doesn't segfault
3133
Win32::IsDeveloperModeEnabled();
3234
ok(1);
35+
36+
Win32::IsSymlinkCreationAllowed();
37+
ok(1);
38+
39+
$skip = $^O ne 'MSWin32' ? 'MSWin32-only test' : 0;
40+
$skip ||= !$Config{d_symlink} ? 'this perl doesn\'t have symlink()' : 0;
41+
42+
skip($skip, sub {
43+
my $tmpdir = File::Temp->newdir;
44+
my $dirname = $tmpdir->dirname;
45+
46+
if (Win32::IsSymlinkCreationAllowed()) {
47+
# we expect success
48+
return symlink("foo", $tmpdir->dirname . "/new_symlink") == 1;
49+
}
50+
else {
51+
# we expect failure
52+
return symlink("foo", $tmpdir->dirname . "/new_symlink") == 0;
53+
}
54+
});
55+

0 commit comments

Comments
 (0)