- 6 Files

System Calls

#include #include #include #include

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

flags O_RDONLY (read only) O_WRONLY (write only) O_RDWR (read-write) O_CREAT (create) O_EXCL (exclusive) O_TRUNC (truncate)

return values -1  open( ) failure n >= 0  o 0  stdin o 1  stdout o 2  stderr o n > 2  smallest non-negative file descriptor not already being used by the process making the call

mode only used with the O_CREAT flag filedes = open(“/tmp/newfile”, O_WRONLY | O_CREAT, 0644);

1. example – open( )

char *workfile = “junk”;

if (( filedes = open(workfile, O_RDWR )) == -1) { printf(“%s failed to open”, workfile); exit(1); }

// remainder of program

close(filedes);

POSIX Specification : Number of Open Files == 20 per process 2. example – creat( )

#include #include #include

int creat(const char *pathname, mode_t mode); always truncates an existing file before returning the file descriptor always opens a file for writing only

3. example – close ( ) #include

int close( int filedes); returns -1  error returns 0  successful

4. example – read ( ) #include

ssize_t read(int filedes, void *buffer, size_t n);

int fd; const int SOMEVALUE = 256; ssize_t nread; char buffer[SOMEVALUE];

nread = read(fd, buffer, SOMEVALUE);

maximum number of characters to be read

nread >= 0  number of characters ACTUALLY read nread == -1  read failure

number of characters requested > number of characters remaining in file  system transfers the characters remaining in the file & sets nread to the number of characters read

testing for end of file within a program  testing nread == zero

For best performance make sure that BUFSIZE #define BUFSIZE 512 is a multiple of the disk blocking factor long total = 0; If (( filedes = open(“anotherfile”, O_RDONLY)) == -1) {printf(“ … “); exit(1);} while (( nread = read(filesdes, buffer, BUFSIZE)) > 0) total += nread; Efficiency printf(“Total Read: %ld\n”, total); Define the BUFSIZE to be a multiple exit(0); of the disk block size of the system; see /usr/include/stdio.h

File Pointer for each file descriptor, there exists an unique read-write file pointer records the next byte to be read/written via that file descriptor sequential read-write operations  file pointer position maintained by the random access read-write operations  file pointer position is explicitly changed by the lseek( )

off_t lseek (int filedes, off_t offset, int start_flag);

file descriptor new position of the specifies the starting point from which to read-write, i.e., offset, measure the offset file pointer; it is the number of bytes to be values added to the starting SEEK_SET : beginning of the file position of the file SEEK_CUR : current position of the pointer SEEK_END : end of the file off_t newpos; casting … new position is 16 bytes before the end of the file

… newpos = lseek(fd, (off_t)-16, SEEK_END); new position is 20 bytes after newpos = lseek(fd, (off_t)20, SEEK_BEG); the beginning of the file newpos = lseek(fd, (off_t)+5, SEEK_CUR); new position is 5 bytes after the last position new position of the read-write offset pointer

attempting to move the offset pointer before the start of the file, i.e., newpos = lseek(fd, (off_t)-1, SEEK_BEG); results in an error

it is possible to move to a position beyong the current end of the file, i.e., newpos = lseek(fd, (off_t)100, SEEK_END); the file may have no data in the gap, it will appear to be filled with the ASCII null character filedes = open(, O_RDWR); open file for read-write; lseek(filedes, (off_t)0, SEEK_END); set offset pointer to end of the file; write(filedes, outbuf, OBSIZE); write a block of data to the end of the file filedes = open(filename, O_WRONLY | O_APPEND); (write only, append to end of file) write(filedes, appbuf, BUFFSIZE); subsequent writes will continue to write to the current end of the file determine the filesize = lseek(filedes, (off_t)0, SEEK_END); removing regular files int unlink(const char *pathname); int remove(const char *pathname); returns 0  success removing directories -1  failure int rmdir(const char *pathname); int remove(const char *pathname);

control of open files very irregular function, i.e., a “strange beast”, that performs #include a mixed bag of diverse tasks in Unix, thus the ellipsis “ … “ #include specifying the third parameter in the definition; the third #include parameter depends on the value of the second parameter

Int fcntl(int filedes, int cmd, … );

cmd values

F_GETFL : return the current file status flags

F_SETFL : reset the file status flags

