In this assignment, you will practice pointers and dynamic memory. It is challenging, so you are highly recommended to start early. If you need clarification of the requirements, please feel free to post on the Piazza with the pa3 tag. However, to avoid cluttering the forum with repeated/trivial questions, please do read all the given code, webpage description, sample output, and latest FAQ (refresh this page regularly) carefully before you post your questions. Also please be reminded that we won't debug any student's assignment for the sake of fairness.
We value academic integrity very highly. Please read the Honor Code section on our course webpage to make sure you understand what is considered as plagiarism and what the penalties are. The following are some of the highlights:
Download the skeleton codes HERE. Please download and open it now, as we will refer to it from time to time in the description below.
We had an introduction session on Apr 22. Here is the zoom recording and the slides.
In this assignment, you will implement a simple file system. Just like the one in your computer, our file system is a tree of directories and files, where a directory could contain other directories and files, but a file cannot. In file_sys.h, you can find the definition of two structures, Dir and File. These are the two structures that we use to represent directories and files in this assignment. Here are the meanings of their attributes:
Dir
char name[MAX_NAME_LEN]: the name of the directory, it's a C-string (character array) with a null character at the end.Dir* parent: a pointer to the parent directory.Dir* subdir: the head of a linked list that stores the sub-directories.File* subfile: the head of a linked list that stores the sub-files.Dir* next: a pointer to the next directory in the linked list.File
char name[MAX_NAME_LEN]: the name of the file, it's a C-string (character array) with a null character at the end.Dir* parent: a pointer to the parent directory.unsigned int size: the size of the file.Tag tag: the tag of the file (introduced below).File* next: a pointer to the next file in the linked list.You will also find an enumeration type enum Tag{VIDEO, IMAGE, DOC, COMPRESSED, PROGRAM, MUSIC, OTHER}. There are 7 different tags in this assignment, and each file will have one and only one tag.
Then we use an example to elaborate more on the pointers in Dir and File. The figure below shows a file system from a user's view (left side) and how it looks in this assignment (right side). We only show the pointers here for simplicity. In this example, the directory "/" has two sub-directories "A", "B" and one sub-file "C". Directory "A" is empty, and directory "B" has two sub-files "D" and "E".
parent pointer of a directory or a file will always point to the directory which contains it, so here the parent pointers of "A", "B" and "C" are all pointing to "/", while the parent pointer of "D" and "E" is pointing to "B". However, directory "/" is a bit special, it is a root directory. The root directory doesn't have a parent, just like a root node in the tree doesn't have a parent node. In this assignment, we let the parent pointer of the root directory point to itself. This is only applicable to the root directory, any other directories' parent pointer must point to a different directory. In our file system, there will be one and only one root directory. For any files or directories, if you keep moving to the parent, you should always reach the root directory eventually. subfile pointer will point to the first sub-file in the linked list. If there is no sub-file, the pointer will be null. So here the subfile pointer of "/" points to "C", and the subfile pointer of "B" points to "D". Directory "A" doesn't have sub-files, so its subfile pointer is null. Why "D" is in front of "E" and "A" is in front of "B" in the linked list? This actually depends on how we insert into the linked list and will be introduced in that specific function later.subdir pointer is very similar to the subfile pointer. You can just take a look at the subdir pointer of "/", "A" and "B".next pointer points to the next file or the next directory in a linked list. For example, "A" points "B" and "D" points "E". If it is the last element in the linked list, the next pointer will naturally be null like "B", "C" and "E" here. It is also clear that the root directory could never have a sibling directory, so the next pointer of the root directory will always be null.Finally, you don't have to worry whether a name is too long, we won't test any cases with names beyond MAX_NAME_LEN. The name of the root directory is, by convention, always "/".
There are a few things you CANNOT do. Failure to observe these rules would potentially result in ZERO mark for your assignment. Please carefully check your code before you submit it.
static int x. You can only use those provided in the skeleton codes.There are 8 functions you need to implement in this section which are all file system operations. Implement these functions and your own helper functions in file_sys.cpp.
int createFile(Dir* dir, char* name, unsigned int size, Tag tag)Dir* dir: the newly created file will be placed under this directory.char* name: the name of the file to be created.unsigned int size: the size of the file to be created.Tag tag: the tag of the file to be created.name (which is also applicable to the next function):
name is already taken by another file or another directory under dir.A-Z (uppercase letter), a-z (lowercase letter), 0-9 (digit), . (dot), - (minus) and _ (underscore). If the name contains any other characters, it is considered as an illegal name and this function won't create the file.. (a single dot) or .. (double dots). These two names are reserved for representing the current directory and the parent directory in the command line. If the parameter name is either of these two, it is considered as illegal.- (minus), it is considered as illegal.-1: when the function is not yet implemented. The skeleton codes always return -1, remove that line after you finished this function.1: when parameter dir or name is null.2: when the name is illegal.3: when there is a name conflict with an existing file or directory.0: when nothing above happens and the file is successfully created.Except 0, all other status codes indicate an error. When there are more than one error, always return the smallest status code. For example, if dir is null and at the same time name has a conflict, you should still return 1. Functions you may need: strcpy(), strcmp(), strlen().
int createDir(Dir* dir, char* name)Dir* dir: the newly created directory will be placed under this directory.char* name: the name of the directory to be created.-1: when the function is not yet implemented. The skeleton codes always return -1, remove that line after you finished this function.1: when parameter dir or name is null.2: when the name is illegal.3: when there is a name conflict with an existing file or directory.0: when nothing above happens and the directory is successfully created.Again, return the smallest one if there are multiple errors. Functions you may need: strcpy(), strcmp(), strlen().
int deleteFile(Dir* dir, char* name)Dir* dir: the parent directory of the file to be deleted.char* name: the name of the file to be deleted.-1: when the function is not yet implemented. The skeleton codes always return -1, remove that line after you finished this function.1: when parameter dir or name is null.2: when you cannot find a sub-file under dir with this name.0: when nothing above happens and the file is successfully deleted.Again, return the smallest one if there are multiple errors.
int deleteDir(Dir* dir, char* name, bool recursive)recursive. It works like this:
recursive is.recursive is true.
Dir* dir: will search under this directory for the directory to be deleted.char* name: the name of the directory to be deleted.bool recursive: whether to recursively delete sub-files and sub-directories.-1: when the function is not yet implemented. The skeleton codes always return -1, remove that line after you finished this function.1: when parameter dir or name is null.2: when you cannot find a sub-directory under dir with this name.3: when the directory is not empty and recursive is false.0: when nothing above happens and the directory is successfully deleted.Again, return the smallest one if there are multiple errors.
unsigned int getSize(const Dir* dir)const Dir* dir: the target directory.int moveFile(File* tgt, Dir* dest)File* tgt: the target file to be moved.Dir* dest: the destination directory.-1: when the function is not yet implemented. The skeleton codes always return -1, remove that line after you finished this function.1: when parameter tgt or dest is null.2: when dest is the parent of tgt, then the moving operation is meaningless, the file should stay at its original position in the linked list.3: when there is a name conflict (the name of tgt is already taken by something under dest).0: when nothing above happens and the file is successfully moved.Again, return the smallest one if there are multiple errors.
int moveDir(Dir* tgt, Dir* dest)Dir* tgt: the target directory to be moved.Dir* dest: the destination directory.-1: when the function is not yet implemented. The skeleton codes always return -1, remove that line after you finished this function.1: when parameter tgt or dest is null.2: when dest is the parent of tgt, then the moving operation is meaningless, the directory should stay at its original position in the linked list.3: when there is a name conflict (the name of tgt is already taken by something under dest).4: when dest is a descendant of tgt, or when dest is tgt.0: when nothing above happens and the directory is successfully moved.Again, return the smallest one if there are multiple errors.
const File** filesOfTag(const Dir* dir, Tag tag, unsigned int& length)const Dir* dir: will search recursively under this directory.Tag tag: the target tag.unsigned int& length: an unsigned integer passed by reference. Its value should be the size of the returned dynamic array (number of elements) after this function returns.length to the correct value. If dir is null or you cannot find any files of that tag, length should be set to 0 and the function should return null. Don't create a dynamic array with size 0. The order of the pointers doesn't matter, we will sort your array while grading.
Here is the last task of this assignment, which is to use the functions above to build a simple command line tool so that you can play with your file system interactively. It is also a tool to help you debug your code. Don't worry, you only need to finish a very small part, the skeleton codes will take care of the rest. A quick introduction to command line. A command line is a tool where you use commands to interact with your machine. A command is simply a string with certain syntax that asks the machine to do something. It usually starts with the command name followed by some parameters separated by whitespaces.
cmd param1 param2 param3 ...
Besides, a command line usually has a status called "working directory", it is a directory that you are currently at. When you specify a file or a directory by its name, the command line will usually search for that under the working directory only.
If you don't modify main.cpp, you can compile and run the program, it will start the command line for you. You can type help and "enter", then it will show you all the commands and their usages. Please also check the zoom recording of PA3 introduction (held on 22th) if you still have questions.

