System Calls in Unix/Linux: File Operation Example Programs

Syscalls are suitable for file I/O operations on large blocks of data, i.e. operations that do not need lines, chars or structured records, etc. The following sections show some example programs that use syscalls for file operations.

1. Display File Contents

Example 8.2: Display File Contents. This program behaves somewhat like the Linux cat command, which displays the contents of a file to stdout. If no filename is specified, it gets inputs from the default stdin.

/********* c8.2 file ********/

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#define BLKSIZE 4096

int main(int argc, char *argv[ ])

{

int fd, i, m, n;

char buf[BLKSIZE], dummy;

fd = 0; // default to stdin

if (argc > 1){

fd = open(argv[1], O_RDONLY);

if (fd < 0) exit(1);

}

while (n = read(fd, buf, BLKSIZE)){

m = write(1, buf, n);

}

}

When running the program with no file name, it collects inputs from fd=0, which is the standard input stream stdin. To terminate the program, enter Control-D (0x04), which is the default EOF on stdin. When running the program with a filename, it first opens the file for read. Then it uses a while loop to read and display the file contents until read() returns 0, indicating the file has no more data. In each iteration, it reads up to 4KB chars into a buf[ ] and writes the n chars to the file descriptor 1. In Unix/ Linux files, lines are terminated by the LF=\n char. If a file descriptor refers to a terminal special file, the pseudo terminal emulation program automatically adds a \r for each \n char in order to produce the right visual effect. If the file descriptor refers to an ordinary file, no extra \r chars will be added to the outputs.

2. Copy Files

Example 8.3: Copy Files. This example program behaves like the Linux cp src dest command, which copies a src file to a dest file.

/******** c8.3.c file *******/

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#define BLKSIZE 4096

int main(int argc, char *argv[ ])

{

int fd, gd, n, total=0;

char buf[BLKSIZE];

if (argc < 3) exit(1); // usage a.out src dest

if ((fd = (open(argv[1], O_RDONLY)) < 0)

exit(2);

if ((gd = open(argv[2],O_WRONLY|O_CREAT)) < 0)

exit(3);

while (n = read(fd, buf, BLKSIZE)){

write(gd, buf, n);

total += n;

}

printf(“total bytes copied=%d\n”, total);

close(fd);

close(gd);

}

Exercise 1. The Example program c8.3 has a serious flaw in that we should never copy a file to itself. Besides wasting time, the reader may also find out the reason by looking at the program code. If the src and dest files are the same file, rather than copying, it would just truncate the file size to 0. Modify the program to ensure that src and dest are not the same file. Note that different filenames may refer to the same file due to hard links.

HINT: stat both pathnames and compare their (st_dev, st_ino).

3. Selective File Copy

Example 8.4: Selective File Copy: This example program is a refinement of the simple file copying program in Example 8.3. It behaves like the Linux dd command, which copies selected parts of files. The reader may consult the man pages of dd to find out its full capabilities. Since our objective here is to show how to use syscalls for file operations, the example program is a simplified version of dd. The program runs as follows.

a.out if=in of=out bs=size count=k [skip=m] [seek=n] [conv=notrunc]

where [ ] denote optional entries. If these entries are specified, skip=m means skip m blocks of the input file, seek=n means step forward n blocks of the output file before writing and conv=notrunc means do not truncate the output file if it already exits. The default values of skip and seek are 0, i.e. no skip or seek. For simplicity, we assume the command line parameters contain no white spaces, which simplifies command-line parsing. The only new feature of this program is to parse the command line parameters to set file names, counting variables and flags. For example, if conv=notrunc is specified, the target file must be opened without truncating the file. Second, if skip and seek are specified, opened files must use lseek() to set the RW offsets accordingly.

/************ c8.4.c file **********/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

char in[128], out[128], buf[4096];

int bsize, count, skip, seek, trnc;

int records, bytes;

// parse command line parameters and set variables int parse(char *s)

{

char cmd[128], parm[128];

char *p = index(s, ‘=’);

s[p-s] = 0; // tokenize cmd=parm by ‘=’

strcpy(cmd, s);

strcpy(parm, p+1);

if (!strcmp(cmd, “if”))

strcpy(in, parm);

if (!strcmp(cmd, “of”))

strcpy(out, parm);

if (!strcmp(cmd, “bs”)

bsize = atoi(parm);

if (!strcmp(cmd, “count”))

count = atoi(parm);

if (!strcmp(cmd, “skip”))

skip = atoi(parm);

if (!strcmp(cmd, “seek”))

seek = atoi(parm);

if (!strcmp(cmd, “conv”)){

if (!strcmp(parm, “notrunc”)) trnc = 0;

}

}

int main(int argc, char *argv[])

{

int fd, gd, n, i;

if (argc < 3){

printf(“Usage: a.out if of ….\n”);

exit(1);

}

in[0] = out[0] = 0;              // null file names

bsize = count = skip = seek = 0; // all 0 to start

trnc =1;                         // default = trunc

for (i=1; i<argc; i++){

parse(argv[i]);

}

// error checkings

if (in[0]==0)  || out[0]==0{

printf(“need in/out files\n”); exit(2);

}

if (bsize==0 || count==0){

printf(“need bsize and count\n”); exit(3);

}

// ADD: exit if in and out are the same file

if ((fd = open(in, O_RDONLY)) < 0){

printf(“open %s error\n”, in); exit(4);

}

if (skip) lseek(fd, skip*bsize, SEEK SET);

if (trnc) // truncate out file

gd = open(out, O_WRONLY|O_CREAT|O_TRUNC);

else

gd = open(out, O_WRONLY|O_CREAT); // no truncate

if (gd < 0){

printf(“open %s error\n”, out); exit(5);

}

if (seek) lseek(gd, seek*bsize, SEEK SET);

records = bytes = 0;

while (n = read(fd, buf, bsize)){

write(gd, buf, n);

records++; bytes += n;

count–;              // dec count by 1

if (count==0) break;

}

printf(“records=%d bytes=%d copied\n”, records, bytes);

}

Source: Wang K.C. (2018), Systems Programming in Unix/Linux, Springer; 1st ed. 2018 edition.

Leave a Reply

Your email address will not be published. Required fields are marked *