Copy the file /home/shay/g/ee469/labs/common/lab5.tar to your ee469 directory and untar it (tar -xvf lab5.tar). This will create a directory lab5. When you change over to this directory, you will find three directories, one for each part. You have to implement each of the following parts in each of these directories. DO NOT modify this directory structure. If you modify it, you may loose substantial points (you may even get a zero grade). Follow the instructions in each of the following parts as to which files to modify.
Initialize and format the file system (superblocks,inodes), and raw disk I/O support to the simulated disk.
File System:
fsys.c; find the line #define
FILESYSTEMNAME "/tmp/FileSystem.dlxos" and change it to the
new, group-specific name. This file will store all the necessary
information related to a filesystem such as the superblock, inodes,
and data blocks. When the file system is opened, the stored metadata
of the filesystem should be copied to the variables specified in
fsys.h (superblock -> sb, i-nodes -> inodes, free block vector
-> fbv). The file will consist of blocks of size 1 Kbytes. Here
is the format of the file system:
Note: Because you cannot write to or read from an unallocated block with the provided Read/WriteRawData functions, you have to use a flag (new_flag) in these functions when you try to read/write information from the file system. When this flag is set equal to 1, you can read/write to any block in the filesystem. But when the flag is set to 0, you can only read/write to the allocated data blocks (starting with the 12th block).
For your information, we implemented the above provided functions using Unix file functions. There are several functions dealing with Unix files, as defined in filesys.c and filesys.h. These functions are wrappers. They in turn invoke C functions like fopen, fclose, fseek, fwrite and fread. In implementing OpenFileSys(), you will need to use FsOpen() to check whether "/tmp/ee469gXFileSystem.dlxos" exists. If the file does not exist, you will need to use FsOpen() to create it. Remember to call FsClose() to close it.
Testing and grading: Put your code in fsys.c, where a skeleton has been created for you. Note that all of the functions are kernel functions, meaning that you don't have user interfaces to them. You should test your program by creating system processes. In your test, you should be able to use the provided kernel to create and open a filesystem and be able to successfully close it.
For this part, you will write simple system level file operation. A file table to store information about opened files has been created for you. You will also need to write the following system routines:
Where u is for the user that owns the file and o is for other processes. It should return an error (-1) if the calling process doesn't own the file.
Testing and grading: Put your code in inode.c, where a skeleton has been created for you. Note that all of the functions are kernel functions, meaning that you don't have user interfaces to them. You should test your program by creating system processes. In your test, you should be able to use the provided kernel to create and write files and use your kernel to read them and vice versa. The user interface that eventually call these functions only support 100 bytes of data. Therefore it is ok to assume the maximal size that you have to read or write is 100 bytes or less per function call.
Using the system-level file support in the previous part, implement a directory structure for dlxos. We will specify the filenames the same way they are specified in Unix. Thus the filename "/a/b" means file "b" in directory "a" located in the root directory (denoted by "/"). Also the filename "a/b" means the file "b" in directory "a", which in turn is located in the current directory. Thus you need to maintain the current directory information on per-process basis. We recommend you maintain this in the PCB under an integer field currentDIR, where you would store the inode number of the current directory. To simplify the problem, we do not support the special directory entries "." and "..". Thus the path to any file or directory can be specified only from the root directory or the current directory. To specify the path of a file from the current directory, the file needs to be a child of the current directory in the directory hierarchy.
Fundamentals of Directories: At the system level, we will treat the directories and the files almost identically. This is evident from the fact that we asked you to write common system-level handlers for files and directories. Directories are special files that are written in a proper structure. We will use a convention similar to that in Unix. A directory will be a sequence of fixed-length directory entries. Each directory entry is 34 bytes wide. The first 30 bytes contain the file name, and the last 4 bytes contain the inode number corresponding to that file. Thus a filename can at most be 30 bytes long. If the filename is shorter than 30 characters, the character after the filename should be '\0', thus clearly marking the end of the filename. Also, if the first character in a directory entry is '\0', then it means that the directory entry is empty, and can be used for creating further files. The character '/' is not a legal character in the filename, and hence the directory entry should not have this character. Note that a directory should be considered to be empty if and only if all of its directory entries are empty. Thus to remove a file (or a directory) from the file system, all you have to do is mark the beginning of the corresponding directory entry by '\0', and release the resources associated with that file (or directory). You have written functions for releasing the resources in the previous part. Also, to create a file or directory, you need to create an inode with proper attributes, and create a directory entry for that inode.
Directory and File permissions: As mentioned in the previous part, permissions for a directory or a file are stored in the corresponding inode. These are stored in the format uruwuxorowox, where r, w, and x have their usual meanings. As per the Unix convention, we treat the x (EXECUTE) permission for a directory to be the permission to change over to that directory. To open a file, the calling process needs to have at least EXECUTE permission to all the directories along that path, and required permissions for a specified file. To change over to a directory, a process needs to have execute permission for all the directories along the path. Thus, to open the file "/a/b" in read mode, the process needs to have execute permission to "/", and "a" and read permission to "b". Also, to change over to the directory "/a/c", the process needs to have execute permission to all the three directories "/", "a", and "c". Note that deleting a file (or a directory) is equivalent to writing to its parent directory. Thus, to delete a file or a directory, you need to have write and execute permission to its parent directory, and execute permission to all the other directories along the path. As already mentioned, the permissions are divided into two sets, user (u) and the others (o). Thus to check permissions, each process needs to know its userid. We have modified process.c so that the userid of a process gets stored in the PCB whenever it is created. You will have to compare this field with the ownerid of the file (ownerid of a file is the userid of the process that created it), and check the appropriate set of permissions accordingly.
You will have to support the following functions:
Implementation Issues: Implement all your code in the
files treestruct.c and treestruct.h located in the directory lab5_3. Also copy
the file inode.c from the previous part (lab5_2) to this directory, as you
will be using the functionality that you have implemented in that file. Do not
modify the makefile as it is going to be replaced during grading. Even if you
do modify the makefile, make sure that you are still able to build the kernel
with the old makefile.
For this part you can assume that the depth of the
directory structure is less than or equal to 10. That might simplify your life
while parsing the filename and checking various permissions. Note, you do not
have to assume this. But it's ok if you do it. We won't test your code beyond
the depth of 10.
The definitions of various error conditions in
treestruct.h are only for your convenience. You do not have to use or return
those specific error values. We are not going to grade you based on what error
values you return. As long as your function returns an error when there is an
error, you are fine.
Testing and grading: For your convenience, we have provided some test cases in userprog.c. In no way are these cases exhaustive. While grading your program we will run a number of test cases. You should make sure that your program conforms to the specifications.
After doing make clean, change over to the directory that contains the lab5 directory, and then execute the command
turnin -c ee469 -p labX-Y lab5
It is extremely important that you do not change the directory structure.
Also, while implementing the various parts, modify only those files that you are
asked to. If you modify other files, your program may not compile as we are
going to replace many of the other files to grade your submissions. If you want
to add any new user programs, you can do that. Again, since we are going to
replace the makefile, any files that you add will not compile when we grade your
programs.