Section 12.3. Regular File Management


[Page 436 (continued)]

12.3. Regular File Management

My description of file management system calls is split up into subsections:

  • A primer that describes the main concepts behind Linux files and file descriptors.

  • A description of the basic file management system calls, using a sample program called "reverse" that reverses the lines of a file.

  • An explanation of a few advanced system calls, using a sample program called "monitor," which periodically scans directories and displays the names of files within them that have changed since the last scan.

  • A description of the remaining file management system calls, using some miscellaneous snippets of source code.

12.3.1. A File Management Primer

The file management system calls allow you to manipulate the full collection of regular, directory, and special files, including:

  • disk-based files

  • terminals

  • printers

  • interprocess communication facilities, such as pipes and sockets

In most cases, open () is used to initially access or create a file. If the system call succeeds, it returns a small integer called a file descriptor that is used in subsequent I/O operations on that file. If open () fails, it returns -1. Here's a snippet of code that illustrates a typical sequence of events:

int fd; /* File descriptor */ ... fd = open (fileName, ...); /* Open file, return file descriptor */ if (fd == -1) {  /* deal with error condition */ } ... fcntl (fd, ...); /* Set some I/O flags if necessary */ ... read (fd, ...);  /* Read from file */ ... 
[Page 437]
write (fd, ...); /* Write to file */ ... lseek (fd, ...); /* Seek within file*/ ... close (fd); /* Close the file, freeing file descriptor */


When a process no longer needs to access an open file, it should close it using the close () system call. All of a process's open files are automatically closed when the process terminates. Although this means that you may often omit an explicit call to close (), it's better programming practice to explicitly close your files.

File descriptors are numbered sequentially, starting from zero. By convention, the first three file descriptor values have a special meaning (Figure 12-5).

Figure 12-5. File descriptor values for standard I/O channels.

Value

Meaning

0

standard input (stdin)

1

standard output (stdout)

2

standard error (stderr)


For example, the printf () library function always sends its output using file descriptor 1, and scanf () always reads its input using file descriptor 0. When a reference to a file is closed, the file descriptor is freed and may be reassigned by a subsequent open (). Most I/O system calls require a file descriptor as their first argument so that they know which file to operate on.

A single file may be opened several times and thus have several file descriptors associated with it (Figure 12-6).

Figure 12-6. Many file descriptors, one file.



[Page 438]

Each file descriptor has its own private set of properties that have nothing to do with the file that it is associated with, including:

  • A file pointer that records the offset in the file where it is reading/writing. When a file descriptor is created, its file pointer is positioned at offset 0 in the file (the first character) by default. As the process reads and/or writes, the file pointer is updated accordingly. For example, if a process opened a file and then read 10 bytes from the file, the file pointer would end up positioned at offset 10. If the process then wrote 20 bytes, the bytes at offset 10..29 in the file would be overwritten and the file pointer would end up positioned at offset 30.

  • A flag that indicates whether the descriptor should be automatically closed if the process calls exec (). exec () is described in the "process management" on page 473.

  • A flag that indicates whether all of the output to the file should be appended to the end of the file.

In addition to these values, some other values are meaningful only if the file is a special one such as a pipe or a socket:

  • A flag that indicates whether a process should block on input from the file if the file doesn't currently contain any input.

  • A number that indicates a process ID or process group that should be sent a SIGIO signal if input becomes available on the file. Signals and process groups are discussed later in this chapter.

The system calls open () and fcntl () allow you to manipulate these flags, and are described later in this section.

12.3.2. First Example: reverse

In this first section, I'll describe the most basic I/O system calls. Figure 12-7 lists them, together with a brief description of their function.

Figure 12-7. Linux system calls for basic I/O operations.

Name

Function

open

Opens/creates a file.

read

Reads bytes from a file into a buffer.

write

Writes bytes from a buffer to a file.

lseek

Moves to a particular offset in a file.

close

Closes a file.

unlink

Removes a file.



[Page 439]

To illustrate the use of these system calls, I'll use a small utility program called "reverse.c" (Figure 12-8). As well as being a good vehicle for my presentation, it also doubles as a nice example of how to write a utility.

Figure 12-8. Description of the reverse program.

Utility: reverse -c [ fileName ]

reverse reverses the lines of its input and displays them to standard output. If no file name is specified, reverse reverses its standard input. When the -c option is used, reverse also reverses the characters in each line.


Here's an example of reverse in action:

$ gcc reverse.c -o reverse    ...compile the program. $ cat test                    ...list the test file. Christmas is coming, The days that grow shorter, Remind me of seasons I knew in the past. $ ./reverse test              ...reverse the file. Remind me of seasons I knew in the past. The days that grow shorter, Christmas is coming, $ ./reverse -c test           ...reverse the lines too. .tsap eht ni wenk I snosaes fo em dnimeR ,retrohs worg taht syad ehT ,gnimoc si samtsirhC $ cat test | ./reverse        ...pipe output to "reverse". Remind me of seasons I knew in the past. The days that grow shorter, Christmas is coming, $ _ 


12.3.3. How reverse Works

The reverse utility works by performing two passes over its input. During the first pass, it notes the starting offset of each line in the file and stores this information in an array. During the second pass, it jumps to the start of each line in reverse order, copying it from the original input file to its standard output.

If no file name is specified on the command line, reverse reads from its standard input during the first pass and copies it into a temporary file for the second pass. When the program is finished, the temporary file is removed.

Figure 12-9 gives an overview of the program flow, together with a list of the functions that are associated with each action, and a list of the system calls used by each step.


[Page 440]

Figure 12-9. Description of algorithm used in reverse.c.

Step

Action

Functions

System calls

1

Parse command line.

parseCommandLine, processOptions

open

2

If reading from standard input, create temporary file to store input; otherwise open input file for reading.

pass1

open

3

Read from file in chunks, storing the starting offset of each line in an array. If reading from standard input, copy each chunk to the temporary file.

pass1, trackLines

read, write

4

Read the input file again, backward, copying each line to standard output. Reverse the line if the -c option was chosen.

pass2, processLine, reverseLine

lseek

5

Close file, removing it if it was a temporary file.

pass2

close


The next several pages provide a complete listing of "reverse.c," the source code of reverse. I suggest that you skim through it and then read the description of the system calls that follow. This code is also available online; see the Preface for more information.

12.3.4. reverse.c: Listing

  1  #include <fcntl.h>  /* For file mode definitions */   2  #include <stdio.h>   3  #include <stdlib.h>   4   5   6  /* Enumerator */   7  enum { FALSE, TRUE }; /* Standard false and true values */   8  enum { STDIN, STDOUT, STDERR }; /* Standard I/O channel indices */   9  10  11  /* #define Statements */  12  #define BUFFER_SIZE    4096   /* Copy buffer size */  13  #define NAME_SIZE      12  14  #define MAX_LINES      100000 /* Max lines in file */  15  16  17  /* Globals */  18  char *fileName = 0; /* Points to file name */  19  char tmpName [NAME_SIZE];  20  int charOption = FALSE; /* Set to true if -c option is used */ 
