merged with layer3dev
This commit is contained in:
commit
97886fa1ca
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "googletest"]
|
||||||
|
path = googletest
|
||||||
|
url = https://github.com/google/googletest.git
|
@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD 14)
|
|||||||
include_directories(
|
include_directories(
|
||||||
# fischl include files
|
# fischl include files
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
# ${CMAKE_CURRENT_SOURCE_DIR}/googletest/googletest/include
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(fischl
|
add_executable(fischl
|
||||||
@ -18,8 +19,22 @@ add_executable(fischl
|
|||||||
lib/fs/fs_file_io.cpp
|
lib/fs/fs_file_io.cpp
|
||||||
lib/fs/fs.cpp
|
lib/fs/fs.cpp
|
||||||
lib/fs/inode_manager.cpp
|
lib/fs/inode_manager.cpp
|
||||||
|
lib/files.cpp
|
||||||
|
lib/direntry.cpp
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
|
# add_subdirectory(googletest)
|
||||||
|
|
||||||
|
# Add the -Wall flag
|
||||||
|
target_compile_options(fischl PRIVATE -Wall)
|
||||||
|
|
||||||
|
# Use pkg-config to get flags for fuse3
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_search_module(FUSE3 REQUIRED fuse3)
|
||||||
|
|
||||||
|
# Add the flags from pkg-config for fuse3
|
||||||
|
target_include_directories(fischl PRIVATE ${FUSE3_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(fischl PRIVATE ${FUSE3_LIBRARIES})
|
36
include/direntry.h
Normal file
36
include/direntry.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
typedef struct fileNode {
|
||||||
|
char *name = NULL;
|
||||||
|
int inode_number;
|
||||||
|
int permissions;
|
||||||
|
char *Symbolink;
|
||||||
|
struct treeNode *subdirectory;
|
||||||
|
struct fileNode *next;
|
||||||
|
} FileNode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int size;
|
||||||
|
FileNode **table;
|
||||||
|
} HashTable;
|
||||||
|
|
||||||
|
typedef struct treeNode {
|
||||||
|
char *dirName;
|
||||||
|
HashTable *contents;
|
||||||
|
struct treeNode *parent;
|
||||||
|
FileNode *self_info; //self fileNode infromation
|
||||||
|
} TreeNode;
|
||||||
|
|
||||||
|
/*for root*/
|
||||||
|
TreeNode *fischl_init_entry(int new_inode_number, const char *fileName, INode_Data *new_inode);
|
||||||
|
/*the to be added file in add_entry should be parent-child relationship with treenode, otherwise will wrong */
|
||||||
|
/*see Add_FindFiletest in dir_API.cpp*/
|
||||||
|
int fischl_add_entry(TreeNode *parent, int new_inode_number, const char *fileName, INode_Data *new_inode);
|
||||||
|
int fischl_rm_entry(TreeNode *parent, const char *fileName);
|
||||||
|
/*if want to use dir mode use the subdirectory treeNode pointer */
|
||||||
|
//e.g. FileNode *Dirnode = fischl_find_entry(); can see file inside with Dirnode->subdirectory
|
||||||
|
//e.g. go to the current Dirnode parent directory, use TreeNode *get_Dir_parent = Dirnode->subdirectory->parent;
|
||||||
|
FileNode *fischl_find_entry(TreeNode *root, const char *path);
|
||||||
|
|
||||||
|
void freeTree(TreeNode *node);
|
||||||
|
/*for debug use*/
|
||||||
|
TreeNode *createDirectory(const char *dirName, TreeNode *parent, int hashSize);
|
||||||
|
TreeNode *find_parentPath(TreeNode *root, const char *path);
|
40
include/files.h
Normal file
40
include/files.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include <sys/types.h>
|
||||||
|
#include <fs.hpp>
|
||||||
|
#include <fuse.h>
|
||||||
|
#include "direntry.h"
|
||||||
|
|
||||||
|
class FilesOperation {
|
||||||
|
RawDisk& disk;
|
||||||
|
Fs *fs;
|
||||||
|
void create_dot_dotdot(INode_Data*, u_int64_t);
|
||||||
|
|
||||||
|
public:
|
||||||
|
TreeNode *root_node;
|
||||||
|
FilesOperation(RawDisk&, Fs*);
|
||||||
|
//int read_datablock(const INode_Data& inode, u_int64_t index, char* buffer);
|
||||||
|
//int write_datablock(INode_Data& inode, u_int64_t index, char* buffer);
|
||||||
|
void initialize_rootinode();
|
||||||
|
void printbuffer(const char*,int);
|
||||||
|
void printDirectory(u_int64_t);
|
||||||
|
INode_Data* create_new_inode(u_int64_t parent_inode_number, const char* name, mode_t mode);
|
||||||
|
int insert_inode_to(u_int64_t parent_inode_number, const char* name, INode_Data *new_inode);
|
||||||
|
void unlink_inode(u_int64_t inode_number);
|
||||||
|
u_int64_t disk_namei(const char* path);
|
||||||
|
u_int64_t namei(const char* path);
|
||||||
|
int fischl_mkdir(const char*, mode_t);
|
||||||
|
int fischl_mknod(const char*, mode_t, dev_t);//for special file
|
||||||
|
int fischl_create(const char *, mode_t, struct fuse_file_info *);//for regular file
|
||||||
|
int fischl_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi);
|
||||||
|
int fischl_readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, enum fuse_readdir_flags);
|
||||||
|
int fischl_releasedir(const char* path, struct fuse_file_info *fi);
|
||||||
|
int fischl_unlink (const char *);
|
||||||
|
int fischl_rmdir(const char *);
|
||||||
|
int fischl_rename(const char *path, const char *, unsigned int flags);
|
||||||
|
int fischl_truncate(const char *path, off_t, struct fuse_file_info *fi);
|
||||||
|
int fischl_chmod(const char *path, mode_t, struct fuse_file_info *fi);
|
||||||
|
int fischl_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi);
|
||||||
|
int fischl_open (const char *, struct fuse_file_info *);//open file
|
||||||
|
int fischl_release (const char *, struct fuse_file_info *);//close file
|
||||||
|
int fischl_write(const char *, const char *, size_t, off_t, struct fuse_file_info *);
|
||||||
|
int fischl_read(const char *, char *, size_t, off_t, struct fuse_file_info *);
|
||||||
|
};
|
@ -1,7 +1,3 @@
|
|||||||
class fischl{
|
|
||||||
|
|
||||||
// declare
|
|
||||||
public:
|
|
||||||
int init();
|
|
||||||
|
|
||||||
};
|
int fischl(int argc, char *argv[]);
|
||||||
|
@ -43,7 +43,7 @@ public:
|
|||||||
u_int64_t single_indirect_block, double_indirect_block, triple_indirect_block;
|
u_int64_t single_indirect_block, double_indirect_block, triple_indirect_block;
|
||||||
u_int64_t direct_blocks[NUMBER_OF_DIRECT_BLOCKS];
|
u_int64_t direct_blocks[NUMBER_OF_DIRECT_BLOCKS];
|
||||||
|
|
||||||
INode_Data(u_int64_t inode_num = 0xFFFFFFFFFFFFFFFF);
|
INode_Data(u_int64_t inode_num = (u_int64_t)(0xFFFFFFFFFFFFFFFF));
|
||||||
void serialize(char buf[]);
|
void serialize(char buf[]);
|
||||||
void deserialize(char buf[]);
|
void deserialize(char buf[]);
|
||||||
};
|
};
|
||||||
|
@ -12,11 +12,13 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define IO_BLOCK_SIZE 4096
|
#define IO_BLOCK_SIZE 4096
|
||||||
|
#define INDIRECT_BLOCKS 512
|
||||||
|
|
||||||
#define NUM_INODE_BLOCKS 1023
|
#define NUM_INODE_BLOCKS 1023
|
||||||
|
|
||||||
#define INODE_SIZE 512
|
#define INODE_SIZE 512
|
||||||
|
|
||||||
#define DATABLOCKS_PER_BITMAP_BLOCK 255
|
// TODO: explore the optimal value for this
|
||||||
|
#define DATABLOCKS_PER_BITMAP_BLOCK 2047
|
||||||
|
|
||||||
#endif
|
#endif
|
235
lib/direntry.cpp
Normal file
235
lib/direntry.cpp
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "fs.hpp"
|
||||||
|
#include "direntry.h"
|
||||||
|
/*********************************Hash operation********************************************
|
||||||
|
|
||||||
|
********************************************************************************************/
|
||||||
|
// Hash operation
|
||||||
|
unsigned int hash(HashTable *h, char *key) {
|
||||||
|
unsigned int hashval = 0;
|
||||||
|
for (; *key != '\0'; key++) hashval = *key + (hashval << 5) - hashval;
|
||||||
|
return hashval % h->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashTable *createHashTable(int size) {
|
||||||
|
HashTable *newTable = (HashTable *)malloc(sizeof(HashTable));
|
||||||
|
newTable->size = size;
|
||||||
|
newTable->table = (FileNode **)malloc(sizeof(FileNode *) * size);
|
||||||
|
for (int i = 0; i < size; i++) newTable->table[i] = NULL;
|
||||||
|
return newTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileNode *insertHash(HashTable *h, char *key, TreeNode *subdirectory) {
|
||||||
|
unsigned int hashval = hash(h, key);
|
||||||
|
FileNode *newNode = (FileNode *)malloc(sizeof(FileNode));
|
||||||
|
newNode->name = strdup(key);
|
||||||
|
newNode->subdirectory = subdirectory;
|
||||||
|
newNode->next = h->table[hashval];
|
||||||
|
h->table[hashval] = newNode;
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileNode *lookupHash(HashTable *h, char *key) {
|
||||||
|
unsigned int hashval = hash(h, key);
|
||||||
|
FileNode *node = h->table[hashval];
|
||||||
|
while (node != NULL) {
|
||||||
|
if (strcmp(node->name, key) == 0) return node;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
return NULL; // Not found
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeHash(HashTable *h, char *key) {
|
||||||
|
unsigned int hashval = hash(h, key);
|
||||||
|
FileNode *node = h->table[hashval];
|
||||||
|
if (node == NULL) return false;
|
||||||
|
if (strcmp(node->name, key) == 0) {
|
||||||
|
h->table[hashval] = node->next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
FileNode *prev = NULL;
|
||||||
|
bool foundit = false;
|
||||||
|
while (node != NULL) {
|
||||||
|
if (strcmp(node->name, key) == 0) break;
|
||||||
|
prev = node;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
if (node == NULL) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
prev->next = node->next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode *createDirectory(const char *dirName, TreeNode *parent, int hashSize) {
|
||||||
|
TreeNode *newDir = (TreeNode *)malloc(sizeof(TreeNode));
|
||||||
|
newDir->dirName = strdup(dirName);
|
||||||
|
newDir->contents = createHashTable(hashSize);
|
||||||
|
newDir->parent = parent;
|
||||||
|
if (parent) {
|
||||||
|
newDir->self_info = insertHash(parent->contents, newDir->dirName, newDir);
|
||||||
|
}
|
||||||
|
return newDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode *find_parentPath(TreeNode *root, const char *path) {
|
||||||
|
char *pathCopy = strdup(path);
|
||||||
|
char *segment = strtok(pathCopy, "/");
|
||||||
|
TreeNode *current = root;
|
||||||
|
FileNode *file = NULL;
|
||||||
|
|
||||||
|
while (segment != NULL && current != NULL) {
|
||||||
|
file = lookupHash(current->contents, segment);
|
||||||
|
if (file != NULL && file->subdirectory == NULL) {
|
||||||
|
free(pathCopy);
|
||||||
|
//printf("status current directory %s\n",current->dirName);
|
||||||
|
return current; //File found
|
||||||
|
}
|
||||||
|
current = file ? file->subdirectory : NULL;
|
||||||
|
segment = strtok(NULL, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pathCopy);
|
||||||
|
return current; // NULL if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeHashTable(HashTable *table) {
|
||||||
|
if (table == NULL) return;
|
||||||
|
|
||||||
|
free(table->table);
|
||||||
|
free(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeTree(TreeNode *node) {
|
||||||
|
//printf("***********************FREE TREE %s**************************\n",node->dirName);
|
||||||
|
//printf("***********************FREE TREE **************************\n");
|
||||||
|
if (node == NULL) return;
|
||||||
|
|
||||||
|
if (node->contents != NULL) {
|
||||||
|
for (int i = 0; i < node->contents->size; ++i) {
|
||||||
|
FileNode *current = node->contents->table[i];
|
||||||
|
while (current != NULL) {
|
||||||
|
FileNode *temp = current;
|
||||||
|
current = current->next;
|
||||||
|
|
||||||
|
if (temp->subdirectory != NULL) {
|
||||||
|
freeTree(temp->subdirectory);
|
||||||
|
}
|
||||||
|
// Free the FileNode if it's not a directory
|
||||||
|
// printf("free who %s\n",temp->name);
|
||||||
|
free(temp->name);
|
||||||
|
free(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//printf("free %s's hash table\n",node->dirName);
|
||||||
|
freeHashTable(node->contents);
|
||||||
|
//node->contents = NULL;
|
||||||
|
}
|
||||||
|
//printf("free directory %s\n",node->dirName);
|
||||||
|
free(node->dirName);
|
||||||
|
node->dirName = NULL;
|
||||||
|
free(node);
|
||||||
|
node = NULL;
|
||||||
|
//printf("***********************END**************************\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************Direntry operation******************************************
|
||||||
|
|
||||||
|
********************************************************************************************/
|
||||||
|
//for fake root (mount point)
|
||||||
|
TreeNode *fischl_init_entry(int new_inode_number, const char *fileName, INode_Data *new_inode) {
|
||||||
|
TreeNode *newDir = (TreeNode *)malloc(sizeof(TreeNode));
|
||||||
|
newDir->dirName = strdup(fileName);
|
||||||
|
newDir->contents = createHashTable(20);//hashSize define 20
|
||||||
|
newDir->parent = newDir;
|
||||||
|
FileNode *newFile = (FileNode *)malloc(sizeof(FileNode));
|
||||||
|
newFile->name = strdup(fileName);
|
||||||
|
newFile->inode_number = new_inode_number;
|
||||||
|
newFile->permissions = new_inode->metadata.permissions;
|
||||||
|
newFile->subdirectory = newDir;
|
||||||
|
newDir->self_info = newFile;
|
||||||
|
return newDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fischl_add_entry(TreeNode *parent, int new_inode_number, const char *fileName, INode_Data *new_inode){
|
||||||
|
char *Name = strdup(fileName);
|
||||||
|
TreeNode *newDir = NULL;
|
||||||
|
/*If directory, malloc TreeNode, and then create filenode that belongs to Parent hash table content*/
|
||||||
|
if ((new_inode->metadata.permissions & S_IFMT) == S_IFDIR) {
|
||||||
|
newDir = (TreeNode *)malloc(sizeof(TreeNode));
|
||||||
|
newDir->dirName = Name;
|
||||||
|
newDir->contents = createHashTable(20);//hasSize define 20
|
||||||
|
newDir->parent = parent;
|
||||||
|
}
|
||||||
|
FileNode *newFile = insertHash(parent->contents, Name, newDir); //newDir == NULL indicates it's a file
|
||||||
|
//assign INode *new_inode metadata to data member in FileNode structure
|
||||||
|
newFile->permissions = new_inode->metadata.permissions;
|
||||||
|
newFile->inode_number = new_inode_number;
|
||||||
|
//Diretory have its own file information, that is . here
|
||||||
|
if(newDir != NULL)
|
||||||
|
newDir->self_info = newFile;
|
||||||
|
//free(Name); cannot free name
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fischl_rm_entry(TreeNode *parent, const char *fileName) {
|
||||||
|
char *fileName_dup = strdup(fileName);
|
||||||
|
if (parent->contents == NULL) return -1;
|
||||||
|
FileNode *file = NULL;
|
||||||
|
file = lookupHash(parent->contents, fileName_dup);
|
||||||
|
if (file == NULL) return -1;
|
||||||
|
if (file->subdirectory != NULL) freeTree(file->subdirectory);
|
||||||
|
removeHash(parent->contents, fileName_dup);
|
||||||
|
free(file->name);
|
||||||
|
free(file);
|
||||||
|
delete fileName_dup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FileNode *fischl_find_entry(TreeNode *root, const char *path){
|
||||||
|
//support . and .. function
|
||||||
|
char *pathCopy = strdup(path);
|
||||||
|
char *segment = strtok(pathCopy, "/");
|
||||||
|
TreeNode *current = root;
|
||||||
|
FileNode *file = NULL;
|
||||||
|
|
||||||
|
while (segment != NULL && current != NULL) {
|
||||||
|
if (strcmp(segment, "..") == 0) {
|
||||||
|
// Move up to the parent directory
|
||||||
|
current = current->parent;
|
||||||
|
if (current == NULL) {
|
||||||
|
// If there's no parent, we've reached the top of the tree, but root itself is same
|
||||||
|
file = NULL;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
file = current->self_info;
|
||||||
|
}
|
||||||
|
} else if (strcmp(segment, ".") == 0) {
|
||||||
|
// Stay in the current directory (no action needed)
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
file = lookupHash(current->contents, segment);
|
||||||
|
if (file != NULL && file->subdirectory == NULL) {
|
||||||
|
free(pathCopy);
|
||||||
|
return file; //File found
|
||||||
|
//return current; return filenode
|
||||||
|
}
|
||||||
|
current = file ? file->subdirectory : NULL;
|
||||||
|
}
|
||||||
|
segment = strtok(NULL, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pathCopy);
|
||||||
|
|
||||||
|
if (current != NULL && file == NULL) {
|
||||||
|
// If we've stopped at a directory and not a file, return the directory's self info
|
||||||
|
return current->self_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return file; // NULL if not found
|
||||||
|
}
|
901
lib/files.cpp
Normal file
901
lib/files.cpp
Normal file
@ -0,0 +1,901 @@
|
|||||||
|
//#include "fuse.h" add this when layer3
|
||||||
|
#define FUSE_USE_VERSION 31
|
||||||
|
|
||||||
|
#include "files.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct DirectoryEntry {
|
||||||
|
u_int64_t inode_number;
|
||||||
|
char file_name[256];
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void FilesOperation::printbuffer(const char *buff, int len) {
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
printf("%x ", buff[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
FilesOperation::FilesOperation(RawDisk &disk_, Fs *fs) : disk(disk_) {
|
||||||
|
this->fs = fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilesOperation::create_dot_dotdot(INode_Data *inode,
|
||||||
|
u_int64_t parent_inode_number) {
|
||||||
|
char buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
DirectoryEntry dot;
|
||||||
|
dot.inode_number = inode->inode_num;
|
||||||
|
strcpy(dot.file_name, ".");
|
||||||
|
dot.serialize(buffer);
|
||||||
|
DirectoryEntry dotdot;
|
||||||
|
dotdot.inode_number = parent_inode_number;
|
||||||
|
strcpy(dotdot.file_name, "..");
|
||||||
|
dotdot.serialize(buffer + 264);
|
||||||
|
int ret = fs->write(inode, buffer, IO_BLOCK_SIZE, 0);
|
||||||
|
// printf("in create_dot_dotdot: fs->write returned %d\n",ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilesOperation::initialize_rootinode() {
|
||||||
|
// this method must be called explicitly right after initializion
|
||||||
|
INode_Data *root_inode = new INode_Data();
|
||||||
|
fs->inode_manager->new_inode(0, 0, S_IFDIR, root_inode);
|
||||||
|
u_int64_t root_inode_number = root_inode->inode_num;
|
||||||
|
create_dot_dotdot(root_inode, root_inode_number);
|
||||||
|
root_node = fischl_init_entry(root_inode_number, "/", root_inode);
|
||||||
|
assert(root_node->self_info != NULL);
|
||||||
|
fs->inode_manager->save_inode(root_inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilesOperation::printDirectory(u_int64_t inode_number) {
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = inode_number;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
char buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) {
|
||||||
|
fs->read(&inode, buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(buffer + i);
|
||||||
|
if (ent.inode_number)
|
||||||
|
printf("%s\t%llu;\t", ent.file_name, ent.inode_number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
INode_Data *FilesOperation::create_new_inode(u_int64_t parent_inode_number,
|
||||||
|
const char *name, mode_t mode) {
|
||||||
|
// trys to create a file under parent directory
|
||||||
|
if (strlen(name) >= 256) {
|
||||||
|
perror("Name too long, cannot create file or directory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = parent_inode_number;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
if ((inode.metadata.permissions & S_IFMT) != S_IFDIR) {
|
||||||
|
fprintf(stderr, "[%s ,%d] please create under directory\n", __func__,
|
||||||
|
__LINE__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if file or directory already exists
|
||||||
|
char r_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) {
|
||||||
|
fs->read(&inode, r_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(r_buffer + i);
|
||||||
|
if (strcmp(ent.file_name, name) == 0 && ent.inode_number != 0) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allocated = false;
|
||||||
|
INode_Data *new_inode = new INode_Data();
|
||||||
|
fs->inode_manager->new_inode(0, 0, mode, new_inode);
|
||||||
|
if ((mode & S_IFMT) == S_IFDIR) {
|
||||||
|
create_dot_dotdot(new_inode, parent_inode_number);
|
||||||
|
fs->inode_manager->save_inode(new_inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
char rw_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) {
|
||||||
|
fs->read(&inode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(rw_buffer + i);
|
||||||
|
if (ent.inode_number == 0) {
|
||||||
|
allocated = true;
|
||||||
|
ent.inode_number = new_inode->inode_num;
|
||||||
|
strcpy(ent.file_name, name);
|
||||||
|
ent.serialize(rw_buffer + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allocated) {
|
||||||
|
fs->write(&inode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allocated) {
|
||||||
|
char write_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
DirectoryEntry ent;
|
||||||
|
ent.inode_number = new_inode->inode_num;
|
||||||
|
strcpy(ent.file_name, name);
|
||||||
|
ent.serialize(write_buffer);
|
||||||
|
fs->write(&inode, write_buffer, IO_BLOCK_SIZE,
|
||||||
|
(inode.metadata.size / IO_BLOCK_SIZE) * IO_BLOCK_SIZE);
|
||||||
|
fs->inode_manager->save_inode(&inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
u_int64_t FilesOperation::disk_namei(const char *path) {
|
||||||
|
// returns the inode number corresponding to path
|
||||||
|
u_int64_t current_inode = root_node->self_info->inode_number;
|
||||||
|
std::string current_dirname;
|
||||||
|
std::istringstream pathStream(path);
|
||||||
|
std::string new_name;
|
||||||
|
std::getline(pathStream, new_name, '/');
|
||||||
|
if (!new_name.empty()) {
|
||||||
|
printf("disk_namei: path should start with /\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (std::getline(pathStream, new_name, '/')) {
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = current_inode;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
if ((inode.metadata.permissions & S_IFMT) != S_IFDIR ||
|
||||||
|
inode.metadata.size == 0) {
|
||||||
|
printf("disk_namei: %s is not a non-empty directory\n",
|
||||||
|
current_dirname.c_str());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
u_int64_t new_inode_number = 0;
|
||||||
|
|
||||||
|
char buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) {
|
||||||
|
fs->read(&inode, buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
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)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!new_inode_number) {
|
||||||
|
printf("disk_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_number
|
||||||
|
// (root_node->self_info->inode_number) 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::namei(const char *path) {
|
||||||
|
FileNode *filenode = fischl_find_entry(root_node, path);
|
||||||
|
if (filenode)
|
||||||
|
return filenode->inode_number;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_mkdir(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; <parent path>\0<direcotry name>
|
||||||
|
char *newDirname =
|
||||||
|
lastSlash + 1; //\0<direcotry name>, get from <direcotry name>
|
||||||
|
char *ParentPath = pathdup; // pathdup are separated by pathdup, so it take
|
||||||
|
// <parent path> only
|
||||||
|
|
||||||
|
FileNode *parent_filenode = strlen(ParentPath)
|
||||||
|
? fischl_find_entry(root_node, ParentPath)
|
||||||
|
: root_node->self_info;
|
||||||
|
if (parent_filenode == NULL) {
|
||||||
|
fprintf(stderr, "[%s ,%d] ParentPath:{%s} not found\n", __func__, __LINE__,
|
||||||
|
ParentPath);
|
||||||
|
free(pathdup);
|
||||||
|
return -ENOENT; // parentpath directory does not exist
|
||||||
|
}
|
||||||
|
u_int64_t parent_inode_number = parent_filenode->inode_number;
|
||||||
|
// printf("%s, %llu, %s\n", parent_filenode->name, parent_inode_number,
|
||||||
|
// newDirname); make new inode
|
||||||
|
INode_Data *ret =
|
||||||
|
create_new_inode(parent_inode_number, newDirname,
|
||||||
|
mode | S_IFDIR); // specify S_IFDIR as directory
|
||||||
|
if (ret == NULL)
|
||||||
|
return -1; // ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST
|
||||||
|
fischl_add_entry(parent_filenode->subdirectory, ret->inode_num, newDirname,
|
||||||
|
ret);
|
||||||
|
free(pathdup);
|
||||||
|
return 0; // SUCCESS
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
special file
|
||||||
|
*/
|
||||||
|
int FilesOperation::fischl_mknod(const char *path, mode_t mode, dev_t dev) {
|
||||||
|
// check path
|
||||||
|
char *pathdup = strdup(path);
|
||||||
|
char *lastSlash = strrchr(pathdup, '/');
|
||||||
|
*lastSlash = '\0'; // Split the string into parent path and new directory
|
||||||
|
// name; <parent path>\0<direcotry name>
|
||||||
|
char *newFilename =
|
||||||
|
lastSlash + 1; //\0<direcotry name>, get from <direcotry name>
|
||||||
|
char *ParentPath = pathdup; // pathdup are separated by pathdup, so it take
|
||||||
|
// <parent path> only
|
||||||
|
// 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) {
|
||||||
|
fprintf(stderr, "[%s ,%d] ParentPath:{%s} not found\n", __func__, __LINE__,
|
||||||
|
ParentPath);
|
||||||
|
free(pathdup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
u_int64_t parent_inode_number = parent_filenode->inode_number;
|
||||||
|
// make new inode
|
||||||
|
INode_Data *ret = create_new_inode(parent_inode_number, newFilename, mode);
|
||||||
|
if (ret == NULL)
|
||||||
|
return -1; // ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST
|
||||||
|
// make new node
|
||||||
|
fischl_add_entry(parent_filenode->subdirectory, ret->inode_num, newFilename,
|
||||||
|
ret);
|
||||||
|
free(pathdup);
|
||||||
|
return 0; // SUCESS
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
regular file
|
||||||
|
*/
|
||||||
|
int FilesOperation::fischl_create(const char *path, mode_t mode,
|
||||||
|
struct fuse_file_info *fi) {
|
||||||
|
// check path
|
||||||
|
char *pathdup = strdup(path);
|
||||||
|
char *lastSlash = strrchr(pathdup, '/');
|
||||||
|
*lastSlash = '\0'; // Split the string into parent path and new directory
|
||||||
|
// name; <parent path>\0<direcotry name>
|
||||||
|
char *newFilename =
|
||||||
|
lastSlash + 1; //\0<direcotry name>, get from <direcotry name>
|
||||||
|
char *ParentPath = pathdup; // pathdup are separated by pathdup, so it take
|
||||||
|
// <parent path> only
|
||||||
|
// 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) {
|
||||||
|
fprintf(stderr, "[%s ,%d] ParentPath:{%s} not found\n", __func__, __LINE__,
|
||||||
|
ParentPath);
|
||||||
|
free(pathdup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
u_int64_t parent_inode_number = parent_filenode->inode_number;
|
||||||
|
// make new inode
|
||||||
|
INode_Data *ret = create_new_inode(parent_inode_number, newFilename, mode);
|
||||||
|
if (ret == NULL)
|
||||||
|
return -1; // ENOSPC but create_new_inode handle ENAMETOOLONG EEXIST
|
||||||
|
// make new node in RAM
|
||||||
|
fischl_add_entry(parent_filenode->subdirectory, ret->inode_num, newFilename,
|
||||||
|
ret);
|
||||||
|
// directly give inode number rather than create file descriptor table
|
||||||
|
fi->fh =
|
||||||
|
ret->inode_num; // assign file descriptor as inode number to fuse system
|
||||||
|
free(pathdup);
|
||||||
|
return 0; // SUCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_getattr(const char *path, struct stat *stbuf,
|
||||||
|
struct fuse_file_info *fi) {
|
||||||
|
|
||||||
|
(void)fi;
|
||||||
|
int res = 0;
|
||||||
|
u_int64_t fh = namei(path);
|
||||||
|
|
||||||
|
if (fh == -1) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = fh;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
|
||||||
|
memset(stbuf, 0, sizeof(struct stat));
|
||||||
|
if ((inode.metadata.permissions & S_IFMT) == S_IFDIR) {
|
||||||
|
stbuf->st_mode = S_IFDIR | 0755;
|
||||||
|
stbuf->st_nlink = inode.metadata.reference_count;
|
||||||
|
stbuf->st_uid = inode.metadata.uid;
|
||||||
|
stbuf->st_gid = inode.metadata.gid;
|
||||||
|
} else {
|
||||||
|
stbuf->st_mode = S_IFREG | 0444;
|
||||||
|
stbuf->st_nlink = inode.metadata.reference_count;
|
||||||
|
stbuf->st_uid = inode.metadata.uid;
|
||||||
|
stbuf->st_gid = inode.metadata.gid;
|
||||||
|
stbuf->st_size = inode.metadata.size;
|
||||||
|
}
|
||||||
|
perror(std::to_string(inode.metadata.size).c_str());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_readdir(const char *path, void *buf,
|
||||||
|
fuse_fill_dir_t filler, off_t ft,
|
||||||
|
struct fuse_file_info *fi,
|
||||||
|
enum fuse_readdir_flags flg) {
|
||||||
|
// check path
|
||||||
|
u_int64_t fh = namei(path);
|
||||||
|
|
||||||
|
if (fh == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = fh;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
char buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) {
|
||||||
|
fs->read(&inode, buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(buffer + i);
|
||||||
|
if (ent.inode_number) {
|
||||||
|
filler(buf, ent.file_name, NULL, 0, FUSE_FILL_DIR_PLUS);
|
||||||
|
// printf("%s\t%llu;\t", ent.file_name, ent.inode_number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_releasedir(const char *path,
|
||||||
|
struct fuse_file_info *fi) {
|
||||||
|
if (fischl_find_entry(root_node, path) == NULL)
|
||||||
|
return -ENOENT;
|
||||||
|
// do with file descriptor that cannot be used
|
||||||
|
fi->fh = -1;
|
||||||
|
return 0; // SUCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilesOperation::unlink_inode(u_int64_t inode_number) {
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = inode_number;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
if ((inode.metadata.permissions & S_IFMT) == S_IFDIR) {
|
||||||
|
char buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) {
|
||||||
|
fs->read(&inode, buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
if (ent.inode_number && strcmp(ent.file_name, ".") &&
|
||||||
|
strcmp(ent.file_name, "..")) {
|
||||||
|
unlink_inode(ent.inode_number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: error handling
|
||||||
|
int res = fs->truncate(&inode, 0);
|
||||||
|
fs->inode_manager->free_inode(&inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_rmdir(const char *path) {
|
||||||
|
char *pathdup = strdup(path);
|
||||||
|
char *lastSlash = strrchr(pathdup, '/');
|
||||||
|
*lastSlash = '\0';
|
||||||
|
char *dirname = lastSlash + 1;
|
||||||
|
char *ParentPath = pathdup;
|
||||||
|
if (!strcmp(dirname, ".") || !strcmp(dirname, "..")) {
|
||||||
|
printf("refusing to remove . or ..\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
FileNode *parent_filenode = fischl_find_entry(root_node, ParentPath);
|
||||||
|
if (parent_filenode == NULL) {
|
||||||
|
printf("parent %s not found by fischl_find_entry\n", ParentPath);
|
||||||
|
free(pathdup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
u_int64_t parent_inode_number = parent_filenode->inode_number;
|
||||||
|
u_int64_t target_inode = 0;
|
||||||
|
|
||||||
|
// remove its record from parent
|
||||||
|
INode_Data parent_INode;
|
||||||
|
parent_INode.inode_num = parent_inode_number;
|
||||||
|
fs->inode_manager->load_inode(&parent_INode);
|
||||||
|
char rw_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < parent_INode.metadata.size / IO_BLOCK_SIZE;
|
||||||
|
idx++) {
|
||||||
|
fs->read(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(rw_buffer + i);
|
||||||
|
if (strcmp(ent.file_name, dirname) == 0) {
|
||||||
|
target_inode = ent.inode_number;
|
||||||
|
ent.inode_number = 0;
|
||||||
|
memset(ent.file_name, 0, sizeof(ent.file_name));
|
||||||
|
ent.serialize(rw_buffer + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (target_inode) {
|
||||||
|
fs->write(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove inode itself
|
||||||
|
if (target_inode) {
|
||||||
|
unlink_inode(target_inode);
|
||||||
|
// remove node itself and from parent hash
|
||||||
|
fischl_rm_entry(parent_filenode->subdirectory, dirname);
|
||||||
|
free(pathdup);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
printf("cannot find %s in %s", dirname, ParentPath);
|
||||||
|
free(pathdup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_chmod(const char *path, mode_t mode,
|
||||||
|
struct fuse_file_info *fi) {
|
||||||
|
(void)fi;
|
||||||
|
int res = 0;
|
||||||
|
u_int64_t fh = namei(path);
|
||||||
|
|
||||||
|
if (fh == -1) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = fh;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
inode.metadata.permissions = mode;
|
||||||
|
fs->inode_manager->save_inode(&inode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_chown(const char *path, uid_t uid, gid_t gid,
|
||||||
|
struct fuse_file_info *fi) {
|
||||||
|
(void)fi;
|
||||||
|
int res = 0;
|
||||||
|
u_int64_t fh = namei(path);
|
||||||
|
|
||||||
|
if (fh == -1) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = fh;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
inode.metadata.uid = uid;
|
||||||
|
inode.metadata.gid = gid;
|
||||||
|
fs->inode_manager->save_inode(&inode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_unlink(const char *path) {
|
||||||
|
char *pathdup = strdup(path);
|
||||||
|
char *lastSlash = strrchr(pathdup, '/');
|
||||||
|
*lastSlash = '\0';
|
||||||
|
char *filename = lastSlash + 1;
|
||||||
|
char *ParentPath = pathdup;
|
||||||
|
if (!strcmp(filename, ".") || !strcmp(filename, "..")) {
|
||||||
|
printf("refusing to remove . or ..\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
FileNode *parent_filenode = fischl_find_entry(root_node, ParentPath);
|
||||||
|
if (parent_filenode == NULL) {
|
||||||
|
printf("parent %s not found by fischl_find_entry\n", ParentPath);
|
||||||
|
free(pathdup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
u_int64_t parent_inode_number = parent_filenode->inode_number;
|
||||||
|
u_int64_t target_inode = 0;
|
||||||
|
|
||||||
|
// remove its record from parent
|
||||||
|
INode_Data parent_INode;
|
||||||
|
parent_INode.inode_num = parent_inode_number;
|
||||||
|
fs->inode_manager->load_inode(&parent_INode);
|
||||||
|
char rw_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < parent_INode.metadata.size / IO_BLOCK_SIZE;
|
||||||
|
idx++) {
|
||||||
|
fs->read(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(rw_buffer + i);
|
||||||
|
if (strcmp(ent.file_name, filename) == 0) {
|
||||||
|
target_inode = ent.inode_number;
|
||||||
|
ent.inode_number = 0;
|
||||||
|
memset(ent.file_name, 0, sizeof(ent.file_name));
|
||||||
|
ent.serialize(rw_buffer + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (target_inode) {
|
||||||
|
fs->write(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove inode itself
|
||||||
|
if (target_inode) {
|
||||||
|
unlink_inode(target_inode);
|
||||||
|
// remove node itself and from parent hash
|
||||||
|
fischl_rm_entry(parent_filenode->subdirectory, filename);
|
||||||
|
free(pathdup);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
printf("cannot find %s in %s", filename, ParentPath);
|
||||||
|
free(pathdup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_open(const char *path, struct fuse_file_info *fi) {
|
||||||
|
/*Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be filtered out / handled by
|
||||||
|
the kernel. if no files will use create function
|
||||||
|
*/
|
||||||
|
FileNode *get_file;
|
||||||
|
if ((get_file = fischl_find_entry(root_node, path)) == NULL)
|
||||||
|
return -ENOENT;
|
||||||
|
// if need to do with flag fi->flags ((fi->flags & O_ACCMODE)). Initial
|
||||||
|
// setting ALL access create function will handle file descriptor fi->fh
|
||||||
|
fi->fh = get_file->inode_number;
|
||||||
|
return 0; // SUCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_release(const char *path,
|
||||||
|
struct fuse_file_info *fi) {
|
||||||
|
/*Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be filtered out / handled by
|
||||||
|
the kernel. if no files will use create function
|
||||||
|
*/
|
||||||
|
FileNode *get_file;
|
||||||
|
if ((get_file = fischl_find_entry(root_node, path)) == NULL)
|
||||||
|
return -ENOENT;
|
||||||
|
// do with file descriptor that cannot be used
|
||||||
|
fi->fh = -1;
|
||||||
|
return 0; // SUCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_write(const char *path, const char *buf, size_t size,
|
||||||
|
off_t offset, struct fuse_file_info *fi) {
|
||||||
|
/** Write data to an open file
|
||||||
|
*
|
||||||
|
* Write should return exactly the number of bytes requested
|
||||||
|
* except on error. An exception to this is when the 'direct_io'
|
||||||
|
* mount option is specified (see read operation).
|
||||||
|
*
|
||||||
|
* Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is
|
||||||
|
* expected to reset the setuid and setgid bits.
|
||||||
|
*/
|
||||||
|
// use path for debug, filedescriptor is enough
|
||||||
|
// FileNode *get_file;
|
||||||
|
// if((get_file = fischl_find_entry(root_node, path)) == NULL)
|
||||||
|
// return -ENOENT;
|
||||||
|
// Caution! this based on content in file are multiple of IO_BLOCK_SIZE, not
|
||||||
|
// the exact write size. based on current write_datablock API implement, when
|
||||||
|
// write_datablock pass with actual size not index this function should be
|
||||||
|
// fixed
|
||||||
|
|
||||||
|
INode_Data inode;
|
||||||
|
// Assuming inode is correctly initialized here based on 'path'
|
||||||
|
inode.inode_num = fi->fh;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
// size_t len = (inode.metadata.size/IO_BLOCK_SIZE) * IO_BLOCK_SIZE; //
|
||||||
|
// Assuming each block is 4096 bytes
|
||||||
|
// Determine the length of the buffer
|
||||||
|
// Allocate memory for the new buffer
|
||||||
|
char *buffer = (char *)malloc(size);
|
||||||
|
memcpy(buffer, buf, size);
|
||||||
|
size_t bytes_write = fs->write(&inode, buffer, size, offset);
|
||||||
|
/*size_t block_index = offset / IO_BLOCK_SIZE; // Starting block index
|
||||||
|
size_t block_offset = offset % IO_BLOCK_SIZE; // Offset within the first block
|
||||||
|
while (bytes_write < size) {
|
||||||
|
char block_buffer[IO_BLOCK_SIZE]; // Temporary buffer for each block
|
||||||
|
size_t copy_size = std::min(size - bytes_write, IO_BLOCK_SIZE -
|
||||||
|
block_offset); memcpy(block_buffer + block_offset, buf + bytes_write,
|
||||||
|
copy_size); fs->write(&inode, block_buffer, IO_BLOCK_SIZE,
|
||||||
|
block_index*IO_BLOCK_SIZE);
|
||||||
|
// fprintf(stderr,"[%s ,%d] inode.size %d, block_index %d, block_buffer
|
||||||
|
%s\n",__func__,__LINE__, inode.size, block_index, block_buffer); bytes_write
|
||||||
|
+= copy_size; block_index++; block_offset = 0; // Only the first block might
|
||||||
|
have a non-zero offset
|
||||||
|
}*/
|
||||||
|
fs->inode_manager->save_inode(&inode);
|
||||||
|
free(buffer);
|
||||||
|
return bytes_write; // Return the actual number of bytes read
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::insert_inode_to(u_int64_t parent_inode_number,
|
||||||
|
const char *name, INode_Data *new_inode) {
|
||||||
|
// trys to create a file under parent directory
|
||||||
|
if (strlen(name) >= 256) {
|
||||||
|
perror("Name too long, cannot create file or directory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = parent_inode_number;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
if ((inode.metadata.permissions & S_IFMT) != S_IFDIR) {
|
||||||
|
fprintf(stderr, "[%s ,%d] please create under directory\n", __func__,
|
||||||
|
__LINE__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if file or directory already exists
|
||||||
|
char r_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) {
|
||||||
|
fs->read(&inode, r_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(r_buffer + i);
|
||||||
|
if (strcmp(ent.file_name, name) == 0 && ent.inode_number != 0) {
|
||||||
|
if ((new_inode->metadata.permissions & 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 -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allocated = false;
|
||||||
|
|
||||||
|
char rw_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < inode.metadata.size / IO_BLOCK_SIZE; idx++) {
|
||||||
|
fs->read(&inode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(rw_buffer + i);
|
||||||
|
if (ent.inode_number == 0) {
|
||||||
|
allocated = true;
|
||||||
|
ent.inode_number = new_inode->inode_num;
|
||||||
|
strcpy(ent.file_name, name);
|
||||||
|
ent.serialize(rw_buffer + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allocated) {
|
||||||
|
fs->write(&inode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allocated) {
|
||||||
|
char write_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
DirectoryEntry ent;
|
||||||
|
ent.inode_number = new_inode->inode_num;
|
||||||
|
strcpy(ent.file_name, name);
|
||||||
|
ent.serialize(write_buffer);
|
||||||
|
fs->write(&inode, write_buffer, IO_BLOCK_SIZE,
|
||||||
|
(inode.metadata.size / IO_BLOCK_SIZE) * IO_BLOCK_SIZE);
|
||||||
|
fs->inode_manager->save_inode(&inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: rename dir and rename fail
|
||||||
|
int FilesOperation::fischl_rename(const char *path, const char *new_name,
|
||||||
|
unsigned int flags) {
|
||||||
|
char *pathdup = strdup(path);
|
||||||
|
char *lastSlash = strrchr(pathdup, '/');
|
||||||
|
*lastSlash = '\0';
|
||||||
|
char *filename = lastSlash + 1;
|
||||||
|
char *ParentPath = pathdup;
|
||||||
|
if (!strcmp(filename, ".") || !strcmp(filename, "..")) {
|
||||||
|
printf("refusing to remove . or ..\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
FileNode *parent_filenode = fischl_find_entry(root_node, ParentPath);
|
||||||
|
if (parent_filenode == NULL) {
|
||||||
|
printf("parent %s not found by fischl_find_entry\n", ParentPath);
|
||||||
|
free(pathdup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
u_int64_t parent_inode_number = parent_filenode->inode_number;
|
||||||
|
u_int64_t target_inode = 0;
|
||||||
|
|
||||||
|
// remove its record from parent
|
||||||
|
INode_Data parent_INode;
|
||||||
|
parent_INode.inode_num = parent_inode_number;
|
||||||
|
fs->inode_manager->load_inode(&parent_INode);
|
||||||
|
char rw_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
for (u_int64_t idx = 0; idx < parent_INode.metadata.size / IO_BLOCK_SIZE;
|
||||||
|
idx++) {
|
||||||
|
fs->read(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(rw_buffer + i);
|
||||||
|
if (strcmp(ent.file_name, filename) == 0) {
|
||||||
|
target_inode = ent.inode_number;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (target_inode) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove inode itself
|
||||||
|
if (target_inode) {
|
||||||
|
INode_Data ret;
|
||||||
|
ret.inode_num = target_inode;
|
||||||
|
fs->inode_manager->load_inode(&ret);
|
||||||
|
|
||||||
|
if (flags == RENAME_EXCHANGE) {
|
||||||
|
printf("RENAME_EXCHANGE ");
|
||||||
|
} else if (flags == RENAME_NOREPLACE) {
|
||||||
|
printf("RENAME_NOREPLACE ");
|
||||||
|
} else
|
||||||
|
printf("ELSE ");
|
||||||
|
|
||||||
|
printf("FOUND INODE AT %llu %s\n", target_inode, new_name);
|
||||||
|
char *pathdup2 = strdup(new_name);
|
||||||
|
char *lastSlash2 = strrchr(pathdup2, '/');
|
||||||
|
*lastSlash2 = '\0'; // Split the string into parent path and new directory
|
||||||
|
// name; <parent path>\0<direcotry name>
|
||||||
|
char *newDirname2 =
|
||||||
|
lastSlash2 + 1; //\0<direcotry name>, get from <direcotry name>
|
||||||
|
char *ParentPath2 = pathdup2; // pathdup are separated by pathdup, so it
|
||||||
|
// take <parent path> only
|
||||||
|
|
||||||
|
FileNode *parent_filenode2 = strlen(ParentPath)
|
||||||
|
? fischl_find_entry(root_node, ParentPath2)
|
||||||
|
: root_node->self_info;
|
||||||
|
if (parent_filenode2 == NULL) {
|
||||||
|
fprintf(stderr, "[%s ,%d] ParentPath:{%s} not found\n", __func__,
|
||||||
|
__LINE__, ParentPath2);
|
||||||
|
free(pathdup2);
|
||||||
|
return -ENOENT; // parentpath directory does not exist
|
||||||
|
}
|
||||||
|
u_int64_t parent_inode_number2 = parent_filenode2->inode_number;
|
||||||
|
// printf("%s, %llu, %s\n", parent_filenode->name, parent_inode_number,
|
||||||
|
// newDirname); make new inode
|
||||||
|
if (insert_inode_to(parent_inode_number2, newDirname2, &ret) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u_int64_t idx = 0; idx < parent_INode.metadata.size / IO_BLOCK_SIZE;
|
||||||
|
idx++) {
|
||||||
|
fs->read(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
DirectoryEntry ent;
|
||||||
|
for (int i = 0; i <= IO_BLOCK_SIZE - 264; i += 264) {
|
||||||
|
ent.deserialize(rw_buffer + i);
|
||||||
|
if (strcmp(ent.file_name, filename) == 0) {
|
||||||
|
target_inode = ent.inode_number;
|
||||||
|
ent.inode_number = 0;
|
||||||
|
memset(ent.file_name, 0, sizeof(ent.file_name));
|
||||||
|
ent.serialize(rw_buffer + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (target_inode) {
|
||||||
|
fs->write(&parent_INode, rw_buffer, IO_BLOCK_SIZE, idx * IO_BLOCK_SIZE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fischl_rm_entry(parent_filenode->subdirectory, filename);
|
||||||
|
fischl_add_entry(parent_filenode2->subdirectory, ret.inode_num, newDirname2,
|
||||||
|
&ret);
|
||||||
|
free(pathdup);
|
||||||
|
// remove node itself and from parent hash
|
||||||
|
free(pathdup2);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
printf("cannot find %s in %s", filename, ParentPath);
|
||||||
|
free(pathdup);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_truncate(const char *path, off_t offset,
|
||||||
|
struct fuse_file_info *fi) {
|
||||||
|
(void)fi;
|
||||||
|
int res = 0;
|
||||||
|
u_int64_t fh = namei(path);
|
||||||
|
|
||||||
|
if (fh == -1) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
INode_Data inode;
|
||||||
|
inode.inode_num = fh;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
res = fs->truncate(&inode, offset);
|
||||||
|
fs->inode_manager->save_inode(&inode);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FilesOperation::fischl_read(const char *path, char *buf, size_t size,
|
||||||
|
off_t offset, struct fuse_file_info *fi) {
|
||||||
|
/** Read data from an open file
|
||||||
|
*
|
||||||
|
* Read should return exactly the number of bytes requested except
|
||||||
|
* on EOF or error, otherwise the rest of the data will be
|
||||||
|
* substituted with zeroes. An exception to this is when the
|
||||||
|
* 'direct_io' mount option is specified, in which case the return
|
||||||
|
* value of the read system call will reflect the return value of
|
||||||
|
* this operation.
|
||||||
|
*/
|
||||||
|
// Caution! this based on content in file are multiple of IO_BLOCK_SIZE, not
|
||||||
|
// the exact write size. based on current read_datablock API implement, when
|
||||||
|
// read_datablock pass with actual size not index this function should be
|
||||||
|
// fixed
|
||||||
|
INode_Data inode;
|
||||||
|
// Assuming inode is correctly initialized here based on 'path'
|
||||||
|
inode.inode_num = fi->fh;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
size_t bytes_read = fs->read(&inode, buf, size, offset);
|
||||||
|
/*size_t len = (inode.metadata.size/IO_BLOCK_SIZE) * IO_BLOCK_SIZE; //
|
||||||
|
Assuming each block is 4096 bytes
|
||||||
|
|
||||||
|
if (offset >= len) return 0; // Offset is beyond the end of the file
|
||||||
|
if (offset + size > len) size = len - offset; // Adjust size if it goes
|
||||||
|
beyond EOF
|
||||||
|
|
||||||
|
size_t bytes_read = 0;
|
||||||
|
size_t block_index = offset / IO_BLOCK_SIZE; // Starting block index
|
||||||
|
size_t block_offset = offset % IO_BLOCK_SIZE; // Offset within the first block
|
||||||
|
// fprintf(stderr,"[%s ,%d] inode.metadata.size %d\n",__func__,__LINE__,
|
||||||
|
inode.metadata.size); while (bytes_read < size && block_index <
|
||||||
|
inode.metadata.size/IO_BLOCK_SIZE) { char block_buffer[IO_BLOCK_SIZE]; //
|
||||||
|
Temporary buffer for each block fs->read(&inode, block_buffer, IO_BLOCK_SIZE,
|
||||||
|
block_index*IO_BLOCK_SIZE);
|
||||||
|
// fprintf(stderr,"[%s ,%d] block_index %d\n",__func__,__LINE__,
|
||||||
|
block_index); size_t copy_size = std::min(size - bytes_read, IO_BLOCK_SIZE -
|
||||||
|
block_offset); memcpy(buf + bytes_read, block_buffer + block_offset,
|
||||||
|
copy_size);
|
||||||
|
// fprintf(stderr,"[%s ,%d] buf %s, block_buffer %s\n",__func__,__LINE__,
|
||||||
|
buf, block_buffer); bytes_read += copy_size; block_index++; block_offset = 0;
|
||||||
|
// Only the first block might have a non-zero offset
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return bytes_read; // Return the actual number of bytes read
|
||||||
|
}
|
229
lib/fischl.cpp
229
lib/fischl.cpp
@ -1,8 +1,227 @@
|
|||||||
#include "fischl.h"
|
#define FUSE_USE_VERSION 31
|
||||||
|
|
||||||
#include <cstdio>
|
#include <fuse.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "fs.hpp"
|
||||||
|
#include "files.h"
|
||||||
|
|
||||||
int fischl::init(){
|
/*
|
||||||
printf("Hello Fischl!");
|
* Command line options
|
||||||
return 3;
|
*
|
||||||
|
* We can't set default values for the char* fields here because
|
||||||
|
* fuse_opt_parse would attempt to free() them when the user specifies
|
||||||
|
* different values on the command line.
|
||||||
|
*/
|
||||||
|
static struct options {
|
||||||
|
RawDisk *H; // Use FakeRawDisk here if memory sanitizer complains
|
||||||
|
Fs *fs;
|
||||||
|
FilesOperation *fsop;
|
||||||
|
int show_help;
|
||||||
|
} options;
|
||||||
|
|
||||||
|
#define OPTION(t, p) \
|
||||||
|
{ t, offsetof(struct options, p), 1 }
|
||||||
|
static const struct fuse_opt option_spec[] = {
|
||||||
|
OPTION("-h", show_help),
|
||||||
|
OPTION("--help", show_help),
|
||||||
|
FUSE_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
void* fischl_init(struct fuse_conn_info *conn, struct fuse_config *cfg) {
|
||||||
|
options.fsop->initialize_rootinode();
|
||||||
|
}
|
||||||
|
|
||||||
|
int fischl_create(const char *path, mode_t mode, struct fuse_file_info *fi) {
|
||||||
|
return options.fsop->fischl_create(path, mode, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fischl_destroy(void* private_data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) {
|
||||||
|
return options.fsop->fischl_getattr(path, stbuf, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_access(const char* path, int mask) {
|
||||||
|
|
||||||
|
// return 0 when access is allowed
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_readlink(const char* path, char* buf, size_t size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_opendir(const char* path, struct fuse_file_info* fi) {
|
||||||
|
u_int64_t fh = options.fsop->namei(path);
|
||||||
|
fi->fh = fh;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t ft, struct fuse_file_info *fi, enum fuse_readdir_flags flg) {
|
||||||
|
return options.fsop->fischl_readdir(path, buf, filler, ft, fi, flg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_mknod(const char* path, mode_t mode, dev_t rdev) {
|
||||||
|
return options.fsop->fischl_mknod(path, mode, rdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_mkdir(const char *path, mode_t mode) {
|
||||||
|
return options.fsop->fischl_mkdir(path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_unlink(const char* path) {
|
||||||
|
return options.fsop->fischl_unlink(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_rmdir(const char* path) {
|
||||||
|
return options.fsop->fischl_rmdir(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_symlink(const char* to, const char* from) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_rename(const char *path, const char *new_name, unsigned int flags) {
|
||||||
|
return options.fsop->fischl_rename(path, new_name, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_link(const char* from, const char* to) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_chmod(const char *path, mode_t mode, struct fuse_file_info *fi) {
|
||||||
|
return options.fsop->fischl_chmod(path, mode, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info *fi) {
|
||||||
|
return options.fsop->fischl_chown(path, uid, gid, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_truncate(const char *path, off_t offset, struct fuse_file_info *fi) {
|
||||||
|
return options.fsop->fischl_truncate(path, offset, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_open(const char *path, struct fuse_file_info *fi) {
|
||||||
|
return options.fsop->fischl_open(path, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_read(const char* path, char *buf, size_t size, off_t offset, struct fuse_file_info* fi) {
|
||||||
|
return options.fsop->fischl_read(path, buf, size, offset, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
|
||||||
|
return options.fsop->fischl_write(path, buf, size, offset, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_statfs(const char* path, struct statvfs* stbuf) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_release(const char* path, struct fuse_file_info *fi) {
|
||||||
|
return options.fsop->fischl_release(path, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fischl_releasedir(const char* path, struct fuse_file_info *fi) {
|
||||||
|
return options.fsop->fischl_releasedir(path, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const struct fuse_operations fischl_oper = {
|
||||||
|
|
||||||
|
|
||||||
|
.getattr = fischl_getattr,
|
||||||
|
//.readlink = fischl_readlink,
|
||||||
|
.mknod = fischl_mknod,
|
||||||
|
.mkdir = fischl_mkdir,
|
||||||
|
.unlink = fischl_unlink,
|
||||||
|
.rmdir = fischl_rmdir,
|
||||||
|
//.symlink = fischl_symlink,
|
||||||
|
.rename = fischl_rename,
|
||||||
|
//.link = fischl_link,
|
||||||
|
.chmod = fischl_chmod,
|
||||||
|
.chown = fischl_chown,
|
||||||
|
.truncate = fischl_truncate,
|
||||||
|
.open = fischl_open,
|
||||||
|
.read = fischl_read,
|
||||||
|
.write = fischl_write,
|
||||||
|
//.statfs = fischl_statfs,
|
||||||
|
.release = fischl_release,
|
||||||
|
/*
|
||||||
|
#ifdef HAVE_SETXATTR
|
||||||
|
.setxattr = fischl_setxattr,
|
||||||
|
.getxattr = fischl_getxattr,
|
||||||
|
.listxattr = fischl_listxattr,
|
||||||
|
.removexattr = fischl_removexattr,
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
.opendir = fischl_opendir,
|
||||||
|
.readdir = fischl_readdir,
|
||||||
|
.releasedir = fischl_releasedir,
|
||||||
|
.init = fischl_init,
|
||||||
|
.destroy = fischl_destroy,
|
||||||
|
.access = fischl_access,
|
||||||
|
.create = fischl_create,
|
||||||
|
//.utimens = fischl_utimens,
|
||||||
|
//.bmap = fischl_bmap,
|
||||||
|
//.ioctl = fischl_ioctl,
|
||||||
|
//.poll = fischl_poll,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void show_help(const char *progname)
|
||||||
|
{
|
||||||
|
printf("usage: %s [options] <mountpoint>\n\n", progname);
|
||||||
|
printf("File-system specific options:\n"
|
||||||
|
" --name=<s> Name of the \"fischl\" file\n"
|
||||||
|
" (default: \"fischl\")\n"
|
||||||
|
" --contents=<s> Contents \"fischl\" file\n"
|
||||||
|
" (default \"fischl, World!\\n\")\n"
|
||||||
|
"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int fischl(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||||
|
srand(time(NULL)); // Seed the random number generator
|
||||||
|
//const char* d = (argc < 2) ? "/dev/vdc" : argv[1];
|
||||||
|
|
||||||
|
//setupTestDirectory(&options.root);
|
||||||
|
options.H = new FakeRawDisk(23552);
|
||||||
|
options.fs = new Fs(options.H);
|
||||||
|
options.fs->format();
|
||||||
|
options.fsop = new FilesOperation(*options.H, options.fs);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse options */
|
||||||
|
if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* When --help is specified, first print our own file-system
|
||||||
|
specific help text, then signal fuse_main to show
|
||||||
|
additional help (by adding `--help` to the options again)
|
||||||
|
without usage: line (by setting argv[0] to the empty
|
||||||
|
string) */
|
||||||
|
if (options.show_help) {
|
||||||
|
show_help(argv[0]);
|
||||||
|
assert(fuse_opt_add_arg(&args, "--help") == 0);
|
||||||
|
args.argv[0][0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fuse_main(args.argc, args.argv, &fischl_oper, NULL);
|
||||||
|
fuse_opt_free_args(&args);
|
||||||
|
return ret;
|
||||||
}
|
}
|
@ -1,7 +1,5 @@
|
|||||||
#include "fs.hpp"
|
#include "fs.hpp"
|
||||||
|
|
||||||
const u_int64_t INDIRECT_BLOCKS = IO_BLOCK_SIZE / sizeof(u_int64_t);
|
|
||||||
|
|
||||||
class DatablockOperation {
|
class DatablockOperation {
|
||||||
public:
|
public:
|
||||||
DatablockOperation(int (*_skip)(DatablockOperation *, u_int64_t) = nullptr)
|
DatablockOperation(int (*_skip)(DatablockOperation *, u_int64_t) = nullptr)
|
||||||
@ -302,6 +300,9 @@ ssize_t Fs::read(INode_Data *inode_data, char buf[], size_t count,
|
|||||||
size_t offset) {
|
size_t offset) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
if (offset >= inode_data->metadata.size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
u_int64_t start_block_index = offset / IO_BLOCK_SIZE;
|
u_int64_t start_block_index = offset / IO_BLOCK_SIZE;
|
||||||
size_t internal_offset = offset - (start_block_index * IO_BLOCK_SIZE);
|
size_t internal_offset = offset - (start_block_index * IO_BLOCK_SIZE);
|
||||||
|
|
||||||
|
214
lib/main.cpp
214
lib/main.cpp
@ -8,7 +8,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
int main() {
|
int main(int argc, char *argv[]) {
|
||||||
// printf("hello word!");
|
// printf("hello word!");
|
||||||
// fischl *F = new fischl;
|
// fischl *F = new fischl;
|
||||||
// F->init();
|
// F->init();
|
||||||
@ -170,106 +170,142 @@ int main() {
|
|||||||
// err = fs->lseek_next_hole(&inode_data, offs + 100000);
|
// err = fs->lseek_next_hole(&inode_data, offs + 100000);
|
||||||
// printf("lseek_next_hole (%d): %d\n\n", offs + 100000, err);
|
// printf("lseek_next_hole (%d): %d\n\n", offs + 100000, err);
|
||||||
|
|
||||||
// RawDisk *disk = new FakeRawDisk(2048);
|
RawDisk *disk = new FakeRawDisk(5000);
|
||||||
// Fs *fs = new Fs(disk);
|
|
||||||
// fs->format();
|
|
||||||
|
|
||||||
// INode_Data inode_data;
|
|
||||||
// fs->inode_manager->new_inode(1, 2, 3, &inode_data);
|
|
||||||
|
|
||||||
// char cwd_buf[PATH_MAX];
|
|
||||||
// int fd;
|
|
||||||
|
|
||||||
// assert(getcwd(cwd_buf, sizeof(cwd_buf)) != NULL);
|
|
||||||
|
|
||||||
// printf("\n\n(");
|
|
||||||
|
|
||||||
// printf(cwd_buf);
|
|
||||||
|
|
||||||
// printf(")\n\n");
|
|
||||||
|
|
||||||
// fd = open("/home/connor", O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
|
|
||||||
// assert(fd != -1);
|
|
||||||
|
|
||||||
// u_int64_t test_start_range = IO_BLOCK_SIZE * 100;
|
|
||||||
// u_int64_t test_write_range = IO_BLOCK_SIZE * 50;
|
|
||||||
// u_int64_t test_read_range = IO_BLOCK_SIZE * 50;
|
|
||||||
|
|
||||||
// char zeros[test_write_range] = {0};
|
|
||||||
// char ones[test_write_range];
|
|
||||||
// memset(ones, 1, test_write_range);
|
|
||||||
|
|
||||||
// char *write_buf = ones;
|
|
||||||
// char reference_read_buf[test_read_range];
|
|
||||||
// char test_read_buf[test_read_range];
|
|
||||||
// size_t offset, count;
|
|
||||||
// int test_res, ref_res;
|
|
||||||
|
|
||||||
// for (int i = 0; i < 1000; ++i) {
|
|
||||||
// offset = rand() & test_start_range;
|
|
||||||
|
|
||||||
// switch (rand() % 2) {
|
|
||||||
// case 0:
|
|
||||||
// count = rand() & test_write_range;
|
|
||||||
// write_buf = (write_buf == ones) ? zeros : ones;
|
|
||||||
// printf("write: %ds count=%d offset=%d\n", write_buf[0], count, offset);
|
|
||||||
// test_res = fs->write(&inode_data, write_buf, count, offset);
|
|
||||||
// assert(lseek(fd, offset, SEEK_SET) == offset);
|
|
||||||
// ref_res = write(fd, write_buf, count);
|
|
||||||
// break;
|
|
||||||
// case 1:
|
|
||||||
// count = rand() & test_read_range;
|
|
||||||
// printf("read: count=%d offset=%d\n", count, offset);
|
|
||||||
// test_res = fs->read(&inode_data, test_read_buf, count, offset);
|
|
||||||
// assert(lseek(fd, offset, SEEK_SET) == offset);
|
|
||||||
// ref_res = read(fd, reference_read_buf, count);
|
|
||||||
// bool reads_are_equal = true;
|
|
||||||
// for (size_t j = 0; j < count; ++j)
|
|
||||||
// if (test_read_buf[i] != reference_read_buf[i]) {
|
|
||||||
// reads_are_equal = false;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// assert(reads_are_equal);
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// assert(test_res == ref_res);
|
|
||||||
// }
|
|
||||||
|
|
||||||
RawDisk *disk = new FakeRawDisk(5120);
|
|
||||||
Fs *fs = new Fs(disk);
|
Fs *fs = new Fs(disk);
|
||||||
fs->format();
|
fs->format();
|
||||||
|
|
||||||
int buf_size = IO_BLOCK_SIZE * 200;
|
|
||||||
int loops = 14 * 1024 * 1024 / buf_size;
|
|
||||||
|
|
||||||
char buf[buf_size];
|
|
||||||
|
|
||||||
memset(buf, 1, sizeof(buf));
|
|
||||||
|
|
||||||
INode_Data inode_data;
|
INode_Data inode_data;
|
||||||
fs->inode_manager->new_inode(1, 2, 3, &inode_data);
|
fs->inode_manager->new_inode(1, 2, 3, &inode_data);
|
||||||
|
|
||||||
int res;
|
char cwd_buf[PATH_MAX];
|
||||||
|
int fd;
|
||||||
|
|
||||||
for (int j = 0; j < loops; ++j) {
|
assert(getcwd(cwd_buf, sizeof(cwd_buf)) != NULL);
|
||||||
res = fs->write(&inode_data, buf, sizeof(buf), sizeof(buf) * j);
|
|
||||||
printf("write: %d j=%d\n", res, j);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < loops; ++j) {
|
fd = open("/tmp", O_TMPFILE | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
|
||||||
|
assert(fd != -1);
|
||||||
|
|
||||||
memset(buf, 0, sizeof(buf));
|
u_int64_t test_start_range = IO_BLOCK_SIZE * 3584;
|
||||||
res = fs->read(&inode_data, buf, sizeof(buf), sizeof(buf) * j);
|
u_int64_t test_io_range = IO_BLOCK_SIZE * 100;
|
||||||
|
|
||||||
printf("read: %d j=%d\n", res, j);
|
char ones[test_io_range];
|
||||||
|
memset(ones, 1, test_io_range);
|
||||||
|
char twos[test_io_range];
|
||||||
|
memset(twos, 2, test_io_range);
|
||||||
|
|
||||||
for (int i = 0; i < sizeof(buf); ++i)
|
char *write_buf = ones;
|
||||||
if (buf[1] != 1) {
|
char reference_read_buf[test_io_range];
|
||||||
printf("error: %d\n", i);
|
char test_read_buf[test_io_range];
|
||||||
return -1;
|
size_t offset, count;
|
||||||
|
int test_res, ref_res;
|
||||||
|
bool reads_are_equal;
|
||||||
|
bool canwrite;
|
||||||
|
|
||||||
|
size_t weird_offset = 6508064;
|
||||||
|
|
||||||
|
for (int i = 0; i < 100000; ++i) {
|
||||||
|
offset = rand() % test_start_range;
|
||||||
|
|
||||||
|
reads_are_equal = true;
|
||||||
|
|
||||||
|
switch (rand() % 3) {
|
||||||
|
case 0:
|
||||||
|
count = rand() % test_io_range;
|
||||||
|
write_buf = (write_buf == ones) ? twos : ones;
|
||||||
|
if (offset <= weird_offset && (count + offset) > weird_offset)
|
||||||
|
printf("write: %ds count=%d offset=%d\n", write_buf[0], count, offset);
|
||||||
|
test_res = fs->write(&inode_data, write_buf, count, offset);
|
||||||
|
assert(lseek(fd, offset, SEEK_SET) == offset);
|
||||||
|
ref_res = write(fd, write_buf, count);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
count = rand() % test_io_range;
|
||||||
|
if (offset <= weird_offset && (count + offset) > weird_offset)
|
||||||
|
printf("read: count=%d offset=%d\n", count, offset);
|
||||||
|
test_res = fs->read(&inode_data, test_read_buf, count, offset);
|
||||||
|
assert(lseek(fd, offset, SEEK_SET) == offset);
|
||||||
|
ref_res = read(fd, reference_read_buf, count);
|
||||||
|
for (size_t j = 0; j < count; ++j)
|
||||||
|
if (test_read_buf[i] != reference_read_buf[i]) {
|
||||||
|
reads_are_equal = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (offset <= weird_offset)
|
||||||
|
printf("truncate: length=%d\n", offset);
|
||||||
|
test_res = fs->truncate(&inode_data, offset);
|
||||||
|
ref_res = ftruncate(fd, offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("test_res=%d, ref_res=%d\n", test_res, ref_res);
|
||||||
|
assert(test_res == ref_res);
|
||||||
|
|
||||||
|
if (!reads_are_equal && count > 0) {
|
||||||
|
int prev_test = test_read_buf[0], prev_ref = reference_read_buf[0],
|
||||||
|
same_count = 1;
|
||||||
|
for (size_t j = 1; j < count; ++j) {
|
||||||
|
u_int64_t byte_index = (j + offset);
|
||||||
|
if (byte_index % IO_BLOCK_SIZE == 0)
|
||||||
|
printf("Block: %d\n", byte_index / IO_BLOCK_SIZE);
|
||||||
|
if (prev_test != test_read_buf[j] ||
|
||||||
|
prev_ref != reference_read_buf[j]) {
|
||||||
|
printf("rt %d %d%s\n", prev_ref, prev_test,
|
||||||
|
(prev_test != prev_ref)
|
||||||
|
? " -----DIFF----- -----DIFF----- -----DIFF-----"
|
||||||
|
: "");
|
||||||
|
printf("^^^^ same for %d bytes ending at %d, starting at %d ^^^^\n",
|
||||||
|
same_count, byte_index, byte_index - same_count);
|
||||||
|
prev_test = test_read_buf[j];
|
||||||
|
prev_ref = reference_read_buf[j];
|
||||||
|
same_count = 1;
|
||||||
|
} else {
|
||||||
|
same_count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
printf("rt %d %d%s\n", prev_test, prev_test,
|
||||||
|
(prev_test != prev_ref)
|
||||||
|
? " -----DIFF----- -----DIFF----- -----DIFF-----"
|
||||||
|
: "");
|
||||||
|
printf("^^^^ same for %d bytes ^^^^\n", same_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(reads_are_equal);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
// RawDisk *disk = new FakeRawDisk(5120);
|
||||||
|
// Fs *fs = new Fs(disk);
|
||||||
|
// fs->format();
|
||||||
|
|
||||||
|
// int buf_size = IO_BLOCK_SIZE * 200;
|
||||||
|
// int loops = 14 * 1024 * 1024 / buf_size;
|
||||||
|
|
||||||
|
// char buf[buf_size];
|
||||||
|
|
||||||
|
// memset(buf, 1, sizeof(buf));
|
||||||
|
|
||||||
|
// INode_Data inode_data;
|
||||||
|
// fs->inode_manager->new_inode(1, 2, 3, &inode_data);
|
||||||
|
|
||||||
|
// int res;
|
||||||
|
|
||||||
|
// for (int j = 0; j < loops; ++j) {
|
||||||
|
// res = fs->write(&inode_data, buf, sizeof(buf), sizeof(buf) * j);
|
||||||
|
// printf("write: %d j=%d\n", res, j);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (int j = 0; j < loops; ++j) {
|
||||||
|
|
||||||
|
// memset(buf, 0, sizeof(buf));
|
||||||
|
// res = fs->read(&inode_data, buf, sizeof(buf), sizeof(buf) * j);
|
||||||
|
|
||||||
|
// printf("read: %d j=%d\n", res, j);
|
||||||
|
|
||||||
|
// for (int i = 0; i < sizeof(buf); ++i)
|
||||||
|
// if (buf[1] != 1) {
|
||||||
|
// printf("error: %d\n", i);
|
||||||
|
// return -1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
@ -1,34 +1,69 @@
|
|||||||
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_LAYER1_TEST1 test_layer1_test1)
|
# set(TARGET_LAYER2_API test_layer2_API)
|
||||||
set(DIR_PLACE /dev/vdb)
|
# set(TARGET_DIR_API test_dir_API)
|
||||||
|
# set(DIR_PLACE /dev/vdb)
|
||||||
|
# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address")
|
||||||
|
|
||||||
# add test sources here ...
|
# # add test sources here ...
|
||||||
add_executable(${TARGET_LAYER0}
|
# add_executable(${TARGET_LAYER0}
|
||||||
# add need lib and source code here
|
|
||||||
layer0.cpp
|
|
||||||
|
|
||||||
../lib/rawdisk.cpp
|
|
||||||
|
|
||||||
)
|
|
||||||
add_executable(${TARGET_LAYER1_API}
|
|
||||||
# add need lib and source code here
|
|
||||||
layer1_API.cpp
|
|
||||||
|
|
||||||
../lib/fischl.cpp
|
|
||||||
../lib/rawdisk.cpp
|
|
||||||
../lib/fs/datablock_manager.cpp
|
|
||||||
../lib/fs/fs_data_types.cpp
|
|
||||||
../lib/fs/fs_file_io.cpp
|
|
||||||
../lib/fs/fs.cpp
|
|
||||||
../lib/fs/inode_manager.cpp
|
|
||||||
)
|
|
||||||
# add_executable(${TARGET_LAYER1_TEST1}
|
|
||||||
# # add need lib and source code here
|
# # add need lib and source code here
|
||||||
# layer1_test1.cpp
|
# layer0.cpp
|
||||||
|
|
||||||
|
# ../lib/rawdisk.cpp
|
||||||
|
|
||||||
|
# )
|
||||||
|
# add_executable(${TARGET_LAYER1_API}
|
||||||
|
# # add need lib and source code here
|
||||||
|
# layer1_API.cpp
|
||||||
|
# ../lib/rawdisk.cpp
|
||||||
|
# ../lib/fs/datablock_manager.cpp
|
||||||
|
# ../lib/fs/fs_data_types.cpp
|
||||||
|
# ../lib/fs/fs_file_io.cpp
|
||||||
|
# ../lib/fs/fs.cpp
|
||||||
|
# ../lib/fs/inode_manager.cpp
|
||||||
|
# )
|
||||||
|
# add_executable(${TARGET_LAYER2_API}
|
||||||
|
# ../lib/direntry.cpp
|
||||||
|
# ../lib/rawdisk.cpp
|
||||||
|
# ../lib/fs/datablock_manager.cpp
|
||||||
|
# ../lib/fs/fs_data_types.cpp
|
||||||
|
# ../lib/fs/fs_file_io.cpp
|
||||||
|
# ../lib/fs/fs.cpp
|
||||||
|
# ../lib/fs/inode_manager.cpp
|
||||||
|
# ../lib/files.cpp
|
||||||
|
# layer2_API_dir.cpp
|
||||||
|
# )
|
||||||
|
# add_executable(${TARGET_DIR_API}
|
||||||
|
# ../lib/direntry.cpp
|
||||||
|
# ../lib/rawdisk.cpp
|
||||||
|
# ../lib/fs/datablock_manager.cpp
|
||||||
|
# ../lib/fs/fs_data_types.cpp
|
||||||
|
# ../lib/fs/fs_file_io.cpp
|
||||||
|
# ../lib/fs/fs.cpp
|
||||||
|
# ../lib/fs/inode_manager.cpp
|
||||||
|
# dir_API.cpp
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# add test to activate ctest -VV
|
# # Link Google Test to your test executables
|
||||||
add_test(NAME ${TARGET_LAYER0} COMMAND sudo ./${TARGET_LAYER0} ${DIR_PLACE})
|
# target_link_libraries(${TARGET_LAYER0} gtest gtest_main)
|
||||||
add_test(NAME ${TARGET_LAYER1_API} COMMAND sudo ./${TARGET_LAYER1_API} ${DIR_PLACE})
|
# target_link_libraries(${TARGET_LAYER1_API} gtest gtest_main)
|
||||||
# add_test(NAME ${TARGET_LAYER1_TEST1} COMMAND sudo ./${TARGET_LAYER1_TEST1} ${DIR_PLACE})
|
# target_link_libraries(${TARGET_DIR_API} gtest gtest_main)
|
||||||
|
|
||||||
|
# # 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_LAYER2_API} COMMAND sudo ./${TARGET_LAYER2_API} ${DIR_PLACE})
|
||||||
|
# add_test(NAME ${TARGET_DIR_API} COMMAND sudo ./${TARGET_DIR_API} ${DIR_PLACE})
|
||||||
|
|
||||||
|
|
||||||
|
# # Add the -Wall flag
|
||||||
|
# target_compile_options(${TARGET_LAYER2_API} PRIVATE -Wall)
|
||||||
|
|
||||||
|
# # Use pkg-config to get flags for fuse3
|
||||||
|
# find_package(PkgConfig REQUIRED)
|
||||||
|
# pkg_search_module(FUSE3 REQUIRED fuse3)
|
||||||
|
|
||||||
|
# # Add the flags from pkg-config for fuse3
|
||||||
|
# target_include_directories(${TARGET_LAYER2_API} PRIVATE ${FUSE3_INCLUDE_DIRS})
|
||||||
|
# target_link_libraries(${TARGET_LAYER2_API} PRIVATE ${FUSE3_LIBRARIES} gtest gtest_main)
|
350
test/dir_API.cpp
Normal file
350
test/dir_API.cpp
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
/***********************************************************
|
||||||
|
Directory owns treeNode and FileNode structure, detect S_IFDIR to make treeNode or not (see add_entry Function)
|
||||||
|
File owns FileNode structure only, detect !S_IFDIR
|
||||||
|
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "fs.hpp"
|
||||||
|
#include "direntry.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;
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DirTest, root_test) {
|
||||||
|
//Init fake root directory
|
||||||
|
INode_Data inode_root;
|
||||||
|
u_int64_t file_permissions = 0;
|
||||||
|
inode_root.metadata.permissions = file_permissions | S_IFDIR;
|
||||||
|
root = fischl_init_entry(0, mock_root->name, &inode_root);//0 is inode number assigned by inode_allocate()
|
||||||
|
}
|
||||||
|
TEST(DirTest, AddFile_test) {
|
||||||
|
//assume file and dir itself(content,metadata) same,but different name and inode number
|
||||||
|
INode_Data inode_file;
|
||||||
|
INode_Data inode_dir;
|
||||||
|
u_int64_t file_permissions = 0;
|
||||||
|
file_permissions = 0;
|
||||||
|
inode_dir.metadata.permissions = file_permissions | S_IFDIR;
|
||||||
|
fischl_add_entry(root, 2, mock_root->inFile->name,&inode_file);
|
||||||
|
fischl_add_entry(root, 3, mock_root->subdir->name,&inode_dir);
|
||||||
|
}
|
||||||
|
TEST(DirTest, FindFile_test) {
|
||||||
|
//find file
|
||||||
|
target_filepath = std::string("/") + mock_root->inFile->name;
|
||||||
|
FileNode *get_file = fischl_find_entry(root,target_filepath.c_str());
|
||||||
|
EXPECT_TRUE(get_file != NULL);
|
||||||
|
EXPECT_STREQ(get_file->name, mock_root->inFile->name);
|
||||||
|
//find dir
|
||||||
|
target_filepath = std::string("/") + mock_root->subdir->name + "/";
|
||||||
|
FileNode *get_dir = fischl_find_entry(root,target_filepath.c_str());
|
||||||
|
EXPECT_TRUE(get_dir != NULL);//detect this should find success
|
||||||
|
EXPECT_STREQ(get_dir->name, mock_root->subdir->name);
|
||||||
|
ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory
|
||||||
|
//check . function
|
||||||
|
get_dir = fischl_find_entry(root,"./");
|
||||||
|
EXPECT_TRUE(get_dir != NULL);//detect this should find success
|
||||||
|
EXPECT_STREQ(get_dir->name, mock_root->name);
|
||||||
|
ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory
|
||||||
|
//check .. function
|
||||||
|
get_dir = fischl_find_entry(root,"..");
|
||||||
|
EXPECT_TRUE(get_dir != NULL);//detect this should find success
|
||||||
|
EXPECT_STREQ(get_dir->name, mock_root->name);
|
||||||
|
ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory
|
||||||
|
}
|
||||||
|
TEST(DirTest, Add_FindFile_test) {
|
||||||
|
//add file and dir under subdirectory instead of root
|
||||||
|
INode_Data inode_file;
|
||||||
|
INode_Data inode_dir;
|
||||||
|
u_int64_t file_permissions = 0;
|
||||||
|
file_permissions = 0;
|
||||||
|
inode_dir.metadata.permissions = file_permissions | S_IFDIR;
|
||||||
|
|
||||||
|
/*add with subdirectory*/
|
||||||
|
//Treenode dir(you cannot find here), you only can get Filenode dir based on fischl_find_entry Function
|
||||||
|
//So use Filenode->subdirectory will point to the treenode dir, then can add files
|
||||||
|
target_filepath = std::string("/") + mock_root->subdir->name + "/";
|
||||||
|
FileNode *get_dir = fischl_find_entry(root, target_filepath.c_str());
|
||||||
|
fischl_add_entry(get_dir->subdirectory, 4, mock_root->subdir->inFile->name, &inode_file);
|
||||||
|
|
||||||
|
//verfication treeNode and Filenode relationship
|
||||||
|
target_filepath = std::string("/") + mock_root->subdir->name + "/" + mock_root->subdir->inFile->name;
|
||||||
|
TreeNode *get_dir_tree = find_parentPath(root,target_filepath.c_str());
|
||||||
|
ASSERT_TRUE(get_dir_tree == get_dir->subdirectory);//treeNode dir should be same as treeNode subdir in that Filenode
|
||||||
|
|
||||||
|
//two Ways to get File(include dir itself) information
|
||||||
|
FileNode *get_file = NULL;
|
||||||
|
//1. absolute path, the root(treeNode) will always exist when initialize
|
||||||
|
get_file = fischl_find_entry(root,target_filepath.c_str());
|
||||||
|
EXPECT_TRUE(get_file != NULL);
|
||||||
|
EXPECT_STREQ(get_file->name,mock_root->subdir->inFile->name);
|
||||||
|
//2. relative path, the get_dir(FileNode)->subdirectory(treeNode), use treeNode(dir) to find
|
||||||
|
target_filepath = std::string("/") + mock_root->subdir->inFile->name;
|
||||||
|
get_file = fischl_find_entry(get_dir->subdirectory,target_filepath.c_str());
|
||||||
|
EXPECT_TRUE(get_file != NULL);
|
||||||
|
EXPECT_STREQ(get_file->name, mock_root->subdir->inFile->name);
|
||||||
|
/**********************************************************/
|
||||||
|
//add one more file under fist subdir
|
||||||
|
fischl_add_entry(get_dir->subdirectory, 5, mock_root->subdir->inFile->next->name, &inode_file);
|
||||||
|
//add one more directory under fist subdir
|
||||||
|
fischl_add_entry(get_dir->subdirectory, 6, mock_root->subdir->subdir->name, &inode_dir);
|
||||||
|
//find
|
||||||
|
target_filepath = std::string("./") + mock_root->subdir->inFile->next->name;
|
||||||
|
get_file = fischl_find_entry(get_dir->subdirectory, target_filepath.c_str());
|
||||||
|
EXPECT_TRUE(get_file != NULL);
|
||||||
|
EXPECT_STREQ(get_file->name, mock_root->subdir->inFile->next->name);
|
||||||
|
//use .. from fist subdir to find file1
|
||||||
|
target_filepath = std::string("../") + mock_root->inFile->name;
|
||||||
|
get_file = fischl_find_entry(get_dir->subdirectory,target_filepath.c_str());
|
||||||
|
EXPECT_TRUE(get_file != NULL);
|
||||||
|
EXPECT_STREQ(get_file->name,mock_root->inFile->name);
|
||||||
|
//check fist subdir with .
|
||||||
|
get_dir = fischl_find_entry(get_dir->subdirectory,".");
|
||||||
|
EXPECT_TRUE(get_dir != NULL);//detect this should find success
|
||||||
|
EXPECT_STREQ(get_dir->name, mock_root->subdir->name);
|
||||||
|
ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory
|
||||||
|
//check root via fist subdir
|
||||||
|
get_dir = fischl_find_entry(get_dir->subdirectory,"..");
|
||||||
|
EXPECT_TRUE(get_dir != NULL);//detect this should find success
|
||||||
|
EXPECT_STREQ(get_dir->name, mock_root->name);
|
||||||
|
ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory
|
||||||
|
//use .. to access parent directory
|
||||||
|
target_filepath = std::string("/") + mock_root->subdir->name + "/" + mock_root->subdir->subdir->name + "/..";
|
||||||
|
get_dir = fischl_find_entry(root, target_filepath.c_str());
|
||||||
|
EXPECT_TRUE(get_dir != NULL);
|
||||||
|
EXPECT_STREQ(get_dir->name, mock_root->subdir->name);
|
||||||
|
target_filepath = std::string("/") + mock_root->subdir->name + "/" + mock_root->subdir->subdir->name + "/../..";
|
||||||
|
get_file = fischl_find_entry(root, target_filepath.c_str());
|
||||||
|
EXPECT_TRUE(get_file != NULL);
|
||||||
|
EXPECT_STREQ(get_file->name, mock_root->name);
|
||||||
|
EXPECT_TRUE(get_file->subdirectory != NULL);
|
||||||
|
EXPECT_TRUE(get_file->subdirectory->self_info == get_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEST(DirTest, Scale_test){
|
||||||
|
// INode_Data inode_file;
|
||||||
|
// INode_Data inode_dir;
|
||||||
|
// u_int64_t file_permissions = 0;
|
||||||
|
// file_permissions = 0;
|
||||||
|
// inode_dir.permissions = file_permissions | S_IFDIR;
|
||||||
|
// dir_test* temp = mock_root;
|
||||||
|
// // First loop: Add files and subdirectories under root
|
||||||
|
// file_test* currentFile = temp->inFile;
|
||||||
|
// dir_test* currentSubdir = temp->subdir;
|
||||||
|
//
|
||||||
|
// for (int i = 1; i < 7; ++i) {
|
||||||
|
// if (currentFile) {
|
||||||
|
// //add can still add the same filename and dir name, but it will be linked behind the first added
|
||||||
|
// fischl_add_entry(root, i, currentFile->name, &inode_file);
|
||||||
|
// currentFile = currentFile->next;
|
||||||
|
// }
|
||||||
|
// if (currentSubdir) {
|
||||||
|
// fischl_add_entry(root, i + 1, currentSubdir->name, &inode_dir);
|
||||||
|
// currentSubdir = currentSubdir->next;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Second loop: Process each subdir under root
|
||||||
|
// temp = mock_root->subdir;
|
||||||
|
// while (temp) {
|
||||||
|
// target_filepath = "/" + std::string(temp->name) + "/";
|
||||||
|
// FileNode* get_dir = fischl_find_entry(root, target_filepath.c_str());
|
||||||
|
|
||||||
|
// ASSERT_TRUE(get_dir != NULL);
|
||||||
|
// EXPECT_STREQ(get_dir->name, temp->name);
|
||||||
|
// ASSERT_TRUE(get_dir->subdirectory != NULL);
|
||||||
|
// // Add files and subdirectories in each subdir
|
||||||
|
// file_test* currentFile = temp->inFile;
|
||||||
|
// dir_test* currentSubSubdir = temp->subdir;
|
||||||
|
// for (int j = 7; j < 13; ++j) {
|
||||||
|
// if (currentFile) {
|
||||||
|
// fischl_add_entry(get_dir->subdirectory, j, currentFile->name, &inode_file);
|
||||||
|
// currentFile = currentFile->next;
|
||||||
|
// }
|
||||||
|
// if (currentSubSubdir) {
|
||||||
|
// fischl_add_entry(get_dir->subdirectory, j + 1, currentSubSubdir->name, &inode_dir);
|
||||||
|
// currentSubSubdir = currentSubSubdir->next;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// temp = temp->next; // Move to next subdir
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
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);//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;
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
// const char* d = (argc < 2) ? "/dev/vdc" : argv[1];
|
// const char* d = (argc < 2) ? "/dev/vdc" : argv[1];
|
||||||
|
|
||||||
RawDisk *H = new FakeRawDisk(2048);
|
RawDisk *H = new FakeRawDisk(21504);
|
||||||
Fs *fs = new Fs(H);
|
Fs *fs = new Fs(H);
|
||||||
|
|
||||||
printf("test inode\n");
|
printf("test inode\n");
|
||||||
@ -65,12 +65,16 @@ int main(int argc, char *argv[]) {
|
|||||||
1); // the first 8 bytes of 4k I/O block will store
|
1); // the first 8 bytes of 4k I/O block will store
|
||||||
// the next address(after 2048*4k I/O block)
|
// the next address(after 2048*4k I/O block)
|
||||||
// test the end of the datablock
|
// test the end of the datablock
|
||||||
// H->read_block(NUM_BLOCKS - DATABLOCKS_PER_BITMAP_BLOCK - 1, buffer);
|
|
||||||
// t = 0;
|
|
||||||
// for (int j = 0; j < 8; j++)
|
|
||||||
// t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j);
|
|
||||||
|
|
||||||
// assert(t == NUM_BLOCKS - DATABLOCKS_PER_BITMAP_BLOCK - 1);
|
H->read_block(fs->disk->diskSize / IO_BLOCK_SIZE -
|
||||||
|
DATABLOCKS_PER_BITMAP_BLOCK - 1,
|
||||||
|
buffer);
|
||||||
|
t = 0;
|
||||||
|
for (int j = 0; j < 8; j++)
|
||||||
|
t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j);
|
||||||
|
|
||||||
|
assert(t ==
|
||||||
|
fs->disk->diskSize / IO_BLOCK_SIZE - DATABLOCKS_PER_BITMAP_BLOCK - 1);
|
||||||
|
|
||||||
/***************************test inode
|
/***************************test inode
|
||||||
* de/allocation**********************************/
|
* de/allocation**********************************/
|
||||||
|
120
test/layer2_API.cpp
Normal file
120
test/layer2_API.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#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
|
||||||
|
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);
|
||||||
|
fsop.printDirectory(1);
|
||||||
|
u_int64_t file3 = fsop.fischl_mkdir("/foo/bar",0);
|
||||||
|
printf("/foo/bar is inode %llu, it is a directory\n", file3);
|
||||||
|
fsop.printDirectory(file2);
|
||||||
|
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);
|
||||||
|
u_int64_t f4 = fsop.fischl_mkdir("/test",0);
|
||||||
|
u_int64_t f5 = fsop.fischl_mkdir("/foo/bar",0);
|
||||||
|
u_int64_t f6 = fsop.fischl_mkdir("/foo/bar/..",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);
|
||||||
|
buffer[0] = '5';
|
||||||
|
fsop.write_datablock(inode, 101, 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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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');
|
||||||
|
fsop.read_datablock(inode_read, 101, read_buffer);
|
||||||
|
assert(read_buffer[0] == '5');
|
||||||
|
|
||||||
|
// pressure test create directory
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
373
test/layer2_API_dir.cpp
Normal file
373
test/layer2_API_dir.cpp
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
#define FUSE_USE_VERSION 31
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "fs.hpp"
|
||||||
|
#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
|
||||||
|
std::string target_filepath;
|
||||||
|
dir_test* mock_root = nullptr;
|
||||||
|
RawDisk *H; // Use FakeRawDisk here if memory sanitizer complains
|
||||||
|
Fs *fs;
|
||||||
|
FilesOperation *fsop;
|
||||||
|
std::string prefix = "/pressure/No_";
|
||||||
|
|
||||||
|
int total_dir_num = 0;
|
||||||
|
int total_file_num = 0;
|
||||||
|
int total_free_dir = 0;
|
||||||
|
int total_free_file = 0;
|
||||||
|
|
||||||
|
TEST(FileOperationTest, MkdirnodTest) {
|
||||||
|
|
||||||
|
fsop->initialize_rootinode();
|
||||||
|
struct fuse_file_info fi;
|
||||||
|
|
||||||
|
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_create("/test", mode, &fi), 0);
|
||||||
|
EXPECT_EQ(fsop->fischl_mkdir("/foo", mode), 0);
|
||||||
|
EXPECT_EQ(fsop->fischl_mkdir("/foo/bar", mode),0);
|
||||||
|
fsop->printDirectory(1);
|
||||||
|
EXPECT_EQ(fsop->fischl_create("/foo/bar/baz", mode, &fi), 0);
|
||||||
|
fsop->printDirectory(4);
|
||||||
|
// the following three testcases will fail
|
||||||
|
printf("Failing cases\n");
|
||||||
|
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)
|
||||||
|
//get inode info from disk
|
||||||
|
char buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
INode_Data inode;
|
||||||
|
u_int64_t get_disk_inum;
|
||||||
|
struct fuse_file_info fi;
|
||||||
|
|
||||||
|
//file test
|
||||||
|
printf("point 0\n");
|
||||||
|
get_disk_inum = fsop->disk_namei("/test");
|
||||||
|
printf("point 1\n");
|
||||||
|
fsop->fischl_open("/test", &fi);
|
||||||
|
EXPECT_EQ(fi.fh, get_disk_inum);
|
||||||
|
buffer[0] = '1';
|
||||||
|
fsop->fischl_write("/test", buffer, sizeof(buffer), 0, &fi);
|
||||||
|
printf("point 2\n");
|
||||||
|
//other file baz
|
||||||
|
get_disk_inum = fsop->disk_namei("/foo/bar/baz");
|
||||||
|
buffer[0] = '4';
|
||||||
|
fsop->fischl_open("/foo/bar/baz", &fi);
|
||||||
|
EXPECT_EQ(fi.fh, get_disk_inum);
|
||||||
|
fsop->fischl_write("/foo/bar/baz", buffer, sizeof(buffer), 3*IO_BLOCK_SIZE, &fi);
|
||||||
|
buffer[0] = '5';
|
||||||
|
fsop->fischl_write("/foo/bar/baz", buffer, sizeof(buffer), 101*IO_BLOCK_SIZE, &fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileOperationTest, RamDiskTest) {
|
||||||
|
//use find_entry(specify certain files or directory)
|
||||||
|
FileNode* get_dir;
|
||||||
|
u_int64_t get_disk_inum;
|
||||||
|
|
||||||
|
get_dir = fischl_find_entry(fsop->root_node, "/test");//this is file
|
||||||
|
EXPECT_TRUE(get_dir != NULL);//detect this should find success
|
||||||
|
EXPECT_STREQ(get_dir->name, "test");
|
||||||
|
get_disk_inum = fsop->disk_namei("/test");
|
||||||
|
EXPECT_EQ(get_disk_inum, get_dir->inode_number);
|
||||||
|
|
||||||
|
get_dir = fischl_find_entry(fsop->root_node, "/foo/bar/baz");//this is file
|
||||||
|
EXPECT_TRUE(get_dir != NULL);//detect this should find success
|
||||||
|
EXPECT_STREQ(get_dir->name, "baz");
|
||||||
|
get_disk_inum = fsop->disk_namei("/foo/bar/baz");
|
||||||
|
EXPECT_EQ(get_disk_inum, get_dir->inode_number);
|
||||||
|
|
||||||
|
get_dir = fischl_find_entry(fsop->root_node, "/foo/bar/..");
|
||||||
|
EXPECT_TRUE(get_dir != NULL);//detect this should find success
|
||||||
|
EXPECT_STREQ(get_dir->name, "foo");
|
||||||
|
ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory
|
||||||
|
get_disk_inum = fsop->disk_namei("/foo/bar/..");
|
||||||
|
EXPECT_EQ(get_disk_inum, get_dir->inode_number);
|
||||||
|
fsop->printDirectory(get_disk_inum);
|
||||||
|
|
||||||
|
get_dir = fischl_find_entry(fsop->root_node, "/foo/bar/.");
|
||||||
|
EXPECT_TRUE(get_dir != NULL);//detect this should find success
|
||||||
|
EXPECT_STREQ(get_dir->name, "bar");
|
||||||
|
ASSERT_TRUE(get_dir->subdirectory != NULL);//secure it is directory
|
||||||
|
get_disk_inum = fsop->disk_namei("/foo/bar/.");
|
||||||
|
EXPECT_EQ(get_disk_inum, get_dir->inode_number);
|
||||||
|
fsop->printDirectory(get_disk_inum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(FileOperationTest, ReadTest) {
|
||||||
|
// read files (TODO: fischl_read)
|
||||||
|
char read_buffer[IO_BLOCK_SIZE] = {0};
|
||||||
|
INode_Data inode;
|
||||||
|
u_int64_t get_file_inum;
|
||||||
|
|
||||||
|
//read test file
|
||||||
|
get_file_inum = fsop->namei("/test");
|
||||||
|
inode.inode_num = get_file_inum;
|
||||||
|
fs->inode_manager->load_inode(&inode);
|
||||||
|
//fsop->read_datablock(inode, 0, read_buffer);
|
||||||
|
//EXPECT_EQ(read_buffer[0], '1');
|
||||||
|
|
||||||
|
//read test file again with fischl_read API
|
||||||
|
struct fuse_file_info fi;
|
||||||
|
fsop->fischl_open("/test", &fi);
|
||||||
|
EXPECT_EQ(fi.fh, get_file_inum);
|
||||||
|
fsop->fischl_read("/test", read_buffer, sizeof(read_buffer), 0, &fi);
|
||||||
|
EXPECT_EQ(read_buffer[0], '1');
|
||||||
|
|
||||||
|
//read baz file
|
||||||
|
get_file_inum= fsop->namei("/foo/bar/baz");
|
||||||
|
// inode.inode_construct(get_file_inum, *H);
|
||||||
|
// fsop->read_datablock(inode, 3, read_buffer);
|
||||||
|
// EXPECT_EQ(read_buffer[0], '4');
|
||||||
|
// fsop->read_datablock(inode, 101, read_buffer);
|
||||||
|
// EXPECT_EQ(read_buffer[0], '5');
|
||||||
|
|
||||||
|
//read baz file again with fischl_read API
|
||||||
|
fsop->fischl_open("/foo/bar/baz", &fi);
|
||||||
|
EXPECT_EQ(fi.fh, get_file_inum);
|
||||||
|
fsop->fischl_read("/foo/bar/baz", read_buffer, sizeof(read_buffer), 3*IO_BLOCK_SIZE, &fi);
|
||||||
|
EXPECT_EQ(read_buffer[0], '4');
|
||||||
|
fsop->fischl_read("/foo/bar/baz", read_buffer, sizeof(read_buffer), 101*IO_BLOCK_SIZE, &fi);
|
||||||
|
EXPECT_EQ(read_buffer[0], '5');
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileOperationTest, PressureTest) {
|
||||||
|
mode_t mode;//set mode
|
||||||
|
mode = S_IRWXU | S_IRWXG | S_IRWXO;//future should test permission
|
||||||
|
EXPECT_EQ(fsop->fischl_mkdir("/pressure", mode), 0);
|
||||||
|
fsop->printDirectory(1);
|
||||||
|
|
||||||
|
for(int i=0;i<100;i++){
|
||||||
|
EXPECT_EQ(fsop->fischl_mkdir((prefix+std::to_string(i)).c_str(), mode), 0);
|
||||||
|
}
|
||||||
|
fsop->printDirectory(6);
|
||||||
|
for(int i=0;i<100;i++){
|
||||||
|
EXPECT_EQ(fsop->namei((prefix+std::to_string(i)).c_str()),fsop->disk_namei((prefix+std::to_string(i)).c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FileOperationTest, UnlinkTest) {
|
||||||
|
ssize_t file_pressure = fsop->namei("/pressure");
|
||||||
|
fsop->printDirectory(file_pressure);
|
||||||
|
for(int i=0;i<100;i+=2){
|
||||||
|
EXPECT_EQ(fsop->fischl_unlink((prefix+std::to_string(i)).c_str()), 0);
|
||||||
|
}
|
||||||
|
for(int i=0;i<4;i+=2){
|
||||||
|
EXPECT_EQ(fsop->namei((prefix+std::to_string(i)).c_str()), (u_int64_t)(-1));
|
||||||
|
}
|
||||||
|
printf("Finished Deallocating\n");
|
||||||
|
fsop->printDirectory(file_pressure);
|
||||||
|
std::string newprefix = "/pressure/New";
|
||||||
|
for(int i=0;i<100;i+=2){
|
||||||
|
EXPECT_EQ(fsop->fischl_mkdir((newprefix+std::to_string(i)).c_str(), 0), 0);
|
||||||
|
}
|
||||||
|
for(int i=0;i<100;i+=2){
|
||||||
|
u_int64_t inode_number = fsop->namei((newprefix+std::to_string(i)).c_str());
|
||||||
|
assert(inode_number>0);
|
||||||
|
}
|
||||||
|
printf("Finished Reallocating\n");
|
||||||
|
fsop->printDirectory(file_pressure);
|
||||||
|
|
||||||
|
// long filename test
|
||||||
|
std::string longfilename = std::string(255,'A');
|
||||||
|
EXPECT_EQ(fsop->fischl_mknod((std::string("/")+longfilename).c_str(),0,0),0);
|
||||||
|
u_int64_t filelong = fsop->namei((std::string("/")+longfilename).c_str());
|
||||||
|
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
|
||||||
|
const char* d = (argc < 2) ? "/dev/vdc" : argv[1];
|
||||||
|
|
||||||
|
setupTestDirectory(&mock_root);
|
||||||
|
H = new FakeRawDisk(21504);
|
||||||
|
fs = new Fs(H);
|
||||||
|
fs->format();
|
||||||
|
fsop = new FilesOperation(*H, fs);
|
||||||
|
|
||||||
|
::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(fsop->root_node);
|
||||||
|
delete fsop; // First delete fsop which depends on H
|
||||||
|
delete H;
|
||||||
|
|
||||||
|
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;
|
||||||
|
char* root_name = new char[2];
|
||||||
|
strcpy(root_name, "/");
|
||||||
|
(*root)->name = root_name; // 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user