/**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::openRflFile( FLMUINT uiFileNum) { RCODE rc = NE_SFLM_OK; char szRflPath[ F_PATH_MAX_SIZE]; char szBaseName[ F_FILENAME_SIZE]; FLMUINT uiBaseNameSize; SFLM_DB_HDR dbHdr; IF_FileHdl * pFileHdl = NULL; flmAssert( m_bSetupCalled); flmAssert( uiFileNum); flmAssert( !m_pFileHdl); // Read the database header to determine the version number if( !m_uiDbVersion) { if( RC_BAD( rc = gv_SFlmSysData.pFileSystem->openFile( m_szDbPath, FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT, &pFileHdl))) { goto Exit; } if( RC_BAD( rc = flmReadAndVerifyHdrInfo( NULL, pFileHdl, &dbHdr))) { goto Exit; } pFileHdl->Release(); pFileHdl = NULL; m_uiDbVersion = (FLMUINT)dbHdr.ui32DbVersion; } // Generate the log file name. if( RC_BAD( rc = rflGetDirAndPrefix( m_szDbPath, m_szRflDir, szRflPath))) { goto Exit; } uiBaseNameSize = sizeof( szBaseName); rflGetBaseFileName( uiFileNum, szBaseName, &uiBaseNameSize, NULL); gv_SFlmSysData.pFileSystem->pathAppend( szRflPath, szBaseName); // Open the file. if( RC_BAD( rc = gv_SFlmSysData.pFileSystem->openFile( szRflPath, FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT, &m_pFileHdl))) { goto Exit; } m_bOpen = TRUE; m_ui64Offset = 0; Exit: if( pFileHdl) { pFileHdl->Release(); } return( rc); }
/**************************************************************************** Desc: Returns disk space usage for the database ****************************************************************************/ RCODE XFLAPI F_Db::getDiskSpaceUsage( FLMUINT64 * pui64DataSize, FLMUINT64 * pui64RollbackSize, FLMUINT64 * pui64RflSize) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; FLMUINT uiEndAddress; FLMUINT uiLastFileNumber; FLMUINT64 ui64LastFileSize; char szTmpName [F_PATH_MAX_SIZE]; char szRflDir [F_PATH_MAX_SIZE]; IF_FileHdl * pFileHdl = NULL; IF_DirHdl * pDirHdl = NULL; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } // See if they want the database files sizes. if (pui64DataSize) { uiEndAddress = m_uiLogicalEOF; uiLastFileNumber = FSGetFileNumber( uiEndAddress); // Last file number better be in the proper range. flmAssert( uiLastFileNumber >= 1 && uiLastFileNumber <= MAX_DATA_BLOCK_FILE_NUMBER); // Get the actual size of the last file. if (RC_BAD( rc = m_pSFileHdl->getFileSize( uiLastFileNumber, &ui64LastFileSize))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { if (uiLastFileNumber > 1) { rc = NE_XFLM_OK; ui64LastFileSize = 0; } else { // Should always be a data file #1 RC_UNEXPECTED_ASSERT( rc); goto Exit; } } else { goto Exit; } } // One of two situations exists with respect to the last // file: 1) it has not been fully written out yet (blocks // are still cached, or 2) it has been written out and // extended beyond what the logical EOF shows. We want // the larger of these two possibilities. if (FSGetFileOffset( uiEndAddress) > ui64LastFileSize) { ui64LastFileSize = FSGetFileOffset( uiEndAddress); } if (uiLastFileNumber == 1) { // Only one file - use last file's size. *pui64DataSize = ui64LastFileSize; } else { // Size is the sum of full size for all files except the last one, // plus the calculated (or actual) size of the last one. (*pui64DataSize) = (FLMUINT64)(uiLastFileNumber - 1) * (FLMUINT64)m_pDatabase->m_uiMaxFileSize + ui64LastFileSize; } } // See if they want the rollback files sizes. if (pui64RollbackSize) { uiEndAddress = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RblEOF; uiLastFileNumber = FSGetFileNumber( uiEndAddress); // Last file number better be in the proper range. flmAssert( !uiLastFileNumber || (uiLastFileNumber >= FIRST_LOG_BLOCK_FILE_NUMBER && uiLastFileNumber <= MAX_LOG_BLOCK_FILE_NUMBER)); // Get the size of the last file number. if (RC_BAD( rc = m_pSFileHdl->getFileSize( uiLastFileNumber, &ui64LastFileSize))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { if (uiLastFileNumber) { rc = NE_XFLM_OK; ui64LastFileSize = 0; } else { // Should always have rollback file #0 RC_UNEXPECTED_ASSERT( rc); goto Exit; } } else { goto Exit; } } // If the EOF offset for the last file is greater than the // actual file size, use it instead of the actual file size. if (FSGetFileOffset( uiEndAddress) > ui64LastFileSize) { ui64LastFileSize = FSGetFileOffset( uiEndAddress); } // Special case handling here because rollback file numbers start with // zero and then skip to a file number that is one beyond the // highest data file number - so the calculation for file size needs // to account for this. if (!uiLastFileNumber) { *pui64RollbackSize = ui64LastFileSize; } else { FLMUINT uiFirstLogFileNum = FIRST_LOG_BLOCK_FILE_NUMBER; // Add full size of file zero plus a full size for every file // except the last one. (*pui64RollbackSize) = (FLMUINT64)(uiLastFileNumber - uiFirstLogFileNum + 1) * (FLMUINT64)m_pDatabase->m_uiMaxFileSize + ui64LastFileSize; } } // See if they want the roll-forward log file sizes. if (pui64RflSize) { char * pszDbFileName = m_pDatabase->m_pszDbPath; *pui64RflSize = 0; // Scan the RFL directory for // RFL files. The call below to rflGetDirAndPrefix is done // to get the prefix. It will not return the correct // RFL directory name, because we are passing in a NULL // RFL directory path (which may or may not be correct). // That's OK, because we get the RFL directory directly // from the F_Rfl object anyway. if (RC_BAD( rc = rflGetDirAndPrefix( pszDbFileName, NULL, szRflDir))) { goto Exit; } // We need to get the RFL directory from the F_Rfl object. m_pDatabase->lockMutex(); f_strcpy( szRflDir, m_pDatabase->m_pRfl->getRflDirPtr()); m_pDatabase->unlockMutex(); // See if the directory exists. If not, we are done. if (gv_XFlmSysData.pFileSystem->isDir( szRflDir)) { // Open the directory and scan for RFL files. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openDir( szRflDir, "*", &pDirHdl))) { goto Exit; } for (;;) { if (RC_BAD( rc = pDirHdl->next())) { if (rc == NE_FLM_IO_NO_MORE_FILES) { rc = NE_XFLM_OK; break; } else { goto Exit; } } pDirHdl->currentItemPath( szTmpName); // If the item looks like an RFL file name, get // its size. if (!pDirHdl->currentItemIsDir() && rflGetFileNum( szTmpName, &uiLastFileNumber)) { // Open the file and get its size. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( szTmpName, gv_XFlmSysData.uiFileOpenFlags, &pFileHdl))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; ui64LastFileSize = 0; } else { goto Exit; } } else { if (RC_BAD( rc = pFileHdl->size( &ui64LastFileSize))) { goto Exit; } } if (pFileHdl) { pFileHdl->Release(); pFileHdl = NULL; } (*pui64RflSize) += ui64LastFileSize; } } } } Exit: if (pFileHdl) { pFileHdl->Release(); } if (pDirHdl) { pDirHdl->Release(); } if (bStartedTrans) { abortTrans(); } return( rc); }
/******************************************************************************* Desc: Renames a database *******************************************************************************/ FLMEXP RCODE FLMAPI FlmDbRename( const char * pszDbName, const char * pszDataDir, const char * pszRflDir, const char * pszNewDbName, FLMBOOL bOverwriteDestOk, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; IF_FileHdl * pFileHdl = NULL; FLMUINT uiFileNumber; FILE_HDR FileHdr; LOG_HDR LogHdr; DBRenameInfo * pRenameList = NULL; FLMBOOL bFileFound; FLMBYTE * pucBuffer = NULL; FLMBYTE * pucLogHdr; char * pszOldName; char * pszNewName; char * pszOldDataName; char * pszNewDataName; char * pszFullNewName; char szOldBase[ F_FILENAME_SIZE]; char szNewBase[ F_FILENAME_SIZE]; char * pszExtOld; char * pszExtNew; char * pszDataExtOld; char * pszDataExtNew; // Cannot handle empty database name. flmAssert( pszDbName && *pszDbName); flmAssert( pszNewDbName && *pszNewDbName); // Allocate memory for a read buffer, the log header, and various // file names. if( RC_BAD( rc = f_allocAlignedBuffer( 2048 + LOG_HEADER_SIZE + F_PATH_MAX_SIZE * 5, &pucBuffer))) { goto Exit; } pucLogHdr = pucBuffer + 2048; pszOldName = (char *)(pucLogHdr + LOG_HEADER_SIZE); pszNewName = pszOldName + F_PATH_MAX_SIZE; pszOldDataName = pszNewName + F_PATH_MAX_SIZE; pszNewDataName = pszOldDataName + F_PATH_MAX_SIZE; pszFullNewName = pszNewDataName + F_PATH_MAX_SIZE; // There must be either no directory specified for the new name, or // it must be identical to the old directory. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pszDbName, pszOldName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pszNewDbName, pszNewName, szNewBase))) { goto Exit; } // Directories must be the same. if (*pszNewName && f_stricmp( pszOldName, pszNewName) != 0) { rc = RC_SET( FERR_INVALID_PARM); goto Exit; } f_strcpy( pszNewName, pszOldName); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszNewName, szNewBase))) { goto Exit; } f_strcpy( pszFullNewName, pszNewName); f_strcpy( pszOldName, pszDbName); if( pszDataDir && *pszDataDir) { f_strcpy( pszOldDataName, pszDataDir); f_strcpy( pszNewDataName, pszDataDir); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszOldDataName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszNewDataName, szNewBase))) { goto Exit; } } else { f_strcpy( pszNewDataName, pszNewName); f_strcpy( pszOldDataName, pszOldName); } // First make sure we have closed the databases and gotten rid of // them from our internal memory tables - in case they had been open. if( RC_BAD( rc = FlmConfig( FLM_CLOSE_FILE, (void *)pszDbName, (void *)pszDataDir))) { goto Exit; } if( RC_BAD( rc = FlmConfig( FLM_CLOSE_FILE, (void *)pszFullNewName, (void *)pszDataDir))) { goto Exit; } gv_FlmSysData.pFileHdlCache->closeUnusedFiles(); // Open the file so we can get the log header. if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszDbName, gv_FlmSysData.uiFileOpenFlags, &pFileHdl))) { goto Exit; } // Read the header to get the low and high RFL log // file numbers. if (RC_BAD( flmReadAndVerifyHdrInfo( NULL, pFileHdl, pucBuffer, &FileHdr, &LogHdr, pucLogHdr))) { goto Exit; } // Close the file. pFileHdl->Release(); pFileHdl = NULL; // Start renaming files, beginning with the main DB file. if( RC_BAD( rc = flmRenameFile( pszDbName, pszFullNewName, bOverwriteDestOk, FALSE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } // Find where the extension of the old and new database names are pszExtOld = pszOldName + f_strlen( pszOldName) - 1; pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName) - 1; while (pszExtOld != pszOldName && *pszExtOld != '.') { pszExtOld--; // Both the old db name and old data name have the same // base name, so we can decrement pszDataExtOld // at the same time we decrement pszExtOld. pszDataExtOld--; } if (*pszExtOld != '.') { pszExtOld = pszOldName + f_strlen( pszOldName); pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName); } pszExtNew = pszNewName + f_strlen( pszNewName) - 1; pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName) - 1; while (pszExtNew != pszOldName && *pszExtNew != '.') { pszExtNew--; // Both the new db name and new data name have the same // base name, so we can decrement pszDataExtNew // at the same time we decrement pszExtNew. pszDataExtNew--; } if (*pszExtNew != '.') { pszExtNew = pszNewName + f_strlen( pszNewName); pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName); } // Rename the .lck file, if any. This is necessary for UNIX. f_strcpy( pszExtOld, ".lck"); f_strcpy( pszExtNew, ".lck"); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } // Rename block (data) files. uiFileNumber = 1; for (;;) { F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszDataExtOld); F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszDataExtNew); if (RC_BAD( rc = flmRenameFile( pszOldDataName, pszNewDataName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_DATA_BLOCK_FILE_NUMBER( FileHdr.uiVersionNum)) { break; } uiFileNumber++; } // Rename rollback log files. uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER (FileHdr.uiVersionNum); for (;;) { F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszExtOld); F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszExtNew); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_LOG_BLOCK_FILE_NUMBER( FileHdr.uiVersionNum)) { break; } uiFileNumber++; } // Rename roll-forward log files. if (FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { // For pre-4.3 versions, only need to rename one RFL file. if (RC_BAD( rc = rflGetFileName( FileHdr.uiVersionNum, pszDbName, pszRflDir, 1, pszOldName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( FileHdr.uiVersionNum, pszFullNewName, pszRflDir, 1, pszNewName))) { goto Exit; } if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } } else { // For 4.3 and greater, rename the RFL directory. if (RC_BAD( rc = rflGetDirAndPrefix( FileHdr.uiVersionNum, pszDbName, pszRflDir, pszOldName, szOldBase))) { goto Exit; } if (RC_BAD( rc = rflGetDirAndPrefix( FileHdr.uiVersionNum, pszFullNewName, pszRflDir, pszNewName, szNewBase))) { goto Exit; } if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } } Exit: if( pFileHdl) { pFileHdl->Release(); } if( pucBuffer) { f_freeAlignedBuffer( &pucBuffer); } // Free the list of renamed files. while( pRenameList) { DBRenameInfo * pRenameFile; pRenameFile = pRenameList; pRenameList = pRenameList->pNext; // If we had an error of some sort, attempt to un-rename // the file that had been renamed. if (RC_BAD( rc)) { gv_FlmSysData.pFileSystem->renameFile( pRenameFile->Info.szDstFileName, pRenameFile->Info.szSrcFileName); } f_free( &pRenameFile); } return( rc); }
/**************************************************************************** Desc: Renames all files of a database ****************************************************************************/ RCODE F_DbSystem::dbRename( const char * pszDbName, // [IN] Database to be renamed. const char * pszDataDir, // [IN] Directory for data files. const char * pszRflDir, // [IN] RFL directory of database. NULL can be // passed to indicate that the log files are located // in the same directory as the other database files. const char * pszNewDbName, // [IN] New name to be given to the database. May be // the short name only, or include a directory. If it // includes a directory, it must be the same directory // as the directory given in pszDbName. FLMBOOL bOverwriteDestOk, // [IN] Ok to overwrite existing file with rename? IF_DbRenameStatus * ifpStatus) // [IN] Status callback function. { RCODE rc = NE_SFLM_OK; FLMUINT uiFileNumber; DBRenameInfo * pRenameList = NULL; FLMBOOL bFileFound; char * pszOldName = NULL; char * pszNewName; char * pszOldDataName; char * pszNewDataName; char * pszFullNewName; char szOldBase [F_FILENAME_SIZE]; char szNewBase [F_FILENAME_SIZE]; char * pszExtOld; char * pszExtNew; char * pszDataExtOld; char * pszDataExtNew; // Cannot handle empty database name. flmAssert( pszDbName && *pszDbName); flmAssert( pszNewDbName && *pszNewDbName); // Allocate memory for a read buffer, the log header, and various // file names. if (RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 5, &pszOldName))) { goto Exit; } pszNewName = pszOldName + F_PATH_MAX_SIZE; pszOldDataName = pszNewName + F_PATH_MAX_SIZE; pszNewDataName = pszOldDataName + F_PATH_MAX_SIZE; pszFullNewName = pszNewDataName + F_PATH_MAX_SIZE; // There must be either no directory specified for the new name, or // it must be identical to the old directory. if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathReduce( pszDbName, pszOldName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathReduce( pszNewDbName, pszNewName, szNewBase))) { goto Exit; } // Directories must be the same. if (*pszNewName && f_stricmp( pszOldName, pszNewName) != 0) { rc = RC_SET( NE_SFLM_INVALID_PARM); goto Exit; } f_strcpy( pszNewName, pszOldName); if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathAppend( pszNewName, szNewBase))) { goto Exit; } f_strcpy( pszFullNewName, pszNewName); f_strcpy( pszOldName, pszDbName); if (pszDataDir && *pszDataDir) { f_strcpy( pszOldDataName, pszDataDir); f_strcpy( pszNewDataName, pszDataDir); if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathAppend( pszOldDataName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathAppend( pszNewDataName, szNewBase))) { goto Exit; } } else { f_strcpy( pszNewDataName, pszNewName); f_strcpy( pszOldDataName, pszOldName); } // First make sure we have closed the databases and gotten rid of // them from our internal memory tables - in case they had been open. if (RC_BAD( rc = checkDatabaseClosed( pszDbName, pszDataDir))) { goto Exit; } if (RC_BAD( rc = checkDatabaseClosed( pszFullNewName, pszDataDir))) { goto Exit; } // Start renaming files, beginning with the main DB file. if (RC_BAD( rc = flmRenameFile( pszDbName, pszFullNewName, bOverwriteDestOk, FALSE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } // Find where the extension of the old and new database names are pszExtOld = pszOldName + f_strlen( pszOldName) - 1; pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName) - 1; while (pszExtOld != pszOldName && *pszExtOld != '.') { pszExtOld--; // Both the old db name and old data name have the same // base name, so we can decrement pszDataExtOld // at the same time we decrement pszExtOld. pszDataExtOld--; } if (*pszExtOld != '.') { pszExtOld = pszOldName + f_strlen( pszOldName); pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName); } pszExtNew = pszNewName + f_strlen( pszNewName) - 1; pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName) - 1; while (pszExtNew != pszOldName && *pszExtNew != '.') { pszExtNew--; // Both the new db name and new data name have the same // base name, so we can decrement pszDataExtNew // at the same time we decrement pszExtNew. pszDataExtNew--; } if (*pszExtNew != '.') { pszExtNew = pszNewName + f_strlen( pszNewName); pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName); } // Rename the .lck file, if any. This is necessary for UNIX. f_strcpy( pszExtOld, ".lck"); f_strcpy( pszExtNew, ".lck"); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } // Rename block (data) files. uiFileNumber = 1; for (;;) { F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszDataExtOld); F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszDataExtNew); if (RC_BAD( rc = flmRenameFile( pszOldDataName, pszNewDataName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_DATA_BLOCK_FILE_NUMBER) { break; } uiFileNumber++; } // Rename rollback log files. uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; for (;;) { F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszExtOld); F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszExtNew); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_LOG_BLOCK_FILE_NUMBER) { break; } uiFileNumber++; } // Rename the RFL directory. if (RC_BAD( rc = rflGetDirAndPrefix( pszDbName, pszRflDir, pszOldName))) { goto Exit; } if (RC_BAD( rc = rflGetDirAndPrefix( pszFullNewName, pszRflDir, pszNewName))) { goto Exit; } if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } Exit: if (pszOldName) { f_free( &pszOldName); } // Free the list of renamed files. while (pRenameList) { DBRenameInfo * pRenameFile; pRenameFile = pRenameList; pRenameList = pRenameList->pNext; // If we had an error of some sort, attempt to un-rename // the file that had been renamed. if (RC_BAD( rc)) { gv_SFlmSysData.pFileSystem->renameFile( pRenameFile->Info.szDstFileName, pRenameFile->Info.szSrcFileName); } f_free( &pRenameFile); } return( rc); }
/**************************************************************************** Desc: Copy a database's files, including roll-forward log files. *****************************************************************************/ FSTATIC RCODE flmCopyDb( FLMUINT uiDbVersion, const char * pszSrcDbName, const char * pszSrcDataDir, const char * pszSrcRflDir, const char * pszDestDbName, const char * pszDestDataDir, const char * pszDestRflDir, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; DB_COPY_INFO DbCopyInfo; F_SuperFileHdl * pSrcSFileHdl = NULL; F_SuperFileHdl * pDestSFileHdl = NULL; F_SuperFileClient * pSrcSFileClient = NULL; F_SuperFileClient * pDestSFileClient = NULL; FLMUINT uiFileNumber; FLMUINT uiHighFileNumber; FLMUINT uiHighLogFileNumber; FLMUINT64 ui64FileSize; FFILE * pFile = NULL; FLMBOOL bMutexLocked = FALSE; IF_FileHdl * pLockFileHdl = NULL; IF_FileHdl * pTmpFileHdl = NULL; IF_DirHdl * pDirHdl = NULL; FLMBOOL bFileLocked = FALSE; FLMBOOL bWriteLocked = FALSE; IF_LockObject * pWriteLockObj = NULL; IF_LockObject * pFileLockObj = NULL; COPIED_NAME * pCopiedList = NULL; FLMBOOL bUsedFFile = FALSE; FLMBYTE * pucInMemLogHdr = NULL; eLockType currLockType; FLMUINT uiLockThreadId; char * pszActualSrcRflPath = NULL; char * pszSrcPrefix = NULL; char * pszActualDestRflPath = NULL; char * pszDestPrefix = NULL; FLMBOOL bCreatedDestRflDir = FALSE; F_SEM hWaitSem = F_SEM_NULL; f_memset( &DbCopyInfo, 0, sizeof( DbCopyInfo)); // Should not do anything if the source and destination names // are the same. if (f_stricmp( pszSrcDbName, pszDestDbName) == 0) { goto Exit; } // Allocate a semaphore if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } // Allocate memory for paths we don't want to push onto the stack. if (RC_BAD( rc = f_calloc( (F_PATH_MAX_SIZE + F_FILENAME_SIZE) * 2, &pszActualSrcRflPath))) { goto Exit; } pszSrcPrefix = &pszActualSrcRflPath[ F_PATH_MAX_SIZE]; pszActualDestRflPath = &pszSrcPrefix[ F_FILENAME_SIZE]; pszDestPrefix = &pszActualDestRflPath[ F_PATH_MAX_SIZE]; // Set up the super file object for the source database. // Must at least open the control file. if( (pSrcSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSrcSFileClient->setup( pszSrcDbName, pszSrcDataDir, uiDbVersion))) { goto Exit; } if( (pSrcSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSrcSFileHdl->setup( pSrcSFileClient, gv_FlmSysData.pFileHdlCache, gv_FlmSysData.uiFileOpenFlags, 0))) { goto Exit; } // Lock the destination database, if not already locked. // This is so we can overwrite it without necessarily // deleting it. May unlock and re-lock the global mutex. f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; if (RC_BAD( rc = flmFindFile( pszDestDbName, pszDestDataDir, &pFile))) { goto Exit; } // If we didn't find an FFILE structure, get an // exclusive lock on the file. if (!pFile) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // Attempt to get an exclusive lock on the file. if (RC_BAD( rc = flmCreateLckFile( pszDestDbName, &pLockFileHdl))) { goto Exit; } } else { // The call to flmVerifyFileUse will wait if the file is in // the process of being opened by another thread. if (RC_BAD( rc = flmVerifyFileUse( gv_FlmSysData.hShareMutex, &pFile))) { goto Exit; } // Increment the use count on the FFILE so it will not // disappear while we are copying the file. if (++pFile->uiUseCount == 1) { flmUnlinkFileFromNUList( pFile); } bUsedFFile = TRUE; f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; pucInMemLogHdr = &pFile->ucLastCommittedLogHdr [0]; // Lock the destination file object and transaction // object, if not already locked. pFile->pFileLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL); if (currLockType != FLM_LOCK_EXCLUSIVE || uiLockThreadId != f_threadId()) { pFileLockObj = pFile->pFileLockObj; pFileLockObj->AddRef(); if (RC_BAD( rc = pFileLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bFileLocked = TRUE; } // Lock the write object, if not already locked pFile->pWriteLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL); if( currLockType != FLM_LOCK_EXCLUSIVE || uiLockThreadId != f_threadId()) { pWriteLockObj = pFile->pWriteLockObj; pWriteLockObj->AddRef(); // Only contention here is with the checkpoint thread - wait // forever until the checkpoint thread gives it up. if( RC_BAD( rc = pWriteLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bWriteLocked = TRUE; } } // Set up the super file object for the destination database. if( (pDestSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pDestSFileClient->setup( pszDestDbName, pszDestDataDir, uiDbVersion))) { goto Exit; } if( (pDestSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pDestSFileHdl->setup( pDestSFileClient, gv_FlmSysData.pFileHdlCache, gv_FlmSysData.uiFileOpenFlags, gv_FlmSysData.uiFileCreateFlags))) { goto Exit; } // See how many files we have and calculate the total size. uiHighFileNumber = 0; for (;;) { if( RC_BAD( rc = pSrcSFileHdl->getFileSize( uiHighFileNumber, &ui64FileSize)) || !ui64FileSize) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH || !ui64FileSize) { // If the control file doesn't exist, we will return // path not found. if (!uiHighFileNumber) { goto Exit; } uiHighFileNumber--; rc = FERR_OK; break; } goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; if (uiHighFileNumber == MAX_DATA_BLOCK_FILE_NUMBER( uiDbVersion)) { break; } uiHighFileNumber++; } // See how many rollback log files we have, and calculate // their total size. uiHighLogFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion); for (;;) { if ((RC_BAD( rc = pSrcSFileHdl->getFileSize( uiHighLogFileNumber, &ui64FileSize))) || !ui64FileSize) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH || !ui64FileSize) { if (uiHighLogFileNumber == FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion)) { uiHighLogFileNumber = 0; } else { uiHighLogFileNumber--; } rc = FERR_OK; break; } goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; if (uiHighLogFileNumber == MAX_LOG_BLOCK_FILE_NUMBER( uiDbVersion)) { break; } uiHighLogFileNumber++; } // Get the sizes of the roll-forward log files if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3) { // For pre-4.3 versions, only need to copy one RFL file. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName, pszSrcRflDir, 1, pszActualSrcRflPath))) { goto Exit; } if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszActualSrcRflPath, gv_FlmSysData.uiFileOpenFlags, &pTmpFileHdl))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } else { goto Exit; } } else { if (RC_BAD( rc = pTmpFileHdl->size( &ui64FileSize))) { goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; pTmpFileHdl->Release(); pTmpFileHdl = NULL; } } else { if( RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszSrcDbName, pszSrcRflDir, pszActualSrcRflPath, pszSrcPrefix))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir( pszActualSrcRflPath, (char *)"*", &pDirHdl))) { goto Exit; } for (;;) { if( RC_BAD( rc = pDirHdl->next())) { if (rc == FERR_IO_NO_MORE_FILES) { rc = FERR_OK; break; } else { goto Exit; } } // If the current file is an RFL file, increment ui64BytesToCopy if( rflGetFileNum( uiDbVersion, pszSrcPrefix, pDirHdl->currentItemName(), &uiFileNumber)) { DbCopyInfo.ui64BytesToCopy += pDirHdl->currentItemSize(); } } pDirHdl->Release(); pDirHdl = NULL; } // Close all file handles in the source and destination pSrcSFileHdl->releaseFiles(); pDestSFileHdl->releaseFiles(); // Copy the database files. for (uiFileNumber = 0; uiFileNumber <= uiHighFileNumber; uiFileNumber++) { // Get the source file path and destination file path. if( RC_BAD( rc = pSrcSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if( RC_BAD( rc = pDestSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } // For the control file, don't copy first 2K - it will be set up // to show maintenance in progress. Then the first 2K will be copied // later. if (!uiFileNumber) { DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 2048, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, pucInMemLogHdr, TRUE, fnStatusCallback, UserData))) { goto Exit; } } else { DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } } // Copy the additional rollback log files, if any. for (uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion); uiFileNumber <= uiHighLogFileNumber; uiFileNumber++) { // Get the source file path and destination file path. if (RC_BAD( rc = pSrcSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = pDestSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } // Copy the RFL files if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3) { // Get the source file path and the destination file path. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName, pszSrcRflDir, 1, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszDestDbName, pszDestRflDir, 1, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } else { // Create the destination RFL directory, if needed. The purpose of this // code is two-fold: 1) We want to keep track of the fact that we tried // to create the destination RFL directory so we can try to remove it // if the copy fails; 2) If the destination RFL directory path specifies // a directory with existing files, we want to remove them. if( RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszDestDbName, pszDestRflDir, pszActualDestRflPath, pszDestPrefix))) { goto Exit; } if( RC_OK( gv_FlmSysData.pFileSystem->doesFileExist( pszActualDestRflPath))) { if( gv_FlmSysData.pFileSystem->isDir( pszActualDestRflPath)) { // Remove the existing directory and all files, etc. (void)gv_FlmSysData.pFileSystem->removeDir( pszActualDestRflPath, TRUE); } else { (void)gv_FlmSysData.pFileSystem->deleteFile( pszActualDestRflPath); } } // Try to create the destination RFL directory. This might fail if // another process was accessing the directory for some reason // (i.e., from a command prompt), when we tried to remove it above. // We really don't care if the call to CreateDir is sucessful, because // when we try to create the destination files (below), the FLAIM file // file system code will try to create any necessary directories. (void)gv_FlmSysData.pFileSystem->createDir( pszActualDestRflPath); bCreatedDestRflDir = TRUE; // Copy the RFL files. NOTE: We need to copy all of the RFL files // in the source RFL directory so that they will be available // when performing a database restore operation. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir( pszActualSrcRflPath, (char *)"*", &pDirHdl))) { goto Exit; } for (;;) { if( RC_BAD( rc = pDirHdl->next())) { if (rc == FERR_IO_NO_MORE_FILES) { rc = FERR_OK; break; } else { goto Exit; } } // If the current file is an RFL file, copy it to the destination if( rflGetFileNum( uiDbVersion, pszSrcPrefix, pDirHdl->currentItemName(), &uiFileNumber)) { // Get the source file path and the destination file path. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName, pszSrcRflDir, uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszDestDbName, pszDestRflDir, uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } } pDirHdl->Release(); pDirHdl = NULL; } // Do one final copy on the control file to copy just the first 2K if (RC_BAD( rc = pSrcSFileHdl->getFilePath( 0, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = pDestSFileHdl->getFilePath( 0, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = FALSE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 2048, &DbCopyInfo, NULL, pucInMemLogHdr, FALSE, fnStatusCallback, UserData))) { goto Exit; } Exit: if (bUsedFFile) { if (!bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; } if (!(--pFile->uiUseCount)) { flmLinkFileToNUList( pFile); } } if (bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; } if (bWriteLocked) { if( RC_BAD( rc = pWriteLockObj->unlock())) { goto Exit; } bWriteLocked = FALSE; } if (bFileLocked) { RCODE rc3; if (RC_BAD( rc3 = pFileLockObj->unlock())) { if (RC_OK( rc)) rc = rc3; } bFileLocked = FALSE; } if (pWriteLockObj) { pWriteLockObj->Release(); pWriteLockObj = NULL; } if (pFileLockObj) { pFileLockObj->Release(); pFileLockObj = NULL; } if (pLockFileHdl) { pLockFileHdl->Release(); pLockFileHdl = NULL; } if( pTmpFileHdl) { pTmpFileHdl->Release(); } if( pDirHdl) { pDirHdl->Release(); } // Free all the names of files that were copied. // If the copy didn't finish, try to delete any files // that were copied. while (pCopiedList) { COPIED_NAME * pNext = pCopiedList->pNext; // If the overall copy failed, delete the copied file. if (RC_BAD( rc)) { (void)gv_FlmSysData.pFileSystem->deleteFile( pCopiedList->szPath); } f_free( &pCopiedList); pCopiedList = pNext; } if( RC_BAD( rc) && bCreatedDestRflDir) { (void)gv_FlmSysData.pFileSystem->removeDir( pszActualDestRflPath); } if( pszActualSrcRflPath) { f_free( &pszActualSrcRflPath); } if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } if( pSrcSFileHdl) { pSrcSFileHdl->Release(); } if( pSrcSFileClient) { pSrcSFileClient->Release(); } if( pDestSFileHdl) { pDestSFileHdl->Release(); } if( pDestSFileClient) { pDestSFileClient->Release(); } return( rc); }