mirror of
https://github.com/python/cpython.git
synced 2025-09-10 18:58:35 +00:00
gh-111877: Fixes stat() handling for inaccessible files on Windows (GH-113716)
This commit is contained in:
parent
e68806c712
commit
ed066481c7
3 changed files with 73 additions and 7 deletions
|
@ -3085,6 +3085,66 @@ class Win32NtTests(unittest.TestCase):
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
proc.terminate()
|
proc.terminate()
|
||||||
|
|
||||||
|
@support.requires_subprocess()
|
||||||
|
def test_stat_inaccessible_file(self):
|
||||||
|
filename = os_helper.TESTFN
|
||||||
|
ICACLS = os.path.expandvars(r"%SystemRoot%\System32\icacls.exe")
|
||||||
|
|
||||||
|
with open(filename, "wb") as f:
|
||||||
|
f.write(b'Test data')
|
||||||
|
|
||||||
|
stat1 = os.stat(filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Remove all permissions from the file
|
||||||
|
subprocess.check_output([ICACLS, filename, "/inheritance:r"],
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
except subprocess.CalledProcessError as ex:
|
||||||
|
if support.verbose:
|
||||||
|
print(ICACLS, filename, "/inheritance:r", "failed.")
|
||||||
|
print(ex.stdout.decode("oem", "replace").rstrip())
|
||||||
|
try:
|
||||||
|
os.unlink(filename)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
self.skipTest("Unable to create inaccessible file")
|
||||||
|
|
||||||
|
def cleanup():
|
||||||
|
# Give delete permission. We are the file owner, so we can do this
|
||||||
|
# even though we removed all permissions earlier.
|
||||||
|
subprocess.check_output([ICACLS, filename, "/grant", "Everyone:(D)"],
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
os.unlink(filename)
|
||||||
|
|
||||||
|
self.addCleanup(cleanup)
|
||||||
|
|
||||||
|
if support.verbose:
|
||||||
|
print("File:", filename)
|
||||||
|
print("stat with access:", stat1)
|
||||||
|
|
||||||
|
# First test - we shouldn't raise here, because we still have access to
|
||||||
|
# the directory and can extract enough information from its metadata.
|
||||||
|
stat2 = os.stat(filename)
|
||||||
|
|
||||||
|
if support.verbose:
|
||||||
|
print(" without access:", stat2)
|
||||||
|
|
||||||
|
# We cannot get st_dev/st_ino, so ensure those are 0 or else our test
|
||||||
|
# is not set up correctly
|
||||||
|
self.assertEqual(0, stat2.st_dev)
|
||||||
|
self.assertEqual(0, stat2.st_ino)
|
||||||
|
|
||||||
|
# st_mode and st_size should match (for a normal file, at least)
|
||||||
|
self.assertEqual(stat1.st_mode, stat2.st_mode)
|
||||||
|
self.assertEqual(stat1.st_size, stat2.st_size)
|
||||||
|
|
||||||
|
# st_ctime and st_mtime should be the same
|
||||||
|
self.assertEqual(stat1.st_ctime, stat2.st_ctime)
|
||||||
|
self.assertEqual(stat1.st_mtime, stat2.st_mtime)
|
||||||
|
|
||||||
|
# st_atime should be the same or later
|
||||||
|
self.assertGreaterEqual(stat1.st_atime, stat2.st_atime)
|
||||||
|
|
||||||
|
|
||||||
@os_helper.skip_unless_symlink
|
@os_helper.skip_unless_symlink
|
||||||
class NonLocalSymlinkTests(unittest.TestCase):
|
class NonLocalSymlinkTests(unittest.TestCase):
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:func:`os.stat` calls were returning incorrect time values for files that
|
||||||
|
could not be accessed directly.
|
|
@ -1886,8 +1886,9 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
|
||||||
HANDLE hFile;
|
HANDLE hFile;
|
||||||
BY_HANDLE_FILE_INFORMATION fileInfo;
|
BY_HANDLE_FILE_INFORMATION fileInfo;
|
||||||
FILE_BASIC_INFO basicInfo;
|
FILE_BASIC_INFO basicInfo;
|
||||||
|
FILE_BASIC_INFO *pBasicInfo = NULL;
|
||||||
FILE_ID_INFO idInfo;
|
FILE_ID_INFO idInfo;
|
||||||
FILE_ID_INFO *pIdInfo = &idInfo;
|
FILE_ID_INFO *pIdInfo = NULL;
|
||||||
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
|
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
|
||||||
DWORD fileType, error;
|
DWORD fileType, error;
|
||||||
BOOL isUnhandledTag = FALSE;
|
BOOL isUnhandledTag = FALSE;
|
||||||
|
@ -2038,14 +2039,17 @@ win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result,
|
||||||
retval = -1;
|
retval = -1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Successfully got FileBasicInfo, so we'll pass it along */
|
||||||
|
pBasicInfo = &basicInfo;
|
||||||
|
|
||||||
|
if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) {
|
||||||
|
/* Successfully got FileIdInfo, so pass it along */
|
||||||
|
pIdInfo = &idInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) {
|
_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo, pIdInfo, result);
|
||||||
/* Failed to get FileIdInfo, so do not pass it along */
|
|
||||||
pIdInfo = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, &basicInfo, pIdInfo, result);
|
|
||||||
update_st_mode_from_path(path, fileInfo.dwFileAttributes, result);
|
update_st_mode_from_path(path, fileInfo.dwFileAttributes, result);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue