Files
mars-flaim/flaim/src/fblob.cpp
ahodgkinson 0e2ba5f0b3 Updated support for AIX.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@729 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-08-01 19:31:50 +00:00

666 lines
17 KiB
C++

//-------------------------------------------------------------------------
// Desc: BLOB read routines.
// Tabs: 3
//
// Copyright (c) 1995-2000,2002-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: fblob.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $
//-------------------------------------------------------------------------
#include "flaimsys.h"
#define BLOB_H_VERSION_LEN_POS 0 // 1 - version is also where _H_ ends
#define BLOB_CODE_VERSION 28 // BLOB Version 2.8 ends on offset 28
#define BLOB_H_STORAGE_TYPE_POS 1 // 1 - see byStorageType
#define BLOB_H_FLAGS_POS 2 // 2 - owned, referenced, ...
#define BLOB_H_TYPE_POS 4 // 2 - user defined type
// Type of DATA or 0 if unknown
#define BLOB_H_FUTURE2 6 // ZERO for now
#define BLOB_H_RAW_SIZE_POS 8 // 4 - for large internals
#define BLOB_H_STORAGE_SIZE_POS 12 // 4 - for large internals
#define BLOB_H_MATCH_STAMP_POS 16 // 8 - match this with BLOB header
#define BLOB_MATCH_STAMP_SIZE 8
#define BLOB_H_RIGHT_KEY_POS 24 // 4 - right part of encryption key
/* Non-portable Reference BLOB Field Layout */
#define BLOB_R_CHARSET_POS 28 // 1=ANSI,2=UNICODE,...
#define BLOB_R_STRLENGTH_POS 29 // Char Length of reference path
#define BLOB_R_PATH_POS 30 // variable
/****************************************************************************
Desc: Create a BLOB that references a file.
Notes: The file will not be built by the FLAIM BLOB code, and thus the
format of the file will not be known or controlled by FLAIM.
****************************************************************************/
RCODE FlmBlobImp::referenceFile(
HFDB hDb,
const char * pszFileName,
FLMBOOL bOwned)
{
RCODE rc = FERR_OK;
char szUnportablePath[ F_PATH_MAX_SIZE];
FLMUINT uiFlags;
FDB * pDb = (FDB *)hDb;
flmAssert( !m_pHeaderBuf);
if( RC_BAD( rc = flmCheckDatabaseState( pDb)))
{
goto Exit;
}
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->pathToStorageString(
pszFileName, szUnportablePath)))
{
goto Exit;
}
uiFlags = (bOwned)
? BLOB_OWNED_REFERENCE_FLAG
: BLOB_UNOWNED_REFERENCE_FLAG;
m_hDb = hDb;
if( uiFlags & BLOB_OWNED_REFERENCE_FLAG )
{
m_uiStorageType = BLOB_REFERENCE_TYPE | BLOB_OWNED_TYPE;
}
else if( uiFlags & BLOB_UNOWNED_REFERENCE_FLAG)
{
m_uiStorageType = BLOB_REFERENCE_TYPE;
}
else
{
flmAssert(0);
rc = RC_SET( FERR_SYNTAX);
goto Exit;
}
m_uiFlags = uiFlags;
m_uiAction = BLOB_CREATE_ACTION;
if( RC_BAD( rc = buildBlobHeader( szUnportablePath)))
{
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Builds a reference blob header that will be used as
the data for the field.
****************************************************************************/
RCODE FlmBlobImp::buildBlobHeader(
const char * pszUnportablePath)
{
RCODE rc = FERR_OK;
FLMBYTE * ptr;
FLMUINT uiFileNameLen;
// Determine the number of bytes to allocate.
uiFileNameLen = f_strlen( pszUnportablePath) + 1;
m_uiHeaderLen = BLOB_R_PATH_POS + uiFileNameLen;
if( RC_BAD( rc = f_alloc( m_uiHeaderLen, &m_pHeaderBuf)))
{
goto Exit;
}
ptr = m_pHeaderBuf;
ptr[ BLOB_H_VERSION_LEN_POS] = BLOB_CODE_VERSION; // 28
ptr[ BLOB_H_STORAGE_TYPE_POS] = (FLMBYTE) m_uiStorageType;
UW2FBA( (FLMUINT16)m_uiFlags, &ptr[ BLOB_H_FLAGS_POS ]);
UW2FBA( BLOB_UNKNOWN_TYPE, &ptr[ BLOB_H_TYPE_POS ]);
UW2FBA( 0, &ptr[ BLOB_H_FUTURE2 ]);
UD2FBA( 0, &ptr[ BLOB_H_RAW_SIZE_POS ]);
UD2FBA( 0, &ptr[ BLOB_H_STORAGE_SIZE_POS ]);
f_memset( &ptr[ BLOB_H_MATCH_STAMP_POS ], 0, BLOB_MATCH_STAMP_SIZE );
UD2FBA( 0, &ptr[ BLOB_H_RIGHT_KEY_POS ]);
ptr[ BLOB_R_CHARSET_POS ] = 1;
ptr[ BLOB_R_STRLENGTH_POS ] = (FLMBYTE) uiFileNameLen;
f_memcpy( &ptr[ BLOB_R_PATH_POS ], pszUnportablePath, uiFileNameLen );
// Watch out, the file name is NOT null terminated.
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FlmBlobImp::setupBlobFromField(
FDB * pDb,
const FLMBYTE * pBlobData,
FLMUINT uiBlobDataLength)
{
RCODE rc = FERR_OK;
// See if the database is being forced to close
if( RC_BAD( rc = flmCheckDatabaseState( pDb)))
{
goto Exit;
}
if( getImportDataPtr( uiBlobDataLength) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
f_memcpy( m_pHeaderBuf, pBlobData, uiBlobDataLength);
// Check that the storage length is within reason to get information
if( m_uiHeaderLen <= BLOB_R_PATH_POS)
{
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
m_hDb = (HFDB)pDb;
// Read in the data that is in the header.
m_uiAction = BLOB_OPEN_ACTION;
m_uiStorageType = (FLMUINT) m_pHeaderBuf[ BLOB_H_STORAGE_TYPE_POS];
m_uiFlags = FB2UW( &m_pHeaderBuf[ BLOB_H_FLAGS_POS]);
m_bReadWriteAccess = FALSE;
Exit:
if( RC_BAD(rc) && m_pHeaderBuf)
{
(void) close();
}
return( rc);
}
/****************************************************************************
Desc: Closes a BLOB and frees all associated memory
****************************************************************************/
RCODE FlmBlobImp::close() // Return value is meaningless.
{
FlmBlobImp * pNextBlob;
FlmBlobImp * pPrevBlob;
FDB * pDb;
if( m_pHeaderBuf)
{
f_free( &m_pHeaderBuf);
m_pHeaderBuf = NULL;
}
// The case of a created referenced blob that is not attached
// will have to be deleted by the caller.
if (m_bInDbList)
{
if (m_hDb != HFDB_NULL)
{
// Pull out of the linked list
pPrevBlob = m_pPrevBlob;
pNextBlob = m_pNextBlob;
if( pPrevBlob == NULL) /* New first blob element? */
{
pDb = (FDB *) m_hDb;
pDb->pBlobList = pNextBlob;
}
else
{
pPrevBlob->setNext( pNextBlob); /* Delete pBlob */
}
if( pNextBlob != NULL)
{
pNextBlob->setPrev( pPrevBlob);
}
}
m_bInDbList = FALSE;
}
if( m_pFileHdl)
{
(void) closeFile();
}
return( FERR_OK);
}
/****************************************************************************
Desc: Builds a reference blob header that will be used as
the data for the field.
****************************************************************************/
RCODE FlmBlobImp::closeFile()
{
if( m_pFileHdl)
{
m_pFileHdl->closeFile();
m_bFileAccessed = FALSE;
m_pFileHdl->Release();
m_pFileHdl = NULL;
}
return FERR_OK;
}
/****************************************************************************
Desc: Opens a file given the open flags.
****************************************************************************/
RCODE FlmBlobImp::openFile()
{
RCODE rc = FERR_OK;
char szFileName[ F_PATH_MAX_SIZE];
FDB * pDb = (FDB *) m_hDb;
if( !m_pFileHdl && pDb)
{
buildFileName( szFileName);
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( szFileName,
FLM_IO_SH_DENYNONE |
(m_bReadWriteAccess ? FLM_IO_RDWR : FLM_IO_RDONLY),
&m_pFileHdl)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Build a file name in the input buffer.
****************************************************************************/
RCODE FlmBlobImp::buildFileName(
char * pszFileName)
{
RCODE rc = FERR_OK;
FLMUINT uiNameLength;
// Be carefull not to do a string copy - this is not null terminated.
uiNameLength = m_uiHeaderLen - BLOB_R_PATH_POS;
f_memcpy( pszFileName, &m_pHeaderBuf[ BLOB_R_PATH_POS], uiNameLength);
pszFileName[ uiNameLength ] = '\0';
// Override the file extension set in the gv_FlmSysData structure.
if( gv_FlmSysData.ucBlobExt [0])
{
char szBlobPath[ F_PATH_MAX_SIZE];
char szBlobBaseName [F_FILENAME_SIZE];
char * pszFileExt;
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce(
pszFileName, szBlobPath, szBlobBaseName)))
{
goto Exit;
}
pszFileExt = szBlobBaseName;
while( (*pszFileExt) && (*pszFileExt != '.'))
{
pszFileExt++;
}
// Add period if there was none.
if( !(*pszFileExt))
{
*pszFileExt = '.';
}
// Get past period.
pszFileExt++;
f_strcpy( pszFileExt, (const char *)gv_FlmSysData.ucBlobExt);
f_strcpy( pszFileName, szBlobPath);
gv_FlmSysData.pFileSystem->pathAppend( pszFileName, szBlobBaseName);
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Compare a file name with the file name in the BLOB. Does not
take into account one with a full path and one without.
****************************************************************************/
FLMINT FlmBlobImp::compareFileName(
const char * pszFileName)
{
char szThisFileName[ F_PATH_MAX_SIZE];
if( RC_BAD( buildFileName( szThisFileName)))
{
return( 1);
}
#ifdef FLM_UNIX
return( f_strcmp( szThisFileName, pszFileName));
#else
return( f_stricmp( szThisFileName, pszFileName));
#endif
}
/****************************************************************************
Desc: Transition the action checking for multiple referenced blobs.
****************************************************************************/
void FlmBlobImp::transitionAction(
FLMBOOL bDoTransition)
{
if( bDoTransition)
{
/*
Transition table:
Operation(s) Commit Action
ADD Do nothing
DELETE Delete blob
ADD -> DELETE Delete blob
DELETE -> ADD Do nothing
ADD -> ADD Multi-referenced blob - ASSERT
DELETE -> DELETE Multi-referenced blob - ASSERT
*/
// This is the time to look for multiple-referenced blobs.
if( m_uiCurrentAction == BLOB_ADD_ACTION)
{
// Two ADDs mean multi-referenced blobs.
if( m_uiAction == BLOB_ADD_ACTION)
{
flmAssert(0);
}
m_uiAction = m_uiCurrentAction;
}
else if( m_uiCurrentAction == BLOB_DELETE_ACTION)
{
// Two DELETEs mean multi-referenced blobs.
if( m_uiAction == BLOB_DELETE_ACTION)
{
flmAssert(0);
}
m_uiAction = m_uiCurrentAction;
}
}
m_uiCurrentAction = BLOB_NO_ACTION;
return;
}
/****************************************************************************
Desc:
****************************************************************************/
void FlmBlobImp::setInDbList( FLMBOOL bInDbList)
{
m_bInDbList = bInDbList;
}
/****************************************************************************
Desc: Returns a pointer that the blob header (control) info can be copied
to.
****************************************************************************/
FLMBYTE * FlmBlobImp::getImportDataPtr( FLMUINT uiLength)
{
if( m_pHeaderBuf && m_uiHeaderLen <= uiLength)
{
// This case handles a reuse of an existing BLOB object.
m_uiHeaderLen = uiLength;
return m_pHeaderBuf;
}
if( m_pHeaderBuf)
{
// Have a buffer, but it's not large enough
f_free( &m_pHeaderBuf);
m_pHeaderBuf = NULL;
m_uiHeaderLen = 0;
}
m_uiHeaderLen = uiLength;
if( RC_BAD( f_alloc( m_uiHeaderLen, &m_pHeaderBuf)))
{
m_pHeaderBuf = NULL;
}
return m_pHeaderBuf;
}
/****************************************************************************
Desc: This code only suports referenced blobs at this time.
Look for this blob field within the current transaction list.
If the FILENAME has a match then transition the action to the
current action. If the FILENAME doesn't have a match in the list
then create a new BLOB and add it to the list with either the
BLOB_ADD_ACTION or the BLOB_DELETE_ACTION.
At transaction commit time, all blobs that end with the
BLOB_DELETE_ACTION will have their files removed.
This code does not care about the records that the BLOB comes
from. So, the user can do the following:
Add BLOB( ABCD) in record 1
Delete BLOB( ABCD) in record 1
Add BLOB( ABCD) in record 2
The pre-ver41 code would have deleted BLOB( ABCD) because of the
delete in record one. Then record 2 would be pointing to a
non-existant blob.
Be carefull, because this code still does not support
multiply-referenced blobs; both record 3 and record 4 reference
the same blob file. This is because the first delete will remove
the blob file so the other reference will be corrupt.
Ret: FERR_OK or FERR_MEM
Called: Is only called from KyBuild().
****************************************************************************/
RCODE flmBlobPlaceInTransactionList(
FDB * pDb,
FLMUINT uiAction,
FlmRecord * pRecord,
void * pvBlobField)
{
RCODE rc = FERR_OK;
const FLMBYTE * pBlobData;
FLMUINT uiBlobDataLength;
FLMUINT uiStorageType;
FlmBlobImp * pBlob;
FlmBlobImp * pNewBlob = NULL;
char szFileName[ F_PATH_MAX_SIZE];
// Nothing to work with?
if( (pBlobData = pRecord->getDataPtr( pvBlobField)) == NULL)
{
goto Exit;
}
uiBlobDataLength = pRecord->getDataLength( pvBlobField);
// Don't have to do anything for an unowned reference
uiStorageType = pBlobData[ BLOB_H_STORAGE_TYPE_POS];
if( (uiStorageType & (BLOB_REFERENCE_TYPE|BLOB_OWNED_TYPE))
== BLOB_REFERENCE_TYPE)
{
goto Exit;
}
// Create a temporary new blob - may or may not keep it.
// Need to create it so we can make a file name.
if( (pNewBlob = f_new FlmBlobImp) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = pNewBlob->setupBlobFromField( pDb,
pBlobData, uiBlobDataLength)))
{
pNewBlob->Release();
pNewBlob = NULL;
goto Exit;
}
pNewBlob->setCurrentAction( uiAction);
pNewBlob->buildFileName( szFileName);
for( pBlob = pDb->pBlobList; pBlob; pBlob = pBlob->getNext())
{
if( pBlob->compareFileName( szFileName) == 0)
{
// Found a match!
pBlob->transitionAction( FALSE);
pNewBlob->Release();
pNewBlob = NULL;
break;
}
}
if( !pBlob)
{
// Link to the front of the list - doesn't matter where in the list.
pBlob = pDb->pBlobList;
pDb->pBlobList = pNewBlob;
pNewBlob->setNext( pBlob);
pNewBlob->setInDbList( TRUE);
if( pBlob)
{
pBlob->setPrev( pNewBlob);
}
// Don't delete pNewBlob - in the linked list.
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Commit the record operation (add,modify,delete)
****************************************************************************/
RCODE FB_OperationEnd(
FDB * pDb,
RCODE rcOfOperation)
{
RCODE rc = FERR_OK;
FlmBlobImp * pBlob;
// The pDb may not be all initialized due to errors in
// the fdbInit(). Check for null values.
if( !pDb || pDb->uiTransType == FLM_NO_TRANS)
{
goto Exit;
}
for( pBlob = pDb->pBlobList; pBlob; pBlob = pBlob->getNext())
{
pBlob->transitionAction( rcOfOperation == FERR_OK ? TRUE : FALSE);
}
Exit:
rc = (rcOfOperation != FERR_OK) ? rcOfOperation : rc;
return( rc );
}
/****************************************************************************
Desc: Called after the commit phase.
Go through the BLOB list and delete the file of all deleted blobs.
No longer supports deleting unattached blobs. These were created
blobs that were never attached to a data record field.
****************************************************************************/
void FBListAfterCommit(
FDB * pDb)
{
FlmBlobImp * pBlob;
FlmBlobImp * pNextBlob;
char szFileName[ F_PATH_MAX_SIZE];
for( pBlob = pDb->pBlobList; pBlob; pBlob = pNextBlob )
{
pNextBlob = pBlob->getNext();
if( pBlob->getAction() == BLOB_DELETE_ACTION)
{
// Better not be opened. Build the file name and delete.
if( RC_OK( pBlob->buildFileName( szFileName)))
{
gv_FlmSysData.pFileSystem->deleteFile( szFileName);
}
}
(void) pBlob->close();
pBlob->Release();
}
return;
}
/****************************************************************************
Desc: Called after the abort command. Cleans up the FlmBlob actions
according to the abort rules - doing nothing to the newly added
blobs.
Notes: Could be called before or during the commit call or as part of
FlmTransAbort(). Must handle all of the cases.
****************************************************************************/
void FBListAfterAbort(
FDB * pDb)
{
FlmBlobImp * pBlob;
FlmBlobImp * pNextBlob;
for( pBlob = pDb->pBlobList; pBlob; pBlob = pNextBlob )
{
pNextBlob = pBlob->getNext();
(void) pBlob->close();
pBlob->Release();
}
return;
}
/****************************************************************************
Desc: Allocate a new blob object
****************************************************************************/
FLMEXP RCODE FLMAPI FlmAllocBlob(
FlmBlob ** ppBlob)
{
RCODE rc = FERR_OK;
if( (*ppBlob = f_new FlmBlobImp) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
Exit:
return( rc);
}