[Page 441]
21 int standardInput = FALSE; /* Set to true if reading stdin */ 22 int lineCount = 0; /* Total number of lines in input */ 23 int lineStart [MAX_LINES]; /* Store offsets of each line */ 24 int fileOffset = 0; /* Current position in input */ 25 int fd; /* File descriptor of input */ 26 27 /****************************************************************/ 28 29 main (argc, argv) 30 31 int argc; 32 char* argv []; 33 34 { 35 parseCommandLine (argc,argv); /* Parse command line */ 36 pass1 (); /* Perform first pass through input */ 37 pass2 (); /* Perform second pass through input */ 38 return (/* EXITSUCCESS */ 0); /* Done */ 39 } 40 41 /****************************************************************/ 42 43 parseCommandLine (argc, argv) 44 45 int argc; 46 char* argv []; 47 48 /* Parse command-line arguments */ 49 50 { 51 int i; 52 53 for (i= 1; i < argc; i++) 54 { 55 if(argv[i][0] == '-') 56 processOptions (argv[i]); 57 else if (fileName == 0) 58 fileName= argv[i]; 59 else 60 usageError (); /* An error occurred */ 61 } 62 63 standardInput = (fileName == 0); 64 } 65 66 /****************************************************************/ 67 68 processOptions (str)
[Page 442]
69 70 char* str; 71 72 /* Parse options */ 73 74 { 75 int j; 76 77 for (j= 1; str[j] != 0; j++) 78 { 79 switch(str[j]) /* Switch on command-line flag */ 80 { 81 case 'c': 82 charOption = TRUE; 83 break; 84 85 default: 86 usageError(); 87 break; 88 } 89 } 90 } 91 92 /****************************************************************/ 93 94 usageError () 95 96 { 97 fprintf (stderr, "Usage: reverse -c [filename]\n"); 98 exit (/* EXITFAILURE */ 1); 99 } 100 101 /****************************************************************/ 102 103 pass1 () 104 105 /* Perform first scan through file */ 106 107 { 108 int tmpfd, charsRead, charsWritten; 109 char buffer [BUFFER_SIZE]; 110 111 if (standardInput) /* Read from standard input */ 112 { 113 fd = STDIN; 114 sprintf (tmpName, ".rev.%d",getpid ()); /* Random name */ 115 /* Create temporary file to store copy of input */ 116 tmpfd = open (tmpName, O_CREAT | O_RDWR, 0600);
[Page 443]
117 if (tmpfd == -1) fatalError (); 118 } 119 else /* Open named file for reading */ 120 { 121 fd = open (fileName, O_RDONLY); 122 if (fd == -1) fatalError (); 123 } 124 125 lineStart[0] = 0; /* Offset of first line */ 126 127 while (TRUE) /* Read all input */ 128 { 129 /* Fill buffer */ 130 charsRead = read (fd, buffer, BUFFER_SIZE); 131 if (charsRead == 0) break; /* EOF */ 132 if (charsRead == -1) fatalError (); /* Error */ 133 trackLines (buffer, charsRead); /* Process line */ 134 /* Copy line to temporary file if reading from stdin */ 135 if (standardInput) 136 { 137 charsWritten = write (tmpfd, buffer, charsRead); 138 if(charsWritten != charsRead) fatalError (); 139 } 140 } 141 142 /* Store offset of trailing line, if present */ 143 lineStart[lineCount + 1] = fileOffset; 144 145 /* If reading from standard input, prepare fd for pass2 */ 146 if (standardInput) fd = tmpfd; 147 } 148 149 /****************************************************************/ 150 151 trackLines (buffer, charsRead) 152 153 char* buffer; 154 int charsRead; 155 156 /* Store offsets of each line start in buffer */ 157 158 { 159 int i; 160 161 for (i = 0; i < charsRead; i++) 162 { 163 ++fileOffset; /* Update current file position */ 164 if (buffer[i] == '\n') lineStart[++lineCount] = fileOffset;
[Page 444]
165 } 166 } 167 168 /****************************************************************/ 169 170 int pass2 () 171 172 /* Scan input file again, displaying lines in reverse order */ 173 174 { 175 int i; 176 177 for (i = lineCount - 1; i >= 0; i--) 178 processLine (i); 179 180 close (fd); /* Close input file */ 181 if (standardInput) unlink (tmpName); /* Remove temp file */ 182 } 183 184 /****************************************************************/ 185 186 processLine (i) 187 188 int i; 189 190 /* Read a line and display it */ 191 192 { 193 int charsRead; 194 char buffer [BUFFER_SIZE]; 195 196 lseek (fd, lineStart[i], SEEK_SET); /* Find line and read */ 197 charsRead = read (fd, buffer, lineStart[i+1] - lineStart[i]); 198 /* Reverse line if -c option was selected */ 199 if (charOption) reverseLine (buffer, charsRead); 200 write (1, buffer, charsRead); /* Write it to standard output */ 201 } 202 203 /****************************************************************/ 204 205 reverseLine (buffer, size) 206 207 char* buffer; 208 int size; 209 210 /* Reverse all the characters in the buffer */ 211 212 {
[Page 445]
213 int start = 0, end = size - 1; 214 char tmp; 215 216 if (buffer[end] == '\n') --end; /* Leave trailing newline */ 217 218 /* Swap characters in a pairwise fashion */ 219 while (start < end) 220 { 221 tmp = buffer[start]; 222 buffer[start] = buffer[end]; 223 buffer[end] = tmp; 224 ++start; /* Increment start index */ 225 --end; /* Decrement end index */ 226 } 227 } 228 229 /****************************************************************/ 230 231 fatalError () 232 233 { 234 perror ("reverse: "); /* Describe error */ 235 exit (1); 236 }


12.3.5. Opening a File: open ()

The reverse utility begins by executing parseCommandLine () [43] that sets various flags depending on which options are chosen. If a filename is specified, the variable fileName is set to point to the name and standardInput is set to FALSE; otherwise, fileName is set to a zero-length string and standardInput is set to TRUE. Next, pass1 () [103] is executed, which performs one of the following actions:

  • If reverse is reading from standard input, a temporary file is created. The file is created with read and write permissions for the owner, and no permissions for anyone else (octal mode 600). It is opened in read/write mode, and is used to store a copy of the standard input for use during pass 2. During pass 1, the input is taken from standard input, and so the file descriptor fd is set to STDIN, defined to be 0 at the top of the program. Recall that standard input is always file descriptor zero.

  • If reverse is reading from a named file, the file is opened in read-only mode so that its contents may be read during pass 1 using the file descriptor fd.

