You'll be extending a loop-back NFS file server, labeled CCFS in the diagram below. CCFS mounts itself on a sub-directory of /classfs, and arranges for the NFS client code in the FreeBSD kernel to send it NFS v3 RPCs. Thus CCFS acts as a server only for processes on the local host. CCFS serves a file system stored in a network block server called blockdb. blockdb can run on any host, and can serve blocks to multiple instances of CCFS running on multiple client hosts.
----------------- ------------ | | | | | App CCFS--|----|--blockdb | | | | | | | |---------------| ------------ | | | | | Kernel | | NFS Client | | | -----------------
This architecture is appealing because (in principle) it shouldn't slow down as you add client hosts. Most of the complexity is in the per-client CCFS server, so new clients make use of their own CPUs rather than competing with existing clients for the server's CPU. The blockdb is shared, but hopefully it's simple and fast enough to handle a large number of clients. In contrast, a conventional NFS server is pretty complex (it has a complete file system implementation) so it's more likely to be a bottleneck when shared by many NFS clients. The only fly in the ointment is that multiple CCFS's sharing a single file system stored in a blockdb would need a locking protocol to avoid inconsistent updates -- this will be your job in the next few labs.
% wget http://pdos.lcs.mit.edu.ezproxy.canberra.edu.au/6.824/labs/fs-lab-1.tgz % tar xzvf fs-lab-1.tgz % cd fs-lab-1 % ./setup % ./configure --with-dmalloc --with-classfs=/u/6.824/classfs-0.0 i386-freebsd % gmakeNow you should start the block server on one of the class machines. You'll need to choose a UDP port number that other students aren't using. If, for example, you choose to run the block server on host blood on port 3772, you should type this on blood:
blood% ./blockdbd 3772 &At this point you can start up the file server. You need to tell it a directory under /classfs on which to mount itself, and tell it the host name and port number of the block server. By default, the file server initializes a new file system in the block server, chooses a new random file handle for the root directory, and prints out the root handle. You can optionally specify an existing root handle. (The block server keeps its state in memory, so you can't use a root handle after you re-start blockdb.) Here's how to start the file server and tell it to mount on /classfs/dir:
anguish% ./ccfs dir blood 3772 & root file handle: 2d1b68f779135270 anguish% echo hi > /classfs/dir/foo anguish% ls -lt /classfs/dir/. total 0 -rw-rw-r-- 1 root wheel 3 Feb 20 14:32 foo anguish%Though you don't need to do it for this assignment, you can now run a second copy of your file server with the same root file handle, either under a different directory name on anguish, or on a different host:
suffering% ./ccfs dir blood 3772 2d1b68f779135270 & suffering% cat /classfs/dir/foo hi suffering%The interesting part of the CCFS source is in fs.C. It's a partial NFS file server, and is missing the following features:
If you want to run CCFS on your own machine, you'll need the source to SFS and libasync, and the source to classfs.
An i-node block's key is the file handle, and its content is an fattr3 structure (the NFS v3 file attributes). Use get_fh() and put_fh() to read and write i-node blocks, and remove_fh() to delete them from the block server.
A regular file has zero or more 8192-byte data blocks. The size field of the i-node fattr3 structure determines the real length of a file. The key of a file's i'th data block is the file handle with i appended. Use get_data() and put_data() to read and write file data blocks.
A directory has zero or more directory entry blocks. Each directory entry block contains information about one file name in the directory: an "allocated" flag, the file's handle, and the file's name. The key of a directory's i'th entry is the directory's file handle with i appended. Use pack_dirent() and unpack_dirent() to format and parse directory entry blocks, and get_data() and read_data() to read and write them. The number of entries in a directory is the number of consecutive directory entries that exist. For this reason you'll want to clear an entry's allocated flag rather than deleting the entry from the block server.
You don't need to implement sophisticated error handling. If something unexpected goes wrong, send an error reply to the RPC.
Please modify only fs.C and fs.h. Please don't modify the block server, since we will test your file server against our own copy of the block server.
anguish% ./cdtest.pl /classfs/dir ... Passed all tests!If cdtest.pl exits without printing "Passed all tests!", then it thinks something is wrong with your file server. For example, if you run cdtest.pl on the fs.C we give you, you'll probably see an error message like this:
unlink xcd-45429-0.226790197675466 failed: Protocol not supportedYou are not required to fix any of the other missing features of CCFS listed above; you need only add a REMOVE RPC implementation.
You'll probably want to start by understanding a few of the RPC handler procedures in fs.C, particularly nfs3_lookup() and nfs3_create(). This code will show you how to get hold of RPC arguments and how to send replies. You can look at nfs3_setattr_cb2() for an example of how to send a reply from an RPC with the same reply type as NFS3_REMOVE.
You can find descriptions of the NFS v3 protocol in RFC1813 and the book NFS Illustrated by Brent Callaghan.
CCFS is written using libasync, whose source you can find in /u/6.824/sfs1 or at www.fs.net. CCFS uses NFS RPC definitions from nfs3_prot.x or in /usr/local/include/sfs-0.7.2/nfs3_prot.x. The output of the RPC compiler is in nfs3_prot.h.
The most powerful debugging tool in the world is printf().
You may be able to find memory allocation errors with dmalloc by typing this before running ccfs:
% setenv DMALLOC_OPTIONS debug=0x24f41d03,inter=100
You can see a trace of all RPC requests that your server receives, and its responses, by setting the ASRV_TRACE environment variable, like this:
env ASRV_TRACE=10 ./ccfs ...
gmake dist
. Copy this file to
~/handin/fs-lab-1-handin.tgz. We will use the first
copy of the file that we can find after the deadline.