From bcfb40dde37a69139ef7ea2748ccfa642d3bee53 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Thu, 17 Feb 2011 18:02:24 +0000 Subject: Wow, MyriadFs is actually getting workable. You can create files, read, write, etc. Next up is creating directories, and other special file types. --- src/myriadfs.cpp | 292 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/myriadfs.h | 100 +++++++++++++++++-- 2 files changed, 375 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/myriadfs.cpp b/src/myriadfs.cpp index c8d23b6..4e2a0f6 100644 --- a/src/myriadfs.cpp +++ b/src/myriadfs.cpp @@ -10,6 +10,11 @@ #include #include +#include + +#include "bu/sio.h" +using Bu::sio; +using Bu::Fmt; namespace Bu { subExceptionDef( MyriadFsException ) } @@ -17,8 +22,15 @@ namespace Bu { subExceptionDef( MyriadFsException ) } Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) : rStore( rStore ), - mStore( rStore, iBlockSize ) + mStore( rStore, iBlockSize ), + iUser( 0 ), + iGroup( 0 ) { +#ifndef WIN32 + iUser = getuid(); + iGroup = getgid(); +#endif + if( mStore.hasStream( 1 ) ) { // Check to see if this is a MyriadFs stream. @@ -32,6 +44,8 @@ Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) : "a MyriadFs stream."); int8_t iVer; + ms.read( &iVer, 1 ); + throw MyriadFsException("You totally have a MyriadFs stream, version %d, but they can't be loaded yet.", iVer ); } else { @@ -43,43 +57,299 @@ Bu::MyriadFs::MyriadFs( Bu::Stream &rStore, int iBlockSize ) : int8_t iVer = 1; int32_t iTmp = 1; ms.write( &iVer, 1 ); - ms.write( &iBlockSize, 4 ); ms.write( &iTmp, 4 ); // iNumNodes iTmp = 0; ms.write( &iTmp, 4 ); // iInode - ms.write( &iTmp, 0 ); // iPosition + ms.write( &iTmp, 4 ); // iPosition + hNodeIndex.insert( 0, 0 ); } // Create initial inode stream, with one root node. { mStore.createStream( 2 ); Bu::MyriadStream ms = mStore.openStream( 2 ); - int32_t iUser = 0, iGroup = 0; -#ifndef WIN32 - iUser = getuid(); - iGroup = getgid(); -#endif int32_t iTmp32 = 0; int16_t iTmp16 = 0; + uint16_t uPerms = 775|typeDir; ms.write( &iUser, 4 ); // User ms.write( &iGroup, 4 ); // Group - ms.write( &iTmp16, 2 ); // Meta? I...don't remember this - ms.write( &iTmp16, 2 ); // Permissions/types + ms.write( &uPerms, 2 ); // Permissions/types + ms.write( &iTmp16, 2 ); // Link count iTmp32 = 3; ms.write( &iTmp32, 4 ); // Stream index iTmp32 = 0; - ms.write( &iTmp32, 4 ); // Parent inode + ms.write( &iTmp32, 4 ); // Parent inode (root's it's own parent) + int64_t iTime = time(NULL); + ms.write( &iTime, 8 ); // atime + ms.write( &iTime, 8 ); // mtime + ms.write( &iTime, 8 ); // ctime ms.write( &iTmp16, 2 ); // Name size } // Create inode 0's storage stream. { mStore.createStream( 3 ); + Bu::MyriadStream ms = mStore.openStream( 3 ); + int32_t iTmp32 = 0; + ms.write( &iTmp32, 4 ); // iChildCount } } } Bu::MyriadFs::~MyriadFs() { + writeHeader(); +} + +void Bu::MyriadFs::stat( const Bu::String &sPath, Bu::MyriadFs::Stat &rBuf ) +{ + int32_t iParent; + int32_t iNode = lookupInode( sPath, iParent ); + Bu::MyriadStream is = mStore.openStream( 2 ); + stat( iNode, rBuf, is ); +} + +Bu::MyriadStream Bu::MyriadFs::open( const Bu::String &sPath, int iMode ) +{ + int32_t iParent = -1; + int32_t iNode; + try + { + iNode = lookupInode( sPath, iParent ); + sio << "File found." << sio.nl; + // The file was found + return openByInode( iNode ); + } + catch( Bu::MyriadFsException &e ) + { + if( iParent < 0 ) + throw; + + // The file wasn't found, but the path leading up to it was. + // first, figure out the final path element... + Bu::String::const_iterator iStart = sPath.begin(); + if( *iStart == '/' ) + iStart++; + sio << "Scanning for filename:" << sio.nl; + for( Bu::String::const_iterator iEnd = iStart.find('/')+1; iEnd; iStart = iEnd ) { } + Bu::String sName( iStart, sPath.end() ); + sio << "End filename: " << sName << sio.nl; + sio << "Parent inode: " << iParent << sio.nl; + iNode = create( iParent, sName, 0664|typeRegFile ); + sio << "New iNode: " << iNode << sio.nl; + return openByInode( iNode ); + } + + return mStore.openStream( 2 ); +} + +int32_t Bu::MyriadFs::lookupInode( const Bu::String &sPath, int32_t &iParent ) +{ + if( sPath[0] == '/' ) + { + // Absolute lookup + return lookupInode( sPath.begin()+1, 0, iParent ); + } + else + { + // Relative lookup + throw Bu::ExceptionBase( + "Relative lookups in MyriadFs are not working yet."); + } +} + +int32_t Bu::MyriadFs::lookupInode( Bu::String::const_iterator iStart, + int32_t iNode, int32_t &iParent ) +{ + iParent = iNode; + + Bu::String::const_iterator iEnd = iStart.find('/'); + Bu::String sTok( iStart, iEnd ); + + sio << "Direcotry component: " << sTok << sio.nl; + + Dir lDir = readDir( iNode ); + + for( Dir::iterator i = lDir.begin(); i; i++ ) + { + if( (*i).sName == sTok ) + { + // We found an item + if( !iEnd ) + { + // It's the last one in the requested path, return it + return (*i).iNode; + } + else + { + // Not the last one in our path, double check it's a dir + if( ((*i).uPerms&typeMask) == typeDir ) + { + return lookupInode( iEnd+1, (*i).iNode ); + } + else + { + iParent = -1; + throw Bu::MyriadFsException( + "Element '%s' in given path is not a directory.", + sTok.getStr() ); + } + } + } + } + + throw Bu::MyriadFsException( 1, "Path not found"); +} + +Bu::MyriadFs::Dir Bu::MyriadFs::readDir( int32_t iNode ) +{ + Bu::MyriadStream ms = openByInode( iNode ); + int32_t iNumChildren; + ms.read( &iNumChildren, 4 ); + + Bu::MyriadStream is = mStore.openStream( 2 ); + Dir lDir; + sio << "Reading dir, " << iNumChildren << " entries:" << sio.nl; + for( int32_t j = 0; j < iNumChildren; j++ ) + { + int32_t iChildNode; + ms.read( &iChildNode, 4 ); + Stat s; + stat( iChildNode, s, is ); + lDir.append( s ); + + sio << " " << s.sName << sio.nl; + } + + return lDir; +} + +Bu::MyriadStream Bu::MyriadFs::openByInode( int32_t iNode ) +{ + int32_t iIndex = hNodeIndex.get( iNode ); + Bu::MyriadStream ms = mStore.openStream( 2 ); + ms.setPos( mStore.getBlockSize()*iIndex ); + RawStat rs; + ms.read( &rs, sizeof(RawStat) ); + switch( (rs.uPerms&typeMask) ) + { + case typeDir: + case typeSymLink: + case typeRegFile: + return mStore.openStream( rs.iStreamIndex ); + + default: + throw Bu::MyriadFsException( + "inode incorrect type for low-level openByInode."); + } +} + +int32_t Bu::MyriadFs::create( int32_t iParent, const Bu::String &sName, + uint16_t uPerms ) +{ + Bu::MyriadStream ms = openByInode( iParent ); + int32_t iNumChildren; + ms.read( &iNumChildren, 4 ); + iNumChildren++; + ms.setPos( 0 ); + ms.write( &iNumChildren, 4 ); + ms.setPos( iNumChildren*4 ); // Actually 4+(iNumChildren-1)*4 :-P + int32_t iNode = allocInode( sName, iParent, uPerms ); + ms.write( &iNode, 4 ); + return iNode; +} + +int32_t Bu::MyriadFs::allocInode( const Bu::String &sName, int32_t iParent, + uint16_t uPerms ) +{ + int32_t iNode = 0; + for(; iNode < 0xfffffff; iNode++ ) + { + if( !hNodeIndex.has( iNode ) ) + { + Bu::MyriadStream is = mStore.openStream( 2 ); + is.setSize( (hNodeIndex.getSize()+1)*mStore.getBlockSize() ); + is.setPos( hNodeIndex.getSize()*mStore.getBlockSize() ); + + hNodeIndex.insert( iNode, hNodeIndex.getSize() ); + RawStat rs; + rs.iUser = iUser; + rs.iGroup = iGroup; + rs.uPerms = uPerms; + rs.iLinks = 1; + switch( (uPerms&typeMask) ) + { + case typeDir: + case typeRegFile: + case typeSymLink: + rs.iStreamIndex = mStore.createStream(); + break; + + default: + rs.iStreamIndex = 0; + break; + } + rs.iParentNode = iParent; + rs.iATime = time(NULL); + rs.iMTime = time(NULL); + rs.iCTime = time(NULL); + rs.iNameSize = sName.getSize(); + is.write( &rs, sizeof(RawStat) ); + is.write( sName.getStr(), sName.getSize() ); + + return iNode; + } + } + + throw Bu::MyriadFsException( + "No inode could be allocated. You've run out!"); +} + +void Bu::MyriadFs::stat( int32_t iNode, Stat &rBuf, MyriadStream &rIs ) +{ + rIs.setPos( hNodeIndex.get( iNode )*mStore.getBlockSize() ); + RawStat rs; + rIs.read( &rs, sizeof(RawStat) ); + rBuf.sName.setSize( rs.iNameSize ); + rIs.read( rBuf.sName.getStr(), rs.iNameSize ); + rBuf.iNode = iNode; + rBuf.iUser = rs.iUser; + rBuf.iGroup = rs.iGroup; + rBuf.uPerms = rs.uPerms; + rBuf.iLinks = rs.iLinks; + rBuf.iATime = rs.iATime; + rBuf.iMTime = rs.iMTime; + rBuf.iCTime = rs.iCTime; + switch( (rBuf.uPerms&typeMask) ) + { + case typeRegFile: + case typeSymLink: + rBuf.iSize = mStore.getStreamSize( rs.iStreamIndex ); + break; + + default: + rBuf.iSize = 0; + break; + } +} + +void Bu::MyriadFs::writeHeader() +{ + Bu::MyriadStream ms = mStore.openStream( 1 ); + ms.write( Myriad_Fs_MAGIC_CODE, 4 ); + int8_t iVer = 1; + int32_t iNumNodes = hNodeIndex.getSize(); + ms.write( &iVer, 1 ); + ms.write( &iNumNodes, 4 ); // iNumNodes + for( NodeIndex::iterator i = hNodeIndex.begin(); i; i++ ) + { + int32_t iNode = i.getKey(); + int32_t iPosition = i.getValue(); + ms.write( &iNode, 4 ); + ms.write( &iPosition, 4 ); + } + + // Truncate the stream afterwards so we don't use up too much space. + ms.setSize( ms.tell() ); } diff --git a/src/myriadfs.h b/src/myriadfs.h index 856137c..808444b 100644 --- a/src/myriadfs.h +++ b/src/myriadfs.h @@ -19,13 +19,10 @@ namespace Bu /** * A POSIX compliant, node based filesystem built on top of Myriad. * - * Think about putting this all in one stream, on block boundaries. - * * A header is placed into stream 1. * Header format: * int32_t iMagicHeader (A7188B39) * int8_t iVersion (1) - * int32_t iNodeSize * int32_t iNumNodes * NodeLookup[iNumNodes] nNode * @@ -33,10 +30,10 @@ namespace Bu * int32_t iInode * int32_t iPosition * - * The node headers or inode structures have a base size of 22 bytes. + * The node headers or inode structures have a base size of 46 bytes. * Everything else in the block is used for the name. I.e. if you have * a blocksize of 512 bytes then you wind up with a max name size of - * 512-22=490 characters, or a blocksize of 256 gives you 234 chraacters + * 512-46=466 characters, or a blocksize of 256 gives you 210 chraacters * as a max. The node headers are all stored in stream 2. * Basic node header format: * int32_t iUser @@ -50,6 +47,27 @@ namespace Bu * int64_t iCTime * int16_t iNameSize * char[iNameSize] sName + * + * Some types get special formats for their assosiated data stream, or + * other special considerations, here's a list: + * + * - typeFifo: No stream, iStreamIndex unused (probably) + * - typeChrDev: No stream, iStreamIndex is device hi/lo + * - typeDir: The stream contains a directory contents listing, described + * below + * - typeBlkDev: No stream, iStreamIndex is device hi/lo + * - typeRegFile: The stream is the file data + * - typeSymLink: The stream is the destination of the symlink + * - typeSocket: No steram, iStreamIndex unused (probably) + * + * Directory streams have this simple listing format. They contain a list + * of all child elements, with no particular order at the moment. The . and + * .. entries are not listed, they are implicit: + * int32_t iNumNodes + * NodeTable[iNumNodes] nChildren + * + * NodeTable: + * int32_t iInode */ class MyriadFs { @@ -79,11 +97,81 @@ namespace Bu typeSymLink = 0120000, typeSocket = 0140000, typeMask = 0170000 - } + }; + + enum + { + Read = 0x01, ///< Open file for reading + Write = 0x02, ///< Open file for writing + Create = 0x04, ///< Create file if it doesn't exist + Truncate = 0x08, ///< Truncate file if it does exist + Append = 0x10, ///< Always append on every write + NonBlock = 0x20, ///< Open file in non-blocking mode + Exclusive = 0x44, ///< Create file, if it exists then fail + + // Helpful mixes + ReadWrite = 0x03, ///< Open for reading and writing + WriteNew = 0x0E ///< Create a file (or truncate) for writing. + /// Same as Write|Create|Truncate + }; + + class Stat + { + public: + int32_t iNode; + int32_t iUser; + int32_t iGroup; + uint16_t uPerms; + int16_t iLinks; + int64_t iATime; + int64_t iMTime; + int64_t iCTime; + int32_t iSize; + Bu::String sName; + }; + typedef Bu::List Dir; + + void stat( const Bu::String &sPath, Stat &rBuf ); + MyriadStream open( const Bu::String &sPath, int iMode ); +// void create( const Bu::String &sPath, uint16_t iPerms ); + + + private: + class RawStat + { + public: + int32_t iUser; + int32_t iGroup; + uint16_t uPerms; + int16_t iLinks; + int32_t iStreamIndex; + int32_t iParentNode; + int64_t iATime; + int64_t iMTime; + int64_t iCTime; + int16_t iNameSize; + }; + typedef Bu::Hash NodeIndex; + + private: + int32_t lookupInode( const Bu::String &sPath, int32_t &iParent ); + int32_t lookupInode( Bu::String::const_iterator iStart, + int32_t iNode, int32_t &iParent ); + Dir readDir( int32_t iNode ); + MyriadStream openByInode( int32_t iNode ); + int32_t create( int32_t iParent, const Bu::String &sName, + uint16_t uPerms ); + int32_t allocInode( const Bu::String &sName, int32_t iParent, + uint16_t uPerms ); + void stat( int32_t iNode, Stat &rBuf, MyriadStream &rIs ); + void writeHeader(); private: Bu::Stream &rStore; Bu::Myriad mStore; + NodeIndex hNodeIndex; + int32_t iUser; + int32_t iGroup; }; }; -- cgit v1.2.3