B.2 stat

Using open and read, you can extract the contents of a file. But how about other information? For instance, invoking ls -l displays, for the files in the current directory, such information as the file size, the last modification time, permissions, and the owner.

The stat call obtains this information about a file. Call stat with the path to the file you're interested in and a pointer to a variable of type struct stat. If the call to stat is successful, it returns 0 and fills in the fields of the structure with information about that file; otherwise, it returns -1.

These are the most useful fields in struct stat:

·         st_mode contains the file's access permissions. File permissions are explained in Section 10.3, "File System Permissions."

·         In addition to the access permissions, the st_mode field encodes the type of the file in higher-order bits. See the text immediately following this bulleted list for instructions on decoding this information.

·         st_uid and st_gid contain the IDs of the user and group, respectively, to which the file belongs. User and group IDs are described in Section 10.1, "Users and Groups."

·         st_size contains the file size, in bytes.

·         st_atime contains the time when this file was last accessed (read or written).

·         st_mtime contains the time when this file was last modified.

These macros check the value of the st_mode field value to figure out what kind of file you've invoked stat on. A macro evaluates to true if the file is of that type.

S_ISBLK (mode )

block device

S_ISCHR (mode )

character device

S_ISDIR (mode )

directory

S_ISFIFO (mode )

fifo (named pipe)

S_ISLNK (mode )

symbolic link

S_ISREG (mode )

regular file

S_ISSOCK (mode )

socket

The st_dev field contains the major and minor device number of the hardware device on which this file resides. Device numbers are discussed in Chapter 6. The major device number is shifted left 8 bits; the minor device number occupies the least significant 8 bits. The st_ino field contains the inode number of this file. This locates the file in the file system.

If you call stat on a symbolic link, stat follows the link and you can obtain the information about the file that the link points to, not about the symbolic link itself. This implies that S_ISLNK will never be true for the result of stat. Use the lstat function if you don't want to follow symbolic links; this function obtains information about the link itself rather than the link's target. If you call lstat on a file that isn't a symbolic link, it is equivalent to stat. Calling stat on a broken link (a link that points to a nonexistent or inaccessible target) results in an error, while calling lstat on such a link does not.

If you already have a file open for reading or writing, call fstat instead of stat. This takes a file descriptor as its first argument instead of a path.

Listing B.6 presents a function that allocates a buffer large enough to hold the contents of a file and then reads the file into the buffer. The function uses fstat to determine the size of the buffer that it needs to allocate and also to check that the file is indeed a regular file.

Listing B.6 (read-file.c) Read a File into a Buffer
#include <fcntl.h> 
#include <stdio.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 
 
/* Read the contents of FILENAME into a newly allocated buffer. The 
   size of the buffer is stored in *LENGTH. Returns the buffer, which 
   the caller must free. If FILENAME doesn't correspond to a regular 
   file, returns NULL.  */ 
 
char* read_file (const char* filename, size_t* length) 
{
   int fd; 
   struct stat file_info; 
   char* buffer; 
 
   /* Open the file.  */ 
   fd = open (filename, O_RDONLY); 
 
   /* Get information about the file.  */ 
   fstat (fd, &file_info); 
   *length = file_info.st_size; 
   /* Make sure the file is an ordinary file.  */ 
   if (!S_ISREG (file_info.st_mode)) {
      /* It's not, so give up.  */ 
      close (fd); 
      return NULL; 
   } 
  /* Allocate a buffer large enough to hold the file's contents.  */ 
  buffer = (char*) malloc (*length); 
  /* Read the file into the buffer.  */ 
  read (fd, buffer, *length); 
 
  /* Finish up.  */ 
  close (fd); 
  return buffer; 
}