Skip to content

Conversation

@vstinner
Copy link
Member

@vstinner vstinner commented Jan 11, 2022

Comment on lines +119 to +133
static int
file_exists(PyObject *path)
{
wchar_t *wpath = PyUnicode_AsWideCharString(path, NULL);
if (wpath == NULL) {
return -2;
}

DWORD attr = GetFileAttributesW(wpath);
int res = (attr != INVALID_FILE_ATTRIBUTES);

PyMem_Free(wpath);
return res;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetFileAttributesW() doesn't follow a reparse point, such as a symlink, in the final component of the path. If tcl_library_path is the path of a symlink, checking existence of the target requires CreateFileW(wpath, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL). If the call succeeds, i.e. the call doesn't return INVALID_HANDLE_VALUE, the target exists and is accessible; close the handle.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the suggested implementation:

static int
file_exists(PyObject *path)
{
    wchar_t *wpath = PyUnicode_AsWideCharString(path, NULL);
    if (wpath == NULL) {
        return -2;
    }

    HANDLE hfile = CreateFileW(wpath, 0, 0, NULL, OPEN_EXISTING,
                        FILE_FLAG_BACKUP_SEMANTICS, NULL);
    if (hfile != INVALID_HANDLE_VALUE) {
        CloseHandle(hfile);
    }
    
    PyMem_Free(wpath);

    return (hfile == INVALID_HANDLE_VALUE) ? -1 : 0;
}

This returns -1 when the file doesn't exist, since file_exists() replaces _Py_stat().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't expect that checking if a file exists would be so "complicated" :-) I'm waiting for @pacampbell to see if further changes are needed. Maybe this PR is not needed to build Python with clang.

Copy link
Contributor

@eryksun eryksun Jan 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GetFileAttributesW() call is like checking for existence with lstat() instead of stat(). That's probably good enough for tcl_library_path. Python's r"tcl\tclX.Y" is installed as a normal directory. However, I don't think the CreateFileW() call is complicated, and it makes file_exists() generally correct.

For the current implementation, the use of res == -1 in _get_tcl_lib_path() is wrong. The current implementation returns 1 if the file exists and 0 if it doesn't exist. This has to be fixed even if you keep the GetFileAttributesW() call.

@vstinner
Copy link
Member Author

https://bugs.python.org/issue46303#msg410377 is now fixed, this change is not needed.

@vstinner vstinner closed this Jan 12, 2022
@vstinner vstinner deleted the py_stat branch January 12, 2022 01:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants