Roadmap 7: File System

In this roadmap we study the following issues associated with the file system:

  • Two types of storage unit: byte (memory, user); sector (disk);
  • Data structures of file header (i-node), directory;
  • Implementations of the operations on files;
  • Synchronization in file system;
  • Disk management;
  • Interface between system (software) and disk (hardware device).

  • As before, examine and understand the file system code first. Nachos file operations are similar (but not identical) to those in UNIX. It is a good idea to read the UNIX man pages for creat, open, close, read, write, lseek, and unlink (e.g., type "man creat").

    Command line switches support the following file system operations:

    	-f causes the physical disk to be formatted
    	-cp copies a file from UNIX to nachos
    	-p prints a nachos file to stdout
    	-r removes a nachos file from the file system
    	-l lists the contents of the nachos directory
    	-D prints the contents of the entire file system
    	-t tests the performance of the nachos file system
    

    Examples

    	./nachos -cp ../test/halt halt
    

    Copy the halt program into the simulated disk.

    	./nachos -x halt
    

    Run the halt program from the simulated disk.

    	./nachos -cp ../threads.cc threads.cc
    	./nachos -p threads.cc
    

    Copy a textfile from UNIX into the nachos simulated disk and print it to stdout. See Example for a sample run of the file system.

    Before tracing the program, we first give an overview of nachos file system. It has the following objects:
    FileSystem { OpenFile *freeMapFile; OpenFile *directoryFile; };
    Directory { int tableSize; DirectoryEntry *table; };
    DirectoryEntry { bool inUse; int sector; char name[FileNameMaxLen + 1]; };
    OpenFile { FileHeader *hdr; int seekPosition; };
    FileHeader { Int numBytes; int numSectors; int dataSectors[NumDirect]; };

            ---------- FileSystem ----------
            |                               |
        freeMapFile -------------------- directoryFile
            |               |                     |
          BitMap         OpenFile             Directory
                            |                     |
                        FileHeader          DirectoryEntry
    

    Question

    What file system objects exist in memory and/or on disk?
    How are these objects moved between memory and disk?

    Now, we examine nachos file system by tracing the program. Start with threads/system.cc/Initialize(argc, argv) called by threads/main.cc/main(int argc, char **argv).

    void
    Initialize(int argc, char **argv)
    {
    	...
    #ifdef FILESYS_NEEDED
            if (!strcmp(*argv, "-f"))
                format = TRUE;
    #endif
    	...
    #ifdef FILESYS
        synchDisk = new SynchDisk("DISK");
    #endif
    
    #ifdef FILESYS_NEEDED
        fileSystem = new FileSystem(format);
    #endif
    	...
    }
    

    First, a new synchDisk is constructed before the fileSystem is created. The file system consists of a root directory and a bitmap, which we have studied in user program. Both the directory and bitmap are treated as files, objects of class OpenFile, which provides operations on files. Then, you can test this simple file system using command line switch. The utility functions can be found in fstest.cc.

     

     

    filesys/disk.*, synchdisk.*

    class SynchDisk {
      public:
        SynchDisk(char* name);              // Initialize a synchronous disk,
                                            // by initializing the raw Disk.
        ~SynchDisk();                       // De-allocate the synch disk data
    
        void ReadSector(int sectorNumber, char* data);
                                            // Read/write a disk sector, returning
                                            // only once the data is actually read
                                            // or written.  These call
                                            // Disk::ReadRequest/WriteRequest and
                                            // then wait until the request is done.
        void WriteSector(int sectorNumber, char* data);
    
        void RequestDone();                 // Called by the disk device interrupt
                                            // handler, to signal that the
                                            // current disk operation is complete.
    
      private:
        Disk *disk;                         // Raw disk device
        Semaphore *semaphore;               // To synchronize requesting thread
                                            // with the interrupt handler
        Lock *lock;                         // Only one read/write request
                                            // can be sent to the disk at a time
    };
    
    
    SynchDisk::SynchDisk(char* name)
    {
        semaphore = new Semaphore("synch disk", 0);
        lock = new Lock("synch disk lock");
        disk = new Disk(name, DiskRequestDone, (int) this);
    }
    

    Questions:

  • When synchDisk is constructed, it creates a asynchronous disk. What argument is passed to the disk? What does it do? This argument is part of the interface between the system (software) and the disk (hardware).
  • What are the arguments passed to Disk::ReadRequest() or Disk::WriteRequest? They are also part of the interface between the system (software) and the disk (hardware).
  • When a thread sends a disk I/O request (SynchDisk::ReadSector(), SynchDisk::WriteSector()), how is it blocked?
  •  

     

    machine/disk.*

    
    class Disk {
      public:
        Disk(char* name, VoidFunctionPtr callWhenDone, int callArg);
                                            // Create a simulated disk.
                                            // Invoke (*callWhenDone)(callArg)
                                            // every time a request completes.
        ~Disk();                            // Deallocate the disk.
    
        void ReadRequest(int sectorNumber, char* data);
                                            // Read/write an single disk sector.
                                            // These routines send a request to
                                            // the disk and return immediately.
                                            // Only one request allowed at a time!
        void WriteRequest(int sectorNumber, char* data);
    
        void HandleInterrupt();             // Interrupt handler, invoked when
                                            // disk request finishes.
    
        int ComputeLatency(int newSector, bool writing);
                                            // Return how long a request to
                                            // newSector will take:
                                            // (seek + rotational delay + transfer)
    
      private:
        int fileno;                         // UNIX file number for simulated disk
        VoidFunctionPtr handler;            // Interrupt handler, to be invoked
                                            // when any disk request finishes
        int handlerArg;                     // Argument to interrupt handler
        bool active;                        // Is a disk operation in progress?
        int lastSector;                     // The previous disk request
        int bufferInit;                     // When the track buffer started
                                            // being loaded
    
        int TimeToSeek(int newSector, int *rotate); // time to get to the new track
        int ModuloDiff(int to, int from);        // # sectors between to and from
        void UpdateLast(int newSector);
    };
    

    Questions:

  • How is a disk interrupt scheduled?
  • When a disk I/O completes, how does the thread blocked on I/O wake up?
  •  

     

    filesys/filesys.*

    
    class FileSystem {
      public:
    	...
      private:
       OpenFile* freeMapFile;               // Bit map of free disk blocks,
                                            // represented as a file
       OpenFile* directoryFile;             // "Root" directory -- list of
                                            // file names, represented as a file
    };
    

    The file system consists of freeMapFile (OpenFile *), a bit map treated as a file, and directoryFile (OpenFile *), the root directory also treated as a file. Where are their file headers? The baseline file system has the following restrictions: only one (root) directory, entries in directory are of fixed size, maximum number of entries in the directory is 10.

     

     

    
    FileSystem::FileSystem(bool format)
    {
        DEBUG('f', "Initializing the file system.\n");
        if (format) {
            BitMap *freeMap = new BitMap(NumSectors);
            Directory *directory = new Directory(NumDirEntries);
            FileHeader *mapHdr = new FileHeader;
            FileHeader *dirHdr = new FileHeader;
    
            DEBUG('f', "Formatting the file system.\n");
    
        // First, allocate space for FileHeaders for the directory and bitmap
        // (make sure no one else grabs these!)
            freeMap->Mark(FreeMapSector);
            freeMap->Mark(DirectorySector);
    
        // Second, allocate space for the data blocks containing the contents
        // of the directory and bitmap files.  There better be enough space!
    
            ASSERT(mapHdr->Allocate(freeMap, FreeMapFileSize));
            ASSERT(dirHdr->Allocate(freeMap, DirectoryFileSize));
    
        // Flush the bitmap and directory FileHeaders back to disk
        // We need to do this before we can "Open" the file, since open
        // reads the file header off of disk (and currently the disk has garbage
        // on it!).
    
            DEBUG('f', "Writing headers back to disk.\n");
            mapHdr->WriteBack(FreeMapSector);
            dirHdr->WriteBack(DirectorySector);
    
        // OK to open the bitmap and directory files now
        // The file system operations assume these two files are left open
        // while Nachos is running.
    
            freeMapFile = new OpenFile(FreeMapSector);
            directoryFile = new OpenFile(DirectorySector);
    
        // Once we have the files "open", we can write the initial version
        // of each file back to disk.  The directory at this point is completely
        // empty; but the bitmap has been changed to reflect the fact that
        // sectors on the disk have been allocated for the file headers and
        // to hold the file data for the directory and bitmap.
    
            DEBUG('f', "Writing bitmap and directory back to disk.\n");
            freeMap->WriteBack(freeMapFile);         // flush changes to disk
            directory->WriteBack(directoryFile);
    
            if (DebugIsEnabled('f')) {
                freeMap->Print();
                directory->Print();
    
            delete freeMap;
            delete directory;
            delete mapHdr;
            delete dirHdr;
            }
        } else {
        // if we are not formatting the disk, just open the files representing
        // the bitmap and directory; these are left open while Nachos is running
            freeMapFile = new OpenFile(FreeMapSector);
            directoryFile = new OpenFile(DirectorySector);
        }
    }
    

    When the file system is constructed, if the command line switch is "-f", the disk is formatted (wipe out all files on the disk). It first initializes bitmap and root directory in memory (headers and files); flush the headers back to disk; open bitmap and root so that their headers are kept in memory and we can operate on them; flush bitmap and root directory files back to the disk; clean up memory. If the disk has been formatted, just open bitmap and root directory (bring their file headers into memory).

    Question:

    How many disk reads/writes when the file system is contructed with format=TRUE?

     

     

    The file system provides the following operations. These operations are associated with the file system (directory, bitmap). The operations associated with individual files are in OpenFile object.

    
    class FileSystem {
      public:
    	...
        bool Create(char *name, int initialSize);
    
        OpenFile* Open(char *name);
    
        bool Remove(char *name);
    	...
    };
    

     

     

    
    bool
    FileSystem::Create(char *name, int initialSize)
    {
        Directory *directory;
        BitMap *freeMap;
        FileHeader *hdr;
        int sector;
        bool success;
    
        DEBUG('f', "Creating file %s, size %d\n", name, initialSize);
    
        directory = new Directory(NumDirEntries);
        directory->FetchFrom(directoryFile);
    
        if (directory->Find(name) != -1)
          success = FALSE;                  // file is already in directory
        else {
            freeMap = new BitMap(NumSectors);
            freeMap->FetchFrom(freeMapFile);
            sector = freeMap->Find();       // find a sector to hold the file header
            if (sector == -1)
                success = FALSE;            // no free block for file header
            else if (!directory->Add(name, sector))
                success = FALSE;    // no space in directory
            else {
                hdr = new FileHeader;
                if (!hdr->Allocate(freeMap, initialSize))
                    success = FALSE;        // no space on disk for data
                else {
                    success = TRUE;
                    // everthing worked, flush all changes back to disk
                    hdr->WriteBack(sector);
                    directory->WriteBack(directoryFile);
                    freeMap->WriteBack(freeMapFile);
                }
                delete hdr;
            }
            delete freeMap;
        }
        delete directory;
        return success;
    }
    

    This function creates a file with no data by adding an entry in the directory. Study carefully the data movement between memory and disk.

    The file header is flushed back to the disk before the directory. Because, if system crashes in between, the directory does not have the entry for the file although the file header is on the disk. The file system still functions. If, however, the order is reversed, the directory were flushed back before the file header. If the system crashes in between, we would have the entry in the directory referencing to a sector number (i-number) which is not the file header. The file system would be corrupted.

    Question:

    What if the system crashes after the directory is flushed back but before the free map is flushed back?

     

     

    
    OpenFile *
    FileSystem::Open(char *name)
    {
        Directory *directory = new Directory(NumDirEntries);
        OpenFile *openFile = NULL;
        int sector;
    
        DEBUG('f', "Opening file %s\n", name);
        directory->FetchFrom(directoryFile);
        sector = directory->Find(name);
        if (sector >= 0)
            openFile = new OpenFile(sector);        // name was found in directory
        delete directory;
        return openFile;                            // return NULL if not found
    }
    

    Steps of opening a file:

    1. fetch directory file from the disk (directory header is always in memory);
    2. find the file header sector number in the directory given name;
    3. construct an OpenFile object using the file header sector number;
    4. clean up the directory file in memory.
    
    bool
    FileSystem::Remove(char *name)
    {
        Directory *directory;
        BitMap *freeMap;
        FileHeader *fileHdr;
        int sector;
    
        directory = new Directory(NumDirEntries);
        directory->FetchFrom(directoryFile);
        sector = directory->Find(name);
        if (sector == -1) {
           delete directory;
           return FALSE;                     // file not found
        }
        fileHdr = new FileHeader;
        fileHdr->FetchFrom(sector);
    
        freeMap = new BitMap(NumSectors);
        freeMap->FetchFrom(freeMapFile);
    
        fileHdr->Deallocate(freeMap);               // remove data blocks
        freeMap->Clear(sector);                     // remove header block
        directory->Remove(name);
    
        freeMap->WriteBack(freeMapFile);            // flush to disk
        directory->WriteBack(directoryFile);        // flush to disk
        delete fileHdr;
        delete directory;
        delete freeMap;
        return TRUE;
    }
    

    This function removes a file from disk. In order to find the data sectors and free them, we must bring in the file header, which requires the directory file and the bitmap file.

    Question:

    What if the system crashes after the free map is flushed back but before the directory is flushed back?

     

     

    filesys/openfile.*

    
    class OpenFile {
      public:
    	...
      private:
        FileHeader *hdr;                    // Header for this file
        int seekPosition;                   // Current position within the file
    };
    

    Data structures: a pointer to FileHeader and a position.

    
    	------------------
    	|       hdr      |---> FileHeader
    	------------------
    	|  seekPosition  |
    	|----------------|
    
    
    OpenFile::OpenFile(int sector)
    {
        hdr = new FileHeader;
        hdr->FetchFrom(sector);
        seekPosition = 0;
    }
    
    int
    OpenFile::Read(char *into, int numBytes)
    {
       int result = ReadAt(into, numBytes, seekPosition);
       seekPosition += result;
       return result;
    }
    
    int
    OpenFile::Write(char *into, int numBytes)
    {
       int result = WriteAt(into, numBytes, seekPosition);
       seekPosition += result;
       return result;
    }
    
    
    int
    OpenFile::ReadAt(char *into, int numBytes, int position)
    {
        int fileLength = hdr->FileLength();
        int i, firstSector, lastSector, numSectors;
        char *buf;
    
        if ((numBytes <= 0) || (position >= fileLength))
            return 0;                               // check request
        if ((position + numBytes) > fileLength)
            numBytes = fileLength - position;
        DEBUG('f', "Reading %d bytes at %d, from file of length %d.\n",
                            numBytes, position, fileLength);
    
        firstSector = divRoundDown(position, SectorSize);
        lastSector = divRoundDown(position + numBytes - 1, SectorSize);
        numSectors = 1 + lastSector - firstSector;
    
        // read in all the full and partial sectors that we need
        buf = new char[numSectors * SectorSize];
        for (i = firstSector; i <= lastSector; i++)
            synchDisk->ReadSector(hdr->ByteToSector(i * SectorSize),
                                            &buf[(i - firstSector) * SectorSize]);
    
        // copy the part we want
        bcopy(&buf[position - (firstSector * SectorSize)], into, numBytes);
        delete buf;
        return numBytes;
    }
    
    int
    OpenFile::WriteAt(char *from, int numBytes, int position)
    {
        int fileLength = hdr->FileLength();
        int i, firstSector, lastSector, numSectors;
        bool firstAligned, lastAligned;
        char *buf;
    
        if ((numBytes <= 0) || (position >= fileLength))
            return 0;                               // check request
        if ((position + numBytes) > fileLength)
            numBytes = fileLength - position;
        DEBUG('f', "Writing %d bytes at %d, from file of length %d.\n",
                            numBytes, position, fileLength);
    
        firstSector = divRoundDown(position, SectorSize);
        lastSector = divRoundDown(position + numBytes - 1, SectorSize);
        numSectors = 1 + lastSector - firstSector;
    
        buf = new char[numSectors * SectorSize];
    
        firstAligned = (position == (firstSector * SectorSize));
        lastAligned = ((position + numBytes) == ((lastSector + 1) * SectorSize));
    
    // read in first and last sector, if they are to be partially modified
        if (!firstAligned)
            ReadAt(buf, SectorSize, firstSector * SectorSize);
        if (!lastAligned && ((firstSector != lastSector) || firstAligned))
            ReadAt(&buf[(lastSector - firstSector) * SectorSize],
                                    SectorSize, lastSector * SectorSize);
    
    // copy in the bytes we want to change
        bcopy(from, &buf[position - (firstSector * SectorSize)], numBytes);
    
    // write modified sectors back
        for (i = firstSector; i <= lastSector; i++)
            synchDisk->WriteSector(hdr->ByteToSector(i * SectorSize),
                                            &buf[(i - firstSector) * SectorSize]);
        delete buf;
        return numBytes;
    }
    

    What happens when a new object OpenFile is constructed?

    OpenFile provides operations, Read and Write, on files, which are implemented by ReadAt and WriteAt using the internal seekPosition. Note that we can only transfer one sector at a time from the disk, thus when read, we have to fetch all associated sectors from the disk into memory (kernel space) then copy what we want (read may not start from the beginning of the first sector and may not end at the end of the last sector). When write, we first fetch the first and the last sectors from the disk into the kernel space and then modify them and write all sectors back to the disk (the first and the last sectors may be partially modified).

     

     

    filesys/filehdr.*

    
    class FileHeader {
      public:
    	...
      private:
        int numBytes;                       // Number of bytes in the file
        int numSectors;                     // Number of data sectors in the file
        int dataSectors[NumDirect];         // Disk sector numbers for each data
                                            // block in the file
    };
    

    Data structures of FileHeader:

    
    	-------------------
    	|    numBytes     |
    	-------------------
    	|   numSectors    |
    	-------------------
    	|  dataSectors[0] |
    	-------------------
    	|  dataSectors[1] |
    	-------------------
    	|                 |
    	|       ...       |
    	|                 |
    	-------------------
    

    What is the value of NumDirect?

    
    bool
    FileHeader::Allocate(BitMap *freeMap, int fileSize)
    {
        numBytes = fileSize;
        numSectors  = divRoundUp(fileSize, SectorSize);
        if (freeMap->NumClear() < numSectors)
            return FALSE;           // not enough space
    
        for (int i = 0; i < numSectors; i++)
            dataSectors[i] = freeMap->Find();
        return TRUE;
    }
    
    
    void
    FileHeader::Deallocate(BitMap *freeMap)
    {
        for (int i = 0; i < numSectors; i++) {
            ASSERT(freeMap->Test((int) dataSectors[i]));  // ought to be marked!
            freeMap->Clear((int) dataSectors[i]);
        }
    }
    
    
    void
    FileHeader::FetchFrom(int sector)
    {
        synchDisk->ReadSector(sector, (char *)this);
    }
    

    There are two ways of initializing a file header:

  • Allocate: initialize a fresh file header. In the baseline nachos, the file size must be specified and all data sectors are allocated when the file header is initialized.
  • FetchFrom: read the file header from the disk into memory (kernel space).
  • How is the consistency checked when a file header is deallocated?

    Compare the functions FetchFrom() and WriteBack() here with those in directory. The bitmap also has FetchFrom() and WriteBack() functions.

     

     

    filesys/directory.*

    
    class DirectoryEntry {
      public:
        bool inUse;                         // Is this directory entry in use?
        int sector;                         // Location on disk to find the
                                            //   FileHeader for this file
        char name[FileNameMaxLen + 1];      // Text name for file, with +1 for
                                            // the trailing '\0'
    };
    

    A directory is a table (array) of DirectoryEntry. The data structures of a DirectoryEntry:

    
    	------------
    	|  inUse   |
    	------------
    	|  sector  |   sector number of file header
    	------------
    	|          |
    	|   name   |
    	|          |
    	------------
    
    
    
    class Directory {
      public:
    	...
        void FetchFrom(OpenFile *file);
        void WriteBack(OpenFile *file);
    	...
    };
    

    Compare FetchFrom() and WriteBack() functions with those in FileHeader.

     

     

    filesys/fstest.cc

    
    void Copy(char *from, char *to)
    
    void Print(char *name)
    
    static void FileWrite()
    
    static void FileRead()
    
    void PerformanceTest()
    

    This file provides some utility functions used for testing. It is a good idea to study UNIX file system calls.

     

     

    Example

    Here is a sample run of the file system. Can you identify the disk reads/writes?

    
    Script started on Mon Mar 06 10:22:46 199
    church[1] cd filesys
    church[2] gmake
    
    ...
    
    church[3] ls
    Makefile      filehdr.cc    list.o        scheduler.o   sysdep.o
    addrspace.o   filehdr.h     machine.o     stats.o       system.o
    bitmap.o      filehdr.o     main.o        switch.o      test
    console.o     filesys.cc    mipssim.o     swtch.s       thread.o
    directory.cc  filesys.h     nachos        synch.o       threadtest.o
    directory.h   filesys.o     openfile.cc   synchdisk.cc  timer.o
    directory.o   fstest.cc     openfile.h    synchdisk.h   translate.o
    disk.o        fstest.o      openfile.o    synchdisk.o   typescript
    exception.o   interrupt.o   progtest.o    synchlist.o   utility.o
    
    church[4] nachos -f
    No threads ready or runnable, and no pending interrupts.
    Assuming the program completed.
    Machine halting!
    Ticks: total 82630, idle 82420, system 210, user 0
    Disk I/O: reads 3, writes 5
    Console I/O: reads 0, writes 0
    Paging: faults 0
    Network I/O: packets received 0, sent 0
    Cleaning up...
    
    church[20] nachos -cp ../test/halt halt
    No threads ready or runnable, and no pending interrupts.
    Assuming the program completed.
    Machine halting!
    Ticks: total 821010, idle 819810, system 1200, user 0
    Disk I/O: reads 56, writes 51
    Console I/O: reads 0, writes 0
    Paging: faults 0
    Network I/O: packets received 0, sent 0
    Cleaning up...
    
    church[21] nahos -x halt
    Machine halting!
    Ticks: total 5592, idle 5350, system 230, user 12
    Disk I/O: reads 10, writes 0
    Console I/O: reads 0, writes 0
    Paging: faults 0
    Network I/O: packets received 0, sent 0
    Cleaning up...
    
    church[22] nachos -cp filehdr.h filehdr.h
    No threads ready or runnable, and no pending interrupts.
    Assuming the program completed.
    Machine halting!
    Ticks: total 1150, idle 1000, system 150, user 0
    Disk I/O: reads 2, writes 235
    Console I/O: reads 0, writes 0
    Paging: faults 0
    Network I/O: packets received 0, sent 0
    Cleaning up...
    
    church[29] nachos -p filehdr.h
    // filehdr.h
    //      Data structures for managing a disk file header.
    //
    
    ....
    
        int dataSectors[NumDirect];         // Disk sector numbers for each data
                                            // block in the file
    };
    #endif // FILEHDR_H
    No threads ready or runnable, and no pending interrupts.
    Assuming the program completed.
    Machine halting!
    Ticks: total 131990, idle 129330, system 2660, user 0
    Disk I/O: reads 253, writes 0
    Console I/O: reads 0, writes 0
    Paging: faults 0
    Network I/O: packets received 0, sent 0
    Cleaning up...
    
    church[30] nachos -l
    halt
    filehdr.h
    Ticks: total 2520, idle 2350, system 170, user 0
    Disk I/O: reads 4, writes 0
    Console I/O: reads 0, writes 0
    
    church[32] nachos -D
    Bit map file header:
    FileHeader contents.  File size: 128.  File blocks:
    2
    File contents:
    ?\ff\ff\ff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
    \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
    \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
    \0\0\0\0\0\0\0\
    
    0\0
    Directory file header:
    FileHeader contents.  File size: 200.  File blocks:
    3 4
    File contents:
    \1\0\0\0\0\0\0\5halt\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\afilehdr.h\0\0\0\0\0\0\0\0\0\
    0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
    0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
    0\0
    \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
    \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
    Bitmap set:
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22
    , 23, 24, 25, 26, 27, 28, 29,
    Directory contents:
    Name: halt, Sector: 5
    FileHeader contents.  File size: 440.  File blocks:
    6 7 8 9
    File contents:
    \ad\df\ba\0\0\0\0\0(\0\0\0\90\1\0\0\90\1\0\0\b8\1\0\0\0\0\0\0\90\1\0\0\1c\f8\ff\
    bf\0\0\0\0X\0\0\c\0\0\0\0\8\0\0\c! \0\0\0\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\1\0\2$\c
    \0\0\0\8\0\e0\3\0\0\0\0\2\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\3\0\2$\c\0\0\0\8\0\e0\3\
    0\0\0\0\4\0\2$\
    
    c\0\0\0
    \8\0\e0\3\0\0\0\0\5\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\6\0\2$\c\0\0\0\8\0\e0\3\0\0\0\
    0\7\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\8\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\9\0\2$\c\0\0\0
    \8\0\e0\3\0\0\0\0\a\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\b\0\2$\c\0\0\0\8\0\e0\3\0\0\0\
    0\c\0\2$\c\0\0\
    
    0
    \8\0\e0\3\0\0\0\0\d\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\e\0\2$\c\0\0\0\8\0\e0\3\0\0\0\
    0\f\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\10\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\11\0\2$\c\0\0
    \0\8\0\e0\3\0\0\0\0\12\0\2$\c\0\0\0\8\0\e0\3\0\0\0\0\13\0\2$\c\0\0\0\8\0\e0\3\0\
    0\0\0\8\0\e0\3\
    
    0\0\0\0
    \0\0\0\0\0\0\0\0\e8\ff\bd'\14\0\bf\af\10\0\be\afT\0\0\c!\f0\a0\3\4\0\0\c\0\0\0\0
    !\e8\c0\3\14\0\bf\8f\10\0\be\8f\8\0\e0\3\18\0\bd'
    Name: filehdr.h, Sector: 10
    FileHeader contents.  File size: 2327.  File blocks:
    11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
    File contents:
    // filehdr.h \a//\9Data structures for managing a disk file header.  \a//\a//\9A
     file header describes where on disk to find the data
     in a file,\a//\9along with other information about the file (for instance, its\
    a//\9length, owner, etc.)\a//\a// Copyright (c) 1992-1
    993 The Regents of the University of California.\a// All rights reserved.  See c
    opyright.h for copyright notice and limitation \a/
    / of liability and disclaimer of warranty provisions.\a\a#include "copyright.h"\
    a\a#ifndef FILEHDR_H\a#define FILEHDR_H\a\a#include "di
    sk.h"\a#include "bitmap.h"\a\a#define NumDirect \9((SectorSize - 2 * sizeof(int)
    ) / sizeof(int))\a#define MaxFileSize \9(NumDirect * S
    ectorSize)\a\a// The following class defines the Nachos "file header" (in UNIX t
    erms,  \a// the "i-node"), describing where on disk
     to find all of the data in the file.\a// The file header is organized as a simp
    le table of pointers to\a// data blocks. \a//\a// Th
    e file header data structure can be stored in memory or on disk.\a// When it is
    on disk, it is stored in a single sector -- this
    means\a// that we assume the size of this data structure to be the same\a// as o
    ne disk sector.  Without indirect addressing, this
    \a// limits the maximum file length to just under 4K bytes.\a//\a// There is no
    constructor; rather the file header can be initiali
    zed\a// by allocating blocks for the file (if it is a new file), or by\a// readi
    ng it from disk.\a\aclass FileHeader {\a  public:\a
     bool Allocate(BitMap *bitMap, int fileSize);// Initialize a file header, \a\9\9
    \9\9\9\9//  including allocating space \a\9\9\9\9\9\9//  on di
    sk for the file data\a    void Deallocate(BitMap *bitMap);  \9\9// De-allocate t
    his file's \a\9\9\9\9\9\9//  data blocks\a\a    void FetchFr
    om(int sectorNumber); \9// Initialize file header from disk\a    void WriteBack(
    int sectorNumber); \9// Write modifications to file
     header\a\9\9\9\9\9//  back to disk\a\a    int ByteToSector(int offset);\9// Con
    vert a byte offset into the file\a\9\9\9\9\9// to the disk sect
    or containing\a\9\9\9\9\9// the byte\a\a    int FileLength();\9\9\9// Return the
     length of the file \a\9\9\9\9\9// in bytes\a\a    void Print();\9\9\9
    // Print the contents of the file.\a\a  private:\a    int numBytes;\9\9\9// Numb
    er of bytes in the file\a    int numSectors;\9\9\9// Numbe
    r of data sectors in the file\a    int dataSectors[NumDirect];\9\9// Disk sector
     numbers for each data \a\9\9\9\9\9// block in the file\a}
    ;\a\a#endif // FILEHDR_H\a
    
    Ticks: total 17970, idle 17490, system 480, user 0
    Disk I/O: reads 35, writes 0
    Console I/O: reads 0, writes 0
    Paging: faults 0
    Network I/O: packets received 0, sent 0
    
    Cleaning up...
    church[33] exit
    
    script done on Mon Mar 02 10:33:24 199