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
|
# Header files
|
||||||
lib/fischl.cpp
|
lib/fischl.cpp
|
||||||
lib/main.cpp
|
lib/main.cpp
|
||||||
|
lib/files.cpp
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,7 +1,21 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <fs.h>
|
||||||
|
|
||||||
int fischl_mkdir(const char*, mode_t);
|
class FilesOperation {
|
||||||
int fischl_mknod(const char*, mode_t);
|
RawDisk& disk;
|
||||||
int fischl_readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags);
|
INodeOperation inop;
|
||||||
int fischl_unlink (const char *);
|
u_int64_t root_inode;
|
||||||
int fischl_open (const char *, struct fuse_file_info *);
|
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{
|
class INode{
|
||||||
|
public:
|
||||||
// direct datablocks
|
// direct datablocks
|
||||||
u_int64_t blocks[48];
|
u_int64_t blocks[48];
|
||||||
// indirect address
|
// indirect address
|
||||||
u_int64_t single_indirect, double_indirect, triple_indirect;
|
u_int64_t single_indirect, double_indirect, triple_indirect;
|
||||||
// other
|
// other
|
||||||
|
|
||||||
u_int64_t uid;
|
u_int64_t uid;
|
||||||
u_int64_t gid;
|
u_int64_t gid;
|
||||||
u_int64_t permissions;
|
u_int64_t permissions;
|
||||||
u_int64_t size;
|
u_int64_t size; // Number of datablocks
|
||||||
u_int64_t block_number;
|
u_int64_t block_number;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
200
lib/files.cpp
200
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_LAYER0 test_layer0)
|
||||||
set(TARGET_LAYER1_API test_layer1_API)
|
set(TARGET_LAYER1_API test_layer1_API)
|
||||||
|
set(TARGET_LAYER2_API test_layer2_API)
|
||||||
set(DIR_PLACE /dev/vdb)
|
set(DIR_PLACE /dev/vdb)
|
||||||
|
|
||||||
# add test sources here ...
|
# add test sources here ...
|
||||||
@ -11,7 +12,12 @@ add_executable(${TARGET_LAYER1_API}
|
|||||||
# add need lib and source code here
|
# add need lib and source code here
|
||||||
layer1_API.cpp
|
layer1_API.cpp
|
||||||
)
|
)
|
||||||
|
add_executable(${TARGET_LAYER2_API}
|
||||||
|
../lib/files.cpp
|
||||||
|
layer2_API.cpp
|
||||||
|
)
|
||||||
|
|
||||||
# add test to activate ctest -VV
|
# add test to activate ctest -VV
|
||||||
add_test(NAME ${TARGET_LAYER0} COMMAND sudo ./${TARGET_LAYER0} ${DIR_PLACE})
|
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