On disk, both the list of free sectors and the top-level directory are stored within regular Nachos files. The free list is stored as a bit map, one bit per sector (e.g., allocated or free), with the file itself stored in fnode 0. Thus, finding a free sector in the filesystem requires reading the file associated with fnode 0, using the bitmap functions to locate free sector(s), and then flushing the file changes back to disk (via the appropriate WriteBack routines) to make the changes permanent.
Likewise, the top-level directory is stored in a file associated with fnode 1. Updating the directory when creating a new file requires reading the file associated with fnode 1, finding an unused directory entry, initializing it, and then writing the directory back out to disk.
When a file system is created, the system initializes the freelist and top-level directory and then opens the files containing the two main data structures. Thus, the current contents of the free list and directory (as stored on disk) can be accessed via the ``OpenFile *'' variables freeMapFile and directoryFile.
When creating and modifying Nachos files, one must be careful to keep track of what data structures are on disk, and which ones are in memory. For example, when creating a file, a new FileHeader must be allocated, together with sectors to hold the data (free list file). The top-level directory must also be updated. However, these changes don't become permanent until the updated free list and directory files are flushed to disk.
If one traces through the code for FileSystem::Create, one sees that all the allocations and updates are first made in memory local to the calling thread, and only after all allocations have succeeded without error are the changes made permanent by committing them to disk. This approach greatly simplifies error recovery when (say) there are enough free blocks to create the file, but the directory has no room to hold the new file. By not flushing to disk the allocations that succeeded, they do not actually take effect, and cancelling the entire transaction is straightforward.
Note also that the supplied FileSystem code assumes that only a single thread accesses the filesystem at any one time. Specifically, consider what would happen if two threads attempted to create a file simultaneously. One scenario would have both threads fetching the current freemap, both updating their local copies, and then both writing them later. At best, only one set of changes will appear on disk. At worst, the disk becomes corrupted.
No attempt has been made to reduce disk latencies by clustering related blocks in adjacent sectors.