Introduction
Announcements

Schedule
Labs
Assignments
TA office hours

Tests, exam

Topic videos
Some course notes
Extra problems
Lecture recordings

Discussion board

Grades so far

Part A: Problems not requiring C

1. What is the purpose of the "link count" in the inode in the unix filesystem? It is incremented when the file is linked into the filesystem somewhere, and decremented when the file is unlinked; but why does the unix operating system have to track this information?

[solution]


2. Sketch the inode and directory information involved in the following output from an "ls -liR" of the directory which is inode number 10. ('R' is "recursive", and 'i' is "list inode numbers" (which appear in the left column, i.e. the numbers which are 12 through 15 below)).

That is, list the inode numbers and what data you know is in those inodes; and state the contents of the directories involved.

	total 8
	12 drwxr-xr-x  3 ajr  users 4096 Feb 27 10:05 bar
	13 drwxr-xr-x  3 ajr  users 4096 Feb 27 10:05 baz

	./bar:
	total 8
	14 -rw-r--r--  1 ajr  users   20 Feb 27 10:06 gar

	./baz:
	total 8
	15 -rw-r--r--  1 ajr  users   50 Feb 27 10:06 gaz

[solution]


3. Consider the following portion of a unix filesystem hierarchy:

The '.' and '..' entries are not indicated, of course; otherwise, all of the files below "a2" are shown.

a2.pdf, splitexpr.c, wfold.c, and wfold.1 are plain files; a2, handout, starter, soln, and splitexpr are directories.

a) Indicate the link count for each of these files:

handout:

starter:

soln:

wfold.c:

b) Suppose the user is cd'd to the handout directory and types:

	ln a2.pdf example.pdf
Which file(s) will change their link count, and what will the link count(s) be now?

c) Suppose the user is cd'd to the soln directory and types:

	mkdir new
Which file(s) will change their link count, and what will the link count(s) be now?

[solution]


Part B: Problems requiring C

1. Write a simple ls. For a given directory your program outputs all of the file names in that directory, one per line.

An example which only works on "." appears on the unix (not linux) man page and is also at https://www.teach.cs.toronto.edu/~ajr/209/probs/fsys/readdir.c. One defect in that example program, which you must correct, is that it does not check whether the opendir() function call succeeds. If opendir() fails for some reason, it will return a null pointer. In this case, the reason for the failure has been stashed (in "errno"), and you should pass the directory name to perror() which will print a nice error message using this stashed error value.

Also unlike the version on the man page, your version should not work only on the current directory but should instead apply itself in turn to every directory specified on the command line. Recall that you can loop through the argv array like this:

        for (i = 1; i < argc; i++) {
            ... argv[i] ...
        }

[solution]


2. Using opendir() and friends and running stat() on each file in the directory, write a program which displays the size in bytes of the largest file in a directory specified on the command-line (i.e. in argv).

[solution]


3. Write a program which, for each directory specified on the command line, recursively finds the total byte size of the files in that directory and all subdirectories. Your program will contain a function which takes as argument a directory name and returns the total number of bytes in all files and subdirectories recursively. Suppose this function is called totalbytes(). It will contain a readdir() loop which calls stat() on each file. From S_ISDIR(statbuf.st_mode) you will know whether the file is a (sub)directory. If it is a directory, and is not "." or "..", call totalbytes() recursively. Your totalbytes() should opendir() the new directory and go through the directory, possibly calling itself recursively for subdirectories. Each invocation of totalbytes() should call closedir() before returning or you will run out of file streams.

Optional cheat: To avoid having to construct the subdirectory's full path name to pass to opendir() as you descend the hierarchy, totalbytes() could begin with a chdir() (change working directory, like "cd") to the subdirectory, and end with a chdir("..") to restore the previous state. If you avail yourself of this cheat, please notice that your program no longer always works with multiple command-line arguments; that's why I call it a "cheat". (To experience the problem, use multiple arguments which are relative pathnames but do contain multiple directory levels (i.e. contain slashes).)

[solution]


4. Write a program whose argv[1] is a directory name and which finds a pair of hard-linked files within the subdirectory tree.

Example:

% mkdir glop
% ln blop/foo glop/foo
% findlink .
./blop/foo and ./glop/foo are links to the same file
% 


5. Write pwd. The algorithm is:

  1. Find the inode number and major and minor device numbers of the current directory ("."), using lstat() (or stat()).
  2. Do an opendir() of ".." (actually you probably might as well chdir() there first), and find an entry which matches the inode number and major and minor device numbers of the original directory. This name is then the last component of pwd's output. (Optimization: Note that you can use the d_ino entry in the record returned by readdir() to avoid lstat()ing the entry almost all the time, although you still do have to lstat() when the inode number matches, to see if the major and minor device numbers match.)
  3. Repeat this until you find that ".." is the same as ".". [footnote]
Make sure you understand why the above algorithm is valid, and why all of its components are necessary.

Improvements:

  1. The above generates the pathnames in reverse order. Using recursion rather than iteration it's reasonably easy to get them into the desired order. Make sure you don't have multiple DIR*s from opendir() open, though, or you'll have an unnecessary limit to the depth of the directory you can pwd properly. But note that you will have to duplicate the string if you're going to recurse before printing it. Another possibility is to keep the iterative version but end up with a linked list of dynamically-allocated strings, which you then read out in the reverse order to how they were added (which is easy if you just prepend to the top each time).
  2. Get all the formatting of the output right, with no extraneous slash at the end... and with an extraneous slash at the beginning in the case that the cwd is actually the root directory.
  3. Make sure you're calling lstat() (and/or stat()) as little as possible. (It's possible, and probably usual, to use only lstat() for all such calls in pwd.)
  4. Make sure you diagnose the problem of being unable to read directories correctly. E.g. in the shell, cd somewhere you have only 'x' permission but not 'r' permission, and watch /bin/pwd fail, and then make sure yours does it as cleanly.


[footnote] Or that it's the same as "/", but that involves an additional lstat()/stat(), so it's not as good, and it's no easier really.