diff -r be7f5ba953bd Lib/test/test_os.py --- a/Lib/test/test_os.py Mon Sep 12 22:10:07 2016 -0700 +++ b/Lib/test/test_os.py Tue Sep 13 05:52:17 2016 +0000 @@ -1943,26 +1943,26 @@ @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") @support.skip_unless_symlink class Win32SymlinkTests(unittest.TestCase): - filelink = 'filelinktest' - filelink_target = os.path.abspath(__file__) - dirlink = 'dirlinktest' - dirlink_target = os.path.dirname(filelink_target) - missing_link = 'missing link' + # %TEMP% should be an NTFS volume. + dirlink_target = os.path.join(os.environ['TEMP'], support.TESTFN) + filelink_target = os.path.join(dirlink_target, support.TESTFN) + dirlink = os.path.join(os.environ['TEMP'], 'dirlinktest') + filelink = os.path.join(os.environ['TEMP'], 'filelinktest') + missing_link = os.path.join(os.environ['TEMP'], 'missinglinktest') def setUp(self): - assert os.path.exists(self.dirlink_target) - assert os.path.exists(self.filelink_target) - assert not os.path.exists(self.dirlink) - assert not os.path.exists(self.filelink) - assert not os.path.exists(self.missing_link) - - def tearDown(self): - if os.path.exists(self.filelink): - os.remove(self.filelink) - if os.path.exists(self.dirlink): - os.rmdir(self.dirlink) - if os.path.lexists(self.missing_link): - os.remove(self.missing_link) + assert not os.path.lexists(self.dirlink) + assert not os.path.lexists(self.filelink) + assert not os.path.lexists(self.missing_link) + assert not os.path.lexists(self.dirlink_target) + assert not os.path.lexists(self.filelink_target) + self.addCleanup(support.rmtree, self.dirlink_target) + self.addCleanup(support.rmdir, self.dirlink) + self.addCleanup(support.rmdir, self.missing_link) + self.addCleanup(support.unlink, self.missing_link) + self.addCleanup(support.unlink, self.filelink) + os.mkdir(self.dirlink_target) + create_file(self.filelink_target) def test_directory_link(self): os.symlink(self.dirlink_target, self.dirlink) @@ -1995,16 +1995,12 @@ # was created with target_is_dir==True. os.remove(self.missing_link) - @unittest.skip("currently fails; consider for improvement") def test_isdir_on_directory_link_to_missing_target(self): self._create_missing_dir_link() - # consider having isdir return true for directory links self.assertTrue(os.path.isdir(self.missing_link)) - @unittest.skip("currently fails; consider for improvement") def test_rmdir_on_directory_link_to_missing_target(self): self._create_missing_dir_link() - # consider allowing rmdir to remove directory links os.rmdir(self.missing_link) def check_stat(self, link, target): @@ -2016,17 +2012,11 @@ self.assertNotEqual(os.lstat(bytes_link), os.stat(bytes_link)) def test_12084(self): - level1 = os.path.abspath(support.TESTFN) + file1 = self.filelink_target + level1 = self.dirlink_target level2 = os.path.join(level1, "level2") level3 = os.path.join(level2, "level3") - self.addCleanup(support.rmtree, level1) - - os.mkdir(level1) - os.mkdir(level2) - os.mkdir(level3) - - file1 = os.path.abspath(os.path.join(level1, "file1")) - create_file(file1) + os.makedirs(level3) orig_dir = os.getcwd() try: @@ -2053,33 +2043,37 @@ @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") class Win32JunctionTests(unittest.TestCase): - junction = 'junctiontest' - junction_target = os.path.dirname(os.path.abspath(__file__)) + # %TEMP% should be an NTFS volume. + junction = os.path.join(os.environ['TEMP'], support.TESTFN) + junction_target = junction + '_target' def setUp(self): - assert os.path.exists(self.junction_target) - assert not os.path.exists(self.junction) - - def tearDown(self): - if os.path.exists(self.junction): - # os.rmdir delegates to Windows' RemoveDirectoryW, - # which removes junction points safely. - os.rmdir(self.junction) + assert not os.path.lexists(self.junction) + assert not os.path.lexists(self.junction_target) + self.addCleanup(support.rmdir, self.junction) + self.addCleanup(support.rmdir, self.junction_target) + os.mkdir(self.junction_target) def test_create_junction(self): _winapi.CreateJunction(self.junction_target, self.junction) - self.assertTrue(os.path.exists(self.junction)) + self.assertTrue(os.path.lexists(self.junction)) self.assertTrue(os.path.isdir(self.junction)) - # Junctions are not recognized as links. self.assertFalse(os.path.islink(self.junction)) + def test_stat(self): + _winapi.CreateJunction(self.junction_target, self.junction) + # lstat handles all reparse points, including junctions, + # as if they're links. Should it? islink and readlink + # accept only IO_REPARSE_TAG_SYMLINK. + self.assertEqual(os.stat(self.junction), os.stat(self.junction_target)) + self.assertNotEqual(os.lstat(self.junction), + os.lstat(self.junction_target)) + def test_unlink_removes_junction(self): _winapi.CreateJunction(self.junction_target, self.junction) - self.assertTrue(os.path.exists(self.junction)) - os.unlink(self.junction) - self.assertFalse(os.path.exists(self.junction)) + self.assertFalse(os.path.lexists(self.junction)) @support.skip_unless_symlink diff -r be7f5ba953bd Modules/posixmodule.c --- a/Modules/posixmodule.c Mon Sep 12 22:10:07 2016 -0700 +++ b/Modules/posixmodule.c Tue Sep 13 05:52:17 2016 +0000 @@ -318,10 +318,7 @@ #include #include /* for ShellExecute() */ #include /* for UNLEN */ -#ifdef SE_CREATE_SYMBOLIC_LINK_NAME /* Available starting with Vista */ #define HAVE_SYMLINK -static int win32_can_symlink = 0; -#endif #endif /* _MSC_VER */ #ifndef MAXPATHLEN @@ -7035,23 +7032,6 @@ #ifdef HAVE_SYMLINK #if defined(MS_WINDOWS) - -/* Grab CreateSymbolicLinkW dynamically from kernel32 */ -static DWORD (CALLBACK *Py_CreateSymbolicLinkW)(LPCWSTR, LPCWSTR, DWORD) = NULL; - -static int -check_CreateSymbolicLink(void) -{ - HINSTANCE hKernel32; - /* only recheck */ - if (Py_CreateSymbolicLinkW) - return 1; - hKernel32 = GetModuleHandleW(L"KERNEL32"); - *(FARPROC*)&Py_CreateSymbolicLinkW = GetProcAddress(hKernel32, - "CreateSymbolicLinkW"); - return Py_CreateSymbolicLinkW != NULL; -} - /* Remove the last portion of the path */ static void _dirnameW(WCHAR *path) @@ -7151,18 +7131,6 @@ int result; #endif -#ifdef MS_WINDOWS - if (!check_CreateSymbolicLink()) { - PyErr_SetString(PyExc_NotImplementedError, - "CreateSymbolicLink functions not found"); - return NULL; - } - if (!win32_can_symlink) { - PyErr_SetString(PyExc_OSError, "symbolic link privilege not held"); - return NULL; - } -#endif - if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) { PyErr_SetString(PyExc_ValueError, "symlink: src and dst must be the same type"); @@ -7174,12 +7142,17 @@ Py_BEGIN_ALLOW_THREADS /* if src is a directory, ensure target_is_directory==1 */ target_is_directory |= _check_dirW(src->wide, dst->wide); - result = Py_CreateSymbolicLinkW(dst->wide, src->wide, - target_is_directory); + result = CreateSymbolicLinkW(dst->wide, src->wide, + target_is_directory); Py_END_ALLOW_THREADS - if (!result) - return path_error2(src, dst); + if (!result) { + if (GetLastError() != ERROR_PRIVILEGE_NOT_HELD) + return path_error2(src, dst); + /* Preserve the legacy error message. */ + PyErr_SetString(PyExc_OSError, "symbolic link privilege not held"); + return NULL; + } #else @@ -12264,35 +12237,6 @@ {NULL, NULL} /* Sentinel */ }; - -#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS) -static int -enable_symlink() -{ - HANDLE tok; - TOKEN_PRIVILEGES tok_priv; - LUID luid; - - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &tok)) - return 0; - - if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid)) - return 0; - - 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 0; - - /* ERROR_NOT_ALL_ASSIGNED returned when the privilege can't be assigned. */ - return GetLastError() == ERROR_NOT_ALL_ASSIGNED ? 0 : 1; -} -#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */ - static int all_ins(PyObject *m) { @@ -12865,10 +12809,6 @@ PyObject *list; const char * const *trace; -#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS) - win32_can_symlink = enable_symlink(); -#endif - m = PyModule_Create(&posixmodule); if (m == NULL) return NULL;