Skip to content

Instantly share code, notes, and snippets.

@mlhaufe
Created December 10, 2018 05:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mlhaufe/988ffaa5194666250ae623fac84eab41 to your computer and use it in GitHub Desktop.
Save mlhaufe/988ffaa5194666250ae623fac84eab41 to your computer and use it in GitHub Desktop.
A half assed, buggy implementation of a Tiny File System. Not useful for anything as is.
- 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
#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;
}
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