Each action uses the open () system call; the first action uses it to create a file, and the second action uses it to access an existing file (Figure 12-10).


[Page 446]

Figure 12-10. Description of the open () system call.

System Call: int open (char* fileName, int mode [, int permissions])

open () allows you to open or create a file for reading and/or writing. fileName is an absolute or relative pathname and mode is a bitwise or'ing of a read/write flag together with zero or more miscellaneous flags. permissions is a number that encodes the value of the file's permission flags, and should only be supplied when a file is being created. It is usually written using the octal encoding scheme described in Chapter 3, "GNU Utilities for Nonprogrammers." The permissions value is affected by the process's umask value, described in Chapter 5, "The Linux Shells." The values of the predefined read/write and miscellaneous flags are defined in "/usr/include/fcntl.h". The read/write flags are as follows:

FLAG

MEANING

O_RDONLY

Open for read-only.

O_WRONLY

Open for write-only.

O_RDWR

Open for read and write.


The miscellaneous flags are as follows:

FLAG

MEANING

O_APPEND

Position the file pointer at the end of the file before each write ().

O_CREAT

If the file doesn't exist, create the file, and set the owner ID to the process's effective user ID. The umask value is used when determining the initial permission flag settings.

O_EXCL

If O_CREAT is set and the file exists, then open () fails.

O_NONBLOCK or O_NDELAY

This setting works only for named pipes. If set, an open for read-only will return immediately, regardless of whether the write end is open, and an open for write-only will fail if the read end isn't open. If clear, an open for read-only or write-only will block until the other end is also open.

O_TRUNC

If the file exists, it is truncated to length zero.


open () returns a non-negative file descriptor if successful; otherwise, it returns -1.



[Page 447]
12.3.5.1. Creating a File

To create a file, use the O_CREAT flag as part of the mode flags, and supply the initial file permission flag settings as an octal value. For example, lines 114..117 create a temporary file with read and write permission for the owner, and then open it for reading and writing:

114    sprintf (tmpName, ".rev.%d", getpid ()); /* Random name */ 115    /* Create temporary file to store copy of input */ 116    tmpfd = open (tmpName, O_CREAT | O_RDWR, 0600); 117    if (tmpfd == -1) fatalError (); 


The getpid () function is a system call that returns the process's ID number (PID), which is guaranteed to be unique. This is a handy way to generate unique temporary filenames. For more details on this system call, see the section "Process management" on page 473. Note that I chose the name of the temporary file to begin with a period, making it a hidden file, so that it doesn't show up in an ls listing.

12.3.5.2. Opening an Existing File

To open an existing file, specify the mode flags only. Lines 121..122 open a named file for read-only:

121    fd = open (fileName, O_RDONLY); 122    if (fd == -1) fatalError (); 


12.3.5.3. Other Open Flags

The other more complicated flag settings for open (), such as O_NONBLOCK, are intended for use with the pipes, sockets, and STREAMS that are described later in this chapter. Right now, the O_CREAT flag is probably the only miscellaneous flag that you'll need.

12.3.6. Reading From a File: read ()

Once reverse has initialized the file descriptor fd for input, it reads chunks of input and processes them until the end of the file is reached. To read bytes from a file, it uses the read () system call (Figure 12-11).

Figure 12-11. Description of the read () system call.

System Call: ssize_t read (int fd, void* buf, size_t count)

[Note: This synopsis describes how read () operates when reading a regular file. For information on reading from special files, please refer to later sections of this chapter.]

read () copies count bytes from the file referenced by the file descriptor fd into the buffer buf. The bytes are read from the current file position, which is then updated accordingly.

read () copies as many bytes from the file as it can, up to the number specified by count, and returns the number of bytes actually copied. If a read () is attempted after the last byte has already been read, it returns 0, which indicates end-of-file.

If successful, read () returns the number of bytes that it read; otherwise, it returns -1.



[Page 448]

The read () system call performs low-level input, and has none of the formatting capabilities of scanf (). The benefit of read () is that it bypasses the additional layer of buffering supplied by the C library functions, and is therefore very fast. Although I could have read one character of input at a time, this would have resulted in a large number of system calls, thus slowing down the execution of my program considerably. Instead, I used read () to read up to BUFFER_SIZE characters at a time. BUFFER_SIZE was chosen to be a multiple of the disk block size, for efficient copying. Lines 130..132 perform the read and test the return result:

130     charsRead = read (fd, buffer, BUFFER_SIZE); 131     if (charsRead == 0) break; /* EOF */ 132     if (charsRead == -1) fatalError (); /* Error */ 


As each chunk of input is read, it is passed to the trackLines () function. This function scans the input buffer for newlines and stores the offset of the first character in each line in the lineStart array. The variable fileOffset is used to maintain the current file offset. The contents of lineStart are used during the second pass.

12.3.7. Writing to a File: write ()

When reverse is reading from standard input, it creates a copy of the input for use during pass two. To do this, it sets the file descriptor tmpfd to refer to a temporary file, and then writes each chunk of input to the file during the read loop. To write bytes to a file, it uses the write () system call (Figure 12-12).

Figure 12-12. Description of the write () system call.

System Call: ssize_t write (int fd, void* buf, size_t count)

[Note: This synopsis describes how write () operates when writing to a regular file. For information on writing to special files, please refer to later sections of this chapter.]

write () copies count bytes from a buffer buf to the file referenced by the file descriptor fd. The bytes are written at the current file position, which is then updated accordingly. If the O_APPEND flag was set for fd, the file position is set to the end of the file before each write.

write () copies as many bytes from the buffer as it can, up to the number specified by count, and returns the number of bytes actually copied. Your process should always check the return value. If the return value isn't count, then the disk probably filled up and no space was left.

If successful, write () returns the number of bytes that were written; otherwise, it returns -1.


The write () system call performs low-level output, and has none of the formatting capabilities of printf (). The benefit of write () is that it bypasses the additional layer of buffering supplied by the C library functions, and is therefore very fast. Lines 134..139 perform the write operation:

134    /* Copy line to temporary file if reading standard input */ 135    if (standardInput) 136      { 137        charsWritten = write (tmpfd, buffer, charsRead); 
[Page 449]
138 if (charsWritten != charsRead) fatalError (); 139 }


12.3.8. Moving in a File: lseek ()

Once the first pass has completed, the array lineStart contains the offsets of the first character of each line of the input file. During pass two, the lines are read in reverse order and displayed to standard output. In order to read the lines out of sequence, the program makes use of lseek (), which is a system call that allows a descriptor's file pointer to be changed (Figure 12-13).

Figure 12-13. Description of the lseek () system call.

System Call: off_t lseek (int fd, off_t offset, int mode)

lseek () allows you to change a descriptor's current file position. fd is the file descriptor, offset is a long integer, and mode describes how offset should be interpreted. The three possible values of mode are defined in "/usr/include/stdio.h," and have the following meaning:

VALUE

MEANING

SEEK_SET

offset is relative to the start of the file.

SEEK_CUR

offset is relative to the current file position.

SEEK_END

offset is relative to the end of the file.


lseek () fails if you try to move before the start of the file.

If successful, lseek () returns the current file position; otherwise, it returns -1.


Lines 196..197 seek to the start of a line and then read in all of its characters. Note that the number of characters to read is calculated by subtracting the start offset of the next line from the start offset of the current line.

196    lseek (fd, lineStart[i], SEEK_SET); /* Find line and read it */ 197    charsRead = read (fd, buffer, lineStart[i+1] - lineStart[i]); 


If you want to find out your current location without moving, use an offset value of zero relative to the current position:

currentOffset = lseek (fd, 0, SEEK_CUR); 


If you move past the end of the file and then perform a write (), the kernel automatically extends the size of the file and treats the intermediate file area as if it were filled with NULL (ASCII 0) characters. Interestingly enough, it doesn't allocate disk space for the intermediate area, which is confirmed by the following example:

$ cat sparse.c                ...list the test file. #include <fcntl.h> #include <stdio.h> 
[Page 450]
#include <stdlib.h> /**********************************************************/ main () { int i, fd; /* Create a sparse file */ fd = open ("sparse.txt", O_CREAT | O_RDWR, 0600); write (fd, "sparse", 6); lseek (fd, 60006, SEEK_SET); write (fd, "file", 4); close (fd); /* Create a normal file */ fd = open ("normal.txt", O_CREAT | O_RDWR, 0600); write (fd, "normal", 6); for (i = 1; i <= 60000; i++) write (fd, "/0", 1); write (fd, "file", 4); close (fd); } $ sparse ...execute the file. $ ls -lG *.txt ...look at the files. -rw-r--r-- 1 ables 60010 Aug 16 15:06 normal.txt -rw-r--r-- 1 ables 60010 Aug 16 15:06 sparse.txt $ ls -s *.txt ...list their block usage. 64 normal.txt 12 sparse.txt $ _


Files that contain "gaps" like this are termed "sparse" files. For details on how they are actually stored, consult Chapter 13, "Linux Internals."

12.3.9. Closing a File: close ()

When pass two is over, reverse uses the close () system call to free the input file descriptor. Figure 12-14 gives a description of close ().

Figure 12-14. Description of the close () system call.

System Call: int close (int fd)

close () frees the file descriptor fd. If fd is the last file descriptor associated with a particular open file, the kernel resources associated with the file are deallocated. When a process terminates, all of its file descriptors are automatically closed, but it's better programming practice to close a file when you're done with it. If you close a file descriptor that's already closed, an error occurs.

If successful, close () returns zero; otherwise, it returns -1.



[Page 451]

Line 180 contains the call to close ():

180 close (fd); /* Close input file */ 


When a file is closed, it does not guarantee that the file's buffers are immediately flushed to disk. For more information on file buffering, consult Chapter 13, "Linux Internals."

12.3.10. Deleting a File: unlink ()

If reverse reads from standard input, it stores a copy of the input in a temporary file. At the end of pass two, it removes this file using the unlink () system call (Figure 12-15).

Figure 12-15. Description of the unlink () system call.

System Call: int unlink (const char* fileName)

unlink () removes the hard link from the name fileName to its file. If fileName is the last link to the file, the file's resources are deallocated. In this case, if any process's file descriptors are currently associated with the file, the directory entry is removed immediately but the file is only deallocated after all of the file descriptors are closed. This means that an executable file can unlink itself during execution and still continue to completion.

If successful, unlink () returns zero; otherwise, it returns -1.


Line 181 contains the call to unlink ():

181    if (standardInput) unlink (tmpName); /* Remove temp file */ 


For more information about hard links, consult Chapter 13, "Linux Internals."

12.3.11. Second Example: monitor

This section provides a description of some more advanced system calls (Figure 12-16).

Figure 12-16. Advanced Linux I/O system calls.

Name

Function

stat

Obtains status information about a file.

fstat

Works just like stat.

readdir

Obtains directory entries.



[Page 452]

These calls are demonstrated in the context of a program called "monitor," which allows a user to monitor a series of named files and obtain information whenever any of them are modified. Figure 12-17 describes monitor.

Figure 12-17. Description of the monitor program.

Utility: monitor [-t delay] [-l count]{ fileName }+

monitor scans all of the specified files every delay seconds and displays information about any of the specified files that were modified since the last scan. If fileName is a directory, all of the files inside that directory are scanned. File modification is indicated in one of three ways:

LABEL

MEANING

ADDED

Indicates that the file was created since the last scan. Every file in the file list is given this label during the first scan.

CHANGED

Indicates that the file was modified since the last scan.

DELETED

Indicates that the file was deleted since the last scan.


By default, monitor will scan forever, although you can specify the total number of scans by using the -l option. The default delay time is 10 seconds between scans, although this may be overridden by using the -t option.


In the following example, I monitored a directory, storing the output of monitor into a temporary file. Notice how the contents of the "monitor.out" file reflected the additions, modifications, and deletions in the monitored directory:

% mkdir tmp % ./monitor tmp >& monitor.out & [1] 15771 % cat >tmp/a                   ...create a file in ./tmp. hi there ^D % cat tmp/a tmp/b              ...and another. % vim tmp/b                    ...change one. ... % cat >tmp/file.txt            ...create one more. more data ^D % rm tmp/a                     ...then remove them. % rm tmp/b % jobs 
[Page 453]
[1] + Running ./monitor tmp >& monitor.out % kill %1 ...kill monitor. % [1] Terminated ./monitor tmp >& monitor.out % % cat monitor.out ADDED tmp/a size 9bytes, mod. time = Sun Aug 29 17:47:10 2004 ADDED tmp/b size 9 bytes, mod. time = Sun Aug 29 17:47:18 2004 CHANGED tmp/b size 9 bytes, mod. time = Sun Aug 29 17:47:41 2004 ADDED tmp/file.txt size 10 bytes, mod. time = Sun Aug 29 17:47:52 2004 DELETED tmp/a DELETED tmp/b % _


12.3.12. How monitor Works

The monitor utility continually scans the specified files and directories for modifications. It uses the stat () system call to obtain status information about named files, including their type and last modification time, and uses the readdir () system call to scan directories. It maintains a status table called stats, which holds the following information about each file that it finds:

  • the name of the file

  • the status information obtained by the stat ()

  • a record of whether the file was present during the present scan and the previous scan

