Igor Liferenko Igor Liferenko - 1 month ago 12
C Question

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?

Answer

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.

Comments