Option #1
You may choose to work with a partner on this assignment. The mechanics of how you work with someone are entirely up to you and I will not mediate groups disputes, deal with partner laziness or incompetency, or give any leeway to "group excuses." Group excuses that I particularly don't like include "my partner had another project to work on and so we didn't have time to work on it much this week." In addition, I will ask questions on the final exam referring to the development of this program. Despite splitting up the work, you are required to understand all of the code that your partner has written. In addition, once you declare you are working with a partner, you may not change you mind for the duration of the assignment.Option #2
I am presenting this project in its entirety from the start. I have, however, broken the project into 3 logical parts each of which is due at the end of a single weeks time. You may take the option to hand in the entire assignment at the end of the three weeks providing that: you declare you intent to do this by Wednesday on this week, and you have not missed or been tardy on a single assignment to date. If you ever asked me, even once, for an extra 1/2 hour then you are not eligible for this option. I must approve all requests to follow this option.
Write Part's A and B as static functions in a class called HelperFunc
/** Store an integer into a byte array. * @param n the integer to be stored * @param buf the byte array into which it should be stored * @param offset the index of the first byte to be modified */ static void intToBytes(int n, byte[] buf, int offset) { buf[offset] = (byte)(n >> 24); buf[offset+1] = (byte)(n >> 16); buf[offset+2] = (byte)(n >> 8); buf[offset+3] = (byte)n; } /** Convert a field in a byte array to an integer. * @param buf the byte array containing the data. * @param offset the location in the array where the data starts. * @return the integer value. */ static int bytesToInt(byte[] buf, int offset) { return ((buf[offset] & 0xff) << 24) + ((buf[offset+1] & 0xff) << 16) + ((buf[offset+2] & 0xff) << 8) + (buf[offset+3] & 0xff); }To convert between byte arrays and Strings, use the String constructor String(byte[], int, int) and the method String.getBytes(). When converting from bytes to Strings, be careful to strip off any trailing null bytes used for padding.
/** Check whether a particular bit is set in a bitmap. * @param n the bit to test * @param b the bitmap * @return true if the bit is set */ static boolean bitIsSet(int n, byte[] b) { int whichByte = n / 8; int whichBit = n % 8; return ((b[whichByte] << whichBit) & 0x80) != 0; }
Your superblock must look like this:
class Superblock {
int totalBlocks;
// The number of blocks on this disk
int bitmapBlocks; //
The number of blocks reserved for the bitmap
int rootDirStart; //
The block number of the start of the root directory
int rootDirLength; // The
length of the root directory, in blocks
}
Write functions to set and retrieve each of the individual values in the superblock. In addition your should write each of the following functions.
// constructors
// set the corresponding values
in the superblock
public Superblock(int totalBlocks,
int bitmapBlocks, int rootDirStart, int rootDirLength);
// take the next 16 bytes starting
at offset in the byte array and using your conversion functions, create
a new superblock
public Superblock(byte[] byteArray,
int offset);
// covert the superblock to
a set of bytes, storing them in a byte array starting at offset
public void toBytes(byte[]
byteArray, int offset);
// write a toString function
so you can easily print out a superblock for your own use
public String toString();
A directory entry looks like this in memory:
class DirEnt { short type; // type of entry -- one of the following static final int UNUSED = 0; // directory entry is unused static final int DIRECTORY = 1; // entry points to another directory static final int FILE = 2; // entry points to a non-dir file short start; // block number of the first block of the dir or file short maxLen; // space allocated to this file or directory, in blocks int size; // current size of the file in bytes String name; // name of this entry (pathname component); max 22 chars static final int SIZE = 32; // size of a directory entry on disk. }When when a directory entry is stored in a block on disk, it is converted to a sequence of bytes. Each short field is stored as two bytes, high byte first. The size is stored as four bytes, from high to low. The characters of the name are converted to/from bytes as described below. If the name is less than 22 characters long, it is padded with null bytes. The total size of a directory entry on disk is 32 bytes, so 16 entries fit in a disk block.
Write functions corresponding to all the functions required for the superblock.
public synchronized void readBlock(int blockNumber, byte[] buffer, int offset); public synchronized void writeBlock(int blockNumber, byte[] buffer, int offset);The readBlock method asks the disk to copy the selected block into the supplied buffer at the indicated offset. For example, if offset is zero, the data will be put at the start of the buffer; if offset == 100, the data will be copyied to buffer[100], buffer[101], ... buffer[Disk.BLOCK_SIZE - 1]. It is an error if offset + Disk.BLOCK_SIZE > buffer.length. This method blocks the caller until the data has been copied from the disk to the buffer. Similarly, writeBlock copies one block of data from buffer, starting at offset, to the selected block of the disk. There is no need to do any buffering or scheduling of your own, and no need to deal with disk interrupts.
After the superblock are blocks containing a free block bitmap. The bitmap should be big enough to allow one bit for every block on the disk. If the bit is a 1, the block has been allocated for some purpose. If the bit is a 0, the block is free. The format method marks as allocated the superblock and the blocks allocated to the bitmap itself.
The rest of the blocks of the disk are either free or allocated to directories or other files. In this file system, files and directories are always allocated contiguously, so that a file or directory is completely described by its starting block number and its length in blocks.
You should only keep a minimal amount of data in memory. When on-disk data needs to be changed, send it to the disk as soon as is practical. For example, each time you need to update a directory entry, read the disk block that contains the entry, modify the part of that block that corresponds to the entry, and write the block back to disk. Let the (simulated) buffer cache in the Disk optimize the reads and writes.
Use a bitmap to keep track of which blocks are allocated and which blocks are free. We recommend reading the entire bitmap into a byte array on startup and flushing it back to disk before shutdown. Use a simple first-fit strategy to find space for new files and directories. Here's some simple code for manipulating the bitmap.
Unlike Unix, each file and directory has a maximum size, set when the file is created, as well as a current size.
You must implement the 5 system calls
format,
chdir,
mkdir,
rmdir, and
listdir described
below.
Unless specified otherwise, each of the system calls returns a negative
value to indicate an error and zero for success.
private DirEnt namei(DirEnt start, String[] path)
to simplify walking the directory tree. This method takes a pointer to a particular node in the file system tree (encoded as a DirEnt structure), and walks through the file system following the path indicated by the strings in path. The resulting DirEnt is a pointer to the end of the path (only the start and size fields are meaningful).
java Main sizewhere size is the size of the simulated disk, in blocks. For example, you might try java Main 100. If a (Unix) file named DISK exists in the current directory, it should be the result of any earlier run with the same size parameter. Otherwise, a new DISK will be created, with all blocks filled with null bytes. In this case, you should be careful that the first command you type is format.
You can also run the program to take its commands from a script, as in
java Main 100 test1.scriptInput lines starting with ``/*'' or ``//'' are ignored (the latter are echoed to the output). Other lines have the format
command [ args ]The there is one command for each of the 9 system calls, plus one extra version of write called writeln, which takes its input from the lines following the command. In line with longstanding tradition, some of the commands have slightly different names from the methods they call
command | method |
---|---|
rm | delete |
cd | chdir |
ls | listdir |
Finish your file system by adding the following commands to Kernel.java.
As per the prior assignment, you need not modify any files other than Kernel.java
and Library.java
You must implement the 4 system calls create, read, write, and delete described below. Note: These functions will end up looking very similar to the functions written last week.
Unless specified otherwise, each of the system calls returns a negative value to indicate an error and zero for success.
int create(String pathname, int maxSize);
Creates a new empty file with the indicated pathname and size limit (in blocks). All components of pathname other than the last component must identify existing directories. It is an error if the whole pathname identifies an existing file or directory, if there is not enough space left in the parent directory to create a new entry, or if there is not enough contiguous space on the disk for the new file. Should work similarly to mkdir. You should search the parent directory for the first UNUSED DirEnt and use it to index your new file.int read(String pathname, int offset, byte[] buffer);
Read buffer.length bytes from the file indicated by pathname, starting at offset (number of bytes from the start of the file). If fewer than buffer.length bytes remain between the indicated offset and the current file length, read as many bytes as possible, putting them into the beginning of buffer. (In particular, if the offset is greater than or equal to the current file size, read no bytes and leave the buffer unchanged). The return value is the number of bytes read, or error.int write(String pathname, int offset, byte[] buffer);
Write the contents of buffer to the indicated file starting at the indicated offset. The operation may overwrite existing data in the file and/or append to the end of the file. If the write would extend the file beyond its maximum size, write only as many bytes as will fit. The return value is the number of bytes written, or error.int delete(String pathname);
Destroy the indicated file. Make sure to change the DirEnt in the parent block to be UNUSED and be sure to release all blocks allocated to the file. It is an error if the indicated file does not exist or is a directory.