During a scan, monitor processes each file as follows:

  • If the file isn't currently in the scan table, it's added and the message "ADDED" is displayed.

  • If the file is already in the scan table and has been modified since the last scan, the message "CHANGED" is displayed.

At the end of a scan, all entries that were present during the previous scan but not during the current scan are removed from the table and the message "DELETED" is displayed.

Following is a complete listing of "monitor.c", the source code of monitor. I suggest that you skim through it and then read the descriptions of the system calls that follow.

12.3.13. Monitor.c: Listing

  1 #include <stdio.h>           /* For printf, fprintf */   2 #include <string.h>          /* For strcmp */   3 #include <ctype.h>           /* For isdigit */   4 #include <fcntl.h>           /* For O_RDONLY */   5 #include <dirent.h>          /* For readdir */   6 #include <sys/stat.h>        /* For IS macros */   7 #include <sys/types.h>       /* For modet */   8 #include <time.h>            /* For localtime, asctime */   9 
[Page 454]
10 11 /* #define Statements */ 12 #define MAX_FILES 100 13 #define MAX_FILENAME 50 14 #define NOT_FOUND -1 15 #define FOREVER -1 16 #define DEFAULT_DELAY_TIME 10 17 #define DEFAULT_LOOP_COUNT FOREVER 18 19 20 /* Booleans */ 21 enum { FALSE, TRUE }; 22 23 24 /* Status structure, one per file. */ 25 struct statStruct 26 { 27 char fileName [MAX_FILENAME]; /* File name */ 28 int lastCycle, thisCycle; /* To detect changes */ 29 struct stat status; /* Information from stat () */ 30 }; 31 32 33 /* Globals */ 34 char* fileNames [MAX_FILES]; /* One per file on command line */ 35 int fileCount; /* Count of files on command line */ 36 struct statStruct stats [MAX_FILES]; /* One per matching file */ 37 int loopCount = DEFAULT_LOOP_COUNT; /* Number of times to loop */ 38 int delayTime = DEFAULT_DELAY_TIME; /* Seconds between loops */ 39 40 /****************************************************************/ 41 42 main (argc, argv) 43 44 int argc; 45 char* argv []; 46 47 { 48 parseCommandLine (argc, argv); /* Parse command line */ 49 monitorLoop (); /* Execute main monitor loop */ 50 return (/* EXIT_SUCCESS */ 0); 51 } 52 53 /****************************************************************/ 54
[Page 455]
55 parseCommandLine (argc, argv) 56 57 int argc; 58 char* argv []; 59 60 /* Parse command-line arguments */ 61 62 { 63 int i; 64 65 for (i = 1; ( (i < argc) && (i < MAX_FILES) ); i++) 66 { 67 if (argv[i][0] == '-') 68 processOptions (argv[i]); 69 else 70 fileNames[fileCount++] = argv[i]; 71 } 72 73 if (fileCount == 0) usageError (); 74 } 75 76 /****************************************************************/ 77 78 processOptions (str) 79 80 char* str; 81 82 /* Parse options */ 83 84 { 85 int j; 86 87 for (j = 1; str[j] != 0; j++) 88 { 89 switch(str[j]) /* Switch on option letter */ 90 { 91 case 't': 92 delayTime = getNumber (str, &j); 93 break; 94 95 case 'l': 96 loopCount = getNumber (str, &j); 97 break; 98 } 99 }
[Page 456]
100 } 101 102 /****************************************************************/ 103 104 getNumber (str, i) 105 106 char* str; 107 int* i; 108 109 /* Convert a numeric ASCII option to a number */ 110 111 { 112 int number = 0; 113 int digits = 0; /* Count the digits in the number */ 114 115 while (isdigit (str[(*i) + 1])) /* Convert chars to ints */ 116 { 117 number = number * 10 + str[++(*i)] - '0'; 118 ++digits; 119 } 120 121 if (digits == 0) usageError (); /* There must be a number */ 122 return (number); 123 } 124 125 /****************************************************************/ 126 127 usageError () 128 129 { 130 fprintf (stderr, "Usage: monitor -t<seconds> -l<loops> {filename}+\n"); 131 exit (/* EXIT_FAILURE */ 1); 132 } 133 134 /****************************************************************/ 135 136 monitorLoop () 137 138 /* The main monitor loop */ 139 140 { 141 do 142 { 143 monitorFiles (); /* Scan all files */ 144 fflush (stdout); /* Flush standard output */
[Page 457]
145 fflush (stderr); /* Flush standard error */ 146 sleep (delayTime); /* Wait until next loop */ 147 } 148 while (loopCount == FOREVER || --loopCount > 0); 149 } 150 151 /****************************************************************/ 152 153 monitorFiles () 154 155 /* Process all files */ 156 157 { 158 int i; 159 160 for (i = 0; i < fileCount; i++) 161 monitorFile (fileNames[i]); 162 163 for (i = 0; i< MAX_FILES; i++) /* Update stat array */ 164 { 165 if (stats[i].lastCycle && !stats[i].thisCycle) 166 printf ("DELETED %s\n", stats[i].fileName); 167 168 stats[i].lastCycle = stats[i].thisCycle; 169 stats[i].thisCycle = FALSE; 170 } 171 } 172 173 /****************************************************************/ 174 175 monitorFile (fileName) 176 177 char* fileName; 178 179 /* Process a single file/directory*/ 180 181 { 182 struct stat statBuf; 183 mode_t mode; 184 int result; 185 186 result = stat (fileName, &statBuf); /* Obtain file status */ 187 188 if (result == -1) /* Status was not available */
[Page 458]
189 { 190 fprintf (stderr, "Cannot stat %s\n", fileName); 191 return; 192 } 193 194 mode = statBuf.st_mode; /* Mode of file */ 195 196 if(S_ISDIR (mode)) /* Directory */ 197 processDirectory (fileName); 198 else if (S_ISREG (mode) || S_ISCHR (mode) || S_ISBLK (mode)) 199 updateStat (fileName, &statBuf); /* Regular file */ 200 } 201 202 /****************************************************************/ 203 204 processDirectory (dirName) 205 206 char* dirName; 207 208 /* Process all files in the named directory */ 209 210 { 211 struct dirent *dirEntry; 212 DIR *dp; 213 char fileName [MAX_FILENAME]; 214 215 if ((dp = opendir(dirName)) == NULL ) { 216 fatalError (); 217 } 218 219 dirEntry = readdir(dp); 220 221 while ( dirEntry != NULL ) /* Read all directory entries */ 222 { 223 if (strcmp (dirEntry->d_name, ".") != 0&& 224 strcmp (dirEntry->d_name, "..") != 0) /* Skip . and .. */ 225 { 226 sprintf (fileName, "%s/%s", dirName, dirEntry->d_name); 227 monitorFile (fileName); /* Call recursively */ 228 } 229 230 dirEntry = readdir(dp); 231 } 232 233 closedir(dp);
[Page 459]
234 } 235 236 /****************************************************************/ 237 238 updateStat (fileName, statBuf) 239 240 char* fileName; 241 struct stat* statBuf; 242 243 /* Add a status entry if necessary */ 244 245 { 246 int entryIndex; 247 248 entryIndex = findEntry (fileName); /* Find existing entry */ 249 250 if (entryIndex == NOT_FOUND) 251 entryIndex = addEntry (fileName, statBuf); /* Add new entry */ 252 else 253 updateEntry (entryIndex, statBuf); /* Update existing entry */ 254 255 if (entryIndex != NOT_FOUND) 256 stats[entryIndex].thisCycle = TRUE; /* Update status array */ 257 } 258 259 /****************************************************************/ 260 261 findEntry (fileName) 262 263 char* fileName; 264 265 /* Locate the index of a named filein the status array */ 266 267 { 268 int i; 269 270 for (i = 0; i < MAX_FILES; i++) 271 if (stats[i].lastCycle && 272 strcmp (stats[i].fileName, fileName) == 0) return (i); 273 274 return (NOT_FOUND); 275 } 276 277 /****************************************************************/ 278 279 addEntry (fileName, statBuf)
[Page 460]
280 281 char* fileName; 282 struct stat* statBuf; 283 284 /* Add a new entry into the status array */ 285 286 { 287 int index; 288 289 index = nextFree (); /* Find the next free entry */ 290 if (index == NOT_FOUND) return (NOT_FOUND); /* None left */ 291 strcpy (stats[index].fileName, fileName); /* Add filename */ 292 stats[index].status = *statBuf; /* Add status information */ 293 printf ("ADDED "); /* Notify standard output */ 294 printEntry (index); /* Display status information */ 295 return (index); 296 } 297 298 /****************************************************************/ 299 300 nextFree () 301 302 /* Return the nextfree index in the status array */ 303 304 { 305 int i; 306 307 for (i = 0; i < MAX_FILES; i++) 308 if (!stats[i].lastCycle && !stats[i].thisCycle) return (i); 309 310 return (NOT_FOUND); 311 } 312 313 /****************************************************************/ 314 315 updateEntry (index, statBuf) 316 317 int index; 318 struct stat* statBuf; 319 320 /*Display information if the file has been modified */ 321 322 { 323 if (stats[index].status.st_mtime != statBuf->st_mtime) 324 {
[Page 461]
325 stats[index].status = *statBuf; /* Store stat information */ 326 printf ("CHANGED "); /* Notify standard output */ 327 printEntry (index); 328 } 329 } 330 331 /****************************************************************/ 332 333 printEntry (index) 334 335 int index; 336 337 /* Display an entry of the status array */ 338 339 { 340 printf ("%s ", stats[index].fileName); 341 printStat (&stats[index].status); 342 } 343 344 /****************************************************************/ 345 346 printStat (statBuf) 347 348 struct stat* statBuf; 349 350 /* Display a status buffer */ 351 352 { 353 printf ("size %lu bytes, mod. time = %s", statBuf->st_size, 354 asctime (localtime (&statBuf->st_mtime))); 355 } 356 357 /****************************************************************/ 358 359 fatalError () 360 361 { 362 perror ("monitor: "); 363 exit (/* EXIT_FAILURE */ 1); 364 }


