/****************************************************************************
Desc:
****************************************************************************/
RCODE F_IOBuffer::setupBuffer(
	FLMUINT				uiBufferSize,
	F_IOBufferMgr *	pBufferMgr)
{
	RCODE					rc = NE_FLM_OK;

	if( RC_BAD( rc = f_allocAlignedBuffer( uiBufferSize, 
		(void **)&m_pucBuffer)))
	{
		goto Exit;
	}
	
	m_uiBufferSize = uiBufferSize;
	m_pBufferMgr = pBufferMgr;

Exit:

	return( rc);
}
Example #2
0
/****************************************************************************
Desc: This routine sets up a new F_Database object, allocating member
		variables, linking into lists, etc.
		NOTE: This routine assumes that the global mutex has already
		been locked. It may unlock it temporarily if there is an error,
		but will always relock it before exiting.
****************************************************************************/
RCODE F_Database::setupDatabase(
	const char *		pszDbPath,
	const char *		pszDataDir)
{
	RCODE				rc = NE_XFLM_OK;
	FLMUINT			uiAllocLen;
	FLMUINT			uiDbNameLen;
	FLMUINT			uiDirNameLen;
	char				szDbPathStr[ F_PATH_MAX_SIZE];
	char				szDataDirStr[ F_PATH_MAX_SIZE];

	if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathToStorageString( 
		pszDbPath, szDbPathStr)))
	{
		goto Exit;
	}
	uiDbNameLen = f_strlen( szDbPathStr) + 1;

	if( pszDataDir && *pszDataDir)
	{
		if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathToStorageString( 
			pszDataDir, szDataDirStr)))
		{
			goto Exit;
		}
		uiDirNameLen = f_strlen( szDataDirStr) + 1;

	}
	else
	{
		szDataDirStr[0] = 0;
		uiDirNameLen = 0;
	}

	
	if (RC_BAD( rc = f_mutexCreate( &m_hMutex)))
	{
		goto Exit;
	}


	uiAllocLen = (FLMUINT)(uiDbNameLen + uiDirNameLen);
	if (RC_BAD( rc = f_alloc( uiAllocLen, &m_pszDbPath)))
	{
		goto Exit;
	}
	
	// Allocate a buffer for writing the DB header
	// If we are a temporary database, there is no need
	// for this allocation.

	if (!m_bTempDb)
	{
		if( RC_BAD( rc = f_allocAlignedBuffer( 
			XFLM_MAX_BLOCK_SIZE, (void **)&m_pDbHdrWriteBuf)))
		{
			goto Exit;
		}
	}

	// Setup the write buffer managers.
	
	if( RC_BAD( rc = FlmAllocIOBufferMgr( MAX_PENDING_WRITES,
		MAX_WRITE_BUFFER_BYTES, FALSE, &m_pBufferMgr)))
	{
		goto Exit;
	}

	// Initialize members of F_Database object.

	m_uiBucket = 0xFFFF;
	m_uiFlags = DBF_BEING_OPENED;

	// Copy the database name and directory.
	// NOTE: uiDbNameLen includes the null terminating byte.
	// and uiDirNameLen includes the null terminating byte.

	f_memcpy( m_pszDbPath, szDbPathStr, uiDbNameLen);
	if (uiDirNameLen)
	{
		m_pszDataDir = m_pszDbPath + uiDbNameLen;
		f_memcpy( m_pszDataDir, szDataDirStr, uiDirNameLen);
	}

	// Link the file into the various lists it needs to be linked into.

	if (RC_BAD( rc = linkToBucket()))
	{
		goto Exit;
	}

	// Allocate a lock object for write locking.
	
	if( RC_BAD( rc = FlmAllocLockObject( &m_pWriteLockObj)))
	{
		goto Exit;
	}

	// Allocate a lock object for file locking.
	
	if( RC_BAD( rc = FlmAllocLockObject( &m_pDatabaseLockObj)))
	{
		goto Exit;
	}

