mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
660 lines
15 KiB
660 lines
15 KiB
/*****************************************************************************
|
|
* *
|
|
* BTREE.C *
|
|
* *
|
|
* Copyright (C) Microsoft Corporation 1989, 1990-1995 *
|
|
* All Rights reserved. *
|
|
* *
|
|
******************************************************************************
|
|
* *
|
|
* Module Intent *
|
|
* *
|
|
* Btree manager general functions: open, close, etc. *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
#include "help.h"
|
|
|
|
// #include "inc\btpriv.h"
|
|
|
|
/*
|
|
Global btree error code. This contains the error status for the most
|
|
recent btree function call.
|
|
This error code is shared for all btrees, and, if the DLL version is
|
|
used, it's shared for all instances (this is probably a bug.)
|
|
*/
|
|
|
|
static BOOL STDCALL InitQbthr(char chType, QBTHR qbthr);
|
|
|
|
_private RC rcBtreeError = rcSuccess;
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcMakeCache( qbthr )
|
|
-
|
|
* Purpose: Allocate a btree cache with one block per level.
|
|
*
|
|
* ASSUMES
|
|
* args IN: qbthr - no cache
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess, rcOutOfMemory, rcBadHandle
|
|
* args OUT: qbthr->ghCache is allocated; qbthr->qCache is NULL
|
|
*
|
|
\***************************************************************************/
|
|
|
|
RC STDCALL RcMakeCache(QBTHR qbthr)
|
|
{
|
|
if (qbthr->bth.cLevels > 0) { // would it work to just alloc 0 bytes???
|
|
qbthr->pCache = (PBYTE)
|
|
lcCalloc(qbthr->bth.cLevels * CbCacheBlock(qbthr));
|
|
|
|
if (qbthr->pCache == NULL)
|
|
return (rcBtreeError = rcOutOfMemory);
|
|
}
|
|
else // should never happen
|
|
qbthr->pCache = NULL;
|
|
|
|
ASSERT(qbthr->pCache);
|
|
|
|
return (rcBtreeError = rcSuccess);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcGetBtreeError()
|
|
-
|
|
* Purpose: Return the current global btree error status.
|
|
*
|
|
* ASSUMES
|
|
* globals IN: rcBtreeError
|
|
*
|
|
* PROMISES
|
|
* returns: current btree error status RC
|
|
*
|
|
* Bugs: A single RC is kept for all btrees. If the DLL is used,
|
|
* it's shared between all instances.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
RC STDCALL RcGetBtreeError()
|
|
{
|
|
return rcBtreeError;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: HbtCreateBtreeSz( psz, pbtp )
|
|
-
|
|
* Purpose: Create and open a btree.
|
|
*
|
|
* ASSUMES
|
|
* args IN: psz - name of the btree
|
|
* pbtp - pointer to btree params: NO default because we
|
|
* need an HFS.
|
|
* .bFlags - fFSIsDirectory to create an FS directory
|
|
* PROMISES
|
|
* returns: handle to the new btree
|
|
* globals OUT: rcBtreeError
|
|
*
|
|
* Note: KT supported: KT_SZ, KT_LONG, KT_SZI, KT_SZISCAND.
|
|
* +++
|
|
*
|
|
* Method: Btrees are files inside a FS. The FS directory is a
|
|
* special file in the FS.
|
|
*
|
|
* Note: fFSIsDirectory flag set in qbthr->bth.bFlags if indicated
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HBT STDCALL HbtCreateBtreeSz(PCSTR psz, BTREE_PARAMS * pbtp)
|
|
{
|
|
HF hf;
|
|
HBT hbt;
|
|
QBTHR qbthr;
|
|
|
|
// see if we support key type
|
|
|
|
#ifdef _DEBUG
|
|
if (pbtp != NULL &&
|
|
(pbtp->rgchFormat[0] == KT_SZISCAND ||
|
|
pbtp->rgchFormat[0] == KT_SZICZECH ||
|
|
pbtp->rgchFormat[0] == KT_SZIPOLISH ||
|
|
pbtp->rgchFormat[0] == KT_SZIHUNGAR ||
|
|
pbtp->rgchFormat[0] == KT_SZIRUSSIAN ||
|
|
pbtp->rgchFormat[0] == KT_SZIJAPAN ||
|
|
pbtp->rgchFormat[0] == KT_SZIKOREA ||
|
|
pbtp->rgchFormat[0] == KT_SZITAIWAN)) {
|
|
char szBuf[100];
|
|
wsprintf(szBuf, "%c is an invalid KT value.", pbtp->rgchFormat[0]);
|
|
OkMsgBox(szBuf);
|
|
ASSERT(FALSE);
|
|
rcBtreeError = rcBadArg;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
if (pbtp == NULL ||
|
|
(pbtp->rgchFormat[0] != KT_SZ &&
|
|
pbtp->rgchFormat[0] != KT_LONG &&
|
|
pbtp->rgchFormat[0] != KT_SZI &&
|
|
pbtp->rgchFormat[0] != KT_NLSI &&
|
|
pbtp->rgchFormat[0] != KT_NLS
|
|
))
|
|
{
|
|
rcBtreeError = rcBadArg;
|
|
return NULL;
|
|
}
|
|
|
|
// allocate btree handle struct
|
|
|
|
if ((hbt = lcCalloc(sizeof(BTH_RAM))) == NULL) {
|
|
rcBtreeError = rcOutOfMemory;
|
|
return NULL;
|
|
}
|
|
|
|
qbthr = PtrFromGh(hbt);
|
|
|
|
// initialize bthr struct
|
|
|
|
pbtp->rgchFormat[MAXFORMAT] = '\0';
|
|
lstrcpy(qbthr->bth.rgchFormat, pbtp->rgchFormat[0] == '\0'
|
|
? rgchBtreeFormatDefault
|
|
: pbtp->rgchFormat);
|
|
|
|
if (!InitQbthr(pbtp->rgchFormat[0], qbthr))
|
|
goto error_return;
|
|
|
|
// create the btree file
|
|
|
|
if ((hf = HfCreateFileHfs(pbtp->hfs, psz, pbtp->bFlags)) == NULL) {
|
|
rcBtreeError = RcGetFSError();
|
|
goto error_return;
|
|
}
|
|
|
|
qbthr->bth.wMagic = WBTREEMAGIC;
|
|
qbthr->bth.bVersion = BBTREEVERSION;
|
|
|
|
qbthr->bth.bFlags = pbtp->bFlags | fFSDirty;
|
|
qbthr->bth.cbBlock = pbtp->cbBlock ? pbtp->cbBlock : CBBTREEBLOCKDEFAULT;
|
|
|
|
qbthr->bth.bkFirst =
|
|
qbthr->bth.bkLast =
|
|
qbthr->bth.bkRoot =
|
|
qbthr->bth.bkFree = bkNil;
|
|
qbthr->bth.bkEOF = (BK)0;
|
|
|
|
qbthr->bth.cLevels = 0;
|
|
qbthr->bth.lcEntries = (LONG)0;
|
|
|
|
qbthr->hf = hf;
|
|
qbthr->cbRecordSize = 0;
|
|
qbthr->pCache = NULL;
|
|
|
|
#ifdef _X86_
|
|
LcbWriteHf(qbthr->hf, &(qbthr->bth), (LONG) sizeof(BTH)); // why???
|
|
#else
|
|
{
|
|
LONG lcbStructSize = LcbStructSizeSDFF( ISdffFileIdHf( qbthr->hf ),
|
|
SE_BTH );
|
|
LcbWriteHf(qbthr->hf, &(qbthr->bth), (LONG) lcbStructSize);
|
|
}
|
|
#endif
|
|
|
|
rcBtreeError = rcSuccess;
|
|
return hbt;
|
|
|
|
error_return:
|
|
FreeGh(hbt);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef DEADCODE
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcDestroyBtreeSz( sz, hfs )
|
|
-
|
|
* Purpose: destroy an existing btree
|
|
*
|
|
* Method: look for file and unlink it
|
|
*
|
|
* ASSUMES
|
|
* args IN: sz - name of btree file
|
|
* hfs - file system btree lives in
|
|
* state IN: btree is closed (if not data will be lost)
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess or rcFailure
|
|
* globals OUT: rcBtreeError set
|
|
*
|
|
* Notes: FS directory btree never gets destroyed: you just get rid
|
|
* of the whole fs.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
RC STDCALL RcDestroyBtreeSz(LPCSTR psz, HFS hfs)
|
|
{
|
|
return (rcBtreeError = RcUnlinkFileHfs(hfs, psz));
|
|
}
|
|
|
|
#endif
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: HbtOpenBtreeSz( sz, hfs, bFlags )
|
|
-
|
|
* Purpose: open an existing btree
|
|
*
|
|
* ASSUMES
|
|
* args IN: sz - name of the btree (ignored if isdir is set)
|
|
* hfs - hfs btree lives in
|
|
* bFlags - open mode, isdir flag
|
|
*
|
|
* PROMISES
|
|
* returns: handle to the open btree or NULL on failure
|
|
* isdir flag set in qbthr->bth.bFlags if indicated
|
|
* globals OUT: rcBtreeError set
|
|
*
|
|
\***************************************************************************/
|
|
|
|
HBT STDCALL HbtOpenBtreeSz(LPCSTR psz, HFS hfs, BYTE bFlags)
|
|
{
|
|
HF hf;
|
|
QBTHR qbthr;
|
|
HBT hbt;
|
|
LONG lcb;
|
|
|
|
// allocate struct
|
|
|
|
if (!(hbt = GhAlloc(GPTR, (LONG) sizeof(BTH_RAM)))) {
|
|
rcBtreeError = rcOutOfMemory;
|
|
return NULL;
|
|
}
|
|
qbthr = PtrFromGh(hbt);
|
|
|
|
// open btree file
|
|
|
|
hf = HfOpenHfs(hfs, (LPSTR) psz, bFlags);
|
|
if (!hf) {
|
|
rcBtreeError = RcGetFSError();
|
|
goto error_locked;
|
|
}
|
|
|
|
// read header from file
|
|
|
|
#ifdef _X86_
|
|
lcb = LcbReadHf(hf, &(qbthr->bth), sizeof(BTH));
|
|
if (lcb != sizeof(BTH)) {
|
|
rcBtreeError = RcGetFSError() == rcSuccess ? rcInvalid : RcGetFSError();
|
|
goto error_openfile;
|
|
}
|
|
#else
|
|
/* Translate file format to memory format: */
|
|
{ QV qvQuickBuff = QvQuickBuffSDFF( sizeof( BTH ) );
|
|
LONG lcbStructSize = LcbStructSizeSDFF( ISdffFileIdHf( hf ),
|
|
SE_BTH );
|
|
|
|
lcb = LcbReadHf( hf, qvQuickBuff, lcbStructSize );
|
|
|
|
if ( lcb != lcbStructSize )
|
|
{
|
|
rcBtreeError = RcGetFSError() == rcSuccess ? rcInvalid : RcGetFSError() ;
|
|
goto error_openfile;
|
|
}
|
|
|
|
LcbMapSDFF( ISdffFileIdHf( hf ), SE_BTH,
|
|
&(qbthr->bth), qvQuickBuff );
|
|
}
|
|
#endif
|
|
|
|
qbthr->bth.bFlags |= fFSIsDirectory;
|
|
|
|
if (qbthr->bth.wMagic != WBTREEMAGIC) { // check magic number
|
|
rcBtreeError = rcInvalid;
|
|
goto error_openfile;
|
|
}
|
|
|
|
if (qbthr->bth.bVersion != BBTREEVERSION) { // support >1 vers someday
|
|
rcBtreeError = rcBadVersion;
|
|
goto error_openfile;
|
|
}
|
|
|
|
// initialize stuff
|
|
|
|
if ((rcBtreeError = RcMakeCache(qbthr)) != rcSuccess)
|
|
goto error_openfile;
|
|
|
|
qbthr->hf = hf;
|
|
qbthr->cbRecordSize = 0;
|
|
|
|
if (InitQbthr(qbthr->bth.rgchFormat[0], qbthr)) {
|
|
ASSERT(!(qbthr->bth.bFlags & (fFSDirty)));
|
|
|
|
if ((bFlags | qbthr->bth.bFlags) & (fFSReadOnly | fFSOpenReadOnly))
|
|
qbthr->bth.bFlags |= fFSOpenReadOnly;
|
|
|
|
return hbt;
|
|
}
|
|
|
|
error_openfile:
|
|
RcCloseHf(hf);
|
|
|
|
error_locked:
|
|
FreeGh(hbt);
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcCloseOrFlushHbt( hbt, fClose )
|
|
-
|
|
* Purpose: Close or flush the btree. Flush only works for directory
|
|
* btree. (Is this true? If so, why?)
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt
|
|
* fClose - TRUE to close the btree, FALSE to flush it
|
|
*
|
|
* PROMISES
|
|
* returns: rc
|
|
* args OUT: hbt - the btree is still open and cache still exists
|
|
*
|
|
* NOTE: This function gets called by RcCloseOrFlushHfs() even if
|
|
* there was an error (just to clean up memory.)
|
|
*
|
|
\***************************************************************************/
|
|
|
|
RC STDCALL RcCloseOrFlushHbt(HBT hbt, BOOL fClose)
|
|
{
|
|
QBTHR qbthr;
|
|
HF hf;
|
|
|
|
if (!hbt)
|
|
return rcSuccess;
|
|
|
|
qbthr = PtrFromGh(hbt);
|
|
rcBtreeError = rcSuccess;
|
|
hf = qbthr->hf;
|
|
|
|
if (qbthr->bth.bFlags & fFSDirty) {
|
|
ASSERT(!(qbthr->bth.bFlags & (fFSReadOnly | fFSOpenReadOnly)));
|
|
|
|
if (qbthr->pCache != NULL && RcFlushCache(qbthr) != rcSuccess) {
|
|
goto error_return;
|
|
}
|
|
|
|
qbthr->bth.bFlags &= ~( fFSDirty );
|
|
Ensure( LSeekHf( hf, (LONG)0, wFSSeekSet ), (LONG)0 );
|
|
|
|
#ifdef _X86_
|
|
if ( LcbWriteHf( hf, &(qbthr->bth), (LONG)sizeof( BTH ) )
|
|
!=
|
|
(LONG)sizeof( BTH ) )
|
|
{
|
|
rcBtreeError =
|
|
RcGetFSError( ) == rcSuccess ? rcFailure : RcGetFSError();
|
|
goto error_return;
|
|
}
|
|
#else
|
|
/* Translate memory format to file format: */
|
|
{ QV qvQuickBuff = QvQuickBuffSDFF( sizeof( BTH ) );
|
|
LONG lcbStructSize = LcbStructSizeSDFF( ISdffFileIdHf( hf ),
|
|
SE_BTH );
|
|
LcbReverseMapSDFF( ISdffFileIdHf( hf ), SE_BTH,
|
|
qvQuickBuff, &(qbthr->bth) );
|
|
|
|
if ( LcbWriteHf( hf, qvQuickBuff, lcbStructSize )
|
|
!=
|
|
lcbStructSize )
|
|
{
|
|
rcBtreeError =
|
|
RcGetFSError( ) == rcSuccess ? rcFailure : RcGetFSError();
|
|
goto error_return;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
error_return:
|
|
|
|
#ifdef _X86_
|
|
if ( rcSuccess != RcCloseOrFlushHf( hf, fClose,
|
|
qbthr->bth.bFlags & fFSOptCdRom
|
|
? sizeof( BTH ) : 0 )
|
|
&&
|
|
rcSuccess == rcBtreeError )
|
|
{
|
|
rcBtreeError = RcGetFSError();
|
|
}
|
|
#else
|
|
{
|
|
LONG lcbBTH = LcbStructSizeSDFF( ISdffFileIdHf( hf ), SE_BTH );
|
|
|
|
if ( rcSuccess != RcCloseOrFlushHf( hf, fClose,
|
|
qbthr->bth.bFlags & fFSOptCdRom
|
|
? lcbBTH : 0 )
|
|
&&
|
|
rcSuccess == rcBtreeError )
|
|
{
|
|
rcBtreeError = RcGetFSError();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (qbthr->pCache != NULL) {
|
|
if (fClose)
|
|
lcClearFree(&qbthr->pCache);
|
|
}
|
|
if (fClose)
|
|
FreeGh(hbt);
|
|
|
|
return rcBtreeError;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RC RcFreeCacheHbt( hbt )
|
|
-
|
|
* Purpose: Free the btree cache.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt - ghCache is NULL or allocated; qCache not locked
|
|
*
|
|
* PROMISES
|
|
* returns: rcSuccess; rcFailure; (rcDiskFull when implemented)
|
|
* args OUT: hbt - ghCache is NULL; qCache is NULL
|
|
*
|
|
\***************************************************************************/
|
|
|
|
RC STDCALL RcFreeCacheHbt(HBT hbt)
|
|
{
|
|
QBTHR qbthr;
|
|
RC rc;
|
|
|
|
ASSERT(hbt != NULL);
|
|
qbthr = PtrFromGh(hbt);
|
|
|
|
if (qbthr->pCache) {
|
|
rc = RcFlushCache(qbthr);
|
|
lcClearFree(&qbthr->pCache);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcGetBtreeInfo( hbt, qchFormat, qlcKeys )
|
|
-
|
|
* Purpose: Return btree info: format string and/or number of keys
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt
|
|
* qchFormat - pointer to buffer for fmt string or NULL
|
|
* qlcKeys - pointer to long for key count or NULL
|
|
* qcbBlock - pointer to int for block size in bytes or NULL
|
|
*
|
|
* PROMISES
|
|
* returns: rc
|
|
* args OUT: qchFormat - btree format string copied here
|
|
* qlcKeys - gets number of keys in btree
|
|
* qcbBlock - gets number of bytes in a block
|
|
*
|
|
\***************************************************************************/
|
|
RC STDCALL RcGetBtreeInfo(HBT hbt, LPSTR qchFormat, QL qlcKeys, QI qcbBlock)
|
|
{
|
|
QBTHR qbthr;
|
|
|
|
ASSERT(hbt);
|
|
qbthr = PtrFromGh(hbt);
|
|
|
|
if (qchFormat)
|
|
lstrcpy(qchFormat, qbthr->bth.rgchFormat);
|
|
|
|
if (qlcKeys)
|
|
*qlcKeys = qbthr->bth.lcEntries;
|
|
|
|
if (qcbBlock)
|
|
*qcbBlock = qbthr->bth.cbBlock;
|
|
|
|
return rcSuccess;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
- Function: RcAbandonHbt( hbt )
|
|
-
|
|
* Purpose: Abandon an open btree. All changes since btree was opened
|
|
* will be lost. If btree was opened with a create, it is
|
|
* as if the create never happened.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt
|
|
*
|
|
* PROMISES
|
|
* returns: rc
|
|
* globals OUT: rcBtreeError
|
|
* +++
|
|
*
|
|
* Method: Just abandon the file and free memory.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
RC STDCALL RcAbandonHbt(HBT hbt)
|
|
{
|
|
QBTHR qbthr;
|
|
|
|
ASSERT(hbt != NULL);
|
|
qbthr = PtrFromGh(hbt);
|
|
|
|
if (qbthr->pCache)
|
|
lcClearFree(&qbthr->pCache);
|
|
|
|
rcBtreeError = RcAbandonHf(qbthr->hf);
|
|
|
|
FreeGh(hbt);
|
|
|
|
return rcBtreeError;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
/***************************************************************************\
|
|
*
|
|
- Function: VerifyHbt( hbt )
|
|
-
|
|
* Purpose: Verify the consistency of an HBT. The main criterion
|
|
* is whether an RcAbandonHbt() would succeed.
|
|
*
|
|
* ASSUMES
|
|
* args IN: hbt
|
|
*
|
|
* PROMISES
|
|
* state OUT: Asserts on failure.
|
|
*
|
|
* Note: hbt == NULL is considered OK.
|
|
* +++
|
|
*
|
|
* Method: Check the qfshr and cache memory. Check the HF.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
VOID STDCALL VerifyHbt(HBT hbt)
|
|
{
|
|
QBTHR qbthr;
|
|
|
|
if (hbt == NULL)
|
|
return;
|
|
|
|
lcHeapCheck();
|
|
qbthr = PtrFromGh(hbt);
|
|
|
|
VerifyHf(qbthr->hf);
|
|
}
|
|
#endif // DEBUG
|
|
|
|
static BOOL STDCALL InitQbthr(char chType, QBTHR qbthr)
|
|
{
|
|
if (chType == KT_LONG) {
|
|
qbthr->BkScanInternal = BkScanLInternal;
|
|
qbthr->RcScanLeaf = RcScanLLeaf;
|
|
}
|
|
else {
|
|
qbthr->BkScanInternal = BkScanSzInternal;
|
|
qbthr->RcScanLeaf = RcScanSzLeaf;
|
|
}
|
|
|
|
switch ((KT) chType) {
|
|
case KT_LONG:
|
|
return TRUE;
|
|
|
|
case KT_SZ:
|
|
qbthr->SzCmp = WCmpSz;
|
|
return TRUE;
|
|
|
|
case KT_SZI:
|
|
qbthr->SzCmp = WCmpiSz;
|
|
return TRUE;
|
|
|
|
case KT_NLSI:
|
|
qbthr->SzCmp = WNlsCmpiSz;
|
|
return TRUE;
|
|
|
|
case KT_NLS:
|
|
qbthr->SzCmp = WNlsCmpSz;
|
|
return TRUE;
|
|
|
|
#ifdef _PRIVATE
|
|
case KT_SZIKOREA:
|
|
case KT_SZIJAPAN:
|
|
case KT_SZITAIWAN:
|
|
case KT_SZICZECH:
|
|
case KT_SZISCAND:
|
|
case KT_SZIPOLISH:
|
|
case KT_SZIRUSSIAN:
|
|
case KT_SZIHUNGAR:
|
|
{
|
|
char szBuf[100];
|
|
if (!lcid) {
|
|
wsprintf(szBuf, "%c specified without an LCID.", chType);
|
|
OkMsgBox(szBuf);
|
|
rcBtreeError = rcInvalid;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
qbthr->SzCmp = WNlsCmpiSz;
|
|
return TRUE;
|
|
#endif
|
|
|
|
default:
|
|
if (lcid) {
|
|
qbthr->SzCmp = WNlsCmpiSz;
|
|
return TRUE;
|
|
}
|
|
// unsupported KT
|
|
|
|
rcBtreeError = rcInvalid;
|
|
return FALSE;
|
|
}
|
|
}
|