12.3.14. Obtaining File Information: stat ()

monitor obtains its file information by calling stat () (Figure 12-18).


[Page 462]

Figure 12-18. Description of the stat () system call.

System Call: int stat (const char* name, struct stat* buf)

 

int lstat (const char* name, struct stat* buf)

 

int fstat (int fd, struct stat* buf)

stat () fills the buffer buf with information about the file name. The stat structure is defined in "/usr/include/sys/stat.h". lstat () returns information about a symbolic link itself rather than the file it references. fstat () performs the same function as stat (), except that it takes the file descriptor of the file to be stat'ed as its first parameter. The stat structure contains the following members:

 

NAME

MEANING

 

st_dev

the device number

 

st_ino

the inode number

 

st_mode

the permission flags

 

st_nlink

the hard link count

 

st_uid

the user ID

 

st_gid

the group ID

 

st_size

the file size

 

st_atime

the last access time

 

st_mtime

the last modification time

 

st_ctime

the last status change time

There are some predefined macros defined in "/usr/include/sys/stat.h" that take st_mode as their argument and return true (1) for the following file types:

 

MACRO

RETURNS TRUE FOR FILE TYPE

 

S_ISDIR

directory

 

S_ISCHR

character-oriented special device

 

S_ISBLK

block-oriented special device

 

S_ISREG

regular file

 

S_ISFIFO

pipe

The time fields may be decoded using the standard C library asctime () and localtime () subroutines.

 

stat () and fstat () return 0 if successful and -1 otherwise.



[Page 463]

The monitor utility invokes stat () from monitorFile () [175] on line 186:

186    result = stat (fileName, &statBuf); /* Obtain file status */ 


It examines the mode of the file using the S_ISDIR, S_ISREG, S_ISCHR, and S_ISBLK macros, processing directory files and other files as follows:

  • If the file is a directory file, it calls processDirectory () [204], which applies monitorFile () recursively to each of its directory entries.

  • If the file is a regular file, character-oriented special file, or block-oriented special file, it calls updateStat () [238], which either adds or updates the file's status entry. If the status changes in any way, updateEntry () [315] is called to display the file's new status. The decoding of the time fields is performed by the localtime () and asctime () routines in printStat () [346].

12.3.15. Reading Directory Information: opendir (), readdir (), and closedir ()

processDirectory () [204] opens a directory file with opendir () and then uses readdir () to obtain every entry in the directory (Figure 12-19).

Figure 12-19. Description of the opendir (), readdir (), and closedir () library functions.

Library Function: DIR * opendir (char * fileName)

 

struct dirent * readdir (DIR *dir)

 

int closedir (DIR *dir)

opendir () opens a directory file for reading and returns a pointer to a stream descriptor which is used as the argument to readdir () and closedir (). readdir () returns a pointer to a dirent structure containing information about the next directory entry each time it is called. closedir () is used to close the directory. The dirent structure is defined in the system header file "/usr/include/dirent.h"

 

NAME

MEANING

 

d_ino

the inode number

 

d_off

the offset of the next directory entry

 

d_reclen

the length of the directory entry structure

 

d_name

the filename

opendir () returns the directory stream pointer when successful, NULL when not successful. readdir () returns 1 when a directory entry has been successfully read, 0 when the last directory entry has already been read, and -1 in the case of an error. closedir () returns 0 on success, -1 on failure.



[Page 464]

