Unix low-level file operations

The stdio library has a high-level interface to the file operations, with printf, putchar, etc. These are built from a lower-level interface. Typical calls from this are #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int creat(char *path, mode_t mode); int open(char *path, int oflag, ...); int close(int fildes); int read(int fildes, char *buf, unsigned nbyte); int write(int fildes, char *buf, unsigned nbyte); int chmod(char *path, mode_t mode); int stat(char *path, struct stat *buf); creat takes the pathname of a file and creates a new file, removing the contents if it already existed. The mode sets the access permissions. This is a bit-wise OR of the file permissions: S_IRUSR 0400 S_IWUSR 0200 S_IXUSR 0100 S_IRGRP 040 S_IWGRP 020 S_IXGRP 010 S_IROTH 04 S_IWOTH 02 S_IXOTH 01 This follows the rwx for user, group and other that is shown by ``ls -l''.

The open call opens the file in the mode given by oflag, and returns a file descriptor. This is an integer which the O/S uses to index into a per process file descriptor table, which is used to keep info about open files. Zero is always stdin, 1 is stdout, 2 is stderr.

oflag is a bit-wise OR of a number of constants

O_RDONLY O_WRONLY O_CREAT O_TRUNC When O_CREAT is one of these flags, an additional argument to open is the file permission mode.

The read call is a block read of a given number of bytes. This could be tuned to the physical characteristics of the device read from, for example. For example, it may be much faster to do a block read of 1024 bytes from a hard disk than to do 1024 reads of 1 byte from the disk.

The value returned is the actual number of bytes read. This may be less than the number asked for. For example

``read'' returns zero on EOF, and -1 on error. A while loop to read using this call should always test the return value for > 0: while ((nread = read(...)) > 0) ... if (nread == 0) /* EOF code */ else /* error code */

The write call is also a block write of a number of bytes. It returns -1 if a write error occurs (such as no space left).

Example

This is a simple version of cp, using the low-level I/O.

#include 
#include 
#include 
#include 

#define SIZE 1024
#define MODE (S_IRUSR | S_IWUSR | \
              S_IRGRP | S_IROTH)

int main(int argc,
         char *argv[])
{
  int src, dst;
  int in_count, out_count;
  char buf[SIZE];
  int nread;

  if (argc != 3) {
    fprintf(stderr,
      "Usage: cp f1 f2
");
    exit(1);
  }

  if ((src = open(argv[1], 
            O_RDONLY)) == -1) {
    fprintf(stderr,
          "Cant open %s
", 
          argv[1]);
    exit(2);
  }

  if ((dst = creat(argv[2], 
            MODE)) == -1) {
    fprintf(stderr, 
      "cant create %s
",
      argv[2]);
    exit(3);
  }

  while ((nread =
          read(src, buf, SIZE))
                    > 0) {
    if (write(dst, buf, nread)
                      == -1) {
      fprintf(stderr,
         "cant write
"),
      exit(4);
    }
  }
  exit(0);
}
#include 
#include 
#include 
#include 

#define SIZE 1024
#define MODE (S_IRUSR | S_IWUSR | \
              S_IRGRP | S_IROTH)

int main(int argc,
         char *argv[])
{
  int src, dst;
  int in_count, out_count;
  char buf[SIZE];
  int nread;

  if (argc != 3) {
    fprintf(stderr,
      "Usage: cp f1 f2
");
    exit(1);
  }

  if ((src = open(argv[1], 
            O_RDONLY)) == -1) {
    fprintf(stderr,
          "Cant open %s
", 
          argv[1]);
    exit(2);
  }

  if ((dst = creat(argv[2], 
            MODE)) == -1) {
    fprintf(stderr, 
      "cant create %s
",
      argv[2]);
    exit(3);
  }

  while ((nread =
          read(src, buf, SIZE))
                    > 0) {
    if (write(dst, buf, nread)
                      == -1) {
      fprintf(stderr,
         "cant write
"),
      exit(4);
    }
  }
  exit(0);
}
When should you use these low-level functions? Only when you have to. In general use the high-level functions. There will be many times when we can't.

Note that these functions are specific to the Unix API, and are not in Microsoft C, for example. So code written using these functions will only be portable across Unix, not to other Operating Systems.


Jan Newmarch (http://pandonia.canberra.edu.au) <jan@ise.canberra.edu.au>

Copyright © Jan Newmarch
Last modified: Wed Jun 18 20:53:05 1997