Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Unit 2

Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 103

UNIT 2 UNIX FILE APIs

General file API’s


Files in a UNIX and POSIX system may be any one of
the following types:
Regular file
Directory File
FIFO file
Block device file
character device file
Symbolic link file.
There are special API’s to create these types of files.
There is a set of Generic API’s that can be used to
manipulate and create more than one type of files.
What is the File Descriptor??
File descriptor is integer that uniquely identifies an open file
of the process.

File Descriptor table: File descriptor table is the collection of


integer array indices that are file descriptors in which
elements are pointers to file table entries. One unique file
descriptors table is provided in operating system for each
process.
File Table Entry: File table entries is a structure In-memory
surrogate for an open file, which is created when process
request to opens file and these entries maintains file position.
Standard File Descriptors: When any process starts, then
that process file descriptors table’s fd(file descriptor) 0, 1, 2
open automatically, (By default) each of these 3 fd
references file table entry for a file named /dev/tty

/dev/tty: In-memory surrogate for the terminal

Terminal: Combination keyboard/video screen


Read from stdin => read from fd 0 : Whenever we
write any character from keyboard, it read from stdin
through fd 0 and save to file named /dev/tty.

Write to stdout => write to fd 1 : Whenever we see


any output to the video screen, it’s from the file
named /dev/tty and written to stdout in screen
through fd 1.

Write to stderr => write to fd 2 : We see any error to


the video screen, it is also from that file write to
stderr in screen through fd 2.
I/O System calls
Basically there are total 5 types of I/O system calls:

1.Create: Used to Create a new empty file.

2.Syntax in C language:

int create(char *filename, mode_t mode)


Parameter :
•filename : name of the file which you want to create
•mode : indicates permissions of new file.
Returns :
return first unused file descriptor (generally 3 when first creat
use in process beacuse 0, 1, 2 fd are reserved)
return -1 when error.

How it work in OS
Create new empty file on disk
Create file table entry
Set first unused file descriptor to point to file table entry
Return file descriptor used, -1 upon failure
open: Used to Open the file for reading, writing or
both.Syntax in C language
#include<sys/types.h>
#includ<sys/stat.h>
#include <fcntl.h>
int open (const char* Path, int flags [, int
mode ]);
Parameters
•Path : path to file which you want to use
•use absolute path begin with “/”, when you are not
work in same directory of file.
•Use relative path which is only file name with
extension, when you are work in same directory of file.
•flags : How you like to use
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.
How it works in OS
Find existing file on disk
Create file table entry
Set first unused file descriptor to point to file table entry
Return file descriptor used, -1 upon failure
//C program to illustrate open system call
#include<stdio.h>
#include<fcntl.h>
#include<errno.h>
extern int errno;
int main()
{ Output:
// if file does not have in directory fd = 3
// then file foo.txt is created.
int fd = open("foo.txt", O_RDONLY | O_CREAT);

printf("fd = %d/n", fd);

if (fd ==-1)
{
// print which type of error have in a code
printf("Error Number % d\n", errno);

// print program detail "Success or failure"


perror("Program");
}
return 0;
}
close: Tells the operating system you are done with a file descriptor
and Close the file which pointed by fd.
Syntax in C language
#include <fcntl.h>
int close(int fd);
Parameter
•fd : file descriptor
Return
•0 on success.
•-1 on error.
How it works in the OS
•Destroy file table entry referenced by element fd of file descriptor
table
– As long as no other process is pointing to it!
•Set element fd of file descriptor table to NULL
//C program to illustrate close system Call
#include<stdio.h> Output:
#include <fcntl.h> opened the fd = 3 closed the fd.
int main()
{
int fd1 = open("foo.txt", O_RDONLY);
if (fd1 < 0)
{
perror("c1");
exit(1);
}
printf("opened the fd = % d\n", fd1);

// Using close system Call


if (close(fd1) < 0)
{
perror("c1");
exit(1);
}
printf("closed the fd.\n");
}
Output:
// C program to illustrate close system Call fd2 = 3
#include<stdio.h>
#include<fcntl.h>
int main()
{
// assume that foo.txt is already created
int fd1 = open("foo.txt", O_RDONLY, 0);
close(fd1);

// assume that baz.txt is already created


int fd2 = open("baz.txt", O_RDONLY, 0);
printf("fd2 = % d\n", fd2);
exit(0);
}
Here, In this code first open() returns 3 because when main
process created, then fd 0, 1, 2 are already taken
by stdin, stdout and stderr.

So first unused file descriptor is 3 in file descriptor table.


After that in close() system call, it frees it this 3 file
descriptor and then after set 3 file descriptor as null.

So when we called second open(), then first unused fd is


also 3. So, output of this program is 3.
read: From the file indicated by the file descriptor fd, the read()
function reads cnt bytes of input into the memory area indicated by
buf. A successful read() updates the access time for the file.
Syntax in C language
size_t read (int fd, void* buf, size_t cnt);
Parameters
•fd: file descripter
•buf: buffer to read data from
•cnt: length of buffer
Returns: How many bytes were actually read
•return Number of bytes read on success
•return 0 on reaching end of file
•return -1 on error
•return -1 on signal interrupt
Important points
buf needs to point to a valid memory location with length not
smaller than the specified size because of overflow.
fd should be a valid file descriptor returned from open() to
perform read operation because if fd is NULL then read
should generate error.
cnt is the requested number of bytes read, while the return
value is the actual number of bytes read. Also, some times
read system call should read less bytes than cnt.
If write() is interrupted by a signal, the effect is one of the following:

