Issue21191
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2014-04-09 18:24 by Dima.Tisnek, last changed 2022-04-11 14:58 by admin. This issue is now closed.
Messages (17) | |||
---|---|---|---|
msg215832 - (view) | Author: Dima Tisnek (Dima.Tisnek) * | Date: 2014-04-09 18:24 | |
os.fdopen() should either: * consume file descriptor and return file/io object, or * leave file descriptor alone and raise an exception this invariant is broken in the following test case: fd = os.open("/", os.O_RDONLY) try: obj = os.fdopen(fd, "r") except EnvironmentError: os.close(fd) # cleanup what happens: fd = os.open("/", os.O_RDONLY) --> some fd os.fdopen(fd, "r") --> exception, fd refers to a directory os.close(fd) --> exception, no such fd For reference: 1. invariant is held in Python 3.3. 2. following positive test case works correctly fd = os.open("/etc/passwd", os.O_RDONLY) try: obj = os.fdopen(fd, "r") # success except EnvironmentError: os.close(fd) # not reached 3. following negative test case works correctly fd = os.open("/etc/passwd", os.O_RDONLY) try: obj = os.fdopen(fd, "w") # wrong mode on purpose except EnvironmentError: os.close(fd) # closed ok |
|||
msg215835 - (view) | Author: Benjamin Peterson (benjamin.peterson) * | Date: 2014-04-09 19:01 | |
I think this is just won't fix. I agree the behavior in 3.x is better, but at least fdopen() is consistent about closing in 2.x, so you could work around it by dupping the fd. |
|||
msg215837 - (view) | Author: Dima Tisnek (Dima.Tisnek) * | Date: 2014-04-09 19:22 | |
Benjamin, I think you missed the key point: file + matching mode -> fd eaten, object created file + mode mismatch -> fd remains, exception raised dir + matching mode -> fd eaten, exception raised The issue is about resouce (fd) management Thus, how can user code handle the error without either leaking file descriptor or possibly closing someone else's file descriptor? |
|||
msg215838 - (view) | Author: Benjamin Peterson (benjamin.peterson) * | Date: 2014-04-09 19:26 | |
Ah, you're right. I misread the code. |
|||
msg215839 - (view) | Author: Roundup Robot (python-dev) | Date: 2014-04-09 19:40 | |
New changeset 4a3b455abf76 by Benjamin Peterson in branch '2.7': make sure fdopen always closes the fd in error cases (closes #21191) http://hg.python.org/cpython/rev/4a3b455abf76 |
|||
msg215840 - (view) | Author: Dima Tisnek (Dima.Tisnek) * | Date: 2014-04-09 19:48 | |
I don't like proposed patch -- it changes semantics of more (?) common failure modes. I think it's best to keep semantics in line with Python 3.3 -- if fdopen fails, it leaves file descriptor alone. |
|||
msg215841 - (view) | Author: Benjamin Peterson (benjamin.peterson) * | Date: 2014-04-09 19:52 | |
On Wed, Apr 9, 2014, at 12:48, Dima Tisnek wrote: > > Dima Tisnek added the comment: > > I don't like proposed patch -- it changes semantics of more (?) common > failure modes. I don't know if opening the file with the wrong mode is any more wrong than opening a directory. |
|||
msg215844 - (view) | Author: Dima Tisnek (Dima.Tisnek) * | Date: 2014-04-09 20:18 | |
Good point. Personally I think it's more pythonic to consume fd only on success. I accept that's just an opinion. In any case, let's keep error semantics in py2.7 and py3.3 same. |
|||
msg215851 - (view) | Author: Benjamin Peterson (benjamin.peterson) * | Date: 2014-04-09 21:50 | |
On Wed, Apr 9, 2014, at 13:18, Dima Tisnek wrote: > > Dima Tisnek added the comment: > > Good point. > > Personally I think it's more pythonic to consume fd only on success. I > accept that's just an opinion. > > In any case, let's keep error semantics in py2.7 and py3.3 same. Unfortunately, it's not easy to implement with the 2.7 implementation of the file type. |
|||
msg215859 - (view) | Author: Benjamin Peterson (benjamin.peterson) * | Date: 2014-04-10 00:19 | |
I should note the C level fdopen has the the 2.x semantics. |
|||
msg215895 - (view) | Author: Dima Tisnek (Dima.Tisnek) * | Date: 2014-04-10 17:46 | |
I'm not sure if you are referring to Python's C-level fdopen or GNU libc fdopen. GNU libc fdopen does not consume file descriptor on error: #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> int main(int argc, char** argv) { int fd = -1; int rv = 0; FILE* fh = NULL; if (argc<3) return 1; errno = 0; fd = open(argv[1], O_RDONLY); printf("got fd %d errno %d text %s\n", fd, errno, strerror(errno)); errno = 0; fh = fdopen(fd, argv[2]); printf("got fh %x errno %d text %s\n", fh, errno, strerror(errno)); errno = 0; rv = close(fd); printf("got rv %d errno %d text %s\n", rv, errno, strerror(errno)); } [dima@bmg ~]$ ./a.out /etc/passwd w got fd 4 errno 0 text Success got fh 0 errno 22 text Invalid argument got rv 0 errno 0 text Success To be fair, GNU libc fdopen doesn't consider it an error to use a file descriptor that refers to a directory, which is the crux of this bug. Anyhow, point is the semantics change your patch brings in sets Python 2.7+ in contrast with both Python 3.x and GNU libc. Perhaps if it's too hard to implement properly, it's better to leave this issue as won't fix / technical limitation? |
|||
msg215912 - (view) | Author: Benjamin Peterson (benjamin.peterson) * | Date: 2014-04-10 22:55 | |
On Thu, Apr 10, 2014, at 10:46, Dima Tisnek wrote: > > Dima Tisnek added the comment: > > I'm not sure if you are referring to Python's C-level fdopen or GNU libc > fdopen. > > GNU libc fdopen does not consume file descriptor on error: > > #include <sys/types.h> > #include <sys/stat.h> > #include <fcntl.h> > #include <stdio.h> > #include <string.h> > #include <errno.h> > #include <unistd.h> > > > int main(int argc, char** argv) > { > int fd = -1; > int rv = 0; > FILE* fh = NULL; > if (argc<3) return 1; > > errno = 0; > fd = open(argv[1], O_RDONLY); > printf("got fd %d errno %d text %s\n", fd, errno, strerror(errno)); > > errno = 0; > fh = fdopen(fd, argv[2]); > printf("got fh %x errno %d text %s\n", fh, errno, strerror(errno)); > > errno = 0; > rv = close(fd); > printf("got rv %d errno %d text %s\n", rv, errno, strerror(errno)); > } > > [dima@bmg ~]$ ./a.out /etc/passwd w > got fd 4 errno 0 text Success > got fh 0 errno 22 text Invalid argument > got rv 0 errno 0 text Success > > To be fair, GNU libc fdopen doesn't consider it an error to use a file > descriptor that refers to a directory, which is the crux of this bug. I meant once you manage to get fdopen to succeed, the fd has basically been consumed. > > Anyhow, point is the semantics change your patch brings in sets Python > 2.7+ in contrast with both Python 3.x and GNU libc. I realize. > > Perhaps if it's too hard to implement properly, it's better to leave this > issue as won't fix / technical limitation? I mean if you'd prefer for it to just be inconsistent in 2.x... |
|||
msg215921 - (view) | Author: Dima Tisnek (Dima.Tisnek) * | Date: 2014-04-11 09:40 | |
I think consistency between Python versions is just as important as consistency between fd types. Here's my hack quickfix outline: fd = os.open(...) try: if not stat.S_ISREG(os.fstat(fd).st_mode): raise OSError(None, "Not a regular file", ...) f = os.fdopen(fd, ...) except EnvironmentError: os.close(fd) Can something like this be implemented in os.py There's already a check `if not isinstance(fd, int): raise ...` Granted we'd have to get fd type check exactly right. fdopen should probably succeed for regular files, pipes, char devices, block device, ptry's ... fdopen should fail where underlying implementation fails, i.e. directories, sockets(?), epoll(?), timerfd(?) There's a list at http://en.wikipedia.org/wiki/File_descriptor I'm not sure about some types. P.S. I wish there was a way to rescue fd from FILE*, but nothing like that seems to exist... P.P.S. another option is to always use dup(), but that may break existing programs is they expect fd == fdopen(fd).fileno() |
|||
msg216066 - (view) | Author: Benjamin Peterson (benjamin.peterson) * | Date: 2014-04-14 04:05 | |
Feel free to submit a patch. On Fri, Apr 11, 2014, at 2:40, Dima Tisnek wrote: > > Dima Tisnek added the comment: > > I think consistency between Python versions is just as important as > consistency between fd types. > > Here's my hack quickfix outline: > > fd = os.open(...) > try: > if not stat.S_ISREG(os.fstat(fd).st_mode): > raise OSError(None, "Not a regular file", ...) > f = os.fdopen(fd, ...) > except EnvironmentError: > os.close(fd) > > Can something like this be implemented in os.py > There's already a check `if not isinstance(fd, int): raise ...` > > Granted we'd have to get fd type check exactly right. Well, you just have check exactly what it's checking now, which seems to be !S_ISDIR. > fdopen should probably succeed for regular files, pipes, char devices, > block device, ptry's ... > fdopen should fail where underlying implementation fails, i.e. > directories, sockets(?), epoll(?), timerfd(?) > > There's a list at http://en.wikipedia.org/wiki/File_descriptor > I'm not sure about some types. > > P.S. I wish there was a way to rescue fd from FILE*, but nothing like > that seems to exist... Yes, this is one of the main problems. > > P.P.S. another option is to always use dup(), but that may break existing > programs is they expect fd == fdopen(fd).fileno() |
|||
msg216252 - (view) | Author: Roundup Robot (python-dev) | Date: 2014-04-14 23:46 | |
New changeset 339c79791b65 by Benjamin Peterson in branch '2.7': when an exception is raised in fdopen, never close the fd (changing on my mind on #21191) http://hg.python.org/cpython/rev/339c79791b65 |
|||
msg216283 - (view) | Author: Dima Tisnek (Dima.Tisnek) * | Date: 2014-04-15 09:34 | |
Banjamin, Your patch looks good to me! I have a small concern regarding "We now know we will succeed..." -- should there be a test case to make sure fstat test here matches whatever test is/was done on a lower level? Or is your code now such that it will explicitly succeed because it doesn't call anything that can fail after the comment? |
|||
msg216285 - (view) | Author: Benjamin Peterson (benjamin.peterson) * | Date: 2014-04-15 12:58 | |
On Tue, Apr 15, 2014, at 2:34, Dima Tisnek wrote: > > Dima Tisnek added the comment: > > Banjamin, Your patch looks good to me! > > I have a small concern regarding "We now know we will succeed..." -- > should there be a test case to make sure fstat test here matches whatever > test is/was done on a lower level? > > Or is your code now such that it will explicitly succeed because it > doesn't call anything that can fail after the comment? That's what I mean. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:58:01 | admin | set | github: 65390 |
2014-04-15 12:58:39 | benjamin.peterson | set | messages: + msg216285 |
2014-04-15 09:34:17 | Dima.Tisnek | set | messages: + msg216283 |
2014-04-14 23:46:08 | python-dev | set | messages: + msg216252 |
2014-04-14 04:05:29 | benjamin.peterson | set | messages: + msg216066 |
2014-04-11 09:40:20 | Dima.Tisnek | set | messages: + msg215921 |
2014-04-10 22:55:33 | benjamin.peterson | set | messages: + msg215912 |
2014-04-10 17:46:50 | Dima.Tisnek | set | messages: + msg215895 |
2014-04-10 00:19:35 | benjamin.peterson | set | messages: + msg215859 |
2014-04-09 21:50:02 | benjamin.peterson | set | messages: + msg215851 |
2014-04-09 20:18:38 | Dima.Tisnek | set | messages: + msg215844 |
2014-04-09 19:52:59 | benjamin.peterson | set | messages: + msg215841 |
2014-04-09 19:48:55 | Dima.Tisnek | set | messages: + msg215840 |
2014-04-09 19:40:36 | python-dev | set | status: open -> closed nosy: + python-dev messages: + msg215839 resolution: fixed stage: resolved |
2014-04-09 19:26:03 | benjamin.peterson | set | status: closed -> open resolution: wont fix -> (no value) messages: + msg215838 |
2014-04-09 19:22:54 | Dima.Tisnek | set | messages: + msg215837 |
2014-04-09 19:01:06 | benjamin.peterson | set | status: open -> closed nosy: + benjamin.peterson messages: + msg215835 resolution: wont fix |
2014-04-09 18:24:55 | Dima.Tisnek | create |