CS 537
Programming Assignment 5
Frequently Asked Questions

Last modified Tue May 8 07:00:27 CDT 2007
Q1:
Just how careful do we have to be about race conditions in this project?

A:
Not very. We are deliberately de-emphasizing multi-user operation for this project. You had enough problems dealing with these issues on projects 2, 3, and 4, and if you haven't learned it by now, you probably never will. You may assume there is just one instance of FileTester running, doing one kernel operation at a time.

Q2:
How can I tell whether the disk has already been formatted? Is it an error to format a disk that has already been formatted?

A:
When you call FastDisk.flush, it writes the contents of the simulated disk to a Unix file named DISK. When FastDisk starts up, it looks for this file, and if it finds it, it reads it to initialize the contents of the simulated disk. Otherwise, it creates an "empty" disk in which the first block is filled with null bytes. Thus you can tell whether the disk has been formatted by looking at the start of the free map. In a formatted disk, the first block is not free (since it contains the first block of the free map), so the first bit in the free map is 1, so the first block of the free map is not all zeros.

It is not an error to format an already formatted disk, but of course any data previously written to the disk is lost. The format operation also sets the current working directory to be the root directory.


Q3:
If DISK already exists, and next time we run our program and offer a different disk size from the previous disk size, what should we do ?

A:
If you want to change the size of the disk, type "rm DISK" before you run the program. The Disk constructor reports an error if the DISK file exists and its size does not match the argument to the constructor.

Q4:
Are we supposed to keep the entire directory tree in memory, or is this structure only to be stored on the disk?

A:
The only caching you do should be the free map. For any other block, you should read it from disk each time you need to look at it, and write it back to disk each time you modify it. You should definitely not keep the entire directory tree in memory.

Q5:
I've seen most of the codes in the project 5 desription contains the '>>' operator. Can you explain what does this operator does and give me some examples on the correct usage of this operator?

A:
As you probably know, integers are stored internally as 32-bit binary values in "2's complement" notation. The expression x << y returns the value of x shifted left y places, with zeros shifed into the low-order bits. Because of the way 2's complement works, this has the effect of returning the value x * 2y. Similarly, >> shifts right y places, returning the value x / 2y. The bits shifted in from the left are duplicates of the original high-order bit. Due to the details of the way 2's complement arithmetic works, this gives x / 2y even if x is negative.

You will also need to use the operator &, which performs "bitwise and". Each bit of the result is the logical "and" of corresponding bits of the operands. Thus x & mask has the effect of returning a copy of x in which bit positions corresponing to zero bits in mask are cleared (set to zero) and bit positions where mask has a one are unchanged.

You will find examples of use of these operators in the code for pack and unpackShort.

See also Q6.


Q6:
In the sample unpackShort, why do a bitwise & of a byte with a hex ff? That doesn't seem to do anything at all.

A:
Recall that in Java, the type "byte" means 8-bit signed integer. In an expression, all the operands are first converted to int (or double, or long, as appropriate) before the arithmetic is done. A "negative" byte (one with the high bit set) gets converted to an integer by padding it on the left (high-order) end with 24 one bits. Try the following example and see if you can explain the results.

    byte b = (byte) 128;
    int n = (b<<8) + 1;
    System.out.println(n);

See also Q5.


Q7:
When I try to write disk.read(blockNumber, buffer) I get the compile-time error: No method named "read" was found in type "Disk". How can I call the method FastDisk.read?

A:
Try this: ((FastDisk) disk).read(blockNumber, buffer).

Q8:
In case the name passed to mkdir or create was more than 13 characters should the method return an error or truncate the name?

A:
Truncate the name.

Q9:
What happens if you remove the current working directory?

A:
That should be an error.

Q10:
Is there any special meaning to a trailing slash in a pathname, as in /foo/bar/?

A:
No. Trailing and extra slashes should be ignored. Thus "/foo/bar/", "/foo//bar" and "//foo///bar////" are all equivalent to "/foo/bar"; they all represent an absolute pathname with the two components "foo" and "bar". However, they are not equivalent to "foo/bar", which is a relative pathname.

Q11:
Is it ok for a symlink to specify itself? Doesn't that set things up for a nice infinite loop? (or can a symlink only specify a dir, and not other symlinks?)

A:
A symlink can specify a file, directory, or another symlink. Loops are possible. Try this on Unix or Linux:
    cd /tmp
    ln -s /tmp/bad bad
    cd bad
    rm bad
You will get a message like "bad: Too many levels of symbolic links." or "bad: Number of symbolic links encountered during path name traversal exceeds MAXSYMLINKS." Your implementation of namei should check the depth of recursion of calls to namei and fail if it exceeds some arbitrary bound. Unix generally limits the number of levels of links to 128.

Q12:
What about inodes?

A:
Unlike the real Unix file system, this file system has no inodes. Instead of pointing to an inode, each directory entry points at the file contents, using the block number of the file's unique block. Hard links are not supported, so there should be exactly one directory entry pointing at each file or symlink. The only metadata associated with a file are its name and its type (directory, symlink, or ordinary), both of which are stored in the file's unique directory entry.

Q13:
If read is called on a symlink, should this be an error or should the location that the link points to be output?

A:
In almost all cases, the pathname argument passed to a system call should be agressively dereferenced. That means every symlink encountered should be replaced by the file or directory to which it refers. For example, if you pass “/a/b/c/d” to read and /a/b/c/d denotes a symlink, it must denote an ordinary file, and you should read that file. Moreover, if /a/b is a symlink, it must point to a directory (or another symlink that points to a directory, etc.) and you should look up c in that directory while resolving the path /a/b/c/d.

The only exceptions to this rule are readlink, mkdir, rmdir, create, and delete. Clearly readlink would be useless if it dereferenced a symlink argument. In fact, that is the only difference between read and readlink. However, it should still follow “internal” symlinks as in case of /a/b above.

As for mkdir, rmdir, create, and delete, you can think of the pathname argument as a pair of arguments, a pathname for a directory and a simple name. For example,

    create("/a/b/c/d")
looks for a directory named /a/b/c and inserts a new entry for d. As before, /a/b/c many be either a directory or a symlink designating a directory.