Your task is to finish the function that handles such commands introduced above. It is in cli.cpp:
Dir *execute(Dir* wd, char* rest, bool& exit)if blocks, each block handling one type of command. Some blocks are already implemented, you can regard them as examples to implement the remaining three blocks. In this task you only need the first two function parameters Dir* wd and char* rest, you don't need to deal with bool& exit and you don't need to write any return statements. Plus, please don't write any output statements in this task. You can add some while debugging, but remember to remove them before submission, the three blocks you implement should output nothing to the standard output while grading.
Dir* wd: the working directory.char* rest: a string of command parameters, like "param1 param2 param3 ..."mkdir, rm and mv, they will be used like this:
// mkdir <dirname>
mkdir images // make a directory "images"
// rm [-r] <name>
// "-r" option should only matter when deleting dirs, it determines the "recursive" parameter of "deleteDir()"
rm main.cpp // remove "main.cpp" (say it's a file)
rm -r main.cpp // remove "main.cpp" (say it's a file)
rm images // remove "images" (say it's a dir)
rm -r images // remove "images" recursively (say it's a dir)
// mv <target> <destination>
// <target> can be either File or Dir
mv lecture.pdf download // move "lecture.pdf" (say it's a file) to "download"
mv comp2011 download // move "comp2011" (say it's a dir) to "download"
. and .. will not appeared as input in the test cases, you don't need to handle them.
fetch to easily extract parameters from char* rest. You do it like this:
// rest == "hello world";
char param1[MAX_CMD_LEN] = ""; // define an empty string
fetch(rest, param1); // fetch the next parameter into the string you just defined
// rest == "world"
// param1 == "hello"
char param2[MAX_CMD_LEN] = ""; // do it again to fetch more parameters
fetch(rest, param2);
// rest == ""
// param2 == "world"
char param3[MAX_CMD_LEN] = "";
fetch(rest, param3); // fetch from an empty string will give you an empty string
// rest == ""
// param1 == ""
...
Since this assignment is about dynamic memory, it is also important that your program doesn't have any memory leak. Below is a table specifying the dynamic memory and by whom it should be created and released.
| Item | Created by | Released by |
|---|---|---|
| the root directory | skeleton codes | skeleton codes |
other Dir objects |
skeleton codes / you | skeleton codes calling your implemented deleteDir |
File objects |
skeleton codes / you | skeleton codes calling your implemented deleteFile |
dynamic array returned by filesOfTag() |
you | skeleton codes |
| anything else you use | you | you |
Memory leak checking is done via the -fsanitize=address,leak,undefined option (related documentation here) of a recent g++ compiler on Linux (it won't work on Windows for the versions we have tested). Check the "Errors" tab (next to "Your Output" tab in the test case details popup) for errors such as memory leak. Other errors/bugs such as out-of-bounds, use-after-free bugs, and some undefined-behavior-related bugs may also be detected. Note that if your program has no errors detected by the sanitizers, then the "Errors" tab may not appear. If you wish to check for memory leak yourself using the same options, you may follow our Checking for memory leak yourself guide.
If your program has any error reported by -fsanitize, you will receive a penalty of 10% of the total score. So, please pay attention to the relevant pre-deadline test cases in ZINC.
ZINC will grade your submission on some pre-deadline test cases to help you debug. After the deadline, ZINC will release all the test cases and regrade your submission. Also note that each test case is only given at most 5 seconds to run on ZINC. If your program takes more time than that to be done with a test case, ZINC will give you 0 for that. Five seconds should be more than enough for any test cases in this assignment. If your program runs for more than 5 seconds, it is very likely that there is an infinite loop/recursion. For example, if your pointers are messed up, and there is a circular linked list, this could easily trigger an infinite loop/recursion.
The table below gives you a rough idea of the weights of the tasks:
| Task name | Number of pre-deadline test cases | Number of all test cases | Contribution to the total score | Weight of each case |
|---|---|---|---|---|
| createFile & createDir | 14 | 28 | 10% | ≈ 0.357% |
| deleteFile & deleteDir | 12 | 22 | 15% | ≈ 0.682% |
| getSize | 2 | 6 | 15% | ≈ 2.5% |
| moveFile & moveDir | 7 | 22 | 20% | ≈ 0.909% |
| filesOfTag | 2 | 6 | 25% | ≈ 4.167% |
| command line | 5 | 9 | 15% | ≈ 1.667% |
There are 42 pre-deadline test cases in total, but you will see 84 in ZINC. Test case #43~#84 is a duplicate of #1~#42. Test case #1~#42 ignores memory leak while #43~#84 checks memory leak with -fsanitize. Say if you pass #2 but fail #44, that means your program has some issues reported by -fsanitize in #2.
23:59:00, May 11 (Wed)
Please submit a zip file containing file_sys.cpp and cli.cpp only to ZINC. ZINC usage instructions can be found here. Friendly reminder: before you submit your code, make sure it complies with all the rules in important requirements. No mark will be given if you violate the rules.
Notes:
It is required that your submissions can be compiled and run successfully in our online autograder ZINC. If we cannot even compile your work, it won't be graded. Therefore, for parts that you cannot finish, just put in dummy/empty implementation so that your whole program can be compiled for ZINC to grade the other parts that you have done.
There will be a penalty of -1 point (out of a maximum 100 points) for every minute you are late. For instance, since the deadline of the assignment is 23:59:00 on May 11th, if you submit your solution at 1:00:00 on May 12th, there will be a penalty of -61 points for your assignment. However, the lowest grade you may get from an assignment is zero: any negative score after the deduction due to late penalty (and any other penalties) will be reset to zero.
moveDir(), if tgt is dest, it should still return 4. Because the targets cannot be the destination. (May 6)rm -r <filename> is equivalent to rm <filename>. (Apr 27)cli.cpp as long as you don't modify the existing parts. Some comments in the skeleton codes tell you to write helper functions in file_sys.cpp, we will not update that, just remember that you can also put helper functions in cli.cpp. (Apr 25)-fsanitize will lead to the 10% penalty. Make sure you pass #43~#84 of the pre-deadline cases. (Apr 20). and .. as input, they will not be in the test cases. (Apr 20)-. (Apr 19)
deleteDir(), getSize(), filesOfTag()).
Page last modified at .