only certain flags can be reset by this command int filestatus(int filedes) { int arg1; if(( arg1 = fcntl(filedes, F_GETFL)) == -1) { printf (“filestatus failed\n”); return (-1); }

printf (“file descriptor %d: “, filedes);

switch ( arg1 & O_ACCMODE) O_ACCMODE is a mask defined in { the file status bits in arg1 are compared to case O_WRONLY: those in O_ACCMODE by the bit-wise operator printf(“write-only”); AND, i.e., “&” break: case O_RDWR: : return the current file status flags printf(“read-write”); F_SETFL : reset the file status flags break: case O_RDONLY: printf(“read-only”); break: default: printf(“mode does not exist”); } if (arg1 & O_APPEND) printf(“ – append flag set”); printf (“\n”); return (0); } If ( fcntl( filedes, F_SETFL, O_APPEND) == -1) printf(“fcntl edrror \n”); // all future writes will append to the end of the file

Standard I/O Library

stdin 0 stdout 1 stderr 2

#include #include

main( ) { FILE *stream; if ( stream = fopen(“junk”, “r”)) == NULL) { printf(“file junk could not be opened\n”); exit(1); } } FILE Structure Open File Table Buffer stream

fd

int getc(FILE *istream); // read a character from istream int putc(int c, FILE *ostream); // write a character to ostream

while( ( c = getc(istream)) != EOF) putc(c, ostream); EOF == -1

The first call to getc( ) or to putc( ) results in the construction of a buffer of BUFSIZE which is processes by either a read( ) or a write( ) system call; on input, the buffer is filled by the first call to getc( ) and the following BUFSIZE -1 calls obtains data from the buffer; the next call to getc( ) refills the buffer; output works in reverse.

Error Numbers

errno : error variable

#include < stdio.h> #include < fcntl.h> #include < errno.h> main ( ) errno is not reset – only use errno immediately after { a system call has failed int fd; if ( ( fd = open(“acct_rec”, O_RDONLY) ) == -1) fprintf(sterr, “error, acct_rec not opened %d\n”, errno); perror(“error, acct_rec not opened”); }

include only one of these statements Executable File Permissions

set user-id on execution 000 100 000 000 000 o let a program be created and stored in a file with this bit set o the process which executes the program assumes the effective user-id of the files’ owner (rather than that of the process which is executing the program)

set group-id on execution 000 010 000 000 000 o let a program be created and stored in a file with this bit set o the process which executes the program assumes the effective group-id of the files’ owner

save-text-image (sticky bit) 000 001 000 000 000 o only defined for directories – discussed later

Change Ownership

int ( const char *pathname, uid_t owner_id, gid_t group_id);

new owner new group

Hard Links ( hard links are only used within a particular filesystem)

link count : number of links associated with a particular file

int link( const char *original_path, const char *new_path);

original file name new path & file name

not possible to create a link to a directory cannot be used to link across filesystems o each filesystem has a unique set of o hardlinks use the numbers

Rename int rename( const char *oldname, const char *newpathname); // rename files and directories

Symbolic Links is a file that contains the pathname to the linked file symbolic link can be thought of as a pointer to the linked file int symlink(const char *realname, const char *symname);

file symname is created and points to the file realname

int readlink( const char *sympath, char *buffer, size_t bufsize); opens file sympath, reads bufsize characters into buffer and finally closes the sympath file File Status

#include #include

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

int fstat( int filedes, struct stat buf); // used with open files only

● ● ●

struct stat s; int filedes, retval;

filedes = open(“/tmp/junk”, O_RDWR); retval = stat(“/tmp/junk”, &s); // OR retval = fstat(filedes, &s);

includes the definition of struct stat

the types used in struct stat are defined in

struct stat includes

dev_t st_dev; // logical device

ino_t st_ino; // inode number of the file

mode_t st_mode; // lower 12 bits records the file permissions

nlink_t st_nlink; // number of non-symbolic links

uid_t st_uid;

gid_t st_gid;

dev_t st_rdev; // used if the file entry is used to describe a device

off_t st_size; // logical size of the file in bytes

time_t st_atime; // time the file was last read

time_t st_mtime; // time the file was last modified

time_t st_ctime; // last time any data item in struct stat was altered

long st_blksize; // specific block size for this particular file

long st_blocks; // number of physical file system blocks

allocated to this particular file

● ● ●

Unix File Structure directories regular files o text files o binary files o script files o program files special files – peripheral devices o the call to a special file activates the device driver code within the kernel that is responsible for controlling the particular device

