Unix low-level file operations

Contents

File access permissions
Unix API
creat
open
read
write
Example
File status
Use of these functions

File access permissions

Unix files have 10 bits set premissions
as in
      -rwxr-x---
(read, write, execute for owner; read, execute for group; none for others).

Modes can be added or subtracted using the user-level chmod command eg

chmod ug+rx file
chmod o-rwx file
Modes may also be set numerically using a 3-digit octal number. The first digit sets user permissions, the second sets group permissions and the last sets other permissions. Read has value 4, write has value 2, execute has value 1, and the bits are or-ed together to give a single octal digit. For example
chmod 751 file
gives r,w,x permission to user; r,x to group; x to other.

Unix API

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 unlink(char *path);
int rename(char *old, char *new);
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);
off_t lseek(int filedes, off_t offset, int whence);
int link(char *path1, char *path2);
int chmod(char *path,
	  mode_t mode);
int stat(char *path,
	  struct stat *buf);

creat

int creat(char *path,
          mode_t mode);
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''.

open

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.

read

int read(int fildes,
         char *buf,
         unsigned nbyte);
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 */

write

int write(int fildes,
          char *buf,
          unsigned nbyte);
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 <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#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) {
    cerr << "Usage: cp f1 f2\n";
    exit(1);
  }

  if ((src = open(argv[1], 
            O_RDONLY)) == -1) {
          cerr << "Cant open " << 
                  argv[1]) << endl;
    exit(2);
  }

  if ((dst = creat(argv[2], 
            MODE)) == -1) {
      cerr << "cant create " << 
              argv[2] << endl;
    exit(3);
  }

  while ((nread =
          read(src, buf, SIZE))
                    > 0) {
    if (write(dst, buf, nread)
                      == -1) {
         cerr << "cant write\n",
      exit(4);
    }
  }
  exit(0);
}


File status

The function stat() is used to get file status
#include <sys/types.h>
#include <sys/stat.h>

int stat(char *path, struct stat *buf)
where
struct stat {
  mode_t st_mode;  // permissions mode
  uid_t st_uid;    // user id
  gid_t st_gid;    // group id
  time_t st_atime; // time of last access
  time_t mtime;    // last modification time
  time_t st_ctime; // last file status change
  ...
A set of macros and bit constants can be used on st_mode:
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <iostream.h>

int main(int argc, char *argv[])
{
  struct stat buff;

  char filename[NAME_MAX + 1];

  cout << "Enter file name: ";
  cin >> filename;

  if (stat(filename, &buff) == -1) {
    perror(filename);
    exit(1);
  } else {
    cout << "File owner: " << buff.st_uid << endl;
  }
  exit(0);
}


Use of these functions

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 you need finer control.

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.

create, open

These two functions control what happens when a file has to be created. In addition
Home Program Development using Unix Home
Jan Newmarch (http://jan.newmarch.name) <jan@newmarch.name>

Copyright © Jan Newmarch
Last modified: Wed Nov 19 18:00:06 EST 1997