diff options
Diffstat (limited to 'filesystem.c')
-rw-r--r-- | filesystem.c | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/filesystem.c b/filesystem.c new file mode 100644 index 0000000..e041f56 --- /dev/null +++ b/filesystem.c @@ -0,0 +1,713 @@ +/* + * File: filesystem.c + * + * This file contains the source code that simulates the UNIX file system using + * C code. Function prototypes are included from filesystem.h, which also + * includes the required structure declarations in filesystem-datastructure.h. + * + * Author: Filip Rabiega + */ + +/* -------------------- Include files -------------------- */ +#include "filesystem.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* -------------------- Function Prototypes -------------------- */ +static File_node *search_file(Dir_node *const dir, const char name[]); +static Dir_node *search_subdir(Dir_node *const dir, const char name[]); +static void print_whole_dir(Dir_node *const dir); +static Name_node *insert_name(Name_node *head, char *name, int is_dir); +static int search_and_remove_dir(Dir_node *const cur_dir, const char name[]); +static void remove_dir(Dir_node *dir); +static int search_and_remove_file(Dir_node *const cur_dir, const char name[]); +static void remove_file(File_node *file); + +/* -------------------- Function Definitions -------------------- */ + +/* + * Initializes the FileSystem parameter filesystem. + * This allows more than one file systems to coexist, as each of them are + * initialized to their own unique memory addresses. + */ +void mkfs(FileSystem *const filesystem) { + /* Create and initialize root directory */ + Dir_node *root = malloc(sizeof(*root)); + + root->name = "root"; + root->path = ""; + root->file_list = NULL; + root->subdir_list = NULL; + root->next_dir = NULL; + root->par_dir = NULL; + + /* Assign root directory to the filesystem */ + filesystem->root = root; + filesystem->cur_dir = root; +} + +/* + * Creates a file with the specified name in the file system's current + * directory. + * - If a file with the same name already exists, then it simply + * updates the timestamp by incrementing it by 1. + * - If a subdirectory with the same name exists, then it will not make any + * modifications. + */ +int touch(FileSystem *const filesystem, const char name[]) { + File_node *cur, *prev = NULL, *new_file; + char *new_name; + int result = 0; + + /* Checks if parameters are valid, and also whether + name consists of a forward-slash character */ + if (filesystem != NULL && name != NULL && strlen(name) != 0 && + strchr(name, '/') == NULL) { + result = 1; + + /* These names would cause the function to have no + effects, but are not classified as error cases */ + if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0 && + strcmp(name, "/") != 0) { + /* Searches subdirectories with the same name. + If there is no directory with the same name, continue. */ + if (search_subdir(filesystem->cur_dir, name) == NULL) { + /* Set cur to the head of the file + list in the current directory */ + cur = filesystem->cur_dir->file_list; + + /* Find location to insert node within file_list */ + while (cur != NULL && strcmp(name, cur->name) >= 0) { + /* If there is a file of the same name, + increment timestamp and return 1*/ + if (strcmp(name, cur->name) == 0) { + cur->timestamp += 1; + return result; + } + + prev = cur; + cur = cur->next_file; + } + + /* Insert new File node */ + new_file = malloc(sizeof(*new_file)); + if (new_file != NULL) { + new_name = malloc(sizeof(char) * (strlen(name) + 1)); + if (new_name != NULL) { + /* Copies name to the allocated string */ + strcpy(new_name, name); + + /* Initializes new file structure members */ + new_file->name = new_name; + new_file->timestamp = 1; + new_file->next_file = cur; + + /* Case: File is inserted at the head */ + if (prev == NULL) { + filesystem->cur_dir->file_list = new_file; + } + /* Case: File is inserted elsewhere */ + else { + prev->next_file = new_file; + } + } + } + } + } + } + + return result; +} + +/* + * Creates a subdirectory with the specified name in the file system's + * current directory. + * - If a subdirectory/file with the same name already exists, or if name + * is invalid, then it will return 0. + */ +int mkdir(FileSystem *const filesystem, const char name[]) { + Dir_node *cur, *prev = NULL, *new_dir; + char *new_name; + char *new_path; + int result = 0; + + /* Checks if parameters are valid, and also whether name is an illegal + special case in this context or it consists of a forward-slash character */ + if (filesystem != NULL && name != NULL && strlen(name) != 0 && + strcmp(name, ".") != 0 && strcmp(name, "..") != 0 && + strcmp(name, "/") != 0 && strchr(name, '/') == NULL) { + /* Searches files with the same name. + If there is no file with the same name, continue. */ + if (search_subdir(filesystem->cur_dir, name) == NULL) { + + result = 1; + + /* Set cur to the head of the + subdirectory list of the current directory */ + cur = filesystem->cur_dir->subdir_list; + + /* Find location to insert node within subdir_list */ + while (cur != NULL && strcmp(name, cur->name) >= 0) { + /* If there is a subdirectory of the same name, + do nothing and return 1 */ + if (strcmp(name, cur->name) == 0) { + return result; + } + prev = cur; + cur = cur->next_dir; + } + + /* Insert new Subdirectory node */ + new_dir = malloc(sizeof(*new_dir)); + if (new_dir != NULL) { + new_name = malloc(sizeof(char) * (strlen(name) + 1)); + if (new_name != NULL) { + new_path = + malloc(sizeof(char) * + (strlen(name) + strlen(filesystem->cur_dir->path) + 2)); + + if (new_path != NULL) { + /* Copies name to the allocated name string */ + strcpy(new_name, name); + /* Creates path name to the allocated path string */ + strcpy(new_path, filesystem->cur_dir->path); + strcat(new_path, "/"); + strcat(new_path, name); + + /* Initializes new directory structure members */ + new_dir->name = new_name; + new_dir->path = new_path; + new_dir->file_list = NULL; + new_dir->subdir_list = NULL; + new_dir->next_dir = cur; + new_dir->par_dir = filesystem->cur_dir; + + /* Case: Directory is inserted at the head */ + if (prev == NULL) { + filesystem->cur_dir->subdir_list = new_dir; + } + /* Case: Directory is inserted elsewhere */ + else { + prev->next_dir = new_dir; + } + } + } + } + } + } + + return result; +} + +/* + * Moves the current directory of filesystem. + * The directory to be moved to is specified by name using the parameter name. + * - If name is a single period (.), there will be no effect since this tells + * the function to "move to the current directory." + * However, it is not an error case. + * - If name is a double adjacent period (..), it will move the current + * directory to its parent directory. + * - If name is solely a forward-slash (/), it will move the current + * directory to the root directory. + * - If name is a valid name of an existing subdirectory, then it will move + * the current directory there. + * - Other cases would be errors, including consists a forward-slash, but + * is not solely a forward-slash. + */ +int cd(FileSystem *const filesystem, const char name[]) { + Dir_node *dir; + int result = 0; + + /* Checks if parameters are valid */ + if (filesystem != NULL && name != NULL && strlen(name) != 0) { + /* Move to current directory (no effect) */ + if (strcmp(".", name) == 0) { + result = 1; + } + /* Move to parent directory */ + else if (strcmp("..", name) == 0) { + /* If the current directory is the root directory, then its + parent directory would be NULL, and therefore, should be + prevented from crashing the program */ + if (filesystem->cur_dir->par_dir != NULL) { + filesystem->cur_dir = filesystem->cur_dir->par_dir; + } + result = 1; + } + /* Move to root directory */ + else if (strcmp("/", name) == 0) { + filesystem->cur_dir = filesystem->root; + result = 1; + } + /* Move to an existing subdirectory */ + else { + /* If name consists of a forward-slash, it is invalid */ + if (strchr(name, '/') == NULL) { + /* Searches for subdirectories with the specified name, + and sets the current directory to be inside it. If not + found, then 0 will be returned. */ + dir = search_subdir(filesystem->cur_dir, name); + if (dir != NULL) { + filesystem->cur_dir = dir; + result = 1; + } + } + } + } + + return result; +} + +/* + * Displays a subdirectory, a file, or a certain directory. + * - If name is an existing file in the current directory, then it will print + * its name followed by a timestamp. + * - If name is an existing subdirectory in the current directory, then it + * will print all the files and and subdirectories within it in + * lexicographic order. + * - If name is a single period (.), or name is an empty string, it will print + * out the current directory. + * - If name is a double adjacent period (..), it will print out the parent + * directory of the current directory. + * - If name is a forward-slash (/), it will print out the root directory. + * - Other cases would be errors, including consists a forward-slash, but + * is not solely a forward-slash. + */ +int ls(FileSystem *const filesystem, const char name[]) { + Dir_node *dir; + File_node *file; + int result = 0; + + /* Checks if parameters are valid */ + if (filesystem != NULL && name != NULL) { + + /* Prints out current directory */ + if (strcmp(".", name) == 0 || strlen(name) == 0) { + print_whole_dir(filesystem->cur_dir); + result = 1; + } + /* Prints out parent directory */ + else if (strcmp("..", name) == 0) { + /* If the current directory is the root directory, then its + parent directory would be NULL, and therefore, should be + prevented from crashing the program */ + if (filesystem->cur_dir->par_dir != NULL) { + print_whole_dir(filesystem->cur_dir->par_dir); + } + result = 1; + } + /* Prints out root directory */ + else if (strcmp("/", name) == 0) { + print_whole_dir(filesystem->root); + result = 1; + } + /* Prints out an existing file or subdirectory */ + else { + /* If name consists of a forward-slash, it is invalid */ + if (strchr(name, '/') == NULL) { + /* Searches for a subdirectory with the specified name and + prints it out. */ + dir = search_subdir(filesystem->cur_dir, name); + if (dir != NULL) { + print_whole_dir(dir); + result = 1; + } + /* If a subdirectory is not found, then it will try searching + for files with the specified name */ + else { + file = search_file(filesystem->cur_dir, name); + if (file != NULL) { + printf("%s %d\n", file->name, file->timestamp); + result = 1; + } + } + /* If both are not found, then function will return 0 */ + } + } + } + + return result; +} + +/* + * Prints out the current directories full path, all the way from the root, + * with each directory in between separated by forward-slashes. + */ +void pwd(FileSystem *const filesystem) { + /* Check if parameter is valid */ + if (filesystem != NULL) { + /* If the current directory is the root directory, then print out + a forward-slash. The root is initialized such that its path would + be an empty string, to simplify the process of keeping track of + other directory paths */ + if (strcmp(filesystem->cur_dir->path, "") == 0) { + printf("/\n"); + } else { + printf("%s\n", filesystem->cur_dir->path); + } + } +} + +/* + * Deallocates all dynamically-allocated memory that is being used by the + * Ournix file system. As a result, all allocated memory of subdirectories + * and files inside the root, and even the root itself, will be freed and + * returned to the memory pool for future use. This also means that + * whatever data the file system is holding will be removed. + */ +void rmfs(FileSystem *const filesystem) { + /* Checks if paramter is valid */ + if (filesystem != NULL) { + remove_dir(filesystem->root); + } +} + +/* + * Removes a file or subdirectory within the current directory. This also + * means that whatever content is stored within them is also removed, + * as any allocated memory being used within it freed and returned to the + * memory pool. + */ +int rm(FileSystem *const filesystem, const char name[]) { + int result = 0; + + /* Checks if parameters are valid, and also whether name is an illegal + special case in this context or it consists of a forward-slash character */ + if (filesystem != NULL && name != NULL && strlen(name) != 0 && + strcmp(name, ".") != 0 && strcmp(name, "..") != 0 && + strcmp(name, "/") != 0 && strchr(name, '/') == NULL) { + /* Tries removing a directory with the specified name */ + result = search_and_remove_dir(filesystem->cur_dir, name); + + /* If directory is not found, it will try + removing a file with the specified name */ + if (!result) { + result = search_and_remove_file(filesystem->cur_dir, name); + } + + /* If both a directory or file is not + found, then result would stay 0 */ + } + + return result; +} + +/* + * A helper function to search for a file with the specified name within + * the specified directory. + */ +static File_node *search_file(Dir_node *const dir, const char name[]) { + File_node *cur; + + /* Set cur to the head of the file + list in the specified directory */ + cur = dir->file_list; + + /* Traverses the file list to search for the desired file */ + while (cur != NULL) { + if (strcmp(cur->name, name) == 0) { + return cur; + } + cur = cur->next_file; + } + + /* If a file with the specified name is not found, + then it will return NULL */ + return NULL; +} + +/* + * A helper function to search for a directory with the specified name within + * the specified directory. + */ +static Dir_node *search_subdir(Dir_node *const dir, const char name[]) { + Dir_node *cur; + + /* Set cur to the head of the subdirectory + list in the specified directory */ + cur = dir->subdir_list; + + /* Traverses the subdirectory list to search for the + desired subdirectory */ + while (cur != NULL) { + if (strcmp(cur->name, name) == 0) { + return cur; + } + cur = cur->next_dir; + } + + /* If a subdirectory with the specified name is not found, + then it will return NULL */ + return NULL; +} + +/* + * A helper function to print the contents of the whole specified directory. + * To do this, the function takes two steps: + * 1. It will create an ordered linked list (arranged in lexicographic order) + * storing the names of all files and subdirectories (if any) within the + * specified directory. + * 2. Prints all the names being stored in the ordered linked list while + * deallocating any memory being used by it. We can safely do this since + * the linked list will be of no use for us in the future. + */ +static void print_whole_dir(Dir_node *const dir) { + Name_node *name_list = NULL, *printed_name; + Dir_node *cur_dir; + File_node *cur_file; + + /* Make an ordered Linked List of File/Directory names. */ + + /* Traverse directory's files. */ + cur_file = dir->file_list; + + while (cur_file != NULL) { + name_list = insert_name(name_list, cur_file->name, 0); + cur_file = cur_file->next_file; + } + + /* Traverse directory's subdirectories. */ + cur_dir = dir->subdir_list; + + while (cur_dir != NULL) { + name_list = insert_name(name_list, cur_dir->name, 1); + cur_dir = cur_dir->next_dir; + } + + /* Start printing names. If the name_list is NULL, it indicates that + the directory is empty, and therefore, nothing is printed. */ + while (name_list != NULL) { + printf("%s\n", name_list->name); + + /* Set printed_name to the name that has just been printed + and move name_list to the next node. */ + printed_name = name_list; + name_list = name_list->next_name; + + /* Free allocated memory being used by printed_name. */ + free(printed_name->name); + free(printed_name); + } +} + +/* + * A helper function to assist the print_whole_dir() function in creating + * an ordered linked list of names. + * - The head parameter represents the head of the name linked list. + * - The name parameter represents the name to be stored within the node. + * - The is_dir parameter indicates whether the node being inserted will + * store the name of a directory. This distinction is important since + * directories are printed with a preceding forward-slash. + */ +static Name_node *insert_name(Name_node *head, char *name, int is_dir) { + Name_node *cur, *prev = NULL, *new_node; + char *new_name; + + /* The boolean value flag is used to indicate whether an error has + occured in the process of allocating memory, to prevent the function + from executing more statements and causing the program to crash. */ + int flag = 1; + + /* Initialize new_name node. */ + new_node = malloc(sizeof(*new_node)); + + if (new_node != NULL) { + new_name = malloc(sizeof(char) * (strlen(name) + 2)); + if (new_name != NULL) { + strcpy(new_name, name); + + /* If the name being inserted is the name of a directory, + concatenate a forward-slash to the end of the string. */ + if (is_dir) { + strcat(new_name, "/"); + } + + /* Initialize values of new_name node */ + new_node->name = new_name; + new_node->next_name = NULL; + } else { + /* Malloc Error */ + flag = 0; + } + } else { + /* Malloc Error */ + flag = 0; + } + + /* Insert the new_name node into the linked list. */ + if (flag) { + /* If head is NULL, it means that the node to be + inserted is the first node in the linked list. */ + if (head == NULL) { + return new_node; + } + + cur = head; + + /* Find a location to insert the node */ + while (cur != NULL && strcmp(name, cur->name) >= 0) { + prev = cur; + cur = cur->next_name; + } + + /* Initializes the pointer to the next node. */ + new_node->next_name = cur; + + /* Case: File is inserted at the head */ + if (prev == NULL) { + head = new_node; + } + + /* Case: File is inserted elsewhere */ + else { + prev->next_name = new_node; + } + } + + return head; +} + +/* + * A helper method to search for a directory and remove it from the list + * of subdirectories. This function is distinct from simply searching for + * a directory using the search_dir() helper function and removing it using + * the remove_dir() helper function as it also modifies the links within + * the subdirectory linked list. + */ +static int search_and_remove_dir(Dir_node *const cur_dir, const char name[]) { + Dir_node *cur, *prev = NULL; + int result = 0; + + /* Traverses the subdirectory list to look for the desired subdirectory */ + cur = cur_dir->subdir_list; + + while (cur != NULL && strcmp(cur->name, name) != 0) { + prev = cur; + cur = cur->next_dir; + } + + /* If cur is NULL, it would indicate that the subdirectory with the + specified name is not found, and result would stay 0. */ + if (cur != NULL) { + result = 1; + + /* Case: The subdirectory to be removed is the head of the list */ + if (prev == NULL) { + cur_dir->subdir_list = cur->next_dir; + } + + /* Case: The subdirectory to be removed is between the list */ + else { + prev->next_dir = cur->next_dir; + } + + /* Remove all subdirectory contents and free all + allocated memory being used by it */ + remove_dir(cur); + } + + return result; +} + +/* + * A recursive helper function used to remove all the contents within a + * directory, which includes all files and subdirectories within it, and + * frees all allocated memory being used by any of the content. + */ +static void remove_dir(Dir_node *dir) { + File_node *cur_file, *file_to_be_removed; + Dir_node *cur_dir, *dir_to_be_removed; + + if (dir != NULL) { + /* Remove all files within this directory */ + cur_file = dir->file_list; + + /* If cur_file is NULL, this would indicate + that the directory has no files */ + while (cur_file != NULL) { + file_to_be_removed = cur_file; + cur_file = cur_file->next_file; + + remove_file(file_to_be_removed); + } + + /* Remove all subdirectories within this directory */ + cur_dir = dir->subdir_list; + + /* If cur_dir is NULL, this would indicate + that the directory has no subdirectories */ + while (cur_dir != NULL) { + dir_to_be_removed = cur_dir; + cur_dir = cur_dir->next_dir; + + remove_dir(dir_to_be_removed); /* Recursive call */ + } + + /* Free allocated memory being used + by other directory struct members */ + if (strcmp("root", dir->name) != 0) { + free(dir->name); + } + + if (strlen(dir->path) != 0) { + free(dir->path); + } + + /* Free allocated memory being used by the directory itself */ + free(dir); + } +} + +/* + * A helper method to search for a file and remove it from the list of files. + * This function is distinct from simply searching for a file using the + * search_file() helper function and removing it using the remove_file() + * helper function as it also modifies the links within the file linked list. + */ +static int search_and_remove_file(Dir_node *const cur_dir, const char name[]) { + File_node *cur, *prev = NULL; + int result = 0; + + /* Traverses the file list to look for the desired file */ + cur = cur_dir->file_list; + + while (cur != NULL && strcmp(cur->name, name)) { + prev = cur; + cur = cur->next_file; + } + + /* If cur is NULL, it would indicate that the file with the + specified name is not found, and result would stay 0. */ + if (cur != NULL) { + result = 1; + + /* Case: The file to be removed is the head of the list */ + if (prev == NULL) { + cur_dir->file_list = cur->next_file; + } + + /* Case: The file to be removed is between the list */ + else { + prev->next_file = cur->next_file; + } + + /* Remove all file contents and free all + allocated memory being used by it */ + remove_file(cur); + } + + return result; +} + +/* + * A helper function used to remove all the contents within a + * file, namely its name and timestamp. + */ +static void remove_file(File_node *file) { + if (file != NULL) { + + free(file->name); + free(file); + } +} |