#include "config.h" #include #include #include #include "daedef.h" #include "util.h" #include "pib.h" #include "ssib.h" #include "page.h" #include "fmp.h" #include "fucb.h" #include "fcb.h" #include "stapi.h" #include "node.h" #include "logapi.h" #include "log.h" #include "nver.h" #include "dirapi.h" #include "fileapi.h" #include "systab.h" #include "dbapi.h" #include "bm.h" DeclAssertFile; /* Declare file name for Assert macros */ extern int itibGlobal; typedef struct { BYTE bDbid; BYTE bLoggable; /* must be last field in structure */ CHAR rgchDatabaseName[1]; } DBA; extern PIB * __near ppibAnchor; #ifdef DEBUG extern BOOL fDBGForceFlush; #endif LOCAL ERR ErrDBRemoveDatabase( PIB *ppib, CHAR *szDatabaseName ); LOCAL ERR ErrDBAddDatabase( PIB *ppib, CHAR *szDatabaseName, DBID dbid ); LOCAL ERR ErrDBInitDatabase( PIB *ppib, DBID dbid, CPG cpg, BOOL fNoLogging ); LOCAL ERR ErrDBValidDatabaseFile( CHAR *szDatabaseName, ULONG *pulVersion, BOOL *pfLoggingCapable); ERR ErrFMPSetDatabases( PIB *ppib ) { ERR err; FUCB *pfucb; DIB dib; DBID dbid; INT cchDatabaseName; CallR( ErrDIROpen( ppib, pfcbNil, dbidSystemDatabase, &pfucb ) ); DIRGotoFDPRoot( pfucb ); dib.pos = posFirst; dib.fFlags = fDIRNull; Call( ErrDIRSeekPath( pfucb, 1, pkeyDatabases, 0 ) ); Call( ErrDIRDown( pfucb, &dib ) ); do { Call( ErrDIRGet( pfucb ) ); Assert( pfucb->lineData.cb > sizeof(BYTE) * 2 ); dbid = (DBID) ((DBA *)pfucb->lineData.pb)->bDbid; rgfmp[dbid].fLogOn = rgfmp[dbid].fDBLoggable = (BOOL) ((DBA *)pfucb->lineData.pb)->bLoggable; cchDatabaseName = pfucb->lineData.cb - offsetof( DBA, rgchDatabaseName ); /* allocate bytes for database name with NULL terminator /**/ if (!(rgfmp[dbid].szDatabaseName = SAlloc( cchDatabaseName + 1 ))) Error( JET_errOutOfMemory, HandleError ); strncpy( rgfmp[dbid].szDatabaseName, (BYTE *)&(((DBA *)pfucb->lineData.pb)->rgchDatabaseName), cchDatabaseName ); rgfmp[dbid].szDatabaseName[cchDatabaseName] = '\0'; DBIDSetAttached( dbid ); err = ErrDIRNext( pfucb, &dib ); } while ( err >= 0 ); HandleError: DIRClose( pfucb ); /* polymorph end of file to JET_errSuccess /**/ if ( err == JET_errRecordNotFound || err == JET_errNoCurrentRecord ) err = JET_errSuccess; /* return error code /**/ return err; } //+local // ErrDBInitDatabase // ======================================================================== // ErrDBInitDatabase( PIB *ppib, DBID dbid, CPG cpgPrimary ) // // Initializes database structure. Structure is customized for // system, temporary and user databases which are identified by // the dbid. Primary extent is set to cpgPrimary but no allocation // is performed. The effect of this routine can be entirely // represented with page operations. // // PARAMETERS ppib ppib of database creator // dbid dbid of created database // cpgPrimary number of pages to show in primary extent // // RETURNS JET_errSuccess or error returned from called routine. //- LOCAL ERR ErrDBInitDatabase( PIB *ppib, DBID dbid, CPG cpgPrimary, BOOL fNoLogging ) { ERR err; THREEBYTES tbSize; THREEBYTES tbLast; LINE line; KEY key; BYTE rgbT[sizeof(THREEBYTES)]; FUCB *pfucb = pfucbNil; BYTE rgb[sizeof(DBROOT)]; /* set up the root page /**/ CallR( ErrDIRBeginTransaction( ppib ) ); /* open cursor on database domain. /**/ Call( ErrDIROpen( ppib, pfcbNil, dbid, &pfucb ) ); rgfmp[dbid].ulDBTime = 0; /* set system root node ( pgno, itag )=( 1, 0 ) as empty FDP node /**/ Call( ErrNDNewPage( pfucb, pgnoSystemRoot, pgnoSystemRoot, pgtypFDP, fTrue ) ); DIRGotoFDPRoot( pfucb ); /* data for root of database is magic number followed /* by version number (followed by timestamp followed by flags) /**/ ((DBROOT *)rgb)->ulMagic = ulDAEMagic; ((DBROOT *)rgb)->ulVersion = ulDAEVersion; /* initialize timestamp */ ( (DBROOT *)rgb)->ulDBTime = 0; /* initialize flags, set flags to "loggable" except TMP */ ( (DBROOT *)rgb)->usFlags = ( FSysTabDatabase( dbid ) && !fNoLogging ) ? dbrootfLoggable : 0; line.cb = sizeof(DBROOT); line.pb = rgb; Call( ErrDIRReplace( pfucb, &line, fDIRNoVersion ) ); /* make the "ownext" node /**/ ThreeBytesFromL( *( THREEBYTES *)rgbT, cpgPrimary ); line.cb = sizeof(THREEBYTES); line.pb = rgbT; Call( ErrDIRInsert( pfucb, &line, pkeyOwnExt, fDIRNoVersion | fDIRBackToFather ) ); /* make the "availext" node /**/ ThreeBytesFromL( *( THREEBYTES *) rgbT, pgnoNull ); Assert( line.cb == sizeof(THREEBYTES) ); Assert( line.pb == (BYTE *)rgbT ); Call( ErrDIRInsert( pfucb, &line, pkeyAvailExt, fDIRNoVersion | fDIRBackToFather ) ); /* setup OwnExt tree /**/ TbKeyFromPgno( tbLast, cpgPrimary ); key.cb = sizeof(THREEBYTES); key.pb = (BYTE *) &tbLast; ThreeBytesFromL( tbSize, cpgPrimary ); line.cb = sizeof(THREEBYTES); line.pb = (BYTE *) &tbSize; DIRGotoOWNEXT( pfucb, PgnoFDPOfPfucb( pfucb ) ); Call( ErrDIRInsert( pfucb, &line, &key, fDIRNoVersion | fDIRBackToFather ) ); /* setup AvailExt tree if there are any pages left /**/ if ( --cpgPrimary > 0 ) { DIRGotoAVAILEXT( pfucb, PgnoFDPOfPfucb( pfucb ) ); ThreeBytesFromL( tbSize, cpgPrimary ); Assert( line.cb == sizeof(THREEBYTES) ); Assert( line.pb == (BYTE *) &tbSize ); /* tbLast should still contain last page key */ Assert( key.cb == sizeof(THREEBYTES) ); Assert( key.pb == (BYTE *) &tbLast ); Call( ErrDIRInsert( pfucb, &line, &key, fDIRNoVersion | fDIRBackToFather ) ); } /* goto FDP root and add pkeyTables son node. /**/ DIRGotoFDPRoot( pfucb ); line.cb = sizeof(WORD); line.pb = rgbT; *(WORD *)rgbT = 0; Call( ErrDIRInsert( pfucb, &line, pkeyTables, fDIRNoVersion | fDIRBackToFather ) ); /* system database /**/ if ( dbid == dbidSystemDatabase ) { /* add "databases" node /**/ line.cb = 0; Call( ErrDIRInsert( pfucb, &line, pkeyDatabases, fDIRNoVersion | fDIRBackToFather ) ); } /* close cursor and commit operations /**/ DIRClose( pfucb ); pfucb = pfucbNil; Call( ErrDIRCommitTransaction( ppib ) ); return err; HandleError: if ( pfucb != pfucbNil ) DIRClose( pfucb ); CallS( ErrDIRRollback( ppib ) ); return err; } #ifdef NO_OVER_PREREAD //to prevent read ahead over-preread, we may want to keep track of last //page of the database. ERR ErrDBSetLastPage( PIB *ppib, DBID dbid ) { ERR err; DIB dib; FUCB *pfucb; CallR( ErrDIROpen( ppib, pfcbNil, dbid, &pfucb ) ); DIRGotoAVAILEXT( pfucb, PgnoFDPOfPfucb( pfucb ) ); dib.fFlags = fDIRNull; dib.pos = posLast; CallJ( ErrDIRDown( pfucb, &dib ), CloseFucb ); Assert( pfucb->keyNode.cb == sizeof( THREEBYTES ) ); PgnoFromTbKey( rgfmp[dbid].pgnoLast, *(THREEBYTES *) pfucb->keyNode.pb ); CloseFucb: DIRClose( pfucb ); return err; } #endif #ifdef DISPATCHING LOCAL ERR ErrDABAlloc( PIB *ppib, VDBID *pvdbid, DBID dbid, JET_GRBIT grbit ) { VDBID vdbid = (VDBID)VdbidMEMAlloc(); if ( vdbid == NULL ) return JET_errTooManyOpenDatabases; vdbid->dbid = dbid; vdbid->ppib = ppib; /* set the mode of DBopen */ /**/ if ( FDBIDReadOnly( dbid ) ) vdbid->grbit = JET_bitDbReadOnly; else vdbid->grbit = grbit; /* insert DAB/VDBID into ppib's dabList /**/ vdbid->pdabNext = ppib->pdabList; ppib->pdabList = vdbid; *pvdbid = vdbid; return JET_errSuccess; } #endif LOCAL ERR ErrDABDealloc( PIB *ppib, VDBID vdbid) { DAB **pdabPrev; DAB *pdab; pdab = ppib->pdabList; pdabPrev = &ppib->pdabList; /* search thru thread's DAB list and unlink this DAB /**/ for( ; pdab != pdabNil; pdabPrev = &pdab->pdabNext, pdab = pdab->pdabNext ) { Assert( ppib == pdab->ppib ); if ( pdab == vdbid ) { *pdabPrev = pdab->pdabNext; ReleaseVDbid( vdbid ); return( JET_errSuccess ); } } Assert( fFalse ); return( JET_errSuccess ); } ERR ISAMAPI ErrIsamCreateDatabase( JET_VSESID sesid, const char __far*szDatabaseName, const char __far *szConnect, JET_DBID *pjdbid, JET_GRBIT grbit ) { ERR err; PIB *ppib; DBID dbid; VDBID vdbid = vdbidNil; /* check parameters */ Assert(sizeof(JET_VSESID) == sizeof(PIB *)); ppib = (PIB*) sesid; dbid = 0; CallR( ErrDBCreateDatabase( ppib, (char *) szDatabaseName, (char *) szConnect, &dbid, grbit ) ); #ifdef DISPATCHING Call( ErrDABAlloc( ppib, &vdbid, (DBID) dbid, JET_bitDbExclusive ) ); Assert(sizeof(vdbid) == sizeof(JET_VDBID)); Call( ErrAllocateDbid( pjdbid, (JET_VDBID) vdbid, &vdbfndefIsam ) ); #else *pjdbid = (JET_DBID) dbid; #endif /* !DISPATCHING */ return JET_errSuccess; HandleError: if ( vdbid != vdbidNil ) CallS( ErrDABDealloc( ppib, vdbid ) ); (VOID)ErrDBCloseDatabase( ppib, dbid, grbit ); (VOID)ErrDBRemoveDatabase( ppib, (char *) szDatabaseName ); return err; } ERR ErrDBCreateDatabase( PIB *ppib, CHAR *szDatabaseName, CHAR *szConnect, DBID *pdbid, ULONG grbit ) { ERR err; DBID dbid; CHAR rgbFullName[JET_cbFullNameMost]; CHAR *szFullName; ULONG cpgPrimary = cpgDatabaseMin; BOOL fNoLoggingCapability; CHAR *szFileName; CheckPIB( ppib ); NotUsed(szConnect); Assert( *pdbid >= 0 && *pdbid < dbidUserMax ); if (grbit & JET_bitDbNoLogging) grbit |= JET_bitDbRecoveryOff; if ( ppib->level > 0 ) return JET_errInTransaction; if ( cpgPrimary == 0 ) cpgPrimary = cpgDatabaseMin; if ( cpgPrimary > cpgDatabaseMax ) return( JET_errDatabaseInvalidPages ); /* if recovering and dbid is a known one, the lock the dbid first */ if ( fRecovering && *pdbid != dbidTemp ) { dbid = *pdbid; /* get corresponding dbid */ CallS( ErrIOLockNewDbid( &dbid, rgfmp[dbid].szDatabaseName ) ); } if ( fRecovering && *pdbid != dbidTemp && rgfmp[dbid].szRestorePath ) { szFullName = rgfmp[dbid].szDatabaseName; szFileName = rgfmp[dbid].szRestorePath; } else { if ( _fullpath( rgbFullName, szDatabaseName, JET_cbFullNameMost ) == NULL ) { return(JET_errDatabaseNotFound); } szFullName = rgbFullName; szFileName = szFullName; } /* check if database file already exists /**/ if ( FIOFileExists( szFileName ) ) { err = JET_errDatabaseDuplicate; return err; } if ( !( fRecovering && *pdbid != dbidTemp ) ) { err = ErrIOLockNewDbid( &dbid, szFullName ); if ( err != JET_errSuccess ) { if ( err == JET_wrnDatabaseAttached ) err = JET_errDatabaseDuplicate; return err; } } // UNDONE: add support to FFileExists and remove check for file in // use here. if ( Hf(dbid) != handleNil ) { IOUnlockDbid( dbid ); err = JET_errDatabaseDuplicate; return err; } err = ErrIOOpenDatabase( dbid, szFileName, cpgPrimary ); if ( err < 0 ) { IOFreeDbid( dbid ); return err; } /* set database non-loggable during CreateDB /**/ rgfmp[dbid].fLogOn = fFalse; /* not in a transaction, but still need to set lgposRC of the buffers /* used by this function such that when get checkpoint, it will get /* right check point. /**/ if ( !( fLogDisabled || fRecovering ) ) { EnterCriticalSection(critLGBuf); GetLgposOfPbEntry( &ppib->lgposStart ); LeaveCriticalSection(critLGBuf); } /* initialize the database file. Logging of page operations is /* turned off (during creation only). After creation the database /* is marked "loggable" and logging is turned on. /**/ SetOpenDatabaseFlag( ppib, dbid ); fNoLoggingCapability = grbit & JET_bitDbNoLogging; Call( ErrDBInitDatabase(ppib, dbid, cpgPrimary, fNoLoggingCapability)); #ifdef SYSTABLES if ( FSysTabDatabase( dbid ) ) { Call( ErrSysTabCreate( ppib, dbid ) ); // Create system tables } #endif /* SYSTABLES */ #ifdef NO_OVER_PREREAD /* * set the last page of the database, used to prevent over preread. */ Call( ErrDBSetLastPage(ppib, dbid) ); #endif /* * set database status to loggable */ if ( grbit & JET_bitDbRecoveryOff ) { rgfmp[ dbid ].fDBLoggable = fFalse; } else if ( FSysTabDatabase( dbid ) ) { /* set all databases loggable except Temp if allowed /**/ rgfmp[dbid].fDBLoggable = !fNoLoggingCapability; } rgfmp[dbid].fLogOn = rgfmp[dbid].fDBLoggable; /* log Create Database prior to page modifications so file will /* be open during redo of page modifications if soft crash occurs /**/ Call( ErrLGCreateDB( ppib, dbid, rgfmp[ dbid ].fLogOn, grbit, szFullName, strlen(szFullName) + 1)); #ifdef DEBUG { /* must be a complete flush since the whole create db is log as one /* operation. /**/ BOOL fDBGForceFlushSav = fDBGForceFlush; fDBGForceFlush = fTrue; #endif /* flush buffers if not temporary database /**/ if ( dbid != dbidTemp ) { Call( ErrBFFlushBuffers( dbid, fBFFlushSome ) ); } #ifdef DEBUG fDBGForceFlush = fDBGForceFlushSav; rgfmp[ dbid ].lBFFlushPattern = grbit >> 24; } #endif /* if recover, the insertion is done by redoing logged insertion * operations. No need to do it again. Only do the database initialization. */ if ( !fRecovering && FUserDbid( dbid ) ) { err = ErrDBAddDatabase( ppib, szFullName, dbid ); if (err < 0 && err != JET_errKeyDuplicate ) goto HandleError; } *pdbid = dbid; IOSetAttached( dbid ); IOUnlockDbid( dbid ); return JET_errSuccess; HandleError: /* functions may only use the call macro when the system state /* is file exists, file open or closed, database record fWait /* set, database record name valid and user logging status /* valid. /**/ BFPurge( dbid, 0 ); /* Purge bad db */ if ( FIODatabaseOpen(dbid) ) IOCloseDatabase( dbid ); IODeleteDatabase( dbid ); ResetOpenDatabaseFlag( ppib, dbid ); IOFreeDbid( dbid ); return err; } ERR ISAMAPI ErrIsamAttachDatabase( JET_VSESID sesid, const char __far *szDatabaseName, JET_GRBIT grbit ) { PIB *ppib; ERR err; DBID dbid; CHAR rgbFullName[JET_cbFullNameMost]; CHAR *szFullName; ULONG ulVersion; BOOL fLoggingCapable; /* check parameters */ Assert(sizeof(JET_VSESID) == sizeof(PIB *)); ppib = (PIB*) sesid; CheckPIB( ppib ); if ( ppib->level > 0 ) return JET_errInTransaction; if ( _fullpath( rgbFullName, szDatabaseName, JET_cbFullNameMost ) == NULL ) { return(JET_errDatabaseNotFound); } szFullName = rgbFullName; /* depend on _fullpath to make same files same name /* thereby preventing same file to be multiply attached /**/ err = ErrIOLockNewDbid( &dbid, szFullName ); if ( err != JET_errSuccess ) { Assert( err == JET_wrnDatabaseAttached || err == JET_errOutOfMemory || err == JET_errTooManyAttachedDatabases ); return err; } Call( ErrDBValidDatabaseFile( szFullName, &ulVersion, &fLoggingCapable ) ); #ifdef NO_OVER_PREREAD /* * set the last page of the database, used to prevent over preread. */ Call( ErrDBSetLastPage( ppib, dbid ) ); #endif /* set database loggable flags. /**/ if ( grbit & JET_bitDbRecoveryOff ) { rgfmp[dbid].fDBLoggable = fFalse; } else if ( FSysTabDatabase( dbid ) ) { /* set all databases loggable except Temp if allowed /**/ rgfmp[dbid].fDBLoggable = fLoggingCapable; } rgfmp[dbid].fLogOn = rgfmp[dbid].fDBLoggable; #ifdef DEBUG rgfmp[ dbid ].lBFFlushPattern = grbit >> 24; #endif Call( ErrDBAddDatabase( ppib, szFullName, dbid ) ); /* log Attach /**/ Assert( dbid != dbidTemp ); Call( ErrLGAttachDB( ppib, dbid, rgfmp[ dbid ].fLogOn, (char *) szFullName, strlen(szFullName) + 1) ); IOSetAttached( dbid ); IOUnlockDbid( dbid ); return JET_errSuccess; HandleError: IOFreeDbid( dbid ); return err; } ERR ErrDBAccessDatabaseRoot( DBID dbid, SSIB *pssib, DBROOT **ppdbroot ) { PN pn; ERR err; BOOL fRecoveringSav; /* read in the FDP */ pn = ((PN)dbid << 24) | (PGNO)1; /* set frecovering = fTrue to disable the Access page time stamp */ /* check. The time stamp check access pfmp->ulDBTime which is */ /* not initialized util this function is called. */ fRecoveringSav = fRecovering; fRecovering = fTrue; CallR( ErrBFAccessPage( pssib->ppib, &pssib->pbf, pn ) ); fRecovering = fRecoveringSav; /* root node */ pssib->itag = 0; CallR( ErrPMGet( pssib, pssib->itag ) ); *ppdbroot = (DBROOT*)PbNDData( pssib->line.pb ); /* point to root struct */ return JET_errSuccess; } /* This is a special function always followed by BFFlush. No logging no /* Version for it. It is called in detach database or system shut down. /**/ ERR ErrDBUpdateDatabaseRoot( DBID dbid ) { ERR err; LINE rgline[3]; DBROOT *pdbroot; USHORT usFlags; SSIB ssibT; /* if database updatable then update root /**/ if( !FDBIDReadOnly( dbid ) ) { /* no operations on the database. This check is needed for recovering. /* We may attach a database, without proper ulDBTime set, and then no /* operation for this database. In this case, we do not want to /* update its ulDBTime. /**/ if ( rgfmp[ dbid ].ulDBTime == 0 ) return JET_errSuccess; CallR( ErrDBAccessDatabaseRoot(dbid, &ssibT, &pdbroot ) ); /* since database root node is fixed, we can wait /* for write latch without refreshing currency /**/ while ( FBFWriteLatchConflict( ssibT.ppib, ssibT.pbf ) ) { BFSleep( cmsecWaitWriteLatch ); } /* write latch page from dirty until log completion /**/ BFPin( ssibT.pbf ); BFSetWriteLatch( ssibT.pbf, ssibT.ppib ); /* dirty here but not to increment the DBTime /**/ BFSetDirtyBit ( ssibT.pbf ); usFlags = pdbroot->usFlags; rgline[0].pb = ssibT.line.pb; rgline[0].cb = ssibT.line.cb - sizeof(ULONG) - sizeof(USHORT); Assert( !fRecovering || fDBGForceFlush || /* will be greater if UNDO is appended. */ rgfmp[ dbid ].ulDBTime >= rgfmp[ dbid ].ulDBTimeCurrent); Assert( rgfmp[ dbid ].ulDBTime > 0 ); /* timestamp /**/ rgline[1].pb = (char *)&(rgfmp[ dbid ].ulDBTime); rgline[1].cb = sizeof( ULONG ); rgline[2].pb = (char *)&usFlags; rgline[2].cb = sizeof(USHORT); CallS( ErrPMReplace( &ssibT, rgline, 3 ) ); BFResetWriteLatch( ssibT.pbf, ssibT.ppib ); BFUnpin( ssibT.pbf ); } return JET_errSuccess; } ERR ISAMAPI ErrIsamDetachDatabase( JET_VSESID sesid, const char __far *szDatabaseName ) { ERR err; PIB *ppib; DBID dbid; CHAR rgbFullName[JET_cbFullNameMost]; CHAR *szFullName; DBID dbidDetach; /* check parameters /**/ Assert( sizeof(JET_VSESID) == sizeof(PIB *) ); ppib = (PIB *)sesid; CheckPIB( ppib ); if ( ppib->level > 0 ) return JET_errInTransaction; if ( szDatabaseName == NULL ) { dbidDetach = dbidUserMin; } Start: if ( szDatabaseName == NULL ) { for ( dbidDetach++; dbidDetach < dbidUserMax && rgfmp[dbidDetach].szDatabaseName == NULL; dbidDetach++ ); Assert( dbidDetach != dbidSystemDatabase ); if ( dbidDetach == dbidUserMax ) return JET_errSuccess; szFullName = rgfmp[dbidDetach].szDatabaseName; } else { if ( _fullpath( rgbFullName, szDatabaseName, JET_cbFullNameMost ) == NULL ) { return(JET_errDatabaseNotFound); } szFullName = rgbFullName; } /* purge all MPL entries for this dbid /**/ MPLPurge( dbid ); err = ErrIOLockDbidByNameSz( szFullName, &dbid ); if ( err == JET_errSuccess && FIODatabaseInUse( dbid ) ) { IOUnlockDbid( dbid ); err = JET_errDatabaseInUse; } if ( err < 0 ) { return err; } /* clean up all version store. Actually we only need to clean up /* the entries that had dbid as the dbid for the new database. /**/ Call( ErrRCECleanAllPIB() ); if ( FIODatabaseOpen(dbid) ) { /* if database is updatable then update time stamp. /**/ if ( !FDBIDReadOnly( dbid ) ) { Call( ErrDBUpdateDatabaseRoot( dbid ) ); } /* flush all database buffers /**/ err = ErrBFFlushBuffers( dbid, fBFFlushSome ); if ( err < 0 ) { IOUnlockDbid( dbid ); return err; } /* purge all buffers for this dbid */ BFPurge( dbid, (PGNO)0 ); IOCloseDatabase( dbid ); } Call( ErrDBRemoveDatabase( ppib, szFullName ) ); // log Detach Assert( dbid != dbidTemp ); Call( ErrLGDetachDB( ppib, dbid, rgfmp[dbid].fLogOn, (char *) szFullName, strlen(szFullName) + 1) ); /* purge all MPL entries for this dbid /**/ MPLPurge( dbid ); /* clean up all version store again in case any versions created /* by on-line compaction or update of database root. /**/ Call( ErrRCECleanAllPIB() ); /* do not free dbid on detach to avoid problems related to /* version RCE aliasing and database name conflict during /* recovery. /**/ #ifdef REUSEDBID IOResetAttached( dbid ); IOResetExclusive( dbid ); IOUnlockDbid( dbid ); #else IOFreeDbid( dbid ); #endif /* purge open table fcbs to avoid future confusion /**/ FCBPurgeDatabase( dbid ); if ( szDatabaseName == NULL ) goto Start; return JET_errSuccess; HandleError: IOUnlockDbid( dbid ); return err; } /* DAE databases are repaired automatically on system restart /**/ ERR ISAMAPI ErrIsamRepairDatabase( JET_VSESID sesid, const char __far *lszDbFile, JET_PFNSTATUS pfnstatus ) { PIB *ppib; Assert(sizeof(JET_VSESID) == sizeof(PIB *)); ppib = (PIB*) sesid; NotUsed(ppib); NotUsed(lszDbFile); NotUsed(pfnstatus); return JET_errFeatureNotAvailable; } ERR ISAMAPI ErrIsamOpenDatabase( JET_VSESID sesid, const char __far *szDatabaseName, const char __far *szConnect, JET_DBID *pjdbid, JET_GRBIT grbit ) { ERR err; PIB *ppib; DBID dbid; VDBID vdbid = vdbidNil; /* check parameters */ Assert(sizeof(JET_VSESID) == sizeof(PIB *)); ppib = (PIB*) sesid; NotUsed(szConnect); CheckPIB( ppib ); dbid = 0; CallR( ErrDBOpenDatabase( ppib, (char *)szDatabaseName, &dbid, grbit ) ); #ifdef DISPATCHING Call( ErrDABAlloc( ppib, &vdbid, dbid, grbit ) ); Assert(sizeof(vdbid) == sizeof(JET_VDBID)); Call( ErrAllocateDbid( pjdbid, (JET_VDBID) vdbid, &vdbfndefIsam ) ); #else /* !DISPATCHING */ *pjdbid = (JET_DBID) dbid; #endif /* !DISPATCHING */ return JET_errSuccess; HandleError: if ( vdbid != vdbidNil ) CallS( ErrDABDealloc( ppib, vdbid ) ); CallS( ErrDBCloseDatabase( ppib, dbid, grbit ) ); return err; } ERR ErrDBOpenDatabase( PIB *ppib, CHAR *szDatabaseName, DBID *pdbid, ULONG grbit ) { ERR err; CHAR rgbFullName[JET_cbFullNameMost]; CHAR *szFullName; CHAR *szFileName; DBID dbid; ULONG ulVersion; if ( fRecovering ) { CallS( ErrIOLockDbidByNameSz( szDatabaseName, &dbid ) ); } if ( fRecovering && rgfmp[dbid].szRestorePath ) { szFullName = rgfmp[dbid].szDatabaseName; szFileName = rgfmp[dbid].szRestorePath; } else { if ( _fullpath( rgbFullName, szDatabaseName, JET_cbFullNameMost ) == NULL ) { return(JET_errDatabaseNotFound); } szFullName = rgbFullName; szFileName = szFullName; } if ( !fRecovering ) { CallR( ErrIOLockDbidByNameSz( szFullName, &dbid ) ); } /* during recovering, we could open an non-detached database /* to force to initialize the fmp entry. /* if database has been detached, then return error. /**/ if ( !fRecovering && !FIOAttached( dbid ) ) { err = JET_errDatabaseNotFound; goto HandleError; } if ( FIOExclusiveByAnotherSession( dbid, ppib ) ) { IOUnlockDbid( dbid ); return JET_errDatabaseLocked; } if ( ( grbit & JET_bitDbExclusive ) ) { if ( FIODatabaseInUse( dbid ) ) { IOUnlockDbid( dbid ); return JET_errDatabaseInUse; } IOSetExclusive( dbid, ppib ); } if ( Hf(dbid) == handleNil ) { /* newly opened database /**/ SSIB ssibT; DBROOT *pdbroot; BOOL fLoggingCapable; Call( ErrDBValidDatabaseFile( szFileName, &ulVersion, &fLoggingCapable ) ); Call( ErrIOOpenDatabase( dbid, szFileName, 0L ) ); CallR( ErrDBAccessDatabaseRoot(dbid, &ssibT, &pdbroot ) ); rgfmp[ dbid ].ulDBTime = pdbroot->ulDBTime; /* copy last timestamp */ /* loggable flag /**/ Assert( pdbroot->usFlags & dbrootfLoggable || ( !rgfmp[ dbid ].fDBLoggable && !rgfmp[ dbid ].fLogOn ) ); IOSetDatabaseVersion( dbid, ulVersion ); } IOUnlockDbid( dbid ); SetOpenDatabaseFlag( ppib, dbid ); *pdbid = dbid; return JET_errSuccess; HandleError: IOResetExclusive( dbid ); IOUnlockDbid( dbid ); return err; } ERR ISAMAPI ErrIsamCloseDatabase( JET_VSESID sesid, JET_VDBID vdbid, JET_GRBIT grbit ) { ERR err; PIB *ppib; DBID dbid = DbidOfVDbid( vdbid ); ULONG grbitOpen; // flags for corresponding open /* check parameters /**/ Assert( sizeof(JET_VSESID) == sizeof(PIB *) ); ppib = (PIB *)sesid; NotUsed(grbit); CheckPIB( ppib ); CheckDBID( ppib, dbid ); grbitOpen = GrbitOfVDbid( vdbid ); err = ErrDBCloseDatabase( ppib, dbid, grbitOpen ); #ifdef DISPATCHING if ( err == JET_errSuccess ) { ReleaseDbid( DbidOfVdbid( vdbid, &vdbfndefIsam ) ); CallS( ErrDABDealloc( ppib, (VDBID) vdbid ) ); } #endif /* DISPATCHING */ return err; } ERR ErrDBCloseDatabase( PIB *ppib, DBID dbid, ULONG grbit ) { ERR err; FUCB *pfucb; FUCB *pfucbNext; if ( !( FUserOpenedDatabase( ppib, dbid ) ) ) { return JET_errDatabaseNotFound; } CallR( ErrIOLockDbidByDbid( dbid ) ); Assert( FIODatabaseOpen( dbid ) ); if ( FLastOpen( ppib, dbid ) ) { /* close all open FUCBs on this database /**/ /* get first table FUCB /**/ pfucb = ppib->pfucb; while ( pfucb != pfucbNil && ( pfucb->dbid != dbid || !FFCBClusteredIndex( pfucb->u.pfcb ) ) ) pfucb = pfucb->pfucbNext; while ( pfucb != pfucbNil ) { /* get next table FUCB /**/ pfucbNext = pfucb->pfucbNext; while ( pfucbNext != pfucbNil && ( pfucbNext->dbid != dbid || !FFCBClusteredIndex( pfucbNext->u.pfcb ) ) ) pfucbNext = pfucbNext->pfucbNext; if ( !( FFUCBDeferClosed( pfucb ) ) ) { if ( pfucb->fVtid ) { CallS( ErrDispCloseTable( (JET_SESID)ppib, TableidOfVtid( pfucb ) ) ); } else { CallS( ErrFILECloseTable( ppib, pfucb ) ); } } pfucb = pfucbNext; } } /* if we opened it exclusively, we reset the flag /**/ ResetOpenDatabaseFlag( ppib, dbid ); if ( grbit & JET_bitDbExclusive ) IOResetExclusive( dbid ); IOUnlockDbid( dbid ); /* do not close file until file map space needed or database /* detached. /**/ return JET_errSuccess; } /* called by bookmark clean up to open database for bookmark /* clean up operation. Returns error if database is in use for /* attachment/detachment. /**/ ERR ErrDBOpenDatabaseByDbid( PIB *ppib, DBID dbid ) { if ( !FIODatabaseAvailable( dbid ) ) { return JET_errDatabaseNotFound; } SetOpenDatabaseFlag( ppib, dbid ); return JET_errSuccess; } /* called by bookmark clean up to close database. /**/ ERR ErrDBCloseDatabaseByDbid( PIB *ppib, DBID dbid ) { ResetOpenDatabaseFlag( ppib, dbid ); return JET_errSuccess; } LOCAL ERR ErrDBRemoveDatabase( PIB *ppib, CHAR *szDatabaseName ) { ERR err = JET_errSuccess; FUCB *pfucb; KEY rgkey[2]; BYTE rgbKey[ JET_cbKeyMost ]; DBID dbidSysDb; /* remove the entry added under DATABASES in the system database /* for the detached database /**/ CallR( ErrDIRBeginTransaction( ppib ) ); dbidSysDb = dbidSystemDatabase; Call( ErrDBOpenDatabase( ppib, rgfmp[dbidSystemDatabase].szDatabaseName, &dbidSysDb, 0 ) ); CallJ( ErrDIROpen( ppib, pfcbNil, dbidSystemDatabase, &pfucb ), DbClose ); /* normalize database name and set key and seek /**/ rgkey[0] = *pkeyDatabases; SysNormText( szDatabaseName, strlen( szDatabaseName ), rgbKey, sizeof( rgbKey ), &rgkey[1].cb ); rgkey[1].pb = rgbKey; err = ErrDIRSeekPath( pfucb, 2, rgkey, 0 ); Assert( err != JET_errNoCurrentRecord ); if ( err < 0 ) { if ( err == JET_errRecordNotFound ) err = JET_errDatabaseNotFound; goto Close; } CallJ( ErrDIRDelete( pfucb, fDIRVersion ), Close ); DIRClose( pfucb ); Call( ErrDBCloseDatabase( ppib, dbidSysDb, 0 ) ); Call( ErrDIRCommitTransaction( ppib ) ); return JET_errSuccess; Close: DIRClose( pfucb ); DbClose: CallS( ErrDBCloseDatabase( ppib, dbidSysDb, 0 ) ); HandleError: CallS( ErrDIRRollback( ppib ) ); return err; } LOCAL ERR ErrDBAddDatabase( PIB *ppib, CHAR *szDatabaseName, DBID dbid ) { ERR err = JET_errSuccess; FUCB *pfucb; KEY key; BYTE rgbKey[ JET_cbKeyMost ]; LINE line; DBID dbidSysDb; BYTE rgdba[sizeof(BYTE) + sizeof(BYTE) + JET_cbNameMost]; DBA *pdba; /* allocate space for DBA /**/ pdba = (DBA *)rgdba; /* add entry in DATABASES node of system database for created /* database /**/ CallR( ErrDIRBeginTransaction( ppib ) ); dbidSysDb = dbidSystemDatabase; #ifdef NJETNT Call( ErrDBOpenDatabase( ppib, rgtib[itibGlobal].szSysDbPath, &dbidSysDb, 0 ) ); #else Call( ErrDBOpenDatabase( ppib, szSysDbPath, &dbidSysDb, 0 ) ); #endif if ( FDBIDReadOnly( dbidSystemDatabase ) ) { err = JET_errFileAccessDenied; goto DbClose; } CallJ( ErrDIROpen( ppib, pfcbNil, dbidSysDb, &pfucb ), DbClose ); CallJ( ErrDIRSeekPath( pfucb, 1, pkeyDatabases, 0 ), Close ); /* normalize database name and set key /**/ SysNormText( szDatabaseName, strlen( szDatabaseName ), rgbKey, sizeof( rgbKey ), &key.cb ); key.pb = rgbKey; /* set dba with dbid, fLogOn and database name without NULL terminator /**/ pdba->bDbid = (BYTE)dbid; pdba->bLoggable = (BYTE)rgfmp[dbid].fLogOn; strncpy( (BYTE *)pdba->rgchDatabaseName, szDatabaseName, strlen( szDatabaseName ) ); line.pb = (BYTE *)pdba; line.cb = sizeof(BYTE) + sizeof(BYTE) + strlen( szDatabaseName); CallJ( ErrDIRInsert( pfucb, &line, &key, fDIRVersion ), Close ) DIRClose( pfucb ); Call( ErrDBCloseDatabase( ppib, dbidSysDb, 0 ) ); Call( ErrDIRCommitTransaction( ppib ) ); return JET_errSuccess; Close: DIRClose( pfucb ); DbClose: CallS( ErrDBCloseDatabase( ppib, dbidSysDb, 0 ) ); HandleError: CallS( ErrDIRRollback( ppib ) ); return err; } ERR ErrDBValidDatabaseFile( CHAR *szDatabaseName, ULONG *pulVersion, BOOL *pfLoggingCapable ) { ERR err = JET_errSuccess; UINT cb; HANDLE hf; PAGE *ppage; INT ibTag; INT cbTag; BYTE *pb; CallR( ErrSysOpenFile( szDatabaseName, &hf, 0L, fTrue, fFalse ) ); if ( ( ppage = ( PAGE * ) PvSysAllocAndCommit( cbPage ) ) == NULL ) { err = JET_errOutOfMemory; goto HandleError; } SysChgFilePtr( hf, 0, NULL, FILE_BEGIN, &cb ); Assert( cb == 0 ); err = ErrSysReadBlock( hf, (BYTE*)ppage, cbPage, &cb ); /* since file exists and we are unable to read data, /* it may not be a system.mdb /**/ if ( err == JET_errDiskIO ) err = JET_errDatabaseCorrupted; Call( err ); IbCbFromPtag(ibTag, cbTag, &ppage->rgtag[0]); if ( ibTag < (BYTE*)&ppage->rgtag[1] - (BYTE*)ppage || (BYTE*)ppage + ibTag + cbTag >= (BYTE*)(ppage + 1) ) { err = JET_errDatabaseCorrupted; goto HandleError; } /* at least FILES, OWNEXT, AVAILEXT /**/ pb = (BYTE *)ppage + ibTag; if ( !FNDVisibleSons( *pb ) || CbNDKey( pb ) != 0 || FNDNullSon( *pb ) ) { err = JET_errDatabaseCorrupted; goto HandleError; } /* check data length /**/ cb = cbTag - ( PbNDData( pb ) - pb ); if ( cb != sizeof(DBROOT) ) { err = JET_errDatabaseCorrupted; goto HandleError; } /* return database version /**/ *pulVersion = ((DBROOT *)PbNDData(pb))->ulVersion; *pfLoggingCapable = ((DBROOT *)PbNDData(pb))->usFlags & dbrootfLoggable; HandleError: if ( ppage != NULL ) SysFree( (VOID *)ppage ); (VOID)ErrSysCloseFile( hf ); return err; } /* ErrDABCloseAllDBs: Close all databases (except system database) opened by this thread /**/ ERR ErrDABCloseAllDBs( PIB *ppib ) { ERR err; while( ppib->pdabList != pdabNil ) { Assert( FUserOpenedDatabase( ppib, ppib->pdabList->dbid ) ); CallR( ErrIsamCloseDatabase( ( JET_VSESID ) ppib, (JET_VDBID) ppib->pdabList, 0 ) ); } return JET_errSuccess; }