bpo-34711: On systems that open() a regular file with a trailing slash respond with ENOTDIR#9360
bpo-34711: On systems that open() a regular file with a trailing slash respond with ENOTDIR#9360aixtools wants to merge 8 commits into
Conversation
| be an error - ENOTDIR */ | ||
| if (S_ISREG(fdfstat.st_mode)) { | ||
| #ifdef MS_WINDOWS | ||
| rcpt = strrchr(widename, '\'); |
There was a problem hiding this comment.
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;
}
}| if (S_ISREG(fdfstat.st_mode)) { | ||
| int trailing_slash = 0; | ||
| #ifdef MS_WINDOWS | ||
| size_t len = wcslen(widename); |
There was a problem hiding this comment.
widename and name can be NULL at this point.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| /* 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)) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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"
|
On 18/09/2018 23:50, Eryk Sun wrote:
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`.
Your right! Did not know "how this works". I'll make the change when I
have time again. Many thanks for the clarification.
Michael
|
|
On 9/25/2018 4:50 PM, Eryk Sun wrote:
On second thought, I think this code is simple enough to eliminate
|trailing_slash|. Also, per PEP 7, ...
Are we "all good" now?
|
|
The code looks good to me. @vstinner? |
vstinner
left a comment
There was a problem hiding this comment.
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.
|
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 |
|
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? |
|
I got access to my Windows VM. I elaborated my reply: |
|
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:
|
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.