1 Tasks
The lab is divided into two sections of about five steps each. Each step is worth three points. The first section involves implementing a basic file system with one directory and single-block files. The second section involves implementing file operations such as read and write.
1.1 A Basic File System
In this section, you will create a file system in which all files share the same directory. We will assume that all files can fit into a single 1024 byte block. We will also observe the following limitations:
1. Filenames will contain only letters, numbers, underscores, and the period (dot) character.
2. Filenames will be a maximum of 15 characters long.
3. There will never be more than 4000 files in the file system.
4. Filenames must be unique to the directory.
The directory will consist of a series of “rows” corresponding to one file. Each row will contain a 15-character filename (terminated by a null byte for a total of 16 characters), a four-byte integer index which tells where in the file system the file starts, and a four-byte integer representing the size of the file. In other words, each entry consists of 24 bytes: the filename, which can take up to 16 bytes, the index, which takes 4 bytes, and the size, which takes 4 more.

1.1.1 Formatting the File System
We need some way of telling whether the values stored in each directory entry actually refer to real files or if they are simply garbage bytes. The easiest (though definitely not the ’best’) way to do this is to “format” the file system by copying some sort of sentinel character over the entire directory record. Since filenames cannot contain asterisk characters, we can use those for our sentinel.
In the “format” function of operations.cpp add code that copies asterisk characters into the over the entire directory structure of the m_disk array.
1.1.2 Creating a File
Creating a file really just means finding a free block and creating a directory entry that maps the filename to that block. For now, we are not going to worry about internal fragmentation. Finding a free block is the trickiest part. We don’t want to reuse a block that is being used by another file and we don’t have a FAT table or inode table, so we have to use the directory to find a free block.
Hint: Make a struct which represents a directory entry. Then cast the m_disk array to be an array of directory entries. You can then loop through those entries to check positions.
Your create function should return false if:
1. The directory is full and there is no more room for a file to be created.
2. The name of the new file matches the name of an existing file.
3. It is not possible to allocate a block for the file (for instance if the disk is full).
Otherwise, it should return true.
1.1.3 Listing the files in a directory
The “list” function should create a nicely formatted listing of all the files you have created and their sizes and return that listing as a string. It does not need to (and should not) include the position of the file since this is an implementation detail and not something that should be exposed to the user.
The filename should appear, left-justified, in a column 16 characters wide, padded with spaces.
The size should follow, left-justified, in a column 8 characters wide, padded with spaces. You do not need to (and should not) add commas or units to the size – simply use the number of bytes.
Be sure not to include any deleted or not yet created files. (You may want to come back to this part after doing the next step).
Hint: As for create, your best bet here is to cast the m_disk array to an array of directory entry objects and loop through them.
1.1.4 Deleting a File
To delete a file, we need to remove it from the directory. We can remove it by zeroing the filename entry out with * characters, but if we do that the entries that come after it could become unreachable (depending on how you implemented your list and create functions). A different solution is to swap the last entry of the directory with the one we are deleting and write the asterisks over the duplicate last entry. However, as long as your delete works properly (the file is removed from the directory listing and we can reuse its space for new files), you can take any approach you want.
Write code in the “rm” function that takes a string, the filename, as a parameter and removes that file from the directory if it exists.
The rm function should return false if the file to be deleted does not exist. It should return true if it is able to successfully delete the file.

1.1.5 Renaming a File
If the user wants to rename a file, all we have to do is modify the directory. Write a function named “rename” that scans the directory for the old filename. If it can’t find it, it should return false. If it does find it, it should scan through a second time to make sure that no other file is already using the new name (if so, return false) and then replace the old name with the new name.
1.1.6 Copying a File
To copy a file, we need to create a new directory entry and then copy the actual data from the first file’s data block to the second file’s data block. One way to implement this is to use the “create” function you wrote and then copy the data. However, you do need to do error checking to be sure that:
1. The source file exists.
2. There is space available (both in the directory and the disk array) for the copy.
1.2 File Operations
All of the functions we have written so far directly affect the external layout and the way files are presented to the user. They don’t directly allow you to access the data within a file. To actually store information in a file, we need to be able to read from the file and write to it.
We can do that more conveniently if we use “file handles” to keep some state information. We can manage the file handles using functions named “open” and “close”. Open and close are really just convenience functions. They make it easier to read and write so that you don’t have to remember a filename and file position every time you access a file.
1.2.1 Opening a File
The open function creates a “file handle” object that can be used in the read and write functions.
To help you out a bit, I have created a FileHandle class for you in handle.h. It has no functions – it’s really just a “struct” that keeps track of three things:
1. The position (in m_disk) of the start of the file
2. The size of the file.
3. An offset that keeps track of where we left off the last time we read from the file or wrote to it.
The open function should:
1. Create a new file handle object.
2. Set the file handle’s size and position fields by looking the filename up in the directory and copying values out of the directory entry.
3. Initially, set the offset to 0. (The “file cursor” starts at the beginning of the file)
4. Insert the file handle into some sort of list or array so that it can be used by read, write, and close. You will need to create this list. I recommend using a C++ vector, which you can declare at the top of operations.cpp.
5. Return a file descriptor which uniquely identifies the file handle.

