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 calledlsdir-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 thesort
program to sort the long
form output (with the file name at the end of each line)?
Modified: 13 March 2008