示例#1
0
/****************************************************************************
Desc: This routine obtains exclusive access to a database by creating
		a .lck file.  FLAIM holds the .lck file open as long as the database
		is open.  When the database is finally closed, it deletes the .lck
		file.  This is only used for 3.x databases.
****************************************************************************/
RCODE F_Database::getExclAccess(
	const char *	pszFilePath)
{
	RCODE				rc = NE_XFLM_OK;
	FLMBOOL			bNotifyWaiters = FALSE;
	FLMBOOL			bMutexLocked = FALSE;
	F_SEM				hWaitSem = F_SEM_NULL;

	// If m_pLockFileHdl is non-NULL, it means that we currently
	// have the database locked with a lock file.  There is no need to make
	// this test inside a mutex lock, because the lock file handle can only
	// be set to NULL when the use count goes to zero, meaning that the thread
	// that sets it to NULL will be the only thread accessing it.

	// However, it is possible that two or more threads will simultaneously
	// test m_pLockFileHdl and discover that it is NULL.  In that case,
	// we allow one thread to proceed and attempt to get a lock on the database
	// while the other threads wait to be notified of the results of the
	// attempt to lock the database.

	if (m_pLockFileHdl)
	{
		goto Exit;
	}

	lockMutex();
	bMutexLocked = TRUE;

	if (m_bBeingLocked)
	{
		// If the database is in the process of being locked by another
		// thread, wait for the lock to complete.  NOTE: f_notifyWait will
		// re-lock the mutex before returning.
		
		if( RC_BAD( rc = f_semCreate( &hWaitSem)))
		{
			goto Exit;
		}

		rc = f_notifyWait( m_hMutex, hWaitSem, NULL, &m_pLockNotifies);
		goto Exit;
	}

	// No other thread was attempting to lock the database, so
	// set this thread up to make the attempt.  Other threads
	// coming in at this point will be required to wait and
	// be notified of the results.

	m_bBeingLocked = TRUE;
	bNotifyWaiters = TRUE;
	unlockMutex();
	bMutexLocked = FALSE;
	if (RC_BAD( rc = flmCreateLckFile( pszFilePath, &m_pLockFileHdl)))
	{
		goto Exit;
	}

Exit:

	if (bNotifyWaiters)
	{
		F_NOTIFY_LIST_ITEM *		pNotify;
		F_SEM							hSem;

		// Notify any thread waiting on the lock what its status is.

		if( !bMutexLocked)
		{
			lockMutex();
			bMutexLocked = TRUE;
		}

		pNotify = m_pLockNotifies;
		while (pNotify)
		{
			*(pNotify->pRc) = rc;
			hSem = pNotify->hSem;
			pNotify = pNotify->pNext;
			f_semSignal( hSem);
		}

		m_bBeingLocked = FALSE;
		m_pLockNotifies = NULL;
		unlockMutex();
		bMutexLocked = FALSE;
	}

	if( bMutexLocked)
	{
		unlockMutex();
	}
	
	if( hWaitSem != F_SEM_NULL)
	{
		f_semDestroy( &hWaitSem);
	}

	return( rc);
}
示例#2
0
/****************************************************************************
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);
}