/**************************************************************************** Desc: Opens an existing 64-bit file ****************************************************************************/ RCODE F_MultiFileHdl::openFile( const char * pszPath) { RCODE rc = NE_FLM_OK; IF_FileSystem * pFileSystem = f_getFileSysPtr(); IF_DirHdl * pDir = NULL; FLMUINT uiTmp; FLMUINT uiHighFileNum = 0; FLMUINT64 ui64HighOffset = 0; if( m_bOpen) { rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); goto Exit; } if( RC_BAD( pFileSystem->doesFileExist( pszPath)) || !pFileSystem->isDir( pszPath)) { rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND); goto Exit; } f_strcpy( m_szPath, pszPath); // Create the lock file if( RC_BAD( rc = createLockFile( m_szPath))) { goto Exit; } // Need to determine the current EOF if( RC_BAD( rc = pFileSystem->openDir( m_szPath, (char *)"*.64", &pDir))) { goto Exit; } // Find all data files to determine the EOF for( rc = pDir->next(); !RC_BAD( rc); rc = pDir->next()) { if( RC_OK( getFileNum( pDir->currentItemName(), &uiTmp))) { if( uiTmp >= uiHighFileNum) { uiHighFileNum = uiTmp; ui64HighOffset = pDir->currentItemSize(); } } } rc = NE_FLM_OK; m_ui64EOF = (((FLMUINT64)uiHighFileNum) * m_uiMaxFileSize) + ui64HighOffset; m_bOpen = TRUE; Exit: if( pDir) { pDir->Release(); } // Release the lock file if( RC_BAD( rc)) { releaseLockFile( m_szPath, FALSE); } 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); }