directory entry (file_name/directory_name, inode number) inodes are used to access files/directories defined within a particular filesystem

inode number used by the O/S to locate a disk-based inode structure which contains the administrative information for the desired file, i.e., size, owners user-id, group-id, permissions, date last modified and disk addresses of the blocks on disk that hold the file data

Directory Operations

#include includes a definition for DIR which represents a directory stream #include

DIR *opendir(const char *dirname); if dirname is successfully opened, returns a pointer to a DIR type, and the offset pointer is positioned to the first entry of the directory; if the call to opendir( ) fails, it returns -1

int closedir(DIR *dirptr); valid directory stream pointer, e.g., return value from the opendir( ) system call struct dirent *readdir(DIR *dirptr);

directory pointer automatically increments after every successful read operation; at the end of the file the NULL pointer will be returned

void rewinddir(DIR *dirptr); // repositions offset pointer to start of the file

Home Directory set by system administrator current working directory (cwd) is initially set to be the home directory for all processes

a particular process can change its cwd by invoking the chdir( ) system call

int chdir(const char * path);

this allows the process to access additional files in the new cwd with shorter path names than would otherwise be the case

Return CWD Pathname

large enough to hold the length of the pathname to be returned #include

char *getcwd(char *name, size_t size);

If successful, the cwd pathname will be copied into the array

#include #include #define VB 200

void my_pwd( );

main ( ) { my_pwd( ); }

void my_pwd(void) { char dirname[VB]; if( getcwd( dirname, VB ) == NULL ) perror(“getcwd error”); else printf(“%s\n”, dirname); }

Traverse a Directory

#include starting point of the directory search

int ftw( const char *path, int(*func)( ), int depth);

pointer to a user defined function; specifies the number of file descriptors that the function can this function will be called for have open at any one time; the larger the value of depth, every file or directory found in the the faster the functions returns; HOWEVER, depth must be search path; func must be defined less than the total number of file descriptors allocated to the as follows below process – see getrlimit( )

int func( const char *name, const struct stat *sptr, int type );

string holding the object name stat structure contains contains a value which describes the object information about the encountered – defined in object FTW_F object  file FTW_D object  directory FTW_DNR object  directory, but it could not read descendants will not be processed FTW_SL object  symbolic link FTW_NS object  not symbolic link; stat did not execute successfully, i.e., it contains undefined values

Process a Directory Tree

The following program prints the names of files directories appended by an asterisk symbolic links appended by an asterisk and their access permissions

#include #include int list( const char *name, const struct stat *status, int type ) { if(type == FTW_NS) return 0;

/* print object name, permissions and a postfix “*” if object is a directory or symbolic link */

if(type == FTW_F) printf(“%-30s\t0%3o\n”, name, status -> st_mode&0777); else printf(“%-30s*\t0%3o\n”, name, status -> st_mode&0777); return 0; } main(int argc, char **argv) { int list( const char *, const struct stat *, int ); if (argc == 1 ) ftw(“.”, list, 1); else ftw(argv[1], list, 1); exit(0); }

Unix File Systems

Unix File Hierarchy o separated into distinct sections and distributed across the entire system

Unix File System o resides on a disk partition that is identified by a Unix o starts at the directory node within the tree

Directory Structures o can be accommodated across several distinct physical disks or partitions o linkages across partitions must be done with symbolic links

Logical Disk Layout o Bootstrap Block block 0 memory-image program to load kernel, o (File System) Super Block block 1 total file size, modification date-time, number of blocks reserved for inodes, pointers to chain of free inode numbers, pointers to chain of free data blocks, . mounted file system  Super Block is memory-resident

o Inode Structures blocks 2  n size of inode structures are file system dependent inode numbers are unique only within the file system

o Data Blocks blocks n+1 r

File System Creation o mkfs utility program o sizes of the inode areas and data areas must be specified when the file system is created, however the areas can later be resized

Mounted File System SOME PORTIONS OF THE FILE SYSTEM ARE MEMORY-RESIDENT WHILE OTHER PORTIONS ARE DISK-RESIDENT; maintain data congruency, e.g., the same data item kept in two different locations, one updated, the other not updated, hence incongruent data

ALL DATA TRANSFERS FROM MEMORY TO DISK, I.E., WRITES, are typically cached in the systems data space and written to memory when the buffers are becoming full