1. If write() has not written any data yet, it returns -1 and sets errno to
EINTR.

2. If write() has successfully written some data, it returns the number


of bytes it wrote before it was interrupted.
// C program to illustrate write system Call
#include<stdio.h>
#include <fcntl.h>
main()
{
int sz;
int fd = open("foo.txt", O_WRONLY | O_CREAT | O_TRUNC,
0644);
if (fd < 0)
{
perror("r1");
exit(1);
}
sz = write(fd, "hello geeks\n", strlen("hello geeks\n"));
printf("called write(% d, \"hello geeks\\n\", %d)."
" It returned %d\n", fd, strlen("hello geeks\n"), sz);
close(fd);
}
// C program to illustrate
// read system Call
#include<stdio.h>
#include <fcntl.h>
int main()
{
int fd, sz;
char *c = (char *) calloc(100, sizeof(char));

fd = open("foo.txt", O_RDONLY);
if (fd < 0)
{
perror("r1"); exit(1);
}
sz = read(fd, c, 10);
printf("called read(% d, c, 10). returned that”" %d bytes were read.\n", fd, sz);
c[sz] = '\0';
printf("Those bytes are as follows: % s\n", c);
}
Output:
called read(3, c, 10).

returned that 10 bytes were read. Those bytes are as


follows: 0 0 0 foo
.
These API’s are:
open
This is used to establish a connection between a process
and a file i.e. it is used to open an existing file for data
transfer function or else it may be also be used to create a
new file.
The returned value of the open system call is the file
descriptor (row number of the file table), which contains the
inode information.
The prototype of open function is
#include<sys/types.h>
#include<sys/fcntl.h>
int open(const char *pathname, int access mode,
mode_t permission);
If successful, open returns a nonnegative integer
representing the open file descriptor.
If unsuccessful, open returns –1.
The first argument is the name of the file to be created or
opened. This may be an absolute pathname or relative
pathname.
If the given pathname is symbolic link, the open function
will resolve the symbolic link reference to a non symbolic link
file to which it refers.
The second argument is access modes, which is an integer
value that specifies how actually the file should be accessed
by the calling process.
Generally the access modes are specified in <fcntl.h>.

Various access modes are:


O_RDONLY - open for reading file only.
O_WRONLY - open for writing file only.
O_RDWR - opens for reading and writing file.

There are other access modes, which are termed as access modifier
flags, and one or more of the following can be specified by bitwise-
ORing them with one of the above access mode flags to alter the access
mechanism of the file.
O_APPEND - Append data to the end of file.
O_CREAT - Create the file if it doesn’t exist.
O_EXCL - Generate an error if O_CREAT is also
specified and the file already exists.
O_TRUNC - If file exists discard the file content and
set the file size to zero bytes.
O_NONBLOCK - Specify subsequent read or write on
the file should be non-blocking.
O_NOCTTY - Specify not to use terminal device file
as the calling process control terminal.
To illustrate the use of the above flags, the following
example statement opens a file called /usr/divya/usp
for read and write in append mode:
int fd=open(“/usr/divya/usp”, O_RDWR | O_APPEND,0);
If the file is opened in read only, then no other
modifier flags can be used.
If a file is opened in write only or read write, then
we are allowed to use any modifier flags along with
them.
The third argument is used only when a new file is
being created. The symbolic names for file permission
are given in the table in the previous page.
creat
This system call is used to create new regular files.
The prototype of creat is
#include <sys/types.h>
#include<unistd.h>
int creat(const char *pathname, mode_t mode);

Returns: file descriptor opened for write-only if OK, -1 on


error.
The first argument pathname specifies name of the file to
be created.
The second argument mode_t, specifies permission of a
file to be accessed by owner group and others.
The creat function can be implemented using open
function as:

#define creat(path_name, mode)


open (pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
read
The read function fetches a fixed size of block of
data from a file referenced by a given file descriptor.
The prototype of read function is:

#include<sys/types.h>
#include<unistd.h>
size_t read(int fdesc, void *buf, size_t nbyte);

If successful, read returns the number of bytes


actually read.
If unsuccessful, read returns –1.
The first argument is an integer, fdesc that refers to an opened file.

The second argument, buf is the address of a buffer holding any data
read.

The third argument specifies how many bytes of data are to be read
from the file.

The size_t data type is defined in the <sys/types.h> header and


should be the same as unsigned int.
There are several cases in which the number of bytes actually read is
less than the amount requested:

o When reading from a regular file, if the end of file is reached before
the requested number of bytes has been read. For example, if 30 bytes
remain until the end of file and we try to read 100 bytes, read returns
30. The next time we call read, it will return 0 (end of file).

o When reading from a terminal device. Normally, up to one line is read


at a time.
o When reading from a network. Buffering within the network may
cause less than the requested amount to be returned.
o When reading from a pipe or FIFO. If the pipe contains fewer bytes
than requested, read will return only what is available.
write
The write system call is used to write data into a file.
The write function puts data to a file in the form of fixed block size
referred by a given file descriptor.
The prototype of write is
#include<sys/types.h>
#include<unistd.h>
ssize_t write(int fdesc, const void *buf, size_t size);
If successful, write returns the number of bytes actually written.
If unsuccessful, write returns –1.
The first argument, fdesc is an integer that refers to an opened file.
The second argument, buf is the address of a buffer that contains
data to be written.
The third argument, size specifies how many bytes of data are in the
buf argument.
The return value is usually equal to the number of bytes of data
successfully written to a file. (size value)
close
The close system call is used to terminate the connection to a file from
a process.
The prototype of the close is
#include<unistd.h>
int close(int fdesc);
If successful, close returns 0.
If unsuccessful, close returns –1.
The argument fdesc refers to an opened file.
Close function frees the unused file descriptors so that they can be
reused to reference other files. This is important because a process may
open up to OPEN_MAX files at any time and the close function allows a
process to reuse file descriptors to access more than OPEN_MAX files in
the course of its execution.
The close function de-allocates system resources like file table entry
and memory buffer allocated to hold the read/write.
Application Programming :
It deals with Abstraction .
Solve the problems according to interest of people . For instance - Healthcare, game
playing, Finance .
Concern with anything high level.
It is almost always device of platform independent .
It is often done in languages Java , C# , Perl , Ruby , Python , LISP , javascript .
If it is done properly then it can be very efficient . For instance - Amazon , MS
word , Excel .
About System Programming:
Deals with the concept extremely close to the hardware like registors and memory
locations .
Controls and manages the computer system .
Concerned with data transfer , Reading and writing to files , Compiling , Linking ,
Loading , Starting .
It is used in writing the Drivers , Operating System , Compilers , Debuggers .
It provides Services to computer hardware .
It is often done is Assembly language because Programmers have to manage memory
themselves . For instance System Software are - Compilers, Assemblers,
Linker,Loader,Divice drivers,Interpretor,System libraries .
An application program interface (API) is a set
of routines, protocols, and tools for building software
applications. Basically, an API specifies how software
components should interact. Additionally, APIs are used when
programming graphical user interface (GUI) components. A
good API makes it easier to develop a program by providing
all the building blocks. A programmer then puts the blocks
together.
Different Types of APIs
There are many different types of APIs for operating systems,
applications or websites. Windows, for example, has many API
sets that are used by system hardware and applications —
when you copy and paste text from one application to
another, it is the API that allows that to work.
Most operating environments, such as MS-Windows, provide
APIs, allowing programmers to write applications consistent
with the operating environment. Today, APIs are also specified
by websites. For example, Amazon or eBay APIs allow
developers to use the existing retail infrastructure to create
specialized web stores. Third-party software developers also use
Web APIs to create software solutions for end-users.
Popular API Examples
ProgrammableWeb, a site that tracks more than 15,500 APIs, lists Google Maps, Twitter,
YouTube, Flickr and Amazon Product Advertising as some of the the most popular APIs. The
following list contains several examples of popular APIs:
1. Google Maps API: Google Maps APIs lets developers embed Google Maps on webpages
using a JavaScript or Flash interface. The Google Maps API is designed to work on mobile
devices and desktop browsers.
2. YouTube APIs: YouTube API: Google's APIs lets developers integrate YouTube videos and
functionality into websites or applications. YouTube APIs include the YouTube Analytics API,
YouTube Data API, YouTube Live Streaming API, YouTube Player APIs and others.
3. Flickr API: The Flickr API is used by developers to access the Flick photo sharing community
data. The Flickr API consists of a set of callable methods, and some API endpoints.
4. Twitter APIs: Twitter offers two APIs. The REST API allows developers to access core Twitter
data and the Search API provides methods for developers to interact with Twitter Search and
trends data.
5. Amazon Product Advertising API: Amazon's Product Advertising API gives developers
access to Amazon's product selection and discovery functionality to advertise Amazon
products to monetize a website.
fcntl
The fcntl function helps a user to query or set access control
flags and the close-on-exec flag of any file descriptor.

The prototype of fcntl is


#include<fcntl.h>
int fcntl(int fdesc, int cmd, …);
The first argument is the file descriptor.
The second argument cmd specifies what operation has to
be performed.
The third argument is dependent on the actual cmd value.
The possible cmd values are defined in <fcntl.h> header.

cmd value Use


F_GETFL Returns the access control flags of a file descriptor fdesc.

F_SETFL Sets or clears access control flags that are specified in the third
argument to fcntl. The allowed access control flags are O_APPEND &
O_NONBLOCK.
F_GETFD Returns the close-on-exec flag of a file referenced by fdesc. If a
return value is zero, the flag is off; otherwise on.

F_SETFD Sets or clears the close-on-exec flag of a fdesc. The third argument to
fcntl is an integer value, which is 0 to clear the flag, or 1 to set the
flag.
F_DUPFD Duplicates file descriptor fdesc with another file descriptor. The third
argument to fcntl is an integer value which specifies that the
duplicated file descriptor must be greater than or equal to that
value. The return value of fcntl is the duplicated file descriptor
The fcntl function is useful in changing the access control flag of a file
descriptor.

For example: after a file is opened for blocking read-write access and
the process needs to change the access to non-blocking and in write-
append mode, it can call:
int cur_flags= fcntl(fdesc, F_GETFL);
int rc = fcntl(fdesc, F_SETFL, cur_flag | O_APPEND | O_NONBLOCK);
The following example reports the close-on-exec flag of fdesc, sets it to
on afterwards:
cout<<fdesc<<”close-on-exec”<<fcntl(fdesc, F_GETFD)<<endl;
(void) fcntl(fdesc, F_SETFD, 1); //turn on close-on-exec flag
The following statements change the standard input of a process to a file called FOO:

int fdesc=open(“F1”,O_RDONLY); //open F1 for read


close(0); //close standard input
if(fcntl(fdesc,F_DUPFD,0)==-1)
perror(“fcntl”); //stdin from F1 now
char buf[256];
int rc=read(0,buf,256); //read data from FOO

The dup and dup2 functions in UNIX perform the same file duplication function as fcntl.

They can be implemented using fcntl as:

#define dup(fdesc) fcntl(fdesc, F_DUPFD,0)


#define dup2(fdesc1,fd2)
close(fd2),
fcntl(fdesc, F_DUPFD, fd2)
lseek
The lseek function is also used to change the file offset to a different
value.
Thus lseek allows a process to perform random access of data on any
opened file.
The prototype of lseek is
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fdesc, off_t pos, int whence);
On success it returns new file offset, and –1 on error.
The first argument fdesc, is an integer file descriptor that refer to an
opened file.
The second argument pos, specifies a byte offset to be added to a
reference location in deriving the new file offset value.
The third argument whence, is the reference location.
Whence value Reference location
SEEK_CUR Current file pointer address
SEEK_SET The beginning of a file
SEEK_END The end of a file
They are defined in the <unistd.h> header.
If an lseek call will result in a new file offset that is beyond the
current end-of-file, two outcomes possible are:
o If a file is opened for read-only, lseek will fail.
o If a file is opened for write access, lseek will succeed.
o The data between the end-of-file and the new file offset address will
be initialised with NULL characters.
link
The link function creates a new link for the existing file.
The prototype of the link function is
#include <unistd.h>
int link(const char *cur_link, const char *new_link);
If successful, the link function returns 0.
If unsuccessful, link returns –1.
The first argument cur_link, is the pathname of existing file.
The second argument new_link is a new pathname to be
assigned to the same file.
If this call succeeds, the hard link count will be increased by
1.
The UNIX ln command is implemented using the link API.
/*test_ln.c*/
#include<iostream.h>
#include<stdio.h>
#include<unistd.h>
int main(int argc, char * argv)
{
if(argc!=3)
{
cerr<<”usage:”<<argv[0]<<”<src_file><dest_file>\n”;
return 0;
}
if(link(argv[1],argv[2])==-1)
{
perror(“link”);
return 1;
}
return 0; }
unlink
The unlink function deletes a link of an existing file.
This function decreases the hard link count attributes of the named
file, and removes the file name entry of the link from directory file.
A file is removed from the file system when its hard link count is zero
and no process has any file descriptor referencing that file.
The prototype of unlink is
#include <unistd.h>
int unlink(const char * cur_link);
If successful, the unlink function returns 0.
If unsuccessful, unlink returns –1.
The argument cur_link is a path name that references an existing
file.
ANSI C defines the rename function which does the similar unlink
operation.
The prototype of the rename function is:

#include<stdio.h>
int rename(const char * old_path_name,
const char * new_path_name);
The UNIX mv command can be implemented using the link and unlink APIs as shown:
#include <iostream.h>
#include <unistd.h>
#include<string.h>
int main ( int argc, char *argv[ ])
{
if (argc != 3 || strcmp(argv[1],argcv[2]))
cerr<<”usage:”<<argv[0]<<””<old_link><new_link>\n”;
else
if(link(argv[1],argv[2]) == 0)
return unlink(argv[1]);
return 1;
}
stat, fstat
The stat and fstat function retrieves the file attributes of a given file.
The only difference between stat and fstat is that the first argument of
a stat is a file pathname, where as the first argument of fstat is file
descriptor.
The prototypes of these functions are

#include<sys/stat.h>
#include<unistd.h>
int stat(const char *pathname, struct stat *statv);
int fstat(const int fdesc, struct stat *statv);
The second argument to stat and fstat is the address of a struct stat-
typed variable which is defined in the <sys/stat.h> header.
Its declaration is as follows:
Struct stat
{
dev_t st_dev; /* file system ID */
ino_t st_ino; /* file inode number */
mode_t st_mode; /* contains file type and permission */
nlink_t st_nlink; /* hard link count */
uid_t st_uid; /* file user ID */
gid_t st_gid; /* file group ID */
dev_t st_rdev; /*contains major and minor device#*/
off_t st_size; /* file size in bytes */
time_t st_atime; /* last access time */
time_t st_mtime; /* last modification time */
time_t st_ctime; /* last status change time */
};
The return value of both functions is
o 0 if they succeed
o -1 if they fail
o errno contains an error status code
The lstat function prototype is the same as that of stat:
int lstat(const char * path_name, struct stat* statv);
We can determine the file type with the macros as shown.

Macro Type of file


S_ISREG() regular file
S_ISDIR() directory file
S_ISCHR() character special file
S_ISBLK() block special file
S_ISFIFO() pipe or FIFO
S_ISLNK() symbolic link S_ISSOCK() socket
access
The access system call checks the existence and access
permission of user to a named file.
The prototype of access function is:

#include<unistd.h>
int access(const char *path_name, int flag);

On success access returns 0, on failure it returns –1.


The first argument is the pathname of a file.
The second argument flag, contains one or more of the following bit flag
.
Bit flag Uses
F_OK Checks whether a named file exist
R_OK Test for read permission
W_OK Test for write permission
X_OK Test for execute permission

The flag argument value to an access call is composed by bitwise-


ORing one or more of the above bit flags as shown:
int rc=access(“/usr/divya/usp.txt”, R_OK | W_OK);
example to check whether a file exists:

if(access(“/usr/divya/usp.txt”, F_OK)==-1)
printf(“file does not exists”);
else
printf(“file exists”);
chmod, fchmod
The chmod and fchmod functions change file access
permissions for owner, group & others as well as the set_UID,
set_GID and sticky flags.
A process must have the effective UID of either the super-
user/owner of the file.
The prototypes of these functions are

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int chmod(const char *pathname, mode_t flag);
int fchmod(int fdesc, mode_t flag);
The pathname argument of chmod is the path name of a file whereas
the fdesc argument of fchmod is the file descriptor of a file.

The chmod function operates on the specified file, whereas the


fchmod function operates on a file that has already been opened.

To change the permission bits of a file, the effective user ID of the


process must be equal to the owner ID of the file, or the process must
have super-user permissions.

The mode is specified as the bitwise OR of the constants shown below.


Mode Description
S_ISUID set-user-ID on execution
S_ISGID set-group-ID on execution
S_ISVTX saved-text (sticky bit)
S_IRWXU read, write, and execute by user (owner)
S_IRUSR read by user (owner)
S_IWUSR write by user (owner)
S_IXUSR execute by user (owner)
S_IRWXG read, write, and execute by group
S_IRGRP read by group
S_IWGRP write by group
S_IXGRP execute by group
S_IRWXO read, write, and execute by other
S_IROTH read by other
S_IWOTH write by other
S_IXOTH execute by other
chown, fchown, lchown

The chown functions changes the user ID and group ID of files.


The prototypes of these functions are

#include<unistd.h>
#include<sys/types.h>
int chown(const char *path_name, uid_t uid, gid_t gid);
int fchown(int fdesc, uid_t uid, gid_t gid);
int lchown(const char *path_name, uid_t uid, gid_t gid);

The path_name argument is the path name of a file.


The uid argument specifies the new user ID to be assigned to the file.
The gid argument specifies the new group ID to be assigned to the file.
/* Program to illustrate chown function */
#include<iostream.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<pwd.h>
int main(int argc, char *argv[ ])
{
if(argc>3)
{ cerr<<”usage:”<<argv[0]<<”<usr_name><file>....\n”; return 1; }
struct passwd * pwd = getpwuid(argv[1]) ;
uid_t UID = pwd ? pwd -> pw_uid : -1 ;
struct stat statv;
if (UID == (uid_t)-1) cerr <<“Invalid user name”;
else for (int i = 2; i < argc ; i++)
if (stat(argv[i], &statv)==0)
{
if (chown(argv[i], UID,statv.st_gid))
perror (“chown”); else perror (“stat”);
}
return 0; }
The above program takes at least two command line arguments:
o The first one is the user name to be assigned to files
o The second and any subsequent arguments are file path names.

The program first converts a given user name to a user ID via getpwuid
function.
If that succeeds, the program processes each named file as follows:

it calls stat to get the file group ID, then it calls chown to change the file
user ID.

If either the stat or chown fails, error is displayed.


utime Function
The utime function modifies the access time and the modification
time stamps of a file.
The prototype of utime function is
#include<sys/types.h>
#include<unistd.h>
#include<utime.h>
int utime(const char *path_name, struct utimbuf *times);
On success it returns 0, on failure it returns –1.
The path_name argument specifies the path name of a file.
The times argument specifies the new access time and modification
time for the file.
Access - the last time the file was read
Modify - the last time the file was modified (content has been modified)
Change - the last time meta data of the file was changed (e.g.
permissions)
The struct utimbuf is defined in the <utime.h> header as:
struct utimbuf
{
time_t actime; /* access time */
time_t modtime; /* modification time */
}
The time_t datatype is an unsigned long and its data is the number of
the seconds elapsed since the birthday of UNIX : 12 AM , Jan 1 of 1970.
If the times (variable) is specified as NULL, the function will set the
named file access and modification time to the current time.
If the times (variable) is an address of the variable of the type struct
utimbuf, the function will set the file access time and modification time
to the value specified by the variable.
File and Record Locking

Multiple processes performs read and write operation on


the same file concurrently.
This provides a means for data sharing among processes,
but it also renders difficulty for any process in determining
when the other process can override data in a file.
So, in order to overcome this drawback UNIX and POSIX
standard support file locking mechanism.
File locking is applicable for regular files.
Only a process can impose a write lock or read lock on
either a portion of a file or on the entire file.

The differences between the read lock and the write lock is
that when write lock is set, it prevents the other process from
setting any over-lapping read or write lock on the locked file.

Similarly when a read lock is set, it prevents other processes


from setting any overlapping write locks on the locked region.
The intension of the write lock is to prevent other processes
from both reading and writing the locked region while the
process that sets the lock is modifying the region, so write
lock is termed as “Exclusive lock”.

The use of read lock is to prevent other processes from


writing to the locked region while the process that sets the
lock is reading data from the region.

Other processes are allowed to lock and read data from


the locked regions. Hence a read lock is also called as
“shared lock “.
File lock may be mandatory if they are enforced by an
operating system kernel.
If a mandatory exclusive lock is set on a file, no process can
use the read or write system calls to access the data on the
locked region.
These mechanisms can be used to synchronize reading and
writing of shared files by multiple processes.
If a process locks up a file, other processes that attempt to
write to the locked regions are blocked until the former
process releases its lock.
Problem with mandatory lock is – if a runaway process sets a
mandatory exclusive lock on a file and never unlocks it, then, no other
process can access the locked region of the file until the runway
process is killed or the system has to be rebooted.

If locks are not mandatory, then it has to be advisory lock.

A kernel at the system call level does not enforce advisory locks.
This means that even though a lock may be set on a file, no other
processes can still use the read and write functions to access the file.
To make use of advisory locks, process that manipulate the
same file must co-operate such that they follow the given
below procedure for every read or write operation to the file.

1. Try to set a lock at the region to be accesses. If this fails, a


process can either wait for the lock request to become
successful.
2. After a lock is acquired successfully, read or write the
locked region.
3. Release the lock.
If a process sets a read lock on a file, for example from address 0 to 256,
then sets a write lock on the file from address 0 to 512, the process will
own only one write lock on the file from 0 to 512, the previous read lock
from 0 to 256 is now covered by the write lock and the process does not
own two locks on the region from 0 to 256. This process is called “Lock
Promotion”.

Furthermore, if a process now unblocks the file from 128 to 480, it will
own two write locks on the file: one from 0 to 127 and the other from
481 to 512. This process is called “Lock Splitting”.
UNIX systems provide fcntl function to support file locking.
By using fcntl it is possible to impose read or write locks on
either a region or an entire file.

The prototype of fcntl is


#include<fcntl.h>
int fcntl(int fdesc, int cmd_flag, ....);

The first argument specifies the file descriptor.


The second argument cmd_flag specifies what operation
has to be performed.
If fcntl is used for file locking then it can values as

F_SETLK sets a file lock, do not block if this cannot succeed


immediately.
F_SETLKW sets a file lock and blocks the process until the lock is
acquired.
F_GETLK queries as to which process locked a specified region of
file.

For file locking purpose, the third argument to fctnl is an address of a


struct flock type variable.

This variable specifies a region of a file where lock is to be set, unset


or queried.
struct flock
{
short l_type; /* what lock to be set or to unlock file */
short l_whence; /* Reference address for the next field */
off_t l_start ; /*offset from the l_whence reference addr*/
off_t l_len ; /*how many bytes in the locked region */
pid_t l_pid ; /*pid of a process which has locked the file */
};

The l_type field specifies the lock type to be set or unset.


The possible values, which are defined in the <fcntl.h> header and their
uses are
l_type value Use

F_RDLCK Set a read lock on a specified region


F_WRLCK Set a write lock on a specified region
F_UNLCK Unlock a specified region

The l_whence, l_start & l_len define a region of a file to be locked or


unlocked.
The possible values of l_whence and their uses are
l_whence value Use
SEEK_CUR The l_start value is added to current file pointer
address
SEEK_SET The l_start value is added to byte 0 of the file
SEEK_END The l_start value is added to the end of the file
A lock set by the fcntl API is an advisory lock but we can also use
fcntl for mandatory locking purpose with the following attributes set
before using fcntl

1. Turn on the set-GID flag of the file.


2. Turn off the group execute right permission of the file.
In the given example program we have performed a read lock on a
file “abc” from the 10th byte to 25th byte.
Example Program
#include <unistd.h>
#include<fcntl.h>
int main ( )
{
int fd;
struct flock lock;
fd=open(“abc”, O_RDONLY);
lock.l_type=F_RDLCK;
lock.l_whence=0;
lock.l_start=10;
lock.l_len=15;
fcntl(fd, F_SETLK, &lock);
}
Directory File API’s
A Directory file is a record-oriented file, where each record
stores a file name and the inode number of a file that resides
in that directory.
Directories are created with the mkdir API and deleted with
the rmdir API.
The prototype of mkdir is
#include<sys/stat.h>
#include<unistd.h>
int mkdir(const char *path_name, mode_t mode);
The first argument is the path name of a directory file to be
created.
The second argument mode, specifies the access permission
for the owner, groups and others to be assigned to the file.
This function creates a new empty directory.

The entries for “.” and “..” are automatically created. The
specified file access permission, mode, are modified by the
file mode creation mask of the process.

To allow a process to scan directories in a file system


independent manner, a directory record is defined as
struct dirent in the <dirent.h> header for UNIX.
Some of the functions that are defined for directory file operations in the above
header are
#include<sys/types.h>
#if defined (BSD)&&!_POSIX_SOURCE
#include<sys/dir.h>
typedef struct direct Dirent;
#else
#include<dirent.h>
typedef struct direct Dirent;
#endif
DIR *opendir(const char *path_name);
Dirent *readdir(DIR *dir_fdesc);
int closedir(DIR *dir_fdesc);
void rewinddir(DIR *dir_fdsec);
The uses of these functions are

Function Use
opendir Opens a directory file for read-only. Returns a file
handle dir * for future reference of the file.
readdir Reads a record from a directory file referenced by dir-
fdesc and returns that record information.
rewinddir Resets the file pointer to the beginning of the directory
file referenced by dir-fdesc. The next call to readdir will
read the first record from the file.
closedir closes a directory file referenced by dir-fdesc.
An empty directory is deleted with the rmdir API.
The prototype of rmdir is
#include<unistd.h>
int rmdir (const char * path_name);

If the link count of the directory becomes 0, with the call and no other
process has the directory open then the space occupied by the directory
is freed.
UNIX systems have defined additional functions for random access of
directory file records.
Function Use
telldir Returns the file pointer of a given dir_fdesc
seekdir Changes the file pointer of a given dir_fdesc to a
specified address
following list_dir.C program illustrates the uses of the mkdir, opendir,
readdir, closedir and rmdir APIs:

#include<iostream.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>
#if defined(BSD) && !_POSIX_SOURCE
#include<sys/dir.h>
typedef struct dirent Dirent;
#else
#include<dirent.h>
typedef struct dirent Dirent;
#endif
int main(int agc, char* argv[])
{
Dirent* dp;
DIR* dir_fdesc;
while(--argc>0)
{
if(!(dir_fdesc=opendir(*++argv)))
{
if(mkdir(*argv,S_IRWXU | S_IRWXG | S_IRWXO)==-1)
perror("opendir"); continue;
}
for(int i=0;i<2;i++)
for(int cnt=0;
dp=readdir(dir_fdesc);)
{
if(i) cout<<dp->d_name<<endl;
if(strcmp(dp->d_name,".") && strcmp(dp->d_name,".."))
cnt++;
}
if(!cnt)
{ rmdir(*argv); break; }
rewinddir(dir_fdesc);
} closedir(dir_fdesc); } }
Device file APIs
Device files are used to interface physical device with application
programs.
A process with superuser privileges to create a device file must call the
mknod API.
The user ID and group ID attributes of a device file are assigned in the
same manner as for regular files.
When a process reads or writes to a device file, the kernel uses the
major and minor device numbers of a file to select a device driver
function to carry out the actual data transfer.
Device file support is implementation dependent. UNIX System
defines the mknod API to create device files.
The prototype of mknod is
#include<sys/stat.h>
#include<unistd.h>
int mknod(const char* path_name, mode_t mode, int device_id);
The first argument pathname is the pathname of a device file to be
created.
The second argument mode specifies the access permission, for the
owner, group and others, also S_IFCHR or S_IBLK flag to be assigned to
the file.
The third argument device_id contains the major and minor device
number.
Example
mknod(“SCSI5”,S_IFBLK | S_IRWXU | S_IRWXG | S_IRWXO,(15<<8) |
3);
The above function creates a block device file “divya”, to which all the
three i.e. read, write and execute permission is granted for user, group
and others with major number as 8 and minor number 3.
On success mknod API returns 0 , else it returns -1
The following test_mknod.C program illustrates the use of the mknod, open, read, write and
close APIs on a block device file.
#include<iostream.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(int argc, char* argv[])
{ if(argc!=4)
{ cout<<"usage:"<<argv[0]<<"<file><major_no><minor_no>"; return 0; }
int major=atoi(argv[2],minor=atoi(argv[3]);
(void) mknod(argv[1], S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, (major<<8) | minor);
int rc=1,fd=open(argv[1],O_RDW | O_NONBLOCK | O_NOCTTY);
char buf[256];
while(rc && fd!=-1)
if((rc=read(fd,buf,sizeof(buf)))<0) perror("read");
else if(rc) cout<<buf<<endl;
close(fd); }
FIFO file API’s
FIFO files are sometimes called named pipes.
Pipes can be used only between related processes when a common
ancestor has created the pipe.
Creating a FIFO is similar to creating a file.
Indeed the pathname for a FIFO exists in the file system.
The prototype of mkfifo is
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int mkfifo(const char *path_name, mode_t mode);
The first argument pathname is the pathname(filename) of a FIFO file
to be created.
The second argument mode specifies the access permission for user,
group and others and as well as the S_IFIFO flag to indicate that it is a
FIFO file.
On success it returns 0 and on failure it returns –1.
Example
mkfifo(“FIFO5”,S_IFIFO | S_IRWXU | S_IRGRP | S_ROTH);
The above statement creates a FIFO file “divya” with read-write-execute permission
for user and only read permission for group and others.
Once we have created a FIFO using mkfifo, we open it using open.
Indeed, the normal file I/O functions (read, write, unlink etc) all work with FIFOs.
When a process opens a FIFO file for reading, the kernel will block the process until
there is another process that opens the same file for writing.
Similarly whenever a process opens a FIFO file write, the kernel will block the
process until another process opens the same FIFO for reading.
This provides a means for synchronization in order to undergo inter-process
communication.
If a particular process tries to write something to a FIFO file that is full, then that
process will be blocked until another process has read data from the FIFO to make
space for the process to write.
Similarly, if a process attempts to read data from an empty FIFO, the
process will be blocked until another process writes data to the FIFO.

From any of the above condition if the process doesn’t want to get
blocked then we should specify O_NONBLOCK in the open call to the
FIFO file.
If the data is not ready for read/write then open returns –1 instead
of process getting blocked.
If a process writes to a FIFO file that has no other process attached
to it for read, the kernel will send SIGPIPE signal to the process to
notify that it is an illegal operation.
Another method to create FIFO files (not exactly) for inter-process
communication is to use the pipe system call.
The prototype of pipe is
#include <unistd.h>
int pipe(int fds[2]);
Returns 0 on success and –1 on failure.
If the pipe call executes successfully, the process can read from fd[0]
and write to fd[1]. A single process with a pipe is not very useful. Usually
a parent process uses pipes to communicate with its children.
The following test_fifo.C example illustrates the use of mkfifo, open,
read, write and close APIs for a FIFO file:
#include<iostream.h>
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
int main(int argc,char* argv[])
{
if(argc!=2 && argc!=3)
{
cout<<"usage:"<<argv[0]<<"<file> [<arg>]";
return 0;
}
int fd; char buf[256];
(void) mkfifo(argv[1], S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO );
if(argc==2)
{
fd=open(argv[1],O_RDONLY | O_NONBLOCK);
while(read(fd,buf,sizeof(buf))==-1 && errno==EAGAIN) sleep(1);
while(read(fd,buf,sizeof(buf))>0)
cout<<buf<<endl;
}
Else
{
fd=open(argv[1],O_WRONLY);
write(fd,argv[2],strlen(argv[2]));
}
close(fd);
}
Symbolic Link File APIs
A symbolic link is an indirect pointer to a file, unlike the hard links
which pointed directly to the inode of the file.
Symbolic links are developed to get around the limitations of hard
links:
o Symbolic links can link files across file systems.
o Symbolic links can link directory files
o Symbolic links always reference the latest version of the files to which
they link
o There are no file system limitations on a symbolic link and what it
points to and anyone can create a symbolic link to a directory.
o Symbolic links are typically used to move a file or an entire directory
hierarchy to some other location on a system.
o A symbolic link is created with the symlink.
o The prototype is

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
int symlink(const char *org_link, const char *sym_link);
int readlink(const char* sym_link, char* buf,int size);
int lstat(const char * sym_link, struct stat* statv);

The org_link and sym_link arguments to a sym_link call specify the


original file path name and the symbolic link path name to be created.
/* Program to illustrate symlink function */
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
int main(int argc, char *argv[])
{
char *buf [256],
tname [256];
if (argc ==4)
return symlink(argv[2], argv[3]); /* create a symbolic link */
Else
return link(argv[1], argv[2]); /* creates a hard link */
}
General file class
In C++, fstream class is used to define objects, which represent files in
a file system.
The fstream class contains member functions like open, close, read,
write, seekg and tellg.
The fstream class does not provide member functions like stat chown,
chmod, utime and link functions on its object.
To overcome the fstream class deficiency a new filebase class is
defined, which incorporates the fstream class properties and additional
function to allow users to get or change object file attributes and to
create hard links.
Refer /* filebase.h */ from first text book terrence chan
/* Program to illustrate the use of the file base class */ refer from first
text book terrence chan
Regfile Class for Regular Files
The regfile class is defined as a subclass of filebase.
The filebase class encapsulates most of the properties and functions
needed to represent regular file objects in UNIX systems except file
locking.
Thus, objects of regfile class can do all regular file operations
permitted in filebase class include file locking functions.
regfile::lock set read lock or write lock.
regfile::lockw wrapper over the regfile::lock and it locks files in
blocking mode.
regfile::getlock queries lock information for a region of a file
associated with a regfile object .
regfile::unlock unlocks a region of a file associated with a regfile
object.

/* Regfile.h*/ */
/* Program to illustrate the use of regfile class */
refer from first text book terrence chan
dirfile class for Directory file
The dirfile class is defined to encapsulate all UNIX directory file
functions. The dirfile class defines the create, open, read, tellg, seekg,
close and remove functions that use the UNIX directory file specific API’s.
The dirfile class definition is: /* dirfile.h*/

/* Program to illustrate the use of dirfile class */ refer from first text
book terrence chan
FIFO file Class
A FIFO file object differs from a filebase object in that a FIFO file is
created, and the tellg and seekg functions are invalid for FIFO file
objects. The following pipefile class encapsulates all the FIFO file type
properties: /* pipefile.h */

Device File Class


A device file object has most of the properties of a regular file object
except in the way that the device file object is created. Also the tellg,
seekg, lock, lockw, unlock and getlock functions are invalid for any
character-based device file objects.
/*devfile.h*/ refer from first text book terrence chan
/* Program to illustrate the use of devfile class */

Symbolic link file class


A symbolic link file object differs from a filebase object in the way it is
created. Also, a new member function called ref_path is provided to
depict the path name of a file to which the symbolic link object
refers. /* symfile.h*/
/* Program to illustrate the use of symfile class */ refer from first
text book terrence chan
File listing Program
A C program re-implements the UNIX ls command to list file attributes
of all path name arguments specified for the program using file base
class and its subclasses. Further if an argument is a directory the
program will list the file attributes of all files in that directory and any
subdirectories. If a file is a symbolic link, the program will echo the
pathname to which the link refers. Thus, the program behaves like the
UNIX ls –lR command.
Summary
The inheritance hierarchy of all the file classes defined in the
chapter is:

You might also like