The key part of this step is to create and manage sort sort of array or list that stores the file handles. We will call this the “open file list”. It’s probably best, actually, to store pointers to file handles rather than actual file handles so that you can free the memory for the handle when you close the file.
If you, instead, use an array, you will need an integer to keep track of the size of the list. You may assume that there are never more than 256 files open at a time. Open should return -1 if it cannot find the file to open or if there are no file descriptors left.
1.2.2 Closing a File
Closing a file means freeing the memory used by its file descriptor. If you implemented open correctly, it should be trivial. You just need to delete the memory you allocated for the file handle.
You do not need to worry about making the file descriptor available for new files. None of my tests will ever open more than 255 files (except to test that your open function correctly bails out when that happens). Close should return false if the file descriptor isn’t valid (does not correspond to a file handle that is open). Otherwise, it should return true.
1.2.3 Reading from a File
Now that you can create a file and open it up, you should be able to write code that reads a string from the file. The read function takes three parameters, the file descriptor to read from, a reference to the string which the data should be placed in (the “buffer” string), and the number of bytes to read.
Write code that:
1. Uses the file descriptor to obtain the file handle.
2. From the file handle, obtains the position of the block where the data is stored.
3. Adds the “offset” field to the position to obtain the location in the array where the data is located.
4. Reads “size” characters from that position of the m_disk array into the buffer string.
You should return false if the file descriptor is invalid or if reading would go past the end of the virtual disk. You should return true if the read succeeds.
1.2.4 Writing to a File
Writing to a file is almost identical to reading from one. You need to use the file descriptor to get the file handle, then use the position and offset from the file handle to locate the position at which to write. One difference is that when you write, the size of the file may change. If the last byte you write goes beyond the current size of the file, you will need to update the size in the directory entry.

Solution PreviewSolution Preview

These solutions may offer step-by-step problem-solving explanations or good writing examples that include modern styles of formatting and construction of bibliographies out of text citations and references. Students may use these solutions for personal skill-building and practice. Unethical use is strictly forbidden.

#include "filesys.h"
#include <sys/stat.h>
#include <fstream>
#include <iostream>

using namespace std;

Filesystem::Filesystem(string name, const unsigned int size = 4194304) : m_disk_filename(name) {

    //Check whether the file already exists in Linux
    struct stat file_info;
    int exists = stat(m_disk_filename.c_str(), &file_info);

    //If not, create a file.
    if (exists < 0) {
       //Allocate size (by default 4Mb) bytes of space
       m_disk_size = size;
       m_disk = new char[m_disk_size];

       //Write 0's to the file.
       ofstream disk_stream(m_disk_filename.c_str());
       for (int i = 0; i < m_disk_size; ++i) {
            disk_stream.put((char) 0);
            m_disk[i] = (char) 0;

       //All done. Close the file.

    } else {
       //Open the file.
       ifstream disk_stream(m_disk_filename.c_str());

       //Set m_disk_size to be the file's size.
       m_disk_size = file_info.st_size;

       //Allocate space
       m_disk = new char[m_disk_size];

       //Read in the data
       for (int i = 0; i < m_disk_size; ++i) {
            m_disk[i] = disk_stream.get();
       //All done. Close the file.

Filesystem::~Filesystem() {
    //Before deleting, save the array to disk.
    ofstream disk_stream...

By purchasing this solution you'll be able to access the following files:

for this solution

or FREE if you
register a new account!

PayPal, G Pay, ApplePay, Amazon Pay, and all major credit cards accepted.

Find A Tutor

View available Computer Science - Other Tutors

Get College Homework Help.

Are you sure you don't want to upload any files?

Fast tutor response requires as much info as possible.

Upload a file
Continue without uploading

We couldn't find that subject.
Please select the best match from the list below.

We'll send you an email right away. If it's not in your inbox, check your spam folder.

  • 1
  • 2
  • 3
Live Chats