processDirectory () is careful not to trace into the "." and ".." directories. When the directory has been completely searched, it is closed.

12.3.16. Miscellaneous File Management System Calls

There now follows a brief description of the following miscellaneous file management system calls (Figure 12-20).

Figure 12-20. Linux file management system calls.

Name

Function

chown

Changes a file's owner and/or group.

chmod

Changes a file's permission settings.

dup

Duplicates a file descriptor.

dup2

Similar to dup.

fchown

Works just like chown.

fchmod

Works just like chmod.

fcntl

Gives access to miscellaneous file characteristics.

ftruncate

Works just like truncate.

ioctl

Controls a device.

link

Creates a hard link.

mknod

Creates a special file.

sync

Schedules all file buffers to be flushed to disk.

truncate

Truncates a file.


12.3.17. Changing a File's Owner and/or Group: chown ()

chown () changes the owner and/or group of a file (Figure 12-21).


[Page 465]

Figure 12-21. Description of the chown () system call.

System Call: int chown (const char* fileName, uid_t ownerId, gid_t groupId)

int lchown (const char* fileName, uid_t ownerId, gid_t groupId)

int fchown (int fd, uid_t ownerId, gid_t groupId)

chown () causes the owner and group IDs of fileName to be changed to ownerId and groupId, respectively. A value of -1 in a particular field means that its associated value should remain unchanged.

Only a super-user can change the ownership of a file, and a user may change the group only to another group that he/she is a member of. If fileName is a symbolic link, the owner and group of the link are changed instead of the file that the link is referencing.

fchown () is just like chown () except that it takes an open descriptor as an argument instead of a filename.

lchown () changes the ownership of a symbolic link itself rather than the file the link references.

They both return -1 if unsuccessful, and 0 otherwise.


In the following example, I changed the group of the file "test.txt" from "music" to "cs," which has group ID number 62. For more information about group IDs and how to locate them, consult Chapter 14, "System Administration."

$ cat mychown.c                 ...list the file. main () {  int flag;  flag = chown ("test.txt", -1, 62); /* Leave user ID unchanged */  if (flag == -1) perror("mychown.c"); } $ ls -l test.txt              ...examine file before. -rw-r--r--  1 glass     music       3 May 25 11:42 test.txt $ ./mychown                   ...run program. $ ls -l test.txt              ...examine file after. -rw-r--r--  1 glass     cs          3 May 25 11:42 test.txt $ _ 


12.3.18. Changing a File's Permissions: chmod ()

The system call chmod () changes a file's permission flags (Figure 12-22).


[Page 466]

Figure 12-22. Description of the chmod () system call.

System Call: int chmod (const char* fileName, int mode)

int fchmod (int fd, mode_t mode);

chmod () changes the mode of fileName to mode, where mode is usually supplied as an octal number as described in Chapter 3, "GNU Utilities for Nonprogrammers." The "set user ID" and "set group ID" flags have the octal values 4000 and 2000, respectively. To change a file's mode, you must either own it or be a super-user.

fchmod () works just like chmod () except that it takes an open file descriptor as an argument instead of a filename.

They both return -1 if unsuccessful, and 0 otherwise.


In the following example, I changed the permission flags of the file "test.txt" to 600 octal, which corresponds to read and write permission for the owner only:

$ cat mychmod.c                ...list the file. main () {  int flag;  flag = chmod ("test.txt", 0600); /* Use an octal encoding */  if (flag == -1) perror ("mychmod.c"); } $ ls -lG test.txt              ...examine file before. -rw-r--r-- 1 glass             3 May 25 11:42 test.txt $ ./mychmod                    ...run the program. $ ls -lG test.txt              ...examine file after. -rw------- 1 glass             3 May 25 11:42 test.txt $ _ 


12.3.19. Duplicating a File Descriptor: dup ()

The system call dup () allows you to duplicate file descriptors (Figure 12-23).

Figure 12-23. Description of the dup () system call.

System Call: int dup (int oldFd)

int dup2 (int oldFd, int newFd)

dup () finds the smallest free file descriptor entry and points it to the same file as oldFd. dup2 () closes newFd if it's currently active and then points it to the same file as oldFd. In both cases, the original and copied file descriptors share the same file pointer and access mode.

They both return the index of the new file descriptor if successful, and -1 otherwise.



[Page 467]

The shells use dup2 () to perform redirection and piping. For examples that show how this is done, see "Process management" on page 473.

In the following example, I created a file called "test.txt" and wrote to it via four different file descriptors:

  • The first file descriptor was the original descriptor.

  • The second descriptor was a copy of the first, allocated in slot 4.

  • The third descriptor was a copy of the first, allocated in slot 0 that was freed by the close (0) statement (the standard input channel).

  • The fourth descriptor was a copy of descriptor 3, copied over the existing descriptor in slot 2 (the standard error channel).

$ cat mydup.c                 ...list the file. #include <stdio.h> #include <fcntl.h> main () {  int fd1, fd2, fd3;   fd1 = open ("test.txt", 0_RDWR | 0_TRUNC);  printf ("fd1 = %d\n", fd1);  write (fd1, "what's", 6);   fd2 = dup (fd1); /* Make a copy of fd1 */  printf ("fd2 = %d\n", fd2);  write (fd2, " up", 3);   close (0); /* Close standard input */  fd3 = dup (fd1); /* Make another copy of fd1 */  printf ("fd3 = %d\n", fd3);  write (0, " doc", 4);   dup2 (3, 2); /* Duplicate channel 3 to channel 2 */  write (2, "?\n", 2); } $ ./mydup               ...run the program. fd1 = 3 fd2 = 4 fd3 = 0 $ cat test.txt          ...list the output file. what's up doc? $ _ 


12.3.20. File Descriptor Operations: fcntl ()

The system call fcntl () directly controls the settings of the flags associated with a file descriptor (Figure 12-24).


[Page 468]

Figure 12-24. Description of the fcntl () system call.

System Call: int fcntl (int fd, int cmd, int arg)

fcntl () performs the operation encoded by cmd on the file associated with the file descriptor fd. arg is an optional argument for cmd. Here are the most common values of cmd:

VALUE

OPERATION

F_SETFD

Set the close-on-exec flag to the lowest bit of arg (0 or 1).

F_GETFD

Return a number whose lowest bit is 1 if the close-on-exec flag is set, and 0 otherwise.

F_GETFL

Return a number corresponding to the current file status flags and access modes.

F_SETFL

Set the current file status flags to arg.

F_GETOWN

Return the process ID or process group that is currently set to receive SIGIO/SIGURG signals. If the returned value is positive, it refers to a process ID. If it's negative, its absolute value refers to a process group.

F_SETOWN

Set the process ID or process group that should receive SIGIO/SIGURG signals to arg. The encoding scheme is as described for F_GETOWN.