#include void (void); /* flush to disk all main memory buffers containing file system data; sync( ) returns immediately after the writing has been scheduled */ int fsync(int filedes); /* flush all data and attributes associated with a particular file; fsync( ) does not return until all the data has been written to disk */

Unix Device Files

device files access peripheral devices attached to Unix systems reads/writes via device files  data transfer directly between the system and the peripheral device disk partition – file system – accessed via a device file device files directory /dev o terminal ports, e.g., /dev/tty00 /dev/console o line printers & mag tape drives, e.g., /dev/lp /dev/rmt0 /dev/rmt0cbn o disk partitions, e.g., /dev/dsk/hd0d /dev/dsk/edew3

$mount /dev/mnt lecture // mount lecture USB drive on /dev/mnt directory $umount /dev/mnt // unmount device from /dev/mnt directory

#include main( ) { int I, fd; fd = open(“/dev/tty00”, O_WRONLY); for( i = 0;i < 100; i++ ) write(fd, “x”, 1); close(fd); }

Block Device Files disks & magtapes transfer between kernel and device – standard size blocks random access supported kernel data structures and routines control access to block device files block device I/O structure very similar, variance only at the low-level controls

Character Device Files terminal lines, modem lines, printer devices random access may/may not be supported data transfer takes place in terms of byte streams of arbitrary length I/O structure varies widely from device to device

FILE SYSTEMS can only exist on block devices; thus block devices have associated with them character devices for fast access – often called a RAW DEVICE

Association of a Peripheral Device with the Device-Specific Code to Drive that Device

read( ) or write( ) system call accesses the device file’s inode structure

O/S checks a flag within the inode structure to determine whether device is a block or character device; the major device number is retrieved from the inode

Major Device Number is used to index into the appropriate device configuration table Operating System Configuration Tables Block Device Switch Character Device Switch from which the device-specific driver routine is accessed to perform the data transfer

Minor Device Number, also stored in the device file’s inode structure, is retrieved and passed to the device-driver routines in order to identify exactly which port is being accessed (on those devices that support more than one peripheral port)

Stat Structure – Device Files

st_mode device file  block device add 060000 to the file permissions character device add 020000 to the permissions symbolic constants defined in S_IFBLK and S_IFCHR st_rdev device file  contains Major and Minor Device Numbers

$ –l /dev/tty3 crw- - w- -w- 1 fred grp1 8,3 Jul 15 11:42 /dv/tty3

character device file minor major device device number number

File System Information

#include

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

int fstatvfs(int fd, struct statvfs *buf);

struct statvfs is defined in and includes the following data items

unsigned long f_bsize file system block size currently used by the system

unsigned long f_frsize fundamental file system block size as specified when file system was created

unsigned long f_blocks total number of blocks on file system measured in units of f_frsize

unsigned long f_bfree total number of free blocks

unsigned long f_bavail number of free blocks available to non-privileged processes

unsigned long f_files total number of inode numbers

unsigned long f_ffree total number of free inode numbers

unsigned long f_favail number of inode numbers available to non-privileged processes

unsigned long f_fsid file system id

unsigned long f_flag bit mask of flag values

unsigned long f_namemax maximum file length

Program to Print the Number of FreeBlocks and Free Inodes on a File System

#include #include #include

main(int argc, char **argv) { struct statvfs buf; if(argc != 2) { fprintf(stderr, “usage: fsys filename\n”); exit(1); }

if(statvfs(argv[1], &buf) != 0) { fprintf(stderr, “statvfs error\n”); exit(2); } printf(“%s:\tfree blocks %d\tfree inodes %d\n”, argv[1], buf.f_bfree, buf.f_ffree); exit(0); }

File and Directory Limits symbolic name of the variable to be #include interrogated -- see

long int pathconf(const char *pathname, int name);

long int fpathconf(int filedes, int name);

#include #include

typedef struct { int val; char *name; } Table;

main( ) { Table *tb; static Table options[ ] = in the directory for the files in the directory { { _PC_LINK_MAX, “Maximum number of links”}, (_PC_NAME_MAX, “Maximum length of a filename”}, {_PC_PATH_MAX, “Maximum length of a pathname”}, { -1, NULL } };

for( tb = options; tb -> name != NULL; tb++) printf(“%-28.28s\t%ld\n”, tb->name, pathconf(“/tmp”, tb->val)); }

System-Wide Limits are documented in system call sysconf( ) can be used to access the system-wide limits