A half assed, buggy implementation of a Tiny File System. Not useful for anything as is.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- The application is currently single threaded | |
- The following commands have been implemented: | |
- exit | |
- create <PATH> | |
- open <PATH> | |
- display | |
- ls | |
- cd <TFS> (partial) | |
- mkdir <TFS> (partial). currently buggy due to nibble manipulation | |
- import <PATH> <TFS> (partial) stub + error checking only | |
- export <PATH> <TFS> (partial) stub + error checking only | |
- rm <TFS> stub only |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef _WIN32 | |
#define _CRT_SECURE_NO_DEPRECATE | |
#endif | |
#define BLOCK_SIZE 16 | |
#define BLOCK_COUNT 16 | |
#include <cctype> | |
#include <fstream> | |
#include <iomanip> | |
#include <iostream> | |
#include <iterator> | |
#include <math.h> | |
//#include <pthread.h> | |
#include <sstream> | |
#include <stdlib.h> | |
#include <sys/stat.h> | |
#include <vector> | |
#include <map> | |
using namespace std; | |
typedef unsigned char byte; | |
FILE* tfsDisk = NULL; | |
byte workingDir = 0; | |
string workingDirName() { | |
if(workingDir == 0) | |
return "/"; | |
else //TODO: generate path | |
return "/.."; | |
} | |
//TODO: lock function that will lock a given block (range?) or wait until it can lock those blocks | |
// perhaps use a mutex for each block on the disk | |
//static pthread_mutex_t mutexes[BLOCK_COUNT] = { P99_DUPL(BLOCK_COUNT, PTHREAD_MUTEX_INITIALIZER) }; | |
//<http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html> | |
bool fileExists(string fileName){ | |
struct stat my_stat; | |
return (stat(fileName.c_str(), &my_stat) == 0); | |
} | |
bool directoryExists(string fileName) { | |
struct stat my_stat; | |
if (stat(fileName.c_str(), &my_stat) != 0) return false; | |
return ((my_stat.st_mode & S_IFDIR) != 0); | |
} | |
vector<string> parseCommand() { | |
vector<string> tokens; | |
char inputBuffer[256]; | |
cin.getline(inputBuffer, sizeof(inputBuffer)); | |
string inputString = inputBuffer; | |
string Buffer; | |
stringstream ss(inputString); | |
while(ss >> Buffer) | |
tokens.push_back(Buffer); | |
return tokens; | |
} | |
bool validArgCount(vector<string> tokens, int size) { | |
if(tokens.size() == size) | |
return true; | |
cerr << "Invalid number of arguments" << endl; | |
return false; | |
} | |
bool diskAvailable() { | |
if(tfsDisk) return true; | |
cerr << "A disk has not been loaded" << endl; | |
return false; | |
} | |
byte* getBlock(byte blockNum){ | |
static byte block[BLOCK_SIZE] = {}; | |
rewind(tfsDisk); | |
for(byte i = 0; i < BLOCK_COUNT; i++) | |
fread(block,1,BLOCK_SIZE,tfsDisk); | |
return block; | |
} | |
bool hasFreeSpace() { | |
byte* block = getBlock(0); | |
if((block[0] & 0x0F) == 0xF) | |
return true; | |
cerr << "No free space on disk" << endl; | |
return false; | |
} | |
void cmd_import(vector<string> args){ | |
if(validArgCount(args,3) && diskAvailable() && hasFreeSpace()/* && isValidPath(args[1]) && isValidTfsPath(args[2])*/) { | |
//TODO | |
cout << "not implemented" << endl; | |
} | |
} | |
void cmd_export(vector<string> args){ | |
if(validArgCount(args,3) && diskAvailable() /*&& isValidPath(args[1]) && isValidTfsPath(args[2])*/) { | |
//TODO | |
cout << "not implemented" << endl; | |
} | |
} | |
bool blockHasFolder(byte dir, char name){ | |
fseek(tfsDisk,dir+1,SEEK_SET); | |
for(unsigned int i = 0; i < 10; i++) { | |
if(name == (char)fgetc(tfsDisk)) | |
return true; | |
} | |
return false; | |
} | |
byte getFolderBlock(byte dir, char name){ | |
fseek(tfsDisk,dir+1,SEEK_SET); //move to contents section | |
unsigned int i = 1; //find integer corresponding to folder name | |
for(i; i <= 10; i++) | |
if(name == (char)fgetc(tfsDisk)) | |
break; | |
//move to the byte containing the nibble desired | |
fseek(tfsDisk,dir + 10 + ceil((double)(i/2)),SEEK_SET); | |
byte c = fgetc(tfsDisk); | |
byte newDir = i % 2 == 0 ? | |
(c & 0x0F) //right nibble | |
: (c & 0xF0) >> 4; //left nibble | |
return newDir; | |
} | |
void cmd_ls(vector<string> args){ | |
if(validArgCount(args,1) && diskAvailable()) { | |
fseek(tfsDisk,workingDir+1,SEEK_SET); | |
for(unsigned int i = 0; i < 10; i++) | |
cout << ((char)fgetc(tfsDisk)) << " "; | |
cout << endl; | |
} | |
} | |
void cmd_cd(vector<string> args){ | |
cout << "not implemented" << endl; | |
if(diskAvailable()) { | |
if(args.size() == 1) { | |
workingDir = 0; | |
} else if(args.size() == 2){ | |
string tfsPath = args[1]; | |
byte dir = 0; | |
if(tfsPath == "/") { | |
dir = 0; | |
} else { | |
unsigned int len = tfsPath.length(); | |
if(len %2 != 0) { | |
cerr << "Invalid path" << endl; | |
return; | |
} else { | |
char fName; | |
for(unsigned int i = 0; i < len; i += 2) { | |
fName = tfsPath.at(i+1); | |
if(tfsPath.at(i) != '/' || fName < 'A' || fName > 'Z' || !blockHasFolder(dir,fName)) { | |
cerr << "Invalid path" << endl; | |
return; | |
} else { | |
dir = getFolderBlock(dir,fName); | |
} | |
} | |
} | |
} | |
workingDir = dir; | |
} else { | |
validArgCount(args,2); | |
} | |
} | |
} | |
void cmd_display(vector<string> args) { | |
if(validArgCount(args,1) && diskAvailable()) { | |
rewind(tfsDisk); | |
byte buffer[BLOCK_SIZE]; | |
for(unsigned int i = 0; i < BLOCK_COUNT; i++){ | |
cout << hex << i << " | "; | |
fread(buffer,1,BLOCK_SIZE,tfsDisk); | |
for(unsigned int j = 0; j < BLOCK_SIZE; j++) | |
cout << setw(2) << setfill('0') << hex << (int)buffer[j] << " "; | |
cout << endl; | |
} | |
} | |
} | |
void cmd_open(vector<string> args){ | |
if(validArgCount(args,2)) { | |
string path = args[1]; | |
if(fileExists(path)) { | |
struct stat filestatus; //check size | |
stat(path.c_str(),&filestatus); | |
if(filestatus.st_size != 256){ | |
cerr << "invalid file size. 256 bytes expected (16 blocks x 16 bytes) " << endl; | |
} else { | |
if(tfsDisk != NULL) fclose(tfsDisk); | |
try{ | |
tfsDisk = fopen(path.c_str(), "rb+"); | |
workingDir = 0; | |
} catch(int e) { | |
cerr << e << endl; | |
} | |
} | |
} else { | |
cerr << "File does not exist" << endl; | |
} | |
} | |
} | |
FILE* createFile(string& filePath){ | |
FILE* file; | |
try{ | |
file = fopen(filePath.c_str(), "wb"); | |
} catch(int e) { | |
cerr << e << endl; | |
} | |
return file; | |
} | |
void writeBlock(FILE* file, byte* block) { | |
for(byte i = 0; i < BLOCK_COUNT; i++) | |
fputc(block[i],file); | |
} | |
void replaceBlock(byte position, byte* block){ | |
fseek(tfsDisk,position * BLOCK_SIZE,SEEK_SET); | |
for(unsigned int i = 0; i < BLOCK_SIZE; i++) | |
fputc(block[0],tfsDisk); | |
} | |
void cmd_create(vector<string> args){ | |
if(validArgCount(args,2)) { | |
string path = args[1]; | |
if(fileExists(path)) { | |
cerr << "File already exists" << endl; | |
} else { | |
FILE* file = createFile(path); | |
if(file) { | |
byte block[BLOCK_SIZE] = {}; | |
//root directory | |
block[0] = 0xF1; | |
writeBlock(file,block); | |
//free space blocks 1-14 | |
for(byte i = 1; i < BLOCK_COUNT - 1; i++){ | |
block[0] = (byte)(i+1); | |
writeBlock(file,block); | |
} | |
//create last free space block | |
block[0] = 0xFF; | |
writeBlock(file,block); | |
fclose(file); | |
} | |
} | |
} | |
} | |
void cmd_exit(vector<string> args){ | |
if(validArgCount(args,1)) { | |
if (tfsDisk != NULL) | |
fclose(tfsDisk); | |
exit(EXIT_SUCCESS); | |
} | |
} | |
bool dirBlockHasEntrySpace(byte* block){ | |
for(unsigned int i = 1; i <= 10; i++){ | |
if(block[i] == 0) | |
return true; | |
} | |
return false; | |
} | |
bool dirBlockHasEntryName(byte* block, char fName) { | |
for(unsigned int i = 1; i <= 10; i++){ | |
if(block[i] == fName) | |
return true; | |
} | |
return false; | |
} | |
byte getFreeEntryPos(byte* block){ | |
unsigned int i; | |
for(i = 1; i <= 10; i++) | |
if(block[i] == 0) | |
break; | |
return i; | |
} | |
void cmd_mkdir(vector<string> args){ | |
if(validArgCount(args,2) && diskAvailable() && hasFreeSpace()) { | |
string tfsPath = args[1]; | |
unsigned int len = tfsPath.length(); | |
if(tfsPath == "/" || len < 2) { | |
cerr << "invalid path" << endl; | |
} else { | |
string parentPath = ""; | |
char fName; | |
byte saveDir = workingDir; //leveraging CD command, so need to save workingDir to later restore | |
if(len % 2 != 0 || tfsPath.at(len-2) != '/') { | |
cerr << "invalid path" << endl; | |
} else { | |
parentPath = len == 2 ? | |
tfsPath.substr(0,1) : | |
tfsPath.substr(0,len-2); | |
fName = tfsPath.at(len-1); | |
if(fName < 'A' || fName > 'Z') { | |
cerr << "invalid path" << endl; | |
return; | |
} | |
workingDir = 0xFF; //flag for error checking | |
vector<string> args2; //navigate to the parent folder of the path | |
args2.push_back("cd"); | |
args2.push_back(parentPath); | |
cmd_cd(args2); | |
if(workingDir != 0xFF) { //success | |
byte * curBlock = getBlock(workingDir); | |
if(dirBlockHasEntrySpace(curBlock) && !dirBlockHasEntryName(curBlock,fName)){ | |
//get free space position from the root | |
byte* rootBlock = getBlock(0); | |
byte freeSpacePosition = rootBlock[0] & 0xF0; | |
//get the block at the free space position | |
byte* newBlock = getBlock(freeSpacePosition); | |
// add name to the entry location, and update directory target | |
byte freeEntryPos = getFreeEntryPos(curBlock); | |
byte freeEntryTargetPos = 10 + ceil((double)(freeEntryPos/2)); | |
curBlock[freeEntryPos] = fName; | |
// from the free space block, get the position of the next free space block | |
// set the root dir free space value to this new value | |
byte nextFreePosition = rootBlock[0] & 0x0F; | |
//set the target position | |
curBlock[freeEntryTargetPos] &= freeEntryPos % 2 == 0 ? | |
(nextFreePosition & 0x0F) //right nibble | |
: (nextFreePosition & 0xF0) >> 4; //left nibble | |
//update free space position | |
rootBlock[0] = nextFreePosition & 0x0F; | |
replaceBlock(0,rootBlock); | |
//createNewDirectory (byte[0] bit[0-3] = parentDir) (byte[0] bit[4-7] = position) | |
newBlock[0] = (workingDir & 0xF0) & (freeSpacePosition & 0x0F); | |
for(unsigned int i = 1; i < BLOCK_SIZE; i++) | |
newBlock[i] = 0; //byte[1-15] = 0 | |
replaceBlock(freeSpacePosition,newBlock); | |
cout << "created " << tfsPath << endl; | |
} | |
} | |
} | |
workingDir = saveDir; //restore workingDir | |
} | |
} | |
} | |
void cmd_rm(vector<string> args){ | |
if(validArgCount(args,2) && diskAvailable()) { | |
//TODO | |
cout << "not implemented" << endl; | |
} | |
} | |
void mountDisk(int argc, char *argv[]) { | |
if(argc == 1) { | |
tfsDisk = 0; | |
} else if(argc == 2) { | |
vector<string> cmd; | |
cmd.push_back("open"); | |
cmd.push_back(argv[1]); | |
cmd_open(cmd); | |
} else { | |
cerr << "usage: " << argv[0] << " [TFS-disk path]" << endl; | |
exit(EXIT_FAILURE); | |
} | |
} | |
typedef void (*function)(vector<string>); //function pointer type | |
typedef map<string,function> commandMap; | |
int main(int argc, char *argv[]) { | |
commandMap cmdMap; | |
cmdMap.insert(make_pair("import",&cmd_import)); | |
cmdMap.insert(make_pair("export",&cmd_export)); | |
cmdMap.insert(make_pair("ls",&cmd_ls)); | |
cmdMap.insert(make_pair("cd",&cmd_cd)); | |
cmdMap.insert(make_pair("display",&cmd_display)); | |
cmdMap.insert(make_pair("open",&cmd_open)); | |
cmdMap.insert(make_pair("create",&cmd_create)); | |
cmdMap.insert(make_pair("exit",&cmd_exit)); | |
cmdMap.insert(make_pair("mkdir",&cmd_mkdir)); | |
cmdMap.insert(make_pair("rm",&cmd_rm)); | |
mountDisk(argc,argv); | |
workingDir = 0; | |
//TODO: reset mutex array | |
while(true) { | |
cout << "TFS " << "<" << workingDirName() << ">" << ": "; | |
vector<string> tokens = parseCommand(); | |
if(tokens.size() == 0 || tokens.size() > 3) { | |
cerr << "Invalid command" << endl; | |
} else { | |
//TODO: create a new thread for each command? | |
//https://stackoverflow.com/questions/13203307/variable-number-of-threads-c | |
string cmd = tokens[0]; | |
if(cmdMap.count(cmd)){ //find and call the command with the given name | |
commandMap::const_iterator iter = cmdMap.find(cmd); | |
(*iter->second)(tokens); | |
} else { | |
cerr << "Invalid command" << endl; | |
} | |
} | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
tfs: | |
g++ main.cpp -o tfs.exe |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment