Commit 52173d49 authored by Brian Curtin's avatar Brian Curtin

Fix #9333. Expose os.symlink on Windows only when usable.

In order to create symlinks on Windows, SeCreateSymbolicLinkPrivilege
is an account privilege that is required to be held by the user. Not only
must the privilege be enabled for the account, the activated privileges for
the currently running application must be adjusted to enable the requested
privilege.

Rather than exposing an additional function to be called prior to the user's
first os.symlink call, we handle the AdjustTokenPrivileges Windows API call
internally and only expose os.symlink when the privilege escalation was
successful.

Due to the change of only exposing os.symlink when it's available, we can
go back to the original test skipping methods of checking via `hasattr`.
parent 02524629
...@@ -1392,7 +1392,15 @@ Files and Directories ...@@ -1392,7 +1392,15 @@ Files and Directories
Symbolic link support was introduced in Windows 6.0 (Vista). :func:`symlink` Symbolic link support was introduced in Windows 6.0 (Vista). :func:`symlink`
will raise a :exc:`NotImplementedError` on Windows versions earlier than 6.0. will raise a :exc:`NotImplementedError` on Windows versions earlier than 6.0.
The *SeCreateSymbolicLinkPrivilege* is required in order to create symlinks.
.. note::
The *SeCreateSymbolicLinkPrivilege* is required in order to create
symlinks, so the function is only available when the privilege is held.
This privilege is not typically granted to regular users but is available
to accounts which can escalate privileges to the administrator level.
Either obtaining the privilege or running your application as an
administrator are ways to successfully create symlinks.
Availability: Unix, Windows. Availability: Unix, Windows.
......
...@@ -42,7 +42,7 @@ __all__ = [ ...@@ -42,7 +42,7 @@ __all__ = [
"set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner", "set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner",
"run_unittest", "run_doctest", "threading_setup", "threading_cleanup", "run_unittest", "run_doctest", "threading_setup", "threading_cleanup",
"reap_children", "cpython_only", "check_impl_detail", "get_attribute", "reap_children", "cpython_only", "check_impl_detail", "get_attribute",
"swap_item", "swap_attr", "can_symlink", "skip_unless_symlink"] "swap_item", "swap_attr"]
class Error(Exception): class Error(Exception):
...@@ -1256,27 +1256,6 @@ def reap_children(): ...@@ -1256,27 +1256,6 @@ def reap_children():
except: except:
break break
try:
from .symlink_support import enable_symlink_privilege
except:
enable_symlink_privilege = lambda: True
def can_symlink():
"""It's no longer sufficient to test for the presence of symlink in the
os module - on Windows XP and earlier, os.symlink exists but a
NotImplementedError is thrown.
"""
has_symlink = hasattr(os, 'symlink')
is_old_windows = sys.platform == "win32" and sys.getwindowsversion().major < 6
has_privilege = False if is_old_windows else enable_symlink_privilege()
return has_symlink and (not is_old_windows) and has_privilege
def skip_unless_symlink(test):
"""Skip decorator for tests that require functional symlink"""
selector = can_symlink()
msg = "Requires functional symlink implementation"
return [unittest.skip(msg)(test), test][selector]
@contextlib.contextmanager @contextlib.contextmanager
def swap_attr(obj, attr, new_val): def swap_attr(obj, attr, new_val):
"""Temporary swap out an attribute with a new object. """Temporary swap out an attribute with a new object.
......
import unittest import unittest
from test.support import run_unittest, TESTFN, skip_unless_symlink, can_symlink from test.support import run_unittest, TESTFN
import glob import glob
import os import os
import shutil import shutil
...@@ -25,7 +25,7 @@ class GlobTests(unittest.TestCase): ...@@ -25,7 +25,7 @@ class GlobTests(unittest.TestCase):
self.mktemp('ZZZ') self.mktemp('ZZZ')
self.mktemp('a', 'bcd', 'EF') self.mktemp('a', 'bcd', 'EF')
self.mktemp('a', 'bcd', 'efg', 'ha') self.mktemp('a', 'bcd', 'efg', 'ha')
if can_symlink(): if hasattr(os, "symlink"):
os.symlink(self.norm('broken'), self.norm('sym1')) os.symlink(self.norm('broken'), self.norm('sym1'))
os.symlink(self.norm('broken'), self.norm('sym2')) os.symlink(self.norm('broken'), self.norm('sym2'))
...@@ -98,7 +98,8 @@ class GlobTests(unittest.TestCase): ...@@ -98,7 +98,8 @@ class GlobTests(unittest.TestCase):
# either of these results are reasonable # either of these results are reasonable
self.assertIn(res[0], [self.tempdir, self.tempdir + os.sep]) self.assertIn(res[0], [self.tempdir, self.tempdir + os.sep])
@skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_glob_broken_symlinks(self): def test_glob_broken_symlinks(self):
eq = self.assertSequencesEqual_noorder eq = self.assertSequencesEqual_noorder
eq(self.glob('sym*'), [self.norm('sym1'), self.norm('sym2')]) eq(self.glob('sym*'), [self.norm('sym1'), self.norm('sym2')])
......
...@@ -304,7 +304,7 @@ class CGIHTTPServerTestCase(BaseTestCase): ...@@ -304,7 +304,7 @@ class CGIHTTPServerTestCase(BaseTestCase):
# The shebang line should be pure ASCII: use symlink if possible. # The shebang line should be pure ASCII: use symlink if possible.
# See issue #7668. # See issue #7668.
if support.can_symlink(): if hasattr(os, "symlink"):
self.pythonexe = os.path.join(self.parent_dir, 'python') self.pythonexe = os.path.join(self.parent_dir, 'python')
os.symlink(sys.executable, self.pythonexe) os.symlink(sys.executable, self.pythonexe)
else: else:
......
...@@ -541,7 +541,7 @@ class WalkTests(unittest.TestCase): ...@@ -541,7 +541,7 @@ class WalkTests(unittest.TestCase):
f = open(path, "w") f = open(path, "w")
f.write("I'm " + path + " and proud of it. Blame test_os.\n") f.write("I'm " + path + " and proud of it. Blame test_os.\n")
f.close() f.close()
if support.can_symlink(): if hasattr(os, "symlink"):
os.symlink(os.path.abspath(t2_path), link_path) os.symlink(os.path.abspath(t2_path), link_path)
sub2_tree = (sub2_path, ["link"], ["tmp3"]) sub2_tree = (sub2_path, ["link"], ["tmp3"])
else: else:
...@@ -585,7 +585,7 @@ class WalkTests(unittest.TestCase): ...@@ -585,7 +585,7 @@ class WalkTests(unittest.TestCase):
self.assertEqual(all[flipped + 1], (sub1_path, ["SUB11"], ["tmp2"])) self.assertEqual(all[flipped + 1], (sub1_path, ["SUB11"], ["tmp2"]))
self.assertEqual(all[2 - 2 * flipped], sub2_tree) self.assertEqual(all[2 - 2 * flipped], sub2_tree)
if support.can_symlink(): if hasattr(os, "symlink"):
# Walk, following symlinks. # Walk, following symlinks.
for root, dirs, files in os.walk(walk_path, followlinks=True): for root, dirs, files in os.walk(walk_path, followlinks=True):
if root == link_path: if root == link_path:
...@@ -1146,14 +1146,8 @@ class Win32KillTests(unittest.TestCase): ...@@ -1146,14 +1146,8 @@ class Win32KillTests(unittest.TestCase):
self._kill_with_event(signal.CTRL_BREAK_EVENT, "CTRL_BREAK_EVENT") self._kill_with_event(signal.CTRL_BREAK_EVENT, "CTRL_BREAK_EVENT")
def skipUnlessWindows6(test):
if (hasattr(sys, 'getwindowsversion')
and sys.getwindowsversion().major >= 6):
return test
return unittest.skip("Requires Windows Vista or later")(test)
@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"), "Requires symlink implementation")
class Win32SymlinkTests(unittest.TestCase): class Win32SymlinkTests(unittest.TestCase):
filelink = 'filelinktest' filelink = 'filelinktest'
filelink_target = os.path.abspath(__file__) filelink_target = os.path.abspath(__file__)
......
...@@ -10,7 +10,8 @@ class PlatformTest(unittest.TestCase): ...@@ -10,7 +10,8 @@ class PlatformTest(unittest.TestCase):
def test_architecture(self): def test_architecture(self):
res = platform.architecture() res = platform.architecture()
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_architecture_via_symlink(self): # issue3762 def test_architecture_via_symlink(self): # issue3762
# On Windows, the EXE needs to know where pythonXY.dll is at so we have # On Windows, the EXE needs to know where pythonXY.dll is at so we have
# to add the directory to the path. # to add the directory to the path.
......
...@@ -155,7 +155,7 @@ class PosixPathTest(unittest.TestCase): ...@@ -155,7 +155,7 @@ class PosixPathTest(unittest.TestCase):
f.write(b"foo") f.write(b"foo")
f.close() f.close()
self.assertIs(posixpath.islink(support.TESTFN + "1"), False) self.assertIs(posixpath.islink(support.TESTFN + "1"), False)
if support.can_symlink(): if hasattr(os, "symlink"):
os.symlink(support.TESTFN + "1", support.TESTFN + "2") os.symlink(support.TESTFN + "1", support.TESTFN + "2")
self.assertIs(posixpath.islink(support.TESTFN + "2"), True) self.assertIs(posixpath.islink(support.TESTFN + "2"), True)
os.remove(support.TESTFN + "1") os.remove(support.TESTFN + "1")
...@@ -180,7 +180,8 @@ class PosixPathTest(unittest.TestCase): ...@@ -180,7 +180,8 @@ class PosixPathTest(unittest.TestCase):
@unittest.skipIf( @unittest.skipIf(
sys.platform.startswith('win'), sys.platform.startswith('win'),
"posixpath.samefile does not work on links in Windows") "posixpath.samefile does not work on links in Windows")
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_samefile_on_links(self): def test_samefile_on_links(self):
test_fn1 = support.TESTFN + "1" test_fn1 = support.TESTFN + "1"
test_fn2 = support.TESTFN + "2" test_fn2 = support.TESTFN + "2"
...@@ -204,7 +205,8 @@ class PosixPathTest(unittest.TestCase): ...@@ -204,7 +205,8 @@ class PosixPathTest(unittest.TestCase):
@unittest.skipIf( @unittest.skipIf(
sys.platform.startswith('win'), sys.platform.startswith('win'),
"posixpath.samestat does not work on links in Windows") "posixpath.samestat does not work on links in Windows")
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_samestat_on_links(self): def test_samestat_on_links(self):
test_fn1 = support.TESTFN + "1" test_fn1 = support.TESTFN + "1"
test_fn2 = support.TESTFN + "2" test_fn2 = support.TESTFN + "2"
...@@ -273,7 +275,8 @@ class PosixPathTest(unittest.TestCase): ...@@ -273,7 +275,8 @@ class PosixPathTest(unittest.TestCase):
self.assertEqual(posixpath.normpath(b"///..//./foo/.//bar"), self.assertEqual(posixpath.normpath(b"///..//./foo/.//bar"),
b"/foo/bar") b"/foo/bar")
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash @skip_if_ABSTFN_contains_backslash
def test_realpath_basic(self): def test_realpath_basic(self):
# Basic operation. # Basic operation.
...@@ -283,7 +286,8 @@ class PosixPathTest(unittest.TestCase): ...@@ -283,7 +286,8 @@ class PosixPathTest(unittest.TestCase):
finally: finally:
support.unlink(ABSTFN) support.unlink(ABSTFN)
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash @skip_if_ABSTFN_contains_backslash
def test_realpath_symlink_loops(self): def test_realpath_symlink_loops(self):
# Bug #930024, return the path unchanged if we get into an infinite # Bug #930024, return the path unchanged if we get into an infinite
...@@ -307,7 +311,8 @@ class PosixPathTest(unittest.TestCase): ...@@ -307,7 +311,8 @@ class PosixPathTest(unittest.TestCase):
support.unlink(ABSTFN+"1") support.unlink(ABSTFN+"1")
support.unlink(ABSTFN+"2") support.unlink(ABSTFN+"2")
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash @skip_if_ABSTFN_contains_backslash
def test_realpath_resolve_parents(self): def test_realpath_resolve_parents(self):
# We also need to resolve any symlinks in the parents of a relative # We also need to resolve any symlinks in the parents of a relative
...@@ -328,7 +333,8 @@ class PosixPathTest(unittest.TestCase): ...@@ -328,7 +333,8 @@ class PosixPathTest(unittest.TestCase):
safe_rmdir(ABSTFN + "/y") safe_rmdir(ABSTFN + "/y")
safe_rmdir(ABSTFN) safe_rmdir(ABSTFN)
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash @skip_if_ABSTFN_contains_backslash
def test_realpath_resolve_before_normalizing(self): def test_realpath_resolve_before_normalizing(self):
# Bug #990669: Symbolic links should be resolved before we # Bug #990669: Symbolic links should be resolved before we
...@@ -358,7 +364,8 @@ class PosixPathTest(unittest.TestCase): ...@@ -358,7 +364,8 @@ class PosixPathTest(unittest.TestCase):
safe_rmdir(ABSTFN + "/k") safe_rmdir(ABSTFN + "/k")
safe_rmdir(ABSTFN) safe_rmdir(ABSTFN)
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash @skip_if_ABSTFN_contains_backslash
def test_realpath_resolve_first(self): def test_realpath_resolve_first(self):
# Bug #1213894: The first component of the path, if not absolute, # Bug #1213894: The first component of the path, if not absolute,
......
...@@ -271,7 +271,8 @@ class TestShutil(unittest.TestCase): ...@@ -271,7 +271,8 @@ class TestShutil(unittest.TestCase):
shutil.rmtree(src_dir) shutil.rmtree(src_dir)
shutil.rmtree(os.path.dirname(dst_dir)) shutil.rmtree(os.path.dirname(dst_dir))
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_dont_copy_file_onto_link_to_itself(self): def test_dont_copy_file_onto_link_to_itself(self):
# bug 851123. # bug 851123.
os.mkdir(TESTFN) os.mkdir(TESTFN)
...@@ -303,7 +304,8 @@ class TestShutil(unittest.TestCase): ...@@ -303,7 +304,8 @@ class TestShutil(unittest.TestCase):
except OSError: except OSError:
pass pass
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_rmtree_on_symlink(self): def test_rmtree_on_symlink(self):
# bug 1669. # bug 1669.
os.mkdir(TESTFN) os.mkdir(TESTFN)
...@@ -328,7 +330,8 @@ class TestShutil(unittest.TestCase): ...@@ -328,7 +330,8 @@ class TestShutil(unittest.TestCase):
finally: finally:
os.remove(TESTFN) os.remove(TESTFN)
@unittest.skipUnless(hasattr(os, 'mkfifo'), 'requires os.mkfifo') @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_copytree_named_pipe(self): def test_copytree_named_pipe(self):
os.mkdir(TESTFN) os.mkdir(TESTFN)
try: try:
...@@ -364,7 +367,8 @@ class TestShutil(unittest.TestCase): ...@@ -364,7 +367,8 @@ class TestShutil(unittest.TestCase):
shutil.copytree(src_dir, dst_dir, copy_function=_copy) shutil.copytree(src_dir, dst_dir, copy_function=_copy)
self.assertEqual(len(copied), 2) self.assertEqual(len(copied), 2)
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_copytree_dangling_symlinks(self): def test_copytree_dangling_symlinks(self):
# a dangling symlink raises an error at the end # a dangling symlink raises an error at the end
......
...@@ -12,7 +12,7 @@ import shutil ...@@ -12,7 +12,7 @@ import shutil
from copy import copy, deepcopy from copy import copy, deepcopy
from test.support import (run_unittest, TESTFN, unlink, get_attribute, from test.support import (run_unittest, TESTFN, unlink, get_attribute,
captured_stdout, skip_unless_symlink) captured_stdout)
import sysconfig import sysconfig
from sysconfig import (get_paths, get_platform, get_config_vars, from sysconfig import (get_paths, get_platform, get_config_vars,
...@@ -245,7 +245,8 @@ class TestSysConfig(unittest.TestCase): ...@@ -245,7 +245,8 @@ class TestSysConfig(unittest.TestCase):
'posix_home', 'posix_prefix', 'posix_user') 'posix_home', 'posix_prefix', 'posix_user')
self.assertEqual(get_scheme_names(), wanted) self.assertEqual(get_scheme_names(), wanted)
@skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_symlink(self): def test_symlink(self):
# On Windows, the EXE needs to know where pythonXY.dll is at so we have # On Windows, the EXE needs to know where pythonXY.dll is at so we have
# to add the directory to the path. # to add the directory to the path.
......
...@@ -322,7 +322,8 @@ class MiscReadTest(CommonReadTest): ...@@ -322,7 +322,8 @@ class MiscReadTest(CommonReadTest):
@unittest.skipUnless(hasattr(os, "link"), @unittest.skipUnless(hasattr(os, "link"),
"Missing hardlink implementation") "Missing hardlink implementation")
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_extract_hardlink(self): def test_extract_hardlink(self):
# Test hardlink extraction (e.g. bug #857297). # Test hardlink extraction (e.g. bug #857297).
tar = tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") tar = tarfile.open(tarname, errorlevel=1, encoding="iso8859-1")
...@@ -840,7 +841,8 @@ class WriteTest(WriteTestBase): ...@@ -840,7 +841,8 @@ class WriteTest(WriteTestBase):
os.remove(target) os.remove(target)
os.remove(link) os.remove(link)
@support.skip_unless_symlink @unittest.skipUnless(hasattr(os, "symlink"),
"Missing symlink implementation")
def test_symlink_size(self): def test_symlink_size(self):
path = os.path.join(TEMPDIR, "symlink") path = os.path.join(TEMPDIR, "symlink")
os.symlink("link_target", path) os.symlink("link_target", path)
......
...@@ -10,6 +10,9 @@ What's New in Python 3.2 Beta 1? ...@@ -10,6 +10,9 @@ What's New in Python 3.2 Beta 1?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #9333: Expose os.symlink only when the SeCreateSymbolicLinkPrivilege
is held by the user's account, i.e., when the function can actually be used.
- Issue #7475: Added transform() and untransform() methods to both bytes - Issue #7475: Added transform() and untransform() methods to both bytes
and string types. They can be used to access those codecs providing and string types. They can be used to access those codecs providing
bytes-to-bytes and string-to-string mappings. bytes-to-bytes and string-to-string mappings.
......
...@@ -277,6 +277,9 @@ extern int lstat(const char *, struct stat *); ...@@ -277,6 +277,9 @@ extern int lstat(const char *, struct stat *);
#include <windows.h> #include <windows.h>
#include <shellapi.h> /* for ShellExecute() */ #include <shellapi.h> /* for ShellExecute() */
#include <lmcons.h> /* for UNLEN */ #include <lmcons.h> /* for UNLEN */
#ifdef SE_CREATE_SYMBOLIC_LINK_NAME /* Available starting with Vista */
#define HAVE_SYMLINK
#endif
#endif /* _MSC_VER */ #endif /* _MSC_VER */
#if defined(PYCC_VACPP) && defined(PYOS_OS2) #if defined(PYCC_VACPP) && defined(PYOS_OS2)
...@@ -5091,7 +5094,7 @@ posix_readlink(PyObject *self, PyObject *args) ...@@ -5091,7 +5094,7 @@ posix_readlink(PyObject *self, PyObject *args)
#endif /* HAVE_READLINK */ #endif /* HAVE_READLINK */
#ifdef HAVE_SYMLINK #if defined(HAVE_SYMLINK) && !defined(MS_WINDOWS)
PyDoc_STRVAR(posix_symlink__doc__, PyDoc_STRVAR(posix_symlink__doc__,
"symlink(src, dst)\n\n\ "symlink(src, dst)\n\n\
Create a symbolic link pointing to src named dst."); Create a symbolic link pointing to src named dst.");
...@@ -5179,7 +5182,7 @@ win_readlink(PyObject *self, PyObject *args) ...@@ -5179,7 +5182,7 @@ win_readlink(PyObject *self, PyObject *args)
#endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */ #endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */
#if !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) #if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
/* Grab CreateSymbolicLinkW dynamically from kernel32 */ /* Grab CreateSymbolicLinkW dynamically from kernel32 */
static int has_CreateSymbolicLinkW = 0; static int has_CreateSymbolicLinkW = 0;
...@@ -5257,7 +5260,7 @@ win_symlink(PyObject *self, PyObject *args, PyObject *kwargs) ...@@ -5257,7 +5260,7 @@ win_symlink(PyObject *self, PyObject *args, PyObject *kwargs)
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
#endif /* !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */ #endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
#ifdef HAVE_TIMES #ifdef HAVE_TIMES
#if defined(PYCC_VACPP) && defined(PYOS_OS2) #if defined(PYCC_VACPP) && defined(PYOS_OS2)
...@@ -7779,13 +7782,13 @@ static PyMethodDef posix_methods[] = { ...@@ -7779,13 +7782,13 @@ static PyMethodDef posix_methods[] = {
{"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__}, {"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__},
{"stat", posix_stat, METH_VARARGS, posix_stat__doc__}, {"stat", posix_stat, METH_VARARGS, posix_stat__doc__},
{"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__}, {"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__},
#ifdef HAVE_SYMLINK #if defined(HAVE_SYMLINK) && !defined(MS_WINDOWS)
{"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__}, {"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__},
#endif /* HAVE_SYMLINK */ #endif /* HAVE_SYMLINK */
#if !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) #if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
{"symlink", (PyCFunction)win_symlink, METH_VARARGS | METH_KEYWORDS, {"_symlink", (PyCFunction)win_symlink, METH_VARARGS | METH_KEYWORDS,
win_symlink__doc__}, win_symlink__doc__},
#endif /* !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */ #endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
#ifdef HAVE_SYSTEM #ifdef HAVE_SYSTEM
{"system", posix_system, METH_VARARGS, posix_system__doc__}, {"system", posix_system, METH_VARARGS, posix_system__doc__},
#endif #endif
...@@ -8099,6 +8102,46 @@ static int insertvalues(PyObject *module) ...@@ -8099,6 +8102,46 @@ static int insertvalues(PyObject *module)
} }
#endif #endif
#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
void
enable_symlink()
{
HANDLE tok;
TOKEN_PRIVILEGES tok_priv;
LUID luid;
int meth_idx = 0;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &tok))
return;
if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid))
return;
tok_priv.PrivilegeCount = 1;
tok_priv.Privileges[0].Luid = luid;
tok_priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(tok, FALSE, &tok_priv,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL))
return;
if(GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
/* We couldn't acquire the necessary privilege, so leave the
method hidden for this user. */
return;
} else {
/* We've successfully acquired the symlink privilege so rename
the method to it's proper "os.symlink" name. */
while(posix_methods[meth_idx].ml_meth != (PyCFunction)win_symlink)
meth_idx++;
posix_methods[meth_idx].ml_name = "symlink";
return;
}
}
#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
static int static int
all_ins(PyObject *d) all_ins(PyObject *d)
{ {
...@@ -8360,6 +8403,10 @@ INITFUNC(void) ...@@ -8360,6 +8403,10 @@ INITFUNC(void)
{ {
PyObject *m, *v; PyObject *m, *v;
#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
enable_symlink();
#endif
m = PyModule_Create(&posixmodule); m = PyModule_Create(&posixmodule);
if (m == NULL) if (m == NULL)
return NULL; return NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment