mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Fix #9333. The symlink function is always available now, raising OSError
when the user doesn't hold the symbolic link privilege rather than hiding it.
This commit is contained in:
parent
baab9d0bf6
commit
3b4499c5c7
11 changed files with 55 additions and 46 deletions
|
@ -43,7 +43,7 @@ __all__ = [
|
||||||
"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", "requires_IEEE_754",
|
"swap_item", "swap_attr", "requires_IEEE_754",
|
||||||
"TestHandler", "Matcher"]
|
"TestHandler", "Matcher", "can_symlink", "skip_unless_symlink"]
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
|
@ -1412,3 +1412,23 @@ class Matcher(object):
|
||||||
else:
|
else:
|
||||||
result = dv.find(v) >= 0
|
result = dv.find(v) >= 0
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
_can_symlink = None
|
||||||
|
def can_symlink():
|
||||||
|
global _can_symlink
|
||||||
|
if _can_symlink is not None:
|
||||||
|
return _can_symlink
|
||||||
|
try:
|
||||||
|
os.symlink(TESTFN, TESTFN + "can_symlink")
|
||||||
|
can = True
|
||||||
|
except OSError:
|
||||||
|
can = False
|
||||||
|
_can_symlink = can
|
||||||
|
return can
|
||||||
|
|
||||||
|
def skip_unless_symlink(test):
|
||||||
|
"""Skip decorator for tests that require functional symlink"""
|
||||||
|
ok = can_symlink()
|
||||||
|
msg = "Requires functional symlink implementation"
|
||||||
|
return test if ok else unittest.skip(msg)(test)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import unittest
|
import unittest
|
||||||
from test.support import run_unittest, TESTFN
|
from test.support import run_unittest, TESTFN, skip_unless_symlink, can_symlink
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -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 hasattr(os, "symlink"):
|
if can_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,8 +98,7 @@ 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])
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"),
|
@skip_unless_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):
|
||||||
|
|
||||||
# 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 hasattr(os, "symlink"):
|
if support.can_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):
|
||||||
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 hasattr(os, "symlink"):
|
if support.can_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):
|
||||||
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 hasattr(os, "symlink"):
|
if support.can_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:
|
||||||
|
@ -1149,7 +1149,7 @@ class Win32KillTests(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
|
@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"), "Requires symlink implementation")
|
@support.skip_unless_symlink
|
||||||
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,8 +10,7 @@ class PlatformTest(unittest.TestCase):
|
||||||
def test_architecture(self):
|
def test_architecture(self):
|
||||||
res = platform.architecture()
|
res = platform.architecture()
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"),
|
@support.skip_unless_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):
|
||||||
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 hasattr(os, "symlink"):
|
if support.can_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")
|
||||||
|
|
|
@ -291,8 +291,7 @@ class TestShutil(unittest.TestCase):
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(TESTFN, ignore_errors=True)
|
shutil.rmtree(TESTFN, ignore_errors=True)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"),
|
@support.skip_unless_symlink
|
||||||
"Missing symlink implementation")
|
|
||||||
def test_dont_copy_file_onto_symlink_to_itself(self):
|
def test_dont_copy_file_onto_symlink_to_itself(self):
|
||||||
# bug 851123.
|
# bug 851123.
|
||||||
os.mkdir(TESTFN)
|
os.mkdir(TESTFN)
|
||||||
|
@ -312,8 +311,7 @@ class TestShutil(unittest.TestCase):
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(TESTFN, ignore_errors=True)
|
shutil.rmtree(TESTFN, ignore_errors=True)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"),
|
@support.skip_unless_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)
|
||||||
|
@ -338,8 +336,7 @@ class TestShutil(unittest.TestCase):
|
||||||
finally:
|
finally:
|
||||||
os.remove(TESTFN)
|
os.remove(TESTFN)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"),
|
@support.skip_unless_symlink
|
||||||
"Missing symlink implementation")
|
|
||||||
def test_copytree_named_pipe(self):
|
def test_copytree_named_pipe(self):
|
||||||
os.mkdir(TESTFN)
|
os.mkdir(TESTFN)
|
||||||
try:
|
try:
|
||||||
|
@ -375,8 +372,7 @@ 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)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"),
|
@support.skip_unless_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
|
||||||
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)
|
captured_stdout, skip_unless_symlink)
|
||||||
|
|
||||||
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,8 +245,7 @@ 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)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"),
|
@skip_unless_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,8 +322,7 @@ class MiscReadTest(CommonReadTest):
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "link"),
|
@unittest.skipUnless(hasattr(os, "link"),
|
||||||
"Missing hardlink implementation")
|
"Missing hardlink implementation")
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"),
|
@support.skip_unless_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")
|
||||||
|
@ -841,8 +840,7 @@ class WriteTest(WriteTestBase):
|
||||||
os.remove(target)
|
os.remove(target)
|
||||||
os.remove(link)
|
os.remove(link)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, "symlink"),
|
@support.skip_unless_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)
|
||||||
|
|
|
@ -18,6 +18,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue 9333: os.symlink now available regardless of user privileges.
|
||||||
|
The function now raises OSError on Windows >=6.0 when the user is unable
|
||||||
|
to create symbolic links. XP and 2003 still raise NotImplementedError.
|
||||||
|
|
||||||
- Issue #10783: struct.pack() doesn't encode implicitly unicode to UTF-8
|
- Issue #10783: struct.pack() doesn't encode implicitly unicode to UTF-8
|
||||||
anymore.
|
anymore.
|
||||||
|
|
||||||
|
|
|
@ -279,6 +279,7 @@ extern int lstat(const char *, struct stat *);
|
||||||
#include <lmcons.h> /* for UNLEN */
|
#include <lmcons.h> /* for UNLEN */
|
||||||
#ifdef SE_CREATE_SYMBOLIC_LINK_NAME /* Available starting with Vista */
|
#ifdef SE_CREATE_SYMBOLIC_LINK_NAME /* Available starting with Vista */
|
||||||
#define HAVE_SYMLINK
|
#define HAVE_SYMLINK
|
||||||
|
static int win32_can_symlink = 0;
|
||||||
#endif
|
#endif
|
||||||
#endif /* _MSC_VER */
|
#endif /* _MSC_VER */
|
||||||
|
|
||||||
|
@ -5243,6 +5244,10 @@ win_symlink(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i:symlink",
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i:symlink",
|
||||||
kwlist, &src, &dest, &target_is_directory))
|
kwlist, &src, &dest, &target_is_directory))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (win32_can_symlink == 0)
|
||||||
|
return PyErr_Format(PyExc_OSError, "symbolic link privilege not held");
|
||||||
|
|
||||||
if (!convert_to_unicode(&src)) { return NULL; }
|
if (!convert_to_unicode(&src)) { return NULL; }
|
||||||
if (!convert_to_unicode(&dest)) {
|
if (!convert_to_unicode(&dest)) {
|
||||||
Py_DECREF(src);
|
Py_DECREF(src);
|
||||||
|
@ -7801,7 +7806,7 @@ static PyMethodDef posix_methods[] = {
|
||||||
{"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
|
||||||
|
@ -8118,7 +8123,7 @@ static int insertvalues(PyObject *module)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
|
#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
|
||||||
void
|
static int
|
||||||
enable_symlink()
|
enable_symlink()
|
||||||
{
|
{
|
||||||
HANDLE tok;
|
HANDLE tok;
|
||||||
|
@ -8127,10 +8132,10 @@ enable_symlink()
|
||||||
int meth_idx = 0;
|
int meth_idx = 0;
|
||||||
|
|
||||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &tok))
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &tok))
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid))
|
if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid))
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
tok_priv.PrivilegeCount = 1;
|
tok_priv.PrivilegeCount = 1;
|
||||||
tok_priv.Privileges[0].Luid = luid;
|
tok_priv.Privileges[0].Luid = luid;
|
||||||
|
@ -8139,21 +8144,10 @@ enable_symlink()
|
||||||
if (!AdjustTokenPrivileges(tok, FALSE, &tok_priv,
|
if (!AdjustTokenPrivileges(tok, FALSE, &tok_priv,
|
||||||
sizeof(TOKEN_PRIVILEGES),
|
sizeof(TOKEN_PRIVILEGES),
|
||||||
(PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL))
|
(PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL))
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
if(GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
|
/* ERROR_NOT_ALL_ASSIGNED returned when the privilege can't be assigned. */
|
||||||
/* We couldn't acquire the necessary privilege, so leave the
|
return GetLastError() == ERROR_NOT_ALL_ASSIGNED ? 0 : 1;
|
||||||
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) */
|
#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
|
||||||
|
|
||||||
|
@ -8419,7 +8413,7 @@ INITFUNC(void)
|
||||||
PyObject *m, *v;
|
PyObject *m, *v;
|
||||||
|
|
||||||
#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
|
#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
|
||||||
enable_symlink();
|
win32_can_symlink = enable_symlink();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m = PyModule_Create(&posixmodule);
|
m = PyModule_Create(&posixmodule);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue