Skip to content

bpo-34711: On systems that open() a regular file with a trailing slash respond with ENOTDIR#9360

Closed
aixtools wants to merge 8 commits into
python:masterfrom
aixtools:bpo-34711
Closed

bpo-34711: On systems that open() a regular file with a trailing slash respond with ENOTDIR#9360
aixtools wants to merge 8 commits into
python:masterfrom
aixtools:bpo-34711

Conversation

@aixtools

@aixtools aixtools commented Sep 17, 2018

Copy link
Copy Markdown
Contributor

https://bugs.python.org/issue34711

from http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html

[ENOTDIR]
A component of the path prefix names an existing file that is neither a directory nor a symbolic link to a directory; or O_CREAT and O_EXCL are not specified, the path argument contains at least one non- character and ends with one or more trailing characters, and the last pathname component names an existing file that is neither a directory nor a symbolic link to a directory; or O_DIRECTORY was specified and the path argument resolves to a non-directory file.

Comment thread Modules/_io/fileio.c Outdated
be an error - ENOTDIR */
if (S_ISREG(fdfstat.st_mode)) {
#ifdef MS_WINDOWS
rcpt = strrchr(widename, '\');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For Windows use wcslen(widename) and wcsrchr(widename, L'\\'). The latter returns a wchar_t *. The Windows code also needs to look for forward slash.

You could separate the Windows and POSIX checks using a boolean. For example:

if (S_ISREG(fdfstat.st_mode)) {
    int trailing_slash = 0;
#ifdef MS_WINDOWS
    size_t len = wcslen(widename);
    if (len && (widename[len - 1] == L'\\' || widename[len - 1] == L'/')) {
        trailing_slash = 1;
    }
#else
    size_t len = strlen(name);
    if (len && name[len - 1] == '/') {
        trailing_slash = 1;
    }
#endif
    if (trailing_slash) {
        errno = ENOTDIR;
        PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
        goto error;
    }
}

Comment thread Modules/_io/fileio.c Outdated
if (S_ISREG(fdfstat.st_mode)) {
int trailing_slash = 0;
#ifdef MS_WINDOWS
size_t len = wcslen(widename);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

widename and name can be NULL at this point.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I cannot imagine how, at this point (as the first call when they must be defined should have returned the error THEN), but yes, since you could conceiveable get to this code without having to call open() - because it was already opened.

will prefix it all that name || widename respectively, is not NULL. If they are, they must have already passed successfully.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@aixtools, Python's open function accepts an integer FD instead of a filename. See my comment above. There's no need to check name and widename separately. Just check the common fd value.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

tomorrow then. But if there is a fd already - should I care? assume the file was opened by an extern module. If that has accepted trailing slash, I, as python, no longer care. I, python, only care when I am doing an open() using a (wide)name.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You've misunderstood me. The fd variable is not self->fd. Please re-read the function from the top. It's assigned as fd = _PyLong_AsInt(nameobj). If Python open was called with an integer fd, then this variable will be 0 or greater. If it's less than 0, and we've made it this far, then we know that open was called with a filename, not an fd. This is all you have to test. You do not have to check whether name or widename is NULL.

Comment thread Modules/_io/fileio.c Outdated
/* On AIX and Windows, open may succeed for files with a trailing slash.
The Open Group specifies filenames ending with a trailing slash should
be an error - ENOTDIR */
if (S_ISREG(fdfstat.st_mode)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Change this condition to fd < 0 && S_ISREG(fdfstat.st_mode) to ignore opening a file descriptor, so we know widename / name aren't NULL.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

a) If fd < 0 I do not think we are even in this block. See lines 432-449
+432 Py_BEGIN_ALLOW_THREADS
+433 fstat_result = _Py_fstat_noraise(self->fd, &fdfstat);
+434 Py_END_ALLOW_THREADS
+435 if (fstat_result < 0) {
+436 /* Tolerate fstat() errors other than EBADF. See Issue #25717, where
+437 an anonymous file on a Virtual Box shared folder filesystem would
+438 raise ENOENT. */
+439 #ifdef MS_WINDOWS
+440 if (GetLastError() == ERROR_INVALID_HANDLE) {
+441 PyErr_SetFromWindowsErr(0);
+442 #else
+443 if (errno == EBADF) {
+444 PyErr_SetFromErrno(PyExc_OSError);
+445 #endif
+446 goto error;
+447 }
+448 }
+449 else {
b) afaik, if fd<0 the open() call did not succeed and the old logic will report "something"

@aixtools

aixtools commented Sep 24, 2018 via email

Copy link
Copy Markdown
Contributor Author

Comment thread Modules/_io/fileio.c Outdated
@aixtools

aixtools commented Sep 26, 2018 via email

Copy link
Copy Markdown
Contributor Author

@eryksun

eryksun commented Sep 26, 2018

Copy link
Copy Markdown
Contributor

The code looks good to me. @vstinner?

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't understand this change. Why Python would have to check manually the filename? It's the job of the operating system to do that. If you dislike the behavior of your OS, report the issue to the kernel of your OS, no?

I don't understand the current behavior of the code (especially on Windows) and I don't understand the NEWS entry message.

@bedevere-bot

Copy link
Copy Markdown

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@vstinner

vstinner commented Oct 1, 2018

Copy link
Copy Markdown
Member

There are much more functions which "open a file". Open Python/fileutils.c for a few mores. What about os.open()? What about all other functions which accept a filename and then call a third party library which calls open() directly?

@vstinner

vstinner commented Oct 2, 2018

Copy link
Copy Markdown
Member

I got access to my Windows VM. I elaborated my reply:
https://bugs.python.org/issue34711#msg326867

@aixtools

aixtools commented Oct 2, 2018

Copy link
Copy Markdown
Contributor Author

Per your comment in the issue tracker, shall close this PR and make a simple change to the test to skipIf(AcceptsBackslash, ...)

On 10/2/2018 7:36 PM, Michael Felt wrote:

Python is designed as a thin wrapper to the operating system. IMHO Python must not validate the filename itself.

@aixtools aixtools closed this Oct 2, 2018
@aixtools
aixtools deleted the bpo-34711 branch October 2, 2018 17:50
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.

6 participants