Search code examples
cfile-ioioposix

Why read() on file descriptor fails if lseek() is used?


In the following example we close default stderr and reopen it on the temporary file via fdopen(), using descriptor 2, which was dup()'ed from the temporary file descriptor. Then we write() directly to this descriptor 2. We can safely do this, because the is the first write operation on file and thus it has the empty buffer. After this, we fprintf() to the new stderr. Then we close stderr (thus, its associated descriptor 2 is automatically closed). Original descriptor fd remains valid. Through it we go to the beginning of the temporary file, read its contents and print them to stdout. But the output is garbled:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(void)
{
  int fd;
  char buf[200];
  int n;
  char fname[] = "/tmp/tst-perror.XXXXXX";
  fd = mkstemp (fname);
  fclose (stderr);
  dup2 (fd, 2);
  stderr = fdopen (2, "w");
  fd = fileno(stderr);
  char *s = "this is a test\n";
  n = write(fd, s, strlen(s));
  fprintf(stderr, "multibyte string\n");
  fclose (stderr);
//  close(fd);
//  fd = open(fname, O_RDONLY);
  lseek (fd, 0, SEEK_SET);
  n = read (fd, buf, sizeof (buf));
  printf("%.*s", (int) n, buf);
  close (fd);
  return 0;
}

The output is:

$ ./a.out
����

If we uncomment the "close" and "open" lines and comment "lseek" line, the output is as expected:

$ ./a.out
this is a test
multibyte string

write() does not have a buffer, and stderr is written-off when it is closed, so why the output is garbled if we do not close the file before reading it?


Solution

  • There is no check on return values from the functions. If it had been there you would have found the error.

    Anyway, the issue is:

      fd = fileno(stderr);     // getting the fd from current stderr
      fclose (stderr);         // closing stderr
      ...
      lseek (fd, 0, SEEK_SET); // seeking on fd which was already closed
    

    In the last call and subsequent calls fd is actually undefined (or rather it references closed file descriptor). Therefore any operation on fd will fail EBADF (not valid file descriptor).

    Obviously if you include the fd = open(...) again, fd will become valid and the code will work.