From ab76c06e4b9e5116d46c8456bebdab3427589e72 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sat, 13 Apr 2024 12:03:17 +0200 Subject: [PATCH 01/41] Speedup `ntpath.exists` Co-authored-by: Eryk Sun --- Lib/ntpath.py | 1 + Lib/test/test_genericpath.py | 33 +++++---- Lib/test/test_ntpath.py | 2 + Modules/clinic/posixmodule.c.h | 65 ++++++++++++++++- Modules/posixmodule.c | 129 ++++++++++++++++++++------------- 5 files changed, 164 insertions(+), 66 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index f5d1a2195dd633..d8da88993e9a38 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -899,6 +899,7 @@ def commonpath(paths): from nt import _path_isfile as isfile from nt import _path_islink as islink from nt import _path_exists as exists + from nt import _path_lexists as lexists except ImportError: # Use genericpath.* as imported above pass diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index f407ee3caf154c..58ad4aba0c9db4 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -128,31 +128,34 @@ def test_filetime(self): ) def test_exists(self): + def check_exists(filename, expected): + bfilename = os.fsencode(filename) + self.assertIs(self.pathmodule.exists(filename), expected) + self.assertIs(self.pathmodule.exists(bfilename), expected) + + def check_lexists(filename, expected): + bfilename = os.fsencode(filename) + self.assertIs(self.pathmodule.lexists(filename), expected) + self.assertIs(self.pathmodule.lexists(bfilename), expected) + filename = os_helper.TESTFN bfilename = os.fsencode(filename) self.addCleanup(os_helper.unlink, filename) - self.assertIs(self.pathmodule.exists(filename), False) - self.assertIs(self.pathmodule.exists(bfilename), False) + check_exists(filename, False) + check_lexists(filename, False) create_file(filename) - self.assertIs(self.pathmodule.exists(filename), True) - self.assertIs(self.pathmodule.exists(bfilename), True) - + check_exists(filename, True) self.assertIs(self.pathmodule.exists(filename + '\udfff'), False) self.assertIs(self.pathmodule.exists(bfilename + b'\xff'), False) - self.assertIs(self.pathmodule.exists(filename + '\x00'), False) - self.assertIs(self.pathmodule.exists(bfilename + b'\x00'), False) - - if self.pathmodule is not genericpath: - self.assertIs(self.pathmodule.lexists(filename), True) - self.assertIs(self.pathmodule.lexists(bfilename), True) + check_exists(filename + '\x00', False) - self.assertIs(self.pathmodule.lexists(filename + '\udfff'), False) - self.assertIs(self.pathmodule.lexists(bfilename + b'\xff'), False) - self.assertIs(self.pathmodule.lexists(filename + '\x00'), False) - self.assertIs(self.pathmodule.lexists(bfilename + b'\x00'), False) + check_lexists(filename, True) + self.assertIs(self.pathmodule.lexists(filename + '\udfff'), False) + self.assertIs(self.pathmodule.lexists(bfilename + b'\xff'), False) + check_lexists(filename + '\x00', False) @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") @unittest.skipIf(is_emscripten, "Emscripten pipe fds have no stat") diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 31156130fcc747..28706298618e04 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1115,6 +1115,8 @@ def test_fast_paths_in_use(self): self.assertFalse(inspect.isfunction(os.path.islink)) self.assertTrue(os.path.exists is nt._path_exists) self.assertFalse(inspect.isfunction(os.path.exists)) + self.assertTrue(os.path.lexists is nt._path_lexists) + self.assertFalse(inspect.isfunction(os.path.lexists)) @unittest.skipIf(os.name != 'nt', "Dev Drives only exist on Win32") def test_isdevdrive(self): diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 0398629e3c10ce..c2c85fb9fbeb36 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -2191,6 +2191,65 @@ os__path_exists(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj #if defined(MS_WINDOWS) +PyDoc_STRVAR(os__path_lexists__doc__, +"_path_lexists($module, /, path)\n" +"--\n" +"\n" +"Test whether a path exists. Returns True for broken symbolic links"); + +#define OS__PATH_LEXISTS_METHODDEF \ + {"_path_lexists", _PyCFunction_CAST(os__path_lexists), METH_FASTCALL|METH_KEYWORDS, os__path_lexists__doc__}, + +static PyObject * +os__path_lexists_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_lexists(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_lexists", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_lexists_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + PyDoc_STRVAR(os__path_islink__doc__, "_path_islink($module, /, path)\n" "--\n" @@ -12051,6 +12110,10 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #define OS__PATH_EXISTS_METHODDEF #endif /* !defined(OS__PATH_EXISTS_METHODDEF) */ +#ifndef OS__PATH_LEXISTS_METHODDEF + #define OS__PATH_LEXISTS_METHODDEF +#endif /* !defined(OS__PATH_LEXISTS_METHODDEF) */ + #ifndef OS__PATH_ISLINK_METHODDEF #define OS__PATH_ISLINK_METHODDEF #endif /* !defined(OS__PATH_ISLINK_METHODDEF) */ @@ -12602,4 +12665,4 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ -/*[clinic end generated code: output=511f0788a6b90db0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=a2b2651b67aa67fa input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 5e54cf64cd563e..991be86f4e62ed 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5280,25 +5280,12 @@ os__path_isfile_impl(PyObject *module, PyObject *path) } -/*[clinic input] -os._path_exists - - path: 'O' - -Test whether a path exists. Returns False for broken symbolic links - -[clinic start generated code]*/ - static PyObject * -os__path_exists_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=f508c3b35e13a249 input=380f77cdfa0f7ae8]*/ +nt_exists(PyObject *path, int follow_symlinks) { - HANDLE hfile; - BOOL close_file = TRUE; path_t _path = PATH_T_INITIALIZE("exists", "path", 0, 1); - int result; - BOOL slow_path = TRUE; - FILE_STAT_BASIC_INFORMATION statInfo; + HANDLE hfile; + int result = 0; if (!path_converter(path, &_path)) { path_cleanup(&_path); @@ -5310,49 +5297,56 @@ os__path_exists_impl(PyObject *module, PyObject *path) } Py_BEGIN_ALLOW_THREADS - if (_path.wide) { + if (_path.fd != -1) { + hfile = _Py_get_osfhandle_noraise(_path.fd); + if (hfile != INVALID_HANDLE_VALUE) { + result = 1; + } + } + else if (_path.wide) { + BOOL slow_path = TRUE; + FILE_STAT_BASIC_INFORMATION statInfo; if (_Py_GetFileInformationByName(_path.wide, FileStatBasicByNameInfo, - &statInfo, sizeof(statInfo))) { - if (!(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + &statInfo, sizeof(statInfo))) + { + if (!(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || + !follow_symlinks && + IsReparseTagNameSurrogate(statInfo.ReparseTag)) + { slow_path = FALSE; result = 1; } - } else if (_Py_GetFileInformationByName_ErrorIsTrustworthy(GetLastError())) { - slow_path = FALSE; - result = 0; - } - } - if (slow_path) { - if (_path.fd != -1) { - hfile = _Py_get_osfhandle_noraise(_path.fd); - close_file = FALSE; - } - else { - hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - } - if (hfile != INVALID_HANDLE_VALUE) { - result = 1; - if (close_file) { - CloseHandle(hfile); - } + } else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( + GetLastError())) + { + slow_path = FALSE; } - else { + if (slow_path) { STRUCT_STAT st; - switch (GetLastError()) { - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_CANT_ACCESS_FILE: - case ERROR_INVALID_PARAMETER: - if (STAT(_path.wide, &st)) { - result = 0; + if (!follow_symlinks) { + if (!LSTAT(_path.wide, &st)) { + result = 1; } - else { + } + else { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (hfile != INVALID_HANDLE_VALUE) { + CloseHandle(hfile); result = 1; } - break; - default: - result = 0; + else { + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (!STAT(_path.wide, &st)) { + result = 1; + } + } + } } } } @@ -5366,6 +5360,40 @@ os__path_exists_impl(PyObject *module, PyObject *path) } +/*[clinic input] +os._path_exists + + path: 'O' + +Test whether a path exists. Returns False for broken symbolic links + +[clinic start generated code]*/ + +static PyObject * +os__path_exists_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=f508c3b35e13a249 input=380f77cdfa0f7ae8]*/ +{ + return nt_exists(path, 1); +} + + +/*[clinic input] +os._path_lexists + + path: 'O' + +Test whether a path exists. Returns True for broken symbolic links + +[clinic start generated code]*/ + +static PyObject * +os__path_lexists_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=b9a42a50b1df6651 input=5f14942c64196ba2]*/ +{ + return nt_exists(path, 0); +} + + /*[clinic input] os._path_islink @@ -16843,6 +16871,7 @@ static PyMethodDef posix_methods[] = { OS__PATH_ISFILE_METHODDEF OS__PATH_ISLINK_METHODDEF OS__PATH_EXISTS_METHODDEF + OS__PATH_LEXISTS_METHODDEF OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF {NULL, NULL} /* Sentinel */ From 1c5350d251b56a325dba753184f79bfb808e63b8 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Sat, 13 Apr 2024 12:13:29 +0200 Subject: [PATCH 02/41] Update comment --- Lib/ntpath.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index d8da88993e9a38..06466bb9cb9017 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -892,8 +892,8 @@ def commonpath(paths): try: - # The isdir(), isfile(), islink() and exists() implementations in - # genericpath use os.stat(). This is overkill on Windows. Use simpler + # The isdir(), isfile(), islink(), exists() and lexists() implementations + # in genericpath use os.stat(). This is overkill on Windows. Use simpler # builtin functions if they are available. from nt import _path_isdir as isdir from nt import _path_isfile as isfile From d7017ac6063c483d6a21dacf3544b3cb844bb669 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 11:30:10 +0000 Subject: [PATCH 03/41] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst new file mode 100644 index 00000000000000..cd73fc06b90b77 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst @@ -0,0 +1 @@ +Speedup :func:`os.path.lexists` on Windows. From 29633380cb0d8f845c694867967a38e7f90feb46 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Mon, 15 Apr 2024 07:59:24 +0200 Subject: [PATCH 04/41] Improve `nt_exists()` Co-authored-by: Eryk Sun --- Modules/posixmodule.c | 58 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 991be86f4e62ed..bf8af2b8576f8b 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5285,6 +5285,7 @@ nt_exists(PyObject *path, int follow_symlinks) { path_t _path = PATH_T_INITIALIZE("exists", "path", 0, 1); HANDLE hfile; + BOOL traverse = follow_symlinks; int result = 0; if (!path_converter(path, &_path)) { @@ -5307,7 +5308,7 @@ nt_exists(PyObject *path, int follow_symlinks) BOOL slow_path = TRUE; FILE_STAT_BASIC_INFORMATION statInfo; if (_Py_GetFileInformationByName(_path.wide, FileStatBasicByNameInfo, - &statInfo, sizeof(statInfo))) + &statInfo, sizeof(statInfo))) { if (!(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || !follow_symlinks && @@ -5316,27 +5317,66 @@ nt_exists(PyObject *path, int follow_symlinks) slow_path = FALSE; result = 1; } - } else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( + else { + // reparse point but not name-surrogate + traverse = TRUE; + } + } + else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( GetLastError())) { slow_path = FALSE; } if (slow_path) { - STRUCT_STAT st; - if (!follow_symlinks) { - if (!LSTAT(_path.wide, &st)) { - result = 1; + BOOL traverse = follow_symlinks; + if (!traverse) { + hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | + FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hfile != INVALID_HANDLE_VALUE) { + FILE_ATTRIBUTE_TAG_INFO info; + if (GetFileInformationByHandleEx(hfile, + FileAttributeTagInfo, &info, sizeof(info))) + { + if (!(info.FileAttributes & + FILE_ATTRIBUTE_REPARSE_POINT) || + IsReparseTagNameSurrogate(info.ReparseTag)) + { + result = 1; + } + else { + // reparse point but not name-surrogate + traverse = TRUE; + } + } + else { + // device or legacy filesystem + result = 1; + } + CloseHandle(hfile); + } + else { + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (!LSTAT(_path.wide, &st)) { + result = 1; + } + } } } - else { + if (traverse) { hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, - NULL); + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hfile != INVALID_HANDLE_VALUE) { CloseHandle(hfile); result = 1; } else { + STRUCT_STAT st; switch (GetLastError()) { case ERROR_ACCESS_DENIED: case ERROR_SHARING_VIOLATION: From e833f0468cdf2fb6c453e75d995af6e91900d980 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 15 Apr 2024 22:35:43 +0200 Subject: [PATCH 05/41] Apply suggestions from code review Co-authored-by: Serhiy Storchaka --- Modules/posixmodule.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index bf8af2b8576f8b..ec401ab0873521 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5403,9 +5403,10 @@ nt_exists(PyObject *path, int follow_symlinks) /*[clinic input] os._path_exists - path: 'O' + path: object + / -Test whether a path exists. Returns False for broken symbolic links +Test whether a path exists. Returns False for broken symbolic links. [clinic start generated code]*/ @@ -5420,9 +5421,10 @@ os__path_exists_impl(PyObject *module, PyObject *path) /*[clinic input] os._path_lexists - path: 'O' + path: object + / -Test whether a path exists. Returns True for broken symbolic links +Test whether a path exists. Returns True for broken symbolic links. [clinic start generated code]*/ From d0493c2ebe196f502e9629a5162e005f525b6dd6 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Mon, 15 Apr 2024 22:48:10 +0200 Subject: [PATCH 06/41] Revert helper functions --- Lib/test/test_genericpath.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index 58ad4aba0c9db4..bf04b3fecf7057 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -128,34 +128,33 @@ def test_filetime(self): ) def test_exists(self): - def check_exists(filename, expected): - bfilename = os.fsencode(filename) - self.assertIs(self.pathmodule.exists(filename), expected) - self.assertIs(self.pathmodule.exists(bfilename), expected) - - def check_lexists(filename, expected): - bfilename = os.fsencode(filename) - self.assertIs(self.pathmodule.lexists(filename), expected) - self.assertIs(self.pathmodule.lexists(bfilename), expected) - filename = os_helper.TESTFN bfilename = os.fsencode(filename) self.addCleanup(os_helper.unlink, filename) - check_exists(filename, False) - check_lexists(filename, False) + self.assertIs(self.pathmodule.exists(filename), False) + self.assertIs(self.pathmodule.exists(bfilename), False) + + self.assertIs(self.pathmodule.lexists(filename), False) + self.assertIs(self.pathmodule.lexists(bfilename), False) create_file(filename) - check_exists(filename, True) + self.assertIs(self.pathmodule.exists(filename), True) + self.assertIs(self.pathmodule.exists(bfilename), True) + self.assertIs(self.pathmodule.exists(filename + '\udfff'), False) self.assertIs(self.pathmodule.exists(bfilename + b'\xff'), False) - check_exists(filename + '\x00', False) + self.assertIs(self.pathmodule.exists(filename + '\x00'), False) + self.assertIs(self.pathmodule.exists(bfilename + b'\x00'), False) + + self.assertIs(self.pathmodule.lexists(filename), True) + self.assertIs(self.pathmodule.lexists(bfilename), True) - check_lexists(filename, True) self.assertIs(self.pathmodule.lexists(filename + '\udfff'), False) self.assertIs(self.pathmodule.lexists(bfilename + b'\xff'), False) - check_lexists(filename + '\x00', False) + self.assertIs(self.pathmodule.lexists(filename + '\x00'), False) + self.assertIs(self.pathmodule.lexists(bfilename + b'\x00'), False) @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") @unittest.skipIf(is_emscripten, "Emscripten pipe fds have no stat") From 4403f4a635b359981e13b330d941baf2963a0027 Mon Sep 17 00:00:00 2001 From: nineteendo Date: Mon, 15 Apr 2024 22:53:48 +0200 Subject: [PATCH 07/41] Update argument clinic --- Modules/clinic/posixmodule.c.h | 106 +++------------------------------ Modules/posixmodule.c | 8 +-- 2 files changed, 11 insertions(+), 103 deletions(-) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index c2c85fb9fbeb36..cd7d8d8ff170f4 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -2133,118 +2133,26 @@ os__path_isfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj #if defined(MS_WINDOWS) PyDoc_STRVAR(os__path_exists__doc__, -"_path_exists($module, /, path)\n" +"_path_exists($module, path, /)\n" "--\n" "\n" -"Test whether a path exists. Returns False for broken symbolic links"); +"Test whether a path exists. Returns False for broken symbolic links."); #define OS__PATH_EXISTS_METHODDEF \ - {"_path_exists", _PyCFunction_CAST(os__path_exists), METH_FASTCALL|METH_KEYWORDS, os__path_exists__doc__}, - -static PyObject * -os__path_exists_impl(PyObject *module, PyObject *path); - -static PyObject * -os__path_exists(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 1 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(path), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"path", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "_path_exists", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[1]; - PyObject *path; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); - if (!args) { - goto exit; - } - path = args[0]; - return_value = os__path_exists_impl(module, path); - -exit: - return return_value; -} + {"_path_exists", (PyCFunction)os__path_exists, METH_O, os__path_exists__doc__}, #endif /* defined(MS_WINDOWS) */ #if defined(MS_WINDOWS) PyDoc_STRVAR(os__path_lexists__doc__, -"_path_lexists($module, /, path)\n" +"_path_lexists($module, path, /)\n" "--\n" "\n" -"Test whether a path exists. Returns True for broken symbolic links"); +"Test whether a path exists. Returns True for broken symbolic links."); #define OS__PATH_LEXISTS_METHODDEF \ - {"_path_lexists", _PyCFunction_CAST(os__path_lexists), METH_FASTCALL|METH_KEYWORDS, os__path_lexists__doc__}, - -static PyObject * -os__path_lexists_impl(PyObject *module, PyObject *path); - -static PyObject * -os__path_lexists(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - - #define NUM_KEYWORDS 1 - static struct { - PyGC_Head _this_is_not_used; - PyObject_VAR_HEAD - PyObject *ob_item[NUM_KEYWORDS]; - } _kwtuple = { - .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(path), }, - }; - #undef NUM_KEYWORDS - #define KWTUPLE (&_kwtuple.ob_base.ob_base) - - #else // !Py_BUILD_CORE - # define KWTUPLE NULL - #endif // !Py_BUILD_CORE - - static const char * const _keywords[] = {"path", NULL}; - static _PyArg_Parser _parser = { - .keywords = _keywords, - .fname = "_path_lexists", - .kwtuple = KWTUPLE, - }; - #undef KWTUPLE - PyObject *argsbuf[1]; - PyObject *path; - - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); - if (!args) { - goto exit; - } - path = args[0]; - return_value = os__path_lexists_impl(module, path); - -exit: - return return_value; -} + {"_path_lexists", (PyCFunction)os__path_lexists, METH_O, os__path_lexists__doc__}, #endif /* defined(MS_WINDOWS) */ @@ -12665,4 +12573,4 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ -/*[clinic end generated code: output=a2b2651b67aa67fa input=a9049054013a1b77]*/ +/*[clinic end generated code: output=66245167be59b12f input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index ec401ab0873521..4bce924f388d97 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5411,8 +5411,8 @@ Test whether a path exists. Returns False for broken symbolic links. [clinic start generated code]*/ static PyObject * -os__path_exists_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=f508c3b35e13a249 input=380f77cdfa0f7ae8]*/ +os__path_exists(PyObject *module, PyObject *path) +/*[clinic end generated code: output=617b7575ba0644bc input=242708cabb67c407]*/ { return nt_exists(path, 1); } @@ -5429,8 +5429,8 @@ Test whether a path exists. Returns True for broken symbolic links. [clinic start generated code]*/ static PyObject * -os__path_lexists_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=b9a42a50b1df6651 input=5f14942c64196ba2]*/ +os__path_lexists(PyObject *module, PyObject *path) +/*[clinic end generated code: output=c7c89aa6d6e341df input=536ed4b0a7d4f723]*/ { return nt_exists(path, 0); } From 7a9038f03255455381e3016a1bcc3bfded8e69c0 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 22 Apr 2024 20:58:23 +0200 Subject: [PATCH 08/41] Update 2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst --- .../2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst index cd73fc06b90b77..3a562884283b1c 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst @@ -1 +1 @@ -Speedup :func:`os.path.lexists` on Windows. +Speedup :func:`os.path.lexists` on Windows with a native implementation. From 30603f05186d922a108427cba2432cf2cf4a379d Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Wed, 8 May 2024 10:34:49 +0200 Subject: [PATCH 09/41] Refactor `nt._path_is*` Co-authored-by: Eryk Sun --- Modules/posixmodule.c | 355 +++++++++++++++++++++--------------------- 1 file changed, 176 insertions(+), 179 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 9f4be98b35186e..daff975c1acb5f 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5088,6 +5088,155 @@ os__path_splitroot_impl(PyObject *module, path_t *path) } +#define PY_IFREG 1 +#define PY_IFDIR 2 +#define PY_IFLNK 3 +#define PY_IFMNT 4 + +static BOOL +_testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) +{ + assert(refFileType == PY_IFREG || refFileType == PY_IFDIR || + refFileType == PY_IFLNK || refFileType == PY_IFMNT); + + DWORD fileDevType = GetFileType(hfile); + if ((fileDevType == FILE_TYPE_CHAR) || + (fileDevType != FILE_TYPE_DISK && diskOnly) || + (fileDevType == FILE_TYPE_UNKNOWN && GetLastError() != 0)) + { + return FALSE; + } + + FILE_ATTRIBUTE_TAG_INFO info; + DWORD attributes, reparseTag; + if (GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, + sizeof(info))) + { + attributes = info.FileAttributes; + reparseTag = info.ReparseTag; + } + else { + FILE_BASIC_INFO info; + if (GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, + sizeof(info))) + { + attributes = info.FileAttributes; + reparseTag = 0; + } + else { + return FALSE; + } + } + + if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (refFileType == PY_IFLNK) { + return reparseTag == IO_REPARSE_TAG_SYMLINK; + } + if (refFileType == PY_IFMNT) { + return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; + } + // Non-surrogate reparse points aren't supported by handle. Just + // return False. Supporting them requires querying and opening the + // final path with reparsing enabled. + } + else if (refFileType == PY_IFREG) { + return ((fileDevType == FILE_TYPE_DISK) && + !(attributes & FILE_ATTRIBUTE_DIRECTORY)); + } + else if (refFileType == PY_IFDIR) { + return attributes & FILE_ATTRIBUTE_DIRECTORY; + } + + return FALSE; +} + +static BOOL +_testFileTypeByName(LPCWSTR path, int refFileType) +{ + assert(refFileType == PY_IFREG || refFileType == PY_IFDIR || + refFileType == PY_IFLNK || refFileType == PY_IFMNT); + + FILE_STAT_BASIC_INFORMATION info; + if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, + &info, sizeof(info))) { + if (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (refFileType == PY_IFLNK) { + return info.ReparseTag == IO_REPARSE_TAG_SYMLINK; + } + else if (refFileType == PY_IFMNT) { + return info.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT; + } + else if ((refFileType == PY_IFREG) && + (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + return FALSE; + } + else if ((refFileType == PY_IFDIR) && + !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + return FALSE; + } + } + else if (refFileType == PY_IFLNK || refFileType == PY_IFMNT) { + return FALSE; + } + else if (refFileType == PY_IFREG) { + return !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY); + } + else if (refFileType == PY_IFDIR) { + return info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; + } + } + else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( + GetLastError())) + { + return FALSE; + } + + HANDLE hfile; + DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; + if (refFileType == PY_IFLNK || refFileType == PY_IFMNT) { + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, + flags, NULL); + if (hfile != INVALID_HANDLE_VALUE) { + BOOL result = _testFileTypeByHandle(hfile, refFileType, FALSE); + CloseHandle(hfile); + return result; + } + + int rc; + STRUCT_STAT st; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + if (refFileType == PY_IFREG || refFileType == PY_IFDIR) { + rc = STAT(path, &st); + } + else { + rc = LSTAT(path, &st); + } + if (!rc) { + switch (refFileType) { + case PY_IFREG: + return S_ISREG(st.st_mode); + case PY_IFDIR: + return S_ISDIR(st.st_mode); + case PY_IFLNK: + return S_ISLNK(st.st_mode); + case PY_IFMNT: + return st.st_reparse_tag == IO_REPARSE_TAG_MOUNT_POINT; + } + } + } + + return FALSE; +} + + /*[clinic input] os._path_isdir @@ -5101,13 +5250,8 @@ static PyObject * os__path_isdir_impl(PyObject *module, PyObject *s) /*[clinic end generated code: output=9d87ab3c8b8a4e61 input=c17f7ef21d22d64e]*/ { - HANDLE hfile; - BOOL close_file = TRUE; - FILE_BASIC_INFO info; path_t _path = PATH_T_INITIALIZE("isdir", "s", 0, 1); - int result; - BOOL slow_path = TRUE; - FILE_STAT_BASIC_INFORMATION statInfo; + BOOL result = FALSE; if (!path_converter(s, &_path)) { path_cleanup(&_path); @@ -5119,62 +5263,15 @@ os__path_isdir_impl(PyObject *module, PyObject *s) } Py_BEGIN_ALLOW_THREADS - if (_path.wide) { - if (_Py_GetFileInformationByName(_path.wide, FileStatBasicByNameInfo, - &statInfo, sizeof(statInfo))) { - if (!(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - slow_path = FALSE; - result = statInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; - } else if (!(statInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - slow_path = FALSE; - result = 0; - } - } else if (_Py_GetFileInformationByName_ErrorIsTrustworthy(GetLastError())) { - slow_path = FALSE; - result = 0; - } - } - if (slow_path) { - if (_path.fd != -1) { - hfile = _Py_get_osfhandle_noraise(_path.fd); - close_file = FALSE; - } - else { - hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - } + if (_path.fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(_path.fd); if (hfile != INVALID_HANDLE_VALUE) { - if (GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, - sizeof(info))) - { - result = info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; - } - else { - result = 0; - } - if (close_file) { - CloseHandle(hfile); - } - } - else { - STRUCT_STAT st; - switch (GetLastError()) { - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_CANT_ACCESS_FILE: - case ERROR_INVALID_PARAMETER: - if (STAT(_path.wide, &st)) { - result = 0; - } - else { - result = S_ISDIR(st.st_mode); - } - break; - default: - result = 0; - } + result = _testFileTypeByHandle(hfile, PY_IFDIR, TRUE); } } + else if (_path.wide) { + result = _testFileTypeByName(_path.wide, PY_IFDIR); + } Py_END_ALLOW_THREADS path_cleanup(&_path); @@ -5198,13 +5295,8 @@ static PyObject * os__path_isfile_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=2394ed7c4b5cfd85 input=de22d74960ade365]*/ { - HANDLE hfile; - BOOL close_file = TRUE; - FILE_BASIC_INFO info; path_t _path = PATH_T_INITIALIZE("isfile", "path", 0, 1); - int result; - BOOL slow_path = TRUE; - FILE_STAT_BASIC_INFORMATION statInfo; + BOOL result = FALSE; if (!path_converter(path, &_path)) { path_cleanup(&_path); @@ -5216,64 +5308,18 @@ os__path_isfile_impl(PyObject *module, PyObject *path) } Py_BEGIN_ALLOW_THREADS - if (_path.wide) { - if (_Py_GetFileInformationByName(_path.wide, FileStatBasicByNameInfo, - &statInfo, sizeof(statInfo))) { - if (!(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - slow_path = FALSE; - result = !(statInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY); - } else if (statInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - slow_path = FALSE; - result = 0; - } - } else if (_Py_GetFileInformationByName_ErrorIsTrustworthy(GetLastError())) { - slow_path = FALSE; - result = 0; - } - } - if (slow_path) { - if (_path.fd != -1) { - hfile = _Py_get_osfhandle_noraise(_path.fd); - close_file = FALSE; - } - else { - hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - } + if (_path.fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(_path.fd); if (hfile != INVALID_HANDLE_VALUE) { - if (GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, - sizeof(info))) - { - result = !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY); - } - else { - result = 0; - } - if (close_file) { - CloseHandle(hfile); - } - } - else { - STRUCT_STAT st; - switch (GetLastError()) { - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_CANT_ACCESS_FILE: - case ERROR_INVALID_PARAMETER: - if (STAT(_path.wide, &st)) { - result = 0; - } - else { - result = S_ISREG(st.st_mode); - } - break; - default: - result = 0; - } + result = _testFileTypeByHandle(hfile, PY_IFREG, TRUE); } } + else if (_path.wide) { + result = _testFileTypeByName(_path.wide, PY_IFREG); + } Py_END_ALLOW_THREADS + path_cleanup(&_path); if (result) { Py_RETURN_TRUE; @@ -5381,13 +5427,8 @@ static PyObject * os__path_islink_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=6d8640b1a390c054 input=38a3cb937ccf59bf]*/ { - HANDLE hfile; - BOOL close_file = TRUE; - FILE_ATTRIBUTE_TAG_INFO info; path_t _path = PATH_T_INITIALIZE("islink", "path", 0, 1); - int result; - BOOL slow_path = TRUE; - FILE_STAT_BASIC_INFORMATION statInfo; + BOOL result = FALSE; if (!path_converter(path, &_path)) { path_cleanup(&_path); @@ -5399,64 +5440,15 @@ os__path_islink_impl(PyObject *module, PyObject *path) } Py_BEGIN_ALLOW_THREADS - if (_path.wide) { - if (_Py_GetFileInformationByName(_path.wide, FileStatBasicByNameInfo, - &statInfo, sizeof(statInfo))) { - slow_path = FALSE; - if (statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - result = (statInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK); - } - else { - result = 0; - } - } else if (_Py_GetFileInformationByName_ErrorIsTrustworthy(GetLastError())) { - slow_path = FALSE; - result = 0; - } - } - if (slow_path) { - if (_path.fd != -1) { - hfile = _Py_get_osfhandle_noraise(_path.fd); - close_file = FALSE; - } - else { - hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, - FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, - NULL); - } + if (_path.fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(_path.fd); if (hfile != INVALID_HANDLE_VALUE) { - if (GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, - sizeof(info))) - { - result = (info.ReparseTag == IO_REPARSE_TAG_SYMLINK); - } - else { - result = 0; - } - if (close_file) { - CloseHandle(hfile); - } - } - else { - STRUCT_STAT st; - switch (GetLastError()) { - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_CANT_ACCESS_FILE: - case ERROR_INVALID_PARAMETER: - if (LSTAT(_path.wide, &st)) { - result = 0; - } - else { - result = S_ISLNK(st.st_mode); - } - break; - default: - result = 0; - } + result = _testFileTypeByHandle(hfile, PY_IFLNK, TRUE); } } + else if (_path.wide) { + result = _testFileTypeByName(_path.wide, PY_IFLNK); + } Py_END_ALLOW_THREADS path_cleanup(&_path); @@ -5466,6 +5458,11 @@ os__path_islink_impl(PyObject *module, PyObject *path) Py_RETURN_FALSE; } +#undef PY_IFREG +#undef PY_IFDIR +#undef PY_IFLNK +#undef PY_IFMNT + #endif /* MS_WINDOWS */ From cd7c6d0de799fe2cafda30b5ee13d72c2b859c32 Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Wed, 8 May 2024 12:05:18 +0200 Subject: [PATCH 10/41] Add `nt._path_isjunction` --- Lib/ntpath.py | 16 +-------- Lib/test/test_ntpath.py | 2 ++ Modules/clinic/posixmodule.c.h | 65 +++++++++++++++++++++++++++++++++- Modules/posixmodule.c | 45 +++++++++++++++++++++++ 4 files changed, 112 insertions(+), 16 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index b833e0bad2645f..8d41846fca49d0 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -288,21 +288,6 @@ def dirname(p): return split(p)[0] -# Is a path a junction? - -if hasattr(os.stat_result, 'st_reparse_tag'): - def isjunction(path): - """Test whether a path is a junction""" - try: - st = os.lstat(path) - except (OSError, ValueError, AttributeError): - return False - return st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT -else: - # Use genericpath.isjunction as imported above - pass - - # Is a path a mount point? # Any drive letter root (eg c:\) # Any share UNC (eg \\server\share) @@ -917,6 +902,7 @@ def commonpath(paths): from nt import _path_isdir as isdir from nt import _path_isfile as isfile from nt import _path_islink as islink + from nt import _path_isjunction as isjunction from nt import _path_exists as exists except ImportError: # Use genericpath.* as imported above diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 7f91bf1c2b837a..09eb8141d50032 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1114,6 +1114,8 @@ def test_fast_paths_in_use(self): self.assertFalse(inspect.isfunction(os.path.isfile)) self.assertTrue(os.path.islink is nt._path_islink) self.assertFalse(inspect.isfunction(os.path.islink)) + self.assertTrue(os.path.isjunction is nt._path_isjunction) + self.assertFalse(inspect.isfunction(os.path.isjunction)) self.assertTrue(os.path.exists is nt._path_exists) self.assertFalse(inspect.isfunction(os.path.exists)) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index a0d1f3238a6733..455ef8c7e04967 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -2248,6 +2248,65 @@ os__path_islink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj #endif /* defined(MS_WINDOWS) */ +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_isjunction__doc__, +"_path_isjunction($module, /, path)\n" +"--\n" +"\n" +"Test whether a path is a junction"); + +#define OS__PATH_ISJUNCTION_METHODDEF \ + {"_path_isjunction", _PyCFunction_CAST(os__path_isjunction), METH_FASTCALL|METH_KEYWORDS, os__path_isjunction__doc__}, + +static PyObject * +os__path_isjunction_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_isjunction(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(path), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"path", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "_path_isjunction", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *path; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { + goto exit; + } + path = args[0]; + return_value = os__path_isjunction_impl(module, path); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + PyDoc_STRVAR(os__path_splitroot_ex__doc__, "_path_splitroot_ex($module, /, path)\n" "--\n" @@ -12113,6 +12172,10 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #define OS__PATH_ISLINK_METHODDEF #endif /* !defined(OS__PATH_ISLINK_METHODDEF) */ +#ifndef OS__PATH_ISJUNCTION_METHODDEF + #define OS__PATH_ISJUNCTION_METHODDEF +#endif /* !defined(OS__PATH_ISJUNCTION_METHODDEF) */ + #ifndef OS_NICE_METHODDEF #define OS_NICE_METHODDEF #endif /* !defined(OS_NICE_METHODDEF) */ @@ -12660,4 +12723,4 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ -/*[clinic end generated code: output=c4698b47007cd6eb input=a9049054013a1b77]*/ +/*[clinic end generated code: output=034909a9050aa9b8 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index daff975c1acb5f..52b05cdb38c598 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5458,6 +5458,51 @@ os__path_islink_impl(PyObject *module, PyObject *path) Py_RETURN_FALSE; } + +/*[clinic input] +os._path_isjunction + + path: 'O' + +Test whether a path is a junction + +[clinic start generated code]*/ + +static PyObject * +os__path_isjunction_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=7b05da937984af21 input=239b95842b12dd68]*/ +{ + path_t _path = PATH_T_INITIALIZE("isjunction", "path", 0, 1); + BOOL result = FALSE; + + if (!path_converter(path, &_path)) { + path_cleanup(&_path); + if (PyErr_ExceptionMatches(PyExc_ValueError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (_path.fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(_path.fd); + if (hfile != INVALID_HANDLE_VALUE) { + result = _testFileTypeByHandle(hfile, PY_IFMNT, TRUE); + } + } + else if (_path.wide) { + result = _testFileTypeByName(_path.wide, PY_IFMNT); + } + Py_END_ALLOW_THREADS + + path_cleanup(&_path); + if (result) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + #undef PY_IFREG #undef PY_IFDIR #undef PY_IFLNK From 168d2995b9071bb403d3a7e6a4618dc0e0388454 Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Wed, 8 May 2024 13:33:43 +0200 Subject: [PATCH 11/41] Fix bug & further refactoring --- Modules/clinic/posixmodule.c.h | 47 +++++++++--- Modules/posixmodule.c | 129 +++++++++++++-------------------- 2 files changed, 85 insertions(+), 91 deletions(-) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 455ef8c7e04967..fd799532adfad0 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -2023,7 +2023,7 @@ PyDoc_STRVAR(os__path_isdir__doc__, #define OS__PATH_ISDIR_METHODDEF \ {"_path_isdir", _PyCFunction_CAST(os__path_isdir), METH_FASTCALL|METH_KEYWORDS, os__path_isdir__doc__}, -static PyObject * +static int os__path_isdir_impl(PyObject *module, PyObject *s); static PyObject * @@ -2057,13 +2057,18 @@ os__path_isdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #undef KWTUPLE PyObject *argsbuf[1]; PyObject *s; + int _return_value; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; } s = args[0]; - return_value = os__path_isdir_impl(module, s); + _return_value = os__path_isdir_impl(module, s); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: return return_value; @@ -2082,7 +2087,7 @@ PyDoc_STRVAR(os__path_isfile__doc__, #define OS__PATH_ISFILE_METHODDEF \ {"_path_isfile", _PyCFunction_CAST(os__path_isfile), METH_FASTCALL|METH_KEYWORDS, os__path_isfile__doc__}, -static PyObject * +static int os__path_isfile_impl(PyObject *module, PyObject *path); static PyObject * @@ -2116,13 +2121,18 @@ os__path_isfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj #undef KWTUPLE PyObject *argsbuf[1]; PyObject *path; + int _return_value; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; } path = args[0]; - return_value = os__path_isfile_impl(module, path); + _return_value = os__path_isfile_impl(module, path); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: return return_value; @@ -2141,7 +2151,7 @@ PyDoc_STRVAR(os__path_exists__doc__, #define OS__PATH_EXISTS_METHODDEF \ {"_path_exists", _PyCFunction_CAST(os__path_exists), METH_FASTCALL|METH_KEYWORDS, os__path_exists__doc__}, -static PyObject * +static int os__path_exists_impl(PyObject *module, PyObject *path); static PyObject * @@ -2175,13 +2185,18 @@ os__path_exists(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj #undef KWTUPLE PyObject *argsbuf[1]; PyObject *path; + int _return_value; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; } path = args[0]; - return_value = os__path_exists_impl(module, path); + _return_value = os__path_exists_impl(module, path); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: return return_value; @@ -2200,7 +2215,7 @@ PyDoc_STRVAR(os__path_islink__doc__, #define OS__PATH_ISLINK_METHODDEF \ {"_path_islink", _PyCFunction_CAST(os__path_islink), METH_FASTCALL|METH_KEYWORDS, os__path_islink__doc__}, -static PyObject * +static int os__path_islink_impl(PyObject *module, PyObject *path); static PyObject * @@ -2234,13 +2249,18 @@ os__path_islink(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj #undef KWTUPLE PyObject *argsbuf[1]; PyObject *path; + int _return_value; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; } path = args[0]; - return_value = os__path_islink_impl(module, path); + _return_value = os__path_islink_impl(module, path); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: return return_value; @@ -2259,7 +2279,7 @@ PyDoc_STRVAR(os__path_isjunction__doc__, #define OS__PATH_ISJUNCTION_METHODDEF \ {"_path_isjunction", _PyCFunction_CAST(os__path_isjunction), METH_FASTCALL|METH_KEYWORDS, os__path_isjunction__doc__}, -static PyObject * +static int os__path_isjunction_impl(PyObject *module, PyObject *path); static PyObject * @@ -2293,13 +2313,18 @@ os__path_isjunction(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P #undef KWTUPLE PyObject *argsbuf[1]; PyObject *path; + int _return_value; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; } path = args[0]; - return_value = os__path_isjunction_impl(module, path); + _return_value = os__path_isjunction_impl(module, path); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); exit: return return_value; @@ -12723,4 +12748,4 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ -/*[clinic end generated code: output=034909a9050aa9b8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=01cf0a4837d06037 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 52b05cdb38c598..e72a7df4775b0f 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5108,32 +5108,18 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) } FILE_ATTRIBUTE_TAG_INFO info; - DWORD attributes, reparseTag; - if (GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, - sizeof(info))) + if (!GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, + sizeof(info))) { - attributes = info.FileAttributes; - reparseTag = info.ReparseTag; - } - else { - FILE_BASIC_INFO info; - if (GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, - sizeof(info))) - { - attributes = info.FileAttributes; - reparseTag = 0; - } - else { - return FALSE; - } + return FALSE; } - if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (info.attributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (refFileType == PY_IFLNK) { - return reparseTag == IO_REPARSE_TAG_SYMLINK; + return info.reparseTag == IO_REPARSE_TAG_SYMLINK; } if (refFileType == PY_IFMNT) { - return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; + return info.reparseTag == IO_REPARSE_TAG_MOUNT_POINT; } // Non-surrogate reparse points aren't supported by handle. Just // return False. Supporting them requires querying and opening the @@ -5141,10 +5127,10 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) } else if (refFileType == PY_IFREG) { return ((fileDevType == FILE_TYPE_DISK) && - !(attributes & FILE_ATTRIBUTE_DIRECTORY)); + !(info.attributes & FILE_ATTRIBUTE_DIRECTORY)); } else if (refFileType == PY_IFDIR) { - return attributes & FILE_ATTRIBUTE_DIRECTORY; + return info.attributes & FILE_ATTRIBUTE_DIRECTORY; } return FALSE; @@ -5157,22 +5143,20 @@ _testFileTypeByName(LPCWSTR path, int refFileType) refFileType == PY_IFLNK || refFileType == PY_IFMNT); FILE_STAT_BASIC_INFORMATION info; - if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, - &info, sizeof(info))) { + if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, + sizeof(info))) + { if (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (refFileType == PY_IFLNK) { return info.ReparseTag == IO_REPARSE_TAG_SYMLINK; } - else if (refFileType == PY_IFMNT) { + if (refFileType == PY_IFMNT) { return info.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT; } - else if ((refFileType == PY_IFREG) && - (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - return FALSE; - } - else if ((refFileType == PY_IFDIR) && - !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + if ((refFileType == PY_IFREG) && + (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || + (refFileType == PY_IFDIR) && + !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { return FALSE; } @@ -5193,13 +5177,12 @@ _testFileTypeByName(LPCWSTR path, int refFileType) return FALSE; } - HANDLE hfile; DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; if (refFileType == PY_IFLNK || refFileType == PY_IFMNT) { flags |= FILE_FLAG_OPEN_REPARSE_POINT; } - hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, - flags, NULL); + HANDLE hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, flags, NULL); if (hfile != INVALID_HANDLE_VALUE) { BOOL result = _testFileTypeByHandle(hfile, refFileType, FALSE); CloseHandle(hfile); @@ -5238,7 +5221,7 @@ _testFileTypeByName(LPCWSTR path, int refFileType) /*[clinic input] -os._path_isdir +os._path_isdir -> bool s: 'O' @@ -5246,9 +5229,9 @@ Return true if the pathname refers to an existing directory. [clinic start generated code]*/ -static PyObject * +static int os__path_isdir_impl(PyObject *module, PyObject *s) -/*[clinic end generated code: output=9d87ab3c8b8a4e61 input=c17f7ef21d22d64e]*/ +/*[clinic end generated code: output=cdcdf654d78788cc input=ad95d2a4615c99ef]*/ { path_t _path = PATH_T_INITIALIZE("isdir", "s", 0, 1); BOOL result = FALSE; @@ -5257,9 +5240,9 @@ os__path_isdir_impl(PyObject *module, PyObject *s) path_cleanup(&_path); if (PyErr_ExceptionMatches(PyExc_ValueError)) { PyErr_Clear(); - Py_RETURN_FALSE; + return FALSE; } - return NULL; + return -1; } Py_BEGIN_ALLOW_THREADS @@ -5275,15 +5258,12 @@ os__path_isdir_impl(PyObject *module, PyObject *s) Py_END_ALLOW_THREADS path_cleanup(&_path); - if (result) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + return result; } /*[clinic input] -os._path_isfile +os._path_isfile -> bool path: 'O' @@ -5291,9 +5271,9 @@ Test whether a path is a regular file [clinic start generated code]*/ -static PyObject * +static int os__path_isfile_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=2394ed7c4b5cfd85 input=de22d74960ade365]*/ +/*[clinic end generated code: output=b40d620efe5a896f input=20efea61e62636e5]*/ { path_t _path = PATH_T_INITIALIZE("isfile", "path", 0, 1); BOOL result = FALSE; @@ -5302,9 +5282,9 @@ os__path_isfile_impl(PyObject *module, PyObject *path) path_cleanup(&_path); if (PyErr_ExceptionMatches(PyExc_ValueError)) { PyErr_Clear(); - Py_RETURN_FALSE; + return FALSE; } - return NULL; + return -1; } Py_BEGIN_ALLOW_THREADS @@ -5321,15 +5301,12 @@ os__path_isfile_impl(PyObject *module, PyObject *path) path_cleanup(&_path); - if (result) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + return result; } /*[clinic input] -os._path_exists +os._path_exists -> bool path: 'O' @@ -5337,9 +5314,9 @@ Test whether a path exists. Returns False for broken symbolic links [clinic start generated code]*/ -static PyObject * +static int os__path_exists_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=f508c3b35e13a249 input=380f77cdfa0f7ae8]*/ +/*[clinic end generated code: output=8f784b3abf9f8588 input=28d91ca9965b5a5a]*/ { HANDLE hfile; BOOL close_file = TRUE; @@ -5352,9 +5329,9 @@ os__path_exists_impl(PyObject *module, PyObject *path) path_cleanup(&_path); if (PyErr_ExceptionMatches(PyExc_ValueError)) { PyErr_Clear(); - Py_RETURN_FALSE; + return 0; } - return NULL; + return -1; } Py_BEGIN_ALLOW_THREADS @@ -5407,15 +5384,12 @@ os__path_exists_impl(PyObject *module, PyObject *path) Py_END_ALLOW_THREADS path_cleanup(&_path); - if (result) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + return result; } /*[clinic input] -os._path_islink +os._path_islink -> bool path: 'O' @@ -5423,9 +5397,9 @@ Test whether a path is a symbolic link [clinic start generated code]*/ -static PyObject * +static int os__path_islink_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=6d8640b1a390c054 input=38a3cb937ccf59bf]*/ +/*[clinic end generated code: output=9d0cf8e4c640dfe6 input=cbeda07cacbbdcea]*/ { path_t _path = PATH_T_INITIALIZE("islink", "path", 0, 1); BOOL result = FALSE; @@ -5434,9 +5408,9 @@ os__path_islink_impl(PyObject *module, PyObject *path) path_cleanup(&_path); if (PyErr_ExceptionMatches(PyExc_ValueError)) { PyErr_Clear(); - Py_RETURN_FALSE; + return FALSE; } - return NULL; + return -1; } Py_BEGIN_ALLOW_THREADS @@ -5452,15 +5426,12 @@ os__path_islink_impl(PyObject *module, PyObject *path) Py_END_ALLOW_THREADS path_cleanup(&_path); - if (result) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + return result; } /*[clinic input] -os._path_isjunction +os._path_isjunction -> bool path: 'O' @@ -5468,9 +5439,9 @@ Test whether a path is a junction [clinic start generated code]*/ -static PyObject * +static int os__path_isjunction_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=7b05da937984af21 input=239b95842b12dd68]*/ +/*[clinic end generated code: output=f1d51682a077654d input=f351d2f01451c6de]*/ { path_t _path = PATH_T_INITIALIZE("isjunction", "path", 0, 1); BOOL result = FALSE; @@ -5479,9 +5450,9 @@ os__path_isjunction_impl(PyObject *module, PyObject *path) path_cleanup(&_path); if (PyErr_ExceptionMatches(PyExc_ValueError)) { PyErr_Clear(); - Py_RETURN_FALSE; + return FALSE; } - return NULL; + return -1; } Py_BEGIN_ALLOW_THREADS @@ -5497,10 +5468,7 @@ os__path_isjunction_impl(PyObject *module, PyObject *path) Py_END_ALLOW_THREADS path_cleanup(&_path); - if (result) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + return result; } #undef PY_IFREG @@ -17095,6 +17063,7 @@ static PyMethodDef posix_methods[] = { OS__PATH_ISDIR_METHODDEF OS__PATH_ISFILE_METHODDEF OS__PATH_ISLINK_METHODDEF + OS__PATH_ISJUNCTION_METHODDEF OS__PATH_EXISTS_METHODDEF OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF From e00df4c8d7743b556b1deaac2ac003d9558da09d Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Wed, 8 May 2024 13:37:47 +0200 Subject: [PATCH 12/41] Fix attribute name --- Modules/posixmodule.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index e72a7df4775b0f..d83342a72cba38 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5114,12 +5114,12 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) return FALSE; } - if (info.attributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (refFileType == PY_IFLNK) { - return info.reparseTag == IO_REPARSE_TAG_SYMLINK; + return info.ReparseTag == IO_REPARSE_TAG_SYMLINK; } if (refFileType == PY_IFMNT) { - return info.reparseTag == IO_REPARSE_TAG_MOUNT_POINT; + return info.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT; } // Non-surrogate reparse points aren't supported by handle. Just // return False. Supporting them requires querying and opening the @@ -5127,10 +5127,10 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) } else if (refFileType == PY_IFREG) { return ((fileDevType == FILE_TYPE_DISK) && - !(info.attributes & FILE_ATTRIBUTE_DIRECTORY)); + !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)); } else if (refFileType == PY_IFDIR) { - return info.attributes & FILE_ATTRIBUTE_DIRECTORY; + return info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; } return FALSE; From b31a25231bb07afe9866451ee35d214f48e2acd6 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Wed, 8 May 2024 19:44:53 +0200 Subject: [PATCH 13/41] Add test for pipe --- Lib/test/test_ntpath.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 09eb8141d50032..fb891e9857592a 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1095,6 +1095,10 @@ def test_isfile_driveletter(self): raise unittest.SkipTest('SystemDrive is not defined or malformed') self.assertFalse(os.path.isfile('\\\\.\\' + drive)) + def test_isfile_pipe(self): + pr, _ = os.pipe() + self.assertFalse(ntpath.isfile(pr)) + @unittest.skipIf(sys.platform != 'win32', "windows only") def test_con_device(self): self.assertFalse(os.path.isfile(r"\\.\CON")) From 59e1407058eaaf6daa9ca5364b8b170828113cbd Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Wed, 8 May 2024 20:27:40 +0200 Subject: [PATCH 14/41] Skip unless `os.pipe` is available --- Lib/test/test_ntpath.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index fb891e9857592a..90cc4e7f5857ec 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1095,6 +1095,7 @@ def test_isfile_driveletter(self): raise unittest.SkipTest('SystemDrive is not defined or malformed') self.assertFalse(os.path.isfile('\\\\.\\' + drive)) + @unittest.skipUnless(hasattr(os, 'pipe'), "need os.pipe()") def test_isfile_pipe(self): pr, _ = os.pipe() self.assertFalse(ntpath.isfile(pr)) From 76acc0d8f4a8b2967759ac5a392940a17cfb0bac Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 18:33:08 +0000 Subject: [PATCH 15/41] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst new file mode 100644 index 00000000000000..772923fab20cc7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst @@ -0,0 +1 @@ +Speedup :func:`os.path.isjunction` on Windows with a native implementation. From 2c8e464329eb693eef259e19531793cf162bc029 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Thu, 9 May 2024 11:50:35 +0200 Subject: [PATCH 16/41] Update Modules/posixmodule.c --- Modules/posixmodule.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d83342a72cba38..b51aac1bcfe737 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5107,8 +5107,16 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) return FALSE; } - FILE_ATTRIBUTE_TAG_INFO info; - if (!GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, + FILE_INFO_BY_HANDLE_CLASS FileInformationClass; + if (refFileType == PY_IFLNK || refFileType == PY_IFMNT) { + FileInformationClass = FileAttributeTagInfo; + FILE_ATTRIBUTE_TAG_INFO info; + } + else { + FileInformationClass = FileBasicInfo; + FILE_BASIC_INFO info; + } + if (!GetFileInformationByHandleEx(hfile, FileInformationClass, &info, sizeof(info))) { return FALSE; From a8bc5f221c43c1e2396c77a209da84766e427fe7 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Thu, 9 May 2024 11:56:37 +0200 Subject: [PATCH 17/41] Update Modules/posixmodule.c --- Modules/posixmodule.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b51aac1bcfe737..2400622ce0c3f5 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5108,13 +5108,12 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) } FILE_INFO_BY_HANDLE_CLASS FileInformationClass; + LPVOID info; if (refFileType == PY_IFLNK || refFileType == PY_IFMNT) { FileInformationClass = FileAttributeTagInfo; - FILE_ATTRIBUTE_TAG_INFO info; } else { FileInformationClass = FileBasicInfo; - FILE_BASIC_INFO info; } if (!GetFileInformationByHandleEx(hfile, FileInformationClass, &info, sizeof(info))) From 55b6b80fe57beaaccc39e2857cd41a0cc9d07d39 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Thu, 9 May 2024 12:41:55 +0200 Subject: [PATCH 18/41] Update Lib/test/test_ntpath.py Co-authored-by: Eryk Sun --- Lib/test/test_ntpath.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 90cc4e7f5857ec..2571b7c651a894 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1097,8 +1097,12 @@ def test_isfile_driveletter(self): @unittest.skipUnless(hasattr(os, 'pipe'), "need os.pipe()") def test_isfile_pipe(self): - pr, _ = os.pipe() - self.assertFalse(ntpath.isfile(pr)) + pr, pw = os.pipe() + try: + self.assertFalse(ntpath.isfile(pr)) + finally: + os.close(pr) + os.close(pw) @unittest.skipIf(sys.platform != 'win32', "windows only") def test_con_device(self): From 61a62ced7eec741d28bc69b86a7de537c0fe680a Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Thu, 9 May 2024 13:09:58 +0200 Subject: [PATCH 19/41] Fix compilation errors --- Modules/posixmodule.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 2400622ce0c3f5..78278a6ca782d9 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5107,26 +5107,34 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) return FALSE; } - FILE_INFO_BY_HANDLE_CLASS FileInformationClass; - LPVOID info; - if (refFileType == PY_IFLNK || refFileType == PY_IFMNT) { - FileInformationClass = FileAttributeTagInfo; + FILE_ATTRIBUTE_TAG_INFO atInfo; + FILE_BASIC_INFO baInfo; + DWORD attributes, reparseTag; + if ((refFileType == PY_IFLNK || refFileType == PY_IFMNT)) { + if (!GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &atInfo, + sizeof(atInfo))) + { + return FALSE; + } + attributes = atInfo.FileAttributes; + reparseTag = atInfo.ReparseTag; } else { - FileInformationClass = FileBasicInfo; - } - if (!GetFileInformationByHandleEx(hfile, FileInformationClass, &info, - sizeof(info))) - { - return FALSE; + if (!GetFileInformationByHandleEx(hfile, FileBasicInfo, &baInfo, + sizeof(baInfo))) + { + return FALSE; + } + attributes = baInfo.FileAttributes; + reparseTag = 0; } - if (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (refFileType == PY_IFLNK) { - return info.ReparseTag == IO_REPARSE_TAG_SYMLINK; + return reparseTag == IO_REPARSE_TAG_SYMLINK; } if (refFileType == PY_IFMNT) { - return info.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT; + return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; } // Non-surrogate reparse points aren't supported by handle. Just // return False. Supporting them requires querying and opening the @@ -5134,10 +5142,10 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) } else if (refFileType == PY_IFREG) { return ((fileDevType == FILE_TYPE_DISK) && - !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)); + !(attributes & FILE_ATTRIBUTE_DIRECTORY)); } else if (refFileType == PY_IFDIR) { - return info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; + return attributes & FILE_ATTRIBUTE_DIRECTORY; } return FALSE; From ba791519f550dd2f71876dbcdf5bd6588e430205 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Thu, 9 May 2024 13:44:04 +0200 Subject: [PATCH 20/41] Update Modules/posixmodule.c Co-authored-by: Eryk Sun --- Modules/posixmodule.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 78278a6ca782d9..bf1f449dc453bb 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5107,25 +5107,25 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) return FALSE; } - FILE_ATTRIBUTE_TAG_INFO atInfo; - FILE_BASIC_INFO baInfo; DWORD attributes, reparseTag; if ((refFileType == PY_IFLNK || refFileType == PY_IFMNT)) { - if (!GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &atInfo, - sizeof(atInfo))) + FILE_ATTRIBUTE_TAG_INFO info; + if (!GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, + sizeof(info))) { return FALSE; } - attributes = atInfo.FileAttributes; - reparseTag = atInfo.ReparseTag; + attributes = info.FileAttributes; + reparseTag = info.ReparseTag; } else { - if (!GetFileInformationByHandleEx(hfile, FileBasicInfo, &baInfo, - sizeof(baInfo))) + FILE_BASIC_INFO info; + if (!GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, + sizeof(info))) { return FALSE; } - attributes = baInfo.FileAttributes; + attributes = info.FileAttributes; reparseTag = 0; } From 07629c64d16fbf68bc97cb9d5925226cf76197a8 Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Thu, 9 May 2024 14:44:36 +0200 Subject: [PATCH 21/41] Merge news entries --- .../2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst | 1 - .../2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst deleted file mode 100644 index 3a562884283b1c..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2024-04-13-11-30-09.gh-issue-117841.eW4w_y.rst +++ /dev/null @@ -1 +0,0 @@ -Speedup :func:`os.path.lexists` on Windows with a native implementation. diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst index 772923fab20cc7..e079da10902849 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst @@ -1 +1 @@ -Speedup :func:`os.path.isjunction` on Windows with a native implementation. +Speedup :func:`os.path.isjunction` & :func:`os.path.lexists` on Windows with a native implementation. From 0fcffb5373112f9e0caf04effa18b25d4c744b1f Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Thu, 9 May 2024 14:52:36 +0200 Subject: [PATCH 22/41] Replace ampersand --- .../2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst index e079da10902849..de1462f0d24fce 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-08-18-33-07.gh-issue-118507.OCQsAY.rst @@ -1 +1 @@ -Speedup :func:`os.path.isjunction` & :func:`os.path.lexists` on Windows with a native implementation. +Speedup :func:`os.path.isjunction` and :func:`os.path.lexists` on Windows with a native implementation. From cae7a552866226397492a5b83bdc07855a9f0d93 Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 08:32:25 +0200 Subject: [PATCH 23/41] More refactoring Co-authored-by: Eryk Sun --- Modules/posixmodule.c | 425 ++++++++++++++++++------------------------ 1 file changed, 177 insertions(+), 248 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 560799ca10b9cd..345803c57a57f8 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5088,27 +5088,30 @@ os__path_splitroot_impl(PyObject *module, path_t *path) } -#define PY_IFREG 1 -#define PY_IFDIR 2 -#define PY_IFLNK 3 -#define PY_IFMNT 4 +#define PY_IFREG 1 // Regular file +#define PY_IFDIR 2 // Directory +#define PY_IFLNK 4 // Symlink +#define PY_IFMNT 8 // Mount Point (junction) +#define PY_IFLRP 16 // Link Reparse Point (name-surrogate, symlink, junction) +#define PY_IFRRP 32 // Regular Reparse Point static BOOL -_testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) +_testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) { - assert(refFileType == PY_IFREG || refFileType == PY_IFDIR || - refFileType == PY_IFLNK || refFileType == PY_IFMNT); + assert(testedType == PY_IFREG || testedType == PY_IFDIR || + testedType == PY_IFLNK || testedType == PY_IFMNT || + testedType == PY_IFLRP || testedType == PY_IFRRP); DWORD fileDevType = GetFileType(hfile); - if ((fileDevType == FILE_TYPE_CHAR) || - (fileDevType != FILE_TYPE_DISK && diskOnly) || - (fileDevType == FILE_TYPE_UNKNOWN && GetLastError() != 0)) + if ((fileDevType == FILE_TYPE_UNKNOWN && GetLastError()) || + (fileDevType == FILE_TYPE_CHAR) || + (diskOnly && fileDevType != FILE_TYPE_DISK)) { return FALSE; } DWORD attributes, reparseTag; - if ((refFileType == PY_IFLNK || refFileType == PY_IFMNT)) { + if ((testedType != PY_IFREG && testedType != PY_IFDIR)) { FILE_ATTRIBUTE_TAG_INFO info; if (!GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, sizeof(info))) @@ -5130,21 +5133,22 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) } if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { - if (refFileType == PY_IFLNK) { + switch (testedType) { + case PY_IFLNK: return reparseTag == IO_REPARSE_TAG_SYMLINK; - } - if (refFileType == PY_IFMNT) { + case PY_IFMNT: return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; + case PY_IFLRP: + return IsReparseTagNameSurrogate(reparseTag); + case PY_IFRRP: + return reparseTag && !IsReparseTagNameSurrogate(reparseTag); } - // Non-surrogate reparse points aren't supported by handle. Just - // return False. Supporting them requires querying and opening the - // final path with reparsing enabled. } - else if (refFileType == PY_IFREG) { + else if (testedType == PY_IFREG) { return ((fileDevType == FILE_TYPE_DISK) && !(attributes & FILE_ATTRIBUTE_DIRECTORY)); } - else if (refFileType == PY_IFDIR) { + else if (testedType == PY_IFDIR) { return attributes & FILE_ATTRIBUTE_DIRECTORY; } @@ -5152,39 +5156,48 @@ _testFileTypeByHandle(HANDLE hfile, int refFileType, BOOL diskOnly) } static BOOL -_testFileTypeByName(LPCWSTR path, int refFileType) +_testFileTypeByName(LPCWSTR path, int testedType) { - assert(refFileType == PY_IFREG || refFileType == PY_IFDIR || - refFileType == PY_IFLNK || refFileType == PY_IFMNT); + assert(testedType == PY_IFREG || testedType == PY_IFDIR || + testedType == PY_IFLNK || testedType == PY_IFMNT || + testedType == PY_IFLRP || testedType == PY_IFRRP); FILE_STAT_BASIC_INFORMATION info; if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, sizeof(info))) { if (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - if (refFileType == PY_IFLNK) { + switch(testedType) { + case PY_IFREG: + if (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + return FALSE; + } + break; + case PY_IFDIR: + if (!(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + return FALSE; + } + break; + case PY_IFLNK: return info.ReparseTag == IO_REPARSE_TAG_SYMLINK; - } - if (refFileType == PY_IFMNT) { + case PY_IFMNT: return info.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT; - } - if ((refFileType == PY_IFREG) && - (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || - (refFileType == PY_IFDIR) && - !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - return FALSE; + case PY_IFLRP: + return IsReparseTagNameSurrogate(info.ReparseTag); + case PY_IFRRP: + return (info.ReparseTag && + !IsReparseTagNameSurrogate(info.ReparseTag)); } } - else if (refFileType == PY_IFLNK || refFileType == PY_IFMNT) { - return FALSE; - } - else if (refFileType == PY_IFREG) { + else if (testedType == PY_IFREG) { return !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY); } - else if (refFileType == PY_IFDIR) { + else if (testedType == PY_IFDIR) { return info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; } + else { + return FALSE; + } } else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( GetLastError())) @@ -5193,40 +5206,43 @@ _testFileTypeByName(LPCWSTR path, int refFileType) } DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; - if (refFileType == PY_IFLNK || refFileType == PY_IFMNT) { + if (testedType != PY_IFREG && testedType != PY_IFDIR) { flags |= FILE_FLAG_OPEN_REPARSE_POINT; } HANDLE hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, flags, NULL); if (hfile != INVALID_HANDLE_VALUE) { - BOOL result = _testFileTypeByHandle(hfile, refFileType, FALSE); + BOOL result = _testFileTypeByHandle(hfile, testedType, FALSE); CloseHandle(hfile); return result; } - int rc; - STRUCT_STAT st; switch (GetLastError()) { case ERROR_ACCESS_DENIED: case ERROR_SHARING_VIOLATION: case ERROR_CANT_ACCESS_FILE: case ERROR_INVALID_PARAMETER: - if (refFileType == PY_IFREG || refFileType == PY_IFDIR) { - rc = STAT(path, &st); + STRUCT_STAT st; + if (testedType == PY_IFREG) { + return !STAT(path, &st) && S_ISREG(st.st_mode); } - else { - rc = LSTAT(path, &st); + if (testedType == PY_IFDIR) { + return !STAT(path, &st) && S_ISDIR(st.st_mode); } - if (!rc) { - switch (refFileType) { - case PY_IFREG: - return S_ISREG(st.st_mode); - case PY_IFDIR: - return S_ISDIR(st.st_mode); + if (!LSTAT(path, &st)) { + switch (testedType) { case PY_IFLNK: - return S_ISLNK(st.st_mode); + return st.st_reparse_tag == IO_REPARSE_TAG_SYMLINK; case PY_IFMNT: return st.st_reparse_tag == IO_REPARSE_TAG_MOUNT_POINT; + case PY_IFLRP: + return IsReparseTagNameSurrogate(st.st_reparse_tag); + case PY_IFRRP: + // This cannot be implemented generally, except a reparse + // point that is not handled by the system, such as + // IO_REPARSE_TAG_APPEXECLINK. + return (st.st_reparse_tag && + !IsReparseTagNameSurrogate(st.st_reparse_tag)); } } } @@ -5235,64 +5251,70 @@ _testFileTypeByName(LPCWSTR path, int refFileType) } -/*[clinic input] -os._path_isdir -> bool - - s: object - -Return true if the pathname refers to an existing directory. - -[clinic start generated code]*/ - -static int -os__path_isdir_impl(PyObject *module, PyObject *s) -/*[clinic end generated code: output=cdcdf654d78788cc input=19c64a44650e17b7]*/ +static BOOL +_testFileExistsByName(LPCWSTR path, BOOL followLinks) { - path_t _path = PATH_T_INITIALIZE("isdir", "s", 0, 1); - BOOL result = FALSE; - - if (!path_converter(s, &_path)) { - path_cleanup(&_path); - if (PyErr_ExceptionMatches(PyExc_ValueError)) { - PyErr_Clear(); - return FALSE; + FILE_STAT_BASIC_INFORMATION info; + if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, + sizeof(info))) + { + if (!(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || + !followLinks && IsReparseTagNameSurrogate(info.ReparseTag)) + { + return TRUE; } - return -1; + } + else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( + GetLastError())) + { + return FALSE; } - Py_BEGIN_ALLOW_THREADS - if (_path.fd != -1) { - HANDLE hfile = _Py_get_osfhandle_noraise(_path.fd); + DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; + if (!followLinks) { + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + HANDLE hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, flags, NULL); + if (hfile != INVALID_HANDLE_VALUE) { + if (followLinks) { + CloseHandle(hfile); + return TRUE; + } + // Regular Reparse Points (PY_IFRRP) have to be traversed. + BOOL result = _testFileTypeByHandle(hfile, PY_IFRRP, FALSE); + CloseHandle(hfile); + if (!result) { + return TRUE; + } + hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hfile != INVALID_HANDLE_VALUE) { - result = _testFileTypeByHandle(hfile, PY_IFDIR, TRUE); + CloseHandle(hfile); + return TRUE; } } - else if (_path.wide) { - result = _testFileTypeByName(_path.wide, PY_IFDIR); + + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + STRUCT_STAT st; + if (followLinks) { + return !STAT(path, &st); + } + return !LSTAT(path, &st); } - Py_END_ALLOW_THREADS - path_cleanup(&_path); - return result; + return FALSE; } -/*[clinic input] -os._path_isfile -> bool - - path: object - -Test whether a path is a regular file - -[clinic start generated code]*/ - static int -os__path_isfile_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=b40d620efe5a896f input=54b428a310debaea]*/ +_testFileExists(path_t *path, BOOL followLinks) { - path_t _path = PATH_T_INITIALIZE("isfile", "path", 0, 1); BOOL result = FALSE; - if (!path_converter(path, &_path)) { path_cleanup(&_path); if (PyErr_ExceptionMatches(PyExc_ValueError)) { @@ -5303,30 +5325,28 @@ os__path_isfile_impl(PyObject *module, PyObject *path) } Py_BEGIN_ALLOW_THREADS - if (_path.fd != -1) { - HANDLE hfile = _Py_get_osfhandle_noraise(_path.fd); + if (path->fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); if (hfile != INVALID_HANDLE_VALUE) { - result = _testFileTypeByHandle(hfile, PY_IFREG, TRUE); + if (GetFileType(hfile) != FILE_TYPE_UNKNOWN || !GetLastError()) { + result = TRUE; + } } } - else if (_path.wide) { - result = _testFileTypeByName(_path.wide, PY_IFREG); + else if (path->wide) { + result = _testFileExistsByName(path->wide, followLinks); } Py_END_ALLOW_THREADS - path_cleanup(&_path); return result; } static int -nt_exists(PyObject *path, int follow_symlinks) +_testFileType(path_t *path, int testedType) { - path_t _path = PATH_T_INITIALIZE("exists", "path", 0, 1); - HANDLE hfile; - BOOL traverse = follow_symlinks, result = FALSE; - + BOOL result = FALSE; if (!path_converter(path, &_path)) { path_cleanup(&_path); if (PyErr_ExceptionMatches(PyExc_ValueError)) { @@ -5337,97 +5357,14 @@ nt_exists(PyObject *path, int follow_symlinks) } Py_BEGIN_ALLOW_THREADS - if (_path.fd != -1) { - hfile = _Py_get_osfhandle_noraise(_path.fd); + if (path->fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); if (hfile != INVALID_HANDLE_VALUE) { - result = 1; + result = _testFileTypeByHandle(hfile, testedType, TRUE); } } - else if (_path.wide) { - BOOL slow_path = TRUE; - FILE_STAT_BASIC_INFORMATION statInfo; - if (_Py_GetFileInformationByName(_path.wide, FileStatBasicByNameInfo, - &statInfo, sizeof(statInfo))) - { - if (!(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || - !follow_symlinks && - IsReparseTagNameSurrogate(statInfo.ReparseTag)) - { - slow_path = FALSE; - result = 1; - } - else { - // reparse point but not name-surrogate - traverse = TRUE; - } - } - else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( - GetLastError())) - { - slow_path = FALSE; - } - if (slow_path) { - BOOL traverse = follow_symlinks; - if (!traverse) { - hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | - FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (hfile != INVALID_HANDLE_VALUE) { - FILE_ATTRIBUTE_TAG_INFO info; - if (GetFileInformationByHandleEx(hfile, - FileAttributeTagInfo, &info, sizeof(info))) - { - if (!(info.FileAttributes & - FILE_ATTRIBUTE_REPARSE_POINT) || - IsReparseTagNameSurrogate(info.ReparseTag)) - { - result = 1; - } - else { - // reparse point but not name-surrogate - traverse = TRUE; - } - } - else { - // device or legacy filesystem - result = 1; - } - CloseHandle(hfile); - } - else { - STRUCT_STAT st; - switch (GetLastError()) { - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_CANT_ACCESS_FILE: - case ERROR_INVALID_PARAMETER: - if (!LSTAT(_path.wide, &st)) { - result = 1; - } - } - } - } - if (traverse) { - hfile = CreateFileW(_path.wide, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (hfile != INVALID_HANDLE_VALUE) { - CloseHandle(hfile); - result = 1; - } - else { - STRUCT_STAT st; - switch (GetLastError()) { - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_CANT_ACCESS_FILE: - case ERROR_INVALID_PARAMETER: - if (!STAT(_path.wide, &st)) { - result = 1; - } - } - } - } - } + else if (path->wide) { + result = _testFileTypeByName(path->wide, testedType); } Py_END_ALLOW_THREADS @@ -5450,7 +5387,8 @@ static int os__path_exists_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=8f784b3abf9f8588 input=2777da15bc4ba5a3]*/ { - return nt_exists(path, 1); + path_t _path = PATH_T_INITIALIZE("_path_exists", "path", 0, 1); + return _testFileExists(&_path, TRUE); } @@ -5468,7 +5406,44 @@ static int os__path_lexists_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=fec4a91cf4ffccf1 input=8843d4d6d4e7c779]*/ { - return nt_exists(path, 0); + path_t _path = PATH_T_INITIALIZE("_path_lexists", "path", 0, 1); + return _testFileExists(&_path, FALSE); +} + + +/*[clinic input] +os._path_isdir -> bool + + s: object + +Return true if the pathname refers to an existing directory. + +[clinic start generated code]*/ + +static int +os__path_isdir_impl(PyObject *module, PyObject *s) +/*[clinic end generated code: output=cdcdf654d78788cc input=19c64a44650e17b7]*/ +{ + path_t _path = PATH_T_INITIALIZE("_path_isdir", "s", 0, 1); + return _testFileType(&_path, PY_IFDIR); +} + + +/*[clinic input] +os._path_isfile -> bool + + path: object + +Test whether a path is a regular file + +[clinic start generated code]*/ + +static int +os__path_isfile_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=b40d620efe5a896f input=54b428a310debaea]*/ +{ + path_t _path = PATH_T_INITIALIZE("_path_isfile", "path", 0, 1); + return _testFileType(&_path, PY_IFREG); } @@ -5485,32 +5460,8 @@ static int os__path_islink_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=9d0cf8e4c640dfe6 input=b71fed60b9b2cd73]*/ { - path_t _path = PATH_T_INITIALIZE("islink", "path", 0, 1); - BOOL result = FALSE; - - if (!path_converter(path, &_path)) { - path_cleanup(&_path); - if (PyErr_ExceptionMatches(PyExc_ValueError)) { - PyErr_Clear(); - return FALSE; - } - return -1; - } - - Py_BEGIN_ALLOW_THREADS - if (_path.fd != -1) { - HANDLE hfile = _Py_get_osfhandle_noraise(_path.fd); - if (hfile != INVALID_HANDLE_VALUE) { - result = _testFileTypeByHandle(hfile, PY_IFLNK, TRUE); - } - } - else if (_path.wide) { - result = _testFileTypeByName(_path.wide, PY_IFLNK); - } - Py_END_ALLOW_THREADS - - path_cleanup(&_path); - return result; + path_t _path = PATH_T_INITIALIZE("_path_islink", "path", 0, 1); + return _testFileType(&_path, PY_IFLNK); } @@ -5527,38 +5478,16 @@ static int os__path_isjunction_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=f1d51682a077654d input=103ccedcdb714f11]*/ { - path_t _path = PATH_T_INITIALIZE("isjunction", "path", 0, 1); - BOOL result = FALSE; - - if (!path_converter(path, &_path)) { - path_cleanup(&_path); - if (PyErr_ExceptionMatches(PyExc_ValueError)) { - PyErr_Clear(); - return FALSE; - } - return -1; - } - - Py_BEGIN_ALLOW_THREADS - if (_path.fd != -1) { - HANDLE hfile = _Py_get_osfhandle_noraise(_path.fd); - if (hfile != INVALID_HANDLE_VALUE) { - result = _testFileTypeByHandle(hfile, PY_IFMNT, TRUE); - } - } - else if (_path.wide) { - result = _testFileTypeByName(_path.wide, PY_IFMNT); - } - Py_END_ALLOW_THREADS - - path_cleanup(&_path); - return result; + path_t _path = PATH_T_INITIALIZE("_path_isjunction", "path", 0, 1); + return _testFileType(&_path, PY_IFMNT); } #undef PY_IFREG #undef PY_IFDIR #undef PY_IFLNK #undef PY_IFMNT +#undef PY_IFLRP +#undef PY_IFRRP #endif /* MS_WINDOWS */ From cbf0bc45df87cef921b9de13ba6683dc7c87f27d Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 08:36:50 +0200 Subject: [PATCH 24/41] Update generated code --- Modules/clinic/posixmodule.c.h | 146 ++++++++++++++++----------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 78c7351c6ea31b..6be56b4983e2fc 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -2014,6 +2014,70 @@ os__path_splitroot(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py #if defined(MS_WINDOWS) +PyDoc_STRVAR(os__path_exists__doc__, +"_path_exists($module, path, /)\n" +"--\n" +"\n" +"Test whether a path exists. Returns False for broken symbolic links."); + +#define OS__PATH_EXISTS_METHODDEF \ + {"_path_exists", (PyCFunction)os__path_exists, METH_O, os__path_exists__doc__}, + +static int +os__path_exists_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_exists(PyObject *module, PyObject *path) +{ + PyObject *return_value = NULL; + int _return_value; + + _return_value = os__path_exists_impl(module, path); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + +PyDoc_STRVAR(os__path_lexists__doc__, +"_path_lexists($module, path, /)\n" +"--\n" +"\n" +"Test whether a path exists. Returns True for broken symbolic links."); + +#define OS__PATH_LEXISTS_METHODDEF \ + {"_path_lexists", (PyCFunction)os__path_lexists, METH_O, os__path_lexists__doc__}, + +static int +os__path_lexists_impl(PyObject *module, PyObject *path); + +static PyObject * +os__path_lexists(PyObject *module, PyObject *path) +{ + PyObject *return_value = NULL; + int _return_value; + + _return_value = os__path_lexists_impl(module, path); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyBool_FromLong((long)_return_value); + +exit: + return return_value; +} + +#endif /* defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + PyDoc_STRVAR(os__path_isdir__doc__, "_path_isdir($module, /, s)\n" "--\n" @@ -2142,70 +2206,6 @@ os__path_isfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj #if defined(MS_WINDOWS) -PyDoc_STRVAR(os__path_exists__doc__, -"_path_exists($module, path, /)\n" -"--\n" -"\n" -"Test whether a path exists. Returns False for broken symbolic links."); - -#define OS__PATH_EXISTS_METHODDEF \ - {"_path_exists", (PyCFunction)os__path_exists, METH_O, os__path_exists__doc__}, - -static int -os__path_exists_impl(PyObject *module, PyObject *path); - -static PyObject * -os__path_exists(PyObject *module, PyObject *path) -{ - PyObject *return_value = NULL; - int _return_value; - - _return_value = os__path_exists_impl(module, path); - if ((_return_value == -1) && PyErr_Occurred()) { - goto exit; - } - return_value = PyBool_FromLong((long)_return_value); - -exit: - return return_value; -} - -#endif /* defined(MS_WINDOWS) */ - -#if defined(MS_WINDOWS) - -PyDoc_STRVAR(os__path_lexists__doc__, -"_path_lexists($module, path, /)\n" -"--\n" -"\n" -"Test whether a path exists. Returns True for broken symbolic links."); - -#define OS__PATH_LEXISTS_METHODDEF \ - {"_path_lexists", (PyCFunction)os__path_lexists, METH_O, os__path_lexists__doc__}, - -static int -os__path_lexists_impl(PyObject *module, PyObject *path); - -static PyObject * -os__path_lexists(PyObject *module, PyObject *path) -{ - PyObject *return_value = NULL; - int _return_value; - - _return_value = os__path_lexists_impl(module, path); - if ((_return_value == -1) && PyErr_Occurred()) { - goto exit; - } - return_value = PyBool_FromLong((long)_return_value); - -exit: - return return_value; -} - -#endif /* defined(MS_WINDOWS) */ - -#if defined(MS_WINDOWS) - PyDoc_STRVAR(os__path_islink__doc__, "_path_islink($module, /, path)\n" "--\n" @@ -12181,14 +12181,6 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #define OS__PATH_SPLITROOT_METHODDEF #endif /* !defined(OS__PATH_SPLITROOT_METHODDEF) */ -#ifndef OS__PATH_ISDIR_METHODDEF - #define OS__PATH_ISDIR_METHODDEF -#endif /* !defined(OS__PATH_ISDIR_METHODDEF) */ - -#ifndef OS__PATH_ISFILE_METHODDEF - #define OS__PATH_ISFILE_METHODDEF -#endif /* !defined(OS__PATH_ISFILE_METHODDEF) */ - #ifndef OS__PATH_EXISTS_METHODDEF #define OS__PATH_EXISTS_METHODDEF #endif /* !defined(OS__PATH_EXISTS_METHODDEF) */ @@ -12197,6 +12189,14 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #define OS__PATH_LEXISTS_METHODDEF #endif /* !defined(OS__PATH_LEXISTS_METHODDEF) */ +#ifndef OS__PATH_ISDIR_METHODDEF + #define OS__PATH_ISDIR_METHODDEF +#endif /* !defined(OS__PATH_ISDIR_METHODDEF) */ + +#ifndef OS__PATH_ISFILE_METHODDEF + #define OS__PATH_ISFILE_METHODDEF +#endif /* !defined(OS__PATH_ISFILE_METHODDEF) */ + #ifndef OS__PATH_ISLINK_METHODDEF #define OS__PATH_ISLINK_METHODDEF #endif /* !defined(OS__PATH_ISLINK_METHODDEF) */ @@ -12752,4 +12752,4 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ -/*[clinic end generated code: output=131721546fb798da input=a9049054013a1b77]*/ +/*[clinic end generated code: output=75cdd3771c019da1 input=a9049054013a1b77]*/ From 4c0a3f7272a17ea473a6c5d8b2dc65f6cb89f043 Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 08:46:03 +0200 Subject: [PATCH 25/41] Fix path conversion --- Modules/posixmodule.c | 44 +++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 345803c57a57f8..277ef22a8162db 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5312,11 +5312,11 @@ _testFileExistsByName(LPCWSTR path, BOOL followLinks) static int -_testFileExists(path_t *path, BOOL followLinks) +_testFileExists(path_t *_path, PyObject *path, BOOL followLinks) { BOOL result = FALSE; - if (!path_converter(path, &_path)) { - path_cleanup(&_path); + if (!path_converter(path, _path)) { + path_cleanup(_path); if (PyErr_ExceptionMatches(PyExc_ValueError)) { PyErr_Clear(); return FALSE; @@ -5325,30 +5325,30 @@ _testFileExists(path_t *path, BOOL followLinks) } Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { - HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); + if (_path->fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(_path->fd); if (hfile != INVALID_HANDLE_VALUE) { if (GetFileType(hfile) != FILE_TYPE_UNKNOWN || !GetLastError()) { result = TRUE; } } } - else if (path->wide) { - result = _testFileExistsByName(path->wide, followLinks); + else if (_path->wide) { + result = _testFileExistsByName(_path->wide, followLinks); } Py_END_ALLOW_THREADS - path_cleanup(&_path); + path_cleanup(_path); return result; } static int -_testFileType(path_t *path, int testedType) +_testFileType(path_t *_path, PyObject *path, int testedType) { BOOL result = FALSE; - if (!path_converter(path, &_path)) { - path_cleanup(&_path); + if (!path_converter(path, _path)) { + path_cleanup(_path); if (PyErr_ExceptionMatches(PyExc_ValueError)) { PyErr_Clear(); return FALSE; @@ -5357,18 +5357,18 @@ _testFileType(path_t *path, int testedType) } Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { - HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); + if (_path->fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(_path->fd); if (hfile != INVALID_HANDLE_VALUE) { result = _testFileTypeByHandle(hfile, testedType, TRUE); } } - else if (path->wide) { - result = _testFileTypeByName(path->wide, testedType); + else if (_path->wide) { + result = _testFileTypeByName(_path->wide, testedType); } Py_END_ALLOW_THREADS - path_cleanup(&_path); + path_cleanup(_path); return result; } @@ -5388,7 +5388,7 @@ os__path_exists_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=8f784b3abf9f8588 input=2777da15bc4ba5a3]*/ { path_t _path = PATH_T_INITIALIZE("_path_exists", "path", 0, 1); - return _testFileExists(&_path, TRUE); + return _testFileExists(&_path, path, TRUE); } @@ -5407,7 +5407,7 @@ os__path_lexists_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=fec4a91cf4ffccf1 input=8843d4d6d4e7c779]*/ { path_t _path = PATH_T_INITIALIZE("_path_lexists", "path", 0, 1); - return _testFileExists(&_path, FALSE); + return _testFileExists(&_path, path, FALSE); } @@ -5425,7 +5425,7 @@ os__path_isdir_impl(PyObject *module, PyObject *s) /*[clinic end generated code: output=cdcdf654d78788cc input=19c64a44650e17b7]*/ { path_t _path = PATH_T_INITIALIZE("_path_isdir", "s", 0, 1); - return _testFileType(&_path, PY_IFDIR); + return _testFileType(&_path, path, PY_IFDIR); } @@ -5443,7 +5443,7 @@ os__path_isfile_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=b40d620efe5a896f input=54b428a310debaea]*/ { path_t _path = PATH_T_INITIALIZE("_path_isfile", "path", 0, 1); - return _testFileType(&_path, PY_IFREG); + return _testFileType(&_path, path, PY_IFREG); } @@ -5461,7 +5461,7 @@ os__path_islink_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=9d0cf8e4c640dfe6 input=b71fed60b9b2cd73]*/ { path_t _path = PATH_T_INITIALIZE("_path_islink", "path", 0, 1); - return _testFileType(&_path, PY_IFLNK); + return _testFileType(&_path, path, PY_IFLNK); } @@ -5479,7 +5479,7 @@ os__path_isjunction_impl(PyObject *module, PyObject *path) /*[clinic end generated code: output=f1d51682a077654d input=103ccedcdb714f11]*/ { path_t _path = PATH_T_INITIALIZE("_path_isjunction", "path", 0, 1); - return _testFileType(&_path, PY_IFMNT); + return _testFileType(&_path, path, PY_IFMNT); } #undef PY_IFREG From 5bf963edeac9cc9de6b0f091649a55c5429475cd Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 08:50:07 +0200 Subject: [PATCH 26/41] Fix undefined variable --- Modules/clinic/posixmodule.c.h | 10 +++++----- Modules/posixmodule.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 6be56b4983e2fc..5ec5635bae3f41 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -2088,7 +2088,7 @@ PyDoc_STRVAR(os__path_isdir__doc__, {"_path_isdir", _PyCFunction_CAST(os__path_isdir), METH_FASTCALL|METH_KEYWORDS, os__path_isdir__doc__}, static int -os__path_isdir_impl(PyObject *module, PyObject *s); +os__path_isdir_impl(PyObject *module, PyObject *path); static PyObject * os__path_isdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -2120,15 +2120,15 @@ os__path_isdir(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje }; #undef KWTUPLE PyObject *argsbuf[1]; - PyObject *s; + PyObject *path; int _return_value; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); if (!args) { goto exit; } - s = args[0]; - _return_value = os__path_isdir_impl(module, s); + path = args[0]; + _return_value = os__path_isdir_impl(module, path); if ((_return_value == -1) && PyErr_Occurred()) { goto exit; } @@ -12752,4 +12752,4 @@ os__supports_virtual_terminal(PyObject *module, PyObject *Py_UNUSED(ignored)) #ifndef OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #define OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF #endif /* !defined(OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF) */ -/*[clinic end generated code: output=75cdd3771c019da1 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=af5074c4ce4b19f1 input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 277ef22a8162db..00feb5b1c0c8ca 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5414,15 +5414,15 @@ os__path_lexists_impl(PyObject *module, PyObject *path) /*[clinic input] os._path_isdir -> bool - s: object + s as path: object Return true if the pathname refers to an existing directory. [clinic start generated code]*/ static int -os__path_isdir_impl(PyObject *module, PyObject *s) -/*[clinic end generated code: output=cdcdf654d78788cc input=19c64a44650e17b7]*/ +os__path_isdir_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=0504fd403f369701 input=2cb54dd97eb970f7]*/ { path_t _path = PATH_T_INITIALIZE("_path_isdir", "s", 0, 1); return _testFileType(&_path, path, PY_IFDIR); From 71cb7f02b7f5890d20f3e3496c8f36082c961807 Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 09:43:25 +0200 Subject: [PATCH 27/41] Add helper function --- Modules/posixmodule.c | 65 +++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 00feb5b1c0c8ca..7a85ffd0a340a6 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5095,6 +5095,22 @@ os__path_splitroot_impl(PyObject *module, path_t *path) #define PY_IFLRP 16 // Link Reparse Point (name-surrogate, symlink, junction) #define PY_IFRRP 32 // Regular Reparse Point +static inline BOOL +_testReparseTag(DWORD reparseTag, int testedType) +{ + switch (testedType) { + case PY_IFLNK: + return reparseTag == IO_REPARSE_TAG_SYMLINK; + case PY_IFMNT: + return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; + case PY_IFLRP: + return IsReparseTagNameSurrogate(reparseTag); + case PY_IFRRP: + return reparseTag && !IsReparseTagNameSurrogate(reparseTag); + } + return FALSE +} + static BOOL _testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) { @@ -5133,16 +5149,7 @@ _testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) } if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { - switch (testedType) { - case PY_IFLNK: - return reparseTag == IO_REPARSE_TAG_SYMLINK; - case PY_IFMNT: - return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; - case PY_IFLRP: - return IsReparseTagNameSurrogate(reparseTag); - case PY_IFRRP: - return reparseTag && !IsReparseTagNameSurrogate(reparseTag); - } + return _testReparseTag(reparseTag, testedType); } else if (testedType == PY_IFREG) { return ((fileDevType == FILE_TYPE_DISK) && @@ -5167,26 +5174,18 @@ _testFileTypeByName(LPCWSTR path, int testedType) sizeof(info))) { if (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - switch(testedType) { - case PY_IFREG: + if (testedType == PY_IFREG) { if (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { return FALSE; } - break; - case PY_IFDIR: + } + else if (testedType == PY_IFDIR) { if (!(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { return FALSE; } - break; - case PY_IFLNK: - return info.ReparseTag == IO_REPARSE_TAG_SYMLINK; - case PY_IFMNT: - return info.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT; - case PY_IFLRP: - return IsReparseTagNameSurrogate(info.ReparseTag); - case PY_IFRRP: - return (info.ReparseTag && - !IsReparseTagNameSurrogate(info.ReparseTag)); + } + else { + return _testReparseTag(info.ReparseTag, testedType); } } else if (testedType == PY_IFREG) { @@ -5229,22 +5228,8 @@ _testFileTypeByName(LPCWSTR path, int testedType) if (testedType == PY_IFDIR) { return !STAT(path, &st) && S_ISDIR(st.st_mode); } - if (!LSTAT(path, &st)) { - switch (testedType) { - case PY_IFLNK: - return st.st_reparse_tag == IO_REPARSE_TAG_SYMLINK; - case PY_IFMNT: - return st.st_reparse_tag == IO_REPARSE_TAG_MOUNT_POINT; - case PY_IFLRP: - return IsReparseTagNameSurrogate(st.st_reparse_tag); - case PY_IFRRP: - // This cannot be implemented generally, except a reparse - // point that is not handled by the system, such as - // IO_REPARSE_TAG_APPEXECLINK. - return (st.st_reparse_tag && - !IsReparseTagNameSurrogate(st.st_reparse_tag)); - } - } + return (!LSTAT(path, &st) && + _testReparseTag(st.st_reparse_tag, testedType)) } return FALSE; From 24d27495b733b03d32d393a1799f92b5abb65e19 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 13 May 2024 16:49:38 +0200 Subject: [PATCH 28/41] Apply suggestions from code review Co-authored-by: Eryk Sun --- Modules/posixmodule.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 7a85ffd0a340a6..9d264b867ac13c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5108,7 +5108,7 @@ _testReparseTag(DWORD reparseTag, int testedType) case PY_IFRRP: return reparseTag && !IsReparseTagNameSurrogate(reparseTag); } - return FALSE + return FALSE; } static BOOL @@ -5118,11 +5118,7 @@ _testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) testedType == PY_IFLNK || testedType == PY_IFMNT || testedType == PY_IFLRP || testedType == PY_IFRRP); - DWORD fileDevType = GetFileType(hfile); - if ((fileDevType == FILE_TYPE_UNKNOWN && GetLastError()) || - (fileDevType == FILE_TYPE_CHAR) || - (diskOnly && fileDevType != FILE_TYPE_DISK)) - { + if (diskOnly && GetFileType(hfile) != FILE_TYPE_DISK) { return FALSE; } @@ -5152,8 +5148,7 @@ _testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) return _testReparseTag(reparseTag, testedType); } else if (testedType == PY_IFREG) { - return ((fileDevType == FILE_TYPE_DISK) && - !(attributes & FILE_ATTRIBUTE_DIRECTORY)); + return attributes && !(attributes & FILE_ATTRIBUTE_DIRECTORY); } else if (testedType == PY_IFDIR) { return attributes & FILE_ATTRIBUTE_DIRECTORY; @@ -5189,7 +5184,8 @@ _testFileTypeByName(LPCWSTR path, int testedType) } } else if (testedType == PY_IFREG) { - return !(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY); + return (info.FileAttributes && !(info.FileAttributes & + FILE_ATTRIBUTE_DIRECTORY)); } else if (testedType == PY_IFDIR) { return info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; From 6edc3b3e1e3a4220b4e18ed412d36ca0340a1041 Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 17:00:54 +0200 Subject: [PATCH 29/41] Fix syntax error --- Modules/posixmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 9d264b867ac13c..62e3884577162d 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5225,7 +5225,7 @@ _testFileTypeByName(LPCWSTR path, int testedType) return !STAT(path, &st) && S_ISDIR(st.st_mode); } return (!LSTAT(path, &st) && - _testReparseTag(st.st_reparse_tag, testedType)) + _testReparseTag(st.st_reparse_tag, testedType)); } return FALSE; From c6dae2ca585416559a7372c6f8ae4c53034e7f4d Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 17:18:01 +0200 Subject: [PATCH 30/41] Further refactoring --- Modules/posixmodule.c | 87 +++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 62e3884577162d..241c3f8d9ad744 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5096,18 +5096,27 @@ os__path_splitroot_impl(PyObject *module, path_t *path) #define PY_IFRRP 32 // Regular Reparse Point static inline BOOL -_testReparseTag(DWORD reparseTag, int testedType) -{ - switch (testedType) { - case PY_IFLNK: - return reparseTag == IO_REPARSE_TAG_SYMLINK; - case PY_IFMNT: - return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; - case PY_IFLRP: - return IsReparseTagNameSurrogate(reparseTag); - case PY_IFRRP: - return reparseTag && !IsReparseTagNameSurrogate(reparseTag); +_testFileType(DWORD attributes, DWORD reparseTag, int testedType) +{ + if (testedType == PY_IFREG) { + return attributes && !(attributes & FILE_ATTRIBUTE_DIRECTORY); + } + if (testedType == PY_IFDIR) { + return attributes & FILE_ATTRIBUTE_DIRECTORY; } + if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { + switch (testedType) { + case PY_IFLNK: + return reparseTag == IO_REPARSE_TAG_SYMLINK; + case PY_IFMNT: + return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; + case PY_IFLRP: + return IsReparseTagNameSurrogate(reparseTag); + case PY_IFRRP: + return reparseTag && !IsReparseTagNameSurrogate(reparseTag); + } + } + return FALSE; } @@ -5144,17 +5153,7 @@ _testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) reparseTag = 0; } - if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { - return _testReparseTag(reparseTag, testedType); - } - else if (testedType == PY_IFREG) { - return attributes && !(attributes & FILE_ATTRIBUTE_DIRECTORY); - } - else if (testedType == PY_IFDIR) { - return attributes & FILE_ATTRIBUTE_DIRECTORY; - } - - return FALSE; + return _testFileType(attributes, reparseTag, testedType); } static BOOL @@ -5168,30 +5167,12 @@ _testFileTypeByName(LPCWSTR path, int testedType) if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, sizeof(info))) { - if (info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - if (testedType == PY_IFREG) { - if (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - return FALSE; - } - } - else if (testedType == PY_IFDIR) { - if (!(info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - return FALSE; - } - } - else { - return _testReparseTag(info.ReparseTag, testedType); - } - } - else if (testedType == PY_IFREG) { - return (info.FileAttributes && !(info.FileAttributes & - FILE_ATTRIBUTE_DIRECTORY)); - } - else if (testedType == PY_IFDIR) { - return info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY; - } - else { - return FALSE; + BOOL result = _testFileType(info.FileAttributes, info.ReparseTag, + testedType); + if (!result || testedType != PY_IFREG || testedType != PY_IFDIR || + !(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + { + return result; } } else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( @@ -5217,15 +5198,17 @@ _testFileTypeByName(LPCWSTR path, int testedType) case ERROR_SHARING_VIOLATION: case ERROR_CANT_ACCESS_FILE: case ERROR_INVALID_PARAMETER: + int rc; STRUCT_STAT st; - if (testedType == PY_IFREG) { - return !STAT(path, &st) && S_ISREG(st.st_mode); + if (testedType == PY_IFREG || testedType == PY_IFDIR) { + rc = STAT(path, &st); + } + else { + rc = LSTAT(path, &st); } - if (testedType == PY_IFDIR) { - return !STAT(path, &st) && S_ISDIR(st.st_mode); + if (!rc) { + return _testFileType(st.FileAttributes, st.ReparseTag, testedType); } - return (!LSTAT(path, &st) && - _testReparseTag(st.st_reparse_tag, testedType)); } return FALSE; From 05dd20e7dba66057b1932f9ef9aaa8ed01cc9c2c Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 17:22:21 +0200 Subject: [PATCH 31/41] Ternary operator --- Modules/posixmodule.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 241c3f8d9ad744..a7c4c6bbab2c0b 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5264,11 +5264,7 @@ _testFileExistsByName(LPCWSTR path, BOOL followLinks) case ERROR_SHARING_VIOLATION: case ERROR_CANT_ACCESS_FILE: case ERROR_INVALID_PARAMETER: - STRUCT_STAT st; - if (followLinks) { - return !STAT(path, &st); - } - return !LSTAT(path, &st); + return followLinks ? !STAT(path, NULL): !LSTAT(path, NULL); } return FALSE; From 2ced9285bb1276309d356f7d2c72bdd2236175be Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 17:24:10 +0200 Subject: [PATCH 32/41] Fix attributes --- Modules/posixmodule.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index a7c4c6bbab2c0b..5ab41c0d143929 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5207,7 +5207,8 @@ _testFileTypeByName(LPCWSTR path, int testedType) rc = LSTAT(path, &st); } if (!rc) { - return _testFileType(st.FileAttributes, st.ReparseTag, testedType); + return _testFileType(st.st_file_attributes, st.st_reparse_tag, + testedType); } } From dfd419607ab5f3cf7d9a20a92ab075d8198f6439 Mon Sep 17 00:00:00 2001 From: Nineteendo Date: Mon, 13 May 2024 17:37:26 +0200 Subject: [PATCH 33/41] No inline --- Modules/posixmodule.c | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 5ab41c0d143929..adff1cea796340 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5095,8 +5095,8 @@ os__path_splitroot_impl(PyObject *module, path_t *path) #define PY_IFLRP 16 // Link Reparse Point (name-surrogate, symlink, junction) #define PY_IFRRP 32 // Regular Reparse Point -static inline BOOL -_testFileType(DWORD attributes, DWORD reparseTag, int testedType) +static BOOL +_testInfo(DWORD attributes, DWORD reparseTag, int testedType) { if (testedType == PY_IFREG) { return attributes && !(attributes & FILE_ATTRIBUTE_DIRECTORY); @@ -5131,29 +5131,16 @@ _testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) return FALSE; } - DWORD attributes, reparseTag; if ((testedType != PY_IFREG && testedType != PY_IFDIR)) { FILE_ATTRIBUTE_TAG_INFO info; - if (!GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, - sizeof(info))) - { - return FALSE; - } - attributes = info.FileAttributes; - reparseTag = info.ReparseTag; + return GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, + sizeof(info)) && + _testInfo(info.FileAttributes, info.ReparseTag, testedType); } - else { - FILE_BASIC_INFO info; - if (!GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, - sizeof(info))) - { - return FALSE; - } - attributes = info.FileAttributes; - reparseTag = 0; - } - - return _testFileType(attributes, reparseTag, testedType); + FILE_BASIC_INFO info; + return GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, + sizeof(info)) && + _testInfo(info.FileAttributes, 0, testedType); } static BOOL @@ -5167,8 +5154,8 @@ _testFileTypeByName(LPCWSTR path, int testedType) if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, sizeof(info))) { - BOOL result = _testFileType(info.FileAttributes, info.ReparseTag, - testedType); + BOOL result = _testInfo(info.FileAttributes, info.ReparseTag, + testedType); if (!result || testedType != PY_IFREG || testedType != PY_IFDIR || !(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { @@ -5207,8 +5194,8 @@ _testFileTypeByName(LPCWSTR path, int testedType) rc = LSTAT(path, &st); } if (!rc) { - return _testFileType(st.st_file_attributes, st.st_reparse_tag, - testedType); + return _testInfo(st.st_file_attributes, st.st_reparse_tag, + testedType); } } From 7a7e942b51074911e4b830438167b940d65598b5 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 13 May 2024 20:47:40 +0200 Subject: [PATCH 34/41] Apply suggestions from code review Co-authored-by: Eryk Sun --- Modules/posixmodule.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index adff1cea796340..ad67a03fef4c32 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5131,7 +5131,7 @@ _testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) return FALSE; } - if ((testedType != PY_IFREG && testedType != PY_IFDIR)) { + if (testedType != PY_IFREG && testedType != PY_IFDIR) { FILE_ATTRIBUTE_TAG_INFO info; return GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, sizeof(info)) && @@ -5156,7 +5156,7 @@ _testFileTypeByName(LPCWSTR path, int testedType) { BOOL result = _testInfo(info.FileAttributes, info.ReparseTag, testedType); - if (!result || testedType != PY_IFREG || testedType != PY_IFDIR || + if (!result || (testedType != PY_IFREG && testedType != PY_IFDIR) || !(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { return result; @@ -5191,6 +5191,8 @@ _testFileTypeByName(LPCWSTR path, int testedType) rc = STAT(path, &st); } else { + // PY_IFRRP is not generally supported in this case, except for + // unhandled reparse points such as IO_REPARSE_TAG_APPEXECLINK. rc = LSTAT(path, &st); } if (!rc) { @@ -5252,7 +5254,8 @@ _testFileExistsByName(LPCWSTR path, BOOL followLinks) case ERROR_SHARING_VIOLATION: case ERROR_CANT_ACCESS_FILE: case ERROR_INVALID_PARAMETER: - return followLinks ? !STAT(path, NULL): !LSTAT(path, NULL); + STRUCT_STAT _st; + return followLinks ? !STAT(path, &_st): !LSTAT(path, &_st); } return FALSE; From 50b3612b3e9af236425de239e3aa6cec96d3cdbf Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 13 May 2024 22:24:08 +0200 Subject: [PATCH 35/41] Update posixmodule.c --- Modules/posixmodule.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index ad67a03fef4c32..20da57664e19b2 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5096,10 +5096,11 @@ os__path_splitroot_impl(PyObject *module, path_t *path) #define PY_IFRRP 32 // Regular Reparse Point static BOOL -_testInfo(DWORD attributes, DWORD reparseTag, int testedType) +_testInfo(DWORD attributes, DWORD reparseTag, BOOL diskDevice, int testedType) { if (testedType == PY_IFREG) { - return attributes && !(attributes & FILE_ATTRIBUTE_DIRECTORY); + return diskDevice && attributes && + !(attributes & FILE_ATTRIBUTE_DIRECTORY); } if (testedType == PY_IFDIR) { return attributes & FILE_ATTRIBUTE_DIRECTORY; @@ -5131,16 +5132,18 @@ _testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) return FALSE; } + BOOL diskDevice = GetFileType(hfile) == FILE_TYPE_DISK; if (testedType != PY_IFREG && testedType != PY_IFDIR) { FILE_ATTRIBUTE_TAG_INFO info; return GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, sizeof(info)) && - _testInfo(info.FileAttributes, info.ReparseTag, testedType); + _testInfo(info.FileAttributes, info.ReparseTag, diskDevice, + testedType); } FILE_BASIC_INFO info; return GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, sizeof(info)) && - _testInfo(info.FileAttributes, 0, testedType); + _testInfo(info.FileAttributes, 0, diskDevice, testedType); } static BOOL @@ -5154,8 +5157,11 @@ _testFileTypeByName(LPCWSTR path, int testedType) if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, sizeof(info))) { + BOOL diskDevice = info.DeviceType == FILE_DEVICE_DISK || + info.DeviceType == FILE_DEVICE_VIRTUAL_DISK || + info.DeviceType == FILE_DEVICE_CD_ROM; BOOL result = _testInfo(info.FileAttributes, info.ReparseTag, - testedType); + diskDevice, testedType); if (!result || (testedType != PY_IFREG && testedType != PY_IFDIR) || !(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { @@ -5196,8 +5202,9 @@ _testFileTypeByName(LPCWSTR path, int testedType) rc = LSTAT(path, &st); } if (!rc) { + BOOL diskDevice = GetFileType(hfile) == FILE_TYPE_DISK; return _testInfo(st.st_file_attributes, st.st_reparse_tag, - testedType); + diskDevice, testedType); } } From eab1cb43926aa28de88c6194bc30abf5566e37d3 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 13 May 2024 22:42:58 +0200 Subject: [PATCH 36/41] Update Modules/posixmodule.c Co-authored-by: Eryk Sun --- Modules/posixmodule.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 20da57664e19b2..56c36a943a004d 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5128,11 +5128,10 @@ _testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) testedType == PY_IFLNK || testedType == PY_IFMNT || testedType == PY_IFLRP || testedType == PY_IFRRP); - if (diskOnly && GetFileType(hfile) != FILE_TYPE_DISK) { + BOOL diskDevice = GetFileType(hfile) == FILE_TYPE_DISK; + if (diskOnly && !diskDevice) { return FALSE; } - - BOOL diskDevice = GetFileType(hfile) == FILE_TYPE_DISK; if (testedType != PY_IFREG && testedType != PY_IFDIR) { FILE_ATTRIBUTE_TAG_INFO info; return GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, From 6f6b07be2e06c8b0b7dfc3cf9c26a319c7bc43eb Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 13 May 2024 22:45:09 +0200 Subject: [PATCH 37/41] Update Modules/posixmodule.c Co-authored-by: Eryk Sun --- Modules/posixmodule.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 56c36a943a004d..6c00078727d542 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5201,9 +5201,8 @@ _testFileTypeByName(LPCWSTR path, int testedType) rc = LSTAT(path, &st); } if (!rc) { - BOOL diskDevice = GetFileType(hfile) == FILE_TYPE_DISK; return _testInfo(st.st_file_attributes, st.st_reparse_tag, - diskDevice, testedType); + st.st_mode & S_IFREG, testedType); } } From 0ca58a0218937510f51e8597457abb4414842fbb Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 13 May 2024 22:51:43 +0200 Subject: [PATCH 38/41] Update posixmodule.c --- Modules/posixmodule.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 6c00078727d542..c427fd122116aa 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5095,27 +5095,27 @@ os__path_splitroot_impl(PyObject *module, path_t *path) #define PY_IFLRP 16 // Link Reparse Point (name-surrogate, symlink, junction) #define PY_IFRRP 32 // Regular Reparse Point -static BOOL +static inline BOOL _testInfo(DWORD attributes, DWORD reparseTag, BOOL diskDevice, int testedType) { - if (testedType == PY_IFREG) { + switch (testedType) { + case PY_IFREG: return diskDevice && attributes && !(attributes & FILE_ATTRIBUTE_DIRECTORY); - } - if (testedType == PY_IFDIR) { + case PY_IFDIR: return attributes & FILE_ATTRIBUTE_DIRECTORY; - } - if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) { - switch (testedType) { - case PY_IFLNK: - return reparseTag == IO_REPARSE_TAG_SYMLINK; - case PY_IFMNT: - return reparseTag == IO_REPARSE_TAG_MOUNT_POINT; - case PY_IFLRP: - return IsReparseTagNameSurrogate(reparseTag); - case PY_IFRRP: - return reparseTag && !IsReparseTagNameSurrogate(reparseTag); - } + case PY_IFLNK: + return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && + reparseTag == IO_REPARSE_TAG_SYMLINK; + case PY_IFMNT: + return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && + reparseTag == IO_REPARSE_TAG_MOUNT_POINT; + case PY_IFLRP: + return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && + IsReparseTagNameSurrogate(reparseTag); + case PY_IFRRP: + return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && + reparseTag && !IsReparseTagNameSurrogate(reparseTag); } return FALSE; From 3f1ea76fb9fa2d836e0496c23175ddfc76431d19 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Mon, 13 May 2024 22:54:57 +0200 Subject: [PATCH 39/41] Update Modules/posixmodule.c Co-authored-by: Thomas Grainger --- Modules/posixmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index c427fd122116aa..87bc77bb16bdad 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5102,7 +5102,7 @@ _testInfo(DWORD attributes, DWORD reparseTag, BOOL diskDevice, int testedType) case PY_IFREG: return diskDevice && attributes && !(attributes & FILE_ATTRIBUTE_DIRECTORY); - case PY_IFDIR: + case PY_IFDIR: return attributes & FILE_ATTRIBUTE_DIRECTORY; case PY_IFLNK: return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && From 222c58aa90b6dc77249fd05dd552c709b098df23 Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Thu, 16 May 2024 14:41:27 +0200 Subject: [PATCH 40/41] Update test_ntpath.py --- Lib/test/test_ntpath.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 4a3728f3404bbc..c3ef362ef42eb3 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1096,7 +1096,7 @@ def test_isfile_driveletter(self): self.assertFalse(os.path.isfile('\\\\.\\' + drive)) @unittest.skipUnless(hasattr(os, 'pipe'), "need os.pipe()") - def test_isfile_pipe(self): + def test_isfile_anonymous_pipe(self): pr, pw = os.pipe() try: self.assertFalse(ntpath.isfile(pr)) @@ -1104,6 +1104,13 @@ def test_isfile_pipe(self): os.close(pr) os.close(pw) + @unittest.skipIf(sys.platform != 'win32', "windows only") + def test_isfile_named_pipe(self): + import _winapi + named_pipe = f'//./PIPE/python_isfile_test_{os.getpid()}' + _winapi.CreateNamedPipe(named_pipe, _winapi.PIPE_ACCESS_INBOUND, 0, 1, 0, 0, 0, 0) + self.assertFalse(ntpath.isfile(named_pipe)) + @unittest.skipIf(sys.platform != 'win32', "windows only") def test_con_device(self): self.assertFalse(os.path.isfile(r"\\.\CON")) From 6e41cbf0981bf8a98c51d04071fffab3b972a42b Mon Sep 17 00:00:00 2001 From: Nice Zombies Date: Thu, 16 May 2024 19:16:50 +0200 Subject: [PATCH 41/41] Update Lib/test/test_ntpath.py Co-authored-by: Eryk Sun --- Lib/test/test_ntpath.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index c3ef362ef42eb3..9aa116682f7480 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -1108,8 +1108,13 @@ def test_isfile_anonymous_pipe(self): def test_isfile_named_pipe(self): import _winapi named_pipe = f'//./PIPE/python_isfile_test_{os.getpid()}' - _winapi.CreateNamedPipe(named_pipe, _winapi.PIPE_ACCESS_INBOUND, 0, 1, 0, 0, 0, 0) - self.assertFalse(ntpath.isfile(named_pipe)) + h = _winapi.CreateNamedPipe(named_pipe, + _winapi.PIPE_ACCESS_INBOUND, + 0, 1, 0, 0, 0, 0) + try: + self.assertFalse(ntpath.isfile(named_pipe)) + finally: + _winapi.CloseHandle(h) @unittest.skipIf(sys.platform != 'win32', "windows only") def test_con_device(self):