Improve your level-1 index file system to a variable-level file system and add subdirectories. Also allow for an existing file to be opened for writing. Also allow for extra-fast transfers of data between two files or between memory and a file. NOTE Friday 12th April: Subdirectories is now extra credit, not required. The variable-level part: (takes some work) A level-0 file does not involve any block pointers/numbers. Let's imagine that you have 10 words of meta-data in your header blocks, in that case a level-0 file has a maximum size of max[0] = 472 bytes. 472 is 512 - 4 * 10. 472 bytes is 118 words, so a level-1 file has a maximum size of 118 * 512 bytes, that is max[1] = max[0]/4 * 512. A level-2 file has an extra layer of 128 pointers, so max[2] = 128 * max[1], and max[3] = 128 * max[2], and so on. You can pre-compute those sizes in a small vec if you want, the working out the level number required for any given file size N is simply a short search for the smallest K such that max[K] >= N. There will be an extra number in every header block that says how many levels of pointers this file has. If the number is 3, that means that all of the block numbers in the header block point to type-2 pointer blocks. A type-2 pointer block contains (up to) 128 block numbers, each of which lead to a type-1 pointer block. A type-1 pointer block contains (up to) 128 block numbers, each of which lead to ordinary 512 byte data blocks. Three levels of pointers. Don't stop at three levels, your solution should work for any number. The calculations are easy: Data blocks contain 512 = 2^9 bytes of data So within a type-1 pointer block, byte N is in the N/2^9th block Each type-1 pointer block leads to 128*512 = 2^16 bytes of data so within a type-2 pointer block, byte N is reached through pointer N/2^16 Each type-2 pointer block leads to 128*128*512 = 2^23 data bytes, so within a level-3 pointer block, byte N is reached through pointer N/2^23 The header block is just a type-? pointer block with extra stuff at the beginning and therefore fewer pointers. The progression of maximum sizes, size[i] is: i: 0, 1, 2, 3, 4, 5, ... size[i]: 2^9, 2^16, 2^23, 2^30, 2^37, 2^44, ... In a level-X file, byte N is reached through pointer N / size[X-1] in the header block. You now have to account another for N % size[X-1] bytes. So in that opinter block you would use pointer (N % size[X-1]) / size[X-2], and having (N % size[X-1]) % size[X-2] bytes left to account for. and the process repeats regularly until you are down to the correct data block. You should definitely keep an in-memory copy of both the header block and the current data block while a file is open. It is up to you whether or not you keep copies of (any of) the intermediate pointer blocks. The write to existing file part: (easy) Trying to open a non-existent file for reading is an error. Opening a non-existent file for writing, simply creates the file, initially empty. Opening a file that already exists for writing should result is all writes adding to the end of the file. The original contents is kept and appended to. This is easy to handle. On opening such a file, simply read the file's last block into the iosb's buffer, and set the position so that the next byte will be written to the end. The extra-fast data transfer part: (easy) Add a very simple function to the iosb. If the file is open for reading, this new function will simply read the whole next block into a buffer. If the file is open for writing, it will write a whole 512 byte buffer to the next block of the file. Do it the way I showed in class 18. The function has one parameter. If the parameter is nil, it will read/write its own buffer that already exists in the iosb. If the parameter is not nil, it will be the address of the buffer that is to be used. This way, reading a whole file to memory is just a matter of allocating a newvec big enough to hold the file ((length + 3) / 4 words) and repeatedly calling this block reading function, each time with the parameter 128 more than the last time. You do not need to keep a record of whether the file is open for reading or writing, as that information is already there. if iosb ! iosb_ichar = illegal_readch it was opened for writing. If it is not, then the file was opened for reading. The sub-directories part: (intermediate) All directories, including the root directory, should now be constructed the same was as any other file (using a variable level index system). Recall that one of the so-far-useless items in a header block is the file's type, something like 'F' for ordinary file, and 'D' for directory. If the user opens a directory, no normal input/output should be allowed: iosb ! iosb_ichar should = illegal_readch and iosb ! iosb_ochar = illegal_writech. Only three operations, apart from opening and closing, are allowed on a directory. They are just like the exisiting directory functions. One takes a file name and searches for it, returning its header block number if it exists, and some error code it it doesn't. Another is given a file name and a header block number, and it adds that entry to the directory (error is a file with that name exists). The third takes a file name and deletes that file, returning all of its blocks, including its header block to the free block list. The order of importance is the same as the order in which I described the parts. The vast majority of the grade is for the variable level part.