From 46ce866c579b23cfc748bc27d9e2dd7f4fa06634 Mon Sep 17 00:00:00 2001 From: WangZiao Date: Thu, 16 Nov 2023 18:36:35 -0800 Subject: [PATCH] support creating files and directories, support namei --- CMakeLists.txt | 1 + include/files.h | 24 ++++-- include/fs.h | 4 +- lib/files.cpp | 202 +++++++++++++++++++++++++++++++++++++++++++- test/CMakeLists.txt | 8 +- test/layer2_API.cpp | 66 +++++++++++++++ 6 files changed, 296 insertions(+), 9 deletions(-) create mode 100644 test/layer2_API.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f0c5624..bb170e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(fischl # Header files lib/fischl.cpp lib/main.cpp + lib/files.cpp ) diff --git a/include/files.h b/include/files.h index fe3adee..678cea6 100644 --- a/include/files.h +++ b/include/files.h @@ -1,7 +1,21 @@ #include +#include -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 *); \ No newline at end of file +class FilesOperation { + RawDisk& disk; + INodeOperation inop; + u_int64_t root_inode; + public: + FilesOperation(RawDisk&); + int read_datablock(INode& inode, u_int64_t index, char* buffer); + int write_datablock(INode& inode, u_int64_t index, char* buffer); + void init_inode(u_int64_t inode_number, u_int64_t permissions); + void initialize_rootinode(); + u_int64_t mkfile(u_int64_t parent_inode_number, const char* name, u_int64_t permissions); + 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_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 *); +}; \ No newline at end of file diff --git a/include/fs.h b/include/fs.h index 63da3f6..c9f6563 100644 --- a/include/fs.h +++ b/include/fs.h @@ -57,16 +57,16 @@ public: }; class INode{ + public: // direct datablocks u_int64_t blocks[48]; // indirect address u_int64_t single_indirect, double_indirect, triple_indirect; // other - u_int64_t uid; u_int64_t gid; u_int64_t permissions; - u_int64_t size; + u_int64_t size; // Number of datablocks u_int64_t block_number; public: diff --git a/lib/files.cpp b/lib/files.cpp index f5b3304..03ca9df 100644 --- a/lib/files.cpp +++ b/lib/files.cpp @@ -1 +1,201 @@ -#include "files.h" \ No newline at end of file +#include "files.h" +#include +#include + +struct DirectoryEntry { + u_int64_t inode_number; + char file_name[56]; + 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); + } +}; + +FilesOperation::FilesOperation(RawDisk& disk_): disk(disk_) { + disk = disk_; + inop.initialize(disk_); +} + +int FilesOperation::read_datablock(INode& inode, u_int64_t index, char* buffer) { + if (index >= inode.size) { + printf("Read datablock out of range, inode number %llu", inode.block_number); + return -1; + } + if (index < 48) { + return disk.rawdisk_read(inode.blocks[index], buffer, IO_BLOCK_SIZE); + } else { + perror("Read indirect datablocks not implemented yet"); + return -1; + } +} + +int FilesOperation::write_datablock(INode& inode, u_int64_t index, char* buffer) { + while (index >= inode.size) { + u_int64_t ret = inode.datablock_allocate(disk); + inode.size += 1; + } + if (index < 48) { + return disk.rawdisk_write(inode.blocks[index], buffer, IO_BLOCK_SIZE); + } else { + perror("Write indirect datablocks not implemented yet"); + return -1; + } +} + +void FilesOperation::init_inode(u_int64_t inode_number, u_int64_t permissions) { + // zero out disk space of inode, because in memory inode is uninitialized by default + char buffer[SECTOR_SIZE] = {0}; + disk.rawdisk_write(inode_number*SECTOR_SIZE, buffer, sizeof(buffer)); + INode inode; + inode.inode_construct(inode_number, disk); + inode.block_number = inode_number; + inode.permissions = permissions; + inode.inode_save(disk); +} + +void FilesOperation::initialize_rootinode() { + // this method must be called explicitly right after initializion + root_inode = inop.inode_allocate(disk); + printf("Info: root inode number: %llu\n", root_inode); + init_inode(root_inode, 1); +} + +u_int64_t FilesOperation::mkfile(u_int64_t parent_inode_number, const char* name, u_int64_t permissions) { + // trys to create a file under parent directory + if (strlen(name)>=56) { + perror("Name too long, cannot create file or directory"); + return -1; + } + INode inode; + inode.inode_construct(parent_inode_number, disk); + if (inode.permissions != 1) { + printf("Parent Inode is not a directory\n"); + return -1; + } + char buffer[IO_BLOCK_SIZE] = {0}; + if (inode.size > 0) read_datablock(inode, 0, buffer); + + // do create inode + u_int64_t new_inode_number = 0; + DirectoryEntry ent; + for(int i=0;i<=IO_BLOCK_SIZE-64;i+=64){ + ent.deserialize(buffer+i); + if (ent.inode_number == 0) { + new_inode_number = inop.inode_allocate(disk); + ent.inode_number = new_inode_number; + strcpy(ent.file_name, name); + ent.serialize(buffer+i); + break; + } + } + if (new_inode_number == 0) { + perror("Failed to create file in directory: First datablock full"); + return -1; + } else { + write_datablock(inode, 0, buffer); + } + inode.inode_save(disk); + + // initialize new file + init_inode(new_inode_number, permissions); + + return new_inode_number; +} + +u_int64_t FilesOperation::namei(const char* path) { + // returns the inode number corresponding to path + u_int64_t current_inode = root_inode; + std::string current_dirname; + std::istringstream pathStream(path); + std::string new_name; + std::getline(pathStream, new_name, '/'); + if(!new_name.empty()){ + printf("namei: path should start with /\n"); + return -1; + } + while (std::getline(pathStream, new_name, '/')) { + INode inode; + inode.inode_construct(current_inode, disk); + if (inode.permissions != 1 || inode.size == 0) { + printf("namei: %s is not a non-empty directory\n", current_dirname.c_str()); + return -1; + } + char buffer[IO_BLOCK_SIZE] = {0}; + read_datablock(inode, 0, buffer); + u_int64_t new_inode_number = 0; + DirectoryEntry ent; + for(int i=0;i<=IO_BLOCK_SIZE-64;i+=64){ + 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) { + printf("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 + // 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::fischl_mkdir(const char* path, mode_t mode) { + char *pathdup = strdup(path); + char *ptr = strrchr(pathdup, '/'); + char *newfilename = ptr+1; + char *prefix = new char[ptr-pathdup+1]; + for(int i=0;i +#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 + // directories that contain more than 64 files use more than one datablocks, it is not supported yet + 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); + u_int64_t file3 = fsop.fischl_mkdir("/foo/bar",0); + printf("/foo/bar is inode %llu, it is a directory\n", file3); + 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); + // TODO: guard against creating an existing file or diretory, such as fsop.fischl_mkdir("/test",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); + 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); + + // 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'); +} \ No newline at end of file