support creating files and directories, support namei
This commit is contained in:
parent
e09e952558
commit
46ce866c57
@ -13,6 +13,7 @@ add_executable(fischl
|
||||
# Header files
|
||||
lib/fischl.cpp
|
||||
lib/main.cpp
|
||||
lib/files.cpp
|
||||
|
||||
)
|
||||
|
||||
|
@ -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 *);
|
||||
};
|
@ -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:
|
||||
|
202
lib/files.cpp
202
lib/files.cpp
@ -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;
|
||||
}
|
@ -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
66
test/layer2_API.cpp
Normal 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');
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user