//------------------------------------------------------------------------- // Desc: Database format conversion routines. // Tabs: 3 // // Copyright (c) 1999-2001,2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program 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 this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsconvrt.cpp 12320 2006-01-19 15:53:51 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE FSConvertNonLeafTree( FDB * pDb, LFILE * pLFile, BTSK * pOldStack, BTSK * pOldStackBase, FLMUINT uiNewVersion, STATUS_HOOK fnStatusCallback, void * UserData, DB_UPGRADE_INFO * pDbConvertInfo); FSTATIC void FSBuildNonLeafDataElement( BTSK * pStack, FLMBYTE * pElement, FLMUINT * puiElmLen, FLMUINT uiNewElmOvhd, FLMBYTE ** ppKey); /*************************************************************************** Desc: File system conversions from one version to another. *****************************************************************************/ RCODE FSVersionConversion40( FDB * pDb, FLMUINT uiNewVersion, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; FFILE * pFile = pDb->pFile; LFILE * pLFileTbl; FLMUINT uiPos, uiTblSize; FLMUINT uiCurrentVersion; BTSK * pStack; BTSK stackBuf[ BH_MAX_LEVELS ]; FLMBYTE pKeyBuf[ DIN_KEY_SIZ + 4 ]; FLMBYTE pDrnKey[ DIN_KEY_SIZ]; DB_UPGRADE_INFO DbConvertInfo; f_memset( &DbConvertInfo, 0, sizeof( DB_UPGRADE_INFO)); // Supported conversions... uiCurrentVersion = pFile->FileHdr.uiVersionNum; // Loop through all of the data blocks in the lfile. pLFileTbl = pDb->pDict->pLFileTbl; uiTblSize = pDb->pDict->uiLFileCnt; FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); for( uiPos = 0; uiPos < uiTblSize; uiPos++) { LFILE * pLFile; pLFile = &pLFileTbl [uiPos]; if( pLFile->uiLfType == LF_CONTAINER) { if( fnStatusCallback) { DbConvertInfo.uiLastDrn = 0; if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &DbConvertInfo.uiLastDrn))) goto Exit; } // Set up the stack FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); pStack = stackBuf; pStack->pKeyBuf = pKeyBuf; f_UINT32ToBigEndian( 0, pDrnKey); if( RC_BAD(rc = FSBtSearch( pDb, pLFile, &pStack, pDrnKey, DIN_KEY_SIZ,0))) goto Exit; // If pStack isn't at stackBuf[] we have a b-tree of 2 or more levels. // VISIT: the change may not be necessary - may be new version. // Look at the root block type. if( pStack != stackBuf) { DbConvertInfo.uiContainer = pLFile->uiLfNum; if( RC_BAD( rc = FSConvertNonLeafTree( pDb, pLFile, pStack - 1, stackBuf, uiNewVersion, fnStatusCallback, UserData, &DbConvertInfo))) goto Exit; } FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); } } // Loop through all of the data b-trees and look for non-leaf blocks. Exit: FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); return( rc); } /*************************************************************************** Desc: Convert the non-leaf data blocks from one version to another version. pOldStack points to the level 1 non-leaf data blocks. Read all of the elements and build a new non-leaf b-tree and make this the new tree while freeing up the old non-leaf blocks. *****************************************************************************/ FSTATIC RCODE FSConvertNonLeafTree( FDB * pDb, LFILE * pLFile, BTSK * pOldStack, BTSK * pOldStackBase, FLMUINT uiNewVersion, STATUS_HOOK fnStatusCallback, void * UserData, DB_UPGRADE_INFO * pDbConvertInfo) { RCODE rc = FERR_OK; BTSK stackBuf[ BH_MAX_LEVELS ]; BTSK * pStack; FLMBYTE pKeyBuf[ DIN_KEY_SIZ + 4 ]; FLMBYTE pDrnKey[ DIN_KEY_SIZ]; FLMBYTE pElement[ DIN_KEY_SIZ + 16]; // Enough bytes for either format SCACHE * pTempSCache; FLMUINT uiBlkAddr; FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); f_UINT32ToBigEndian( 0, pDrnKey); /* Free the b-tree blocks that are above this lowest non-leaf level. This will reuse the blocks instead of allocating from the avail list or end of file. */ pOldStack->uiFlags &= ~FULL_STACK; for( ; pOldStackBase != pOldStack; pOldStackBase++ ) { uiBlkAddr = pOldStackBase->uiBlkAddr; // Release block so that caller doesn't release an avail block. FSReleaseBlock( pOldStackBase, FALSE); while( uiBlkAddr != BT_END) { if( RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiBlkAddr, NULL, &pTempSCache))) goto Exit; uiBlkAddr = FB2UD( &pTempSCache->pucBlk[ BH_NEXT_BLK ]); if( RC_BAD( FSBlockFree( pDb, pTempSCache))) goto Exit; } } // Allocate a new root block in the new NON-LEAF format. pStack = stackBuf; pStack->pKeyBuf = pKeyBuf; pLFile->uiRootBlk = BT_END; if( RC_BAD( flmLFileWrite( pDb, pLFile))) { goto Exit; } if( RC_BAD( rc = flmLFileInit( pDb, pLFile))) { goto Exit; } if( RC_BAD(rc = FSGetBlock( pDb, pLFile, pLFile->uiRootBlk, pStack))) { goto Exit; } if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } // Remove both leading elements in the block and setup as a non-leaf block. UW2FBA( BH_OVHD, &pStack->pBlk[ BH_BLK_END]); if( uiNewVersion >= FLM_FILE_FORMAT_VER_4_0) { pStack->pBlk[ BH_TYPE ] = BHT_NON_LEAF_DATA + BHT_ROOT_BLK; } else { pStack->pBlk[ BH_TYPE ] = BHT_NON_LEAF + BHT_ROOT_BLK; } pStack->pBlk[ BH_LEVEL ] = 1; pStack->uiKeyBufSize = DIN_KEY_SIZ; FSBlkToStack( pStack); pStack->uiFlags = FULL_STACK; // Build the key for the first element in the old stack. if( RC_BAD( rc = FSBtScanTo( pOldStack, NULL, 0, 0))) { goto Exit; } // Start inserting from pOldStack to pStack until pOldStack is done. for(;;) { FLMUINT uiElmLen; FLMBYTE * pKey; FSBuildNonLeafDataElement( pOldStack, pElement, &uiElmLen, pStack->uiElmOvhd, &pKey); if( uiNewVersion <= FLM_FILE_FORMAT_VER_3_02) { flmAssert( pStack->uiCurElm == pStack->uiBlkEnd); if( pStack != stackBuf) { flmAssert( stackBuf[0].uiCurElm + 6 == stackBuf[0].uiBlkEnd); } pStack->uiCurElm = pStack->uiBlkEnd; // Call scanto to place the last element's key in keybuf. if( RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0))) goto Exit; if( pStack->uiBlkEnd > BH_OVHD) { if( uiElmLen > BNE_KEY_START) { pStack->uiPrevElmPKC = FSElmComparePKC( pKeyBuf, DIN_KEY_SIZ, pKey, DIN_KEY_SIZ); } } } if( RC_BAD( rc = FSBtInsert( pDb, pLFile, &pStack, pElement, uiElmLen))) goto Exit; // Go to next element so we always insert at the end. (void) FSBtNextElm( pDb, pLFile, pStack ); uiBlkAddr = pOldStack->uiBlkAddr; if( RC_BAD( rc = FSBtNextElm( pDb, pLFile, pOldStack ))) { // All done? Free the last block in the old b-tree. if( rc == FERR_BT_END_OF_DATA) { if( RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiBlkAddr, NULL, &pTempSCache))) goto Exit; // No need to release pTempSCache. FSBlockFree does it. if( RC_BAD( FSBlockFree( pDb, pTempSCache))) goto Exit; rc = FERR_OK; break; } goto Exit; } // Did next element position to a new block? if( uiBlkAddr != pOldStack->uiBlkAddr) { // Do callback to report progress. if (fnStatusCallback) { pDbConvertInfo->uiDrn = f_bigEndianToUINT32( pOldStack->pKeyBuf); if (RC_BAD( rc = (*fnStatusCallback)( FLM_DB_UPGRADE_STATUS, (void *) pDbConvertInfo, (void *)0, UserData))) { goto Exit; } } // Free the previous block back into the avail list. if( RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiBlkAddr, NULL, &pTempSCache))) goto Exit; if( RC_BAD( FSBlockFree( pDb, pTempSCache))) goto Exit; // No need to release pTempSCache. FSBlockFree does it. } } Exit: FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); return( rc); } /*************************************************************************** Desc: Build a non-leaf data element from the current stack position. *****************************************************************************/ FSTATIC void FSBuildNonLeafDataElement( BTSK * pStack, FLMBYTE * pElement, FLMUINT * puiElmLen, FLMUINT uiNewElmOvhd, FLMBYTE ** ppKey) { FLMUINT uiElmLen; // Grab the key from the key buffer, if( uiNewElmOvhd == BNE_DATA_OVHD) // New fixed length 4.0 format. { FLMBYTE * pOldElm = CURRENT_ELM( pStack); uiElmLen = BNE_DATA_OVHD; *ppKey = pElement; // Check for last element marker. if( pOldElm[ BBE_PKC ] == 0 && pOldElm[ BBE_KL] == 0) { f_UINT32ToBigEndian( (FLMUINT32)DRN_LAST_MARKER, pElement); } else { f_memcpy( pElement, pStack->pKeyBuf, DIN_KEY_SIZ); } } else // Old variable length 3.x format. { pElement [ BBE_PKC ] = 0; // Set PKC, DOMAIN to zero if( FB2UD( pStack->pKeyBuf) == DRN_LAST_MARKER) { pElement [ BBE_KL ] = 0; uiElmLen = BNE_KEY_START; } else { pElement [ BBE_PKC ] = 0; // Set PKC, DOMAIN to zero pElement [ BBE_KL ] = DIN_KEY_SIZ; // sizeof( FLMUINT32) or 4 uiElmLen = BNE_KEY_START + DIN_KEY_SIZ; f_memcpy( pElement + BNE_KEY_START, pStack->pKeyBuf, DIN_KEY_SIZ); } *ppKey = pElement + BNE_KEY_START; } // Set the child block address. FSSetChildBlkAddr( pElement, FSChildBlkAddr( pStack), uiNewElmOvhd); *puiElmLen = uiElmLen; return; }