support creating files and directories, support namei

This commit is contained in:
WangZiao 2023-11-16 18:36:35 -08:00
parent e09e952558
commit 46ce866c57
6 changed files with 296 additions and 9 deletions

View File

@ -13,6 +13,7 @@ add_executable(fischl
# Header files
lib/fischl.cpp
lib/main.cpp
lib/files.cpp
)

View File

@ -1,7 +1,21 @@
#include <sys/types.h>
#include <fs.h>
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 *);
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 *);
};

View File

@ -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:

View File

@ -1 +1,201 @@
#include "files.h"
#include "files.h"
#include <string.h>
#include <sstream>
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<ptr-pathdup;i++){
prefix[i] = pathdup[i];
}
prefix[ptr-pathdup] = '\0';
u_int64_t parent_inode_number = namei(prefix);
if (parent_inode_number == (u_int64_t)-1) {
printf("fischl_mkdir failed because namei failed to find inode for %s\n", prefix);
delete pathdup;
delete [] prefix;
return -1;
}
u_int64_t ret = mkfile(parent_inode_number, newfilename, 1);
delete pathdup;
delete [] prefix;
return ret;
}
u_int64_t FilesOperation::fischl_mknod(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<ptr-pathdup;i++){
prefix[i] = pathdup[i];
}
prefix[ptr-pathdup] = '\0';
u_int64_t parent_inode_number = namei(prefix);
if (parent_inode_number == (u_int64_t)-1) {
printf("fischl_mknod failed because namei failed to find inode for %s", prefix);
delete pathdup;
delete [] prefix;
return -1;
}
u_int64_t ret = mkfile(parent_inode_number, newfilename, 0);
delete pathdup;
delete [] prefix;
return ret;
}

View File

@ -1,5 +1,6 @@
set(TARGET_LAYER0 test_layer0)
set(TARGET_LAYER1_API test_layer1_API)
set(TARGET_LAYER2_API test_layer2_API)
set(DIR_PLACE /dev/vdb)
# add test sources here ...
@ -11,7 +12,12 @@ add_executable(${TARGET_LAYER1_API}
# add need lib and source code here
layer1_API.cpp
)
add_executable(${TARGET_LAYER2_API}
../lib/files.cpp
layer2_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_API} COMMAND sudo ./${TARGET_LAYER1_API} ${DIR_PLACE})
add_test(NAME ${TARGET_LAYER2_API} COMMAND sudo ./${TARGET_LAYER2_API} ${DIR_PLACE})

66
test/layer2_API.cpp Normal file
View File

@ -0,0 +1,66 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#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');
}