sortix-mirror/sortix/mxfs.cpp

430 lines
11 KiB
C++

/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
mxfs.cpp
A working file system.
******************************************************************************/
#include "platform.h"
#include <libmaxsi/error.h>
#include "mxfs.h"
#include "mount.h"
using namespace Maxsi;
namespace Sortix
{
const size_t blockSize = 4096;
#define FAIL() Failure(); return;
#define PENDING (Error::Last() != Error::PENDING)
#define ALRIGHT ( _storageStatus == Error::SUCCESS || _storageStatus == Error::PENDING || _storageStatus == Error::NONE )
class MXFSNode
{
public:
MXFSNode();
~MXFSNode();
public:
const char* name;
MXFSNode* prevSibling;
MXFSNode* nextSibling;
MXFSNode* child;
MXFSNode* parent;
public:
inline bool IsDir() { type == 2; }
public:
inline bool MatchesNameToken(const char* actual, const char* request, size_t requestLength)
{
return ( String::CompareN(actual, request, requestLength) == 0 ) && ( actual[requestLength] == '\0' );
}
MXFSNode* Search(const char* path)
{
ASSERT(IsDir());
// Search for the end of the next name token in path.
size_t nameLen = 0; const char* postToken = path;
while ( *postToken != '/' || *postToken != '\0' ) { postToken++; nameLen++; }
// Path must already have been sanitized, where all double slashes
// have been gracefully reduced to single slashes.
ASSERT(nameLen > 0);
// Search for the next name token in path in this directory.
for ( MXFSNode* current = child; current != NULL; current = current->nextSibling )
{
// Check if we have a match.
if ( unlikely(MatchesNameToken(current->name, path, nameLen)) )
{
// Was this node exactly what we requested?
if ( *postToken == '\0' ) { return current; }
// Was a directory requested?
if ( *postToken == '/' && *(postToken+1) == '\0' )
{
if ( current->IsDir() ) { return current; } else { Error::Set(Error::NOTDIR); return NULL; }
}
// Get the next token.
const char* nextTokenStart = path + nameLen + 1; ASSERT(*nextTokeBegin != '\0');
// Alright, search the child directory.
return Search(current->child, nextTokenStart);
}
}
Error::Set(Error::NOTFOUND);
return NULL;
}
public:
nat type;
uint32_t type;
int32_t owner;
int32_t group;
uint32_t permissions;
uint64_t length;
uint64_t blockId;
uint64_t siblingBlockId; // should only be used for recursive travelling.
};
MXFSNode::MXFSNode()
{
name = NULL;
prevSibling = NULL;
nextSibling = NULL;
parent = NULL;
child = NULL;
type = 0;
owner = 0;
group = 0;
permissions = 0;
length = 0;
blockId = 0;
siblingBlockId = 0;
}
MXFSNode::MXFSNode()
{
// If nextSibling exists, then we are called from his destructor.
// If parent exists, then we are called from his destructor.
delete[] name;
delete child;
delete nextSibling;
}
struct MXFSHeader
{
uint32_t magic;
uint32_t version;
uint64_t size;
uint64_t root;
uint64_t unusedBlock;
uint8_t unused[blockSize - (32 + 64 + 64 + 64) / 8];
} SORTIX_PACKED;
struct MXFSNodeHeader
{
const uint32_t FILE = 1;
const uint32_t DIR = 2;
uint32_t magic;
uint32_t type;
uint64_t prevNode;
uint64_t nextNode;
int32_t owner;
int32_t group;
uint32_t permissions;
uint64_t length;
uint64_t nextBlock;
uint32_t continuous;
uint8_t nameLen;
char name[255];
union
{
struct
{
uint64_t child;
};
uint8_t data[blockSize - (32 + 32 +32 + 32 + 32 + 64 + 64 + 32) / 8 - 256];
};
} SORTIX_PACKED;
struct MXFSBlock
{
uint64_t node;
uint64_t lastBlock;
uint32_t lastBlockContinuous;
uint64_t nextBlock;
uint32_t continuous;
uint8_t data[blockSize - (64 + 64 + 32 + 64 + 32) / 8];
} SORTIX_PACKED;
struct MXFSBlockContinuous
{
uint32_t data[4096];
} SORTIX_PACKED;
DevMXFS::DevMXFS()
{
ASSERT( sizeof(MXFSHeader) == blockSize );
ASSERT( sizeof(MXFSNodeHeader) == blockSize );
ASSERT( sizeof(MXFSBlock) == blockSize );
ASSERT( sizeof(MXFSBlockContinuous) == blockSize );
_storage = NULL;
_children = NULL;
_mountPoint = NULL;
_block = NULL;
_state = 0;
_root = NULL;
_storageStatus = Error::NONE;
}
DevMXFS::~DevMXFS()
{
DevMXFSFile* child = _children;
while ( child )
{
child->_parent = NULL;
child = child->nextSibling;
}
delete[] _block;
delete _root;
Mount::OnMountFailure(_mountPoint, Error::SUCCESS);
}
int DevMXFS::Initialize(MountPoint* mountPoint, const char* /*commandLine*/)
{
_block = new uint8_t[blockSize];
if ( _block == NULL ) { return Error::OUTOFMEM); }
ASSERT(_storage == NULL);
ASSERT(_mountPoint == NULL);
_storage = mountPoint->device;
_mountPoint = mountPoint;
ASSERT(_storage != NULL);
ASSERT(_mountPoint != NULL);
ASSERT(_mountPoint->fs == this);
requestThink();
return Error::PENDING;
}
uint8_t* DevMXFS::GetBlock(intmax_t blockId)
{
ASSERT( _storageStatus != Error::PENDING || _blockId == blockId );
if ( _blockId == blockId )
{
if ( _storageStatus == Error::SUCCESS ) { return _block; } else { return NULL; }
}
if ( !_storage->seek(blockId * blockSize) ) { Failure(Error::Last()); }
if ( _storage->read(block, blockSize) == SIZE_MAX )
{
if ( PENDING ) { return NULL; } else { Failure(Error::Last()); }
}
return _block;
}
MXFSNode* DevMXFS::Search(const char* path);
{
if ( unlikely(_storage == NULL) ) { Error::Set(_storageStatus); return NULL; }
ASSERT(_root != NULL);
// If the root was requested, return it.
if ( *path != '/' ) { Error::Set(Error::NOTFOUND); return NULL; }
if ( *(path+1) == '\0' ) { return _root; } path++;
return _root->Search(path+1);
}
void DevMXFS::Think()
{
if ( !_mountPoint ) { return; }
if ( !ALRIGHT ) { FAIL(); }
// Initialize the filesystem by reading the headers.
if ( _state == 0 )
{
if ( !ParseHeader ) { return; }
// The super block looks good, so far.
_state++;
}
// Load the tree of files and directories.
if ( _state == 1 )
{
if ( !ParseBlocks() ) { return; }
// Alright, we now got the tree of everything loaded!
if ( parsingBlock == 0 ) { _state++; }
}
// Let the mounting system know we are up and running.
if ( _state == 2 )
{
Mount::OnMountSuccess(_mountPoint); _state++;
}
// Alright, now handle requests from various file devices.
if ( _state == 3)
{
}
}
bool DevMXFS::ParseHeader()
{
// Get the super block and read the filesystem's headers.
MXFSHeader* fsHeader = (MXFSHeader*) getBlock(0); if ( !fsHeader ) { return false; }
// Validate that we are dealing with a MXFS.
if ( MXFSHeader->magic != 'M' << 24 | 'X' << 16 | 'F' << 8 | 'S' ) { Failure(Error::BADINPUT); return false; }
// Retrieve and validate the size of the file system.
if ( _storage.size() < MXFSHeader->size ) { Failure(Error::CORRUPT); return false; }
// Validate the existence of the root directory.
if ( MXFSHeader->root == 0 ) { Failure(Error::CORRUPT); return false; }
_parsingBlock = MXFSHeader->root;
// Validate the existence of the root directory.
if ( MXFSHeader->version != 0 ) { Failure(Error::NOSUPPORT); return false;}
return true;
}
bool DevMXFS::ParseBlocks()
{
while ( _parsingBlock != 0 )
{
MXFSNodeHeader* nodeHeader = (MXFSHeader*) getBlock(_parsingBlock); if ( !nodeHeader ) { return false; }
// Validate that we are dealing with a node header.
if ( MXFSHeader->magic != 'N' << 24 | 'O' << 16 | 'D' << 8 | 'E' ) { Failure(Error::CORRUPT); return false; }
// Create a node we can put in your FS tree.
MXFSNode* node = new MXFSNode();
if ( !node ) { Failure(Error::Last()); return false; }
// Copy information from the header to our tree node.
// TODO: copy other header information.
node->type = nodeHeader->type;
// Figure out where to insert our node.
if ( unlikely(_root == NULL) )
{
_root = node;
// Make sure the root is a directory without siblings.
if ( !MXFSHeader->type == MXFSNodeHeader::DIR ) { Failure(Error::CORRUPT); return false; }
if ( !MXFSHeader->nextNode != 0 ) { Failure(Error::CORRUPT); return false; }
// Now visit its children.
_parsingBlock = MXFSHeader->child;
_parsingLastNode = NULL;
_parsingParent = node;
}
else
{
// Insert the node in our tree.
if ( _parsingLastNode )
{
_parsingLastNode->nextSibling = node;
node->prevSibling = _parsingLastNode;
}
if ( _parsingParent->child == NULL ) { _parsingParent->child = node; }
node->parent = _parsingParent;
// Retrieve a copy of the new nodes name and validate it.
node->name = new char[nodeHeader->nameLen + 1];
if ( !node->name ) { Failure(Error::Last()); return false; }
Memory::Copy(node->name, nodeHeader->name, nodeHeader->nameLen);
node->name[nodeHeader->nameLen] = 0;
if ( !legalNodeName ) { Failure(Error::CORRUPT); return false; }
// If node is a directory, visit its children.
if ( MXFSHeader->type == MXFSNodeHeader::DIR && MXFSHeader->child != 0 )
{
_parsingLastNode = NULL;
_parsingParent = node;
_parsingBlock = MXFSHeader->child;
node->siblingBlockId = node->nextNode;
continue;
}
// If it exists, visit the next node in this directory.
if ( nodeHeader->nextNode != 0 )
{
_parsingBlock = nodeHeader->nextNode;
_parsingLastNode = node;
continue;
}
// If we reached the end of a directory, simply continue from
// the parent directory, until we reached the root or we found
// an unfinished directory.
while ( true )
{
node = node->parent;
if ( unlikely(node == NULL) ) { _parsingBlock = 0; break; }
if ( node->siblingBlockId != 0 ) { _parsingBlock = siblingBlockId; _parsingParent = node->parent; _parsingLastNode = node->prevSibling; break; }
}
}
}
return true;
}
void DevMXFS::Failure(int cause = Error::SUCCESS)
{
if ( cause != Error::SUCCESS ) { _storageStatus = cause; }
Mount::OnMountFailure(_mountPoint, _storageStatus);
_mountPoint = NULL;
_storage = NULL;
delete[] _block; _block = NULL;
}
Device* DevMXFS::Open(const char* path, nat flags, nat permissions, nat* type)
{
}
}