Igor Liferenko Igor Liferenko - 12 days ago 4x
C Question

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

In the following example we close default

and reopen it on the temporary file via
, using descriptor
, which was
'ed from the temporary file descriptor. Then we
directly to this descriptor
. We can safely do this, because the is the first write operation on file and thus it has the empty buffer. After this, we
to the new
. Then we close
(thus, its associated descriptor
is automatically closed). Original descriptor
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

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


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.