Opened 8 years ago

Closed 8 years ago

Last modified 8 years ago

#247 closed defect (fixed)

libc: feof(pipe_handle) keeps returning 0 after pipe is closed

Reported by: dmik Owned by: bird
Priority: normal Milestone: libc-0.6.5
Component: libc-backend Version: 0.6
Severity: normal Keywords:
Cc:

Description

Given that stdin of a process is an end of an OS/2 named pipe, the following loop

while (!feof (stdin))
    fread (buf, 1, sizeof (buf), stdin);

will not stop when the other end of the pipe is closed.

While one may argue that the loop is incorrect (since besides the EOF there may be e.g. a permanent read error that isn't checked in the loop), the closed pipe is clearly an EOF condition.

On Linux, the above code does stop when the other end of the pipe is closed and there are some programs that rely on that.

Change History (6)

comment:1 Changed 8 years ago by dmik

There is one more related problem. On Linux, ferror() for a closed pipe returns 0 while on OS/2 it returns 1. This is also unexpected by many programs (e.g. 7z and rar32 when instructed to compress stdin) that treat this as a fatal error and abort.

In other words, the following program:

    char buffer[4096];
    int n;

    _fsetmode(stdin, "b");


    while (!feof (stdin) && !ferror (stdin))

    {

        n = fread (buffer, 1, sizeof (buffer), stdin);

        printf ("n %d errno %d\n", n, errno);

    }

    printf ("feof (stdin) %d, ferror (stdin) %d\n",

            feof (stdin), ferror (stdin));

being fed a file 22 bytes long on its stdin through a pipe prints this on OS/2:

n 22 errno 57
feof (stdin) 0, ferror (stdin) 1

while this on Linux

n 22 errno 0
feof (stdin) 1, ferror (stdin) 0

comment:2 Changed 8 years ago by dmik

errno 57 is ENOTCONN. This error is translated in __read() (lib/sys/__read.c) from ERROR_PIPE_NOT_CONNECTED obtained from DosRead?().

According to CPREF, ERROR_PIPE_NOT_CONNECTED is returned by DosRead?() on the client when the server has called DosDisconnectNPipe() on its end of the pipe. It turns out that if the server bypasses the DosDisconnectNPipe() call and calls DosClose?() directly, the error is not reported to the client, the next read operation simply returns 0 bytes (which is equivalent to EOF and fixes both feof() and ferror()). The CPREF is definitely not certain about what is the right behavior...

Anyway, I think that it makes sense to handle ERROR_PIPE_NOT_CONNECTED specially in __read() by simply ignoring it and returning zero (w/o setting errno). This will fix the problem for servers still using a pair of DosDisconnectNPipe() + DosClose?() for closing their end of the pipe.

comment:3 Changed 8 years ago by bird

  • Component changed from baselayout to libc-backend
  • Milestone set to libc-0.6.5
  • Status changed from new to accepted
  • Summary changed from feof(pipe_handle) keeps returning 0 after pipe is closed to libc: feof(pipe_handle) keeps returning 0 after pipe is closed
  • Version set to 0.6

comment:4 Changed 8 years ago by bird

  • Status changed from accepted to assigned

comment:5 Changed 8 years ago by bird

  • Resolution set to fixed
  • Status changed from assigned to closed

Checked a little bit and seems ERROR_PIPE_NOT_CONNECTED is only used by named pipes. So it should be safe to change read()'s interpretation of this DosRead? return code. Implemented in r3776.

comment:6 Changed 8 years ago by bird

Forgot to check the feof() thing, but it turns out it's not only related, it's the same problem. _fill(FILE*) sets _IOEOF when _stream_read, i.e. read, returns 0. :-)

Note: See TracTickets for help on using tickets.