Compare commits
1 Commits
main
...
victortung
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6d033dfdb7 |
16
.gitignore
vendored
16
.gitignore
vendored
@ -1,15 +1 @@
|
||||
build/*
|
||||
|
||||
# vscode gitignore
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
build/*
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -1,6 +0,0 @@
|
||||
{
|
||||
"cmake.configureOnOpen": false,
|
||||
"cSpell.words": [
|
||||
"unneccary"
|
||||
]
|
||||
}
|
@ -6,35 +6,17 @@ set(CMAKE_CXX_STANDARD 14)
|
||||
include_directories(
|
||||
# fischl include files
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/googletest/googletest/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/googletest/googletest/include
|
||||
)
|
||||
|
||||
add_executable(fischl
|
||||
|
||||
# Header files
|
||||
lib/fischl.cpp
|
||||
lib/main.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
|
||||
lib/direntry.cpp
|
||||
|
||||
)
|
||||
|
||||
enable_testing()
|
||||
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})
|
||||
add_subdirectory(googletest)
|
63
Issues.txt
63
Issues.txt
@ -1,63 +0,0 @@
|
||||
Problem - no header guards
|
||||
Problem - code in header file
|
||||
Problem - inconisitent use of SECTOR_SIZE IO_BLOCK_SIZE
|
||||
|
||||
|
||||
?18: what is directory
|
||||
|
||||
22: read of less than sector-size unpermitted
|
||||
|
||||
?25: does the casting here work as expected, would a bitmask be better
|
||||
|
||||
Question: why are we passing a disk every time to superblock functions, shouldn't that be associated with the superblock (i.e. the file system object)
|
||||
|
||||
37: same issues with this function
|
||||
|
||||
Superblock should be read and written to all at once to avoid unneccary io reads and should be stateful regarding the fs data (also it maybe good to rename superblock to fs and make it in charge of formatting the fs)
|
||||
|
||||
57: magic num
|
||||
|
||||
?69: strange reference pattern with current_pos - usually handled by func returning num bytes read
|
||||
|
||||
?69: should these helper functions be in the inode at all
|
||||
|
||||
?76: these two funcs could be combined together
|
||||
|
||||
?83: inode_construct may want to be named inode_load to fit pattern
|
||||
|
||||
inode objs (block_number) should likely be able to be made with out the use of inode_construct when they are first created
|
||||
|
||||
|
||||
132:
|
||||
|
||||
?142: odd use of block nums - byte addresses and io_block addresses
|
||||
|
||||
143: magic num 32?
|
||||
|
||||
?144: possibly make this a continue also use 0b11111111 and 0b01111111
|
||||
|
||||
?145: j = 0 twice
|
||||
|
||||
?76: why is this function read_byte_at if it reads 8 bytes
|
||||
|
||||
?144: why is the last bit of the bitmask unused
|
||||
|
||||
?171: returned free must be zeroed before use
|
||||
|
||||
177: no error handling for cannot allocate block (free list empty)
|
||||
|
||||
?215: should this be made into a recursive func
|
||||
|
||||
241: no error checking
|
||||
|
||||
278: the empty checking and adding to free list head seems busted
|
||||
|
||||
303: --
|
||||
|
||||
?315: && addr == 0 does nothing
|
||||
|
||||
398: can try to deallocate the super block so // deal with empty needs implementation
|
||||
|
||||
?433: why even mix sector size into free list
|
||||
|
||||
428: no limit checking
|
183
README.md
183
README.md
@ -1,103 +1,80 @@
|
||||
# iloveos
|
||||
|
||||
Simply copying libfuse.
|
||||
|
||||
|
||||
## Branching Rules
|
||||
|
||||
1. Fork the main branch and work on your own branch.
|
||||
2. Test before initiating a Pull Request.
|
||||
3. Get approved to get merged.
|
||||
|
||||
# Quick Start
|
||||
|
||||
## configure and build code
|
||||
make build directory
|
||||
```bash
|
||||
mkdir -p build && cd build
|
||||
cmake ..
|
||||
make # cmake --build . is same
|
||||
```
|
||||
|
||||
## mount and test
|
||||
normal usage:
|
||||
```bash
|
||||
./fischl diskpath n -s mountpoint
|
||||
```
|
||||
diskpath must be provided following ./fischl
|
||||
l/n must be provided following diskpath indicating whether to load the exisiting file system or create a new one.
|
||||
for loading:
|
||||
```bash
|
||||
./fischl diskpath l -s mountpoint
|
||||
```
|
||||
-s is also required because our fs doesn't support multi-threading.
|
||||
|
||||
if the diskpath need to be accessed by root:
|
||||
```bash
|
||||
sudo ./fischl diskpath n -o allow_other -s mountpoint
|
||||
```
|
||||
|
||||
for debugging:
|
||||
```bash
|
||||
sudo ./fischl diskpath n -o allow_other -d -s mountpoint
|
||||
```
|
||||
|
||||
## run test
|
||||
### add your own test file on test/CMakeList.txt
|
||||
```
|
||||
set(TARGET_NAME run_tests)
|
||||
set(TEST_NAME test_test)
|
||||
|
||||
# add test sources here ...
|
||||
add_executable(${TARGET_NAME}
|
||||
|
||||
../lib/fischl.cpp
|
||||
testfischl.cpp
|
||||
|
||||
)
|
||||
add_executable(${TEST_NAME}
|
||||
|
||||
../lib/fischl.cpp
|
||||
testtest.cpp
|
||||
|
||||
)
|
||||
add_test(NAME ${TARGET_NAME} COMMAND ${TARGET_NAME})
|
||||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
|
||||
```
|
||||
|
||||
### ctest
|
||||
```bash
|
||||
ctest -VVV
|
||||
ctest -VV #Displays the output from the tests (e.g., stdout or stderr) in addition to the test information.
|
||||
```
|
||||
Test Result will be like this
|
||||
```bash
|
||||
[cloud-user@ip-172-31-22-147 build]$ ctest -VVV
|
||||
Test project /home/cloud-user/iloveos/build
|
||||
Start 1: run_tests
|
||||
1/1 Test #1: run_tests ........................ Passed 0.00 sec
|
||||
|
||||
100% tests passed, 0 tests failed out of 1
|
||||
|
||||
Total Test time (real) = 0.01 sec
|
||||
```
|
||||
|
||||
Failed demonstration
|
||||
```bash
|
||||
[cloud-user@ip-172-31-22-147 build]$ ctest -VVV
|
||||
Test project /home/cloud-user/iloveos/build
|
||||
Start 1: run_tests
|
||||
1/2 Test #1: run_tests ........................ Passed 0.00 sec
|
||||
Start 2: test_test
|
||||
2/2 Test #2: test_test ........................Subprocess aborted***Exception: 0.26 sec
|
||||
|
||||
50% tests passed, 1 tests failed out of 2
|
||||
|
||||
Total Test time (real) = 0.27 sec
|
||||
|
||||
The following tests FAILED:
|
||||
2 - test_test (Subprocess aborted)
|
||||
Errors while running CTest
|
||||
Output from these tests are in: /home/cloud-user/iloveos/build/Testing/Temporary/LastTest.log
|
||||
Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely.
|
||||
```
|
||||
# iloveos
|
||||
|
||||
Simply copying libfuse.
|
||||
|
||||
|
||||
## Branching Rules
|
||||
|
||||
1. Fork the main branch and work on your own branch.
|
||||
2. Test before initiating a Pull Request.
|
||||
3. Get approved to get merged.
|
||||
|
||||
# Quick Start
|
||||
|
||||
## configure and build code
|
||||
make build directory
|
||||
```bash
|
||||
mkdir -p build && cd build
|
||||
cmake ..
|
||||
make # cmake --build . is same
|
||||
```
|
||||
|
||||
## run test
|
||||
### add your own test file on test/CMakeList.txt
|
||||
```
|
||||
set(TARGET_NAME run_tests)
|
||||
set(TEST_NAME test_test)
|
||||
|
||||
# add test sources here ...
|
||||
add_executable(${TARGET_NAME}
|
||||
|
||||
../lib/fischl.cpp
|
||||
testfischl.cpp
|
||||
|
||||
)
|
||||
add_executable(${TEST_NAME}
|
||||
|
||||
../lib/fischl.cpp
|
||||
testtest.cpp
|
||||
|
||||
)
|
||||
add_test(NAME ${TARGET_NAME} COMMAND ${TARGET_NAME})
|
||||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
|
||||
```
|
||||
|
||||
### ctest
|
||||
```bash
|
||||
ctest -VVV
|
||||
ctest -VV #Displays the output from the tests (e.g., stdout or stderr) in addition to the test information.
|
||||
```
|
||||
Test Result will be like this
|
||||
```bash
|
||||
[cloud-user@ip-172-31-22-147 build]$ ctest -VVV
|
||||
Test project /home/cloud-user/iloveos/build
|
||||
Start 1: run_tests
|
||||
1/1 Test #1: run_tests ........................ Passed 0.00 sec
|
||||
|
||||
100% tests passed, 0 tests failed out of 1
|
||||
|
||||
Total Test time (real) = 0.01 sec
|
||||
```
|
||||
|
||||
Failed demonstration
|
||||
```bash
|
||||
[cloud-user@ip-172-31-22-147 build]$ ctest -VVV
|
||||
Test project /home/cloud-user/iloveos/build
|
||||
Start 1: run_tests
|
||||
1/2 Test #1: run_tests ........................ Passed 0.00 sec
|
||||
Start 2: test_test
|
||||
2/2 Test #2: test_test ........................Subprocess aborted***Exception: 0.26 sec
|
||||
|
||||
50% tests passed, 1 tests failed out of 2
|
||||
|
||||
Total Test time (real) = 0.27 sec
|
||||
|
||||
The following tests FAILED:
|
||||
2 - test_test (Subprocess aborted)
|
||||
Errors while running CTest
|
||||
Output from these tests are in: /home/cloud-user/iloveos/build/Testing/Temporary/LastTest.log
|
||||
Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely.
|
||||
```
|
||||
|
@ -1,66 +0,0 @@
|
||||
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;
|
||||
|
||||
|
||||
typedef struct RenameInfo {
|
||||
FileNode *oldFileNode; // The file node being renamed.
|
||||
FileNode *oldParentNode; // The parent directory of the file node being renamed.
|
||||
FileNode *newParentNode; // The target parent directory where the file node will be moved.
|
||||
char *newName; // The new name of the file node after the rename.
|
||||
FileNode *newFileNode; // The new file node, if one already exists at the target location.
|
||||
bool exchangeExist; // Flag to indicate if the rename should replace an existing file node.
|
||||
} RenameInfo;
|
||||
|
||||
/*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*/
|
||||
FileNode* fischl_add_entry_for_cache(TreeNode *parent, int new_inode_number, const char *fileName, INode_Data *new_inode);
|
||||
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(Fs *fs, 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);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
@ -1,51 +0,0 @@
|
||||
#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 initialize(bool load);
|
||||
void printbuffer(const char*,int);
|
||||
void printDirectory(u_int64_t);
|
||||
bool permission_check(int, INode_Data*);
|
||||
bool permission_check_by_inode_num(int, 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, bool check_replace);
|
||||
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_access(const char* path, int mask);
|
||||
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_opendir(const char* path, struct fuse_file_info* fi);
|
||||
int fischl_rmdir(const char *);
|
||||
int fischl_readlink(const char* path, char* buf, size_t size);
|
||||
int fischl_symlink(const char* from, const char* to);
|
||||
int fischl_link(const char* from, const char* to);
|
||||
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 *);
|
||||
int fischl_utimens(const char *path, const struct timespec tv[2], struct fuse_file_info *fi);
|
||||
int fischl_statfs(const char* path, struct statvfs* stbuf);
|
||||
FileNode *fischl_load_entry(TreeNode *root, const char *path);
|
||||
};
|
@ -1,3 +1,7 @@
|
||||
|
||||
|
||||
int fischl(int argc, char *argv[]);
|
||||
class fischl{
|
||||
|
||||
// declare
|
||||
public:
|
||||
int init();
|
||||
|
||||
};
|
464
include/fs.h
Normal file
464
include/fs.h
Normal file
@ -0,0 +1,464 @@
|
||||
#include "rawdisk.h"
|
||||
/*****************************************************
|
||||
30GB Disk low-level operation and data structure: spuerblock, inode, and buffer cache
|
||||
512 bytes sector for 1 block, 62914560 block(sector)
|
||||
4K bytes sector for 1 block, 7864320 block(sector)
|
||||
|
||||
one inode equipped with one 512 bytes block
|
||||
|
||||
*****************************************************/
|
||||
#define SECTOR_SIZE 512
|
||||
#define IO_BLOCK_SIZE 4096
|
||||
#define MAX_INODE 524288
|
||||
#define MAX_BLOCKNUM MAX_INODE*2 //62914560
|
||||
|
||||
class SuperBlock{
|
||||
|
||||
public:
|
||||
SuperBlock(const char *directory){
|
||||
|
||||
}
|
||||
~SuperBlock(){
|
||||
|
||||
}
|
||||
static u_int64_t getFreeListHead(RawDisk &disk){
|
||||
char buffer[8] = {0};
|
||||
disk.rawdisk_read(0, buffer, sizeof(buffer));
|
||||
u_int64_t t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t = t | (((u_int64_t)(unsigned char)buffer[j])<<(8*j));
|
||||
return t;
|
||||
}
|
||||
|
||||
static void writeFreeListHead(RawDisk &disk, u_int64_t t){
|
||||
char buffer[8] = {0};
|
||||
for (int j = 0; j < 8; j++){
|
||||
buffer[j] = (t >> (8 * j)) & 0xFF;
|
||||
}
|
||||
disk.rawdisk_write(0, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
static u_int64_t getFreeINodeHead(RawDisk &disk){
|
||||
char buffer[8] = {0};
|
||||
disk.rawdisk_read(8, buffer, sizeof(buffer));
|
||||
u_int64_t t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t = t | (((u_int64_t)(unsigned char)buffer[j])<<(8*j));
|
||||
return t;
|
||||
}
|
||||
|
||||
static void writeFreeINodeHead(RawDisk &disk, u_int64_t t){
|
||||
char buffer[8] = {0};
|
||||
for (int j = 0; j < 8; j++){
|
||||
buffer[j] = (t >> (8 * j)) & 0xFF;
|
||||
}
|
||||
disk.rawdisk_write(8, buffer, sizeof(buffer));
|
||||
}
|
||||
};
|
||||
|
||||
class INode{
|
||||
// direct datablocks
|
||||
u_int64_t blocks[48];
|
||||
// indirect address
|
||||
u_int64_t single_indirect, double_indirect, triple_indirect;
|
||||
// other
|
||||
|
||||
u_int64_t uid;
|
||||
u_int64_t gid;
|
||||
u_int64_t permissions;
|
||||
u_int64_t size;
|
||||
u_int64_t block_number;
|
||||
|
||||
public:
|
||||
void read_get_byte(u_int64_t &t, int ¤t_pos, char *buffer){
|
||||
t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t = t | (((u_int64_t)(unsigned char)buffer[j+current_pos])<<(8*j));
|
||||
current_pos += 8;
|
||||
}
|
||||
|
||||
static u_int64_t read_byte_at(int current_pos, char *buffer){
|
||||
u_int64_t t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t = t | (((u_int64_t)(unsigned char)buffer[j+current_pos])<<(8*j));
|
||||
return t;
|
||||
}
|
||||
|
||||
void inode_construct(u_int64_t blockNumber, RawDisk &disk){
|
||||
char buffer[SECTOR_SIZE] = {0};
|
||||
disk.rawdisk_read(blockNumber*SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
block_number = blockNumber;
|
||||
int current_pos = 0;
|
||||
// initialize blocks
|
||||
for (int i = 0; i < 48; i++){
|
||||
read_get_byte(blocks[i], current_pos, buffer);
|
||||
}
|
||||
read_get_byte(single_indirect, current_pos, buffer);
|
||||
read_get_byte(double_indirect, current_pos, buffer);
|
||||
read_get_byte(triple_indirect, current_pos, buffer);
|
||||
read_get_byte(uid, current_pos, buffer);
|
||||
read_get_byte(gid, current_pos, buffer);
|
||||
read_get_byte(permissions, current_pos, buffer);
|
||||
read_get_byte(size, current_pos, buffer);
|
||||
}
|
||||
|
||||
void write_get_byte(u_int64_t t, int ¤t_pos, char *buffer){
|
||||
for (int j = 0; j < 8; j++){
|
||||
buffer[j+current_pos] = t & (((u_int64_t)1<<(8))-1);
|
||||
t >>= 8;
|
||||
}
|
||||
current_pos += 8;
|
||||
}
|
||||
|
||||
static void write_byte_at(u_int64_t t, int current_pos, char *buffer){
|
||||
for (int j = 0; j < 8; j++){
|
||||
buffer[j+current_pos] = t & (((u_int64_t)1<<(8))-1);
|
||||
t >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
void inode_save(RawDisk &disk){
|
||||
char buffer[SECTOR_SIZE] = {0};
|
||||
int current_pos = 0;
|
||||
for (int i = 0; i < 48; i++){
|
||||
write_get_byte(blocks[i], current_pos, buffer);
|
||||
}
|
||||
write_get_byte(single_indirect, current_pos, buffer);
|
||||
write_get_byte(double_indirect, current_pos, buffer);
|
||||
write_get_byte(triple_indirect, current_pos, buffer);
|
||||
write_get_byte(uid, current_pos, buffer);
|
||||
write_get_byte(gid, current_pos, buffer);
|
||||
write_get_byte(permissions, current_pos, buffer);
|
||||
write_get_byte(size, current_pos, buffer);
|
||||
disk.rawdisk_write(block_number*SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
u_int64_t datablock_allocate_in_list(RawDisk &disk){
|
||||
//find a free data block
|
||||
u_int64_t freeListHead = SuperBlock::getFreeListHead(disk);
|
||||
/*
|
||||
1. initialization
|
||||
2. data block starting position
|
||||
3. r/w between storage and rawdisk to maintain
|
||||
*/
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
u_int64_t freeBlockNum = 0;
|
||||
disk.rawdisk_read(freeListHead, buffer, sizeof(buffer));
|
||||
for (int i = 8; i < 264; i++){
|
||||
if((i < 263 && buffer[i] != -1) || (i == 263 && buffer[i] != 127)){
|
||||
int j = 0;
|
||||
for (j = 0; j < 8; j++){
|
||||
if ((buffer[i]&(1<<j)) == 0){
|
||||
buffer[i] |= (1<<j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < 8){
|
||||
freeBlockNum = freeListHead + ((i-8)*8 + j + 1)*IO_BLOCK_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
disk.rawdisk_write(freeListHead, buffer, sizeof(buffer));
|
||||
bool notFull = false;
|
||||
for (int i = 8; i < 264; i++){
|
||||
if((i < 263 && buffer[i] != -1) || (i == 263 && buffer[i] != 127)){
|
||||
notFull = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!notFull){
|
||||
printf("HEADER REMOVAL DETECTED %llu %llu\n", freeListHead, freeBlockNum);
|
||||
u_int64_t next_header = read_byte_at(0, buffer);
|
||||
SuperBlock::writeFreeListHead(disk, next_header);
|
||||
}
|
||||
return freeBlockNum;
|
||||
}
|
||||
|
||||
bool allo_single_indirect(RawDisk &disk, u_int64_t &single_i, u_int64_t freeBlockNum) {
|
||||
if (single_i == 0){
|
||||
single_i = datablock_allocate_in_list(disk);
|
||||
}
|
||||
bool inSingle = false;
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
disk.rawdisk_read(single_i, buffer, sizeof(buffer));
|
||||
for (int i = 0; i < IO_BLOCK_SIZE; i+=8){
|
||||
u_int64_t addr = read_byte_at(i, buffer);
|
||||
if(addr == 0){
|
||||
inSingle = true;
|
||||
write_byte_at(freeBlockNum, i, buffer);
|
||||
disk.rawdisk_write(single_i, buffer, sizeof(buffer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inSingle;
|
||||
}
|
||||
|
||||
bool allo_double_indirect(RawDisk &disk, u_int64_t &double_i, u_int64_t freeBlockNum) {
|
||||
if (double_i == 0){
|
||||
double_i = datablock_allocate_in_list(disk);
|
||||
}
|
||||
bool inDouble = false;
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
disk.rawdisk_read(double_i, buffer, sizeof(buffer));
|
||||
for (int i = 0; i < IO_BLOCK_SIZE; i+=8){
|
||||
u_int64_t addr = read_byte_at(i, buffer);
|
||||
bool flag = allo_single_indirect(disk, addr, freeBlockNum);
|
||||
if (flag){
|
||||
write_byte_at(addr, i, buffer);
|
||||
disk.rawdisk_write(double_i, buffer, sizeof(buffer));
|
||||
inDouble = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inDouble;
|
||||
}
|
||||
|
||||
bool allo_triple_indirect(RawDisk &disk, u_int64_t &triple_i, u_int64_t freeBlockNum) {
|
||||
if (triple_i == 0){
|
||||
triple_i = datablock_allocate_in_list(disk);
|
||||
}
|
||||
bool inTriple = false;
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
disk.rawdisk_read(triple_i, buffer, sizeof(buffer));
|
||||
for (int i = 0; i < IO_BLOCK_SIZE; i+=8){
|
||||
u_int64_t addr = read_byte_at(i, buffer);
|
||||
bool flag = allo_double_indirect(disk, addr, freeBlockNum);
|
||||
if (flag){
|
||||
write_byte_at(addr, i, buffer);
|
||||
disk.rawdisk_write(triple_i, buffer, sizeof(buffer));
|
||||
inTriple = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return inTriple;
|
||||
}
|
||||
|
||||
|
||||
// allowcate 1 datablock and add to the end of the file
|
||||
u_int64_t datablock_allocate(RawDisk &disk){
|
||||
//do we need to check dynamic?
|
||||
|
||||
//add the data block to blocks, single, double, triple
|
||||
u_int64_t freeBlockNum = datablock_allocate_in_list(disk);
|
||||
bool inBlocks = false;
|
||||
for (int i = 0; i < 48; i++)
|
||||
if(blocks[i] == 0){
|
||||
inBlocks = true;
|
||||
blocks[i] = freeBlockNum;
|
||||
break;
|
||||
}
|
||||
if(!inBlocks){
|
||||
bool inSingle = allo_single_indirect(disk, single_indirect, freeBlockNum);
|
||||
if (!inSingle){
|
||||
bool inDouble = allo_double_indirect(disk, double_indirect, freeBlockNum);
|
||||
if (!inDouble){
|
||||
bool inTriple = allo_triple_indirect(disk, triple_indirect, freeBlockNum);
|
||||
// wait to deal with too big files
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//return the block number
|
||||
inode_save(disk);
|
||||
return freeBlockNum;
|
||||
}
|
||||
|
||||
void datablock_deallocate_in_list(u_int64_t freeBlockNum, RawDisk &disk) {
|
||||
// find the related 2048block head
|
||||
u_int64_t freeBlockHead = ((freeBlockNum/SECTOR_SIZE-MAX_INODE)/(8*2048)*(8*2048)+MAX_INODE)*SECTOR_SIZE;
|
||||
|
||||
// mark it alive in its bitmap
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
bool notEmpty = false;
|
||||
disk.rawdisk_read(freeBlockHead, buffer, sizeof(buffer));
|
||||
for (int i = 8; i < 264; i++){
|
||||
if(buffer[i] != 0){
|
||||
notEmpty = true;
|
||||
}
|
||||
}
|
||||
u_int64_t inBlockPos = (freeBlockNum-freeBlockHead)/IO_BLOCK_SIZE-1;
|
||||
buffer[8+inBlockPos/8] &= (-1)^(1<<(inBlockPos%8));
|
||||
|
||||
// if its bitmap was 0, add it back to the list head
|
||||
if(!notEmpty){
|
||||
u_int64_t freeListHead = SuperBlock::getFreeListHead(disk);
|
||||
write_byte_at(freeListHead, 0, buffer);
|
||||
SuperBlock::writeFreeListHead(disk, freeBlockHead);
|
||||
}
|
||||
disk.rawdisk_write(freeBlockHead, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
u_int64_t deallo_single_indirect(RawDisk &disk, u_int64_t &single_i){
|
||||
if (single_i == 0){
|
||||
return 0;
|
||||
}
|
||||
u_int64_t freeBlockNum = 0;
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
int delpoint = -1;
|
||||
disk.rawdisk_read(single_i, buffer, sizeof(buffer));
|
||||
for (int i=4088; i >= 0; i--){
|
||||
u_int64_t addr = read_byte_at(i, buffer);
|
||||
if(addr != 0){
|
||||
freeBlockNum = addr;
|
||||
addr = 0;
|
||||
write_byte_at(addr, i, buffer);
|
||||
delpoint = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
disk.rawdisk_write(single_i, buffer, sizeof(buffer));
|
||||
u_int64_t addr = read_byte_at(0, buffer);
|
||||
if (delpoint == 0 && addr == 0){
|
||||
datablock_deallocate_in_list(single_i, disk);
|
||||
single_i = 0;
|
||||
}
|
||||
return freeBlockNum;
|
||||
}
|
||||
|
||||
bool deallo_double_indirect(RawDisk &disk, u_int64_t &double_i){
|
||||
if (double_i == 0){
|
||||
return false;
|
||||
}
|
||||
u_int64_t freeBlockNum = 0;
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
int delpoint = -1;
|
||||
disk.rawdisk_read(double_i, buffer, sizeof(buffer));
|
||||
for (int i=4088; i >= 0; i-=8){
|
||||
u_int64_t addr = read_byte_at(i, buffer);
|
||||
u_int64_t inSingle = deallo_single_indirect(disk, addr);
|
||||
if (inSingle){
|
||||
freeBlockNum = inSingle;
|
||||
write_byte_at(addr, i, buffer);
|
||||
delpoint = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
disk.rawdisk_write(double_i, buffer, sizeof(buffer));
|
||||
u_int64_t addr = read_byte_at(0, buffer);
|
||||
if (delpoint == 0 && addr == 0){
|
||||
datablock_deallocate_in_list(double_i, disk);
|
||||
double_i = 0;
|
||||
}
|
||||
return freeBlockNum;
|
||||
}
|
||||
|
||||
bool deallo_triple_indirect(RawDisk &disk, u_int64_t &triple_i){
|
||||
if (triple_i == 0){
|
||||
return false;
|
||||
}
|
||||
u_int64_t freeBlockNum = 0;
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
int delpoint = -1;
|
||||
disk.rawdisk_read(triple_i, buffer, sizeof(buffer));
|
||||
for (int i=4088; i >= 0; i-=8){
|
||||
u_int64_t addr = read_byte_at(i, buffer);
|
||||
u_int64_t inDouble = deallo_double_indirect(disk, addr);
|
||||
if (inDouble){
|
||||
freeBlockNum = inDouble;
|
||||
write_byte_at(addr, i, buffer);
|
||||
delpoint = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
disk.rawdisk_write(triple_i, buffer, sizeof(buffer));
|
||||
u_int64_t addr = read_byte_at(0, buffer);
|
||||
if (delpoint == 0 && addr == 0){
|
||||
datablock_deallocate_in_list(triple_i, disk);
|
||||
triple_i = 0;
|
||||
}
|
||||
return freeBlockNum;
|
||||
}
|
||||
|
||||
// deallocate 1 datablock from the end of the file
|
||||
u_int64_t datablock_deallocate(RawDisk &disk){
|
||||
// find the last datablock and remove it from inode (triple->direct)
|
||||
u_int64_t freeBlockNum = 0;
|
||||
freeBlockNum = deallo_triple_indirect(disk, triple_indirect);
|
||||
if(!freeBlockNum){
|
||||
freeBlockNum = deallo_double_indirect(disk, double_indirect);
|
||||
if(!freeBlockNum){
|
||||
freeBlockNum = deallo_single_indirect(disk, single_indirect);
|
||||
if(!freeBlockNum){
|
||||
for(int i = 47; i>=0; i--)
|
||||
if(blocks[i] != 0){
|
||||
freeBlockNum = blocks[i];
|
||||
blocks[i] = 0;
|
||||
break;
|
||||
}
|
||||
// deal with empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add it back to freeBlocklist
|
||||
datablock_deallocate_in_list(freeBlockNum, disk);
|
||||
inode_save(disk);
|
||||
return freeBlockNum;
|
||||
}
|
||||
};
|
||||
|
||||
class INodeOperation{
|
||||
// free list head is at super block (0): first 8 bytes
|
||||
|
||||
public:
|
||||
//initialization of the rawdisk
|
||||
void initialize(RawDisk &disk){
|
||||
// initialize Inode list head
|
||||
SuperBlock::writeFreeINodeHead(disk, 1);
|
||||
for (u_int64_t i = 1; i < MAX_INODE; i++){
|
||||
char buffer[SECTOR_SIZE] = {0};
|
||||
u_int64_t t = i + 1;
|
||||
if (t < MAX_INODE){
|
||||
for (int j = 0; j < 8; j++){
|
||||
buffer[j] = (t >> (8 * j)) & 0xFF;
|
||||
}
|
||||
}
|
||||
disk.rawdisk_write(i*SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
}
|
||||
SuperBlock::writeFreeListHead(disk, MAX_INODE*SECTOR_SIZE); // maximum inode number 2^19 0x80000
|
||||
//Have tested this initialize function but MAX_BLOCK too much, MAX_INODE*2 works
|
||||
for (u_int64_t i = MAX_INODE; i < MAX_BLOCKNUM-4096; i += 2048*8){
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
u_int64_t t = (i + 2048*8)*SECTOR_SIZE;
|
||||
//t is address, storing in to buffer
|
||||
for (int j = 0; j < 8; j++){
|
||||
buffer[j] = (t >> (8 * j)) & 0xFF;
|
||||
}
|
||||
disk.rawdisk_write(i*SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
// allocate an inode from free inode list head and return the number of the inode
|
||||
// the i-th inode is in the i-th block
|
||||
u_int64_t inode_allocate(RawDisk &disk){
|
||||
u_int64_t freeINodeHead = SuperBlock::getFreeINodeHead(disk);
|
||||
char buffer[SECTOR_SIZE] = {0};
|
||||
disk.rawdisk_read(freeINodeHead*SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
u_int64_t newINodeHead = INode::read_byte_at(0, buffer);
|
||||
// deal with no more INode
|
||||
SuperBlock::writeFreeINodeHead(disk, newINodeHead);
|
||||
//to do: initialize the INode on disk at freeINodeHead
|
||||
|
||||
//return inode number
|
||||
return freeINodeHead;
|
||||
}
|
||||
|
||||
// free the inode and add it to the free inode list head
|
||||
void inode_free(RawDisk &disk, u_int64_t INodeNumber){
|
||||
u_int64_t freeINodeHead = SuperBlock::getFreeINodeHead(disk);
|
||||
char buffer[SECTOR_SIZE] = {0};
|
||||
INode::write_byte_at(freeINodeHead, 0, buffer);
|
||||
disk.rawdisk_write(INodeNumber*SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
SuperBlock::writeFreeINodeHead(disk, INodeNumber);
|
||||
}
|
||||
|
||||
//ignore for now
|
||||
void inode_read(){
|
||||
|
||||
}
|
||||
|
||||
void inode_write(){
|
||||
|
||||
}
|
||||
};
|
@ -1,48 +0,0 @@
|
||||
#ifndef FS_HPP
|
||||
#define FS_HPP
|
||||
|
||||
#include "fs/datablock_manager.hpp"
|
||||
#include "fs/fs_data_types.hpp"
|
||||
#include "fs/inode_manager.hpp"
|
||||
#include "fs_constants.hpp"
|
||||
#include "rawdisk.hpp"
|
||||
|
||||
// TEMP:
|
||||
class DatablockOperation;
|
||||
|
||||
class Fs {
|
||||
public:
|
||||
Fs(RawDisk *disk);
|
||||
~Fs();
|
||||
|
||||
ssize_t read(INode_Data *inode_data, char buf[], size_t count, size_t offset);
|
||||
ssize_t write(INode_Data *inode_data, const char buf[], size_t count,
|
||||
size_t offset);
|
||||
int truncate(INode_Data *inode_data, off_t length);
|
||||
ssize_t lseek_next_data(INode_Data *inode_data, size_t offset);
|
||||
ssize_t lseek_next_hole(INode_Data *inode_data, size_t offset);
|
||||
|
||||
int format();
|
||||
|
||||
// should probably be private but is not for testing
|
||||
RawDisk *disk;
|
||||
SuperBlock_Data superblock;
|
||||
INode_Manager *inode_manager;
|
||||
DataBlock_Manager *datablock_manager;
|
||||
|
||||
int load_superblock();
|
||||
int save_superblock();
|
||||
|
||||
int save_free_list_head(u_int64_t new_free_list_head);
|
||||
int save_inode_list_head(u_int64_t new_inode_list_head);
|
||||
|
||||
int sweep_inode_datablocks(INode_Data *inode_data,
|
||||
u_int64_t start_block_index, bool allocate,
|
||||
DatablockOperation *op);
|
||||
|
||||
int sweep_datablocks(u_int64_t *block_num, int indirect_num,
|
||||
u_int64_t start_block_index, bool allocate,
|
||||
DatablockOperation *op);
|
||||
};
|
||||
|
||||
#endif
|
@ -1,35 +0,0 @@
|
||||
#ifndef DATABLOCK_MANAGER_HPP
|
||||
#define DATABLOCK_MANAGER_HPP
|
||||
|
||||
#include "fs_constants.hpp"
|
||||
|
||||
class Fs;
|
||||
|
||||
class DataBlock_Manager {
|
||||
public:
|
||||
DataBlock_Manager(Fs *fs, u_int64_t block_segment_start,
|
||||
u_int64_t block_segment_end);
|
||||
|
||||
virtual int new_datablock(u_int64_t *block_num) = 0;
|
||||
virtual int free_datablock(u_int64_t block_num) = 0;
|
||||
|
||||
virtual int format() = 0;
|
||||
|
||||
protected:
|
||||
Fs *fs;
|
||||
u_int64_t block_segment_start, block_segment_end;
|
||||
};
|
||||
|
||||
class DataBlock_Manager_Bitmap : public DataBlock_Manager {
|
||||
public:
|
||||
DataBlock_Manager_Bitmap(Fs *fs, u_int64_t block_segment_start,
|
||||
u_int64_t block_segment_end)
|
||||
: DataBlock_Manager(fs, block_segment_start, block_segment_end) {}
|
||||
|
||||
int new_datablock(u_int64_t *block_num) override;
|
||||
int free_datablock(u_int64_t block_num) override;
|
||||
|
||||
int format() override;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,57 +0,0 @@
|
||||
#ifndef FS_DATA_TYPES_HPP
|
||||
#define FS_DATA_TYPES_HPP
|
||||
|
||||
#include "fs_constants.hpp"
|
||||
|
||||
size_t write_u64(u_int64_t num, char buf[]);
|
||||
|
||||
size_t read_u64(u_int64_t *num, char buf[]);
|
||||
|
||||
size_t write_u32(u_int32_t num, char buf[]);
|
||||
|
||||
size_t read_u32(u_int32_t *num, char buf[]);
|
||||
|
||||
class SuperBlock_Data {
|
||||
public:
|
||||
u_int64_t free_list_head;
|
||||
u_int64_t inode_list_head;
|
||||
SuperBlock_Data();
|
||||
void serialize(char buf[]);
|
||||
void deserialize(char buf[]);
|
||||
};
|
||||
|
||||
class INode_Data {
|
||||
public:
|
||||
u_int64_t inode_num;
|
||||
|
||||
#define NUMBER_OF_METADATA_BYTES \
|
||||
(6 * sizeof(u_int64_t) + (2 * sizeof(u_int32_t)))
|
||||
struct INode_MetaData {
|
||||
u_int64_t uid;
|
||||
u_int64_t gid;
|
||||
u_int64_t permissions;
|
||||
u_int64_t size; // not yet implemented
|
||||
u_int64_t access_time;
|
||||
u_int64_t modification_time;
|
||||
u_int32_t reference_count;
|
||||
u_int32_t flags;
|
||||
} metadata;
|
||||
size_t serialize_metadata(char buf[]);
|
||||
size_t deserialize_metadata(char buf[]);
|
||||
|
||||
#define NUMBER_OF_DIRECT_BLOCKS \
|
||||
(((INODE_SIZE - NUMBER_OF_METADATA_BYTES) / sizeof(u_int64_t)) - 3)
|
||||
#define FILE_SIZE_MAX \
|
||||
(IO_BLOCK_SIZE * (NUMBER_OF_DIRECT_BLOCKS + INDIRECT_BLOCKS + \
|
||||
(INDIRECT_BLOCKS * INDIRECT_BLOCKS) + \
|
||||
(INDIRECT_BLOCKS * INDIRECT_BLOCKS * INDIRECT_BLOCKS)))
|
||||
|
||||
u_int64_t single_indirect_block, double_indirect_block, triple_indirect_block;
|
||||
u_int64_t direct_blocks[NUMBER_OF_DIRECT_BLOCKS];
|
||||
|
||||
INode_Data(u_int64_t inode_num = (u_int64_t)(0xFFFFFFFFFFFFFFFF));
|
||||
void serialize(char buf[]);
|
||||
void deserialize(char buf[]);
|
||||
};
|
||||
|
||||
#endif
|
@ -1,47 +0,0 @@
|
||||
#ifndef INODE_MANAGER_HPP
|
||||
#define INODE_MANAGER_HPP
|
||||
|
||||
#include "fs_constants.hpp"
|
||||
#include "fs_data_types.hpp"
|
||||
|
||||
class Fs;
|
||||
|
||||
class INode_Manager {
|
||||
public:
|
||||
const int INODES_PER_BLOCK = IO_BLOCK_SIZE / INODE_SIZE;
|
||||
|
||||
INode_Manager(Fs *fs, u_int64_t block_segment_start,
|
||||
u_int64_t block_segment_end);
|
||||
|
||||
virtual int new_inode(u_int64_t uid, u_int64_t gid, u_int64_t permissions,
|
||||
INode_Data *inode_data) = 0;
|
||||
virtual int free_inode(INode_Data *inode_data) = 0;
|
||||
|
||||
virtual int format() = 0;
|
||||
|
||||
u_int64_t get_block_num(u_int64_t inode_data);
|
||||
u_int64_t get_block_offset(u_int64_t inode_data);
|
||||
|
||||
int load_inode(INode_Data *inode_data);
|
||||
int save_inode(INode_Data *inode_data);
|
||||
|
||||
protected:
|
||||
Fs *fs;
|
||||
u_int64_t block_segment_start, block_segment_end;
|
||||
u_int64_t max_num_inodes;
|
||||
};
|
||||
|
||||
class INode_Manager_Freelist : public INode_Manager {
|
||||
public:
|
||||
INode_Manager_Freelist(Fs *fs, u_int64_t block_segment_start,
|
||||
u_int64_t block_segment_end)
|
||||
: INode_Manager(fs, block_segment_start, block_segment_end) {}
|
||||
|
||||
int new_inode(u_int64_t uid, u_int64_t gid, u_int64_t permissions,
|
||||
INode_Data *inode_data) override;
|
||||
int free_inode(INode_Data *inode_data) override;
|
||||
|
||||
int format() override;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,26 +0,0 @@
|
||||
#ifndef FS_CONSTANTS_HPP
|
||||
#define FS_CONSTANTS_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/fs.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define IO_BLOCK_SIZE 4096
|
||||
#define INDIRECT_BLOCKS 512
|
||||
|
||||
#define NUM_INODE_BLOCKS 262143
|
||||
|
||||
#define INODE_SIZE 512
|
||||
|
||||
// TODO: explore the optimal value for this
|
||||
#define DATABLOCKS_PER_BITMAP_BLOCK 2047
|
||||
|
||||
#endif
|
82
include/rawdisk.h
Normal file
82
include/rawdisk.h
Normal file
@ -0,0 +1,82 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
class RawDisk{
|
||||
|
||||
int fd;
|
||||
const char* dir;
|
||||
u_int64_t numSectors;
|
||||
u_int64_t diskSize;
|
||||
|
||||
public:
|
||||
RawDisk(const char *directory) : fd(-1), dir(nullptr), numSectors(0), diskSize(0) {
|
||||
dir = directory;
|
||||
/*dir = strdup("/dev/vdc");
|
||||
numSectors = 62914560;
|
||||
diskSize = 32212254720;*/
|
||||
|
||||
// Open the block device (replace /dev/sdX with the actual device)
|
||||
fd = open(dir, O_RDWR); // Allow read and write
|
||||
if (fd == -1) {
|
||||
perror("Error opening device");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Use ioctl with BLKGETSIZE to get the number of sectors
|
||||
if (ioctl(fd, BLKGETSIZE64, &diskSize) == -1) {
|
||||
perror("Error getting disk size");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Calculate the size in bytes
|
||||
numSectors = diskSize / 512; // Assuming a sector size of 512 bytes
|
||||
|
||||
printf("====Initializing RawDisk====\n");
|
||||
printf("Number of sectors: %llu\n", numSectors);
|
||||
printf("Disk size (in bytes): %llu\n", diskSize);
|
||||
}
|
||||
|
||||
~RawDisk() {
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
int rawdisk_read(u_int64_t offset, char *buffer, size_t length) {
|
||||
if (lseek(fd, offset, SEEK_SET) == (u_int64_t)-1) {
|
||||
perror("Error seeking to offset");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytesRead = read(fd, buffer, length);
|
||||
if (bytesRead == -1) {
|
||||
perror("Error reading from device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write a specified number of bytes at a given byte offset
|
||||
int rawdisk_write(u_int64_t offset, char *buffer, size_t length) {
|
||||
if (lseek(fd, offset, SEEK_SET) == (u_int64_t)-1) {
|
||||
perror("Error seeking to offset");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytesWritten = write(fd, buffer, length);
|
||||
if (bytesWritten == -1) {
|
||||
perror("Error writing to device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
@ -1,40 +0,0 @@
|
||||
#ifndef RAWDISK_HPP
|
||||
#define RAWDISK_HPP
|
||||
|
||||
#include "fs_constants.hpp"
|
||||
|
||||
class RawDisk {
|
||||
public:
|
||||
u_int64_t diskSize;
|
||||
|
||||
virtual int read_block(u_int64_t block_number, char *buffer) = 0;
|
||||
virtual int write_block(u_int64_t block_number, char *buffer) = 0;
|
||||
|
||||
void print_block(u_int64_t block_number);
|
||||
};
|
||||
|
||||
class RealRawDisk : public RawDisk {
|
||||
public:
|
||||
int fd;
|
||||
const char *dir;
|
||||
u_int64_t numSectors;
|
||||
|
||||
RealRawDisk(const char *directory);
|
||||
~RealRawDisk();
|
||||
|
||||
int read_block(u_int64_t block_number, char *buffer) override;
|
||||
int write_block(u_int64_t block_number, char *buffer) override;
|
||||
};
|
||||
|
||||
class FakeRawDisk : public RawDisk {
|
||||
public:
|
||||
char *disk;
|
||||
|
||||
FakeRawDisk(u_int64_t num_blocks);
|
||||
~FakeRawDisk();
|
||||
|
||||
int read_block(u_int64_t block_number, char *buffer) override;
|
||||
int write_block(u_int64_t block_number, char *buffer) override;
|
||||
};
|
||||
|
||||
#endif
|
278
lib/direntry.cpp
278
lib/direntry.cpp
@ -1,278 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
FileNode* fischl_add_entry_for_cache(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;
|
||||
return newFile;
|
||||
}
|
||||
|
||||
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(Fs *fs, TreeNode *root, const char *path){
|
||||
//support . and .. function
|
||||
char *pathCopy = strdup(path);
|
||||
char *segment = strtok(pathCopy, "/");
|
||||
TreeNode *current = root;
|
||||
FileNode *file = NULL;
|
||||
|
||||
printf("FINDING %s %s %llu\n", path, segment, current->self_info->inode_number);
|
||||
|
||||
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) {
|
||||
// find on disk whether this exists
|
||||
INode_Data inode;
|
||||
inode.inode_num = current->self_info->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);
|
||||
//printf("WARNING:%d %llu %llu %s %s\n",__LINE__,inode.inode_num, ent.inode_number, ent.file_name, segment);
|
||||
if (ent.inode_number && strcmp(ent.file_name, segment)==0) {
|
||||
file = fischl_add_entry_for_cache(current, ent.inode_number, ent.file_name, &inode);
|
||||
//printf("DONE !! %llu\n", file->inode_number);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (file != NULL && file->subdirectory == NULL) {
|
||||
free(pathCopy);
|
||||
printf("FOUND !! %llu\n", file->inode_number);
|
||||
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
|
||||
}
|
1333
lib/files.cpp
1333
lib/files.cpp
File diff suppressed because it is too large
Load Diff
316
lib/fischl.cpp
316
lib/fischl.cpp
@ -1,314 +1,8 @@
|
||||
#define FUSE_USE_VERSION 31
|
||||
#include "fischl.h"
|
||||
|
||||
#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"
|
||||
#include <cstdio>
|
||||
|
||||
/*
|
||||
* Command line options
|
||||
*
|
||||
* 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;
|
||||
bool load;
|
||||
} 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) {
|
||||
cfg->use_ino = 1;
|
||||
conn->want &= ~FUSE_CAP_ATOMIC_O_TRUNC;
|
||||
options.fsop->initialize(options.load);
|
||||
}
|
||||
|
||||
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 options.fsop->fischl_access(path, mask);
|
||||
}
|
||||
|
||||
static int fischl_readlink(const char* path, char* buf, size_t size) {
|
||||
return options.fsop->fischl_readlink(path, buf, size);
|
||||
}
|
||||
|
||||
static int fischl_opendir(const char* path, struct fuse_file_info* fi) {
|
||||
return options.fsop->fischl_opendir(path, fi);
|
||||
}
|
||||
|
||||
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* from, const char* to) {
|
||||
return options.fsop->fischl_symlink(from, to);
|
||||
}
|
||||
|
||||
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 options.fsop->fischl_link(from, to);
|
||||
}
|
||||
|
||||
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 options.fsop->fischl_utimens(path, tv, fi);
|
||||
}
|
||||
|
||||
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 options.fsop->fischl_statfs(path, stbuf);
|
||||
}
|
||||
|
||||
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;
|
||||
if(argc < 3){
|
||||
printf("WRONG ARGUMENTS\n");
|
||||
return 0;
|
||||
}
|
||||
std::swap(argv[0], argv[1]);
|
||||
std::swap(argv[1], argv[2]);
|
||||
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc-2, argv+2);
|
||||
srand(time(NULL)); // Seed the random number generator
|
||||
//const char* d = (argc < 2) ? "/dev/vdc" : argv[1];
|
||||
|
||||
//setupTestDirectory(&options.root);
|
||||
if(strcmp(argv[0], "fake")==0){
|
||||
options.H = new FakeRawDisk(27648);
|
||||
}
|
||||
else{
|
||||
options.H = new RealRawDisk(argv[0]);
|
||||
char zero_es[IO_BLOCK_SIZE] = {0};
|
||||
/*printf("zeroed\n");
|
||||
for (int i = 0; i < 200000; i++){
|
||||
options.H->write_block(i, zero_es);
|
||||
}*/
|
||||
}
|
||||
if(strcmp(argv[1], "l")==0){
|
||||
options.load = true;
|
||||
}
|
||||
else if(strcmp(argv[1], "n")==0){
|
||||
options.load = false;
|
||||
}
|
||||
else{
|
||||
printf("WRONG l/n ARGUMENTS\n");
|
||||
return 0;
|
||||
}
|
||||
options.fs = new Fs(options.H);
|
||||
if(!options.load){
|
||||
printf("FORMAT %d\n", options.fs->format());
|
||||
}
|
||||
options.fsop = new FilesOperation(*options.H, options.fs);
|
||||
|
||||
/*INode_Data inode_data;
|
||||
options.fs->inode_manager->new_inode(1, 2, 3, &inode_data);
|
||||
|
||||
int buf_size = 100000;
|
||||
int seg_size = 10;
|
||||
char buf[buf_size];
|
||||
|
||||
int res;
|
||||
int num = 1;
|
||||
|
||||
for (u_int64_t i = 0; i < 30 * 1024 * 1024;) {
|
||||
for (int j = 0; j < buf_size;) {
|
||||
j += sprintf(&buf[j], "%010d\n", ++num);
|
||||
}
|
||||
res = options.fs->write(&inode_data, buf, buf_size, i);
|
||||
if (res < buf_size)
|
||||
printf("ERR: %d %d\n", res, i);
|
||||
i += res;
|
||||
}
|
||||
|
||||
num = 1;
|
||||
|
||||
printf("done write\n");
|
||||
char buf2[buf_size];
|
||||
|
||||
for (u_int64_t i = 0; i < 30 * 1024 * 1024;) {
|
||||
for (int j = 0; j < buf_size;) {
|
||||
j += sprintf(&buf[j], "%010d\n", ++num);
|
||||
}
|
||||
res = options.fs->read(&inode_data, buf2, buf_size, i);
|
||||
if (res < buf_size)
|
||||
printf("ERR2: %d %d\n", res, i);
|
||||
i += res;
|
||||
for (int j = 0; j < res; ++j) {
|
||||
if (buf[j] != buf2[j])
|
||||
printf("err err err: %d %d", buf[j], i);
|
||||
}
|
||||
}
|
||||
|
||||
printf("done read\n");
|
||||
|
||||
num = 1;
|
||||
|
||||
for (u_int64_t i = 0; i < 30 * 1024 * 1024;) {
|
||||
for (int j = 0; j < buf_size;) {
|
||||
j += sprintf(&buf[j], "%010d\n", ++num);
|
||||
}
|
||||
res = options.fs->read(&inode_data, buf2, buf_size, i);
|
||||
if (res < buf_size)
|
||||
printf("ERR2: %d %d\n", res, i);
|
||||
i += res;
|
||||
for (int j = 0; j < res; ++j) {
|
||||
if (buf[j] != buf2[j])
|
||||
printf("err err err: %d %d", buf[j], i);
|
||||
}
|
||||
}
|
||||
|
||||
printf("done read2\n");*/
|
||||
|
||||
|
||||
|
||||
/* 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;
|
||||
int fischl::init(){
|
||||
printf("Hello Fischl!");
|
||||
return 3;
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
#include "fs.hpp"
|
||||
|
||||
DataBlock_Manager::DataBlock_Manager(Fs *fs, u_int64_t block_segment_start,
|
||||
u_int64_t block_segment_end)
|
||||
: fs(fs), block_segment_start(block_segment_start),
|
||||
block_segment_end(block_segment_end) {}
|
||||
|
||||
class BitmapBlock_Data {
|
||||
public:
|
||||
char buf[IO_BLOCK_SIZE];
|
||||
u_int64_t datablocks_per_bitmap;
|
||||
|
||||
BitmapBlock_Data(u_int64_t datablocks_per_bitmap_)
|
||||
: datablocks_per_bitmap(datablocks_per_bitmap_) {}
|
||||
|
||||
u_int64_t get_next_node() {
|
||||
u_int64_t block_num;
|
||||
read_u64(&block_num, buf);
|
||||
return block_num;
|
||||
}
|
||||
void set_next_node(u_int64_t block_num) { write_u64(block_num, buf); }
|
||||
|
||||
u_int64_t find_unfilled() {
|
||||
const char *data = &buf[8];
|
||||
u_int64_t i = 0;
|
||||
|
||||
for (; i < datablocks_per_bitmap; ++i)
|
||||
if ((data[i / 8] & (1 << (i % 8))) == 0)
|
||||
return i + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
u_int64_t claim_relative_block() {
|
||||
u_int64_t unfilled = find_unfilled();
|
||||
if (unfilled)
|
||||
buf[((unfilled - 1) / 8) + 8] |= (1 << ((unfilled - 1) % 8));
|
||||
return unfilled;
|
||||
}
|
||||
|
||||
void release_relative_block(u_int64_t relative_block_num) {
|
||||
relative_block_num -= 1;
|
||||
size_t index = (relative_block_num / 8) + 8;
|
||||
int offset = relative_block_num % 8;
|
||||
buf[index] &= ~(1 << offset);
|
||||
}
|
||||
};
|
||||
|
||||
int DataBlock_Manager_Bitmap::new_datablock(u_int64_t *block_num) {
|
||||
int err;
|
||||
BitmapBlock_Data bitmap = BitmapBlock_Data(DATABLOCKS_PER_BITMAP_BLOCK);
|
||||
u_int64_t bitmap_block_num = fs->superblock.free_list_head;
|
||||
char zero_buf[IO_BLOCK_SIZE] = {0};
|
||||
|
||||
if (bitmap_block_num < block_segment_start ||
|
||||
bitmap_block_num >= block_segment_end) {
|
||||
perror("Error with new_datablock freelist head\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((err = fs->disk->read_block(bitmap_block_num, bitmap.buf)) < 0)
|
||||
return err;
|
||||
|
||||
// if (bitmap.get_next_node() == fs->superblock.free_list_head)
|
||||
// printf("WARNING: ON LAST BITMAP "
|
||||
// "BLOCK!\n");
|
||||
|
||||
u_int64_t relative_block_num = bitmap.claim_relative_block();
|
||||
|
||||
if (relative_block_num == 0) {
|
||||
errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
u_int64_t block_num_ = relative_block_num + bitmap_block_num;
|
||||
|
||||
// NOTE: this could be removed for speed
|
||||
if ((err = fs->disk->write_block(block_num_, zero_buf)) < 0)
|
||||
return err;
|
||||
|
||||
// Could be optimized
|
||||
if (bitmap.find_unfilled() == 0) {
|
||||
if ((err = fs->save_free_list_head(bitmap.get_next_node())) < 0)
|
||||
return err;
|
||||
bitmap.set_next_node(0);
|
||||
}
|
||||
|
||||
if ((err = fs->disk->write_block(bitmap_block_num, bitmap.buf)) < 0)
|
||||
return err;
|
||||
|
||||
(*block_num) = block_num_;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DataBlock_Manager_Bitmap::free_datablock(u_int64_t block_num) {
|
||||
int err;
|
||||
BitmapBlock_Data bitmap = BitmapBlock_Data(DATABLOCKS_PER_BITMAP_BLOCK);
|
||||
const u_int64_t bitmap_region_size = DATABLOCKS_PER_BITMAP_BLOCK + 1;
|
||||
bool update_freelist = false;
|
||||
|
||||
u_int64_t bitmap_block_num =
|
||||
(((block_num - block_segment_start) / bitmap_region_size) *
|
||||
bitmap_region_size) +
|
||||
block_segment_start;
|
||||
|
||||
if ((err = fs->disk->read_block(bitmap_block_num, bitmap.buf)) < 0)
|
||||
return err;
|
||||
|
||||
if (bitmap.find_unfilled() == 0) {
|
||||
update_freelist = true;
|
||||
bitmap.set_next_node(fs->superblock.free_list_head);
|
||||
}
|
||||
|
||||
bitmap.release_relative_block(block_num - bitmap_block_num);
|
||||
|
||||
if ((err = fs->disk->write_block(bitmap_block_num, bitmap.buf)) < 0)
|
||||
return err;
|
||||
|
||||
if (update_freelist)
|
||||
if ((err = fs->save_free_list_head(bitmap_block_num)) < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
|
||||
// placing almost full bitmaps back at start of freelist is slow
|
||||
// potentially like 256 times slower throughput
|
||||
}
|
||||
|
||||
int DataBlock_Manager_Bitmap::format() {
|
||||
const u_int64_t bitmap_region_size = DATABLOCKS_PER_BITMAP_BLOCK + 1;
|
||||
char buf[IO_BLOCK_SIZE] = {0};
|
||||
int err;
|
||||
u_int64_t i = block_segment_start;
|
||||
write_u64(i, buf);
|
||||
for (; i <= block_segment_end - (2 * bitmap_region_size);
|
||||
i += bitmap_region_size) {
|
||||
write_u64(i + bitmap_region_size, buf);
|
||||
if ((err = fs->disk->write_block(i, buf)) < 0)
|
||||
return err;
|
||||
}
|
||||
if ((err = fs->disk->write_block(i, buf)) < 0)
|
||||
return err;
|
||||
if ((err = fs->save_free_list_head(block_segment_start)) < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#include "fs.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
Fs::Fs(RawDisk *disk) : disk(disk) {
|
||||
assert((disk->diskSize / IO_BLOCK_SIZE) >
|
||||
2 + NUM_INODE_BLOCKS + DATABLOCKS_PER_BITMAP_BLOCK);
|
||||
superblock = SuperBlock_Data();
|
||||
inode_manager = new INode_Manager_Freelist(this, 1, 1 + NUM_INODE_BLOCKS);
|
||||
datablock_manager = new DataBlock_Manager_Bitmap(
|
||||
this, 1 + NUM_INODE_BLOCKS, disk->diskSize / IO_BLOCK_SIZE);
|
||||
};
|
||||
|
||||
Fs::~Fs() {
|
||||
delete inode_manager;
|
||||
delete datablock_manager;
|
||||
};
|
||||
|
||||
int Fs::format() {
|
||||
int err;
|
||||
if ((err = save_superblock()) < 0)
|
||||
return err;
|
||||
if ((err = inode_manager->format()) < 0)
|
||||
return err;
|
||||
if ((err = datablock_manager->format()) < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Fs::load_superblock() {
|
||||
char buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
|
||||
if ((err = disk->read_block(0, buf)) < 0)
|
||||
return err;
|
||||
|
||||
superblock.deserialize(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int Fs::save_superblock() {
|
||||
char buf[IO_BLOCK_SIZE] = {0};
|
||||
int err;
|
||||
|
||||
superblock.serialize(buf);
|
||||
|
||||
if ((err = disk->write_block(0, buf)) < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Fs::save_free_list_head(u_int64_t new_free_list_head) {
|
||||
u_int64_t temp = superblock.free_list_head;
|
||||
int err;
|
||||
superblock.free_list_head = new_free_list_head;
|
||||
if ((err = save_superblock()) < 0) {
|
||||
superblock.free_list_head = temp;
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int Fs::save_inode_list_head(u_int64_t new_inode_list_head) {
|
||||
u_int64_t temp = superblock.inode_list_head;
|
||||
int err;
|
||||
superblock.inode_list_head = new_inode_list_head;
|
||||
if ((err = save_superblock()) < 0) {
|
||||
superblock.inode_list_head = temp;
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
#include "fs.hpp"
|
||||
#include <time.h>
|
||||
|
||||
template <typename T> T write_int(T num, char buf[]) {
|
||||
size_t i = 0;
|
||||
for (; i < sizeof(T); ++i)
|
||||
buf[i] = (char)(num >> (i * 8));
|
||||
return i;
|
||||
}
|
||||
|
||||
template <typename T> T read_int(T *num, char buf[]) {
|
||||
size_t i = 0;
|
||||
T temp = 0;
|
||||
for (; i < sizeof(T); ++i)
|
||||
temp |= (((T)buf[i]) & 0xFF) << (8 * i);
|
||||
(*num) = temp;
|
||||
return i;
|
||||
}
|
||||
|
||||
size_t write_u64(u_int64_t num, char buf[]) {
|
||||
return write_int<u_int64_t>(num, buf);
|
||||
}
|
||||
|
||||
size_t read_u64(u_int64_t *num, char buf[]) {
|
||||
return read_int<u_int64_t>(num, buf);
|
||||
}
|
||||
|
||||
size_t write_u32(u_int32_t num, char buf[]) {
|
||||
return write_int<u_int32_t>(num, buf);
|
||||
}
|
||||
|
||||
size_t read_u32(u_int32_t *num, char buf[]) {
|
||||
return read_int<u_int32_t>(num, buf);
|
||||
}
|
||||
|
||||
SuperBlock_Data::SuperBlock_Data() {
|
||||
free_list_head = 0;
|
||||
inode_list_head = 0;
|
||||
}
|
||||
|
||||
void SuperBlock_Data::serialize(char buf[]) {
|
||||
size_t i = 0;
|
||||
i += write_u64(free_list_head, &buf[i]);
|
||||
i += write_u64(inode_list_head, &buf[i]);
|
||||
}
|
||||
|
||||
void SuperBlock_Data::deserialize(char buf[]) {
|
||||
size_t i = 0;
|
||||
i += read_u64(&free_list_head, &buf[i]);
|
||||
i += read_u64(&inode_list_head, &buf[i]);
|
||||
}
|
||||
|
||||
INode_Data::INode_Data(u_int64_t inode_num) : inode_num(inode_num) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
metadata.uid = -1;
|
||||
metadata.gid = -1;
|
||||
metadata.permissions = -1;
|
||||
metadata.size = 0;
|
||||
metadata.access_time = (u_int64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
|
||||
metadata.modification_time = (u_int64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
|
||||
metadata.reference_count = 1;
|
||||
|
||||
single_indirect_block = double_indirect_block = triple_indirect_block = 0;
|
||||
|
||||
for (size_t i = 0; i < NUMBER_OF_DIRECT_BLOCKS; ++i)
|
||||
direct_blocks[i] = 0;
|
||||
}
|
||||
|
||||
size_t INode_Data::serialize_metadata(char buf[]) {
|
||||
size_t i = 0;
|
||||
i += write_u64(metadata.uid, &buf[i]);
|
||||
i += write_u64(metadata.gid, &buf[i]);
|
||||
i += write_u64(metadata.permissions, &buf[i]);
|
||||
i += write_u64(metadata.size, &buf[i]);
|
||||
i += write_u64(metadata.access_time, &buf[i]);
|
||||
i += write_u64(metadata.modification_time, &buf[i]);
|
||||
i += write_u32(metadata.reference_count, &buf[i]);
|
||||
i += write_u32(metadata.flags, &buf[i]);
|
||||
return i;
|
||||
}
|
||||
|
||||
size_t INode_Data::deserialize_metadata(char buf[]) {
|
||||
size_t i = 0;
|
||||
i += read_u64(&metadata.uid, &buf[i]);
|
||||
i += read_u64(&metadata.gid, &buf[i]);
|
||||
i += read_u64(&metadata.permissions, &buf[i]);
|
||||
i += read_u64(&metadata.size, &buf[i]);
|
||||
i += read_u64(&metadata.access_time, &buf[i]);
|
||||
i += read_u64(&metadata.modification_time, &buf[i]);
|
||||
i += read_u32(&metadata.reference_count, &buf[i]);
|
||||
i += read_u32(&metadata.flags, &buf[i]);
|
||||
return i;
|
||||
}
|
||||
|
||||
void INode_Data::serialize(char buf[]) {
|
||||
size_t i = 0;
|
||||
for (size_t j = 0; j < NUMBER_OF_DIRECT_BLOCKS; ++j)
|
||||
i += write_u64(direct_blocks[j], &buf[i]);
|
||||
i += write_u64(single_indirect_block, &buf[i]);
|
||||
i += write_u64(double_indirect_block, &buf[i]);
|
||||
i += write_u64(triple_indirect_block, &buf[i]);
|
||||
i += serialize_metadata(&buf[i]);
|
||||
}
|
||||
void INode_Data::deserialize(char buf[]) {
|
||||
size_t i = 0;
|
||||
for (size_t j = 0; j < NUMBER_OF_DIRECT_BLOCKS; ++j)
|
||||
i += read_u64(&direct_blocks[j], &buf[i]);
|
||||
i += read_u64(&single_indirect_block, &buf[i]);
|
||||
i += read_u64(&double_indirect_block, &buf[i]);
|
||||
i += read_u64(&triple_indirect_block, &buf[i]);
|
||||
i += deserialize_metadata(&buf[i]);
|
||||
}
|
@ -1,418 +0,0 @@
|
||||
#include "fs.hpp"
|
||||
|
||||
class DatablockOperation {
|
||||
public:
|
||||
DatablockOperation(int (*_skip)(DatablockOperation *, u_int64_t) = nullptr)
|
||||
: skip(_skip) {}
|
||||
size_t count;
|
||||
size_t offset;
|
||||
size_t bytes_completed;
|
||||
Fs *fs;
|
||||
virtual int operation(u_int64_t block_num, bool *delete_block) = 0;
|
||||
int (*skip)(DatablockOperation *, u_int64_t);
|
||||
};
|
||||
|
||||
int default_skip_func(DatablockOperation *this_op, u_int64_t num_blocks) {
|
||||
this_op->bytes_completed += (num_blocks * IO_BLOCK_SIZE) - this_op->offset;
|
||||
this_op->offset = 0;
|
||||
|
||||
if (this_op->bytes_completed >= this_op->count)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int truncate_skip_func(DatablockOperation *this_op, u_int64_t num_blocks) {
|
||||
this_op->offset = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Fs::sweep_inode_datablocks(INode_Data *inode_data,
|
||||
u_int64_t start_block_index, bool allocate,
|
||||
DatablockOperation *op) {
|
||||
int result;
|
||||
|
||||
// printf("SWEEP %llu %llu %llu\n", inode_data->inode_num,
|
||||
// inode_data->single_indirect_block, inode_data->double_indirect_block);
|
||||
|
||||
u_int64_t start_index = start_block_index;
|
||||
for (size_t i = start_index; i < NUMBER_OF_DIRECT_BLOCKS; ++i) {
|
||||
if ((result = sweep_datablocks(&(inode_data->direct_blocks[i]), 0, 0,
|
||||
allocate, op)) <= 0)
|
||||
return result;
|
||||
start_index = NUMBER_OF_DIRECT_BLOCKS;
|
||||
}
|
||||
|
||||
start_index -= NUMBER_OF_DIRECT_BLOCKS;
|
||||
|
||||
if (start_index < INDIRECT_BLOCKS) {
|
||||
if ((result = sweep_datablocks(&(inode_data->single_indirect_block), 1,
|
||||
start_index, allocate, op)) <= 0)
|
||||
return result;
|
||||
start_index = INDIRECT_BLOCKS;
|
||||
}
|
||||
|
||||
start_index -= INDIRECT_BLOCKS;
|
||||
|
||||
if (start_index < INDIRECT_BLOCKS * INDIRECT_BLOCKS) {
|
||||
if ((result = sweep_datablocks(&(inode_data->double_indirect_block), 2,
|
||||
start_index, allocate, op)) <= 0)
|
||||
return result;
|
||||
start_index = INDIRECT_BLOCKS * INDIRECT_BLOCKS;
|
||||
}
|
||||
|
||||
start_index -= INDIRECT_BLOCKS * INDIRECT_BLOCKS;
|
||||
|
||||
if (start_index <
|
||||
(u_int64_t)INDIRECT_BLOCKS * INDIRECT_BLOCKS * INDIRECT_BLOCKS) {
|
||||
if ((result = sweep_datablocks(&(inode_data->triple_indirect_block), 3,
|
||||
start_index, allocate, op)) <= 0)
|
||||
return result;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// This can simply be made non recursive by copy pasting - it is just
|
||||
// written this way as a proof of concept
|
||||
int Fs::sweep_datablocks(u_int64_t *block_num, int indirect_num,
|
||||
u_int64_t start_block_index, bool allocate,
|
||||
DatablockOperation *op) {
|
||||
char buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
int result = -1;
|
||||
|
||||
u_int64_t num_blocks_indirect;
|
||||
u_int64_t num_blocks = 1;
|
||||
for (int i = 0; i < indirect_num; ++i) {
|
||||
num_blocks_indirect = num_blocks;
|
||||
num_blocks *= INDIRECT_BLOCKS;
|
||||
}
|
||||
|
||||
if ((*block_num) == 0) {
|
||||
if (allocate) {
|
||||
if ((err = datablock_manager->new_datablock(block_num)) < 0)
|
||||
return err;
|
||||
} else if (op->skip != nullptr) {
|
||||
return (*(op->skip))(op, num_blocks);
|
||||
}
|
||||
}
|
||||
|
||||
// if((*block_num)>30000000000000LL)printf("DIES 1 %llu %d %llu\n",
|
||||
// *block_num, indirect_num, start_block_index);
|
||||
|
||||
if (indirect_num == 0) {
|
||||
bool delete_block = false;
|
||||
if ((result = op->operation(*block_num, &delete_block)) < 0)
|
||||
return result;
|
||||
if (delete_block) {
|
||||
if ((err = datablock_manager->free_datablock(*block_num)) < 0)
|
||||
return err;
|
||||
(*block_num) = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((*block_num) == 0) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
} else {
|
||||
|
||||
if ((err = disk->read_block(*block_num, buf)) < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
u_int64_t this_layer_start_index = start_block_index / num_blocks_indirect;
|
||||
u_int64_t next_layer_start_index =
|
||||
start_block_index - (num_blocks_indirect * this_layer_start_index);
|
||||
|
||||
u_int64_t temp;
|
||||
u_int64_t next_block_num;
|
||||
bool modified = false;
|
||||
|
||||
for (size_t i = this_layer_start_index * sizeof(u_int64_t); i < IO_BLOCK_SIZE;
|
||||
i += sizeof(u_int64_t)) {
|
||||
read_u64(&temp, &buf[i]);
|
||||
next_block_num = temp;
|
||||
|
||||
if ((result = sweep_datablocks(&next_block_num, indirect_num - 1,
|
||||
next_layer_start_index, allocate, op)) < 0)
|
||||
return result;
|
||||
if (next_block_num != temp) {
|
||||
write_u64(next_block_num, &buf[i]);
|
||||
modified = true;
|
||||
}
|
||||
if (result == 0)
|
||||
break;
|
||||
next_layer_start_index = 0;
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
bool delete_block = true;
|
||||
for (size_t i = 0; i < IO_BLOCK_SIZE; ++i)
|
||||
if (buf[i] != 0) {
|
||||
delete_block = false;
|
||||
break;
|
||||
}
|
||||
if (delete_block) {
|
||||
if ((err = datablock_manager->free_datablock(*block_num)) < 0)
|
||||
return err;
|
||||
(*block_num) = 0;
|
||||
} else {
|
||||
if ((err = disk->write_block(*block_num, buf)) < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class ReadDatablockOperation : public DatablockOperation {
|
||||
public:
|
||||
char *buf;
|
||||
ReadDatablockOperation() : DatablockOperation() {}
|
||||
int operation(u_int64_t block_num, bool *delete_block) override {
|
||||
char datablock_buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
|
||||
size_t read_size =
|
||||
std::min(IO_BLOCK_SIZE - offset, count - bytes_completed);
|
||||
|
||||
if (block_num != 0) {
|
||||
if ((block_num) > 3000000000000LL)
|
||||
printf("DIES 2\n");
|
||||
if ((err = fs->disk->read_block(block_num, datablock_buf)) < 0)
|
||||
return err;
|
||||
|
||||
memcpy(&buf[bytes_completed], &datablock_buf[offset], read_size);
|
||||
} else {
|
||||
memset(&buf[bytes_completed], 0, read_size);
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
bytes_completed += read_size;
|
||||
|
||||
if (bytes_completed >= count)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
class WriteDatablockOperation : public DatablockOperation {
|
||||
public:
|
||||
const char *buf;
|
||||
WriteDatablockOperation() : DatablockOperation() {}
|
||||
int operation(u_int64_t block_num, bool *delete_block) override {
|
||||
char datablock_buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
|
||||
size_t write_size =
|
||||
std::min(IO_BLOCK_SIZE - offset, count - bytes_completed);
|
||||
|
||||
if (write_size < IO_BLOCK_SIZE) {
|
||||
if ((block_num) > 3000000000000LL)
|
||||
printf("DIES 3\n");
|
||||
if ((err = fs->disk->read_block(block_num, datablock_buf)) < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
memcpy(&datablock_buf[offset], &buf[bytes_completed], write_size);
|
||||
|
||||
if ((err = fs->disk->write_block(block_num, datablock_buf)) < 0)
|
||||
return err;
|
||||
|
||||
offset = 0;
|
||||
bytes_completed += write_size;
|
||||
|
||||
if (bytes_completed >= count)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
class TruncateDatablockOperation : public DatablockOperation {
|
||||
public:
|
||||
TruncateDatablockOperation() : DatablockOperation(truncate_skip_func) {}
|
||||
int operation(u_int64_t block_num, bool *delete_block) override {
|
||||
char datablock_buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
|
||||
if (offset == 0) {
|
||||
(*delete_block) = true;
|
||||
return 1;
|
||||
}
|
||||
if ((block_num) > 3000000000000LL)
|
||||
printf("DIES 4\n");
|
||||
if ((err = fs->disk->read_block(block_num, datablock_buf)) < 0)
|
||||
return err;
|
||||
|
||||
memset(&datablock_buf[offset], 0, IO_BLOCK_SIZE - offset);
|
||||
|
||||
if ((err = fs->disk->write_block(block_num, datablock_buf)) < 0)
|
||||
return err;
|
||||
|
||||
offset = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
class LseekNextDataDatablockOperation : public DatablockOperation {
|
||||
public:
|
||||
LseekNextDataDatablockOperation() : DatablockOperation(default_skip_func) {}
|
||||
int operation(u_int64_t block_num, bool *delete_block) override { return 0; }
|
||||
};
|
||||
|
||||
class LseekNextHoleDatablockOperation : public DatablockOperation {
|
||||
public:
|
||||
LseekNextHoleDatablockOperation() : DatablockOperation() {}
|
||||
int operation(u_int64_t block_num, bool *delete_block) override {
|
||||
if (block_num == 0)
|
||||
return 0;
|
||||
|
||||
bytes_completed += (IO_BLOCK_SIZE)-offset;
|
||||
offset = 0;
|
||||
|
||||
if (bytes_completed >= count)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
ssize_t Fs::read(INode_Data *inode_data, char buf[], size_t count,
|
||||
size_t offset) {
|
||||
int err;
|
||||
|
||||
if (offset >= inode_data->metadata.size)
|
||||
return 0;
|
||||
|
||||
u_int64_t start_block_index = offset / IO_BLOCK_SIZE;
|
||||
size_t internal_offset = offset - (start_block_index * IO_BLOCK_SIZE);
|
||||
|
||||
ReadDatablockOperation op = ReadDatablockOperation();
|
||||
op.offset = internal_offset;
|
||||
op.buf = buf;
|
||||
op.count = std::min(count, inode_data->metadata.size - offset);
|
||||
op.bytes_completed = 0;
|
||||
op.fs = this;
|
||||
|
||||
// printf("IN READ %llu %llu %llu\n", inode_data->inode_num,
|
||||
// inode_data->single_indirect_block, inode_data->double_indirect_block);
|
||||
if ((err = sweep_inode_datablocks(inode_data, start_block_index, false,
|
||||
&op)) < 0)
|
||||
return err;
|
||||
|
||||
return op.bytes_completed;
|
||||
}
|
||||
|
||||
ssize_t Fs::write(INode_Data *inode_data, const char buf[], size_t count,
|
||||
size_t offset) {
|
||||
int err;
|
||||
|
||||
if (count + offset > FILE_SIZE_MAX) {
|
||||
errno = EFBIG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
u_int64_t start_block_index = offset / IO_BLOCK_SIZE;
|
||||
size_t internal_offset = offset - (start_block_index * IO_BLOCK_SIZE);
|
||||
|
||||
WriteDatablockOperation op = WriteDatablockOperation();
|
||||
op.offset = internal_offset;
|
||||
op.buf = buf;
|
||||
op.count = count;
|
||||
op.bytes_completed = 0;
|
||||
op.fs = this;
|
||||
|
||||
if ((err = sweep_inode_datablocks(inode_data, start_block_index, true, &op)) <
|
||||
0)
|
||||
return err;
|
||||
|
||||
inode_data->metadata.size =
|
||||
std::max(offset + op.bytes_completed, inode_data->metadata.size);
|
||||
|
||||
return op.bytes_completed;
|
||||
}
|
||||
|
||||
int Fs::truncate(INode_Data *inode_data, off_t length) {
|
||||
int err;
|
||||
|
||||
if (length > FILE_SIZE_MAX) {
|
||||
errno = EFBIG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (length < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
u_int64_t start_block_index = length / IO_BLOCK_SIZE;
|
||||
size_t internal_offset = length - (start_block_index * IO_BLOCK_SIZE);
|
||||
|
||||
TruncateDatablockOperation op = TruncateDatablockOperation();
|
||||
op.offset = internal_offset;
|
||||
op.fs = this;
|
||||
|
||||
if ((err = sweep_inode_datablocks(inode_data, start_block_index, false,
|
||||
&op)) < 0)
|
||||
return err;
|
||||
|
||||
inode_data->metadata.size = length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t Fs::lseek_next_data(INode_Data *inode_data, size_t offset) {
|
||||
int err;
|
||||
|
||||
if (offset >= inode_data->metadata.size) {
|
||||
errno = ENXIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
u_int64_t start_block_index = offset / IO_BLOCK_SIZE;
|
||||
size_t internal_offset = offset - (start_block_index * IO_BLOCK_SIZE);
|
||||
|
||||
LseekNextDataDatablockOperation op = LseekNextDataDatablockOperation();
|
||||
op.offset = internal_offset;
|
||||
op.count = inode_data->metadata.size;
|
||||
op.bytes_completed = offset;
|
||||
op.fs = this;
|
||||
|
||||
if ((err = sweep_inode_datablocks(inode_data, start_block_index, false,
|
||||
&op)) < 0)
|
||||
return err;
|
||||
|
||||
if (op.bytes_completed >= inode_data->metadata.size) {
|
||||
errno = ENXIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return op.bytes_completed;
|
||||
}
|
||||
|
||||
ssize_t Fs::lseek_next_hole(INode_Data *inode_data, size_t offset) {
|
||||
int err;
|
||||
|
||||
if (offset >= inode_data->metadata.size) {
|
||||
errno = ENXIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
u_int64_t start_block_index = offset / IO_BLOCK_SIZE;
|
||||
size_t internal_offset = offset - (start_block_index * IO_BLOCK_SIZE);
|
||||
|
||||
LseekNextHoleDatablockOperation op = LseekNextHoleDatablockOperation();
|
||||
op.offset = internal_offset;
|
||||
op.count = inode_data->metadata.size;
|
||||
op.bytes_completed = offset;
|
||||
op.fs = this;
|
||||
|
||||
if ((err = sweep_inode_datablocks(inode_data, start_block_index, false,
|
||||
&op)) < 0)
|
||||
return err;
|
||||
|
||||
if (op.bytes_completed >= inode_data->metadata.size)
|
||||
return inode_data->metadata.size;
|
||||
|
||||
return op.bytes_completed;
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
#include "fs.hpp"
|
||||
|
||||
INode_Manager::INode_Manager(Fs *fs, u_int64_t block_segment_start,
|
||||
u_int64_t block_segment_end)
|
||||
: fs(fs), block_segment_start(block_segment_start),
|
||||
block_segment_end(block_segment_end) {
|
||||
max_num_inodes = (block_segment_end - block_segment_start) * INODES_PER_BLOCK;
|
||||
}
|
||||
|
||||
u_int64_t INode_Manager::get_block_num(u_int64_t inode_num) {
|
||||
if (inode_num > max_num_inodes || inode_num == 0)
|
||||
return 0;
|
||||
u_int64_t block_num =
|
||||
block_segment_start + ((inode_num - 1) / INODES_PER_BLOCK);
|
||||
return block_num;
|
||||
}
|
||||
u_int64_t INode_Manager::get_block_offset(u_int64_t inode_num) {
|
||||
return ((inode_num - 1) % INODES_PER_BLOCK) * INODE_SIZE;
|
||||
}
|
||||
|
||||
int INode_Manager::load_inode(INode_Data *inode_data) {
|
||||
char buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
|
||||
u_int64_t block_num = get_block_num(inode_data->inode_num);
|
||||
if (block_num == 0)
|
||||
return -1;
|
||||
u_int64_t block_offset = get_block_offset(inode_data->inode_num);
|
||||
|
||||
if ((err = fs->disk->read_block(block_num, buf)) < 0)
|
||||
return err;
|
||||
|
||||
inode_data->deserialize(&buf[block_offset]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int INode_Manager::save_inode(INode_Data *inode_data) {
|
||||
char buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
|
||||
u_int64_t block_num = get_block_num(inode_data->inode_num);
|
||||
if (block_num == 0)
|
||||
return -1;
|
||||
u_int64_t block_offset = get_block_offset(inode_data->inode_num);
|
||||
|
||||
if ((err = fs->disk->read_block(block_num, buf)) < 0)
|
||||
return err;
|
||||
|
||||
inode_data->serialize(&buf[block_offset]);
|
||||
|
||||
if ((err = fs->disk->write_block(block_num, buf)) < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int INode_Manager_Freelist::new_inode(u_int64_t uid, u_int64_t gid,
|
||||
u_int64_t permissions,
|
||||
INode_Data *inode_data) {
|
||||
char buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
u_int64_t inode_num = fs->superblock.inode_list_head;
|
||||
if (inode_num > max_num_inodes)
|
||||
return -1;
|
||||
|
||||
u_int64_t block_num = get_block_num(inode_num);
|
||||
u_int64_t block_offset = get_block_offset(inode_num);
|
||||
|
||||
if (block_num == 0)
|
||||
return -1;
|
||||
|
||||
if ((err = fs->disk->read_block(block_num, buf)) < 0)
|
||||
return err;
|
||||
|
||||
u_int64_t new_inode_list_head = 0;
|
||||
read_u64(&new_inode_list_head, &buf[block_offset]);
|
||||
if ((err = fs->save_inode_list_head(new_inode_list_head)) < 0)
|
||||
return err;
|
||||
|
||||
(*inode_data) = INode_Data(inode_num);
|
||||
|
||||
inode_data->metadata.uid = uid;
|
||||
inode_data->metadata.gid = gid;
|
||||
inode_data->metadata.permissions = permissions;
|
||||
|
||||
// It is debatable if this function should do this:
|
||||
if ((err = save_inode(inode_data)) < 0) {
|
||||
inode_data->inode_num = 0xFFFFFFFFFFFFFFFF;
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
int INode_Manager_Freelist::free_inode(INode_Data *inode_data) {
|
||||
char buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
|
||||
u_int64_t block_num = get_block_num(inode_data->inode_num);
|
||||
u_int64_t block_offset = get_block_offset(inode_data->inode_num);
|
||||
|
||||
if (block_num == 0)
|
||||
return -1;
|
||||
|
||||
if ((err = fs->disk->read_block(block_num, buf)) < 0)
|
||||
return err;
|
||||
|
||||
write_u64(fs->superblock.inode_list_head, &buf[block_offset]);
|
||||
|
||||
if ((err = fs->disk->write_block(block_num, buf)) < 0)
|
||||
return err;
|
||||
|
||||
if ((err = fs->save_inode_list_head(inode_data->inode_num)) < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int INode_Manager_Freelist::format() {
|
||||
char buf[IO_BLOCK_SIZE];
|
||||
int err;
|
||||
u_int64_t next_inode_num = 2;
|
||||
for (u_int64_t i = block_segment_start; i < block_segment_end; ++i) {
|
||||
for (int j = 0; j < INODES_PER_BLOCK; ++next_inode_num, ++j) {
|
||||
if (next_inode_num > max_num_inodes)
|
||||
next_inode_num = 0;
|
||||
write_u64(next_inode_num, &buf[j * INODE_SIZE]);
|
||||
}
|
||||
if ((err = fs->disk->write_block(i, buf)) < 0)
|
||||
return err;
|
||||
}
|
||||
if ((err = fs->save_inode_list_head(1)) < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
99
lib/main.cpp
99
lib/main.cpp
@ -1,91 +1,10 @@
|
||||
#include "fischl.h"
|
||||
#include "fs.hpp"
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// printf("hello word!");
|
||||
// fischl *F = new fischl;
|
||||
// F->init();
|
||||
// char *d = strdup("/dev/vdc");
|
||||
|
||||
// RawDisk *disk = new FakeRawDisk(2048);
|
||||
// Fs *fs = new Fs(disk);
|
||||
// fs->format();
|
||||
// disk->print_block(0);
|
||||
// disk->print_block(1);
|
||||
// INode_Data inode_data = INode_Data();
|
||||
// fs->inode_manager->new_inode(1, 2, 3, &inode_data);
|
||||
// int err;
|
||||
// u_int64_t block_num = 0;
|
||||
// for (int i = 0; i < 56 + 512 + 4; ++i)
|
||||
// err = fs->allocate_datablock(&inode_data, &block_num);
|
||||
|
||||
// for (int i = 0; i < 5; ++i)
|
||||
// printf("%d\n", err = fs->deallocate_datablock(&inode_data, &block_num));
|
||||
|
||||
// fs->inode_manager->save_inode(&inode_data);
|
||||
|
||||
// disk->print_block(0);
|
||||
// disk->print_block(1);
|
||||
|
||||
// disk->print_block(1081);
|
||||
|
||||
// disk->print_block(1596);
|
||||
|
||||
// disk->print_block(1597);
|
||||
|
||||
/*
|
||||
int err;
|
||||
|
||||
RawDisk *disk = new FakeRawDisk(2048);
|
||||
Fs *fs = new Fs(disk);
|
||||
fs->format();
|
||||
disk->print_block(0);
|
||||
disk->print_block(1);
|
||||
|
||||
INode_Data inode_data;
|
||||
fs->inode_manager->new_inode(1, 2, 3, &inode_data);
|
||||
|
||||
disk->print_block(0);
|
||||
disk->print_block(1);
|
||||
|
||||
int BL_SIZE = 4096 / 8;
|
||||
|
||||
u_int64_t buf[BL_SIZE * (56 + 512 + 10)];
|
||||
|
||||
for (int i = 0; i < BL_SIZE * (56 + 512 + 10); ++i)
|
||||
buf[i] = (i / BL_SIZE) + 1;
|
||||
|
||||
err = fs->write(&inode_data, (char *)buf, 4096 * (56 + 3) + 16 + 8, 0);
|
||||
fs->inode_manager->save_inode(&inode_data);
|
||||
|
||||
printf("Write %d", err);
|
||||
|
||||
disk->print_block(0);
|
||||
disk->print_block(1);
|
||||
disk->print_block(1025);
|
||||
disk->print_block(1026);
|
||||
disk->print_block(1027);
|
||||
disk->print_block(1080);
|
||||
disk->print_block(1081);
|
||||
disk->print_block(1082);
|
||||
disk->print_block(1083);
|
||||
disk->print_block(1084);
|
||||
disk->print_block(1085);
|
||||
|
||||
int N = 5;
|
||||
|
||||
u_int64_t buf2[4096] = {0};
|
||||
err = fs->read(&inode_data, (char *)buf2, (8 * N), 4096 - 8 - 8);
|
||||
|
||||
printf("\n\nREAD: %d\n", err);
|
||||
for (int i = 0; i < N; ++i)
|
||||
printf("%d ", buf2[i]);
|
||||
printf("\n");*/
|
||||
|
||||
fischl(argc, argv);
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
#include "fischl.h"
|
||||
#include "rawdisk.h"
|
||||
|
||||
int main(){
|
||||
fischl *F = new fischl;
|
||||
F->init();
|
||||
char *d = strdup("/dev/vdc");
|
||||
RawDisk *H = new RawDisk(d);
|
||||
return 0;
|
||||
}
|
146
lib/rawdisk.cpp
146
lib/rawdisk.cpp
@ -1,146 +0,0 @@
|
||||
#include "fs.hpp"
|
||||
|
||||
void RawDisk::print_block(u_int64_t block_number) {
|
||||
const int nums_per_line = 64;
|
||||
char buf[IO_BLOCK_SIZE];
|
||||
u_int64_t num;
|
||||
|
||||
if (read_block(block_number, buf) < 0) {
|
||||
perror("Error printing datablock");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\nBlock %lu:\n", block_number);
|
||||
for (int i = 0; i < IO_BLOCK_SIZE; i += sizeof(u_int64_t)) {
|
||||
num = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
num |= ((u_int64_t)(unsigned char)buf[i + j]) << (8 * j);
|
||||
printf("%lu ", num);
|
||||
if ((i / sizeof(u_int64_t)) % nums_per_line == nums_per_line - 1)
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
RealRawDisk::RealRawDisk(const char *directory)
|
||||
: fd(-1), dir(nullptr), numSectors(0) {
|
||||
dir = directory;
|
||||
diskSize = 0;
|
||||
/*dir = strdup("/dev/vdc");
|
||||
numSectors = 62914560;
|
||||
diskSize = 32212254720;*/
|
||||
|
||||
// Open the block device (replace /dev/sdX with the actual device)
|
||||
fd = open(dir, O_RDWR); // Allow read and write
|
||||
if (fd == -1) {
|
||||
perror("Error opening device");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Use ioctl with BLKGETSIZE to get the number of sectors
|
||||
if (ioctl(fd, BLKGETSIZE64, &diskSize) == -1) {
|
||||
perror("Error getting disk size");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// diskSize = 27648 * IO_BLOCK_SIZE;
|
||||
|
||||
// Calculate the size in bytes
|
||||
numSectors = diskSize / 512; // Assuming a sector size of 512 bytes
|
||||
|
||||
printf("====Initializing RawDisk====\n");
|
||||
printf("Number of sectors: %lu\n", numSectors);
|
||||
printf("Disk size (in bytes): %lu\n", diskSize);
|
||||
}
|
||||
|
||||
RealRawDisk::~RealRawDisk() {
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
int RealRawDisk::read_block(u_int64_t block_number, char *buffer) {
|
||||
u_int64_t offset = block_number * IO_BLOCK_SIZE;
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) == (u_int64_t)-1) {
|
||||
printf("LSEEK ERROR %llu %llu\n", block_number, offset);
|
||||
perror("Error seeking to offset");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO: this is incorrect
|
||||
ssize_t bytesRead = read(fd, buffer, IO_BLOCK_SIZE);
|
||||
// printf("READ BLOCK: %llu\n", block_number);
|
||||
// for (int i = 0; i < IO_BLOCK_SIZE; i++)printf("%x", buffer[i]&0xff);
|
||||
// printf("\n");
|
||||
if (bytesRead < IO_BLOCK_SIZE) {
|
||||
perror("Error reading from device");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RealRawDisk::write_block(u_int64_t block_number, char *buffer) {
|
||||
u_int64_t offset = block_number * IO_BLOCK_SIZE;
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) == (u_int64_t)-1) {
|
||||
perror("Error seeking to offset");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO: this is incorrect
|
||||
ssize_t bytesWritten = write(fd, buffer, IO_BLOCK_SIZE);
|
||||
if (bytesWritten < IO_BLOCK_SIZE) {
|
||||
perror("Error writing to device");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FakeRawDisk::FakeRawDisk(u_int64_t num_blocks) {
|
||||
diskSize = num_blocks * IO_BLOCK_SIZE;
|
||||
disk = new char[diskSize];
|
||||
if (disk == nullptr) {
|
||||
perror("Error allocating fake disk");
|
||||
exit(1);
|
||||
}
|
||||
printf("====Initializing FAKE RawDisk====\n");
|
||||
printf("FAKE Disk size (in bytes): %lu\n", diskSize);
|
||||
perror("!!! USING FAKE RawDisk - THIS IS FOR TESTING ONLY !!!");
|
||||
}
|
||||
|
||||
FakeRawDisk::~FakeRawDisk() { delete[] disk; }
|
||||
|
||||
int FakeRawDisk::read_block(u_int64_t block_number, char *buffer) {
|
||||
u_int64_t offset = block_number * IO_BLOCK_SIZE;
|
||||
|
||||
if (offset + IO_BLOCK_SIZE > diskSize) {
|
||||
perror("Error reading past fake disk size");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buffer, &disk[offset], IO_BLOCK_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FakeRawDisk::write_block(u_int64_t block_number, char *buffer) {
|
||||
u_int64_t offset = block_number * IO_BLOCK_SIZE;
|
||||
|
||||
if (offset + IO_BLOCK_SIZE > diskSize) {
|
||||
perror("Error writing past fake disk size");
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&disk[offset], buffer, IO_BLOCK_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,69 +1,21 @@
|
||||
# set(TARGET_LAYER0 test_layer0)
|
||||
# set(TARGET_LAYER1_API test_layer1_API)
|
||||
# set(TARGET_LAYER2_API test_layer2_API)
|
||||
# set(TARGET_DIR_API test_dir_API)
|
||||
# set(DIR_PLACE /dev/vdb)
|
||||
# # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address")
|
||||
set(TARGET_LAYER0 test_layer0)
|
||||
set(TARGET_LAYER1_API test_layer1_API)
|
||||
set(DIR_PLACE /dev/vdb)
|
||||
|
||||
# # add test sources here ...
|
||||
# add_executable(${TARGET_LAYER0}
|
||||
# # add need lib and source code here
|
||||
# layer0.cpp
|
||||
# add test sources here ...
|
||||
add_executable(${TARGET_LAYER0}
|
||||
# add need lib and source code here
|
||||
layer0.cpp
|
||||
)
|
||||
add_executable(${TARGET_LAYER1_API}
|
||||
# add need lib and source code here
|
||||
layer1_API.cpp
|
||||
)
|
||||
|
||||
# ../lib/rawdisk.cpp
|
||||
# Link Google Test to your test executables
|
||||
target_link_libraries(${TARGET_LAYER0} gtest gtest_main)
|
||||
target_link_libraries(${TARGET_LAYER1_API} gtest gtest_main)
|
||||
|
||||
# )
|
||||
# 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
|
||||
# )
|
||||
|
||||
# # Link Google Test to your test executables
|
||||
# target_link_libraries(${TARGET_LAYER0} gtest gtest_main)
|
||||
# target_link_libraries(${TARGET_LAYER1_API} gtest gtest_main)
|
||||
# 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)
|
||||
# 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})
|
352
test/dir_API.cpp
352
test/dir_API.cpp
@ -1,352 +0,0 @@
|
||||
/***********************************************************
|
||||
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;*/
|
||||
return 0;
|
||||
}
|
@ -1,32 +1,33 @@
|
||||
#include "rawdisk.hpp"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "rawdisk.h"
|
||||
const char* d;
|
||||
TEST(RawDiskTest, WriteReadTest) {
|
||||
RawDisk *H = new RawDisk(d);
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *d = (argc < 2) ? "/dev/vdc" : argv[1];
|
||||
char *buf = "iloveosdfjlseirfnerig";
|
||||
char readBuffer[512] = {0};
|
||||
|
||||
RawDisk *H = new RealRawDisk(d);
|
||||
// Write test
|
||||
for(u_int64_t i = 0; i < 10; i++) {
|
||||
H->rawdisk_write(i*512, buf, strlen(buf));
|
||||
}
|
||||
|
||||
char *buf = "iloveosdfjlseirfnerig";
|
||||
char readBuffer[IO_BLOCK_SIZE] = {0}; // Initialize to zeros
|
||||
// Read and verify test
|
||||
for(u_int64_t i = 0; i < 10; i++) {
|
||||
H->rawdisk_read(i*512, readBuffer, sizeof(readBuffer));
|
||||
EXPECT_EQ(strncmp(readBuffer, buf, strlen(buf)), 0);
|
||||
}
|
||||
|
||||
// printf("dir %s, numSectors %lld, diskSize %lld \n", H->dir, H->numSectors,
|
||||
// H->diskSize);
|
||||
|
||||
// use number to substitute H->getnumSector(), getnumSectors() are not yest
|
||||
// implemented
|
||||
for (u_int64_t i = 0; i < 10; i++) {
|
||||
H->write_block(i, buf); // Change write_API
|
||||
}
|
||||
// use number to substitute H->getnumSector(), getnumSectors() are not yest
|
||||
// implemented
|
||||
for (u_int64_t i = 0; i < 10; i++) {
|
||||
H->read_block(i, readBuffer); // Change read_API
|
||||
assert(strncmp(readBuffer, buf, strlen(buf)) == 0);
|
||||
}
|
||||
|
||||
delete H; // Delete the RawDisk object
|
||||
|
||||
return 0;
|
||||
delete H;
|
||||
}
|
||||
|
||||
TEST(RawDiskTest, AssertionFailureTest) {
|
||||
EXPECT_EQ(2, 3); // Intentional failure
|
||||
EXPECT_EQ(4, 1); // Another intentional failure
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
d = (argc < 2) ? "/dev/vdc" : argv[1];//how to do with this?
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
@ -1,149 +1,123 @@
|
||||
#include "fs.hpp"
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// const char* d = (argc < 2) ? "/dev/vdc" : argv[1];
|
||||
|
||||
RawDisk *H = new FakeRawDisk(21504);
|
||||
Fs *fs = new Fs(H);
|
||||
|
||||
printf("test inode\n");
|
||||
fs->format();
|
||||
char buffer[IO_BLOCK_SIZE] = {0};
|
||||
/**************************test inode
|
||||
* Initialization***************************/
|
||||
// test the begining of inode 1th
|
||||
H->read_block(1, buffer);
|
||||
u_int64_t t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j);
|
||||
|
||||
assert(t ==
|
||||
2); // the first 1th unused inode will store the next unused inode 2th
|
||||
|
||||
assert(IO_BLOCK_SIZE == 4096);
|
||||
assert(INODE_SIZE == 512);
|
||||
|
||||
// test the number before end of inode
|
||||
H->read_block(NUM_INODE_BLOCKS, buffer);
|
||||
t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t |=
|
||||
((u_int64_t)(unsigned char)buffer[j + IO_BLOCK_SIZE - (INODE_SIZE * 2)])
|
||||
<< (8 * j);
|
||||
|
||||
assert(t == NUM_INODE_BLOCKS *
|
||||
(IO_BLOCK_SIZE / INODE_SIZE)); // store the maximun th inode
|
||||
|
||||
// test the end of inode
|
||||
t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t |= ((u_int64_t)(unsigned char)buffer[j + IO_BLOCK_SIZE - INODE_SIZE])
|
||||
<< (8 * j);
|
||||
|
||||
assert(
|
||||
t ==
|
||||
0); // the end of inode(524287th inode) do not have the next inode address
|
||||
/**************************test datablock
|
||||
* Initialization***************************/
|
||||
// we separate 2048 4kB I/O block(1+2047) as a group and the first I/O block
|
||||
// will manage the following 2047 I/O block usage. the first 8 bytes(0~7) in
|
||||
// first I/O block store the address of next first I/O block, the following
|
||||
// 256(8~263) bytes record 2047 I/O block usage. test the begining of free
|
||||
// datablock
|
||||
H->read_block(NUM_INODE_BLOCKS + 1,
|
||||
buffer); // the begining of free datablock will store
|
||||
// from address (MAX_INODE) * SECTOR_SIZE
|
||||
t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j);
|
||||
|
||||
assert(t == NUM_INODE_BLOCKS + 1 + DATABLOCKS_PER_BITMAP_BLOCK +
|
||||
1); // the first 8 bytes of 4k I/O block will store
|
||||
// the next address(after 2048*4k I/O block)
|
||||
// test the end of the datablock
|
||||
|
||||
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
|
||||
* de/allocation**********************************/
|
||||
// when requesting an inode, the inode_allocation will give you the inode
|
||||
// number, we use inode_list to store the sequence allocate inode arrary
|
||||
// version
|
||||
INode_Data inode_list[20];
|
||||
u_int64_t record_free[10] = {
|
||||
0}; // should do a pop up structure to record the free inode
|
||||
int rec = 9;
|
||||
// printf("Allocate 20 inode num:{");
|
||||
for (int i = 0; i < 20; i++) {
|
||||
fs->inode_manager->new_inode(0, 0, 0, &inode_list[i]);
|
||||
|
||||
assert(inode_list[i].inode_num == i + 1);
|
||||
// printf(" %d", inode_list[i].inode_num);
|
||||
}
|
||||
// printf("}\n");
|
||||
for (int i = 10; i < 20; i++) {
|
||||
record_free[i - 10] = inode_list[i].inode_num;
|
||||
fs->inode_manager->free_inode(
|
||||
&inode_list[i]); // free the 10 element from inode_list[10]
|
||||
}
|
||||
// allocate again the last 10
|
||||
printf("Allocate again: inode num:{");
|
||||
for (int i = 10; i < 20; i++) {
|
||||
fs->inode_manager->new_inode(0, 0, 0, &inode_list[i]);
|
||||
// printf("inode %d, rec_f %d\n,", inode_list[i],record_free[rec]);
|
||||
assert(inode_list[i].inode_num == record_free[rec]);
|
||||
rec--;
|
||||
}
|
||||
printf("}\n");
|
||||
/***************************test direct blocks[48]
|
||||
* de/allocation**********************************/
|
||||
// after free the datablock, the program will find the first smallest address
|
||||
// of datablock to give to the inode should test random resize each node, but
|
||||
// should use datablock_free data structure to record
|
||||
// u_int64_t rec_datablock_free[10][3] = {0}; // array version
|
||||
// u_int64_t temp_block_num = 0;
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// // printf("%dth data block starting addres: ", i);
|
||||
// for (int j = 0; j < 6; j++) {
|
||||
// fs->allocate_datablock(&inode_list[i], &temp_block_num);
|
||||
// // printf("%d," ,inode_inside[i].datablock_allocate(*H));
|
||||
// }
|
||||
// // printf("\n");
|
||||
// }
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// // printf("%dth data block free addres: ", i);
|
||||
// for (int j = 2; j >= 0; j--) {
|
||||
// fs->deallocate_datablock(&inode_list[i],
|
||||
// &(rec_datablock_free[i][j]));
|
||||
// // printf("", rec_datablock_free[i][j]);
|
||||
// }
|
||||
// // printf("\n");
|
||||
// }
|
||||
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// // printf("%dth data block allocate again addres: ", i);
|
||||
// for (int j = 0; j < 3; j++) {
|
||||
// fs->allocate_datablock(&inode_list[i], &temp_block_num);
|
||||
// assert(temp_block_num == rec_datablock_free[i][j]);
|
||||
// // printf("%d," ,inode_inside[i].datablock_allocate(*H));
|
||||
// }
|
||||
// // printf("\n");
|
||||
// }
|
||||
|
||||
// printf("}\n");
|
||||
delete H; // Delete the RawDisk object
|
||||
|
||||
return 0;
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "fs.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
const char* d;
|
||||
|
||||
TEST(Layer1Test, APItest) {
|
||||
RawDisk *H = new RawDisk(d);
|
||||
|
||||
printf("test inode\n");
|
||||
INodeOperation inop;
|
||||
inop.initialize(*H);//for inode initialization and datablock initialization
|
||||
char buffer[8] = {0};
|
||||
/**************************test inode Initialization***************************/
|
||||
//test the begining of inode 1th
|
||||
H->rawdisk_read((1) * SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
u_int64_t t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j);
|
||||
|
||||
EXPECT_EQ(t, 2);//the first 1th unused inode will store the next unused inode 2th
|
||||
//test the number before end of inode 524286th
|
||||
H->rawdisk_read((MAX_INODE - 2) * SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j);
|
||||
|
||||
EXPECT_EQ(t, MAX_INODE - 1);//store the maximun th inode
|
||||
//test the end of inode 524287th
|
||||
H->rawdisk_read((MAX_INODE - 1) * SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j);
|
||||
|
||||
EXPECT_EQ(t, 0);//the end of inode(524287th inode) do not have the next inode address
|
||||
/**************************test datablock Initialization***************************/
|
||||
//we separate 2048 4kB I/O block(1+2047) as a group and the first I/O block will manage the following 2047 I/O block usage.
|
||||
//the first 8 bytes(0~7) in first I/O block store the address of next first I/O block, the following 256(8~263) bytes record 2047 I/O block usage.
|
||||
//test the begining of free datablock
|
||||
H->rawdisk_read((MAX_INODE) * SECTOR_SIZE, buffer, sizeof(buffer));//the begining of free datablock will store from address (MAX_INODE) * SECTOR_SIZE
|
||||
t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j);
|
||||
|
||||
EXPECT_EQ(t, (MAX_INODE+2048*8)*SECTOR_SIZE);//the first 8 bytes of 4k I/O block will store the next address(after 2048*4k I/O block)
|
||||
//test the end of the datablock
|
||||
H->rawdisk_read((MAX_BLOCKNUM - 2048*8) * SECTOR_SIZE, buffer, sizeof(buffer));
|
||||
t = 0;
|
||||
for (int j = 0; j < 8; j++)
|
||||
t |= ((u_int64_t)(unsigned char)buffer[j]) << (8 * j);
|
||||
|
||||
EXPECT_EQ(t, (MAX_BLOCKNUM)*SECTOR_SIZE);
|
||||
|
||||
/***************************test inode de/allocation**********************************/
|
||||
//when requesting an inode, the inode_allocation will give you the inode number, we use inode_list to store the sequence allocate inode
|
||||
//arrary version
|
||||
int inode_list[20] = {0};
|
||||
int record_free[10] = {0};//should do a pop up structure to record the free inode
|
||||
int rec = 9;
|
||||
//printf("Allocate 20 inode num:{");
|
||||
for(int i=0;i<20;i++){
|
||||
inode_list[i] = inop.inode_allocate(*H);
|
||||
EXPECT_EQ(inode_list[i], i+1);
|
||||
//printf(" %d", inode_list[i]);
|
||||
}
|
||||
//printf("}\n");
|
||||
for(int i=10;i<20;i++){
|
||||
inop.inode_free(*H,inode_list[i]);//free the 10 element from inode_list[10]
|
||||
record_free[i-10] = inode_list[i];
|
||||
}
|
||||
//allocate again the last 10
|
||||
printf("Allocate again: inode num:{");
|
||||
for(int i=10;i<20;i++){
|
||||
inode_list[i] = inop.inode_allocate(*H);
|
||||
//printf("inode %d, rec_f %d\n,", inode_list[i],record_free[rec]);
|
||||
EXPECT_EQ(inode_list[i], record_free[rec]);
|
||||
rec--;
|
||||
}
|
||||
printf("}\n");
|
||||
/***************************test direct blocks[48] de/allocation**********************************/
|
||||
//after free the datablock, the program will find the first smallest address of datablock to give to the inode
|
||||
//should test random resize each node, but should use datablock_free data structure to record
|
||||
INode inode_inside[10];
|
||||
u_int64_t rec_datablock_free[10][3] = {0};//array version
|
||||
for(int i=0;i<10;i++){
|
||||
inode_inside[i].inode_construct(inode_list[i],*H);
|
||||
//printf("%dth data block starting addres: ", i);
|
||||
for(int j=0;j<6;j++){
|
||||
inode_inside[i].datablock_allocate(*H);
|
||||
//printf("%d," ,inode_inside[i].datablock_allocate(*H));
|
||||
}
|
||||
//printf("\n");
|
||||
}
|
||||
for(int i=0;i<10;i++){
|
||||
//printf("%dth data block free addres: ", i);
|
||||
for(int j = 2;j >=0;j--){
|
||||
rec_datablock_free[i][j] = inode_inside[i].datablock_deallocate(*H);
|
||||
//printf("", rec_datablock_free[i][j]);
|
||||
}
|
||||
//printf("\n");
|
||||
}
|
||||
|
||||
for(int i=0;i<10;i++){
|
||||
//printf("%dth data block allocate again addres: ", i);
|
||||
for(int j=0;j<3;j++){
|
||||
EXPECT_EQ(inode_inside[i].datablock_allocate(*H), rec_datablock_free[i][j]);
|
||||
//printf("%d," ,inode_inside[i].datablock_allocate(*H));
|
||||
}
|
||||
//printf("\n");
|
||||
}
|
||||
|
||||
//printf("}\n");
|
||||
delete H; // Delete the RawDisk object
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
d = (argc < 2) ? "/dev/vdc" : argv[1];//how to do with this?
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
// #include "fs.h"
|
||||
// #include <assert.h>
|
||||
// #include <inttypes.h>
|
||||
// #include <stdio.h>
|
||||
// #include <string.h>
|
||||
|
||||
// // in fs.h:
|
||||
// // #define MAX_INODE 2048
|
||||
// // #define MAX_BLOCKNUM 51200
|
||||
// // 51200 Sectors = 2048 * 25 Sectors = 25MB
|
||||
// // Free List Heads: 2048*512, 2048*9*512, 2048*17*512
|
||||
// // Available INodes: 2047 (-1 because superblock)
|
||||
// // Available DataBlocks (including Indirect Blocks): 2047 * 3 = 6141
|
||||
|
||||
// int main(int argc, char *argv[]) {
|
||||
// const char *d = (argc < 2) ? "/dev/vdc" : argv[1];
|
||||
|
||||
// RawDisk *H = new FakeRawDisk(d);
|
||||
|
||||
// printf("=== INode Alloc/Dealloc Test ===\n");
|
||||
// INodeOperation inop;
|
||||
// inop.initialize(*H);
|
||||
|
||||
// // Test INode alloc and dealloc
|
||||
// int inode_list[2046] = {
|
||||
// 0}; // if we allocate 2047 inodes head will be 0 (faulty)
|
||||
// printf("freeInodeHead: %d \n",
|
||||
// SuperBlock::getFreeINodeHead(*H)); // this impl should give 1
|
||||
// for (int i = 0; i < 2046; i++) {
|
||||
// inode_list[i] = inop.inode_allocate(*H);
|
||||
// if (SuperBlock::getFreeINodeHead(*H) == 0) {
|
||||
// printf("%d\n", i);
|
||||
// assert(false);
|
||||
// }
|
||||
// }
|
||||
// printf("freeInodeHead: %d \n",
|
||||
// SuperBlock::getFreeINodeHead(*H)); // this impl should give 2047
|
||||
// for (int i = 0; i < 1024; i++) {
|
||||
// inop.inode_free(*H, inode_list[i]);
|
||||
// }
|
||||
// for (int i = 0; i < 1022; i++) {
|
||||
// inode_list[i] = inop.inode_allocate(*H);
|
||||
// assert(SuperBlock::getFreeINodeHead(*H) != 0);
|
||||
// }
|
||||
// printf("freeInodeHead: %d \n",
|
||||
// SuperBlock::getFreeINodeHead(*H)); // this impl should give 2
|
||||
// inode_list[1022] = inop.inode_allocate(*H);
|
||||
// printf("freeInodeHead: %d \n",
|
||||
// SuperBlock::getFreeINodeHead(*H)); // this impl should give 1
|
||||
// inode_list[1023] = inop.inode_allocate(*H);
|
||||
// printf("freeInodeHead: %d \n",
|
||||
// SuperBlock::getFreeINodeHead(*H)); // this impl should give 2047
|
||||
|
||||
// // Test Many Files
|
||||
// printf("=== Many Files Test ===\n");
|
||||
// INode inode_inside[100];
|
||||
// for (int i = 0; i < 100; i++) {
|
||||
// inode_inside[i].inode_construct(inode_list[i], *H);
|
||||
// for (int j = 0; j < 60;
|
||||
// j++) { // Note that 1 indirect block is used for each file
|
||||
// u_int64_t allcBlockNum = inode_inside[i].datablock_allocate(*H);
|
||||
// if (SuperBlock::getFreeListHead(*H) >= (u_int64_t)51200 * 512) {
|
||||
// printf("Bad FreeListHead: %d, %d, %llu\n", i, j,
|
||||
// SuperBlock::getFreeListHead(*H));
|
||||
// assert(false);
|
||||
// }
|
||||
// if (allcBlockNum % 2048 != 0 || allcBlockNum < 2048 * 512 ||
|
||||
// allcBlockNum >= 25 * 2048 * 512) {
|
||||
// printf("Bad Allocated Block Number: %d, %d, %llu\n", i, j,
|
||||
// allcBlockNum);
|
||||
// assert(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// printf("Finished Allocating\n");
|
||||
// // in this impl should give 17*2048*512 = 17825792
|
||||
// printf(
|
||||
// "freeListHead: %llu \n",
|
||||
// SuperBlock::getFreeListHead(
|
||||
// *H)); // if all 6141 blocks allocated, would give 51200*512
|
||||
// (faulty)
|
||||
// for (int i = 0; i < 100; i++) {
|
||||
// for (int j = 0; j < 59; j++) {
|
||||
// u_int64_t freedBlkNum = inode_inside[i].datablock_deallocate(*H);
|
||||
// u_int64_t fh = SuperBlock::getFreeListHead(*H);
|
||||
// if (freedBlkNum % 2048 != 0 || freedBlkNum < 2048 * 512 ||
|
||||
// freedBlkNum >= 25 * 2048 * 512 || fh >= 51200 * 512) {
|
||||
// printf("%d, %d, Freed Block Number: %llu\n", i, j, freedBlkNum);
|
||||
// printf("FreeListHead is %llu\n", fh);
|
||||
// assert(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// printf("Finished Deallocating\n");
|
||||
// printf("freeListHead: %d \n", SuperBlock::getFreeListHead(*H));
|
||||
|
||||
// // Test Big File (Use direct, single indirect, double indirect)
|
||||
// printf("=== Big File Test ===\n");
|
||||
// u_int64_t lastAllc = 0;
|
||||
// for (int j = 0; j < 5000; j++) {
|
||||
// u_int64_t allcBlockNum = inode_inside[0].datablock_allocate(*H);
|
||||
// lastAllc = allcBlockNum;
|
||||
// u_int64_t fh = SuperBlock::getFreeListHead(*H);
|
||||
// if (allcBlockNum % 2048 != 0 || allcBlockNum < 2048 * 512 ||
|
||||
// allcBlockNum >= 25 * 2048 * 512 || fh >= 51200 * 512) {
|
||||
// printf("%d, Alloc Block Number: %llu\n", j, allcBlockNum);
|
||||
// printf("FreeListHead is %llu\n", fh);
|
||||
// assert(false);
|
||||
// }
|
||||
// }
|
||||
// printf("last allocate for big file: %llu\n", lastAllc);
|
||||
// printf("Finished Allocating\n");
|
||||
// printf("freeListHead: %d \n", SuperBlock::getFreeListHead(*H));
|
||||
// for (int j = 0; j < 5000; j++) {
|
||||
// u_int64_t freedBlkNum = inode_inside[0].datablock_deallocate(*H);
|
||||
// u_int64_t fh = SuperBlock::getFreeListHead(*H);
|
||||
// if (freedBlkNum % 2048 != 0 || freedBlkNum < 2048 * 512 ||
|
||||
// freedBlkNum >= 25 * 2048 * 512 || fh >= 51200 * 512) {
|
||||
// printf("%d, Freed Block Number: %llu\n", j, freedBlkNum);
|
||||
// printf("FreeListHead is %llu\n", fh);
|
||||
// assert(false);
|
||||
// }
|
||||
// }
|
||||
// printf("Finished Deallocating\n");
|
||||
// printf("freeListHead: %d \n", SuperBlock::getFreeListHead(*H));
|
||||
|
||||
// delete H; // Delete the RawDisk object
|
||||
|
||||
// return 0;
|
||||
// }
|
@ -1,120 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,377 +0,0 @@
|
||||
#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;
|
||||
|
||||
FileNode* fischl_load_entry(TreeNode *root, const char *path){
|
||||
return fischl_find_entry(fs, root, path);
|
||||
}
|
||||
|
||||
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_load_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_load_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_load_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_load_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;
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
#include "fischl.h"
|
||||
#include <assert.h>
|
||||
|
||||
void testFischlInit(){
|
||||
fischl *F = new fischl;
|
||||
assert(F->init()==3);
|
||||
delete F;
|
||||
}
|
||||
|
||||
int main(){
|
||||
testFischlInit();
|
||||
return 0;
|
||||
#include "fischl.h"
|
||||
#include <assert.h>
|
||||
|
||||
void testFischlInit(){
|
||||
fischl *F = new fischl;
|
||||
assert(F->init()==3);
|
||||
delete F;
|
||||
}
|
||||
|
||||
int main(){
|
||||
testFischlInit();
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user