Exit:

	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);
}
Example #4
0
/****************************************************************************
Desc:	Copy a file that is one of the files of the database.
*****************************************************************************/
FSTATIC RCODE flmCopyFile(
	IF_FileSystem *		pFileSystem,
	FLMUINT					uiStartOffset,
	FLMUINT					uiEndOffset,
	DB_COPY_INFO *			pDbCopyInfo,
	COPIED_NAME **			ppCopiedListRV,
	FLMBYTE *				pucInMemLogHdr,
	FLMBOOL					bOkToTruncate,
	STATUS_HOOK				fnStatusCallback,
	void *					UserData)
{
	RCODE				rc = FERR_OK;
	FLMBYTE *		pucBuffer = NULL;
	IF_FileHdl *	pSrcFileHdl = NULL;
	IF_FileHdl *	pDestFileHdl = NULL;
	FLMUINT			uiBufferSize = 32768;
	FLMUINT			uiBytesToRead;
	FLMUINT			uiBytesRead;
	FLMUINT			uiBytesWritten;
	FLMUINT			uiOffset;
	FLMBYTE *		pucLogHdr = NULL;
	FLMUINT			uiNewChecksum;
	FLMBOOL			bCreatedDestFile = FALSE;

	// Open the source file.

	if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( 
		pDbCopyInfo->szSrcFileName, gv_FlmSysData.uiFileOpenFlags, 
		&pSrcFileHdl)))
	{
		goto Exit;
	}

	// First attempt to open the destination file.  If it does
	// not exist, attempt to create it.

	if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( 
		pDbCopyInfo->szDestFileName, gv_FlmSysData.uiFileOpenFlags,
		&pDestFileHdl)))
	{
		if (rc != FERR_IO_PATH_NOT_FOUND &&
			 rc != FERR_IO_INVALID_PATH)
		{
			goto Exit;
		}

		if( RC_BAD( rc = gv_FlmSysData.pFileSystem->createFile( 
			pDbCopyInfo->szDestFileName, gv_FlmSysData.uiFileCreateFlags, 
			&pDestFileHdl)))
		{
			goto Exit;
		}
		bCreatedDestFile = TRUE;
	}
								
	// Allocate a buffer for reading and writing

	if( RC_BAD( rc = f_allocAlignedBuffer( uiBufferSize, &pucBuffer)))
	{
		goto Exit;
	}

	// Allocate a buffer for the log header

	if( RC_BAD( rc = f_allocAlignedBuffer( 2048, &pucLogHdr)))
	{
		goto Exit;
	}

	// If uiStartOffset is 2048, it is the special case of
	// the control file, and we are not copying the first 2K.
	// However, we need to set up the first 2K so that if
	// someone reads the first 2K, it will return them a
	// maintenance in progress error.

	if (uiStartOffset == 2048)
	{
		// Read the first 2K of the source file.

		if (RC_BAD( rc = pSrcFileHdl->read( 0, 2048, pucBuffer, &uiBytesRead)))
		{
			if (rc == FERR_IO_END_OF_FILE)
			{
				rc = FERR_OK;
			}
			else
			{
				goto Exit;
			}
		}

		// Zero out whatever part of the 2K we didn't get on the read.

		if (uiBytesRead < 2048)
		{
			f_memset( &pucBuffer[ uiBytesRead], 0, (int)(2048 - uiBytesRead));
		}

		// Attempt to read the log header from the destination file.
		// It is OK if we can't read it, because if we created the
		// destination file, these bytes may not be present.

		if( bCreatedDestFile ||
			 RC_BAD( pDestFileHdl->read( 0, 2048, pucLogHdr, &uiBytesRead)))
		{
			f_memset( pucLogHdr, 0, sizeof( 2048));
		}

		// Set the transaction ID to zero.  MUST ALSO SET THE TRANS ACTIVE FLAG
		// TO FALSE - OTHERWISE READERS WILL ATTEMPT TO DECREMENT THE
		// TRANSACTION ID AND WILL END UP WITH 0xFFFFFFFF - very bad!
		// We must use zero, because it is the only transaction ID that will not
		// appear on ANY block.

		UD2FBA( 0, &pucLogHdr[ 16 + LOG_CURR_TRANS_ID]);

		// Recalculate the log header checksum so that readers will not get a
		// checksum error.

		uiNewChecksum = lgHdrCheckSum( &pucLogHdr[ 16], FALSE);
		UW2FBA( (FLMUINT16)uiNewChecksum, &pucLogHdr[ 16 + LOG_HDR_CHECKSUM]);
		f_memcpy( &pucBuffer[ 16], &pucLogHdr[ 16], LOG_HEADER_SIZE);

		// Write this "special" first 2K into the destination file.
		// The real first 2K from the source file will be copied in
		// at a later time.

		if (RC_BAD( rc = pDestFileHdl->write( 0, 2048, 
			pucBuffer, &uiBytesWritten)))
		{
			goto Exit;
		}

		// Save the log header to the in-memory version of the log
		// header as well - if pucInMemLogHdr is NULL, it is pointing
		// to the pFile->ucLastCommittedLogHdr buffer.

		if (pucInMemLogHdr)
		{
			f_memcpy( pucInMemLogHdr, &pucLogHdr[ 16], LOG_HEADER_SIZE);
		}
	}

	// Read from source file until we hit EOF in the file or
	// we hit the end offset.

	uiOffset = uiStartOffset;
	for (;;)
	{
		uiBytesToRead = (FLMUINT)((uiEndOffset - uiOffset >=
											uiBufferSize)
										 ? uiBufferSize
										 : (FLMUINT)(uiEndOffset - uiOffset));

		// Read data from source file.

		if (RC_BAD( rc = pSrcFileHdl->read( uiOffset, uiBytesToRead,
									pucBuffer, &uiBytesRead)))
		{
			if (rc == FERR_IO_END_OF_FILE)
			{
				rc = FERR_OK;
				if (!uiBytesRead)
				{
					break;
				}
			}
			else
			{
				goto Exit;
			}
		}

		// Write data to destination file.

		if (RC_BAD( rc = pDestFileHdl->write( uiOffset,
									uiBytesRead, pucBuffer, &uiBytesWritten)))
		{
			goto Exit;
		}

		// See if we wrote out the buffer that has the log header
		// If so, we need to copy it back to the in-memory log
		// header.

		if ((pucInMemLogHdr) &&
			 (uiOffset <= 16) &&
			 (uiOffset + uiBytesWritten >= 16 + LOG_HEADER_SIZE))
		{
			f_memcpy( pucInMemLogHdr, &pucBuffer[ 16 - uiOffset],
								LOG_HEADER_SIZE);
		}

		uiOffset += uiBytesWritten;

		// Do callback to report progress.

		if (fnStatusCallback)
		{
			pDbCopyInfo->ui64BytesCopied += (FLMUINT64)uiBytesWritten;
			if (RC_BAD( rc = (*fnStatusCallback)( FLM_DB_COPY_STATUS,
										(void *)pDbCopyInfo,
										(void *)0, UserData)))
			{
				goto Exit;
			}
			pDbCopyInfo->bNewSrcFile = FALSE;
		}

		// Quit once we reach the end offset or we read fewer bytes
		// than we asked for.

		if (uiOffset >= uiEndOffset || uiBytesRead < uiBytesToRead)
		{
			break;
		}
	}

	// If we overwrote the destination file, as opposed to creating
	// it, truncate it in case it was larger than the number of
	// bytes we actually copied.

	if (!bCreatedDestFile && bOkToTruncate)
	{
		if (RC_BAD( rc = pDestFileHdl->truncateFile( uiOffset)))
		{
			goto Exit;
		}
	}

	// If the copy succeeded, add the destination name to a list
	// of destination files.  This is done so we can clean up
	// copied files if we fail somewhere in the overall database
	// copy.

	if (ppCopiedListRV)
	{
		COPIED_NAME *	pCopyName;

		if( RC_BAD( rc = f_alloc( 
			(FLMUINT)sizeof( COPIED_NAME), &pCopyName)))
		{
			goto Exit;
		}
		f_strcpy( pCopyName->szPath, pDbCopyInfo->szDestFileName);
		pCopyName->pNext = *ppCopiedListRV;
		*ppCopiedListRV = pCopyName;
	}

Exit:

	if( pucBuffer)
	{
		f_freeAlignedBuffer( &pucBuffer);
	}

	if( pucLogHdr)
	{
		f_freeAlignedBuffer( &pucLogHdr);
	}

	if( pSrcFileHdl)
	{
		pSrcFileHdl->Release();
	}

	if( pDestFileHdl)
	{
		pDestFileHdl->flush();
		pDestFileHdl->Release();
	}

	// Attempt to delete the destination file if
	// we didn't successfully copy it.

	if( RC_BAD( rc))
	{
		(void)pFileSystem->deleteFile( pDbCopyInfo->szDestFileName);
	}

	return( rc);
}