Systems Programming

Laboratory: Long Directory Listings — stat()

This labratory continues our work on directories. Last time, we saw how to get the list of files in a directory by using the opendir(), readdir(), and closedir() system calls, and we used that to duplicate the most basic functionality of the ls command.

Today, we will use ls -l as our inspiration to explore the structure of a Unix file system, another system call, and the associated programming skills.

We use ls -l when we want to know more about the files in a directory, e.g., when the files were last updated and what their permissions are. In Unix systems, information about a file's size and permissions is not stored in the directory, nor is it stored with the file contents. Information about a file, its metadata, is stored in a structure on the disk called an inode, for index node. There are three system calls for getting this information:

int stat(char *path, struct stat *buf);
int lstat(char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
We won't be using fstat() now.

These functions return 0 for success or -1 for failure. They set errno in the case of failure. If they succeed, they store the inode information in the structure passed in as buf. This is a very common convention in C.

Note that this means that you must allocate the storage for the struct stat. It could be statically allocated, on the stack, or malloc()ed, but it must exist before you make the call.

Before we get too far along, create a directory in your account for today's lab. Then run ls -l on various files and directories and look carefully at the output. Do you know what all that information is telling you? By now you know about the file name, the permissions, and the user name. There is a file size, too, in bytes. Then there is the last modification time and the file name.

Now create a symbolic link:

ln -s target link_name
The link_name is anything you like. The target is supposed to be an existing file or directory, but can also be anything you want. Try doing this for an existing target and a non-existent target. What does ls -l tell you about this file (look particularly at the size).

Now run ls -l on /dev.


Now we're ready to start coding! You may assume the structure is defined like this:


       struct stat {
           dev_t     st_dev;     /* dev for this FS */
	   ino_t     st_ino;     /* i-node number */
  	   mode_t    st_mode;    /* mode bits */
	   nlink_t   st_nlink;   /* # hard links */
	   uid_t     st_uid;     /* user */
	   gid_t     st_gid;     /* group */
	   dev_t     st_rdev;    /* if it's a device file */
	   off_t     st_size;    /* in bytes */
	   time_t    st_atime;   /* access */
	   time_t    st_mtime;   /* modify */
	   time_t    st_ctime;   /* i-node update */
	   blksize_t st_blksize; /* optimal I/O size */
	   blkcnt_t  st_blocks;  /* might have holes */
       };
      
(In fact, each system will add other fields and may rearrange the fields.)

For ls -l functionality, we are concerned with, in order, the mode bits, the number of blocks, the owner, the group, the file size (in bytes), the last modify time, and finally the file name.

Exercise 1

Write a program called lsdir-l that will output a long format listing of the current directory. (Allow for the possibility that you may eventually allow an argument.) Figure out how to get and present each field separately. The number of links, for example is easy, so maybe do that first. Then add the mode bits, making simplifiying assumptions if you need to (the actual procedure is rather involved). Then if you have time, try to figure out how to get the user and group names, etc.

The include files associated with the stat() family of system calls (stat.h in particular) define handy macros and masks for testing and setting mode bits. Section 3.5 of Rochkind describes them, and you can find them on cs in /usr/include/linux/stat.h. As you're working through this exercise, please refrain from looking at the sample code in Rochkind until you've made a stab at each piece.

Exercise 2

How can you call the sort program to sort the long form output (with the file name at the end of each line)?


Author: Mark A. Sheldon
Modified: 13 March 2008