Project 4 FAQ


Question 1: Is there only one disk?
Answer 1: Any invocation of the OS will only work with one disk. However, there is no guarantee that the "disk" (file) that is passed to the OS has been used before. In this case, it is like a new disk was installed - you need to configure it before it can be used. Your OS should check for this later case when it "boots" up.


Question 2: If we access the the disk, how do we know how what portion of it is used to store the inodes? Should we put the "fake disk" in the memory e.g use mmap)?
Answer 2: You should have a parameter like NUM_INODES that you should define (using #define) in one of your source files or header files (os.h would be a good place for something like this). You do not need to worry about putting the "fake disk" in memory - that is what the Disk_Init, Disk_Load, and Disk_Save functions are for. They allow you to create, load, and save a disk, respectively. The only interaction you have to do with the disk file is through these functions (and the Disk_Read and Disk_Write functions).


Question 3: So would any other signal generate the SIGINIT other than ctrl+c?
Answer 3: It is possible to generate it using the sigsend() in software, but for this project you can assume we will always send it using ctrl-c.


Question 4: In the File Access API functions, should the parameters passed be paths and not just file names so that we would know in exactly what directory it should be created?
Answer 4: Yes, files will always be accessed by the complete path name. Hence, if you want file foo.c, it would be:

/usr/lib/foo.c

This means the parameter passed to the file open function would be something like the following:

char path[50] = {"/usr/lib/foo.c"}
; ...
File_Open(path);

Of course, the actual declaration of path will be different but this should give you an idea of what to do.


Question 5: How do we initialize the inode in the "disk"? Do we use Disk_Write? If yes, how? Disk_Write writes to the "disk" in sector size, right? But we want to make the inode structure to be less than the sector. How do we write the second and third inodes?
Answer 5: You will have to format an entire sector before you can write it out to disk. For example, you will create an array that is 512 bytes in size. you will then stuff 4 inodes into it and write it to disk. For example:

char buf[SECTOR_SIZE];
for(i=0; i<(SECTOR_SIZE / INODE_SIZE); i++)
memcpy(buf, inode, sizeof(inode));
Disk_Write(inode_sector, buf);

The same goes for modifying an inode (or a data block for that matter). You must first read the entire block into memory, modify the correct portion of it, and then write it back out. Remember, you must always access the disk in elements of SECTOR_SIZE. Here is one more example of modifying a portion of a data sector:

char buf[SECTOR_SIZE];
Disk_Read(sector_num, buf);
// Assume the user has passed in a usrBuf that contains 10 bytes
// that need to be copied out.
memcpy(buf + offset, usrBuf, 10);
Disk_Write(sector_num, buf)

Of course, the offset is which bytes in that sector you want to modify.


Questions 6: If an inode represents a file, all the pointers would be used to point to data blocks, right?
Answer 6: That depends. If the file size is less than SECTOR_SIZE, you will only use one data block. if it's between SECTOR_SIZE and 2 * SECTOR_SIZE, you will use two data blocks. if it's between 2 * SECTOR_SIZE and 3 * SECTOR_SIZE you will use three data blocks. And so on - up to a maximum of 30 data blocks being used.


Question 7: If the inode represents a directory, what do we use all 30 pointers for?
Question 7: The first thing to realize is that you treat a directory just like any other file - except it's data is directory entries. There is some set number of directory entries that will fit within a single sector (let's call this n). If you have 1 to n entries in your directory, you will use one data block for your directory. If your directory grows bigger than n entries, you will need to add another data block - this will require using another pointer in the inode. If it grows bigger than 2n entries you will need three data blocks (and three pointers). And so on up to 30 data blocks.


Question 8: How exactly do we search a directory?
Answer 8: Let's assume you are given the following path:

/usr/test.c

The following is a rough sketch of how the important data structures are arranged on disk:

   
root inode (block 0)     usr inode (block 24)     test.c inode (block 12)
---------------------    ---------------------    ---------------------
| type       |  D   |    | type       |  D   |    | type       |  F   |
| size       |  80  |    | size       |  40  |    | size       | 700  |
|-------------------|    |-------------------|    |-------------------|
| block 0    | 25   |    | block 0    | 53   |    | block 0    | 21   |
| block 1    |  0   |    | block 1    |  0   |    | block 1    | 28   |
|   ...      | ...  |    |   ...      | ...  |    | block 2    |  0   |
| block 29   |  0   |    | block 29   |  0   |    |   ...      | ...  |
---------------------    ---------------------    |   ...      | ...  |
                                                  ---------------------


root directory (block 25) usr directory (block 53)      
----------------------    ----------------------   
| usr        |   24  |    | local      |   90  |
| bin        |    5  |    | test.c     |   12  |
| foo        |   16  |    |            |       |
| bar        |   48  |    |            |       |
|            |       |    |            |       |
|            |       |    |            |       |
----------------------    ----------------------

test.c data (block 21)    test.c data (block 28)      
----------------------    ----------------------   
|                    |    |                    |
|                    |    |       DATA         |
|     DATA           |    |                    |
|                    |    |--- end of data --- |   <-- byte 700
|                    |    |                    |
|                    |    |                    |
----------------------    ----------------------

To find the file test.c you would start by going to inode 0 and finding out which data blocks contain the directory entries for the root directory. You will then load data block 25 into memory and start searching thought it. When you find the entry usr you will then load inode 24 into memory. The inode will show that the usr directory only contains one data block and it is located at data block 53 - load data block 53 into memory. Search this data block until you come across the test.c entry. Now load inode 12 into memory. You can now find any of the data that is currently held in test.c (it is all located in blocks 21 and 28).


Question 9 How many data blocks do we need to map in our datablock bitmap? if we use characters as our 'bit' we can only fit 512 into one block... should we just map one block or move on to several blocks of bitmap (or a suggestion on a better datatype to use)?
Answer 9 You should only use one disk block for the data block bitmap. The key is to remember that it is a bit map and not a byte map. Therefore, a single block can represent 4096 (8 * 512) data blocks. Of course, this means you are going to have to do bit manipulation :(

Here is what I suggest you do. First, write functions that allow you access bits. These function might be (but don't have to be) things like:

void getBit(char* bitMap, int bit);
void setBit(char* bitMap, int bit);
void clearBit(char* bitMap, int bit);
int findFirstFreeBlock(char* bitMap);

There could of course be more of these functions. You can write and test these functions completely independant of the rest of your project. Once you are convinced they work properly, you can plug them directly into your project and never worry about doing any bit manipulation again :)

As for how to do this bit manipulation, you're going to need to become familiar with all the bit operators. Here they are:

& - bitwise and
| - bitwise or
^ - bitwise xor
~ - compliment (switch all 1's to zeroes and zeroes to 1's)
>> - shift right
<< - shift left

As a really quick example of how you could use some of these, let's assume you want to set the 3rd bit in a byte equal to zero:

unsigned char byte = getByte(n);
unsigned char mask = 1 << 3;
byte = ~mask & byte;
storeByte(n, byte);

One thing to note, using unsigned char's is probably a better idea than using regular chars. This can save you some headaches in the future.


Question 10 How do I check for the existance of a file or find out other information on a file?
Answer 10 If you want to check for the existance of a file, there are two possible ways (there's actually more but only two i'm going to talk about). The first is to simply use fopen() and then check the return value. You have to be careful with this, however, because if you try to open the file for writing it will erase the original file. If you open it for reading, you should be okay. Another thing to be careful of is to make sure you close the file right away if the fopen() call was successful (this will insure no problems when you call Disk_Load() ).

The second option is to use a function called access(). It basically checks to see if it would be okay for you to open the file without actually opening it.

To get specific information about a file, you can use the stat() function (this could also be used to check for the existance of a file). To get more information on this or the other two functions, check out the man pages.