Introduction to I/O System call, file descriptor and modes in C


In this article at OpenGenus, we shall learn the basics of I/O syscalls, file descriptors and modes.

FILE DESCRIPTORS

A file descriptor uniquely identifies an open file of the process. Some file descriptor values are reserved for system uses.

fd purpose
0 Standard input
1 Standard output
2 Standard error
>3 User created file descriptors using the syscalls mentioned below

PERMISSIONS/MODE

Every file in unix has permissions, that is, which all users can read(r)/write(w)/execute(x) the file. These are often represented using octal numbers: In 0abc, a corresponds to the permissions of the owner, b corresponds to the permissions of the group, c corresponds to the permissions of others.

Number Ref
0 ---
1 --x
2 -w-
3 -wx
4 r--
5 r-x
6 rw-
7 rwx

For example, if the permissions of a file is 0600, then only the owner can read and write, but not execute the file. Other users cannot read, write or execute the file.

SYSCALLS

System call are those that the services of the operating system to the user programs via Application Program Interface(API). The different I/O syscalls used here, include:

1. creat()

Usage

int creat(char *filename, mode_t mode)

Purpose

Creates an empty file at path filename with permissions mode and returns the file descriptor that points to that file in the file table entry. In case of any error, it returns -1.

Sample usage

int new_file = creat("new_file.txt",0600);

Here, a new file with file name "new_file.txt" is created with the permissions 0600. The variable new_file contains the file descriptor

2. open()

Usage

int open (const char* Path, int flags [, int mode ]);

Purpose

Open the file at path Path with the flags flags and permissions mode and returns the file descriptor that points to that file in the file table entry. In case of any error, it returns -1.

The different flags available include

Flag Purpose
O_RDONLY read only
O_WRONLY write only
O_RDWR read and write
O_CREAT create file if it doesn’t exist
O_EXCL prevent creation if it already exists
__O_LARGEFILE used for opening large files(>1GB)
O_TRUNC Truncate the contents of the file, if the file already exists

Sample usage

1) Open a file for writing

int dest_file = open("./output.txt", O_CREAT | O_RDWR | __O_LARGEFILE | O_TRUNC, 0600);

2) Open a file for reading

int source_file = open("./input.txt", O_RDONLY | __O_LARGEFILE);

3. close()

Usage

int close  (int filedes);

Purpose

Close the file. It returns 0 once the file is closed successfully and -1 in case of any error.

Sample usage

close(dest_file);

4. read()

Usage

ssize_t read(int fd, void *buf, size_t count);

Purpose

Read data upto count bytes from a file whose file descriptor is fd and store the results in buf pointer. It returns the number of bytes read. In case of any error, it returns -1.

Sample usage

char *c = (char *)malloc(1000);
int chk = read(source_file, c, 1000);

5. write()

Usage

ssize_t write(int fd, const void *buf, size_t count);

Purpose

Read data upto count bytes from the memory location pointed by buf to a file whose file descriptor is fd. It returns the number of bytes read. In case of any error, it returns -1.

Sample usage

char *c = (char *)malloc(1000);
int chk=write(dest_file, c, 1000);

6. lseek()

Usage

off_t lseek(int fd, off_t offset, int whence);

Purpose

repositions the file offset of the file descriptor fd to offset bytes with respect to whence directive:

  • SEEK_SET: set file offtset to offset bytes from the beginning of the file
  • SEEK_CUR: set file offtset to offset bytes + current location
  • SEEK_END: set file offtset to offset bytes + size of file

It returns the new location of the file offset of the file descriptor

Sample usage

1) Get the current location of the file descriptor

int current_location = lseek(source_file, 0, SEEK_CUR);

2) Move the file descriptor to the start of the file

lseek(source_file, 0, SEEK_SET);

3) Move the file descriptor to the end of the file

lseek(source_file, -1, SEEK_END);

4) Get the length of the file

off_t fileLength = lseek(source_file, 0, SEEK_END);

7. stat()

Usage

int stat(const char *path, struct stat *buf);

Purpose

Store properties of a file at path in buf. It returns 0 in case of success and -1 in case of failure

Sample usage

struct stat buffer;
stat(output_file_path, &buffer);

8. fflush()

Usage

fflush(FILE *ostream);

Purpose

Clear (or flush) the output buffer and move the buffered data to the output stream.

Sample usage

fflush(stdout);

EXAMPLE

These I/O system calls can be used to

  • reverse the contents of a file
  • check if a file is a reverse of the other
  • display the permissions of a file
  • make a shell (similar to bash) using only sys calls

With this article at OpenGenus, you must have got the complete idea of different I/O syscalls, file descriptors and modes in C programming language. Enjoy.