We provide you with skeleton code for both the yfs and extent server modules above.
The yfs module implements the core file system logic. This module runs as a single process called yfs_client that supports a mountpoint on the local host. The code skeleton for this module consists of two pieces:
The Extent server acts as a centralized storage location for all the data representing your filesystem, much like a hard disk would. In later labs, you will serve the same file system contents on multiple hosts, each running its own yfs_client. The only way they can share data is by reading and writing the extent server. The extent server code skeleton consists of two pieces:
% cd ~/yfs % wget -nc http://www.news.cs.nyu.edu/~jinyang/fa08/labs/yfs-lab2.tgz % rsync -av l1/* l2 % tar xzvf yfs-lab2.tgz % makeTo use YFS in this lab, you'll need start the extent server and yfs_client(s). If you are using the class machines, choose a port number that other students aren't using. For example, to run the extent server on port 3772, type this:
% cd yfs/l2 % ./extent_server 3772 &
Next, start the yfs_client process using three parameters: a unique mountpoint (that other students aren't using), the port number for the extent server, and the port number for the running lock server, which is not used in this lab. The mountpoint must be an empty directory that already exists. To start the yfs_client using mountpoint ./myfs and extent_server that listens on port 3772, type this:
% cd yfs/l2 % mkdir myfs % ./yfs_client ./myfs 3772 3762 &
We provide you with the script start.sh to automatically start extent server and yfs_client and stop.sh to kill previously started processes. Actually, start.sh starts two yfs_clients with ./yfs1 and ./yfs2 mountpoints respectively. (In this lab, you should only be concerned with one yfs_client. The next lab will use both yfs_clients.) Thus, instead of typing in commands manually as before, you can simply do:
% cd ~/lab-2 % ./start.sh % ./test-lab-2.pl ./yfs1 % ./stop.sh
The skeleton code implements only the GETATTR and STATFS operations, and so the file system you just mounted will not be useful at all. However, once you finish this lab, you should be able to run the Lab 2 tests successfully, which tests creating empty files, looking up names in a directory, and listing directory contents. Note: testing this lab on the command line using commands like touch will not work until you implement the SETATTR operation, which is not required until the next lab. For now, you should do your testing via the creat/open, lookup, and readdir system calls in a language like Perl, or simply use the provided test script.
When using FUSE on Linux, as in the official class programming environment (see here), files are created via the MKNOD operation. On other operating systems, FUSE uses the CREATE operation. You are encouraged, but not required, to structure your code such that either operation will work. However, we will only be testing your code on Linux, which means the MKNOD operation must work. Please note that under Linux kernel 2.6.15 and newer, (such as on the class machines) if both MKNOD and CREATE are implemented and passed as pointers to the fuseserver_oper structure, then CREATE will be used by FUSE. (So, if you pass a pointer to a CREATE hook, make sure it is implemented and works well.)
As before, if your server passes our tester on the official class programming environment, you are done. If you have questions about whether you have to implement specific pieces of file system functionality, then you should be guided by the tester: if you can pass the tests without implementing something, then you do not have to implement it. For example, you don't need to implement the exclusive create semantics of the CREATE/MKNOD operation. You may modify or add any files you like, other than the tester script and the rpc library.
Lab2 tester is the test-lab-2.pl script. Run it with your YFS mountpoint as the argument. Here's what a successful run of test-lab-2.pl looks like:
% ./test-lab-2.pl ./yfs1 create file-yyuvjztagkprvmxjnzrbczmvmfhtyxhwloulhggy-18674-0 create file-hcmaxnljdgbpirprwtuxobeforippbndpjtcxywf-18674-1 ... Passed all tests!The tester creates lots of files with names like file-XXX-YYY-Z and checks that they appear in directory listings.
If test-lab-2.pl exits without printing "Passed all tests!", then it thinks something is wrong with your file server. For example, if you run test-lab-2.pl on the skeleton code we give you, you'll probably see an error message like this:
test-lab-2: cannot create /tmp/b/file-ddscdywqxzozdoabhztxexkvpaazvtmrmmvcoayp-21501-0 : Function not implemented
This error message appears because you have not yet assigned a method to handle the CREATE/MKNOD operation with FUSE. See the main() method in fuse.cc for examples on how to make this assignment.
YFS should name all files and directories with a unique identifier (much like the i-node number in an on-disk UNIX file system). We have defined such an 64-bit identifier (called inum) in yfs_client.h. Since FUSE accesses each file and directory in the file system using a unique 32-bit identifier, we suggest you use the least significant 32-bits of inum as the corresponding FUSE identifier.
When creating a new file or directory, you must assign a unique inum. The easiest thing to do is to pick a number at random, hoping that it will indeed be unique. (What's the collision probability as the number of files and directories grows?)
YFS needs to tell whether a particular inum refers to a file or a directory. To do this, you should ensure that any 32-bit FUSE identifier (i.e. the lower 32-bit of inum) for a file has the most significant bit equal to one; likewise, that bit for a directory should be set to zero. The provided method yfs_client::isfile assumes this property holds for inum.
Next, you must choose the format for storing and retrieving file system meta-data (i.e. file/directory attributes and directory contents). You do not need to store or retrieve file contents in this lab yet. A file or dir's attribute contains information such as the file's length, modification times. A directory's content contains a list of name to inum mappings. Thus, resolving a file's inum involves a series of lookups starting from the file system root (The root dir has a well-known FUSE id of 0x000000001).
It is convenient to store and retrieve the entire dir content (or file content, for later labs) in the extent server using the file/dir's inum as the key to the extent server's put(key,value) and get(key) functions. However, since YFS must also be able to retrieve the file attribute information based on the file/dir's inum, we ask you to implement a separate function, called extent_server::getattr(key), to retrieve a file/dir's attribute based on its inum. The attribute consists of the file size, last modification time (mtime), change time (ctime), and last access time (atime). Tracking this data in the extent server should be straightforward in the handlers for the put(key,value) and get(key) RPCs. Wikipedia has a succinct description of when these three times should be updated.
For these labs, you will be interfacing with FUSE via its "lowlevel" API. We have provided you with lots of code in the main() method of fuse.cc that handles much of the lowlevel nastiness. You will, however, have to add a method handler for each new operation you'd like to support; you can do this by assigning method pointers to the appropriate fields of fuseserver_oper. We have already done this for getattr, statfs, and readdir, but you will need to add handlers for mknod and lookup. You should study fuse_lowlevel.h for the what these method definitions must be, and what methods they should use to send their information back to FUSE. Study our getattr implementation to get a sense of how a full FUSE operation handler works, and how it communicates its results and errors back to FUSE.
Sending back directory information for the READDIR operation is a bit tricky, so we've provided you with much of the necessary code in the dirbuf_add, reply_buf_limited, and fuseserver_readdir methods. All that's left for you to do for READDIR in fuse.cc is to get the directory listing from your yfs_client, and add it to the b data structure using dirbuf_add.
Though you are free to choose any inumber identifier you like for newly created files, FUSE assumes that the inumber for the root directory is 0x00000001. Thus, you'll need to ensure that when YFS mounts, it is ready to export an empty directory stored under that inumber.
The start.sh scripts redirects the STDOUT and STDERR of the different processes to different files in the current working directory. For example, any output you make from fuse.cc will be written to fuse2yfs1.out. Thus, you should look at these files for any debug information you print out in your code.
You can get FUSE to print out the requests and results of operations it passes to your file system. To turn this on, add the following line of code to the main() function of fuse.cc, just before the assignment of mountpoint into fuse_argv:
fuse_argv[fuse_argc++] = "-d";
See the lab overview for general hacking and debugging tips.
If you wish to test your code with only some of the FUSE hooks implemented, be advised that FUSE may implicitly try to call other hooks. For example, FUSE calls LOOKUP when mounting the file system, so you may want to implement that first. You can use the -d flag (see above) to see exactly what hooks are called at every step.
Tar your l2/ files with the following commands:
cd ~/yfs/l2 make clean cd .. tar czvf yfs-lab2.tgz l2/Go to submit site to upload yfs-lab2.tgz