Obtaining File System Information

The UNIX kernel maintains considerable detail about every file system object. This is true whether the object is a file, a directory, a special device node, or a named pipe. Whatever the file system object is, several properties are tracked and maintained for it.

The stat(2) and fstat(2) functions return information about file system objects in a structure named stat. The synopsis for the stat structure is as follows:

struct stat {
    dev_t     st_dev;               /* device */
    ino_t     st_ino;               /* inode */
    mode_t    st_mode;              /* protection */
    nlink_t   st_nlink;             /* number of hard links */
    uid_t     st_uid;               /* user ID of owner */
    gid_t     st_gid;               /* group ID of owner */
    dev_t     st_rdev;              /* device type (if inode dev) */
#ifndef _POSIX_SOURCE
    struct timespec st_atimespec;   /* time of last access */
    struct timespec st_mtimespec;   /* time of last data modification */
    struct timespec st_ctimespec;   /* time of last file status change */
#else
    time_t    st_atime;             /* time of last access */
    long      st_atimensec;         /* nsec of last access */
    time_t    st_mtime;             /* time of last data modification */
    long      st_mtimensec;         /* nsec of last data modification */
    time_t    st_ctime;             /* time of last file status change */
    long      st_ctimensec;         /* nsec of last file status change */
#endif
    off_t     st_size;              /* file size, in bytes */
    int64_t   st_blocks;            /* blocks allocated for file */
    u_int32_t st_blksize;           /* optimal blocksize for I/O */
    u_int32_t st_flags;             /* user defined flags for file */
    u_int32_t st_gen;               /* file generation number */
} ;

#ifndef _POSIX_SOURCE
#define st_atime st_atimespec.tv_sec
#define st_mtime st_mtimespec.tv_sec
#define st_ctime st_ctimespec.tv_sec
#endif

struct timespec {
    time_t    tv_sec;               /* seconds */
    long      tv_nsec;              /* and nanoseconds */
} ;
					

The definition shown is the one documented by FreeBSD Release 3.4. This definition shows the difference that exists, depending on whether or not POSIX standards are being used. When POSIX standards are not in use, the members st_atimespec, st_mtimespec, and st_ctimespec are defined in terms of the structure timespec. Then macros are used to equate, for example, the name st_atime to st_atimespec.

When POSIX standards are used, the st_atime member is defined in terms of the C type time_t, as has been the traditional type for this member. If finer-grained time information is required, the member st_atimensec can be consulted when compiling to POSIX standards.

Note

SGI's IRIX 6.5 describes the access, modified, and create date/time structure members in terms of the C data type timespec_t. Many other UNIX systems such as HPUX 10 and 11, Solaris 8, and UnixWare 7 describe the stat members in simple terms of the C data type time_t.


The stat(2) Function

The stat(2) function allows the programmer to supply the pathname of the file system object and retrieve file system properties. The function synopsis for stat(2) is as follows:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

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

The stat(2) function returns 0 when it is successful. When the call fails, -1 is returned with an error code placed in the global variable errno.

You can use the following code to obtain information about the executable file bin/a.out:

struct stat sbuf;

