Due Date: April 29, 2009 (11:59pm). 100 points
Single person project. Do your own work!
In this project, you will add a simplified extended attributes functionality to a RAM disk file system that is provided. While this code is not based directly on any OS implementation, it does share the common concepts from a UNIX file system. The code largely follows the extended attribute definitions shown in the manpages for fsetxattr and for fgetxattr -- not as complete and I give you lengths. The full tarball for the project is available here.
In particular, you are going to be required to implement four functions in the file system implementation:
int fileSetAttr( unsigned int fd, char *name, char *value, unsigned int name_size, unsigned int value_size, unsigned int flags ): This function sets an attribute of name (length of name_size) of a file specified by the descriptor fd to value (length of value_size) given the flags value. The flags values can be XATTR_CREATE, which requires that the attribute not be assigned to the file previously, and XATTR_REPLACE, which requires that the attribute already has been assigned to the file. Your code needs to return an error if the conditions are not consistent with the flags. Otherwise, your code should set the attribute's value (more detail below).
A key function of your code is to retrieve the attribute block for the file and assign it to the file (file->attr_block). This can be retrieved from the file control block, but you should look for this value on the file structure before reading from the fcb (the disk). The same should be done for fileGetAttr below.
int fileGetAttr( unsigned int fd, char *name, char *value, unsigned int name_size, unsigned int size ): This function retrieves the value of a file's attribute name (length of name_size). The function also takes a buffer for the value, called value, that is allocated to accept string of up to size bytes. Your code should return the number of bytes read into the value buffer. If no attribute of name is assigned, then nothing (0 bytes) is returned.
int diskSetAttr( unsigned int attr_block, char *name, char *value, unsigned int name_size, unsigned int value_size ): Writes the value to a disk data block associating it with the name attribute. As described in detail below, attr_block is the data block for attribute structures (dxattr_t), so diskSetAttr must create a structure for name if not already there. The attribute values are stored in separate data blocks referenced from the attribute structure.
int diskGetAttr( unsigned int attr_block, char *name, char *value, unsigned int name_size, unsigned int size, unsigned int existsp ): Reads the attribute name from the attr_block to retrieve the attribute data structure. This structure contains an offset in the value data blocks to enable retrieval of the value which is written to the value buffer up to length size. If the existsp flag is set, then this function only returns whether the attribute of name exists (regardless of whether it has a non-null value).
The file system structures are defined in the file cse473-filesys.h. The challenge is to understand the layout of these structures in blocks on the RAM disk. Below is a diagram outlining the blocks in the on-disk file system.
The first block (block 0) is the file system (partition) control block. All the blocks have a bit of block information at the beginning (dblock_t), but after that are the block contents. Block 0 specifies the number of blocks in the file system (bsize), the current first free block (firstfree), and the block number for the root directory (only directory). There is only one file system block.
Block 1 is the block that stores the directory. A directory refers to its hash table of directory entries (i.e., dentry) by its number of buckets which fill the remainder of the block and the location of the next free spot for a dentry (freeblk and free (slot)). Only the heads of the hash table are stored in the hash table. Each dentry has a next reference that is used to traverse the hash table lists. There are usually multiple directory blocks, but in this project there is only one.
Block 2 is the first dentry block, and it contains a set of dentries which each refer to a specific file by its name and (first) block. A next reference specifies the next entries to access for the dentry hash table. There can be multiple of these.
Block 3 is the file control block (FCB) which refers to the file meta data and actual data blocks. There are only 10 blocks in a file currently. There is one FCB per file. Note that the field attr_block is an index number for the block containing the control information for the extended attributes, called the File Xattr Control Block below.
Block 4 is a typical file data block. Other than the block header these blocks contain only file data. There should be lots of these.
This project will focus on two separate data blocks, File Xattr Control Blocks (attribute blocks) and Xattr Data Blocks (value blocks). These are shown in this diagram.
The first block (block 5) is the file's extended attribute (xattr) control block. It is a data block (dblock_t) for a file that contains a structure describing the xattr information, called the xattr control block (xcb_t). At the end of the xcb_t is a reference to an array of attribute structures (dxattr_t). We will store all the attribute structures in this one block. The array of 0 indicates that we do not really know how big these data structures are, nor how many we will really store, so we just leave it to be determined by the situation. This is actually a common practice in the Linux kernel.
Blocks 6 and 7 are data blocks that stores attribute values. As values can vary in size, they are just written like a log to the data block. That is, if we first write an attribute x's value 10, it is written to the data block at the beginning. If we change the value to 11, then we write this value after 10, and update the location in the attribute structure (value_offset).
Setting an attribute entails, allocating a file xattr control block (for the first attribute assigned), initializing the xcb_t structure for this file, creating a new dxattr_t structure for the attribute, writing the attribute name to the dxattr_t structure, allocating an xattr data block for the attributes value (store in xcb_t), and writing the value into the xattr data block. Make sure that the dxattr_t for the attribute name stores the offset of the value in the value blocks (in the field value_offset).
For subsequent attributes, create a new dxattr_t structure (after the current ones) and add the value at the end of the xattr data block. We can remove a value by setting it to a blank value. We never remove an attribute once created. Consider leveraging diskWrite to write the value because the value may span multiple blocks.
In the course of setting attributes, you will have to implement the processing of the flags XATTR_CREATE and XATTR_REPLACE, as described above.
Getting an attribute value entails, retrieving the file xattr control block, reading the xcb_t structure to find the dxattr_t structure with the name string, getting the location of the value string and its size from that dxattr_t structure, and retrieving the corresponding value from the appropriate block. I create the buffer for you and print the value. Consider using diskRead as values may span multiple blocks.
In the assignment, an output file shows the sequence of commands and responses for your file system. You will run 5 commands to generate this output: ./cse473-p4 your_fs cmdi where cmdi is the ith command for (e.g., cmd1 for the first). This program is deterministic, so your output should match mine (bug disclaimer here).
Grading: