Wrap POSIX File Descriptors to C FILE Streams

This article explains how to bridge low-level POSIX I/O and standard C library buffered I/O by converting raw file descriptors into FILE* streams. In C, while POSIX operations like open(), pipe(), or socket() yield integer file descriptors, higher-level stream functions like fprintf() or fgets() require FILE pointers. The POSIX standard provides the dedicated function fdopen() to achieve this association without duplicating system resources.

The Primary Function: fdopen()

The standard function used to wrap an open POSIX file descriptor into a buffered FILE stream is fdopen(). It is declared in <stdio.h> as part of the POSIX standard specification:

FILE *fdopen(int fd, const char *mode);

The function takes two parameters:

  • fd: An existing, valid open file descriptor returned by system calls such as open(), socket(), pipe(), or accept().
  • mode: A string specifying the stream access mode (e.g., "r", "w", "a", "r+", "w+", "a+").

If successful, fdopen() returns a pointer to a valid FILE object. If it fails, it returns NULL and sets errno to indicate the error.

Mode Compatibility

The mode argument supplied to fdopen() must match the access permissions of the underlying file descriptor:

  • If the descriptor was opened read-only (O_RDONLY), the mode must begin with "r".
  • If the descriptor was opened write-only (O_WRONLY), the mode must begin with "w" or "a".
  • If the descriptor was opened read/write (O_RDWR), the mode can include "+".

Providing a mode incompatible with the file descriptor's open flags results in undefined behavior or failure.

Resource Ownership and Lifecycle

When a stream is created using fdopen(), the resulting FILE* object assumes ownership of the file descriptor:

  • Calling fclose(stream) will automatically close the underlying integer file descriptor fd. You should not manually invoke close(fd) after fclose().
  • If the program needs to retain direct descriptor access alongside stream access, avoid calling fclose(), or duplicate the descriptor using dup() prior to wrapping it.

Extracting Descriptors with fileno()

To perform the reverse operation—extracting the raw POSIX file descriptor from an active FILE* stream—use the POSIX function fileno():

int fileno(FILE *stream);

This returns the integer file descriptor associated with the given stream, enabling seamless transitions between buffered stream operations and low-level system calls when necessary.