CS 3113: Project 3
File System Implementation (Directory Structures)
Introduction
Hard disks organize data into fixed-sized blocks. When one wants to
fetch a particular byte, the entire block in which that byte lives
must be fetched. Likewise, when a single byte must be changed, the
entire block is first read into memory, the byte is changed and then
the entire block is written back to the disk.
As application-level programmers, however, we prefer to think in terms
of the file system abstraction: a file is a sequence of bytes of some
arbitrary length. It is convenient to program with this abstraction in
mind: we would like to be able to read a subsequence of the bytes from
a file or write a new subsequence of bytes (either overwriting
existing bytes in the file, or appending onto the file itself). During
these processes, we prefer not to think in terms of which blocks on
the hard disk that our bytes are coming from or going to. In addition,
the file system abstraction also provides us with a convenient and
logical way of finding files. Specifically, we use directories and
subdirectories, along with specific names within a directory that map
to specific files.
For projects 3 and 4, you will implement a miniature file system,
MYFS, that makes the connection between disk blocks and the file
system abstraction. We will use a real file on our Linux systems as a
virtual hard disk drive. This virtual disk will be accessed one block
at a time. Access to the bytes on your virtual disk will be provided
by the vdisk code that we provide.
We also provide the file system data structure and a few other
components. Your job in project 3 is to
implement a hierarchical directory structure. In project 4, you will
add files (with content!) to the file system. Specifically, as part of
project 3, you will:
-
Format the virtual disk with an initial file system. This initial
file system will contain a root directory with . and .. already
initialized.
- Provide an API of system calls (we won't actually be
doing traps, but instead we will be calling functions in a
library). These system
calls include functionality such as
creating/deleting directories and listing the contents of a
directory. These will be implemented in myfs_lib.c/h
- Provide a set of helper functions that make your API easier to
implement. These will be implemented in
myfs_lib_support.c/h
Objectives
By the end of this project, you should be able to:
- Describe the logical data structures that are used by an OS
to represent directories and files.
- Show how these logical data structures change under standard
file system operations.
- Map these logical data structures onto a block-level data
storage device.
- Manipulate the block-level data storage device under standard
file system operations.
Overview
The diagram below shows the relationship of the different components
that we are implementing. Starting from the bottom:
- vdisk (PROVIDED) implements a block-level storage
device. This device allows read/write operations at the level
of individual blocks of bytes. The storage of this block-level
device is a Linux file (hence, this is a virtual disk)
- myfs_lib_support (TO BE IMPLEMENTED, mostly) provides
reusable functionality for manipulating different parts of the
file system at the block level
- myfs_lib (TO BE IMPLEMENTED) provides a set of virtual
system calls that make up the user API
- application programs (PROVIDED) include:
- myfs_inspect: a program for examining the
block-level structure of the disk. This program is about
debugging and testing your code, and is not a user program
- myfs_stats: a program that prints out the sizes
of the various structures on the disk (including blocks
and index nodes)
- myfs_format: format the virtual disk
- myfs_list: list an existing file or the contents of
an existing directory
- myfs_mkd: make a directory
- myfs_rmd: remove a directory
|
|
Proper Academic Conduct
The code solution to this project must be done individually. Do not
copy solutions to this problem from the network and do not look at or
copy solutions from others. However, you may use the net for
inspiration and you may discuss your general approach with
others. These sources must be documented in your README file.
Logical Representation of the File System
Index Nodes
Index nodes contain the meta-data for a logical entity that is stored in
the file system (either a file or a directory). The data inside the
index node include:
- Index node type is one of: T_UNUSED, T_DIRECTORY, T_FILE, or
T_PIPE
- references: the number of references to the index node by a
directory. For index nodes that correspond to directories,
references will always be 1. For index nodes that
correspond to
files, this value can be any positive number. However, when a
file is first created, this count will be 1 (more on this in
project 4)
- content: a BLOCK_REFERENCE to the block that contains the
information associated with the index node
- size: for directories, this counts the number of items within
the directory. For files, this counts the number of bytes in
the file.
All index nodes in the file system are referenced with an integer (of type
INDEX NODE_REFERENCE). An INDEX NODE_REFERENCE can be the following values:
- UNALLOCATED_INDEX NODE: refers to an index node that does not
exist (we will use it like a NULL pointer)
- Other non-negative integers: refer to an index node that is
stored on the disk.
Directory Entries
A single directory entry contains the following information:
- name: a null-terminated string that is the name of the
file or directory relative to the parent directory. Note that
this space is fixed in size; any
names that are too long to fit in this space are truncated so
that they will fit (along with the null termination)
- index_node_reference: the number of the index node that
corresponds to the name. If the directory entry is not used,
then this is set to UNALLOCATED_INDEX NODE
Blocks
A block is a fixed-size unit of storage on the virtual disk. Each
block on the disk is referenced using an integer; the type is
BLOCK_REFERENCE. A BLOCK_REFERENCE can be one of the following
values:
- UNALLOCATED_BLOCK: refers to a block that does not exist
- 0 ... n_blocks-1: identifies a unique block on
the disk
All blocks contain BLOCK_SIZE bytes, which are allocated accordingly:
- next_block: a BLOCK_REFERENCE to the block that is next in a
linked list of blocks. If there is no next block, then this is
set to UNALLOCATED_BLOCK
- content: the remaining bytes in the block (a total of
DATA_BLOCK_SIZE bytes) that actually store the data in the block
Depending on the context, the data stored in the block can be
interpreted in one of several ways:
- data: bytes within a file
- volume: the virtual disk volume block
- index_nodes: an array of N_INDEX_NODES_PER_BLOCK index
nodes
- directory: an array of N_DIRECTORY_ENTRIES_PER_BLOCK
directory entries
Volume Block
The volume block contains several key components:
- n_blocks: the number of blocks that exist on the virtual
disk
- n_allocated_blocks: the number of blocks that are
currently being used to store information on the disk
- n_allocated_index_nodes: the number of index nodes that
are currently being used to represent directories or files
- block_allocation_table: a bitmapped representation of
which blocks are currently allocated
There is a total of n_blocks blocks
on the virtual disk (this number is no more than MAX_BLOCKS).
The block allocation table consists of an array of unsigned
chars (bytes), where each block is mapped to one bit in this
array. The mapping is as follows:
- Block 0: byte 0, bit 0
- Block 1: byte 0, bit 1
- Block 2: byte 0, bit 2
- Block 3: byte 0, bit 3
- Block 4: byte 0, bit 4
- Block 5: byte 0, bit 5
- Block 6: byte 0, bit 6
- Block 7: byte 0, bit 7
- Block 8: byte 1, bit 0
- :
In general, block i corresponds to byte i/8 and bit
i%8 in the table.
A bit value of 1 indicates that the block is being used; a bit value
of 0 indicates that the block is free to be allocated. Unused bits
are left at 0.
Index Node Blocks
Because index nodes are much smaller than blocks, we pack multiple
index nodes into the block. This is represented within an index node
block as an array of individual index nodes (a total of
N_INDEX_NODES_PER_BLOCK).
When the file system is first formatted, one block is allocated to
representing index nodes. However, as needs grow, additional blocks
are allocate. The list of blocks used for the index nodes is
implemented as a linked list of blocks.
Directory Blocks
Because directory entries are much smaller than a block, we fit
N_DIRECTORY_ENTRIES_PER_BLOCK directory entries within the block. As
with index nodes, these are implemented as an array of directory
entries.
When the file system is first formatted, one block is permanently
allocated to represent the content of the root directory. In
addition, as new directories are created, they are allocated a single
block to represent their content. However, as the directory grows in
size, more directory blocks are allocated, forming a linked list of
blocks.
Logical Structure Examples
Format
Given the following command:
./myfs_format 128
The format command creates a disk that contains 128 blocks. After the
format, the logical structure will be as follows:
- Block 0: Volume block (permanent)
- Block 1: Index node block for the root directory (permanent)
- Block 2: Directory block for the root directory (permanent)
- All other blocks: unused
Notes:
- The next block for all blocks is initially UNALLOCATED_BLOCK.
- An index node is one element in an index node block.
- All unused index nodes must have their content property
set to UNALLOCATED_BLOCK.
- Debugging is easier if all bytes in each block are set to zero
before setting up the data structure.
Create Directory
Given the following command:
./myfs_mkd foo
The logical structure will change to be:
Notes:
Create Another Directory
Given the next command:
./myfs_mkd foo/bar
The logical structure will change to be:
Remove Directory
Given the next command:
./myfs_rmd foo/bar
The logical structure will change back to the case above with just the
foo directory
Virtual Disk Layout
The layout of an initial file system is discussed above. Below are
some more general notes.
- Block 0 (Use VOLUME_BLOCK_REFERENCE to refer to this block):
volume block
- Block 1 (Use ROOT_INDEX_NODE_BLOCK): the first index node block in a
linked list of
blocks. Initially, this linked list only contains block 1, but as
the existing set of index node blocks are filled, additional
blocks are allocated and appended onto this linked list.
Note: once additional blocks are allocated to this list, they
are never deallocated.
- Block 2 (ROOT_DIRECTORY_BLOCK): the first directory block in
the linked list for the file system root directory.
- Any directory in the file system will have exactly one index
node
- Any directory in the file system will have at least one
directory block in its linked list. When a new file or
directory is added to a parent directory and there are no
available directory entries, then a new block is allocated and
added to this linked list.
Note: once additional blocks are allocated to this list, they
are never deallocated unless the entire directory is deleted.
|
|
Virtual Disk API
The virtual disk API is provided. This API implements block-level
virtual disk read/write functionality.
#ifndef VDISK_H
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
typedef unsigned short BLOCK_REFERENCE;
// Size of a single block in bytes
#define BLOCK_SIZE 256
// Maximum number of blocks on a disk
#define MAX_BLOCKS 1948
int vdisk_open(char *virtual_disk_name, int truncate_flag);
int vdisk_close();
int vdisk_read_block(BLOCK_REFERENCE block_ref, void *block);
int vdisk_write_block(BLOCK_REFERENCE block_ref, void *block);
#endif
Before any file system operations are performed, an application program
must open the virtual disk. Likewise, when the application
program is complete, it must close the disk.
The MYFS API interacts with the virtual disk at the block
level: it can read from and write to single blocks.
This will be your only interface to storage (do
not circumvent the virtual disk API in your implementation of the MYFS
API).
When your MYFS library is manipulating the virtual disk, it does the
manipulations in memory. This means that when you wish to change the
contents of a block on the virtual disk, you must first read the block
into memory, make the changes there, and then write the block back to
the virtual disk. Hence, your program will typically
have one or two blocks and one or two index nodes in memory at any one
time (but rarely any more).
MYFS Data Structures
The MYFS data structure is provided and must not be changed. Below
are the specifics of this structure:
#ifndef FILE_STRUCTS_H
#define FILE_STRUCTS_H
#include <string.h>
#include <limits.h>
#include "vdisk.h"
// Implementation of min operator
#define MIN(a, b) (((a) > (b)) ? (b) : (a))
/**********************************************************************/
/*
File system layout onto disk blocks:
Block 0: Volume block
Block 1: First Index Node block
Block 2: Root directory block
:
:
(all other blocks are either index node, directory or data blocks)
*/
/**********************************************************************/
// Basic types and sizes
// Chosen carefully so that all block types pack nicely into a full block
//
// NOTE: USE THESE CONSTANTS INSTEAD OF HARD-CODING VALUES IN YOUR CODE
// An index for a block (0, 1, 2, ...)
typedef unsigned short BLOCK_REFERENCE;
// Value used as an index when it does not refer to a block
#define UNALLOCATED_BLOCK (USHRT_MAX-1)
// An index that refers to an index node
typedef unsigned short INDEX_NODE_REFERENCE;
// Value used as an index when it does not refer to an index node
#define UNALLOCATED_INDEX_NODE (USHRT_MAX)
// Number of bytes available for block data
#define DATA_BLOCK_SIZE ((int)(BLOCK_SIZE-sizeof(int)))
// The block on the virtual disk containing the root directory
#define ROOT_DIRECTORY_BLOCK 2
// The block on the virtual disk containing the first index nodes
#define ROOT_INDEX_NODE_BLOCK 1
// The Index node for the root directory
#define ROOT_DIRECTORY_INDEX_NODE 0
// Size of file/directory name
#define FILE_NAME_SIZE ((int)(32 - sizeof(INDEX_NODE_REFERENCE)))
/**********************************************************************/
// Data block: storage for file contents (project 4!)
typedef struct
{
unsigned char data[DATA_BLOCK_SIZE];
} DATA_BLOCK;
/**********************************************************************/
// Index node types
typedef enum {T_UNUSED=0, T_DIRECTORY, T_FILE, T_PIPE} INDEX_NODE_TYPE;
// Single index node
typedef struct
{
// Type of INDEX_NODE
INDEX_NODE_TYPE type;
// Number of directory references to this index node
unsigned char references;
// Contents. UNALLOCATED_BLOCK means that this entry is not used
BLOCK_REFERENCE content;
// File: size in bytes; Directory: number of directory entries
// (including . and ..)
unsigned int size;
} INDEX_NODE;
// Number of index nodes stored in each block
#define N_INDEX_NODES_PER_BLOCK ((int)(DATA_BLOCK_SIZE/sizeof(INDEX_NODE)))
// Block of index_nodes
typedef struct
{
INDEX_NODE index_node[N_INDEX_NODES_PER_BLOCK];
} INDEX_NODE_BLOCK;
/**********************************************************************/
// Block 0: Volume block
#define VOLUME_BLOCK_REFERENCE 0
typedef struct
{
int n_blocks; // Total number of blocks
int n_allocated_blocks; // Allocated == used
int n_allocated_index_nodes;
// 8 blocks per byte: One block per bit: 1 = allocated, 0 = free
// Block 0 (zero) is byte 0, bit 0
// 1 is byte 0, bit 1
// 8 is byte 1, bit 0
unsigned char block_allocation_table[(MAX_BLOCKS+7)>>3];
}VOLUME_BLOCK;
/**********************************************************************/
// Single directory element
typedef struct
{
// Name of file/directory
char name[FILE_NAME_SIZE];
// UNALLOCATED_INDEX_NODE if this directory entry is non-existent
INDEX_NODE_REFERENCE index_node_reference;
} DIRECTORY_ENTRY;
// Number of directory entries stored in one data block
#define N_DIRECTORY_ENTRIES_PER_BLOCK ((int)(DATA_BLOCK_SIZE / sizeof(DIRECTORY_ENTRY)))
// Maximum number of files that can be contained in a directory (note, a directory can span multiple blocks)
#define MAX_ENTRIES_PER_DIRECTORY (N_DIRECTORY_ENTRIES_PER_BLOCK * 10)
// Directory block
typedef struct directory_block_s
{
DIRECTORY_ENTRY entry[N_DIRECTORY_ENTRIES_PER_BLOCK];
} DIRECTORY_BLOCK;
/**********************************************************************/
// All-encompassing structure for a disk block
// The union says that all 4 of these elements occupy overlapping bytes in
// memory (hence, a block will only be one of these 4 at any given time)
typedef struct
{
// Next block in a linked list (if this block belongs to one)
BLOCK_REFERENCE next;
union {
DATA_BLOCK data;
VOLUME_BLOCK volume;
INDEX_NODE_BLOCK index_nodes;
DIRECTORY_BLOCK directory;
} content;
} BLOCK;
/**********************************************************************/
// Representing files (project 4!)
#define MAX_BLOCKS_IN_FILE 1000
typedef struct
{
INDEX_NODE_REFERENCE index_node_reference;
char mode;
int offset;
// Cache for file content details. Use of these is optional
int n_data_blocks;
BLOCK_REFERENCE block_reference_cache[MAX_BLOCKS_IN_FILE];
} MYFILE;
#endif
Environment Variables
We use two environment variables to provide important context to the
executables:
- MYFS_CWD: the current working directory within MYFS
- MYFS_DISK: the name of the storage file
We provide a function that reads these environment variables and fills
in reasonable default values if they do not exist.
MYFS Application Programs
The high-level implementation of all of the MYFS application programs
is given. However, you will be responsible for implementing the
underlying system calls.
myfs_format [size]
Initialize the entire file system with a total of size blocks (size is
optional).
- Zero out all blocks on the disk
- Initialize the volume block
- Indicate that index node 0 has been allocated (and no
other)
- Indicate that blocks 0, 1 and 2 have been allocated
- Set the next reference to UNALLOCATED_BLOCK
- Initialize first index node block
- Index node 0 is the root directory index node
- Mark all other index nodes in the block as unused
- Initialize the root directory block.
This new directory includes two entries, "." and "..", both of
which refer to index node 0. All other entries refer to
UNALLOCATED_INDEX_NODE.
myfs_list [name]
- name is optional and can either be a relative or an
absolute path to a file or directory. If the former, then the
MYFS current working directory is prepended to the name. If name is
not specified, then the current working directory is used
- If a file is specified, then the name of the file is shown (but not its
parent directory/directories)
- If a directory is specified, then:
- All valid directory entries are printed, one per line
- If an entry is a directory, then "/" is appended to the name
- The directory entries are listed in ASCII order
(including the / at the end of the directory names). Hint:
see the qsort() library function
myfs_mkd name
Create a new directory. The behavior is:
- It is an error if name exists (remember that all errors are
printed to STDERR)
- It is an error if the parent of name does not exist
- The first available index node must be used for the new
directory.
- If there are no unused index node entries in the
existing block linked list, then a new block must be
allocated and appended onto the end of the index node
linked list. This new block is the available block with
the smallest reference value.
- It is an error if a new block cannot be allocated
- The first available entry in the parent directory block linked
list must be used.
- If there are no unused directory entries in the existing
directory block linked list, then a new block must be
allocated and appended onto the end of the parent
directory's directory block linked list. This new block
is the available block with
the smallest reference value.
- It is an error if a new block cannot be allocated.
- A new block is allocated for the contents of the new
directory. This new block is the available block with
the smallest reference value.
- The new directory must be initialized to be be empty, except
that it must have entries for "." (referring to its own index node)
and ".." (referring to its parent's index node)
- The parent's index node size property must be incremented
- Note: if a new index node block must be allocated, followed by
the directory block for the new directory and there is only one
available block in the file system, then this is an error and
the file system must not be changed.
- Note: any block allocation changes the volume block
myfs_rmd name
Remove a directory. The behavior is:
- It is an error if name does not exist
- It is an error if name is not a directory
- It is an error if the entry name is "." or ".."
- It is an error if name is not an empty directory (size=2 is an
empty directory, meaning that it only contains . and ..)
- Remove the entry from the parent's directory block
- Deallocate the index node and all of the directory's content blocks
- Modify the freed block
- Modify the volume block
- The parent's index node size property must be decremented
- Note: the number of index node blocks in the linked list is not
changed
- Note: the number of the parent's directory blocks is not changed
Example Interaction
Note that below, you are seeing a mixture of the commands that are
typed and the outputs from the programs. Also note that all error
messages are printed via STDERR.
Simple Interaction
$ ./myfs_format 128
$ ./myfs_list
../
./
$ ./myfs_mkd a
$ ./myfs_list
../
./
a/
$ ./myfs_mkd ab
$ ./myfs_list
../
./
a/
ab/
$ ./myfs_list a
../
./
$ ./myfs_list /a
../
./
$ ./myfs_mkd a/abc
$ ./myfs_list a
../
./
abc/
$ ./myfs_list /a
../
./
abc/
$ ./myfs_list /a/abc
../
./
$ ./myfs_list /a/def
Not found
$ ./myfs_rmd a
Directory not empty
$ ./myfs_rmd a/abc
$ ./myfs_rmd a
$ ./myfs_list
../
./
ab/
$ ./myfs_list ab
../
./
Interaction with FS Checks
Using myfs_inspect, you can examine the details of your file
system at the block and index node levels. It is a useful tool for
debugging your code.
$ ./myfs_stats
BLOCK_SIZE: 256
DATA_BLOCK_SIZE: 252
VOLUME_BLOCK_SIZE: 252
INDEX_NODE_BLOCK_SIZE: 252
BLOCK_REFERENCE_SIZE: 2
DIRECTORY_BLOCK_SIZE: 224
MAX_BLOCKS: 1920
UNALLOCATED_BLOCK reference: 65534
UNALLOCATED_INDEX_NODE reference: 65535
DATA_BLOCK_SIZE: 252
INDEX_NODES_PER_BLOCK: 21
DIRECTORY_ENTRIES_PER_BLOCK: 7
$ ./myfs_format 128
$ ./myfs_inspect -help
Usage:
myfs_inspect -volume Show the volume block
myfs_inspect -help Print this help
myfs_inspect -index <#> Print contents of INDEX_NODE #
myfs_inspect -iblock <#> Print index node contents of block #
myfs_inspect -dir <#> Print the contents of directory block #
myfs_inspect -block <#> Print the top-level block data for block #
myfs_inspect -data <#> Print the raw data contents for the block (including printable characters)
$ ./myfs_inspect -volume
N_BLOCKS: 128
N_ALLOCATED_BLOCKS: 3
N_ALLOCATED_INDEX_NODES: 1
Block allocation table:
00: 07
01: 00
02: 00
03: 00
04: 00
05: 00
06: 00
07: 00
08: 00
09: 00
10: 00
11: 00
12: 00
13: 00
14: 00
15: 00
$ ./myfs_inspect -index 0
Index node: 0
Type: DIRECTORY
Nreferences: 1
Content block: 2
Size: 2
$ ./myfs_inspect -dir 2
Directory at block 2:
Entry 0: name=".", index_node=0
Entry 1: name="..", index_node=0
Next block: 65534
$ ./myfs_inspect -iblock 1
Relative Index Node 0 DIRECTORY Nref=1 Content=2 size=2
Next block: 65534
$ ./myfs_mkd foo
$ ./myfs_inspect -volume
N_BLOCKS: 128
N_ALLOCATED_BLOCKS: 4
N_ALLOCATED_INDEX_NODES: 2
Block allocation table:
00: 0f
01: 00
02: 00
03: 00
04: 00
05: 00
06: 00
07: 00
08: 00
09: 00
10: 00
11: 00
12: 00
13: 00
14: 00
15: 00
$ ./myfs_inspect -dir 2
Directory at block 2:
Entry 0: name=".", index_node=0
Entry 1: name="..", index_node=0
Entry 2: name="foo", index_node=1
Next block: 65534
$ ./myfs_inspect -index 1
Index node: 1
Type: DIRECTORY
Nreferences: 1
Content block: 3
Size: 2
$ ./myfs_inspect -dir 3
Directory at block 3:
Entry 0: name=".", index_node=1
Entry 1: name="..", index_node=0
Next block: 65534
$ ./myfs_mkd foo/bar
$ ./myfs_inspect -volume
N_BLOCKS: 128
N_ALLOCATED_BLOCKS: 5
N_ALLOCATED_INDEX_NODES: 3
Block allocation table:
00: 1f
01: 00
02: 00
03: 00
04: 00
05: 00
06: 00
07: 00
08: 00
09: 00
10: 00
11: 00
12: 00
13: 00
14: 00
15: 00
$ ./myfs_inspect -index 0
Index node: 0
Type: DIRECTORY
Nreferences: 1
Content block: 2
Size: 3
$ ./myfs_inspect -dir 2
Directory at block 2:
Entry 0: name=".", index_node=0
Entry 1: name="..", index_node=0
Entry 2: name="foo", index_node=1
Next block: 65534
$ ./myfs_inspect -index 1
Index node: 1
Type: DIRECTORY
Nreferences: 1
Content block: 3
Size: 3
$ ./myfs_inspect -dir 3
Directory at block 3:
Entry 0: name=".", index_node=1
Entry 1: name="..", index_node=0
Entry 2: name="bar", index_node=2
Next block: 65534
$ ./myfs_inspect -index 2
Index node: 2
Type: DIRECTORY
Nreferences: 1
Content block: 4
Size: 2
$ ./myfs_inspect -dir 4
Directory at block 4:
Entry 0: name=".", index_node=2
Entry 1: name="..", index_node=1
Next block: 65534
$ ./myfs_list
../
./
foo/
$ ./myfs_list foo
../
./
bar/
$ ./myfs_list foo/bar
../
./
$ ./myfs_inspect -iblock 1
Relative Index Node 0 DIRECTORY Nref=1 Content=2 size=3
Relative Index Node 1 DIRECTORY Nref=1 Content=3 size=3
Relative Index Node 2 DIRECTORY Nref=1 Content=4 size=2
Next block: 65534
$ ./myfs_mkd baz
$ ./myfs_list
../
./
baz/
foo/
$ ./myfs_inspect -iblock 1
Relative Index Node 0 DIRECTORY Nref=1 Content=2 size=4
Relative Index Node 1 DIRECTORY Nref=1 Content=3 size=3
Relative Index Node 2 DIRECTORY Nref=1 Content=4 size=2
Relative Index Node 3 DIRECTORY Nref=1 Content=5 size=2
Next block: 65534
$ ./myfs_inspect -index 0
Index node: 0
Type: DIRECTORY
Nreferences: 1
Content block: 2
Size: 4
$ ./myfs_inspect -dir 2
Directory at block 2:
Entry 0: name=".", index_node=0
Entry 1: name="..", index_node=0
Entry 2: name="foo", index_node=1
Entry 3: name="baz", index_node=3
Next block: 65534
$ ./myfs_inspect -volume
N_BLOCKS: 128
N_ALLOCATED_BLOCKS: 6
N_ALLOCATED_INDEX_NODES: 4
Block allocation table:
00: 3f
01: 00
02: 00
03: 00
04: 00
05: 00
06: 00
07: 00
08: 00
09: 00
10: 00
11: 00
12: 00
13: 00
14: 00
15: 00
$ ./myfs_rmd foo/bar
$ ./myfs_inspect -volume
N_BLOCKS: 128
N_ALLOCATED_BLOCKS: 5
N_ALLOCATED_INDEX_NODES: 3
Block allocation table:
00: 2f
01: 00
02: 00
03: 00
04: 00
05: 00
06: 00
07: 00
08: 00
09: 00
10: 00
11: 00
12: 00
13: 00
14: 00
15: 00
$ ./myfs_inspect -iblock 1
Relative Index Node 0 DIRECTORY Nref=1 Content=2 size=4
Relative Index Node 1 DIRECTORY Nref=1 Content=3 size=2
Relative Index Node 3 DIRECTORY Nref=1 Content=5 size=2
Next block: 65534
$ ./myfs_list foo
../
./
$ ./myfs_inspect -dir 3
Directory at block 3:
Entry 0: name=".", index_node=1
Entry 1: name="..", index_node=0
Next block: 65534
$ ./myfs_rmd foo
$ ./myfs_inspect -volume
N_BLOCKS: 128
N_ALLOCATED_BLOCKS: 4
N_ALLOCATED_INDEX_NODES: 2
Block allocation table:
00: 27
01: 00
02: 00
03: 00
04: 00
05: 00
06: 00
07: 00
08: 00
09: 00
10: 00
11: 00
12: 00
13: 00
14: 00
15: 00
$ ./myfs_list
../
./
baz/
$ ./myfs_inspect -iblock 1
Relative Index Node 0 DIRECTORY Nref=1 Content=2 size=3
Relative Index Node 3 DIRECTORY Nref=1 Content=5 size=2
Next block: 65534
$ ./myfs_inspect -dir 2
Directory at block 2:
Entry 0: name=".", index_node=0
Entry 1: name="..", index_node=0
Entry 3: name="baz", index_node=3
Next block: 65534
Checklist
Write / Implement the following files / functions.
Supporting Materials
MYFS API (Library)
The documentation for the following can be found in the skeleton
file. Your contributions are in bold.
- myfs_format_disk()
- myfs_list()
- myfs_mkd()
- myfs_rmd()
OUFS API Support
These helper functions are to be used only by the API. Your
contributions are in bold. The
documentation for the following can be found in the skeleton file.
Take the time to read/understand the functions that we provide.
They are good examples of how to do things and will help a lot.
- myfs_set_index_node()
- myfs_read_index_node_by_reference()
- myfs_write_index_node_by_reference()
- myfs_init_directory_block_and_index_node()
- myfs_find_entity_in_directory()
- myfs_path_to_index_node()
- myfs_find_directory_hole()
- myfs_find_index_node_hole()
- myfs_allocate_new_block()
- myfs_remove_directory_entry()
- myfs_find_open_bit()
- myfs_deallocate_blocks()
- myfs_clear_all_directory_entries()
- myfs_append_new_block_to_existing_block()
- myfs_clear_all_index_node_entries()
Submitting Your Program
- Your final submission is composed of the files required to compile
all of the executables. Those highlighted in bold are the ones
that you are to create / complete. All others can be handed-in
as provided:
- myfs_format.c
- myfs.h
- myfs_inspect.c
- myfs_lib.c/h
- myfs_lib_support.c/h
- myfs_list.c
- myfs_mkd.c
- myfs_rmd.c
- myfs_stats.c
- vdisk.c/h
- Makefile: does the following:
- all: compiles all executables
- clean: deletes the executable files, any
intermediate files (.o, specifically)
- README.txt: documentation
- Include your name and project number at the top of the file
- Document any Internet resources that you
consulted (URLs). State NONE if there are none.
- Document any peer class members that you
discussed your solution with. Note that you may
not look at or share code that solves this
specific problem . State NONE if there are none.
- Documentation requirements: function-level and inline
documentation are important components of writing code. They help
you to organize your thoughts while programming and help to
communicate the method and the intent of the code to your future
self or to collaborators (of course, you will not have collaborators
in this class). While we will not be specifically grading
documentation in this class, we will not be able to comment on your
code unless it is sufficiently documented. This will be true whether
you are asking for help before the submission deadline or looking
for feedback on your solution after the deadline. In short, you
should take the time to properly document your code as you develop
it.
- Submit your files:
- Create a zip file named project3.zip containing
your source code (c and h files), Makefile and README.txt file. The
following Linux shell command will do this for you:
zip project3.zip *.c *.h Makefile README.txt
Note I: this is a good thing to add to your Makefile.
Note II: do not include object files, executables or
temporary files in your zip file.
Note III: if you are using any other mechanism for
zipping, make sure that you do not include the directory
that contains all of the files (your zip file should
only contain the listed files).
- Submit your zip file to Gradescope (access via Canvas).
Shortly after your submission, you will receive
automated feedback and a grade for the
correctness component of your grade.
- You may submit multiple times until the deadline.
Grading Criteria
For this project, we have a sequence of deadlines. They are:
Downloads
The following file contains several header and skeleton C files: project3_skel.tar. These skeleton
files are good starting points for the source files that you must
implement.
Hints
Addenda
- 2020-10-23: Small updates to the document. Added discussion at
the top on Index Node and Directory blocks.
- 2020-10-26: Update to myfs.h (the volume block was too large
to fit in the 256 bytes). The tests have also been updated.
All changes are in the tar file.
Updated the example interactions with the new myfs.h
- 2020-10-29: Update to myfs_lib_support.c (in current skeleton):
added the following line to myfs_find_entity_in_directory:
if(block.content.directory.entry[i].index_node_reference != UNALLOCATED_INDEX_NODE &&
strncmp(block.content.directory.entry[i].name, element_name, FILE_NAME_SIZE) == 0) {
- 2020-11-01: All 3 submission deadlines have been adjusted.
- 2020-11-05: The ordering of the bits relative to the blocks in the block allocation table is that block 0 corresponds to byte 0, bit 0; block 1 is byte 0, bit 1, etc. This is documented above. However, the find_open_bit() documentation that we gave you in myfs_lib_skel.c suggested that the bits are ordered in the opposite way. Stick with what is documented here.
- 2020-11-09: For part b of the project, we have included all of
the part a tests. However, these have been set to a weight of
zero points. If you are failing these tests, gradescope will
still give you an error message. However, its summary suggests
through coloring that you passed the test. So, look carefully
at the detailed text that you get back.
- 2020-11-10: when you call path_to_index_node(), make sure that local_name has MAX_PATH_LENGTH bytes allocated to it.
andrewhfagg at gmail.com
Last modified: Wed Nov 18 14:40:26 2020