diff --git a/.DS_Store b/.DS_Store index 4c4a626..07b8666 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8cf8b5e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "googletest"] + path = googletest + url = https://github.com/google/googletest.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 285b085..eb951da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,25 +1,40 @@ -cmake_minimum_required (VERSION 3.1.0) -project(fischl) - -set(CMAKE_CXX_STANDARD 14) - -include_directories( - # fischl include files - ${CMAKE_CURRENT_SOURCE_DIR}/include -) - -add_executable(fischl - - lib/fischl.cpp - lib/main.cpp - lib/rawdisk.cpp - lib/fs/datablock_manager.cpp - lib/fs/fs_data_types.cpp - lib/fs/fs_file_io.cpp - lib/fs/fs.cpp - lib/fs/inode_manager.cpp - -) - -enable_testing() -add_subdirectory(test) \ No newline at end of file +cmake_minimum_required (VERSION 3.1.0) +project(fischl) + +set(CMAKE_CXX_STANDARD 14) + +include_directories( + # fischl include files + ${CMAKE_CURRENT_SOURCE_DIR}/include + # ${CMAKE_CURRENT_SOURCE_DIR}/googletest/googletest/include +) + +add_executable(fischl + + lib/fischl.cpp + lib/main.cpp + lib/rawdisk.cpp + lib/fs/datablock_manager.cpp + lib/fs/fs_data_types.cpp + lib/fs/fs_file_io.cpp + lib/fs/fs.cpp + lib/fs/inode_manager.cpp + lib/files.cpp + lib/direntry.cpp + +) + +enable_testing() +add_subdirectory(test) +# add_subdirectory(googletest) + +# Add the -Wall flag +target_compile_options(fischl PRIVATE -Wall) + +# Use pkg-config to get flags for fuse3 +find_package(PkgConfig REQUIRED) +pkg_search_module(FUSE3 REQUIRED fuse3) + +# Add the flags from pkg-config for fuse3 +target_include_directories(fischl PRIVATE ${FUSE3_INCLUDE_DIRS}) +target_link_libraries(fischl PRIVATE ${FUSE3_LIBRARIES}) \ No newline at end of file diff --git a/include/direntry.h b/include/direntry.h new file mode 100644 index 0000000..7177f0e --- /dev/null +++ b/include/direntry.h @@ -0,0 +1,36 @@ +typedef struct fileNode { + char *name = NULL; + int inode_number; + int permissions; + char *Symbolink; + struct treeNode *subdirectory; + struct fileNode *next; +} FileNode; + +typedef struct { + int size; + FileNode **table; +} HashTable; + +typedef struct treeNode { + char *dirName; + HashTable *contents; + struct treeNode *parent; + FileNode *self_info; //self fileNode infromation +} TreeNode; + +/*for root*/ +TreeNode *fischl_init_entry(int new_inode_number, const char *fileName, INode_Data *new_inode); +/*the to be added file in add_entry should be parent-child relationship with treenode, otherwise will wrong */ +/*see Add_FindFiletest in dir_API.cpp*/ +int fischl_add_entry(TreeNode *parent, int new_inode_number, const char *fileName, INode_Data *new_inode); +int fischl_rm_entry(TreeNode *parent, const char *fileName); +/*if want to use dir mode use the subdirectory treeNode pointer */ +//e.g. FileNode *Dirnode = fischl_find_entry(); can see file inside with Dirnode->subdirectory +//e.g. go to the current Dirnode parent directory, use TreeNode *get_Dir_parent = Dirnode->subdirectory->parent; +FileNode *fischl_find_entry(TreeNode *root, const char *path); + +void freeTree(TreeNode *node); +/*for debug use*/ +TreeNode *createDirectory(const char *dirName, TreeNode *parent, int hashSize); +TreeNode *find_parentPath(TreeNode *root, const char *path); \ No newline at end of file diff --git a/include/files.h b/include/files.h new file mode 100644 index 0000000..5eeb57a --- /dev/null +++ b/include/files.h @@ -0,0 +1,40 @@ +#include +#include +#include +#include "direntry.h" + +class FilesOperation { + RawDisk& disk; + Fs *fs; + void create_dot_dotdot(INode_Data*, u_int64_t); + + public: + TreeNode *root_node; + FilesOperation(RawDisk&, Fs*); + //int read_datablock(const INode_Data& inode, u_int64_t index, char* buffer); + //int write_datablock(INode_Data& inode, u_int64_t index, char* buffer); + void initialize_rootinode(); + void printbuffer(const char*,int); + void printDirectory(u_int64_t); + INode_Data* create_new_inode(u_int64_t parent_inode_number, const char* name, mode_t mode); + int insert_inode_to(u_int64_t parent_inode_number, const char* name, INode_Data *new_inode); + void unlink_inode(u_int64_t inode_number); + u_int64_t disk_namei(const char* path); + u_int64_t namei(const char* path); + int fischl_mkdir(const char*, mode_t); + int fischl_mknod(const char*, mode_t, dev_t);//for special file + int fischl_create(const char *, mode_t, struct fuse_file_info *);//for regular file + int fischl_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi); + int fischl_readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags); + int fischl_releasedir(const char* path, struct fuse_file_info *fi); + int fischl_unlink (const char *); + int fischl_rmdir(const char *); + int fischl_rename(const char *path, const char *, unsigned int flags); + int fischl_truncate(const char *path, off_t, struct fuse_file_info *fi); + int fischl_chmod(const char *path, mode_t, struct fuse_file_info *fi); + int fischl_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi); + int fischl_open (const char *, struct fuse_file_info *);//open file + int fischl_release (const char *, struct fuse_file_info *);//close file + int fischl_write(const char *, const char *, size_t, off_t, struct fuse_file_info *); + int fischl_read(const char *, char *, size_t, off_t, struct fuse_file_info *); +}; \ No newline at end of file diff --git a/include/fischl.h b/include/fischl.h index 94113c2..d620bc6 100644 --- a/include/fischl.h +++ b/include/fischl.h @@ -1,7 +1,3 @@ -class fischl{ - // declare - public: - int init(); -}; \ No newline at end of file +int fischl(int argc, char *argv[]); diff --git a/include/fs/fs_data_types.hpp b/include/fs/fs_data_types.hpp index cb64dc1..134db85 100644 --- a/include/fs/fs_data_types.hpp +++ b/include/fs/fs_data_types.hpp @@ -43,7 +43,7 @@ public: u_int64_t single_indirect_block, double_indirect_block, triple_indirect_block; u_int64_t direct_blocks[NUMBER_OF_DIRECT_BLOCKS]; - INode_Data(u_int64_t inode_num = 0xFFFFFFFFFFFFFFFF); + INode_Data(u_int64_t inode_num = (u_int64_t)(0xFFFFFFFFFFFFFFFF)); void serialize(char buf[]); void deserialize(char buf[]); }; diff --git a/include/fs_constants.hpp b/include/fs_constants.hpp index ec3ab7b..fb3a53c 100644 --- a/include/fs_constants.hpp +++ b/include/fs_constants.hpp @@ -12,11 +12,13 @@ #include #define IO_BLOCK_SIZE 4096 +#define INDIRECT_BLOCKS 512 #define NUM_INODE_BLOCKS 1023 #define INODE_SIZE 512 -#define DATABLOCKS_PER_BITMAP_BLOCK 255 +// TODO: explore the optimal value for this +#define DATABLOCKS_PER_BITMAP_BLOCK 2047 #endif \ No newline at end of file diff --git a/lib/direntry.cpp b/lib/direntry.cpp new file mode 100644 index 0000000..2366dde --- /dev/null +++ b/lib/direntry.cpp @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include "fs.hpp" +#include "direntry.h" +/*********************************Hash operation******************************************** + +********************************************************************************************/ +// Hash operation +unsigned int hash(HashTable *h, char *key) { + unsigned int hashval = 0; + for (; *key != '\0'; key++) hashval = *key + (hashval << 5) - hashval; + return hashval % h->size; +} + +HashTable *createHashTable(int size) { + HashTable *newTable = (HashTable *)malloc(sizeof(HashTable)); + newTable->size = size; + newTable->table = (FileNode **)malloc(sizeof(FileNode *) * size); + for (int i = 0; i < size; i++) newTable->table[i] = NULL; + return newTable; +} + +FileNode *insertHash(HashTable *h, char *key, TreeNode *subdirectory) { + unsigned int hashval = hash(h, key); + FileNode *newNode = (FileNode *)malloc(sizeof(FileNode)); + newNode->name = strdup(key); + newNode->subdirectory = subdirectory; + newNode->next = h->table[hashval]; + h->table[hashval] = newNode; + return newNode; +} + +FileNode *lookupHash(HashTable *h, char *key) { + unsigned int hashval = hash(h, key); + FileNode *node = h->table[hashval]; + while (node != NULL) { + if (strcmp(node->name, key) == 0) return node; + node = node->next; + } + return NULL; // Not found +} + +bool removeHash(HashTable *h, char *key) { + unsigned int hashval = hash(h, key); + FileNode *node = h->table[hashval]; + if (node == NULL) return false; + if (strcmp(node->name, key) == 0) { + h->table[hashval] = node->next; + return true; + } + FileNode *prev = NULL; + bool foundit = false; + while (node != NULL) { + if (strcmp(node->name, key) == 0) break; + prev = node; + node = node->next; + } + if (node == NULL) { + return false; + } else { + prev->next = node->next; + return true; + } +} + +TreeNode *createDirectory(const char *dirName, TreeNode *parent, int hashSize) { + TreeNode *newDir = (TreeNode *)malloc(sizeof(TreeNode)); + newDir->dirName = strdup(dirName); + newDir->contents = createHashTable(hashSize); + newDir->parent = parent; + if (parent) { + newDir->self_info = insertHash(parent->contents, newDir->dirName, newDir); + } + return newDir; +} + +TreeNode *find_parentPath(TreeNode *root, const char *path) { + char *pathCopy = strdup(path); + char *segment = strtok(pathCopy, "/"); + TreeNode *current = root; + FileNode *file = NULL; + + while (segment != NULL && current != NULL) { + file = lookupHash(current->contents, segment); + if (file != NULL && file->subdirectory == NULL) { + free(pathCopy); + //printf("status current directory %s\n",current->dirName); + return current; //File found + } + current = file ? file->subdirectory : NULL; + segment = strtok(NULL, "/"); + } + + free(pathCopy); + return current; // NULL if not found +} + +void freeHashTable(HashTable *table) { + if (table == NULL) return; + + free(table->table); + free(table); +} + +void freeTree(TreeNode *node) { + //printf("***********************FREE TREE %s**************************\n",node->dirName); + //printf("***********************FREE TREE **************************\n"); + if (node == NULL) return; + + if (node->contents != NULL) { + for (int i = 0; i < node->contents->size; ++i) { + FileNode *current = node->contents->table[i]; + while (current != NULL) { + FileNode *temp = current; + current = current->next; + + if (temp->subdirectory != NULL) { + freeTree(temp->subdirectory); + } + // Free the FileNode if it's not a directory + // printf("free who %s\n",temp->name); + free(temp->name); + free(temp); + } + } + //printf("free %s's hash table\n",node->dirName); + freeHashTable(node->contents); + //node->contents = NULL; + } + //printf("free directory %s\n",node->dirName); + free(node->dirName); + node->dirName = NULL; + free(node); + node = NULL; + //printf("***********************END**************************\n"); +} + +/*********************************Direntry operation****************************************** + +********************************************************************************************/ +//for fake root (mount point) +TreeNode *fischl_init_entry(int new_inode_number, const char *fileName, INode_Data *new_inode) { + TreeNode *newDir = (TreeNode *)malloc(sizeof(TreeNode)); + newDir->dirName = strdup(fileName); + newDir->contents = createHashTable(20);//hashSize define 20 + newDir->parent = newDir; + FileNode *newFile = (FileNode *)malloc(sizeof(FileNode)); + newFile->name = strdup(fileName); + newFile->inode_number = new_inode_number; + newFile->permissions = new_inode->metadata.permissions; + newFile->subdirectory = newDir; + newDir->self_info = newFile; + return newDir; +} + +int fischl_add_entry(TreeNode *parent, int new_inode_number, const char *fileName, INode_Data *new_inode){ + char *Name = strdup(fileName); + TreeNode *newDir = NULL; + /*If directory, malloc TreeNode, and then create filenode that belongs to Parent hash table content*/ + if ((new_inode->metadata.permissions & S_IFMT) == S_IFDIR) { + newDir = (TreeNode *)malloc(sizeof(TreeNode)); + newDir->dirName = Name; + newDir->contents = createHashTable(20);//hasSize define 20 + newDir->parent = parent; + } + FileNode *newFile = insertHash(parent->contents, Name, newDir); //newDir == NULL indicates it's a file + //assign INode *new_inode metadata to data member in FileNode structure + newFile->permissions = new_inode->metadata.permissions; + newFile->inode_number = new_inode_number; + //Diretory have its own file information, that is . here + if(newDir != NULL) + newDir->self_info = newFile; + //free(Name); cannot free name + return 0; +} + +int fischl_rm_entry(TreeNode *parent, const char *fileName) { + char *fileName_dup = strdup(fileName); + if (parent->contents == NULL) return -1; + FileNode *file = NULL; + file = lookupHash(parent->contents, fileName_dup); + if (file == NULL) return -1; + if (file->subdirectory != NULL) freeTree(file->subdirectory); + removeHash(parent->contents, fileName_dup); + free(file->name); + free(file); + delete fileName_dup; +} + + +FileNode *fischl_find_entry(TreeNode *root, const char *path){ + //support . and .. function + char *pathCopy = strdup(path); + char *segment = strtok(pathCopy, "/"); + TreeNode *current = root; + FileNode *file = NULL; + + while (segment != NULL && current != NULL) { + if (strcmp(segment, "..") == 0) { + // Move up to the parent directory + current = current->parent; + if (current == NULL) { + // If there's no parent, we've reached the top of the tree, but root itself is same + file = NULL; + break; + } else { + file = current->self_info; + } + } else if (strcmp(segment, ".") == 0) { + // Stay in the current directory (no action needed) + } + else{ + file = lookupHash(current->contents, segment); + if (file != NULL && file->subdirectory == NULL) { + free(pathCopy); + return file; //File found + //return current; return filenode + } + current = file ? file->subdirectory : NULL; + } + segment = strtok(NULL, "/"); + } + + free(pathCopy); + + if (current != NULL && file == NULL) { + // If we've stopped at a directory and not a file, return the directory's self info + return current->self_info; + } + + return file; // NULL if not found +} \ No newline at end of file diff --git a/lib/files.cpp b/lib/files.cpp new file mode 100644 index 0000000..45bee67 --- /dev/null +++ b/lib/files.cpp @@ -0,0 +1,901 @@ +//#include "fuse.h" add this when layer3 +#define FUSE_USE_VERSION 31 + +#include "files.h" +#include +#include +#include + +struct DirectoryEntry { + u_int64_t inode_number; + char file_name[256]; + void serialize(char *buffer) { + u_int64_t t = inode_number; + for (int j = 0; j < 8; j++) { + buffer[j] = t & (((u_int64_t)1 << (8)) - 1); + t >>= 8; + } + strcpy(buffer + 8, file_name); + } + void deserialize(char *buffer) { + inode_number = 0; + for (int j = 0; j < 8; j++) + inode_number = + inode_number | (((u_int64_t)(unsigned char)buffer[j]) << (8 * j)); + strcpy(file_name, buffer + 8); + } +}; + +void FilesOperation::printbuffer(const char *buff, int len) { + for (int i = 0; i < len; i++) { + printf("%x ", buff[i]); + } + printf("\n"); +} + +FilesOperation::FilesOperation(RawDisk &disk_, Fs *fs) : disk(disk_) { + this->fs = fs; +} + +void FilesOperation::create_dot_dotdot(INode_Data *inode, + u_int64_t parent_inode_number) { + char buffer[IO_BLOCK_SIZE] = {0}; + DirectoryEntry dot; + dot.inode_number = inode->inode_num; + strcpy(dot.file_name, "."); + dot.serialize(buffer); + DirectoryEntry dotdot; + dotdot.inode_number = parent_inode_number; + strcpy(dotdot.file_name, ".."); + dotdot.serialize(buffer + 264); + int ret = fs->write(inode, buffer, IO_BLOCK_SIZE, 0); + // printf("in create_dot_dotdot: fs->write returned %d\n",ret); +} + +void FilesOperation::initialize_rootinode() { + // this method must be called explicitly right after initializion + INode_Data *root_inode = new INode_Data(); + fs->inode_manager->new_inode(0, 0, S_IFDIR, root_inode); + u_int64_t root_inode_number = root_inode->inode_num; + create_dot_dotdot(root_inode, root_inode_number); + root_node = fischl_init_entry(root_inode_number, "/", root_inode); + assert(root_node->self_info != NULL); + fs->inode_manager->save_inode(root_inode); +} + +void FilesOperation::printDirectory(u_int64_t inode_number) { + INode_Data inode; + inode.inode_num = inode_number; + fs->inode_manager->load_inode(&inode); + char buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) { + fs->read(&inode, buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(buffer + i); + if (ent.inode_number) + printf("%s\t%llu;\t", ent.file_name, ent.inode_number); + } + } + printf("\n"); +} + +INode_Data *FilesOperation::create_new_inode(u_int64_t parent_inode_number, + const char *name, mode_t mode) { + // trys to create a file under parent directory + if (strlen(name) >= 256) { + perror("Name too long, cannot create file or directory"); + return NULL; + } + INode_Data inode; + inode.inode_num = parent_inode_number; + fs->inode_manager->load_inode(&inode); + if ((inode.metadata.permissions & S_IFMT) != S_IFDIR) { + fprintf(stderr, "[%s ,%d] please create under directory\n", __func__, + __LINE__); + return NULL; + } + + // Check if file or directory already exists + char r_buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) { + fs->read(&inode, r_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(r_buffer + i); + if (strcmp(ent.file_name, name) == 0 && ent.inode_number != 0) { + if ((mode & S_IFMT) == S_IFDIR) { + fprintf(stderr, "[%s ,%d] %s/ already exists\n", __func__, __LINE__, + name); + } else { + fprintf(stderr, "[%s ,%d] %s already exists\n", __func__, __LINE__, + name); + } + return NULL; + } + } + } + + bool allocated = false; + INode_Data *new_inode = new INode_Data(); + fs->inode_manager->new_inode(0, 0, mode, new_inode); + if ((mode & S_IFMT) == S_IFDIR) { + create_dot_dotdot(new_inode, parent_inode_number); + fs->inode_manager->save_inode(new_inode); + } + + char rw_buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) { + fs->read(&inode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(rw_buffer + i); + if (ent.inode_number == 0) { + allocated = true; + ent.inode_number = new_inode->inode_num; + strcpy(ent.file_name, name); + ent.serialize(rw_buffer + i); + break; + } + } + if (allocated) { + fs->write(&inode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + break; + } + } + + if (!allocated) { + char write_buffer[IO_BLOCK_SIZE] = {0}; + DirectoryEntry ent; + ent.inode_number = new_inode->inode_num; + strcpy(ent.file_name, name); + ent.serialize(write_buffer); + fs->write(&inode, write_buffer, IO_BLOCK_SIZE, + (inode.metadata.size / IO_BLOCK_SIZE) * IO_BLOCK_SIZE); + fs->inode_manager->save_inode(&inode); + } + + return new_inode; +} + +u_int64_t FilesOperation::disk_namei(const char *path) { + // returns the inode number corresponding to path + u_int64_t current_inode = root_node->self_info->inode_number; + std::string current_dirname; + std::istringstream pathStream(path); + std::string new_name; + std::getline(pathStream, new_name, '/'); + if (!new_name.empty()) { + printf("disk_namei: path should start with /\n"); + return -1; + } + while (std::getline(pathStream, new_name, '/')) { + INode_Data inode; + inode.inode_num = current_inode; + fs->inode_manager->load_inode(&inode); + if ((inode.metadata.permissions & S_IFMT) != S_IFDIR || + inode.metadata.size == 0) { + printf("disk_namei: %s is not a non-empty directory\n", + current_dirname.c_str()); + return -1; + } + u_int64_t new_inode_number = 0; + + char buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) { + fs->read(&inode, buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(buffer + i); + if (strcmp(ent.file_name, new_name.c_str()) == 0) { + new_inode_number = ent.inode_number; + break; + } + } + if (new_inode_number) + break; + } + if (!new_inode_number) { + printf("disk_namei: no name matching %s under directory %s\n", + new_name.c_str(), current_dirname.c_str()); + return -1; + } + current_inode = new_inode_number; + current_dirname = new_name; + } + return current_inode; + // path = "/" should return root_inode_number + // (root_node->self_info->inode_number) path = "/foo.txt" should return inode + // for foo.txt path = "/mydir" should return inode for mydir path = + // "/nonemptydir/foo" should return inode for foo path = "/notnonemptydir/foo" + // should raise error +} + +u_int64_t FilesOperation::namei(const char *path) { + FileNode *filenode = fischl_find_entry(root_node, path); + if (filenode) + return filenode->inode_number; + else + return -1; +} + +int FilesOperation::fischl_mkdir(const char *path, mode_t mode) { + // check path + char *pathdup = strdup(path); + char *lastSlash = strrchr(pathdup, '/'); + *lastSlash = '\0'; // Split the string into parent path and new directory + // name; \0 + char *newDirname = + lastSlash + 1; //\0, get from + char *ParentPath = pathdup; // pathdup are separated by pathdup, so it take + // only + + FileNode *parent_filenode = strlen(ParentPath) + ? fischl_find_entry(root_node, ParentPath) + : root_node->self_info; + if (parent_filenode == NULL) { + fprintf(stderr, "[%s ,%d] ParentPath:{%s} not found\n", __func__, __LINE__, + ParentPath); + free(pathdup); + return -ENOENT; // parentpath directory does not exist + } + u_int64_t parent_inode_number = parent_filenode->inode_number; + // printf("%s, %llu, %s\n", parent_filenode->name, parent_inode_number, + // newDirname); make new inode + INode_Data *ret = + create_new_inode(parent_inode_number, newDirname, + mode | S_IFDIR); // specify S_IFDIR as directory + if (ret == NULL) + return -1; // ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST + fischl_add_entry(parent_filenode->subdirectory, ret->inode_num, newDirname, + ret); + free(pathdup); + return 0; // SUCCESS +} +/* + special file +*/ +int FilesOperation::fischl_mknod(const char *path, mode_t mode, dev_t dev) { + // check path + char *pathdup = strdup(path); + char *lastSlash = strrchr(pathdup, '/'); + *lastSlash = '\0'; // Split the string into parent path and new directory + // name; \0 + char *newFilename = + lastSlash + 1; //\0, get from + char *ParentPath = pathdup; // pathdup are separated by pathdup, so it take + // only + // fprintf(stderr,"[%s ,%d] ParentPath:%s, strlen=%d\n",__func__,__LINE__, + // ParentPath, strlen(ParentPath)); + FileNode *parent_filenode = strlen(ParentPath) + ? fischl_find_entry(root_node, ParentPath) + : root_node->self_info; + if (parent_filenode == NULL) { + fprintf(stderr, "[%s ,%d] ParentPath:{%s} not found\n", __func__, __LINE__, + ParentPath); + free(pathdup); + return -1; + } + u_int64_t parent_inode_number = parent_filenode->inode_number; + // make new inode + INode_Data *ret = create_new_inode(parent_inode_number, newFilename, mode); + if (ret == NULL) + return -1; // ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST + // make new node + fischl_add_entry(parent_filenode->subdirectory, ret->inode_num, newFilename, + ret); + free(pathdup); + return 0; // SUCESS +} +/* + regular file +*/ +int FilesOperation::fischl_create(const char *path, mode_t mode, + struct fuse_file_info *fi) { + // check path + char *pathdup = strdup(path); + char *lastSlash = strrchr(pathdup, '/'); + *lastSlash = '\0'; // Split the string into parent path and new directory + // name; \0 + char *newFilename = + lastSlash + 1; //\0, get from + char *ParentPath = pathdup; // pathdup are separated by pathdup, so it take + // only + // fprintf(stderr,"[%s ,%d] ParentPath:%s, strlen=%d\n",__func__,__LINE__, + // ParentPath, strlen(ParentPath)); + FileNode *parent_filenode = strlen(ParentPath) + ? fischl_find_entry(root_node, ParentPath) + : root_node->self_info; + if (parent_filenode == NULL) { + fprintf(stderr, "[%s ,%d] ParentPath:{%s} not found\n", __func__, __LINE__, + ParentPath); + free(pathdup); + return -1; + } + u_int64_t parent_inode_number = parent_filenode->inode_number; + // make new inode + INode_Data *ret = create_new_inode(parent_inode_number, newFilename, mode); + if (ret == NULL) + return -1; // ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST + // make new node in RAM + fischl_add_entry(parent_filenode->subdirectory, ret->inode_num, newFilename, + ret); + // directly give inode number rather than create file descriptor table + fi->fh = + ret->inode_num; // assign file descriptor as inode number to fuse system + free(pathdup); + return 0; // SUCESS +} + +int FilesOperation::fischl_getattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) { + + (void)fi; + int res = 0; + u_int64_t fh = namei(path); + + if (fh == -1) { + return -ENOENT; + } + + INode_Data inode; + inode.inode_num = fh; + fs->inode_manager->load_inode(&inode); + + memset(stbuf, 0, sizeof(struct stat)); + if ((inode.metadata.permissions & S_IFMT) == S_IFDIR) { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = inode.metadata.reference_count; + stbuf->st_uid = inode.metadata.uid; + stbuf->st_gid = inode.metadata.gid; + } else { + stbuf->st_mode = S_IFREG | 0444; + stbuf->st_nlink = inode.metadata.reference_count; + stbuf->st_uid = inode.metadata.uid; + stbuf->st_gid = inode.metadata.gid; + stbuf->st_size = inode.metadata.size; + } + perror(std::to_string(inode.metadata.size).c_str()); + return res; +} + +int FilesOperation::fischl_readdir(const char *path, void *buf, + fuse_fill_dir_t filler, off_t ft, + struct fuse_file_info *fi, + enum fuse_readdir_flags flg) { + // check path + u_int64_t fh = namei(path); + + if (fh == -1) { + return -1; + } + + INode_Data inode; + inode.inode_num = fh; + fs->inode_manager->load_inode(&inode); + char buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) { + fs->read(&inode, buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(buffer + i); + if (ent.inode_number) { + filler(buf, ent.file_name, NULL, 0, FUSE_FILL_DIR_PLUS); + // printf("%s\t%llu;\t", ent.file_name, ent.inode_number); + } + } + } + + return 0; +} + +int FilesOperation::fischl_releasedir(const char *path, + struct fuse_file_info *fi) { + if (fischl_find_entry(root_node, path) == NULL) + return -ENOENT; + // do with file descriptor that cannot be used + fi->fh = -1; + return 0; // SUCESS +} + +void FilesOperation::unlink_inode(u_int64_t inode_number) { + INode_Data inode; + inode.inode_num = inode_number; + fs->inode_manager->load_inode(&inode); + if ((inode.metadata.permissions & S_IFMT) == S_IFDIR) { + char buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) { + fs->read(&inode, buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + if (ent.inode_number && strcmp(ent.file_name, ".") && + strcmp(ent.file_name, "..")) { + unlink_inode(ent.inode_number); + } + } + } + } + // TODO: error handling + int res = fs->truncate(&inode, 0); + fs->inode_manager->free_inode(&inode); +} + +int FilesOperation::fischl_rmdir(const char *path) { + char *pathdup = strdup(path); + char *lastSlash = strrchr(pathdup, '/'); + *lastSlash = '\0'; + char *dirname = lastSlash + 1; + char *ParentPath = pathdup; + if (!strcmp(dirname, ".") || !strcmp(dirname, "..")) { + printf("refusing to remove . or ..\n"); + return -1; + } + FileNode *parent_filenode = fischl_find_entry(root_node, ParentPath); + if (parent_filenode == NULL) { + printf("parent %s not found by fischl_find_entry\n", ParentPath); + free(pathdup); + return -1; + } + u_int64_t parent_inode_number = parent_filenode->inode_number; + u_int64_t target_inode = 0; + + // remove its record from parent + INode_Data parent_INode; + parent_INode.inode_num = parent_inode_number; + fs->inode_manager->load_inode(&parent_INode); + char rw_buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < parent_INode.metadata.size / IO_BLOCK_SIZE; + idx++) { + fs->read(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(rw_buffer + i); + if (strcmp(ent.file_name, dirname) == 0) { + target_inode = ent.inode_number; + ent.inode_number = 0; + memset(ent.file_name, 0, sizeof(ent.file_name)); + ent.serialize(rw_buffer + i); + break; + } + } + if (target_inode) { + fs->write(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + break; + } + } + + // remove inode itself + if (target_inode) { + unlink_inode(target_inode); + // remove node itself and from parent hash + fischl_rm_entry(parent_filenode->subdirectory, dirname); + free(pathdup); + return 0; + } else { + printf("cannot find %s in %s", dirname, ParentPath); + free(pathdup); + return -1; + } +} + +int FilesOperation::fischl_chmod(const char *path, mode_t mode, + struct fuse_file_info *fi) { + (void)fi; + int res = 0; + u_int64_t fh = namei(path); + + if (fh == -1) { + return -ENOENT; + } + + INode_Data inode; + inode.inode_num = fh; + fs->inode_manager->load_inode(&inode); + inode.metadata.permissions = mode; + fs->inode_manager->save_inode(&inode); + return 0; +} + +int FilesOperation::fischl_chown(const char *path, uid_t uid, gid_t gid, + struct fuse_file_info *fi) { + (void)fi; + int res = 0; + u_int64_t fh = namei(path); + + if (fh == -1) { + return -ENOENT; + } + + INode_Data inode; + inode.inode_num = fh; + fs->inode_manager->load_inode(&inode); + inode.metadata.uid = uid; + inode.metadata.gid = gid; + fs->inode_manager->save_inode(&inode); + return 0; +} + +int FilesOperation::fischl_unlink(const char *path) { + char *pathdup = strdup(path); + char *lastSlash = strrchr(pathdup, '/'); + *lastSlash = '\0'; + char *filename = lastSlash + 1; + char *ParentPath = pathdup; + if (!strcmp(filename, ".") || !strcmp(filename, "..")) { + printf("refusing to remove . or ..\n"); + return -1; + } + FileNode *parent_filenode = fischl_find_entry(root_node, ParentPath); + if (parent_filenode == NULL) { + printf("parent %s not found by fischl_find_entry\n", ParentPath); + free(pathdup); + return -1; + } + u_int64_t parent_inode_number = parent_filenode->inode_number; + u_int64_t target_inode = 0; + + // remove its record from parent + INode_Data parent_INode; + parent_INode.inode_num = parent_inode_number; + fs->inode_manager->load_inode(&parent_INode); + char rw_buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < parent_INode.metadata.size / IO_BLOCK_SIZE; + idx++) { + fs->read(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(rw_buffer + i); + if (strcmp(ent.file_name, filename) == 0) { + target_inode = ent.inode_number; + ent.inode_number = 0; + memset(ent.file_name, 0, sizeof(ent.file_name)); + ent.serialize(rw_buffer + i); + break; + } + } + if (target_inode) { + fs->write(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + break; + } + } + + // remove inode itself + if (target_inode) { + unlink_inode(target_inode); + // remove node itself and from parent hash + fischl_rm_entry(parent_filenode->subdirectory, filename); + free(pathdup); + return 0; + } else { + printf("cannot find %s in %s", filename, ParentPath); + free(pathdup); + return -1; + } +} + +int FilesOperation::fischl_open(const char *path, struct fuse_file_info *fi) { + /*Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be filtered out / handled by + the kernel. if no files will use create function + */ + FileNode *get_file; + if ((get_file = fischl_find_entry(root_node, path)) == NULL) + return -ENOENT; + // if need to do with flag fi->flags ((fi->flags & O_ACCMODE)). Initial + // setting ALL access create function will handle file descriptor fi->fh + fi->fh = get_file->inode_number; + return 0; // SUCESS +} + +int FilesOperation::fischl_release(const char *path, + struct fuse_file_info *fi) { + /*Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be filtered out / handled by + the kernel. if no files will use create function + */ + FileNode *get_file; + if ((get_file = fischl_find_entry(root_node, path)) == NULL) + return -ENOENT; + // do with file descriptor that cannot be used + fi->fh = -1; + return 0; // SUCESS +} + +int FilesOperation::fischl_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) { + /** Write data to an open file + * + * Write should return exactly the number of bytes requested + * except on error. An exception to this is when the 'direct_io' + * mount option is specified (see read operation). + * + * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is + * expected to reset the setuid and setgid bits. + */ + // use path for debug, filedescriptor is enough + // FileNode *get_file; + // if((get_file = fischl_find_entry(root_node, path)) == NULL) + // return -ENOENT; + // Caution! this based on content in file are multiple of IO_BLOCK_SIZE, not + // the exact write size. based on current write_datablock API implement, when + // write_datablock pass with actual size not index this function should be + // fixed + + INode_Data inode; + // Assuming inode is correctly initialized here based on 'path' + inode.inode_num = fi->fh; + fs->inode_manager->load_inode(&inode); + // size_t len = (inode.metadata.size/IO_BLOCK_SIZE) * IO_BLOCK_SIZE; // + // Assuming each block is 4096 bytes + // Determine the length of the buffer + // Allocate memory for the new buffer + char *buffer = (char *)malloc(size); + memcpy(buffer, buf, size); + size_t bytes_write = fs->write(&inode, buffer, size, offset); + /*size_t block_index = offset / IO_BLOCK_SIZE; // Starting block index + size_t block_offset = offset % IO_BLOCK_SIZE; // Offset within the first block + while (bytes_write < size) { + char block_buffer[IO_BLOCK_SIZE]; // Temporary buffer for each block + size_t copy_size = std::min(size - bytes_write, IO_BLOCK_SIZE - + block_offset); memcpy(block_buffer + block_offset, buf + bytes_write, + copy_size); fs->write(&inode, block_buffer, IO_BLOCK_SIZE, + block_index*IO_BLOCK_SIZE); + // fprintf(stderr,"[%s ,%d] inode.size %d, block_index %d, block_buffer + %s\n",__func__,__LINE__, inode.size, block_index, block_buffer); bytes_write + += copy_size; block_index++; block_offset = 0; // Only the first block might + have a non-zero offset + }*/ + fs->inode_manager->save_inode(&inode); + free(buffer); + return bytes_write; // Return the actual number of bytes read +} + +int FilesOperation::insert_inode_to(u_int64_t parent_inode_number, + const char *name, INode_Data *new_inode) { + // trys to create a file under parent directory + if (strlen(name) >= 256) { + perror("Name too long, cannot create file or directory"); + return -1; + } + INode_Data inode; + inode.inode_num = parent_inode_number; + fs->inode_manager->load_inode(&inode); + if ((inode.metadata.permissions & S_IFMT) != S_IFDIR) { + fprintf(stderr, "[%s ,%d] please create under directory\n", __func__, + __LINE__); + return -1; + } + + // Check if file or directory already exists + char r_buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) { + fs->read(&inode, r_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(r_buffer + i); + if (strcmp(ent.file_name, name) == 0 && ent.inode_number != 0) { + if ((new_inode->metadata.permissions & S_IFMT) == S_IFDIR) { + fprintf(stderr, "[%s ,%d] %s/ already exists\n", __func__, __LINE__, + name); + } else { + fprintf(stderr, "[%s ,%d] %s already exists\n", __func__, __LINE__, + name); + } + return -1; + } + } + } + + bool allocated = false; + + char rw_buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) { + fs->read(&inode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(rw_buffer + i); + if (ent.inode_number == 0) { + allocated = true; + ent.inode_number = new_inode->inode_num; + strcpy(ent.file_name, name); + ent.serialize(rw_buffer + i); + break; + } + } + if (allocated) { + fs->write(&inode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + break; + } + } + + if (!allocated) { + char write_buffer[IO_BLOCK_SIZE] = {0}; + DirectoryEntry ent; + ent.inode_number = new_inode->inode_num; + strcpy(ent.file_name, name); + ent.serialize(write_buffer); + fs->write(&inode, write_buffer, IO_BLOCK_SIZE, + (inode.metadata.size / IO_BLOCK_SIZE) * IO_BLOCK_SIZE); + fs->inode_manager->save_inode(&inode); + } + + return 0; +} + +// TODO: rename dir and rename fail +int FilesOperation::fischl_rename(const char *path, const char *new_name, + unsigned int flags) { + char *pathdup = strdup(path); + char *lastSlash = strrchr(pathdup, '/'); + *lastSlash = '\0'; + char *filename = lastSlash + 1; + char *ParentPath = pathdup; + if (!strcmp(filename, ".") || !strcmp(filename, "..")) { + printf("refusing to remove . or ..\n"); + return -1; + } + FileNode *parent_filenode = fischl_find_entry(root_node, ParentPath); + if (parent_filenode == NULL) { + printf("parent %s not found by fischl_find_entry\n", ParentPath); + free(pathdup); + return -1; + } + u_int64_t parent_inode_number = parent_filenode->inode_number; + u_int64_t target_inode = 0; + + // remove its record from parent + INode_Data parent_INode; + parent_INode.inode_num = parent_inode_number; + fs->inode_manager->load_inode(&parent_INode); + char rw_buffer[IO_BLOCK_SIZE] = {0}; + for (u_int64_t idx = 0; idx < parent_INode.metadata.size / IO_BLOCK_SIZE; + idx++) { + fs->read(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(rw_buffer + i); + if (strcmp(ent.file_name, filename) == 0) { + target_inode = ent.inode_number; + break; + } + } + if (target_inode) { + break; + } + } + + // remove inode itself + if (target_inode) { + INode_Data ret; + ret.inode_num = target_inode; + fs->inode_manager->load_inode(&ret); + + if (flags == RENAME_EXCHANGE) { + printf("RENAME_EXCHANGE "); + } else if (flags == RENAME_NOREPLACE) { + printf("RENAME_NOREPLACE "); + } else + printf("ELSE "); + + printf("FOUND INODE AT %llu %s\n", target_inode, new_name); + char *pathdup2 = strdup(new_name); + char *lastSlash2 = strrchr(pathdup2, '/'); + *lastSlash2 = '\0'; // Split the string into parent path and new directory + // name; \0 + char *newDirname2 = + lastSlash2 + 1; //\0, get from + char *ParentPath2 = pathdup2; // pathdup are separated by pathdup, so it + // take only + + FileNode *parent_filenode2 = strlen(ParentPath) + ? fischl_find_entry(root_node, ParentPath2) + : root_node->self_info; + if (parent_filenode2 == NULL) { + fprintf(stderr, "[%s ,%d] ParentPath:{%s} not found\n", __func__, + __LINE__, ParentPath2); + free(pathdup2); + return -ENOENT; // parentpath directory does not exist + } + u_int64_t parent_inode_number2 = parent_filenode2->inode_number; + // printf("%s, %llu, %s\n", parent_filenode->name, parent_inode_number, + // newDirname); make new inode + if (insert_inode_to(parent_inode_number2, newDirname2, &ret) < 0) { + return -1; + } + + for (u_int64_t idx = 0; idx < parent_INode.metadata.size / IO_BLOCK_SIZE; + idx++) { + fs->read(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + DirectoryEntry ent; + for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) { + ent.deserialize(rw_buffer + i); + if (strcmp(ent.file_name, filename) == 0) { + target_inode = ent.inode_number; + ent.inode_number = 0; + memset(ent.file_name, 0, sizeof(ent.file_name)); + ent.serialize(rw_buffer + i); + break; + } + } + if (target_inode) { + fs->write(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE); + break; + } + } + + fischl_rm_entry(parent_filenode->subdirectory, filename); + fischl_add_entry(parent_filenode2->subdirectory, ret.inode_num, newDirname2, + &ret); + free(pathdup); + // remove node itself and from parent hash + free(pathdup2); + return 0; + } else { + printf("cannot find %s in %s", filename, ParentPath); + free(pathdup); + return -1; + } +} + +int FilesOperation::fischl_truncate(const char *path, off_t offset, + struct fuse_file_info *fi) { + (void)fi; + int res = 0; + u_int64_t fh = namei(path); + + if (fh == -1) { + return -ENOENT; + } + + INode_Data inode; + inode.inode_num = fh; + fs->inode_manager->load_inode(&inode); + res = fs->truncate(&inode, offset); + fs->inode_manager->save_inode(&inode); + return res; +} + +int FilesOperation::fischl_read(const char *path, char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) { + /** Read data from an open file + * + * Read should return exactly the number of bytes requested except + * on EOF or error, otherwise the rest of the data will be + * substituted with zeroes. An exception to this is when the + * 'direct_io' mount option is specified, in which case the return + * value of the read system call will reflect the return value of + * this operation. + */ + // Caution! this based on content in file are multiple of IO_BLOCK_SIZE, not + // the exact write size. based on current read_datablock API implement, when + // read_datablock pass with actual size not index this function should be + // fixed + INode_Data inode; + // Assuming inode is correctly initialized here based on 'path' + inode.inode_num = fi->fh; + fs->inode_manager->load_inode(&inode); + size_t bytes_read = fs->read(&inode, buf, size, offset); + /*size_t len = (inode.metadata.size/IO_BLOCK_SIZE) * IO_BLOCK_SIZE; // + Assuming each block is 4096 bytes + + if (offset >= len) return 0; // Offset is beyond the end of the file + if (offset + size > len) size = len - offset; // Adjust size if it goes + beyond EOF + + size_t bytes_read = 0; + size_t block_index = offset / IO_BLOCK_SIZE; // Starting block index + size_t block_offset = offset % IO_BLOCK_SIZE; // Offset within the first block + // fprintf(stderr,"[%s ,%d] inode.metadata.size %d\n",__func__,__LINE__, + inode.metadata.size); while (bytes_read < size && block_index < + inode.metadata.size/IO_BLOCK_SIZE) { char block_buffer[IO_BLOCK_SIZE]; // + Temporary buffer for each block fs->read(&inode, block_buffer, IO_BLOCK_SIZE, + block_index*IO_BLOCK_SIZE); + // fprintf(stderr,"[%s ,%d] block_index %d\n",__func__,__LINE__, + block_index); size_t copy_size = std::min(size - bytes_read, IO_BLOCK_SIZE - + block_offset); memcpy(buf + bytes_read, block_buffer + block_offset, + copy_size); + // fprintf(stderr,"[%s ,%d] buf %s, block_buffer %s\n",__func__,__LINE__, + buf, block_buffer); bytes_read += copy_size; block_index++; block_offset = 0; + // Only the first block might have a non-zero offset + }*/ + + return bytes_read; // Return the actual number of bytes read +} \ No newline at end of file diff --git a/lib/fischl.cpp b/lib/fischl.cpp index 2246652..433faa7 100644 --- a/lib/fischl.cpp +++ b/lib/fischl.cpp @@ -1,8 +1,227 @@ -#include "fischl.h" - -#include - -int fischl::init(){ - printf("Hello Fischl!"); - return 3; +#define FUSE_USE_VERSION 31 + +#include +#include +#include +#include +#include +#include +#include +#include "fs.hpp" +#include "files.h" + +/* + * Command line options + * + * We can't set default values for the char* fields here because + * fuse_opt_parse would attempt to free() them when the user specifies + * different values on the command line. + */ +static struct options { + RawDisk *H; // Use FakeRawDisk here if memory sanitizer complains + Fs *fs; + FilesOperation *fsop; + int show_help; +} options; + +#define OPTION(t, p) \ + { t, offsetof(struct options, p), 1 } +static const struct fuse_opt option_spec[] = { + OPTION("-h", show_help), + OPTION("--help", show_help), + FUSE_OPT_END +}; + +void* fischl_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { + options.fsop->initialize_rootinode(); +} + +int fischl_create(const char *path, mode_t mode, struct fuse_file_info *fi) { + return options.fsop->fischl_create(path, mode, fi); +} + + +void fischl_destroy(void* private_data) { + +} + +static int fischl_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { + return options.fsop->fischl_getattr(path, stbuf, fi); +} + +static int fischl_access(const char* path, int mask) { + + // return 0 when access is allowed + return 0; +} + +static int fischl_readlink(const char* path, char* buf, size_t size) { + return -1; +} + +static int fischl_opendir(const char* path, struct fuse_file_info* fi) { + u_int64_t fh = options.fsop->namei(path); + fi->fh = fh; + return 0; +} + +static int fischl_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t ft, struct fuse_file_info *fi, enum fuse_readdir_flags flg) { + return options.fsop->fischl_readdir(path, buf, filler, ft, fi, flg); +} + +static int fischl_mknod(const char* path, mode_t mode, dev_t rdev) { + return options.fsop->fischl_mknod(path, mode, rdev); +} + +static int fischl_mkdir(const char *path, mode_t mode) { + return options.fsop->fischl_mkdir(path, mode); +} + +static int fischl_unlink(const char* path) { + return options.fsop->fischl_unlink(path); +} + +static int fischl_rmdir(const char* path) { + return options.fsop->fischl_rmdir(path); +} + +static int fischl_symlink(const char* to, const char* from) { + return -1; +} + +static int fischl_rename(const char *path, const char *new_name, unsigned int flags) { + return options.fsop->fischl_rename(path, new_name, flags); +} + +static int fischl_link(const char* from, const char* to) { + return -1; +} + +static int fischl_chmod(const char *path, mode_t mode, struct fuse_file_info *fi) { + return options.fsop->fischl_chmod(path, mode, fi); +} + +static int fischl_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi) { + return options.fsop->fischl_chown(path, uid, gid, fi); +} + +static int fischl_truncate(const char *path, off_t offset, struct fuse_file_info *fi) { + return options.fsop->fischl_truncate(path, offset, fi); +} + +static int fischl_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi) { + return -1; +} + +static int fischl_open(const char *path, struct fuse_file_info *fi) { + return options.fsop->fischl_open(path, fi); +} + +static int fischl_read(const char* path, char *buf, size_t size, off_t offset, struct fuse_file_info* fi) { + return options.fsop->fischl_read(path, buf, size, offset, fi); +} + +static int fischl_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { + return options.fsop->fischl_write(path, buf, size, offset, fi); +} + +static int fischl_statfs(const char* path, struct statvfs* stbuf) { + return -1; +} + +static int fischl_release(const char* path, struct fuse_file_info *fi) { + return options.fsop->fischl_release(path, fi); +} + +static int fischl_releasedir(const char* path, struct fuse_file_info *fi) { + return options.fsop->fischl_releasedir(path, fi); +} + + +static const struct fuse_operations fischl_oper = { + + + .getattr = fischl_getattr, + //.readlink = fischl_readlink, + .mknod = fischl_mknod, + .mkdir = fischl_mkdir, + .unlink = fischl_unlink, + .rmdir = fischl_rmdir, + //.symlink = fischl_symlink, + .rename = fischl_rename, + //.link = fischl_link, + .chmod = fischl_chmod, + .chown = fischl_chown, + .truncate = fischl_truncate, + .open = fischl_open, + .read = fischl_read, + .write = fischl_write, + //.statfs = fischl_statfs, + .release = fischl_release, + /* +#ifdef HAVE_SETXATTR + .setxattr = fischl_setxattr, + .getxattr = fischl_getxattr, + .listxattr = fischl_listxattr, + .removexattr = fischl_removexattr, +#endif +*/ + .opendir = fischl_opendir, + .readdir = fischl_readdir, + .releasedir = fischl_releasedir, + .init = fischl_init, + .destroy = fischl_destroy, + .access = fischl_access, + .create = fischl_create, + //.utimens = fischl_utimens, + //.bmap = fischl_bmap, + //.ioctl = fischl_ioctl, + //.poll = fischl_poll, +}; + +static void show_help(const char *progname) +{ + printf("usage: %s [options] \n\n", progname); + printf("File-system specific options:\n" + " --name= Name of the \"fischl\" file\n" + " (default: \"fischl\")\n" + " --contents= Contents \"fischl\" file\n" + " (default \"fischl, World!\\n\")\n" + "\n"); +} + + +int fischl(int argc, char *argv[]) +{ + int ret; + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + srand(time(NULL)); // Seed the random number generator + //const char* d = (argc < 2) ? "/dev/vdc" : argv[1]; + + //setupTestDirectory(&options.root); + options.H = new FakeRawDisk(23552); + options.fs = new Fs(options.H); + options.fs->format(); + options.fsop = new FilesOperation(*options.H, options.fs); + + + + /* Parse options */ + if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1) + return 1; + + /* When --help is specified, first print our own file-system + specific help text, then signal fuse_main to show + additional help (by adding `--help` to the options again) + without usage: line (by setting argv[0] to the empty + string) */ + if (options.show_help) { + show_help(argv[0]); + assert(fuse_opt_add_arg(&args, "--help") == 0); + args.argv[0][0] = '\0'; + } + + ret = fuse_main(args.argc, args.argv, &fischl_oper, NULL); + fuse_opt_free_args(&args); + return ret; } \ No newline at end of file diff --git a/lib/fs/fs_file_io.cpp b/lib/fs/fs_file_io.cpp index 7430804..8905a3d 100644 --- a/lib/fs/fs_file_io.cpp +++ b/lib/fs/fs_file_io.cpp @@ -1,7 +1,5 @@ #include "fs.hpp" -const u_int64_t INDIRECT_BLOCKS = IO_BLOCK_SIZE / sizeof(u_int64_t); - class DatablockOperation { public: DatablockOperation(int (*_skip)(DatablockOperation *, u_int64_t) = nullptr) @@ -302,6 +300,9 @@ ssize_t Fs::read(INode_Data *inode_data, char buf[], size_t count, size_t offset) { int err; + if (offset >= inode_data->metadata.size) + return 0; + u_int64_t start_block_index = offset / IO_BLOCK_SIZE; size_t internal_offset = offset - (start_block_index * IO_BLOCK_SIZE); diff --git a/lib/main.cpp b/lib/main.cpp index c77fa38..76875e2 100644 --- a/lib/main.cpp +++ b/lib/main.cpp @@ -8,7 +8,7 @@ #include #include -int main() { +int main(int argc, char *argv[]) { // printf("hello word!"); // fischl *F = new fischl; // F->init(); @@ -170,106 +170,142 @@ int main() { // err = fs->lseek_next_hole(&inode_data, offs + 100000); // printf("lseek_next_hole (%d): %d\n\n", offs + 100000, err); - // RawDisk *disk = new FakeRawDisk(2048); - // Fs *fs = new Fs(disk); - // fs->format(); - - // INode_Data inode_data; - // fs->inode_manager->new_inode(1, 2, 3, &inode_data); - - // char cwd_buf[PATH_MAX]; - // int fd; - - // assert(getcwd(cwd_buf, sizeof(cwd_buf)) != NULL); - - // printf("\n\n("); - - // printf(cwd_buf); - - // printf(")\n\n"); - - // fd = open("/home/connor", O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR); - // assert(fd != -1); - - // u_int64_t test_start_range = IO_BLOCK_SIZE * 100; - // u_int64_t test_write_range = IO_BLOCK_SIZE * 50; - // u_int64_t test_read_range = IO_BLOCK_SIZE * 50; - - // char zeros[test_write_range] = {0}; - // char ones[test_write_range]; - // memset(ones, 1, test_write_range); - - // char *write_buf = ones; - // char reference_read_buf[test_read_range]; - // char test_read_buf[test_read_range]; - // size_t offset, count; - // int test_res, ref_res; - - // for (int i = 0; i < 1000; ++i) { - // offset = rand() & test_start_range; - - // switch (rand() % 2) { - // case 0: - // count = rand() & test_write_range; - // write_buf = (write_buf == ones) ? zeros : ones; - // printf("write: %ds count=%d offset=%d\n", write_buf[0], count, offset); - // test_res = fs->write(&inode_data, write_buf, count, offset); - // assert(lseek(fd, offset, SEEK_SET) == offset); - // ref_res = write(fd, write_buf, count); - // break; - // case 1: - // count = rand() & test_read_range; - // printf("read: count=%d offset=%d\n", count, offset); - // test_res = fs->read(&inode_data, test_read_buf, count, offset); - // assert(lseek(fd, offset, SEEK_SET) == offset); - // ref_res = read(fd, reference_read_buf, count); - // bool reads_are_equal = true; - // for (size_t j = 0; j < count; ++j) - // if (test_read_buf[i] != reference_read_buf[i]) { - // reads_are_equal = false; - // break; - // } - // assert(reads_are_equal); - // break; - // } - - // assert(test_res == ref_res); - // } - - RawDisk *disk = new FakeRawDisk(5120); + RawDisk *disk = new FakeRawDisk(5000); Fs *fs = new Fs(disk); fs->format(); - int buf_size = IO_BLOCK_SIZE * 200; - int loops = 14 * 1024 * 1024 / buf_size; - - char buf[buf_size]; - - memset(buf, 1, sizeof(buf)); - INode_Data inode_data; fs->inode_manager->new_inode(1, 2, 3, &inode_data); - int res; + char cwd_buf[PATH_MAX]; + int fd; - for (int j = 0; j < loops; ++j) { - res = fs->write(&inode_data, buf, sizeof(buf), sizeof(buf) * j); - printf("write: %d j=%d\n", res, j); - } + assert(getcwd(cwd_buf, sizeof(cwd_buf)) != NULL); - for (int j = 0; j < loops; ++j) { + fd = open("/tmp", O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR); + assert(fd != -1); - memset(buf, 0, sizeof(buf)); - res = fs->read(&inode_data, buf, sizeof(buf), sizeof(buf) * j); + u_int64_t test_start_range = IO_BLOCK_SIZE * 3584; + u_int64_t test_io_range = IO_BLOCK_SIZE * 100; - printf("read: %d j=%d\n", res, j); + char ones[test_io_range]; + memset(ones, 1, test_io_range); + char twos[test_io_range]; + memset(twos, 2, test_io_range); - for (int i = 0; i < sizeof(buf); ++i) - if (buf[1] != 1) { - printf("error: %d\n", i); - return -1; + char *write_buf = ones; + char reference_read_buf[test_io_range]; + char test_read_buf[test_io_range]; + size_t offset, count; + int test_res, ref_res; + bool reads_are_equal; + bool canwrite; + + size_t weird_offset = 6508064; + + for (int i = 0; i < 100000; ++i) { + offset = rand() % test_start_range; + + reads_are_equal = true; + + switch (rand() % 3) { + case 0: + count = rand() % test_io_range; + write_buf = (write_buf == ones) ? twos : ones; + if (offset <= weird_offset && (count + offset) > weird_offset) + printf("write: %ds count=%d offset=%d\n", write_buf[0], count, offset); + test_res = fs->write(&inode_data, write_buf, count, offset); + assert(lseek(fd, offset, SEEK_SET) == offset); + ref_res = write(fd, write_buf, count); + break; + case 1: + count = rand() % test_io_range; + if (offset <= weird_offset && (count + offset) > weird_offset) + printf("read: count=%d offset=%d\n", count, offset); + test_res = fs->read(&inode_data, test_read_buf, count, offset); + assert(lseek(fd, offset, SEEK_SET) == offset); + ref_res = read(fd, reference_read_buf, count); + for (size_t j = 0; j < count; ++j) + if (test_read_buf[i] != reference_read_buf[i]) { + reads_are_equal = false; + break; + } + break; + case 2: + if (offset <= weird_offset) + printf("truncate: length=%d\n", offset); + test_res = fs->truncate(&inode_data, offset); + ref_res = ftruncate(fd, offset); + break; + } + + // printf("test_res=%d, ref_res=%d\n", test_res, ref_res); + assert(test_res == ref_res); + + if (!reads_are_equal && count > 0) { + int prev_test = test_read_buf[0], prev_ref = reference_read_buf[0], + same_count = 1; + for (size_t j = 1; j < count; ++j) { + u_int64_t byte_index = (j + offset); + if (byte_index % IO_BLOCK_SIZE == 0) + printf("Block: %d\n", byte_index / IO_BLOCK_SIZE); + if (prev_test != test_read_buf[j] || + prev_ref != reference_read_buf[j]) { + printf("rt %d %d%s\n", prev_ref, prev_test, + (prev_test != prev_ref) + ? " -----DIFF----- -----DIFF----- -----DIFF-----" + : ""); + printf("^^^^ same for %d bytes ending at %d, starting at %d ^^^^\n", + same_count, byte_index, byte_index - same_count); + prev_test = test_read_buf[j]; + prev_ref = reference_read_buf[j]; + same_count = 1; + } else { + same_count++; + } } + printf("rt %d %d%s\n", prev_test, prev_test, + (prev_test != prev_ref) + ? " -----DIFF----- -----DIFF----- -----DIFF-----" + : ""); + printf("^^^^ same for %d bytes ^^^^\n", same_count); + } + + assert(reads_are_equal); } - return 0; + // RawDisk *disk = new FakeRawDisk(5120); + // Fs *fs = new Fs(disk); + // fs->format(); + + // int buf_size = IO_BLOCK_SIZE * 200; + // int loops = 14 * 1024 * 1024 / buf_size; + + // char buf[buf_size]; + + // memset(buf, 1, sizeof(buf)); + + // INode_Data inode_data; + // fs->inode_manager->new_inode(1, 2, 3, &inode_data); + + // int res; + + // for (int j = 0; j < loops; ++j) { + // res = fs->write(&inode_data, buf, sizeof(buf), sizeof(buf) * j); + // printf("write: %d j=%d\n", res, j); + // } + + // for (int j = 0; j < loops; ++j) { + + // memset(buf, 0, sizeof(buf)); + // res = fs->read(&inode_data, buf, sizeof(buf), sizeof(buf) * j); + + // printf("read: %d j=%d\n", res, j); + + // for (int i = 0; i < sizeof(buf); ++i) + // if (buf[1] != 1) { + // printf("error: %d\n", i); + // return -1; + // } + // } } \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7e445d8..21dcccd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,34 +1,69 @@ -set(TARGET_LAYER0 test_layer0) -set(TARGET_LAYER1_API test_layer1_API) -# set(TARGET_LAYER1_TEST1 test_layer1_test1) -set(DIR_PLACE /dev/vdb) +# set(TARGET_LAYER0 test_layer0) +# set(TARGET_LAYER1_API test_layer1_API) +# set(TARGET_LAYER2_API test_layer2_API) +# set(TARGET_DIR_API test_dir_API) +# set(DIR_PLACE /dev/vdb) +# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address") -# add test sources here ... -add_executable(${TARGET_LAYER0} - # add need lib and source code here - layer0.cpp - - ../lib/rawdisk.cpp - -) -add_executable(${TARGET_LAYER1_API} - # add need lib and source code here - layer1_API.cpp - - ../lib/fischl.cpp - ../lib/rawdisk.cpp - ../lib/fs/datablock_manager.cpp - ../lib/fs/fs_data_types.cpp - ../lib/fs/fs_file_io.cpp - ../lib/fs/fs.cpp - ../lib/fs/inode_manager.cpp -) -# add_executable(${TARGET_LAYER1_TEST1} +# # add test sources here ... +# add_executable(${TARGET_LAYER0} # # add need lib and source code here -# layer1_test1.cpp +# layer0.cpp + +# ../lib/rawdisk.cpp + +# ) +# add_executable(${TARGET_LAYER1_API} +# # add need lib and source code here +# layer1_API.cpp +# ../lib/rawdisk.cpp +# ../lib/fs/datablock_manager.cpp +# ../lib/fs/fs_data_types.cpp +# ../lib/fs/fs_file_io.cpp +# ../lib/fs/fs.cpp +# ../lib/fs/inode_manager.cpp +# ) +# add_executable(${TARGET_LAYER2_API} +# ../lib/direntry.cpp +# ../lib/rawdisk.cpp +# ../lib/fs/datablock_manager.cpp +# ../lib/fs/fs_data_types.cpp +# ../lib/fs/fs_file_io.cpp +# ../lib/fs/fs.cpp +# ../lib/fs/inode_manager.cpp +# ../lib/files.cpp +# layer2_API_dir.cpp +# ) +# add_executable(${TARGET_DIR_API} +# ../lib/direntry.cpp +# ../lib/rawdisk.cpp +# ../lib/fs/datablock_manager.cpp +# ../lib/fs/fs_data_types.cpp +# ../lib/fs/fs_file_io.cpp +# ../lib/fs/fs.cpp +# ../lib/fs/inode_manager.cpp +# dir_API.cpp # ) -# add test to activate ctest -VV -add_test(NAME ${TARGET_LAYER0} COMMAND sudo ./${TARGET_LAYER0} ${DIR_PLACE}) -add_test(NAME ${TARGET_LAYER1_API} COMMAND sudo ./${TARGET_LAYER1_API} ${DIR_PLACE}) -# add_test(NAME ${TARGET_LAYER1_TEST1} COMMAND sudo ./${TARGET_LAYER1_TEST1} ${DIR_PLACE}) \ No newline at end of file +# # Link Google Test to your test executables +# target_link_libraries(${TARGET_LAYER0} gtest gtest_main) +# target_link_libraries(${TARGET_LAYER1_API} gtest gtest_main) +# target_link_libraries(${TARGET_DIR_API} gtest gtest_main) + +# # add test to activate ctest -VV +# add_test(NAME ${TARGET_LAYER0} COMMAND sudo ./${TARGET_LAYER0} ${DIR_PLACE}) +# add_test(NAME ${TARGET_LAYER1_API} COMMAND sudo ./${TARGET_LAYER1_API} ${DIR_PLACE}) +# add_test(NAME ${TARGET_LAYER2_API} COMMAND sudo ./${TARGET_LAYER2_API} ${DIR_PLACE}) +# add_test(NAME ${TARGET_DIR_API} COMMAND sudo ./${TARGET_DIR_API} ${DIR_PLACE}) + + +# # Add the -Wall flag +# target_compile_options(${TARGET_LAYER2_API} PRIVATE -Wall) + +# # Use pkg-config to get flags for fuse3 +# find_package(PkgConfig REQUIRED) +# pkg_search_module(FUSE3 REQUIRED fuse3) + +# # Add the flags from pkg-config for fuse3 +# target_include_directories(${TARGET_LAYER2_API} PRIVATE ${FUSE3_INCLUDE_DIRS}) +# target_link_libraries(${TARGET_LAYER2_API} PRIVATE ${FUSE3_LIBRARIES} gtest gtest_main) \ No newline at end of file diff --git a/test/dir_API.cpp b/test/dir_API.cpp new file mode 100644 index 0000000..265d608 --- /dev/null +++ b/test/dir_API.cpp @@ -0,0 +1,350 @@ +/*********************************************************** + Directory owns treeNode and FileNode structure, detect S_IFDIR to make treeNode or not (see add_entry Function) + File owns FileNode structure only, detect !S_IFDIR + +*/ +#include +#include +#include +#include +#include +#include +#include +#include "fs.hpp" +#include "direntry.h" + +typedef struct file_test{ + const char* name; + file_test* next;//use linked-list to know the file at the same level directory +}file_test; +typedef struct dir_test{ + const char* name; + file_test* inFile; + dir_test* subdir; + dir_test* next;//use linked-list to know the other dir at the same parent dir. +}dir_test; + +//global can be taken +const char* d; +TreeNode *root; +std::string target_filepath; +dir_test* mock_root = nullptr; + +int total_dir_num = 0; +int total_file_num = 0; +int total_free_dir = 0; +int total_free_file = 0; + + +const char* generateRandomName(size_t length) { + const std::string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + std::string randomString; + + for (size_t i = 0; i < length; ++i) { + randomString += chars[rand() % chars.size()]; + } + + // Allocate memory and copy the string + char* name = new char[randomString.length() + 1]; + strcpy(name, randomString.c_str()); + return name; +} + +// Recursive function to create directory hierarchy +dir_test* createDirHierarchy(int level, int maxLevel) { + if (level > maxLevel) { + return nullptr; + } + + dir_test* head = nullptr; + dir_test* current = nullptr; + + for (int i = 0; i < 3; ++i) { + dir_test* newDir = new dir_test; + newDir->name = generateRandomName(6); // Generate a random name for the directory + newDir->inFile = nullptr; // Initialize file list to nullptr + newDir->subdir = createDirHierarchy(level + 1, maxLevel); // Recursively create subdirectories + newDir->next = nullptr; + + // Create file list for this directory + file_test* fileHead = nullptr; + file_test* fileCurrent = nullptr; + for (int j = 0; j < 3; ++j) { + file_test* newFile = new file_test; + newFile->name = generateRandomName(6); // Generate a random name for the file + newFile->next = nullptr; + + if (!fileHead) { + fileHead = newFile; + } else { + fileCurrent->next = newFile; + } + fileCurrent = newFile; + } + newDir->inFile = fileHead; + + // Add the new directory to the list + if (!head) { + head = newDir; + } else { + current->next = newDir; + } + current = newDir; + } + + return head; +} + +// Setup function for the test directory +void setupTestDirectory(dir_test** root) { + // Allocate memory for root + *root = new dir_test; + (*root)->name = strdup("/"); // use / as begin + (*root)->inFile = nullptr; // Initialize file list to nullptr + (*root)->subdir = createDirHierarchy(0, 1); + (*root)->next = nullptr; + file_test* fileHead = nullptr; + file_test* fileCurrent = nullptr; + for (int j = 0; j < 3; ++j) { + file_test* newFile = new file_test; + newFile->name = generateRandomName(6); // Generate a random name for the file + newFile->next = nullptr; + + if (!fileHead) { + fileHead = newFile; + } else { + fileCurrent->next = newFile; + } + fileCurrent = newFile; + } + (*root)->inFile = fileHead; +} + +// Function to free a list of files +void freeFileList(file_test* fileList) { + while (fileList != nullptr) { + file_test* temp = fileList; + fileList = fileList->next; + total_free_file++;//for debug + delete[] temp->name; // Free the name string + delete temp; // Free the current file + } +} + +// Recursive function to free the directory hierarchy +void freeDirHierarchy(dir_test* dir) { + while (dir != nullptr) { + dir_test* temp = dir; + dir = dir->next; + total_free_dir++;//for debug + freeFileList(temp->inFile); // Free the list of files in the directory + freeDirHierarchy(temp->subdir); // Recursively free subdirectories + delete[] temp->name; // Free the name string + delete temp; // Free the current directory + } +} + +// Function to print the list of files in a directory +void printFileList(const file_test* fileList) { + const file_test* currentFile = fileList; + while (currentFile != nullptr) { + // std::cout << " File: " << currentFile->name << std::endl; + total_file_num++; + currentFile = currentFile->next; + } +} + +void traverseDirHierarchy(const dir_test* dir, int depth = 0) { + while (dir != nullptr) { + // std::cout << "Depth " << depth << ", Directory: " << dir->name << std::endl; + total_dir_num++;//for debug + + // Print files in this directory + printFileList(dir->inFile); + // Recursively traverse subdirectories + traverseDirHierarchy(dir->subdir, depth + 1); + + // Go to the next directory at the same level + dir = dir->next; + } +} + +TEST(DirTest, root_test) { + //Init fake root directory + INode_Data inode_root; + u_int64_t file_permissions = 0; + inode_root.metadata.permissions = file_permissions | S_IFDIR; + root = fischl_init_entry(0, mock_root->name, &inode_root);//0 is inode number assigned by inode_allocate() +} +TEST(DirTest, AddFile_test) { + //assume file and dir itself(content,metadata) same,but different name and inode number + INode_Data inode_file; + INode_Data inode_dir; + u_int64_t file_permissions = 0; + file_permissions = 0; + inode_dir.metadata.permissions = file_permissions | S_IFDIR; + fischl_add_entry(root, 2, mock_root->inFile->name,&inode_file); + fischl_add_entry(root, 3, mock_root->subdir->name,&inode_dir); +} +TEST(DirTest, FindFile_test) { + //find file + target_filepath = std::string("/") + mock_root->inFile->name; + FileNode *get_file = fischl_find_entry(root,target_filepath.c_str()); + EXPECT_TRUE(get_file != NULL); + EXPECT_STREQ(get_file->name, mock_root->inFile->name); + //find dir + target_filepath = std::string("/") + mock_root->subdir->name + "/"; + FileNode *get_dir = fischl_find_entry(root,target_filepath.c_str()); + EXPECT_TRUE(get_dir != NULL);//detect this should find success + EXPECT_STREQ(get_dir->name, mock_root->subdir->name); + ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory + //check . function + get_dir = fischl_find_entry(root,"./"); + EXPECT_TRUE(get_dir != NULL);//detect this should find success + EXPECT_STREQ(get_dir->name, mock_root->name); + ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory + //check .. function + get_dir = fischl_find_entry(root,".."); + EXPECT_TRUE(get_dir != NULL);//detect this should find success + EXPECT_STREQ(get_dir->name, mock_root->name); + ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory +} +TEST(DirTest, Add_FindFile_test) { + //add file and dir under subdirectory instead of root + INode_Data inode_file; + INode_Data inode_dir; + u_int64_t file_permissions = 0; + file_permissions = 0; + inode_dir.metadata.permissions = file_permissions | S_IFDIR; + + /*add with subdirectory*/ + //Treenode dir(you cannot find here), you only can get Filenode dir based on fischl_find_entry Function + //So use Filenode->subdirectory will point to the treenode dir, then can add files + target_filepath = std::string("/") + mock_root->subdir->name + "/"; + FileNode *get_dir = fischl_find_entry(root, target_filepath.c_str()); + fischl_add_entry(get_dir->subdirectory, 4, mock_root->subdir->inFile->name, &inode_file); + + //verfication treeNode and Filenode relationship + target_filepath = std::string("/") + mock_root->subdir->name + "/" + mock_root->subdir->inFile->name; + TreeNode *get_dir_tree = find_parentPath(root,target_filepath.c_str()); + ASSERT_TRUE(get_dir_tree == get_dir->subdirectory);//treeNode dir should be same as treeNode subdir in that Filenode + + //two Ways to get File(include dir itself) information + FileNode *get_file = NULL; + //1. absolute path, the root(treeNode) will always exist when initialize + get_file = fischl_find_entry(root,target_filepath.c_str()); + EXPECT_TRUE(get_file != NULL); + EXPECT_STREQ(get_file->name,mock_root->subdir->inFile->name); + //2. relative path, the get_dir(FileNode)->subdirectory(treeNode), use treeNode(dir) to find + target_filepath = std::string("/") + mock_root->subdir->inFile->name; + get_file = fischl_find_entry(get_dir->subdirectory,target_filepath.c_str()); + EXPECT_TRUE(get_file != NULL); + EXPECT_STREQ(get_file->name, mock_root->subdir->inFile->name); + /**********************************************************/ + //add one more file under fist subdir + fischl_add_entry(get_dir->subdirectory, 5, mock_root->subdir->inFile->next->name, &inode_file); + //add one more directory under fist subdir + fischl_add_entry(get_dir->subdirectory, 6, mock_root->subdir->subdir->name, &inode_dir); + //find + target_filepath = std::string("./") + mock_root->subdir->inFile->next->name; + get_file = fischl_find_entry(get_dir->subdirectory, target_filepath.c_str()); + EXPECT_TRUE(get_file != NULL); + EXPECT_STREQ(get_file->name, mock_root->subdir->inFile->next->name); + //use .. from fist subdir to find file1 + target_filepath = std::string("../") + mock_root->inFile->name; + get_file = fischl_find_entry(get_dir->subdirectory,target_filepath.c_str()); + EXPECT_TRUE(get_file != NULL); + EXPECT_STREQ(get_file->name,mock_root->inFile->name); + //check fist subdir with . + get_dir = fischl_find_entry(get_dir->subdirectory,"."); + EXPECT_TRUE(get_dir != NULL);//detect this should find success + EXPECT_STREQ(get_dir->name, mock_root->subdir->name); + ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory + //check root via fist subdir + get_dir = fischl_find_entry(get_dir->subdirectory,".."); + EXPECT_TRUE(get_dir != NULL);//detect this should find success + EXPECT_STREQ(get_dir->name, mock_root->name); + ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory + //use .. to access parent directory + target_filepath = std::string("/") + mock_root->subdir->name + "/" + mock_root->subdir->subdir->name + "/.."; + get_dir = fischl_find_entry(root, target_filepath.c_str()); + EXPECT_TRUE(get_dir != NULL); + EXPECT_STREQ(get_dir->name, mock_root->subdir->name); + target_filepath = std::string("/") + mock_root->subdir->name + "/" + mock_root->subdir->subdir->name + "/../.."; + get_file = fischl_find_entry(root, target_filepath.c_str()); + EXPECT_TRUE(get_file != NULL); + EXPECT_STREQ(get_file->name, mock_root->name); + EXPECT_TRUE(get_file->subdirectory != NULL); + EXPECT_TRUE(get_file->subdirectory->self_info == get_file); +} + +// TEST(DirTest, Scale_test){ +// INode_Data inode_file; +// INode_Data inode_dir; +// u_int64_t file_permissions = 0; +// file_permissions = 0; +// inode_dir.permissions = file_permissions | S_IFDIR; +// dir_test* temp = mock_root; +// // First loop: Add files and subdirectories under root +// file_test* currentFile = temp->inFile; +// dir_test* currentSubdir = temp->subdir; +// +// for (int i = 1; i < 7; ++i) { +// if (currentFile) { +// //add can still add the same filename and dir name, but it will be linked behind the first added +// fischl_add_entry(root, i, currentFile->name, &inode_file); +// currentFile = currentFile->next; +// } +// if (currentSubdir) { +// fischl_add_entry(root, i + 1, currentSubdir->name, &inode_dir); +// currentSubdir = currentSubdir->next; +// } +// } + +// // Second loop: Process each subdir under root +// temp = mock_root->subdir; +// while (temp) { +// target_filepath = "/" + std::string(temp->name) + "/"; +// FileNode* get_dir = fischl_find_entry(root, target_filepath.c_str()); + +// ASSERT_TRUE(get_dir != NULL); +// EXPECT_STREQ(get_dir->name, temp->name); +// ASSERT_TRUE(get_dir->subdirectory != NULL); +// // Add files and subdirectories in each subdir +// file_test* currentFile = temp->inFile; +// dir_test* currentSubSubdir = temp->subdir; +// for (int j = 7; j < 13; ++j) { +// if (currentFile) { +// fischl_add_entry(get_dir->subdirectory, j, currentFile->name, &inode_file); +// currentFile = currentFile->next; +// } +// if (currentSubSubdir) { +// fischl_add_entry(get_dir->subdirectory, j + 1, currentSubSubdir->name, &inode_dir); +// currentSubSubdir = currentSubSubdir->next; +// } +// } + +// temp = temp->next; // Move to next subdir +// } +// } + +int main(int argc, char **argv) { + srand(time(NULL)); // Seed the random number generator + d = (argc < 2) ? "/dev/vdc" : argv[1]; + + setupTestDirectory(&mock_root); + + ::testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); + + total_dir_num = 0; + total_file_num = 0; + traverseDirHierarchy(mock_root);//mock_root + printf("Traverse Total: Dir %d, File %d\n",total_dir_num, total_file_num); + + // Cleanup + freeDirHierarchy(mock_root);//mock_root + printf("Free Total: Dir %d, File %d\n",total_free_dir, total_free_file); + freeTree(root); + return result; +} \ No newline at end of file diff --git a/test/layer0.cpp b/test/layer0.cpp index 265ad60..1e99e3b 100644 --- a/test/layer0.cpp +++ b/test/layer0.cpp @@ -1,32 +1,32 @@ -#include "rawdisk.hpp" -#include -#include -#include - -int main(int argc, char *argv[]) { - const char *d = (argc < 2) ? "/dev/vdc" : argv[1]; - - RawDisk *H = new RealRawDisk(d); - - char *buf = "iloveosdfjlseirfnerig"; - char readBuffer[IO_BLOCK_SIZE] = {0}; // Initialize to zeros - - // printf("dir %s, numSectors %lld, diskSize %lld \n", H->dir, H->numSectors, - // H->diskSize); - - // use number to substitute H->getnumSector(), getnumSectors() are not yest - // implemented - for (u_int64_t i = 0; i < 10; i++) { - H->write_block(i, buf); // Change write_API - } - // use number to substitute H->getnumSector(), getnumSectors() are not yest - // implemented - for (u_int64_t i = 0; i < 10; i++) { - H->read_block(i, readBuffer); // Change read_API - assert(strncmp(readBuffer, buf, strlen(buf)) == 0); - } - - delete H; // Delete the RawDisk object - - return 0; -} +#include "rawdisk.hpp" +#include +#include +#include + +int main(int argc, char *argv[]) { + const char *d = (argc < 2) ? "/dev/vdc" : argv[1]; + + RawDisk *H = new RealRawDisk(d); + + char *buf = "iloveosdfjlseirfnerig"; + char readBuffer[IO_BLOCK_SIZE] = {0}; // Initialize to zeros + + // printf("dir %s, numSectors %lld, diskSize %lld \n", H->dir, H->numSectors, + // H->diskSize); + + // use number to substitute H->getnumSector(), getnumSectors() are not yest + // implemented + for (u_int64_t i = 0; i < 10; i++) { + H->write_block(i, buf); // Change write_API + } + // use number to substitute H->getnumSector(), getnumSectors() are not yest + // implemented + for (u_int64_t i = 0; i < 10; i++) { + H->read_block(i, readBuffer); // Change read_API + assert(strncmp(readBuffer, buf, strlen(buf)) == 0); + } + + delete H; // Delete the RawDisk object + + return 0; +} diff --git a/test/layer1_API.cpp b/test/layer1_API.cpp index 3a5a6f0..29996ff 100644 --- a/test/layer1_API.cpp +++ b/test/layer1_API.cpp @@ -7,7 +7,7 @@ int main(int argc, char *argv[]) { // const char* d = (argc < 2) ? "/dev/vdc" : argv[1]; - RawDisk *H = new FakeRawDisk(2048); + RawDisk *H = new FakeRawDisk(21504); Fs *fs = new Fs(H); printf("test inode\n"); @@ -65,12 +65,16 @@ int main(int argc, char *argv[]) { 1); // the first 8 bytes of 4k I/O block will store // the next address(after 2048*4k I/O block) // test the end of the datablock - // H->read_block(NUM_BLOCKS - DATABLOCKS_PER_BITMAP_BLOCK - 1, buffer); - // t = 0; - // for (int j = 0; j < 8; j++) - // t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j); - // assert(t == NUM_BLOCKS - DATABLOCKS_PER_BITMAP_BLOCK - 1); + H->read_block(fs->disk->diskSize / IO_BLOCK_SIZE - + DATABLOCKS_PER_BITMAP_BLOCK - 1, + buffer); + t = 0; + for (int j = 0; j < 8; j++) + t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j); + + assert(t == + fs->disk->diskSize / IO_BLOCK_SIZE - DATABLOCKS_PER_BITMAP_BLOCK - 1); /***************************test inode * de/allocation**********************************/ @@ -142,4 +146,4 @@ int main(int argc, char *argv[]) { delete H; // Delete the RawDisk object return 0; -} +} \ No newline at end of file diff --git a/test/layer2_API.cpp b/test/layer2_API.cpp new file mode 100644 index 0000000..0630e16 --- /dev/null +++ b/test/layer2_API.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include "files.h" + +int main(int argc, char *argv[]) { + const char* d = (argc < 2) ? "/dev/vdc" : argv[1]; + + RawDisk *H = new RawDisk(d); + + printf("test files\n"); + FilesOperation fsop(*H); + fsop.initialize_rootinode(); + + // create multiple files using mkdir or mknod + printf("=== Part 1: create files by path ===\n"); + u_int64_t file1 = fsop.fischl_mknod("/test",0); // mode here is not used yet + printf("/test is inode %llu, it is a file\n", file1); + u_int64_t file2 = fsop.fischl_mkdir("/foo",0); + printf("/foo is inode %llu, it is a directory\n", file2); + fsop.printDirectory(1); + u_int64_t file3 = fsop.fischl_mkdir("/foo/bar",0); + printf("/foo/bar is inode %llu, it is a directory\n", file3); + fsop.printDirectory(file2); + u_int64_t file4 = fsop.fischl_mknod("/foo/bar/baz",0); + printf("/foo/bar/baz is inode %llu, it is a file\n", file4); + // the following three testcases will fail + u_int64_t f1 = fsop.fischl_mkdir("foo/bar",0); + u_int64_t f2 = fsop.fischl_mkdir("/doesnt_exist/bar",0); + u_int64_t f3 = fsop.fischl_mkdir("/test/bar",0); + u_int64_t f4 = fsop.fischl_mkdir("/test",0); + u_int64_t f5 = fsop.fischl_mkdir("/foo/bar",0); + u_int64_t f6 = fsop.fischl_mkdir("/foo/bar/..",0); + + // write to files (TODO: fischl_write) + // read and write to indirect datablocks are not supported yet + printf("=== Part 2: write to files ===\n"); + char buffer[IO_BLOCK_SIZE] = {0}; + INode inode; + inode.inode_construct(file1, *H); + buffer[0] = '1'; + fsop.write_datablock(inode, 0, buffer); + inode.inode_save(*H); + inode.inode_construct(file4, *H); + buffer[0] = '4'; + fsop.write_datablock(inode, 3, buffer); + buffer[0] = '5'; + fsop.write_datablock(inode, 101, buffer); + inode.inode_save(*H); + // TODO: guard against overwriting directory datablocks + + // retrieve inode-number by path + printf("=== Part 3: retrieve inode number using path (namei) ===\n"); + u_int64_t file_test = fsop.namei("/test"); + printf("inode number for \"/test\" is %llu\n", file_test); + assert(file_test == file1); + u_int64_t file_baz = fsop.namei("/foo/bar/baz"); + printf("inode number for \"/foo/bar/baz\" is %llu\n", file_baz); + assert(file_baz == file4); + u_int64_t file_foo = fsop.namei("/foo/bar/.."); + printf("inode number for \"/foo/bar/..\" is %llu\n", file_foo); + assert(file_foo == file2); + u_int64_t file_bar = fsop.namei("/foo/bar/."); + printf("inode number for \"/foo/bar/.\" is %llu\n", file_bar); + assert(file_bar == file3); + + // read files (TODO: fischl_read) + printf("=== Part 4: read from files ===\n"); + char read_buffer[IO_BLOCK_SIZE] = {0}; + INode inode_read; + inode_read.inode_construct(file_test, *H); + fsop.read_datablock(inode_read, 0, read_buffer); + assert(read_buffer[0] == '1'); + inode_read.inode_construct(file_baz, *H); + fsop.read_datablock(inode_read, 3, read_buffer); + assert(read_buffer[0] == '4'); + fsop.read_datablock(inode_read, 101, read_buffer); + assert(read_buffer[0] == '5'); + + // pressure test create directory + printf("=== Part 5: pressure test create files ===\n"); + u_int64_t file_pressure = fsop.fischl_mkdir("/pressure", 0); + u_int64_t inode_numbers[700]; + std::string prefix = "/pressure/No_"; + for(int i=0;i<700;i++){ + inode_numbers[i] = fsop.fischl_mkdir((prefix+std::to_string(i)).c_str(), 0); + } + for(int i=0;i<700;i++){ + u_int64_t inode_number = fsop.namei((prefix+std::to_string(i)).c_str()); + assert(inode_number == inode_numbers[i]); + } + + printf("=== Part 6: unlink test ===\n"); + fsop.printDirectory(file_pressure); + for(int i=0;i<700;i+=2){ + assert(!fsop.fischl_unlink((prefix+std::to_string(i)).c_str())); + } + for(int i=0;i<4;i+=2){ + assert(fsop.namei((prefix+std::to_string(i)).c_str())==(u_int64_t)(-1)); + } + for(int i=1;i<700;i+=2){ + u_int64_t inode_number = fsop.namei((prefix+std::to_string(i)).c_str()); + assert(inode_number == inode_numbers[i]); + } + fsop.printDirectory(file_pressure); + std::string newprefix = "/pressure/New"; + for(int i=0;i<700;i+=2){ + inode_numbers[i] = fsop.fischl_mkdir((newprefix+std::to_string(i)).c_str(), 0); + } + for(int i=0;i<700;i+=2){ + u_int64_t inode_number = fsop.namei((newprefix+std::to_string(i)).c_str()); + assert(inode_number == inode_numbers[i]); + } + fsop.printDirectory(file_pressure); + + // long filename test + std::string longfilename = std::string(255,'A'); + u_int64_t filelong = fsop.fischl_mknod((std::string("/")+longfilename).c_str(),0); + printf("/AAA...AAA is inode %llu, it is a file\n", filelong); +} \ No newline at end of file diff --git a/test/layer2_API_dir.cpp b/test/layer2_API_dir.cpp new file mode 100644 index 0000000..6e83f33 --- /dev/null +++ b/test/layer2_API_dir.cpp @@ -0,0 +1,373 @@ +#define FUSE_USE_VERSION 31 + +#include +#include +#include +#include +#include +#include +#include +#include "fs.hpp" +#include "files.h" + + +typedef struct file_test{ + const char* name; + file_test* next;//use linked-list to know the file at the same level directory +}file_test; +typedef struct dir_test{ + const char* name; + file_test* inFile; + dir_test* subdir; + dir_test* next;//use linked-list to know the other dir at the same parent dir. +}dir_test; + +void setupTestDirectory(dir_test** root); +void freeDirHierarchy(dir_test* dir); +void traverseDirHierarchy(const dir_test* dir, int depth); + +//global can be taken +std::string target_filepath; +dir_test* mock_root = nullptr; +RawDisk *H; // Use FakeRawDisk here if memory sanitizer complains +Fs *fs; +FilesOperation *fsop; +std::string prefix = "/pressure/No_"; + +int total_dir_num = 0; +int total_file_num = 0; +int total_free_dir = 0; +int total_free_file = 0; + +TEST(FileOperationTest, MkdirnodTest) { + + fsop->initialize_rootinode(); + struct fuse_file_info fi; + + mode_t mode;//set mode + mode = S_IRWXU | S_IRWXG | S_IRWXO;//future should test permission + //S_IRWXU(S_IRUSR | S_IWUSR | S_IXUSR) (owner), S_IRWXG(S_IRGRP | S_IWGRP | S_IXGRP) (group), S_IRWXO(S_IROTH | S_IWOTH | S_IXOTH) + EXPECT_EQ(fsop->fischl_create("/test", mode, &fi), 0); + EXPECT_EQ(fsop->fischl_mkdir("/foo", mode), 0); + EXPECT_EQ(fsop->fischl_mkdir("/foo/bar", mode),0); + fsop->printDirectory(1); + EXPECT_EQ(fsop->fischl_create("/foo/bar/baz", mode, &fi), 0); + fsop->printDirectory(4); + // the following three testcases will fail + printf("Failing cases\n"); + EXPECT_TRUE(fsop->fischl_mkdir("foo/bar", mode) < 0); + EXPECT_TRUE(fsop->fischl_mkdir("/doesnt_exist/bar", mode) < 0); + EXPECT_TRUE(fsop->fischl_mkdir("/test/bar", mode) < 0); + EXPECT_TRUE(fsop->fischl_mkdir("/test", mode) < 0); + EXPECT_TRUE(fsop->fischl_mkdir("/foo/bar", mode) < 0); + EXPECT_TRUE(fsop->fischl_mkdir("/foo/bar/..", mode) < 0); +} + +TEST(FileOperationTest, WriteTest) { + // write to files (TODO: fischl_write) + //get inode info from disk + char buffer[IO_BLOCK_SIZE] = {0}; + INode_Data inode; + u_int64_t get_disk_inum; + struct fuse_file_info fi; + + //file test + printf("point 0\n"); + get_disk_inum = fsop->disk_namei("/test"); + printf("point 1\n"); + fsop->fischl_open("/test", &fi); + EXPECT_EQ(fi.fh, get_disk_inum); + buffer[0] = '1'; + fsop->fischl_write("/test", buffer, sizeof(buffer), 0, &fi); + printf("point 2\n"); + //other file baz + get_disk_inum = fsop->disk_namei("/foo/bar/baz"); + buffer[0] = '4'; + fsop->fischl_open("/foo/bar/baz", &fi); + EXPECT_EQ(fi.fh, get_disk_inum); + fsop->fischl_write("/foo/bar/baz", buffer, sizeof(buffer), 3*IO_BLOCK_SIZE, &fi); + buffer[0] = '5'; + fsop->fischl_write("/foo/bar/baz", buffer, sizeof(buffer), 101*IO_BLOCK_SIZE, &fi); +} + +TEST(FileOperationTest, RamDiskTest) { + //use find_entry(specify certain files or directory) + FileNode* get_dir; + u_int64_t get_disk_inum; + + get_dir = fischl_find_entry(fsop->root_node, "/test");//this is file + EXPECT_TRUE(get_dir != NULL);//detect this should find success + EXPECT_STREQ(get_dir->name, "test"); + get_disk_inum = fsop->disk_namei("/test"); + EXPECT_EQ(get_disk_inum, get_dir->inode_number); + + get_dir = fischl_find_entry(fsop->root_node, "/foo/bar/baz");//this is file + EXPECT_TRUE(get_dir != NULL);//detect this should find success + EXPECT_STREQ(get_dir->name, "baz"); + get_disk_inum = fsop->disk_namei("/foo/bar/baz"); + EXPECT_EQ(get_disk_inum, get_dir->inode_number); + + get_dir = fischl_find_entry(fsop->root_node, "/foo/bar/.."); + EXPECT_TRUE(get_dir != NULL);//detect this should find success + EXPECT_STREQ(get_dir->name, "foo"); + ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory + get_disk_inum = fsop->disk_namei("/foo/bar/.."); + EXPECT_EQ(get_disk_inum, get_dir->inode_number); + fsop->printDirectory(get_disk_inum); + + get_dir = fischl_find_entry(fsop->root_node, "/foo/bar/."); + EXPECT_TRUE(get_dir != NULL);//detect this should find success + EXPECT_STREQ(get_dir->name, "bar"); + ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory + get_disk_inum = fsop->disk_namei("/foo/bar/."); + EXPECT_EQ(get_disk_inum, get_dir->inode_number); + fsop->printDirectory(get_disk_inum); +} + + +TEST(FileOperationTest, ReadTest) { + // read files (TODO: fischl_read) + char read_buffer[IO_BLOCK_SIZE] = {0}; + INode_Data inode; + u_int64_t get_file_inum; + + //read test file + get_file_inum = fsop->namei("/test"); + inode.inode_num = get_file_inum; + fs->inode_manager->load_inode(&inode); + //fsop->read_datablock(inode, 0, read_buffer); + //EXPECT_EQ(read_buffer[0], '1'); + + //read test file again with fischl_read API + struct fuse_file_info fi; + fsop->fischl_open("/test", &fi); + EXPECT_EQ(fi.fh, get_file_inum); + fsop->fischl_read("/test", read_buffer, sizeof(read_buffer), 0, &fi); + EXPECT_EQ(read_buffer[0], '1'); + + //read baz file + get_file_inum= fsop->namei("/foo/bar/baz"); + // inode.inode_construct(get_file_inum, *H); + // fsop->read_datablock(inode, 3, read_buffer); + // EXPECT_EQ(read_buffer[0], '4'); + // fsop->read_datablock(inode, 101, read_buffer); + // EXPECT_EQ(read_buffer[0], '5'); + + //read baz file again with fischl_read API + fsop->fischl_open("/foo/bar/baz", &fi); + EXPECT_EQ(fi.fh, get_file_inum); + fsop->fischl_read("/foo/bar/baz", read_buffer, sizeof(read_buffer), 3*IO_BLOCK_SIZE, &fi); + EXPECT_EQ(read_buffer[0], '4'); + fsop->fischl_read("/foo/bar/baz", read_buffer, sizeof(read_buffer), 101*IO_BLOCK_SIZE, &fi); + EXPECT_EQ(read_buffer[0], '5'); +} + +TEST(FileOperationTest, PressureTest) { + mode_t mode;//set mode + mode = S_IRWXU | S_IRWXG | S_IRWXO;//future should test permission + EXPECT_EQ(fsop->fischl_mkdir("/pressure", mode), 0); + fsop->printDirectory(1); + + for(int i=0;i<100;i++){ + EXPECT_EQ(fsop->fischl_mkdir((prefix+std::to_string(i)).c_str(), mode), 0); + } + fsop->printDirectory(6); + for(int i=0;i<100;i++){ + EXPECT_EQ(fsop->namei((prefix+std::to_string(i)).c_str()),fsop->disk_namei((prefix+std::to_string(i)).c_str())); + } +} + +TEST(FileOperationTest, UnlinkTest) { + ssize_t file_pressure = fsop->namei("/pressure"); + fsop->printDirectory(file_pressure); + for(int i=0;i<100;i+=2){ + EXPECT_EQ(fsop->fischl_unlink((prefix+std::to_string(i)).c_str()), 0); + } + for(int i=0;i<4;i+=2){ + EXPECT_EQ(fsop->namei((prefix+std::to_string(i)).c_str()), (u_int64_t)(-1)); + } + printf("Finished Deallocating\n"); + fsop->printDirectory(file_pressure); + std::string newprefix = "/pressure/New"; + for(int i=0;i<100;i+=2){ + EXPECT_EQ(fsop->fischl_mkdir((newprefix+std::to_string(i)).c_str(), 0), 0); + } + for(int i=0;i<100;i+=2){ + u_int64_t inode_number = fsop->namei((newprefix+std::to_string(i)).c_str()); + assert(inode_number>0); + } + printf("Finished Reallocating\n"); + fsop->printDirectory(file_pressure); + + // long filename test + std::string longfilename = std::string(255,'A'); + EXPECT_EQ(fsop->fischl_mknod((std::string("/")+longfilename).c_str(),0,0),0); + u_int64_t filelong = fsop->namei((std::string("/")+longfilename).c_str()); + printf("/AAA...AAA is inode %llu, it is a file\n", filelong); +} + + +int main(int argc, char **argv) { + srand(time(NULL)); // Seed the random number generator + const char* d = (argc < 2) ? "/dev/vdc" : argv[1]; + + setupTestDirectory(&mock_root); + H = new FakeRawDisk(21504); + fs = new Fs(H); + fs->format(); + fsop = new FilesOperation(*H, fs); + + ::testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); + + total_dir_num = 0; + total_file_num = 0; + + traverseDirHierarchy(mock_root, 0);//mock_root + printf("Traverse Total: Dir %d, File %d\n",total_dir_num, total_file_num); + + // Cleanup + freeDirHierarchy(mock_root);//mock_root + printf("Free Total: Dir %d, File %d\n",total_free_dir, total_free_file); + + freeTree(fsop->root_node); + delete fsop; // First delete fsop which depends on H + delete H; + + return result; +} + +const char* generateRandomName(size_t length) { + const std::string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + std::string randomString; + + for (size_t i = 0; i < length; ++i) { + randomString += chars[rand() % chars.size()]; + } + + // Allocate memory and copy the string + char* name = new char[randomString.length() + 1]; + strcpy(name, randomString.c_str()); + return name; +} + +// Recursive function to create directory hierarchy +dir_test* createDirHierarchy(int level, int maxLevel) { + if (level > maxLevel) { + return nullptr; + } + + dir_test* head = nullptr; + dir_test* current = nullptr; + + for (int i = 0; i < 3; ++i) { + dir_test* newDir = new dir_test; + newDir->name = generateRandomName(6); // Generate a random name for the directory + newDir->inFile = nullptr; // Initialize file list to nullptr + newDir->subdir = createDirHierarchy(level + 1, maxLevel); // Recursively create subdirectories + newDir->next = nullptr; + + // Create file list for this directory + file_test* fileHead = nullptr; + file_test* fileCurrent = nullptr; + for (int j = 0; j < 3; ++j) { + file_test* newFile = new file_test; + newFile->name = generateRandomName(6); // Generate a random name for the file + newFile->next = nullptr; + + if (!fileHead) { + fileHead = newFile; + } else { + fileCurrent->next = newFile; + } + fileCurrent = newFile; + } + newDir->inFile = fileHead; + + // Add the new directory to the list + if (!head) { + head = newDir; + } else { + current->next = newDir; + } + current = newDir; + } + + return head; +} + +// Setup function for the test directory +void setupTestDirectory(dir_test** root) { + // Allocate memory for root + *root = new dir_test; + char* root_name = new char[2]; + strcpy(root_name, "/"); + (*root)->name = root_name; // use / as begin + (*root)->inFile = nullptr; // Initialize file list to nullptr + (*root)->subdir = createDirHierarchy(0, 1); + (*root)->next = nullptr; + file_test* fileHead = nullptr; + file_test* fileCurrent = nullptr; + for (int j = 0; j < 3; ++j) { + file_test* newFile = new file_test; + newFile->name = generateRandomName(6); // Generate a random name for the file + newFile->next = nullptr; + + if (!fileHead) { + fileHead = newFile; + } else { + fileCurrent->next = newFile; + } + fileCurrent = newFile; + } + (*root)->inFile = fileHead; +} + +// Function to free a list of files +void freeFileList(file_test* fileList) { + while (fileList != nullptr) { + file_test* temp = fileList; + fileList = fileList->next; + total_free_file++;//for debug + delete[] temp->name; // Free the name string + delete temp; // Free the current file + } +} + +// Recursive function to free the directory hierarchy +void freeDirHierarchy(dir_test* dir) { + while (dir != nullptr) { + dir_test* temp = dir; + dir = dir->next; + total_free_dir++;//for debug + freeFileList(temp->inFile); // Free the list of files in the directory + freeDirHierarchy(temp->subdir); // Recursively free subdirectories + delete[] temp->name; // Free the name string + delete temp; // Free the current directory + } +} + +// Function to print the list of files in a directory +void printFileList(const file_test* fileList) { + const file_test* currentFile = fileList; + while (currentFile != nullptr) { + // std::cout << " File: " << currentFile->name << std::endl; + total_file_num++; + currentFile = currentFile->next; + } +} + +void traverseDirHierarchy(const dir_test* dir, int depth = 0) { + while (dir != nullptr) { + // std::cout << "Depth " << depth << ", Directory: " << dir->name << std::endl; + total_dir_num++;//for debug + + // Print files in this directory + printFileList(dir->inFile); + // Recursively traverse subdirectories + traverseDirHierarchy(dir->subdir, depth + 1); + + // Go to the next directory at the same level + dir = dir->next; + } +} \ No newline at end of file