fcntl () returns -1 if unsuccessful.


In the following example, I opened an existing file for writing and overwrote the initial few letters with the phrase "hi there." I then used fcntl () to set the file descriptor's APPEND flag, which instructed it to append all further writes. This caused "guys" to be placed at the end of the file, even though I moved the file position pointer back to the start with lseek ():

$ cat myfcntl.c               ...list the program. #include <stdio.h> #include <fcntl.h> main () {  int fd;   fd = open ("test.txt", O_WRONLY); /* Open file for writing */  write (fd, "hi there\n", 9);  lseek (fd, 0, SEEK_SET); /* Seek to beginning of file */  fcntl (fd, F_SETFL, O_WRONLY | O_APPEND); /* Set APPEND flag */ 
[Page 469]
write (fd, " guys\n", 6); close (fd); } $ cat test.txt ...list the original file. here are the contents of the original file. $ ./myfcntl ...run the program. $ cat test.txt ...list the new contents. hi there the contents of the original file. guys ...note that "guys" is at the end. $ _


12.3.21. Controlling Devices: ioctl ()

Figure 12-25 describes the ioctl () system call.

Figure 12-25. Description of the ioctl () system call.

System Call: int ioctl (int fd, int cmd, int arg)

ioctl () performs the operation encoded by cmd on the file associated with the file descriptor fd. arg is an optional argument for cmd. The valid values of cmd depend on the device that fd refers to, and are typically documented in the manufacturer's operating instructions. I therefore supply no examples for this system call.

ioctl () returns -1 if unsuccessful.


12.3.22. Creating Hard Links: link ()

The system call link () creates a hard link to an existing file (Figure 12-26).

Figure 12-26. Description of the link () system call.

System Call: int link (const char* oldPath, const char* newPath)

link () creates a new label newPath and links it to the same file as the label oldPath. The hard link count of the associated file is incremented by one. If oldPath and newPath reside on different physical devices, a hard link cannot be made and link () fails. For more information about hard links, consult the description of ln in Chapter 4, "GNU Utilities for Power Users."

link () returns -1 if unsuccessful, and 0 otherwise.



[Page 470]

In the following example, I created the filename "another.txt" and linked it to the file referenced by the existing name "original.txt". I then demonstrated that both labels were linked to the same file.

$ cat mylink.c                   ...list the program. main () {  link ("original.txt", "another.txt"); } $ cat original.txt               ...list original file. this is a file. $ ls -lG original.txt another.txt    ...examine files before. another.txt not found -rw-r--r--  1 glass          16 May 25 12:18 original.txt $ ./mylink                           ...run the program. $ ls -lG original.txt another.txt    ...examine files after. -rw-r--r--  2 glass          16 May 25 12:18 another.txt -rw-r--r--  2 glass          16 May 25 12:18 original.txt $ cat >> another.txt                 ...alter "another.txt". hi ^D $ ls -lG original.txt another.txt    ...both labels reflect change. -rw-r--r--  2 glass          20 May 25 12:19 another.txt -rw-r--r--  2 glass          20 May 25 12:19 original.txt $ rm original.txt              ...remove original label. $ ls -lG original.txt another.txt    ...examine labels. original.txt not found -rw-r--r--  1 glass          20 May 25 12:19 another.txt $ cat another.txt       ...list contents via other label. this is a file. hi $ _ 


12.3.23. Creating Special Files: mknod (), mkdir (), and mkfifo ()

The system call mknod () allows you to create a special file, mkdir () allows you to create a directory, and they work as described in Figure 12-27.


[Page 471]

Figure 12-27. Description of the mknod () and mkdir () system calls.

System Call: int mknod (const char* fileName, mode_t type, dev_t device)

int mkdir (const char* fileName, mode_t mode)

mknod () creates a new regular, directory, or special file called fileName whose type can be one of the following:

VALUE

MEANING

S_IFDIR

directory

S_IFCHR

character-oriented special file

S_IFBLK

block-oriented special file

S_IFREG

regular file

S_IFIFO

named pipe


Only a super-user can use mknod () to create directories or special files.

mkdir () creates a directory with permission setting created by a logical AND of mode with the process's current umask setting.

It is now typical to use the mkdir () system call to create directories and mkfifo () (see below) to make named pipes rather than mknod ().

Both mknod () and mkdir () return -1 if unsuccessful, and 0 otherwise.


While it is possible to create a named pipe with mknod (), Linux provides the more specialized library function mkfifo () for this purpose (Figure 12-28).

Figure 12-28. Description of the mkfifo () library function.

Library Function: int mkfifo (const char* fileName, mode_t mode)

mkfifo () creates a named pipe called fileName with permission setting created by a logical AND of mode with the process's current umask setting.


12.3.24. Flushing the File System Buffer: sync ()

The system call sync () flushes the file system buffers (Figure 12-29).


[Page 472]

Figure 12-29. Description of the sync () system call.

System Call: void sync ()

sync () schedules all of the file system buffers to be written to disk. For more information on the buffer system, consult Chapter 13, "Linux Internals." sync () should be performed by any programs that bypass the file system buffers and examine the raw file system.

sync () always succeeds.


12.3.25. Truncating a File: truncate ()

The system call truncate () sets the length of a file (Figure 12-30).

Figure 12-30. Description of the truncate () system call.

System Call: int truncate (const char* fileName, off_t length)

int ftruncate (int fd, off_t length)

truncate () sets the length of the file fileName to be length bytes. If the file is longer than length, it is truncated. If it is shorter than length, it is padded with ASCII nulls.

ftruncate () works just like truncate () except that it takes an open file descriptor as an argument instead of a filename.

They both return -1 if unsuccessful, and 0 otherwise.


In the following example, I set the length of two files to 10 bytes; one of the files was originally shorter than that, and the other was longer:

$ cat truncate.c                 ...list the program. main () {  truncate ("file1.txt", 10);  truncate ("file2.txt", 10); } $ cat file1.txt                  ...list "file1.txt". short $ cat file2.txt                  ...list "file2.txt". 
[Page 473]
long file with lots of letters $ ls -lG file*.txt ...examine both files. -rw-r--r-- 1 glass 6 May 25 12:16 file1.txt -rw-r--r-- 1 glass 32 May 25 12:17 file2.txt $ ./truncate ...run the program. $ ls -lG file*.txt ...examine both files again. -rw-r--r-- 1 glass 10 May 25 12:16 file1.txt -rw-r--r-- 1 glass 10 May 25 12:17 file2.txt $ cat file1.txt ..."file1.txt" is longer. short $ cat file2.txt ..."file2.txt" is shorter. long file $ _





Linux for Programmers and Users
Linux for Programmers and Users
ISBN: 0131857487
EAN: 2147483647
Year: 2007
Pages: 339

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net