if ( stat("bin/a.out",&sbuf) == -1 ) {
    fprintf(stderr,"%s: stat(2)
",strerror(errno));
    abort();
}

The code shows how the properties are returned to the structure sbuf for the file a.out. The programmer can then access the members of variable sbuf to work with the file properties.

Warning

stat(2) and fstat(2) under SGI IRIX 6.5 are capable of returning EINTR if a signal is caught.

Table 6.1 reviews the stat structure members in detail, complete with units.


Table 6.1. The stat Structure
Data Type Member Name Description
dev_t st_dev The device number for this file system.
ino_t st_ino The i-node number for this file system entry.
mode_t st_mode File system object permission bits.
nlink_t st_nlink The number of hard links to this file.
uid_t st_uid The uid number of the owning user for this file system object.
gid_t st_gid The gid number of the group for this file system object.
dev_t st_rdev The device type, if the device is an i-node device.
time_t st_atime The time this file system object was last accessed.
long st_atimensec The last access time in nanoseconds.
time_t st_mtime The time this file system object was last modified.
long st_mtimensec The time of last modification in nanoseconds.
time_t st_ctime The time of creation for this file system object.
long st_ctimensec The time of creation in nanoseconds.
off_t st_size The total size in bytes of this file system object.
int64_t st_blocks The number of blocks allocated to this file system object.
u_int32_t st_blksize The block size for file system I/O.
u_int32_t st_flags User-defined flags for file. This appears to be a FreeBSD extension.
u_int32_t st_gen File generation number. This appears to be a FreeBSD extension.

The HPUX operating system also includes another useful piece of information:

struct stat {
    …
    uint       st_acl:1;            /* Set if the file has optional */
                                    /* access control list entries */
                                    /* HFS File Systems only */
} ;

When set, the flag bit member st_acl indicates that access control list (ACL) entries exist for that file. Only certain types of file systems, including HP's HFS file system, support access control list entries.

The fstat(2) Function

There are situations where it is necessary to obtain properties of the file system object that is open on a file descriptor. In this situation, you may not have the pathname for the object. The fstat(2) function solves this problem by allowing you to retrieve properties for the object open on the file descriptor.

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int fstat(int fd, struct stat *sb);

For a file that is open on file descriptor fd, the following example shows how fstat(2) is used:

int fd;                        /* Open file descriptor */
struct stat sbuf;

if ( fstat(fd,&sbuf) == -1 ) {
    fprintf(stderr,"%s: fstat(2)
",strerror(errno));
    abort();
}

In this example, the structure sbuf receives all of the file properties for the object open on file unit fd.

Working with File Properties

In order to put the stat(2) and fstat(2) functions through their paces, a simple C++ object will be created to allow a few simple tests to be performed. The system call functions will be encapsulated in the object and then tested by calling upon the object methods. Listing 6.1 shows the C++ program.

Code Listing 6.1. stat.cc—The Stat Class and Test Program
  1:   // stat.cc
  2:  
  3:   #include <iostream.h>
  4:   #include <string.h>
  5:   #include <errno.h>
  6:   #include <sys/types.h>
  7:   #include <sys/stat.h>
  8:  
  9:  
 10:  ////////////////////////////////////////////////////////////
 11:  // Simple Stat object :
 12:  ////////////////////////////////////////////////////////////
 13: 
 14:  class Stat : public stat {
 15: 
 16:  private:
 17:      char        *path;          // Pathname
 18:      int         fd;             // File descriptor
 19: 
 20:  public:
 21:      Stat() {  path = 0; fd = -1; }
 22:      ~Stat();
 23: 
 24:      Stat & examine(const char *pathname);
 25:      Stat & examine(int fd);
 26:      int operator==(Stat &o);
 27: 
 28:      friend ostream & operator<<(ostream &out,Stat &o);
 29:  } ;
 30: 
 31:  ////////////////////////////////////////////////////////////
 32:  // Destructor :
 33:  ////////////////////////////////////////////////////////////
 34: 
 35:  Stat::~Stat() {
 36:      if ( path )                 // Path allocated?
 37:          delete path;            // Yes, release string
 38:  }
 39: 
 40:  ////////////////////////////////////////////////////////////
 41:  // stat(2) on pathname :
 42:  ////////////////////////////////////////////////////////////
 43: 
 44:  Stat &
 45:  Stat::examine(const char *pathname) {
 46:  
 47:      if ( path )                 // Is there a prior path?
 48:          delete path;            // Yes, release string
 49: 
 50:      path = strdup(pathname);    // Create a new string
 51:      fd = -1;                    // Not using fd here
 52:
 53:      // Obtain stat info :
 54:      if ( ::stat(path,this) == -1 )
 55:          throw errno;            // Oops- error
 56: 
 57:      return *this;               // Successful
 58:  }
 59: 
 60:  ////////////////////////////////////////////////////////////
 61:  // Perform fstat(2) on fd :
 62:  ////////////////////////////////////////////////////////////
 63: 
 64:  Stat &
 65:  Stat::examine(int fd) {
 66: 
 67:      if ( path ) {                // Is there a path?
 68:          delete path;            // Yes, release string
 69:          path = 0;               // Mark as gone
 70:      }
 71: 
 72:      this->fd = fd;              // Save fd
 73: 
 74:      // Obtain stat info :
 75:      if ( ::fstat(fd,this) == -1 )
 76:          throw errno;            // Oops- error
 77: 
 78:      return *this;               // Successful
 79:  }
 80: 
 81:  ////////////////////////////////////////////////////////////
 82:  // This friend function can be called to dump the
 83:  // contents of the stat structure :
 84:  ////////////////////////////////////////////////////////////
 85: 
 86:  ostream &
 87:  operator<<(ostream &out,Stat &o) {
 88: 
 89:      // If there is no information, say so :
 90:      if ( o.fd == -1 && !o.path ) {
 91:          out << "No current information.";
 92:          return out;
 93:      }
 94:  
 95:      // Otherwise, show what sort of stat() info it is:
 96:      if ( o.path )
 97:          cout << "stat(" << o.path << ") { 
";
 98:      else
 99:          cout << "fstat(" << o.fd << ") { 
";
100:
101:     // Dump all other structure members :
102:
103:     cout<< "	st_dev =	" << o.st_dev << ";
"
104:         << "	st_ino =	" << o.st_ino << ";
";
105:
106:     cout.setf(ios::oct,ios::basefield);
107:     cout<< "	st_mode =	" << '0'<< o.st_mode << ";
";
108:
109:     cout.setf(ios::dec,ios::basefield);
110:     cout<< "	st_nlink =	" << o.st_nlink << ";
"
111:         << "	st_uid =	" << o.st_uid << ";
"
112:         << "	st_gid =	" << o.st_gid << ";
"
113:         << "	st_rdev =	" << o.st_rdev << ";
"
114:         << "	st_atime =	" << o.st_atime << ";
"
115:         << "	st_mtime =	" << o.st_mtime << ";
"
116:         << "	st_ctime =	" << o.st_ctime << ";
"
117:         << "	st_size =	" << o.st_size << ";
"
118:         << "	st_blocks =	" << o.st_blocks << ";
"
119:         << "	st_blksize =	" << o.st_blksize << ";
"
120:         << "	st_flags =	" << o.st_flags << ";
"
121:         << "	st_gen = 	" << o.st_gen << ";
"
122:         << "
} ;";
123:
124:     return out;
125: }
126:
127: ////////////////////////////////////////////////////////////
128: // This method tests to see if two file system objects
129: // are the same one :
130: ////////////////////////////////////////////////////////////
131:
132: int
133: Stat::operator==(Stat &o) {
134:
135:     // Does either object lack information?
136:
137:     if ( fd == -1 && !path )
138:         throw EINVAL;               // No information here
139:     if ( o.fd == -1 && !path )
140:         throw EINVAL;               // No information there
141: 
142:     // Now test to see if these are the same objects:
143:    
144:     if ( o.st_dev != st_dev         // Devices match?
145:     ||   o.st_ino != st_ino )       // Inodes match?
146:         return 0;                   // Devices or inodes don't match
147:
148:     return 1;                       // Return TRUE, they are the same
149: }
150:
151: ////////////////////////////////////////////////////////////
152: // Test Main Program :
153: ////////////////////////////////////////////////////////////
154:
155: int
156: main(int argc,char **argv) {
157:     int x;                          // work index
158:     Stat t;                         // stat("./stat")
159:     Stat s;                         // work stat object
160:    
161:     t.examine("./stat");            // Do stat(2)
162:
163:     // Now try all command line arguments :
164:
165:     for ( x=1; x<argc; ++x ) {
166:
167:         try {
168:             s.examine(argv[x]);     // Stat this pathname
169:         }  catch ( int e ) {
170:             // e is errno value :
171:             cerr << strerror(e) << ": stat(2) of "
172:                 << argv[x] << '
';
173:             continue;
174:         }
175:
176:         cout << s << '
';          // Dump stat info
177:
178:         // Test if s is same as t :
179:
180:         cout << "'" << argv[x] << "'is "
181:             << ( s == t ? "same" : "not the same" )
182:             << " file as ./stat
";
183:     }
184:
185:     return 0;
186: }
187:
188: // End stat.cc
						

The program in Listing 6.1 defines the class Stat, beginning at line 14. This class inherits from the stat structure and leaves the stat members exposed for simplicity (note the public keyword in line 14). Two additional private members, path and fd, are declared in lines 17 and 18 for tracking purposes.

Two examine C++ methods are declared in lines 24 and 25 to allow the object to inquire by pathname or by file descriptor. This eventually translates to a call to stat(2) or fstat(2), respectively.

Lines 45–58 declare the implementation of the inquiry by pathname. Line 54 shows the call to stat(2). Note that this method is coded to throw the errno value if an error is returned by stat(2). Lines 64–79 likewise define the implementation of the inquiry by open file descriptor. The fstat(2) call appears in line 75, and again, errno is thrown if an error is returned.

Lines 86–125 define a friend function (see line 28) that allows the class Stat to be sent to cout with the << operator. This provides a simple dump of the stat structure members.

The loop in the main() program in lines 165–183 performs the task of examining every pathname provided on the command line (line 168). Any error is caught in line 169 and reported in lines 171–173. If s.examine(argv[x]) executes successfully, control passes to line 176, where the contents of the object are formatted for output.

The following session shows the program in Listing 6.1 being compiled and tested using the file Makefile:

$ make stat
cc -c -D_POSIX_C_SOURCE=199309L -Wall -fhandle-exceptions stat.cc
cc stat.o -o stat -lstdc++
$ ./stat Makefile
stat(Makefile) {
        st_dev =        196613;
        st_ino =        125953;
        st_mode =       0100644;
        st_nlink =      1;
        st_uid =        1001;
        st_gid =        1001;
        st_rdev =       525400;
        st_atime =      956797796;
        st_mtime =      956723168;
        st_ctime =      956723168;
        st_size =       378;
        st_blocks =     2;
        st_blksize =    8192;
        st_flags =      0;
        st_gen =        0;

} ;
'Makefile'is not the same file as ./stat
$

Notice that, if we verify a few attributes of the file Makefile, they will agree with the output shown:

$ ls -li Makefile
125953 -rw-r--r--  1 me   mygrp 378 Apr 26 00:26 Makefile
$

The file size of 378 bytes matches the value shown for st_size, and the permissions -rw-r--r-- match the lower 3 octal digits of st_mode for permission bits. The -i option of the ls(1) command causes the i-node number to be displayed. It is shown as 125953 and agrees with the st_ino value shown.

Testing Links for the Same File

When the device number in st_dev and the i-node in st_ino match for two different pathnames, this indicates that these are links to the same file. The method int operator==(Stat &o) is defined in the class Stat of Listing 6.1 to allow the user of Stat objects to perform such a comparison test. The method is implemented in lines 132–149.

This class method is tested in the main() program by initially obtaining the stat(2) information on the executable file ./stat in line 161. Then the command-line argument is compared against this in line 181 (note the s == t expression before the ? operator).

In the earlier test run, the message

'Makefile'is not the same file as ./stat

was shown. However, if this program is tested again with new_link as the argument that is linked to ./stat, then the following results are obtained:

$ ln ./stat new_link
$ ./stat new_link
stat(new_link) {
        st_dev =        196613;
        st_ino =        125955;
        st_mode =       0100755;
        st_nlink =      2;
        st_uid =        1001;
        st_gid =        1001;
        st_rdev =       525312;
        st_atime =      956797797;
        st_mtime =      956797797;
        st_ctime =      956798301;
        st_size =       12080;
        st_blocks =     24;
        st_blksize =    8192;
        st_flags =      0;
        st_gen =        0;

} ;
'new_link'is same file as ./stat
$ ls -li new_link stat
125955 -rwxr-xr-x  2 me   mygrp 12080 Apr 26 21:09 new_link
125955 -rwxr-xr-x  2 me   mygrp 12080 Apr 26 21:09 stat
$

After creating a link new_link to ./stat, the program correctly states that the pathname new_link is the same file as ./stat. This is reported from lines 180–182 of the main() program.

Testing for File Type

The st_mode member also holds information about the type of file system object. To determine the object type, use one of the following macros, where m is the st_mode value to be tested. The following tests and macros can be used:

symbolic link S_ISLNK(m)
regular file S_ISREG(m)
directory S_ISDIR(m)
character special device S_ISCHR(m)
block special device S_ISBLK(m)
named pipe (FIFO) S_ISFIFO(m)
socket S_ISSOCK(m)

These macros test the high order bits in the stat structure member st_mode.

The following code shows how a function could report the type of the file system object path that is provided as an argument:

static void
report_type(const char *path) {
    struct stat sbuf;
    char *cp = "?"; 

    if ( stat(path,&sbuf) == -1 ) {
        /* Report stat(2) error */
        fprintf(stderr,"%s: stat(%s)
",
            strerror(errno),path);
        return;
    }

    if ( S_ISDIR(sbuf.st_mode) )
        cp = "directory";
    else if ( S_ISREG(sbuf.st_mode) )
        cp = "regular file";
    else if ( S_ISCHR(sbuf.st_mode) )
        cp = "character raw device";
    else if ( S_ISBLK(sbuf.st_mode) )
        cp = "block raw device";
    else if ( S_ISFIFO(sbuf.st_mode) )
        cp = "named pipe (FIFO)";
    else if ( S_ISSOCK(sbuf.st_mode) )
        cp = "UNIX socket";
    else if ( S_ISLNK(sbuf.st_mode) )
        cp = "symbolic link";

    printf("Path %s is a %s
",path,cp);
}

This example shows how the stat structure member st_mode is used in each of the test macro calls.

Modification, Access, and Creation Times

The time values st_atime, st_mtime, and st_ctime are sometimes valuable assets to the programmer. Most of the time, the value st_mtime is examined, which represents the last modification for the object. However, the time of last access, st_atime, can be extremely useful if you need to see if the object has been recently accessed. The creation time, st_ctime, indicates when the object was created. The data type time_t is discussed in Chapter 11, "UNIX Date and Time Facilities."

Note

Calling stat(2) or fstat(2) to query a file system object's properties does not alter its date and time accessed.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.174.55