diff --git a/include/files.h b/include/files.h index a2ab95c..d97bd3c 100644 --- a/include/files.h +++ b/include/files.h @@ -18,8 +18,8 @@ class FilesOperation { void unlink_inode(u_int64_t inode_number); u_int64_t disk_namei(const char* path); u_int64_t namei(const char* path); - u_int64_t fischl_mkdir(const char*, mode_t); - u_int64_t fischl_mknod(const char*, mode_t); + int fischl_mkdir(const char*, mode_t); + int fischl_mknod(const char*, mode_t); //int fischl_readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags); int fischl_unlink (const char *); //int fischl_open (const char *, struct fuse_file_info *); diff --git a/lib/files.cpp b/lib/files.cpp index 2b78405..93ca218 100644 --- a/lib/files.cpp +++ b/lib/files.cpp @@ -111,7 +111,7 @@ void FilesOperation::create_dot_dotdot(INode* inode, u_int64_t parent_inode_numb void FilesOperation::initialize_rootinode() { // this method must be called explicitly right after initializion u_int64_t root_inode_number = inop.inode_allocate(disk); - printf("Info: root inode number: %llu\n", root_inode_number); + // printf("Info: root inode number: %llu\n", root_inode_number); INode *root_inode = new_inode(root_inode_number, S_IFDIR); create_dot_dotdot(root_inode, root_inode_number); root_node = fischl_init_entry(root_inode_number, "/", root_inode); @@ -142,7 +142,7 @@ INode* FilesOperation::create_new_inode(u_int64_t parent_inode_number, const cha INode inode; inode.inode_construct(parent_inode_number, disk); if ((inode.permissions & S_IFMT) != S_IFDIR) { - printf("Parent Inode is not a directory\n"); + fprintf(stderr,"[%s ,%d] please create under directory\n",__func__,__LINE__); return NULL; } @@ -154,7 +154,11 @@ INode* FilesOperation::create_new_inode(u_int64_t parent_inode_number, const cha for(int i=0;i<=IO_BLOCK_SIZE-264;i+=264){ ent.deserialize(r_buffer+i); if (strcmp(ent.file_name, name)==0) { - printf("Already exists file or directory with name %s, cannot not create\n", name); + 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; } } @@ -255,7 +259,7 @@ u_int64_t FilesOperation::namei(const char* path) { else return -1; } -u_int64_t FilesOperation::fischl_mkdir(const char* path, mode_t mode) { +int FilesOperation::fischl_mkdir(const char* path, mode_t mode) { //check path char *pathdup = strdup(path); char *lastSlash = strrchr(pathdup, '/'); @@ -265,44 +269,41 @@ u_int64_t FilesOperation::fischl_mkdir(const char* path, mode_t mode) { FileNode *parent_filenode = strlen(ParentPath)? fischl_find_entry(root_node, ParentPath): root_node->self_info; if (parent_filenode == NULL) { - printf("parent %s not found by fischl_find_entry\n", ParentPath); + fprintf(stderr,"[%s ,%d] ParentPath:{%s} not found\n",__func__,__LINE__, ParentPath); delete pathdup; - return -1; + return -ENOENT;//parentpath directory does not exist } u_int64_t parent_inode_number = parent_filenode->inode_number; //make new inode INode* ret = create_new_inode(parent_inode_number, newDirname, mode|S_IFDIR);//specify S_IFDIR as directory - if (ret == NULL) return -1; + if (ret == NULL) return -1;//ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST fischl_add_entry(parent_filenode->subdirectory, ret->block_number, newDirname, ret); delete pathdup; - return ret->block_number; - //after new_inode(mkfile), go to fischl_add_entry record - - + return 0;//SUCCESS } -u_int64_t FilesOperation::fischl_mknod(const char* path, mode_t mode) { +int FilesOperation::fischl_mknod(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 *newFilename = lastSlash+1; //\0, get from char *ParentPath = pathdup;//pathdup are separated by pathdup, so it take only - printf("mknod ParentPath:%s, strlen=%d\n", ParentPath, strlen(ParentPath)); + // 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) { - printf("parent %s not found by fischl_find_entry\n", ParentPath); + fprintf(stderr,"[%s ,%d] ParentPath:{%s} not found\n",__func__,__LINE__, ParentPath); delete pathdup; return -1; } u_int64_t parent_inode_number = parent_filenode->inode_number; //make new inode INode* ret = create_new_inode(parent_inode_number, newFilename, mode); - if (ret == NULL) return -1; + if (ret == NULL) return -1;//ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST //make new node fischl_add_entry(parent_filenode->subdirectory, ret->block_number, newFilename, ret); delete pathdup; - return ret->block_number; + return 0;//SUCESS } void FilesOperation::unlink_inode(u_int64_t inode_number) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 23db167..d32305e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,7 +16,7 @@ add_executable(${TARGET_LAYER1_API} add_executable(${TARGET_LAYER2_API} ../lib/direntry.cpp ../lib/files.cpp - layer2_API.cpp + layer2_API_dir.cpp ) add_executable(${TARGET_DIR_API} ../lib/direntry.cpp @@ -26,6 +26,7 @@ add_executable(${TARGET_DIR_API} # 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_LAYER2_API} gtest gtest_main) target_link_libraries(${TARGET_DIR_API} gtest gtest_main) # add test to activate ctest -VV diff --git a/test/layer2_API_dir.cpp b/test/layer2_API_dir.cpp new file mode 100644 index 0000000..570a4e9 --- /dev/null +++ b/test/layer2_API_dir.cpp @@ -0,0 +1,320 @@ +#include +#include +#include +#include +#include +#include +#include +#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 +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; + +TEST(FileOperationTest, MkdirnodTest) { + RawDisk *H = new RawDisk(d); + + FilesOperation fsop(*H); + fsop.initialize_rootinode(); + + 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_mknod("/test", mode), 0); // mode here is not used yet + EXPECT_EQ(fsop.fischl_mkdir("/foo", mode), 0); + EXPECT_EQ(fsop.fischl_mkdir("/foo/bar", mode),0); + EXPECT_EQ(fsop.fischl_mknod("/foo/bar/baz", mode), 0); + // the following three testcases will fail + 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) +// // 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 +// } + +// TEST(FileOperationTest, RamTest) { +// // retrieve inode-number by path +// 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); +// } + +// TEST(FileOperationTest, DiskTest) { +// // retrieve inode-number by path +// 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); +// } + +// TEST(FileOperationTest, ReadTest) { +// // read files (TODO: fischl_read) +// 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'); +// } +// TEST(FileOperationTest, PressureTest) { +// 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]); +// } +// } +// TEST(FileOperationTest, UnlinkTest) { +// 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); +// } + + +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, 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(root); + 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; + (*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; + } +} \ No newline at end of file