Due Tuesday, April 27th, 2004 (9:00 AM)
The purpose of this project is to add a new filesystem to
GeekOS, as well as the standard operations for file management
The main part of this project is to develop a new filesystem for the GeekOS. This filesystem will reside on the second IDE disk drive in the Bochs emulator. This will allow you to continue to use your existing PFAT drive to load user programs while you test your filesystem. The second IDE disk's image is called diskd.img. It has 2MB by default, but you must change the size/disk geometry in .bochsrc to make it larger. See the How to... section.
GOSFS will provide a filesystem that includes multiple directories and long file name support.
The Mount system call allows you to associate a filesystem with a place in the file name hierarchy.The Mount call is implemented as part of the VFS code we supply. You will need to modify the init code to call Mount to mount the PFAT file system on drive 0 onto /c.
Then you can mount the GOSFS file system on drive 1 onto /d, for instance.Since GEEKOS will have two types of filesystems (PFAT and GOSFS), it will have a virtual filesystem layer (VFS) to handle sending requests to an appropriate filesystem (see figure below). We have provided an implementation of the VFS layer in the file vfs.c. The VFS layer will call the appropriate GOSFS routines when a file operation refers a file in the GOSFS filesystem.
The System Call layer is already implemented in syscall.c.add and the PFAT in pfat.c. Thus the only component you need to take care of is the GOSFS one.
Each user space process will have a file descriptor table that
keeps track of which files that process can currently read and write. Any user
process should be able to have up to 10 files open at once.
The file descriptors for a user process are kept in the files[MAX_OPEN_FILES] array in struct User_Context (see user.h.add). Note that not all the entries in the files are open files, since usually a process has less than 10 files open at once. If the field openFile.fsType == FS_TYPE_NONE that represents a free slot (file descriptor not used). But the good news is that file descriptor management is already implemented for you (see Open() function in vfs.c).
Your filesystem should support long filenames (at most 64 bytes, including a null at the end). A full path to a file will be no more than 1024 characters.
You should keep track of free disk blocks using a bit vector (as described in class). A library called bitset is provided (see bitset.h and bitset.c) that manages a set of bits and provides functions to find bits that are 0 (i.e. correspond to free disk blocks).
All disk allocations will be in units of 4KB (i.e. 8 physical disk blocks). Thus one bit in a bitset corresponds to a 4KB block. A bitset that is 8192 bits (1024 bytes) large will obviously keep track of 8192 * 4KB = 32 MB of data.
The filenode for a directory is distinguished by the isDirectory bit. The location of the block that holds the data for the directory will be stored in the first entry in the blocks array of the directory's filenode (hence entries blocks[1]..blocks[7] are unused).
You have to implement the sematics of the new system calls as described below. As you see, the semantics is very similar to the UNIX one.
Call | User Function | Return on success | Return on failure | Reasons for failure | Comment |
SYS_OPEN | Open(char *name, int permissions) | new file descriptor number | -1 |
|
|
SYS_CLOSE | Close(int fd) | 0 | -1 |
|
  |
SYS_DELETE | Delete(char *name) | 0 | -1 |
|
if Delete(file) is called and file is still open in other threads or even in the thread that called Delete(), all the subsequent operations on that file (except Close()) should fail |
SYS_READ | Read(int fd, char *buffer, int length) | number of bytes read | -1 |
|
There is special behavior when SYS_READ is called on a directory:
|
SYS_WRITE | Write(int fd, char *buffer, int length) | number of bytes written | -1 |
|
|
SYS_STAT | Stat(int fd, fileStat *stat) | 0 | -1 |
|
  |
SYS_SEEK | Seek(int fd, int offset) | 0 | -1 |
|
offset is an absolute position; could be equal to fileSize, then Write appends, see above |
SYS_CREATEDIR | CreateDirectory(char *name) | 0 | -1 |
|
Should create directories recursively if needed, e.g. CreateDirectory("/d/d1/d2/d3/d4"), will create d1 inside of d, d2 inside of d1, etc. if they don't exist already. This operation should be atomic, in the sense that either the whole directory chain is created or no directory is created. |
SYS_FORMAT | Format(int drive) | 0 | -1 |
|
formats a drive with GOSFS; don't need to support formatting with PFAT ; don't need to format in init code; so you can save your data between sessions |
A guideline is provided above. First block (0) is called SUPERBLOCK, and contains filesystem housekeeping data. Blocks >= 1 contain files and directories.
When you do a Format() , you make a raw disk usable with GOSFS. That is:
All changes should be committed to disk before the system returns from the syscall that made the changes. If you cache any structures while processing a call, you should write any changes to them to disk before returning from the syscall.
If a read() is called on a directory, the data returned should be in the form of an array of dirEntry structures. The length argument and the return value will indicate the number of entries to read and the number of entries that were read, rather than the number of bytes.