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:
You will need to merge your solution to Lab 1 with the new code we have supplied for Lab 2. To do this, first commit your solution to Lab 1:
% cd lab % git commit -am 'my solution to lab1' Created commit 254dac5: my solution to lab1 3 files changed, 31 insertions(+), 6 deletions(-)(Before you commit, you will need to explicitly
Next, fetch the latest version of the course repository, then create a local branch called lab2 based on our lab2 branch, origin/lab2:
% git pull remote: Generating pack... [...] % git checkout -b lab2 origin/lab2 Branch lab2 set up to track remote branch refs/remotes/origin/lab2. Switched to a new branch "lab2" %
You will now need to merge the changes you made in your lab1 branch into the lab2 branch, as follows:
% git merge lab1In some cases, Git may not be able to figure out how to merge your changes with the new lab assignment (e.g., if you modified some of the code that is changed in the second lab assignment). In that case, the
% cd ~/lab % ./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 ~/lab % mkdir myfs % ./yfs_client ./myfs 3772 3762 &On Linux, you may need to run yfs_client as root, since normal users don't usually have mount permission. On FreeBSD, you can run sysctl vfs.usermount=1 as root, and then you should be able to run yfs_client as a normal user.
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 % ./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.
Your job is to implement the extent server, then implement the LOOKUP, CREATE/MKNOD, and READDIR FUSE operations in YFS. You must store the file system's contents in the extent server, so that in future labs you can share one file system among multiple servers. For our labs, it is okay to implement a simple extent server that stores data only in memory; this means that if you restart it, all the data previously stored will be lost.
On some systems, FUSE uses the MKNOD operation to create files, and on others, it uses CREATE. The two interfaces have slight differences, but in order to spare you the details, we have given you wrappers for both that calls a single common routine called createhelper(). You should implement this routine.
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.
The Lab 2 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.
Note that if you implemented at-most-once RPC correctly, the tests should pass with RPC_LOSSY set to 5 as well.
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 : No such file or directory
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 directory 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.
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, along with skeleton code for the operations you will need to implement. You can find details on what the methods you implement need to do at fuse_lowlevel.h. 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. Every FUSE handler should either pass its successful result using one of the fuse_reply_... routines, or else call fuse_reply_err to report an error.
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 yfs_client1.log. Thus, you should look at these files for any debug information you print out in your code.
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. FUSE prints out to the yfs_client1.log file the requests and results of operations it passes to your file system. You can study this file to see exactly what hooks are called at every step.
% cd ~/ds-class/lab % make clean % cd .. % tar czvf yfs-lab2.tgz lab/That should produce a file called yfs-lab2.tgz in your ds-class/ directory. Go to submit site to upload yfs-lab2.tgz
You will receive full credit if your software passes the same tests we gave you when we run your software on our machines.