Lightning Rose Projects
Lightning Rose
mffs_lib.c
Description and Links to the Current API Library
/****************************************************************************** ** ** ** Multi-Forked File System ** ** Copyright (C) L. N. Reed 1985 thru 2005 ** ** ** ** Released under the GNU Lesser Public License ** ** http://www.gnu.org/copyleft/lesser.txt ** ** ** ******************************************************************************/ #include
#include
#include
#include
#include
#include
#include
#include
#include "lnr.h" // typedef's found here #include "misc_lib.h" #include "link_lib.h" #include "string_lib.h" #include "debug_lib.h" /* ** mffs_lib.h is included after the data definitions */ //**************************************************************************** /* ** For 64 bit file support on 32 bit Linux systems, all source ** code modules should use the following GNU cc compiler options: ** ** -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE ** ** These options force the use of 64 bit library functions and ** off_t is set to 64 bits. ** ** See the makefile or shell scripts included with this distribution ** for an example. */ //**************************************************************************** /* ** To Do: ** ** Optimize mffsInsert() & mffsDelete() to merge contiguous mCD's ** ** Check error returns and BUGOUT()'s ** ** mffsOpenFork(), mffsInsert(), mffsRead() & mffsWrite() should check mode ** ** Expand & improve errCode's ** ** Conditional compilation to support big/little endian systems??? */ //**************************************************************************** /* ** Not every compiler recognizes the gnu style ** compiler directives for file and function names. */ #ifndef __FUNCTION__ #define __FUNCTION__ "*Unknown*" #endif /* ** Since not all compilers support long long's, we'll define ** a few easy-to-change macros for our debugging stuff. */ #define DBG_FORMAT_1 "%s %12llu" #define DBG_FORMAT_2 "%s %12llu %12llu" #define DBG_FORMAT_3 "%s %12llu %12llu %12llu" #define BUGOUT_1 "*** Needs 64 Bit File System: %lld ***\n\n" #define BUGOUT_2 "*** %s Needs 64 Bit File System: %lld ***\n\n" #define BUGOUT_3 "*** lseek( %lld, %d ) Error from %s ***\n\n" //**************************************************************************** //#define MFFS_DEBUG //#define FD_DEBUG //#define INSERT_DEBUG //#define DELETE_DEBUG //#define WRITE_DEBUG //#define MAPCD_DEBUG //#define MALLOC_DEBUG //**************************************************************************** #ifdef MFFS_DEBUG #define logMffs(x) logDB(x) #else #define logMffs(x) #endif //**************************************************************************** #ifdef FD_DEBUG #define logFD(x) logDB(x) #else #define logFD(x) #endif //**************************************************************************** #ifdef INSERT_DEBUG #define logInsert(x) logDB(x) #define insertDB(x) x #else #define logInsert(x) #define insertDB(x) #endif //**************************************************************************** #ifdef DELETE_DEBUG #define logDelete(x) logDB(x) #else #define logDelete(x) #endif //**************************************************************************** #ifdef WRITE_DEBUG #define logWrite(x) logDB(x) #else #define logWrite(x) #endif //**************************************************************************** #ifdef MAPCD_DEBUG #define logMapCD(x) logDB(x) #else #define logMapCD(x) #endif //**************************************************************************** #ifdef MALLOC_DEBUG #define logMalloc(x) logDB(x) #define mallocDB(x) x #else #define logMalloc(x) #define mallocDB(x) #endif //**************************************************************************** //**************************************************************************** /* ** This are the data descriptors as embedded in the file. ** ** All file offsets are from the beginning of the file. ** ** Because we're writing data in binary mode, ** it's important we use variables of a known size. */ #define NAME_SIZE 31 #define NULL_OFFSET ((bigOff_t) -1) typedef struct { int32 version; // The version of this file system #define MFFS_VERSION 0x19530727L // Just some value. int32 mffsType; #define MFFS_NAME ( *(int32 *) "MFFS") #define MFFS_FORK_LINK ( *(int32 *) "FLNK") #define MFFS_FORK ( *(int32 *) "FORK") #define MFFS_CHUNK ( *(int32 *) "CHNK") } mffsVersion; typedef struct // This is the very last thing in the file { mffsVersion versionInfo; // The version of this MFFS int32 revLevel; bigTime_t timeStamp; bigOff_t firstFork; // Where to find the first mFL, or -1 bigOff_t prevFileHeader; // Where to find the previous mFH, or -1 } mffsFileHeader; #define MFH_SIZE ((bigSize_t) sizeof( mffsFileHeader )) typedef struct { mffsVersion versionInfo; // The version of this MFFS bigOff_t forkPointer; // Where to find the mFD bigOff_t nextForkLink; // Where to find the next mFL } mffsForkLink; #define MFL_SIZE ((bigSize_t) sizeof( mffsForkLink )) typedef struct { mffsVersion versionInfo; // The version of this MFFS char forkName[NAME_SIZE+1]; // As defined by the user. int32 revLevel; bigTime_t timeStamp; bigOff_t firstChunk; // Where to find the first mCD } mffsForkDescriptor; #define MFD_SIZE ((bigSize_t) sizeof( mffsForkDescriptor )) typedef struct { mffsVersion versionInfo; // The version of this file system bigOff_t nextChunk; // Where to find the next mCD bigOff_t chunkPointer; // Where the chunk data is actually located bigSize_t chunkSize; // Number of bytes in this chunk } mffsChunkDescriptor; #define MCD_SIZE ((bigSize_t) sizeof( mffsChunkDescriptor )) //**************************************************************************** /* ** This are the data descriptors as used by the run-time code. ** ** All file offsets are from the beginning of the file. */ typedef struct // Chunk Descriptor Info { linkInfo cdLink; // link to next cdInfo bigOff_t chunkPointer; // Where the chunk data is actually located bigOff_t forkOffset; // bigSize_t chunkSize; // Number of bytes in this chunk bigOff_t filePointer; // Calculated for each read()/insert() bigSize_t lbytes; // Number of bytes left in chunk bigSize_t rbytes; // Number of bytes that can be read() } cdInfo; typedef struct // Fork Descriptor Info { linkInfo fdLink; // link to next fdInfo; int32 forkID; // Assigned by mffsOpen() or openFork() char forkName[NAME_SIZE+1]; // As defined by the user. bigOff_t priorMfd; // Link to prior mFD in file bigOff_t forkPointer; // Our virtual file pointer bigSize_t forkBytes; // how many bytes in the fork. int modFlag; // TRUE if fork has been modified mode_t mode; int revLevel; time_t timeStamp; linkList cdList; // list of cdInfo's } fdInfo; typedef struct // File Descriptor Info { int fileDes; // as returned by open() int numForks; // The number of forks currently defined. int nextForkID; // For assigning Fork ID's int errCode; // mffs error code int modFlag; // TRUE if file has been modified int revLevel; time_t timeStamp; bigOff_t prevOffset; // offset to the previous mFH bigOff_t currOffset; // offset to the current mFH linkList fdList; // list of fdInfo's } mffsFile; //**************************************************************************** #define returnErrCode(x) {mffsFP->errCode=(int)(x); return(x);} #include "mffs_lib.h" //**************************************************************************** //**************************************************************************** /* ** Counters used to verify we have as many free()'s as malloc()'s */ mallocDB( static long mffsFileCount = 0 ); mallocDB( static long cdInfoCount = 0 ); mallocDB( static long fdInfoCount = 0 ); void mffsDebugDump( void ) { mallocDB(( logMalloc(( "mffsFileCount: %5ld", mffsFileCount )))); mallocDB(( logMalloc(( "cdInfoCount: %5ld", cdInfoCount )))); mallocDB(( logMalloc(( "fdInfoCount: %5ld", fdInfoCount )))); } //**************************************************************************** //**************************************************************************** static void delete_cdInfo( cdInfo *cdPtr ) { logMffs(( DBG_FORMAT_3, "", (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->chunkSize, (ubigInt) cdPtr->forkOffset )); mallocDB(( cdInfoCount-- )); mallocDB( memset( cdPtr, 0, sizeof( cdInfo ))); free( cdPtr ); } //**************************************************************************** static void delete_fdInfo( fdInfo *fdPtr ) { mallocDB(( fdInfoCount-- )); mallocDB( memset( fdPtr, 0, sizeof( fdInfo ))); free( fdPtr ); } //**************************************************************************** static void delete_fork( fdInfo *fdPtr ) { cdInfo *cdPtr, *nextCd; while ( fdPtr->cdList.head ) { cdPtr = (cdInfo *) fdPtr->cdList.head; while ( cdPtr ) { nextCd = (cdInfo *) cdPtr->cdLink.next; linkRemove( &cdPtr->cdLink, &fdPtr->cdList ); delete_cdInfo( cdPtr ); cdPtr = nextCd; } } delete_fdInfo( fdPtr ); } //**************************************************************************** static void delete_allForks( mffsFile *mffsFP ) { fdInfo *fdPtr, *nextfd; while ( mffsFP->fdList.head ) { fdPtr = (fdInfo *) mffsFP->fdList.head; while ( fdPtr ) { nextfd = (fdInfo *) fdPtr->fdLink.next; linkRemove( &fdPtr->fdLink, &mffsFP->fdList ); delete_fork( fdPtr ); fdPtr = nextfd; } } } //**************************************************************************** static void delete_mffsFile( mffsFile *mffsFP ) { delete_allForks( mffsFP ); mallocDB(( mffsFileCount-- )); mallocDB( memset( mffsFP, 0, sizeof( mffsFile ))); free( mffsFP ); } //**************************************************************************** static cdInfo *new_cdInfo( bigOff_t chunkPointer, bigSize_t chunkSize, bigOff_t forkOffset ) { cdInfo *cdPtr; logMffs(( DBG_FORMAT_3, "", (ubigInt) chunkPointer, (ubigInt) chunkSize, (ubigInt) forkOffset )); cdPtr = (cdInfo *) calloc( 1, sizeof( cdInfo )); if ( cdPtr EQ NULL ) BUGOUT(( -1, "*** Can't malloc( cdInfo ) ***\n\n" )); //??? cdPtr->chunkPointer = chunkPointer; cdPtr->chunkSize = chunkSize; cdPtr->forkOffset = forkOffset; mallocDB(( cdInfoCount++ )); return( cdPtr ); } //**************************************************************************** static fdInfo *new_fdInfo( int forkID, const char *forkName, bigOff_t priorMfd, time_t timeStamp, int revLevel ) { fdInfo *fdPtr; logFD(( "Fork: %2d, %3d %s", forkID, revLevel, forkName )); fdPtr = (fdInfo *) calloc( 1, sizeof( fdInfo )); if ( fdPtr EQ NULL ) BUGOUT(( -1, "*** Can't malloc( fdInfo ) ***\n\n" )); //??? stringNCopy( fdPtr->forkName, forkName, NAME_SIZE ); fdPtr->forkID = forkID; fdPtr->priorMfd = priorMfd; fdPtr->timeStamp = timeStamp; fdPtr->revLevel = revLevel; mallocDB(( fdInfoCount++ )); return( fdPtr ); } //**************************************************************************** static mffsFile *new_mffsFile( void ) { mffsFile *mffsFp; mffsFp = (mffsFile *) calloc( 1, sizeof( mffsFile )); if ( mffsFp EQ NULL ) BUGOUT(( -1, "*** Can't malloc( mffsFile ) ***\n\n" )); //??? mallocDB(( mffsFileCount++ )); return( mffsFp ); } //**************************************************************************** /* ** Make sure our filesystem can handle a given file offset. */ static int checkOffset( bigOff_t offset ) { if ( sizeof( off_t ) NE sizeof( bigOff_t )) { if ( offset GT (bigOff_t) 0xFFFFffffLL ) return( TRUE ); } return( FALSE ); } //**************************************************************************** /* ** Make sure our filesystem can handle a given size */ static int checkSize( bigSize_t size ) { if ( sizeof( size_t ) NE sizeof( bigSize_t )) { if ( size GT (bigOff_t) 0xFFFFffffLL ) return( TRUE ); } return( FALSE ); } //**************************************************************************** /* ** We put the lseek() inside this wrapper so we ** only have to error check the return value once. ** ** If we ever re-write this to not BUGOUT(), then all ** calling functions should have error checking added. */ static bigOff_t doSeek( int fileDes, bigOff_t offset, int whence, const char *funcName ) { bigOff_t newOffset; if ( checkOffset( offset )) BUGOUT(( -1, BUGOUT_2, funcName, (ubigInt) offset )); newOffset = lseek( fileDes, offset, whence ); if ( newOffset EQ NULL_OFFSET ) BUGOUT(( -1, BUGOUT_3, (ubigInt) offset, whence, funcName )); return( newOffset ); } //**************************************************************************** /* ** This returns bigOff_t instead of bigSize_t because there's ** no known way to get a 64 bit size_t on a 32 bit file system. */ static bigOff_t fileSize( int fileDes ) { bigOff_t size, where; where = doSeek( fileDes, 0, SEEK_CUR, __FUNCTION__ ); size = doSeek( fileDes, 0, SEEK_END, __FUNCTION__ ); doSeek( fileDes, where, SEEK_SET, __FUNCTION__ ); return( size ); } //**************************************************************************** static bigSize_t doRead( mffsFile *mffsFP, void *buff, bigSize_t nbytes, bigOff_t offset ) { bigOff_t newOffset; mffsFP->errCode = 0; if ( checkOffset( offset )) BUGOUT(( -1, BUGOUT_1, (ubigInt) offset )); if ( checkSize( nbytes )) BUGOUT(( -1, BUGOUT_1, (ubigInt) nbytes )); newOffset = doSeek( mffsFP->fileDes, offset, SEEK_SET, __FUNCTION__ ); if ( newOffset NE offset ) { mffsFP->errCode = MFFS_SEEK; return( 0 ); } return( read( mffsFP->fileDes, buff, nbytes ) ); } //**************************************************************************** static bigSize_t doWrite( mffsFile *mffsFP, void *buff, bigSize_t nbytes ) { bigOff_t newOffset; mffsFP->errCode = 0; if ( checkSize( nbytes )) BUGOUT(( -1, BUGOUT_1, (ubigInt) nbytes )); newOffset = doSeek( mffsFP->fileDes, 0, SEEK_END, __FUNCTION__ ); return( write( mffsFP->fileDes, buff, nbytes ) ); } //**************************************************************************** static int verifyVersion( mffsVersion *versionInfo, int32 type ) { if ( versionInfo->version NE MFFS_VERSION ) return( FALSE ); if ( versionInfo->mffsType NE type ) return( FALSE ); return( TRUE ); } //**************************************************************************** static int buildChunkList( mffsFile *mffsFP, fdInfo *fdPtr, bigOff_t chunkOffset ) { bigSize_t nbytes; cdInfo *cdPtr; mffsChunkDescriptor mCD; bigOff_t forkOffset = 0; while ( chunkOffset NE NULL_OFFSET ) { nbytes = doRead( mffsFP, &mCD, MCD_SIZE, chunkOffset ); if ( nbytes NE MCD_SIZE ) returnErrCode( MFFS_READ ); if ( NOT verifyVersion( &mCD.versionInfo, MFFS_CHUNK )) returnErrCode( MFFS_INVALID_MCD ); cdPtr = new_cdInfo( mCD.chunkPointer, mCD.chunkSize, forkOffset ); linkAppend( &cdPtr->cdLink, &fdPtr->cdList ); fdPtr->forkBytes += mCD.chunkSize; forkOffset += mCD.chunkSize; chunkOffset = mCD.nextChunk; } returnErrCode( 0 ); } //**************************************************************************** static int buildFork( mffsFile *mffsFP, bigOff_t forkLink ) { int rc; bigSize_t nbytes; fdInfo *fdPtr; mffsForkDescriptor mFD; mffsFP->errCode = 0; if ( forkLink NE NULL_OFFSET ) { nbytes = doRead( mffsFP, &mFD, MFD_SIZE, forkLink ); if ( nbytes NE MFD_SIZE ) returnErrCode( MFFS_READ ); if ( NOT verifyVersion( &mFD.versionInfo, MFFS_FORK )) returnErrCode( MFFS_INVALID_MFD ); logFD(( " Fork: %3d %s", mFD.revLevel, mFD.forkName )); fdPtr = new_fdInfo( mffsFP->numForks + 1, mFD.forkName, forkLink, mFD.timeStamp, mFD.revLevel ); rc = buildChunkList( mffsFP, fdPtr, mFD.firstChunk ); if ( rc ) return ( rc ); mffsFP->numForks++; mffsFP->nextForkID++; linkAppend( &fdPtr->fdLink, &mffsFP->fdList ); } return( 0 ); } //**************************************************************************** static int buildForkList( mffsFile *mffsFP, bigOff_t forkOffset ) { int rc; bigSize_t nbytes; mffsForkLink mFL; mffsFP->errCode = 0; while ( forkOffset NE NULL_OFFSET ) { nbytes = doRead( mffsFP, &mFL, MFL_SIZE, forkOffset ); if ( nbytes NE MFL_SIZE ) returnErrCode( MFFS_READ ); if ( NOT verifyVersion( &mFL.versionInfo, MFFS_FORK_LINK )) returnErrCode( MFFS_INVALID_MFD ); rc = buildFork( mffsFP, mFL.forkPointer ); if ( rc ) return ( rc ); forkOffset = mFL.nextForkLink; } return( 0 ); } //**************************************************************************** static int buildMffs( mffsFile *mffsFP, mffsFileHeader *mFhPtr, bigOff_t offset ) { mffsFP->currOffset = offset; mffsFP->prevOffset = mFhPtr->prevFileHeader; mffsFP->timeStamp = mFhPtr->timeStamp; mffsFP->revLevel = mFhPtr->revLevel; return( buildForkList( mffsFP, mFhPtr->firstFork )); } //**************************************************************************** static int readMFH( mffsFile *mffsFP, mffsFileHeader *mFH, bigOff_t offset ) { bigSize_t nbytes; nbytes = doRead( mffsFP, mFH, MFH_SIZE, offset ); if ( nbytes NE MFH_SIZE ) returnErrCode( MFFS_READ ); if ( NOT verifyVersion( &mFH->versionInfo, MFFS_NAME )) returnErrCode( MFFS_INVALID_MFH ); returnErrCode( 0 ); } //**************************************************************************** static int readMffs( mffsFile *mffsFP ) { int rc; bigSize_t fsize; bigOff_t offset; mffsFileHeader mFH; mffsFP->prevOffset = NULL_OFFSET; mffsFP->currOffset = NULL_OFFSET; fsize = fileSize( mffsFP->fileDes ); if ( fsize EQ 0 ) // Nothing to build returnErrCode( 0 ); if ( fsize LT MFH_SIZE ) // Nothing to build returnErrCode( MFFS_INVALID_MFH ); offset = fsize - MFH_SIZE; rc = readMFH( mffsFP, &mFH, offset ); if ( rc ) return( rc ); return( buildMffs( mffsFP, &mFH, offset )); } //**************************************************************************** static fdInfo *SearchForkName( mffsFile *mffsFP, const char *forkName ) { fdInfo *fdPtr; fdPtr = (fdInfo *) mffsFP->fdList.head; while ( fdPtr ) { if ( strcmp( forkName, fdPtr->forkName ) EQ 0 ) return( fdPtr ); fdPtr = (fdInfo *) fdPtr->fdLink.next; } return( NULL ); } //**************************************************************************** static fdInfo *createNewFork( mffsFile *mffsFP, const char *forkName, mode_t mode ) { fdInfo *fdPtr; fdPtr = new_fdInfo( mffsFP->nextForkID + 1, forkName, NULL_OFFSET,time( NULL ), 0 ); mffsFP->numForks++; mffsFP->nextForkID++; linkAppend( &fdPtr->fdLink, &mffsFP->fdList ); fdPtr->mode = mode; fdPtr->modFlag = TRUE; mffsFP->modFlag = TRUE; return( fdPtr ); } //**************************************************************************** /* ** Searches for the Fork Descriptor with the given ID */ static fdInfo *getFdInfo( mffsFile *mffsFP, int forkID ) { fdInfo *fdPtr; fdPtr = (fdInfo *) mffsFP->fdList.head; while ( fdPtr ) { if ( fdPtr->forkID EQ forkID ) return( fdPtr ); fdPtr = (fdInfo *) fdPtr->fdLink.next; } mffsFP->errCode = MFFS_INVALID_FORK_ID; return( NULL ); } //**************************************************************************** static cdInfo *mapCdInfo( fdInfo *fdPtr, bigOff_t forkPtr ) { cdInfo *cdPtr; bigOff_t forkEnd; logMapCD(( DBG_FORMAT_1, " ", (ubigInt) forkPtr )); cdPtr = (cdInfo *) fdPtr->cdList.head; while ( cdPtr ) { forkEnd = cdPtr->forkOffset + cdPtr->chunkSize; logMapCD(( DBG_FORMAT_2, " ", (ubigInt) cdPtr->forkOffset, (ubigInt) forkEnd )); if (( forkPtr GE cdPtr->forkOffset ) AND ( forkPtr LT forkEnd )) { cdPtr->rbytes = forkEnd - forkPtr; cdPtr->lbytes = cdPtr->chunkSize - cdPtr->rbytes; cdPtr->filePointer = cdPtr->chunkPointer + cdPtr->lbytes; logMapCD(( DBG_FORMAT_3, " ", (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->filePointer, (ubigInt) cdPtr->rbytes )); return( cdPtr ); } cdPtr = (cdInfo *) cdPtr->cdLink.next; } return( NULL ); } //**************************************************************************** static bigOff_t saveChunks( mffsFile *mffsFP, fdInfo *fdPtr ) { bigOff_t filePtr, nextChunk; cdInfo *cdPtr; mffsChunkDescriptor mCD; nextChunk = NULL_OFFSET; memset( &mCD, 0, (size_t) MCD_SIZE ); mCD.versionInfo.version = MFFS_VERSION; mCD.versionInfo.mffsType = MFFS_CHUNK; cdPtr = (cdInfo *) fdPtr->cdList.tail; while ( cdPtr ) { logMffs(( DBG_FORMAT_3, " ", (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->chunkSize, (ubigInt) cdPtr->forkOffset )); mCD.nextChunk = nextChunk; mCD.chunkPointer = cdPtr->chunkPointer; mCD.chunkSize = cdPtr->chunkSize; filePtr = fileSize( mffsFP->fileDes ); nextChunk = filePtr; if ( doWrite( mffsFP, &mCD, MCD_SIZE ) NE MCD_SIZE ) BUGOUT(( -1, "*** write() Error ***\n\n" )); //??? cdPtr = (cdInfo *) cdPtr->cdLink.prev; } return( nextChunk ); } //**************************************************************************** /* ** Returns the file offset to the mFD */ static bigOff_t saveFork( mffsFile *mffsFP, fdInfo *fdPtr ) { bigOff_t forkPointer; mffsForkDescriptor mFD; logMffs(( " Fork Name: %s", fdPtr->forkName )); if ( NOT fdPtr->modFlag ) // Nothing to save. return( fdPtr->priorMfd ); fdPtr->revLevel++; memset( &mFD, 0, (size_t) MFD_SIZE ); mFD.versionInfo.version = MFFS_VERSION; mFD.versionInfo.mffsType = MFFS_FORK; strcpy( mFD.forkName, fdPtr->forkName ); mFD.revLevel = fdPtr->revLevel; mFD.timeStamp = mffsFP->timeStamp; mFD.firstChunk = saveChunks( mffsFP, fdPtr ); forkPointer = fileSize( mffsFP->fileDes ); if ( doWrite( mffsFP, &mFD, MFD_SIZE ) NE MFD_SIZE ) BUGOUT(( -1, "*** write() Error ***\n\n" )); //??? fdPtr->modFlag = FALSE; logFD(( " Fork: %3d %s", mFD.revLevel, mFD.forkName )); return( forkPointer ); } //**************************************************************************** static bigOff_t saveAllForks( mffsFile *mffsFP ) { bigOff_t nextFork; fdInfo *fdPtr; mffsForkLink mFL; nextFork = NULL_OFFSET; memset( &mFL, 0, (size_t) MFL_SIZE ); mFL.versionInfo.version = MFFS_VERSION; mFL.versionInfo.mffsType = MFFS_FORK_LINK; fdPtr = (fdInfo *) mffsFP->fdList.tail; while ( fdPtr ) { mFL.nextForkLink = nextFork; nextFork = saveFork( mffsFP, fdPtr ); mFL.forkPointer = nextFork; nextFork = fileSize( mffsFP->fileDes ); if ( doWrite( mffsFP, &mFL, MFL_SIZE ) NE MFL_SIZE ) BUGOUT(( -1, "*** write() Error ***\n\n" )); //??? fdPtr->modFlag = TRUE; fdPtr = (fdInfo *) fdPtr->fdLink.prev; } return( nextFork ); } //**************************************************************************** static void adjustCdForkOffsets( cdInfo *cdPtr, bigOff_t nbytes ) { while( cdPtr ) { cdPtr->forkOffset += nbytes; cdPtr = (cdInfo *) cdPtr->cdLink.next; } } //**************************************************************************** static void checkEmptyCD( cdInfo *cdPtr, fdInfo *fdPtr ) { if ( cdPtr->chunkSize EQ 0 ) { logDelete(( DBG_FORMAT_3, "Trashing: ", (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->chunkSize, (ubigInt) cdPtr->forkOffset )); linkRemove( &cdPtr->cdLink, &fdPtr->cdList ); delete_cdInfo( cdPtr ); } } //**************************************************************************** static void doCdTrace( fdInfo *fdPtr ) { cdInfo *cdPtr; int i = 0; printf( "\n Fork: %s", fdPtr->forkName ); printf( "\n Chunk Offset Chunk Size Fork Offset" ); cdPtr = (cdInfo *) fdPtr->cdList.head; while( cdPtr ) { printf( "\n%3d) %12llu %12llu %12llu", ++i, (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->chunkSize, (ubigInt) cdPtr->forkOffset ); cdPtr = (cdInfo *) cdPtr->cdLink.next; } printf( "\n" ); } //**************************************************************************** int mffsCdTrace( mffsFile *mffsFP, int forkID ) { fdInfo *fdPtr; fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) returnErrCode( MFFS_INVALID_FORK_ID ); doCdTrace( fdPtr ); returnErrCode( 0 ); } //**************************************************************************** // Linkable Library Functions ************************************************ //**************************************************************************** int mffsClose( mffsFile *mffsFP ) { int rc; mffsSave( mffsFP ); rc = close( mffsFP->fileDes ); if ( rc EQ 0 ) { delete_mffsFile( mffsFP ); mffsFP = NULL; } return( rc ); } //**************************************************************************** //**************************************************************************** static bigSize_t singleChunkDelete( fdInfo *fdPtr, cdInfo *cdPtr, bigSize_t count ) { cdInfo *newCdPtr; bigSize_t nbytes; nbytes = cdPtr->rbytes - count; logDelete(( DBG_FORMAT_3, "", (ubigInt) cdPtr->chunkSize, (ubigInt) count, (ubigInt) nbytes )); if ( nbytes ) { logDelete(( DBG_FORMAT_3, "", (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->chunkSize, (ubigInt) cdPtr->forkOffset )); newCdPtr = new_cdInfo( cdPtr->chunkPointer + cdPtr->lbytes + count, nbytes, cdPtr->forkOffset + cdPtr->lbytes ); logDelete(( DBG_FORMAT_3, "", (ubigInt) newCdPtr->chunkPointer, (ubigInt) newCdPtr->chunkSize, (ubigInt) newCdPtr->forkOffset )); linkAddAfter( &newCdPtr->cdLink, &cdPtr->cdLink, &fdPtr->cdList ); } checkEmptyCD( cdPtr, fdPtr ); return( count ); } //**************************************************************************** static void multiChunkDelete( fdInfo *fdPtr, cdInfo *cdPtr, bigSize_t count ) { cdInfo *nextCdPtr; bigSize_t nbytes; nbytes = count - cdPtr->rbytes; logDelete(( DBG_FORMAT_3, " ", (ubigInt) cdPtr->chunkSize, (ubigInt) count, (ubigInt) nbytes )); cdPtr = (cdInfo *) cdPtr->cdLink.next; while ( nbytes ) { nextCdPtr = (cdInfo *) cdPtr->cdLink.next; if ( nbytes GE cdPtr->chunkSize ) { nbytes -= cdPtr->chunkSize; logDelete(( DBG_FORMAT_3, "Trashing: ", (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->chunkSize, (ubigInt) cdPtr->forkOffset )); linkRemove( &cdPtr->cdLink, &fdPtr->cdList ); delete_cdInfo( cdPtr ); } else { logDelete(( DBG_FORMAT_3, " ", (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->chunkSize, (ubigInt) cdPtr->forkOffset )); cdPtr->forkOffset += nbytes; cdPtr->chunkPointer += nbytes; cdPtr->chunkSize -= nbytes; return; } cdPtr = nextCdPtr; } } //**************************************************************************** bigSize_t mffsDelete( mffsFile *mffsFP, int forkID, bigSize_t count ) { fdInfo *fdPtr; cdInfo *cdPtr; fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) return( 0 ); mffsFP->errCode = 0; count = MIN( count, fdPtr->forkBytes - fdPtr->forkPointer ); if ( count EQ 0 ) return( 0 ); cdPtr = mapCdInfo( fdPtr, fdPtr->forkPointer ); if ( cdPtr EQ NULL ) return( 0 ); logDelete(( DBG_FORMAT_3, " ", (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->chunkSize, (ubigInt) cdPtr->forkOffset )); adjustCdForkOffsets( (cdInfo *) cdPtr->cdLink.next, count * -1 ); fdPtr->forkBytes -= count; fdPtr->modFlag = TRUE; mffsFP->modFlag = TRUE; cdPtr->chunkSize = cdPtr->lbytes; if ( count LE cdPtr->rbytes ) return( singleChunkDelete( fdPtr, cdPtr, count )); multiChunkDelete( fdPtr, cdPtr, count ); checkEmptyCD( cdPtr, fdPtr ); return( count ); } //**************************************************************************** //**************************************************************************** int mffsDeleteFork( mffsFile *mffsFP, int forkID ) { fdInfo *fdPtr; mffsFP->errCode = 0; fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) returnErrCode( MFFS_INVALID_FORK_ID ); linkRemove( &fdPtr->fdLink, &mffsFP->fdList ); delete_fork( fdPtr ); mffsFP->modFlag = TRUE; return( 0 ); } //**************************************************************************** //**************************************************************************** int mffsEOF( mffsFile *mffsFP, int forkID ) { fdInfo *fdPtr; mffsFP->errCode = 0; fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) returnErrCode( MFFS_INVALID_FORK_ID ); if ( fdPtr->forkPointer EQ fdPtr->forkBytes ) return( 1 ); return( 0 ); } //**************************************************************************** //**************************************************************************** int mffsErrCode( mffsFile *mffsFP ) { return( mffsFP->errCode ); } //**************************************************************************** //**************************************************************************** int mffsForkCount( mffsFile *mffsFP ) { mffsFP->errCode = 0; return( mffsFP->numForks ); } //**************************************************************************** //**************************************************************************** bigSize_t mffsForkSize( mffsFile *mffsFP, int forkID ) { fdInfo *fdPtr; fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) return( 0 ); mffsFP->errCode = 0; return( fdPtr->forkBytes ); } //**************************************************************************** //**************************************************************************** int mffsGetForkIDs( mffsFile *mffsFP, int *forkIdList ) { fdInfo *fdPtr; int fdCount = 0; mffsFP->errCode = 0; fdPtr = (fdInfo *) mffsFP->fdList.head; while ( fdPtr ) { fdCount++; *forkIdList++ = fdPtr->forkID; fdPtr = (fdInfo *) fdPtr->fdLink.next; } return( fdCount ); } //**************************************************************************** //**************************************************************************** int mffsGetForkInfo( mffsFile *mffsFP, int forkID, mffsForkInfo *forkInfo ) { fdInfo *fdPtr; fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) { memset( forkInfo, 0, sizeof( mffsForkInfo )); returnErrCode( MFFS_INVALID_FORK_ID ); } mffsFP->errCode = 0; forkInfo->forkID = fdPtr->forkID; forkInfo->forkName = fdPtr->forkName; forkInfo->revLevel = fdPtr->revLevel; forkInfo->timeStamp = fdPtr->timeStamp; forkInfo->forkBytes = fdPtr->forkBytes; return( 0 ); } //**************************************************************************** //**************************************************************************** bigSize_t mffsGetline( mffsFile *mffsFP, int forkID, uchar *buff, bigSize_t count ) { fdInfo *fdPtr; bigSize_t i, nbytes; uchar *chPtr; nbytes = mffsRead( mffsFP, forkID, buff, count ); if ( nbytes EQ 0 ) return( 0 ); mffsFP->errCode = 0; fdPtr = getFdInfo( mffsFP, forkID ); // No need to check for NULL chPtr = buff; for ( i = 0; i LT nbytes; i++ ) { if ( *chPtr EQ '\n' ) { *++chPtr = '\0'; i++; fdPtr->forkPointer = ( fdPtr->forkPointer - nbytes ) + i; return( i ); } chPtr++; } *chPtr = '\0'; return( nbytes ); } //**************************************************************************** //**************************************************************************** static void doInsert( fdInfo *fdPtr, cdInfo *cdPtr, bigSize_t nbytes, bigOff_t filePtr ) { cdInfo *newCdPtr; insertDB(( doCdTrace( fdPtr ))); adjustCdForkOffsets( (cdInfo *) cdPtr->cdLink.next, nbytes ); cdPtr->chunkSize = cdPtr->lbytes; newCdPtr = new_cdInfo( cdPtr->chunkPointer + cdPtr->lbytes, cdPtr->rbytes, cdPtr->forkOffset + cdPtr->lbytes + nbytes ); linkAddAfter( &newCdPtr->cdLink, &cdPtr->cdLink, &fdPtr->cdList ); insertDB(( doCdTrace( fdPtr ))); newCdPtr = new_cdInfo( filePtr, nbytes, fdPtr->forkPointer ); linkAddAfter( &newCdPtr->cdLink, &cdPtr->cdLink, &fdPtr->cdList ); insertDB(( doCdTrace( fdPtr ))); } //**************************************************************************** static void checkInsert( fdInfo *fdPtr, bigSize_t nbytes, bigOff_t filePtr ) { cdInfo *cdPtr; cdPtr = mapCdInfo( fdPtr, fdPtr->forkPointer ); if ( cdPtr ) doInsert( fdPtr, cdPtr, nbytes, filePtr ); else { cdPtr = new_cdInfo( filePtr, nbytes, fdPtr->forkPointer ); linkAppend( &cdPtr->cdLink, &fdPtr->cdList ); logInsert(( DBG_FORMAT_3, "Append: ", (ubigInt) cdPtr->chunkPointer, (ubigInt) cdPtr->chunkSize, (ubigInt) cdPtr->forkOffset )); } checkEmptyCD( cdPtr, fdPtr ); } //**************************************************************************** bigSize_t mffsInsert( mffsFile *mffsFP, int forkID, char *buff, bigSize_t count ) { fdInfo *fdPtr; bigSize_t nbytes; bigOff_t filePtr; mffsFP->errCode = 0; if ( count EQ 0 ) return( 0 ); fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) return( 0 ); filePtr = doSeek( mffsFP->fileDes, 0, SEEK_END, __FUNCTION__ ); nbytes = doWrite( mffsFP, buff, count ); if ( nbytes EQ 0 ) return( 0 ); checkInsert( fdPtr, nbytes, filePtr ); fdPtr->forkBytes += nbytes; fdPtr->forkPointer += nbytes; fdPtr->modFlag = TRUE; mffsFP->modFlag = TRUE; return( nbytes ); } //**************************************************************************** //**************************************************************************** mffsFile *mffsOpen( char *path, int oflag, mode_t mode ) { mffsFile *mffsFP; int fileDes; fileDes = open( path, oflag, mode ); if ( fileDes EQ -1 ) return( NULL ); mffsFP = new_mffsFile(); if ( mffsFP EQ NULL ) return( NULL ); mffsFP->fileDes = fileDes; mffsFP->timeStamp = time( NULL ); readMffs( mffsFP ); return( mffsFP ); } //**************************************************************************** //**************************************************************************** int mffsOpenFork( mffsFile *mffsFP, const char *forkName, mode_t mode ) { fdInfo *fdPtr; mffsFP->errCode = 0; fdPtr = SearchForkName( mffsFP, forkName ); if ( fdPtr ) { fdPtr->mode = mode; return( fdPtr->forkID ); } if ( BIT_TEST( mode, O_CREAT )) { fdPtr = createNewFork( mffsFP, forkName, mode ); return( fdPtr->forkID ); } returnErrCode( MFFS_INVALID_FORK_NAME ); } //**************************************************************************** //**************************************************************************** bigSize_t mffsRead( mffsFile *mffsFP, int forkID, char *buff, bigSize_t count ) { fdInfo *fdPtr; cdInfo *cdPtr; bigSize_t nbytes, totalbytes; fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) return( 0 ); mffsFP->errCode = 0; totalbytes = 0; while ( count ) { cdPtr = mapCdInfo( fdPtr, fdPtr->forkPointer ); if ( cdPtr EQ NULL ) return( totalbytes ); nbytes = MIN( count, cdPtr->rbytes ); nbytes = doRead( mffsFP, buff, nbytes, cdPtr->filePointer ); if ( nbytes EQ 0 ) return( totalbytes ); fdPtr->forkPointer += nbytes; totalbytes += nbytes; count -= nbytes; buff += nbytes; } return( totalbytes ); } //**************************************************************************** //**************************************************************************** int mffsRecover( mffsFile *mffsFP ) { int rc; bigOff_t offset; bigSize_t fsize; mffsFileHeader mFH; fsize = fileSize( mffsFP->fileDes ); if ( fsize LT MFH_SIZE ) // Nothing to recover returnErrCode( MFFS_INVALID_MFH ); offset = fsize - MFH_SIZE; while ( offset ) { if ( readMFH( mffsFP, &mFH, offset ) EQ 0 ) { rc = ftruncate( mffsFP->fileDes, doSeek( mffsFP->fileDes, 0, SEEK_CUR, __FUNCTION__ )); if ( rc ) returnErrCode( MFFS_TRUNCATE ); delete_allForks( mffsFP ); return( buildMffs( mffsFP, &mFH, offset )); } --offset; } returnErrCode( MFFS_INVALID_MFH ); } //**************************************************************************** //**************************************************************************** int mffsRestore( mffsFile *mffsFP, int n ) { int rc; bigOff_t mfhOffset; mffsFileHeader mFH; mfhOffset = mffsFP->prevOffset; while ( mfhOffset NE NULL_OFFSET ) { rc = readMFH( mffsFP, &mFH, mfhOffset ); if ( rc ) return( rc ); if ( mFH.revLevel EQ n ) { rc = ftruncate( mffsFP->fileDes, doSeek( mffsFP->fileDes, 0, SEEK_CUR, __FUNCTION__ )); if ( rc ) returnErrCode( MFFS_TRUNCATE ); delete_allForks( mffsFP ); return( buildMffs( mffsFP, &mFH, mfhOffset )); } mfhOffset = mFH.prevFileHeader; } returnErrCode( MFFS_INVALID_MFH ); } //**************************************************************************** //**************************************************************************** int mffsRevLevel( mffsFile *mffsFP ) { mffsFP->errCode = 0; return( mffsFP->revLevel ); } //**************************************************************************** //**************************************************************************** int mffsRootToFork( mffsFile *mffsFP, const char *forkName, bigOff_t offset, bigSize_t nbytes, mode_t mode ) { fdInfo *fdPtr; cdInfo *cdPtr; mffsFP->errCode = 0; fdPtr = SearchForkName( mffsFP, forkName ); if ( fdPtr ) returnErrCode( MFFS_DUPLICATE_FORK_NAME ); fdPtr = createNewFork( mffsFP, forkName, mode ); if ( nbytes EQ (bigSize_t) -1 ) nbytes = fileSize( mffsFP->fileDes ); cdPtr = new_cdInfo( offset, nbytes, 0 ); linkAppend( &cdPtr->cdLink, &fdPtr->cdList ); fdPtr->forkBytes = nbytes; return( fdPtr->forkID ); } //**************************************************************************** //**************************************************************************** int mffsSave( mffsFile *mffsFP ) { bigOff_t filePtr; mffsFileHeader mFH; mffsFP->errCode = 0; if ( NOT mffsFP->modFlag ) // Nothing to save. return( mffsFP->revLevel ); mffsFP->revLevel++; mffsFP->timeStamp = time( NULL ); filePtr = saveAllForks( mffsFP ); memset( &mFH, 0, (size_t) MFH_SIZE ); mFH.firstFork = filePtr; mFH.revLevel = mffsFP->revLevel; mFH.timeStamp = mffsFP->timeStamp; mFH.prevFileHeader = mffsFP->currOffset; mFH.versionInfo.version = MFFS_VERSION; mFH.versionInfo.mffsType = MFFS_NAME; mffsFP->currOffset = doSeek( mffsFP->fileDes, 0, SEEK_END, __FUNCTION__ ); if ( doWrite( mffsFP, &mFH, MFH_SIZE ) NE MFH_SIZE ) BUGOUT(( -1, "*** write() Error ***\n\n" )); //??? mffsFP->modFlag = FALSE; return( mffsFP->revLevel ); } //**************************************************************************** //**************************************************************************** bigOff_t mffsSeek( mffsFile *mffsFP, int forkID, bigOff_t offset, int whence ) { fdInfo *fdPtr; fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) return( NULL_OFFSET ); mffsFP->errCode = 0; switch ( whence ) { case SEEK_SET: offset = MIN( offset, fdPtr->forkBytes ); fdPtr->forkPointer = offset; break; case SEEK_CUR: offset += fdPtr->forkPointer; offset = MIN( offset, fdPtr->forkBytes ); fdPtr->forkPointer = offset; break; case SEEK_END: offset += fdPtr->forkBytes; offset = MIN( offset, fdPtr->forkBytes ); fdPtr->forkPointer = offset; break; default: offset = NULL_OFFSET; } return( offset ); } //**************************************************************************** //**************************************************************************** typedef struct { int errCode; const char *text; } errorInfo; static const errorInfo errorTable[]= { { MFFS_SEEK, "Seeking in File" }, { MFFS_READ, "Reading MFFS Data" }, { MFFS_INVALID_FORK_NAME, "Invalid Fork Name" }, { MFFS_INVALID_FORK_ID, "Invalid Fork ID Number" }, { MFFS_INVALID_MCD, "Invalid Chunk Descriptor" }, { MFFS_INVALID_MFD, "Invalid Fork Descriptor" }, { MFFS_INVALID_MFH, "Not a MFFS File" }, { MFFS_DUPLICATE_FORK_NAME, "Duplicate Fork Name" }, { MFFS_TRUNCATE, "Error in ftruncate()" }, { 0, NULL } }; const char *mffsStrError( int errCode ) { const errorInfo *tablePtr; tablePtr = errorTable; while ( tablePtr->errCode ) { if ( errCode EQ tablePtr->errCode ) return( tablePtr->text ); tablePtr++; } return( "Unknown Error Code" ); } //**************************************************************************** //**************************************************************************** time_t mffsTimeStamp( mffsFile *mffsFP ) { return( mffsFP->timeStamp ); } //**************************************************************************** //**************************************************************************** static void appendWrite( fdInfo *fdPtr, bigOff_t filePtr, bigSize_t nbytes ) { cdInfo *cdPtr; cdPtr = mapCdInfo( fdPtr, fdPtr->forkPointer - 1 ); if ( cdPtr ) { if ( cdPtr->filePointer EQ filePtr - 1 ) cdPtr->chunkSize += nbytes; else { cdPtr = new_cdInfo( filePtr, nbytes, fdPtr->forkPointer ); linkAppend( &cdPtr->cdLink, &fdPtr->cdList ); } } else { cdPtr = new_cdInfo( filePtr, nbytes, fdPtr->forkPointer ); linkAppend( &cdPtr->cdLink, &fdPtr->cdList ); } } //**************************************************************************** bigSize_t mffsWrite( mffsFile *mffsFP, int forkID, char *buff, bigSize_t count ) { fdInfo *fdPtr; bigOff_t filePtr; bigSize_t nbytes; mffsFP->errCode = 0; if ( count EQ 0 ) return( 0 ); fdPtr = getFdInfo( mffsFP, forkID ); if ( fdPtr EQ NULL ) return( 0 ); if ( fdPtr->forkBytes NE fdPtr->forkPointer ) // over write { logWrite(( DBG_FORMAT_1, "Over Write 1:", (ubigInt) fdPtr->forkPointer )); mffsDelete( mffsFP, forkID, count ); return( mffsInsert( mffsFP, forkID, buff, count )); } filePtr = doSeek( mffsFP->fileDes, 0, SEEK_END, __FUNCTION__ ); nbytes = doWrite( mffsFP, buff, count ); logWrite(( DBG_FORMAT_3, " ", (ubigInt) filePtr, (ubigInt) fdPtr->forkPointer, (ubigInt) nbytes )); if ( nbytes EQ 0 ) return( 0 ); if ( nbytes EQ (bigSize_t) -1 ) return( (bigSize_t) -1 ); appendWrite( fdPtr, filePtr, nbytes ); fdPtr->forkBytes += nbytes; fdPtr->forkPointer += nbytes; fdPtr->modFlag = TRUE; mffsFP->modFlag = TRUE; return( nbytes ); } // EoFile ******************************************************************** //****************************************************************************