12424 lines
438 KiB
C
12424 lines
438 KiB
C
/*++
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
nmsdb.c
|
|
|
|
|
|
|
|
Abstract:
|
|
|
|
This module contains the functions used to interface with the
|
|
database engine of choice. Currently that engine is the JetBlue
|
|
engine
|
|
|
|
Functions:
|
|
NmsDbInit
|
|
NmsDbInsertRowInd
|
|
NmsDbInsertRowGrp
|
|
NmsDbRelRow
|
|
NmsDbQueryRow
|
|
NmsDbUpdateRow
|
|
NmsDbSeekNUpdateRow
|
|
NmsDbGetDataRecs
|
|
StoreGrpMems
|
|
CreateTbl
|
|
InitColInfo
|
|
ReadOwnAddTbl
|
|
NmsDbWriteOwnAddTbl
|
|
NmsDbThdInit
|
|
UpdateDb
|
|
NmsDbUpdateVersNo
|
|
NmsDbEndSession
|
|
GetGrpMem
|
|
NmsDbRelRes
|
|
GetMaxVersNos
|
|
InsertGrpMemsInCol
|
|
NmsDbSetCurrentIndex
|
|
NmsDbUpdNQueryIfMatch
|
|
SetSystemParams
|
|
|
|
Portability:
|
|
|
|
This module is portable to different platforms.
|
|
It is not portable across different engines
|
|
|
|
|
|
Author:
|
|
|
|
Pradeep Bahl (PradeepB) Dec-1992
|
|
|
|
Revision History:
|
|
|
|
Modification date Person Description of modification
|
|
----------------- ------- ----------------------------
|
|
--*/
|
|
|
|
|
|
/*
|
|
Includes
|
|
*/
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include "wins.h"
|
|
#include "nms.h"
|
|
#include "nmsnmh.h"
|
|
#include "winsthd.h" //
|
|
|
|
#include "esent.h" //blue jet engine's header file
|
|
|
|
#include "nmsdb.h" //
|
|
#include "winsmsc.h" //
|
|
#include "winscnf.h" //
|
|
#include "winsevt.h" //
|
|
#include "comm.h" //
|
|
#include "rpl.h"
|
|
#include "rplpull.h"
|
|
#include "rplpush.h"
|
|
#include "winsintf.h"
|
|
#include "nmfilter.h"
|
|
|
|
/*
|
|
* Local Macro Declarations
|
|
*/
|
|
#define NAMUSR "admin"
|
|
#define PASSWD ""
|
|
|
|
#define SYS_DB_PATH ".\\wins\\system.mdb"
|
|
#define TEMP_DB_PATH ".\\wins\\winstmp.mdb"
|
|
#define CHKPOINT_PATH ".\\wins"
|
|
#define LOGFILE_PATH CHKPOINT_PATH
|
|
|
|
// this constants are gone from the jet600 (ese.h) header file. But we still need
|
|
// these constants for jet500/jet200 code path.
|
|
#define JET_bitIndexClustered 0x00000010
|
|
#define JET_bitCommitFlush 0x00000001 /* commit and flush page buffers. */
|
|
|
|
#define INIT_NO_PAGES_IN_DB 1000 //initial size of database in pages
|
|
#define MAX_FIXED_FLD_LEN 255 //maximum size of a fixed field
|
|
|
|
|
|
|
|
#define PAD_FOR_REC_HEAP 1000 //pad to use when creating the
|
|
//heap for getting records from
|
|
//the db. This pad is to take
|
|
//care of heap creation overhead
|
|
// and for allocating memory for
|
|
// group members.
|
|
|
|
|
|
#define MAX_RECS_BEFORE_COMMIT 100 //max records to retrieve in
|
|
//NmsDbGetDataRecs before doing a
|
|
//commit
|
|
//
|
|
// Owner Id of the special record that stores the version number
|
|
// of an owned record deleted or replaced with a replica
|
|
//
|
|
|
|
//
|
|
// Don't want to wrap around to a negative number. Keep a pad of 16 just
|
|
// for the heck of it.
|
|
//
|
|
#define OWNER_ID_OF_SPEC_REC 0x7FFFFFF0
|
|
#define OWNER_ID_OF_SPEC_REC_OLD 250
|
|
|
|
//
|
|
// This determines the max. size (in bytes) of the buffer allocated the
|
|
// first time a range of records need to retrived.
|
|
//
|
|
#define INIT_NO_OF_ENTRIES 1000
|
|
|
|
#define NO_COLS_NAM_ADD_TBL 6 //no. of cols in Name Ip table
|
|
#define NO_COLS_OWN_ADD_TBL 5 //no. of cols in Name Ip table
|
|
|
|
//
|
|
// Passed as third arg to JetCreateDatabase
|
|
//
|
|
#define CONNECT_INFO ";COUNTRY=1; LANGID=0x0409; CP=1252"
|
|
|
|
//
|
|
// Maximum number of sessions that can be active at any one time
|
|
//
|
|
// There can be a max of MAX_CNCRNT_STATIC_INITS (3 currently; check
|
|
// winsintf.c) going on at any one time.
|
|
//
|
|
#define MAX_NO_SESSIONS (NMS_MAX_RPC_CALLS + WINSTHD_MAX_NO_NBT_THDS + \
|
|
WINSTHD_NO_RPL_THDS + WINSTHD_NO_SCV_THDS +\
|
|
WINSTHD_NO_CHL_THDS + WINSCNF_MAX_CNCRNT_STATIC_INITS )
|
|
|
|
|
|
#pragma warning(4:4532) // Turn off return from __finally block warning until this code is cleaned
|
|
// up to use __leave correctly.
|
|
|
|
#define RET_M(JetRetStat) \
|
|
{ \
|
|
DBGPRINT2(ERR, "Jet Error: JetRetStat is (%d). Line is (%d)\n", \
|
|
(JetRetStat), __LINE__); \
|
|
WINSEVT_LOG_M(JetRetStat, WINS_EVT_DATABASE_ERR); \
|
|
return(WINS_FAILURE); \
|
|
}
|
|
|
|
|
|
#define CALL_M(fn) \
|
|
{ \
|
|
JET_ERR _JetRetStat; \
|
|
if ((_JetRetStat = (fn)) != JET_errSuccess) \
|
|
{ \
|
|
RET_M(_JetRetStat); \
|
|
} \
|
|
}
|
|
// this macro always requires JetRetStat local variable to receive the return value.
|
|
#define CALL_N_JMP_M(fn, label) \
|
|
{ \
|
|
if ((JetRetStat = (fn)) != JET_errSuccess) \
|
|
{ \
|
|
DBGPRINT2(ERR, "Jet Error: JetRetStat is (%d). Line is (%d)\n", \
|
|
(JetRetStat), __LINE__); \
|
|
WINSEVT_LOG_M(JetRetStat, WINS_EVT_DATABASE_ERR); \
|
|
goto label; \
|
|
} \
|
|
}
|
|
#define CALL_N_RAISE_EXC_IF_ERR_M(fn) \
|
|
{ \
|
|
JET_ERR _JetRetStat; \
|
|
if ((_JetRetStat = (fn)) != JET_errSuccess) \
|
|
{ \
|
|
DBGPRINT2(ERR, "Jet Error: _JetRetStat is (%d). Line is (%d)\n", \
|
|
_JetRetStat, __LINE__); \
|
|
WINSEVT_LOG_M(_JetRetStat, WINS_EVT_DATABASE_ERR); \
|
|
WINS_RAISE_EXC_M(WINS_EXC_FAILURE); \
|
|
} \
|
|
}
|
|
|
|
#if 0
|
|
#define COMMIT_M(pSesId) \
|
|
{ \
|
|
(JetRetStat) = JetCommitTransaction( \
|
|
*pSesId, JET_bitCommitFlush); \
|
|
if (JetRetStat != JET_errSuccess) \
|
|
{ \
|
|
DBGPRINT1(ERR, "COMMIT FAILED: JetRetStat is (%d). \n", \
|
|
(JetRetStat)); \
|
|
WINSEVT_LOG_M((JetRetStat), WINS_EVT_COMMIT_ERR); \
|
|
} \
|
|
pTls->fTransActive = FALSE; \
|
|
}
|
|
|
|
#define ROLLBACK_M(pSesId) \
|
|
{ \
|
|
WINS_TLS_T _pTls; \
|
|
JET_SESID _SessId; \
|
|
JET_ERR _JetRetStat; \
|
|
if (pSesId == NULL) { GET_TLS_M(_pTls); ASSERT(_pTls != NULL)} \
|
|
_JetRetStat = JetRollback( \
|
|
_pTls->SesId, JET_bitRollbackAll));\
|
|
if (_JetRetStat != JET_errSuccess) \
|
|
{ \
|
|
DBGPRINT1(ERR, "ROllBACK FAILED: JetRetStat is (%d). \n", \
|
|
_JetRetStat); \
|
|
WINSEVT_LOG_M(_JetRetStat, WINS_EVT_ROLLBACK_ERR); \
|
|
} \
|
|
_pTls->fTransActive = FALSE; \
|
|
}
|
|
#endif
|
|
|
|
#define JETRET_M(fn) \
|
|
{ \
|
|
JET_ERR _JetRetStat; \
|
|
if ((_JetRetStat = (fn)) != JET_errSuccess) \
|
|
{ \
|
|
DBGPRINT2(ERR, "Jet Error: JetRetStat is (%d). Line is (%d)\n", \
|
|
_JetRetStat, __LINE__); \
|
|
WINSEVT_LOG_M(_JetRetStat, WINS_EVT_DATABASE_ERR); \
|
|
return(_JetRetStat); \
|
|
} \
|
|
}
|
|
|
|
|
|
/*
|
|
* Local Typedef Declarations
|
|
*/
|
|
|
|
|
|
/*
|
|
FLD_T -- describes various attributes of a fld/col of a table
|
|
*/
|
|
typedef struct _FLD_T {
|
|
PBYTE pName; //name of field
|
|
WORD FldTyp; //field type (unsigned byte, long, etc)
|
|
BOOL fIndex; //Is it an index field
|
|
BOOL fUnique;//Is the field value supposed to be unq
|
|
PBYTE pIndex; //Index name
|
|
PBYTE pb;
|
|
DWORD Cb;
|
|
DWORD Fid; //field id.
|
|
} FLD_T, *PFLD_T;
|
|
|
|
|
|
/*
|
|
* Global Variable Definitions
|
|
*/
|
|
|
|
|
|
/*
|
|
NmsDbNoOfOwners -- This is the number of owners that are in the
|
|
owner id to address mapping table. This variable is set
|
|
by NmsDbInit (when it reads in the above table) and subsequently
|
|
by the replicator
|
|
|
|
This variable is protected by a critical section (not used at
|
|
initialization time)
|
|
|
|
*/
|
|
|
|
DWORD NmsDbNoOfOwners = 0; //No. of owners in the Nam - Add table
|
|
|
|
|
|
/*
|
|
NmsDbOwnAddTbl -- This is the in-memory table that stores the mappings
|
|
between the owner id and the addresses.
|
|
|
|
This table is initialized at init time with the database
|
|
table NMSDB_OWN_ADD_TBL_NM if it exists.
|
|
|
|
subsequently, more entries may be inserted into this
|
|
table at replications as WINS learns of other WINS owners
|
|
|
|
The insertions into this table are tagged at the end.
|
|
In case of a configuration change, an entry may get
|
|
flagged as DELETED, in which case it can be reused.
|
|
This particular facet concering deletion is not
|
|
operational currently
|
|
|
|
This table is used by RPL_FIND_ADD_BY_OWNER_ID_M and
|
|
by RplFindOwnrId
|
|
|
|
*/
|
|
|
|
PNMSDB_ADD_STATE_T pNmsDbOwnAddTbl;
|
|
DWORD NmsDbTotNoOfSlots = NMSDB_MAX_OWNERS_INITIALLY;
|
|
CRITICAL_SECTION NmsDbOwnAddTblCrtSec;
|
|
|
|
VERS_NO_T NmsDbStartVersNo;
|
|
WINS_UID_T NmsDbUid;
|
|
|
|
//
|
|
// Must be initialized to 0. It is used by JetInit, JetBeginSession,
|
|
// JetGetSystemParameter, JetSetSystemParameter, and JetTerm
|
|
//
|
|
// Only JetInit and JetSetSystemParameter take it by reference. Only
|
|
// JetInit modifies it (Cheen Liao - 2/2/94).
|
|
//
|
|
JET_INSTANCE sJetInstance = 0;
|
|
|
|
/*
|
|
Name of the database file.
|
|
|
|
This name will be read from the registry. For now, we are STATICally
|
|
initializing the file name.
|
|
*/
|
|
FUTURES("when jet is internationalized, use WINSCNF_DB_NAME")
|
|
//BYTE NmsDbDatabaseFileName[WINS_MAX_FILENAME_SZ] = WINSCNF_DB_NAME_ASCII;
|
|
|
|
|
|
//
|
|
// STATICs for storing information about the special record that stores the
|
|
// max. version number of an updated local record (one that got deleted or
|
|
// replaced by a replica)
|
|
//
|
|
STATIC BOOL sfHighestVersNoRecExists = FALSE;
|
|
|
|
//
|
|
// Choose a name that is not likely to be used by any NBT client
|
|
//
|
|
STATIC LPBYTE spHighestVersNoRecNameOld = "xx--WINS--xx";
|
|
STATIC LPBYTE spHighestVersNoRecName = "xx--WINS--xx--DHCP--xx--DNS--xx--GARBAGE1--1EGABRAG"; //more than a valid netbios name can store
|
|
|
|
//
|
|
// Stores the version number stored in the special record.
|
|
//
|
|
STATIC VERS_NO_T sHighestVersNoSaved;
|
|
|
|
BOOL fConvJetDbCalled; //set to TRUE when the convert process has
|
|
//been invoked. Checked in NmsDbInit
|
|
BOOL fDbIs200; //set to TRUE when the convert process has
|
|
//been invoked to convert 200 series db to latest format.
|
|
//Checked in NmsDbInit.
|
|
BOOL fDbIs500; //set to TRUE when the convert process has
|
|
//been invoked to convert 500 series db to latest format.
|
|
//Checked in NmsDbInit
|
|
/*
|
|
* Local Variable Definitions
|
|
*/
|
|
|
|
/*
|
|
Values indicating the type of index to be formed on a field.
|
|
*/
|
|
|
|
#define CLUSTERED 0
|
|
#define NOINDEX 1
|
|
#define PRIMARYPART 2
|
|
|
|
/*
|
|
sNamAddTblRow
|
|
|
|
Metadata about table that maps Names to IP addresses
|
|
|
|
Note: The third and fourth fields are not used even though they are
|
|
initialized.
|
|
*/
|
|
|
|
STATIC FLD_T sNamAddTblRow[NO_COLS_NAM_ADD_TBL] =
|
|
{
|
|
{ "name", JET_coltypBinary, CLUSTERED, TRUE, "dname" },
|
|
{ "address", JET_coltypLongBinary, NOINDEX, FALSE, NULL },
|
|
{ "flags", JET_coltypLong, NOINDEX, FALSE, NULL },
|
|
#if NEW_OWID
|
|
{ "ownerid", JET_coltypLong, PRIMARYPART, TRUE, "ownerid"},
|
|
#else
|
|
{ "ownerid", JET_coltypUnsignedByte, PRIMARYPART, TRUE, "ownerid"},
|
|
#endif
|
|
{ "versionno", JET_coltypCurrency, PRIMARYPART, FALSE,"Version"},
|
|
{ "timestamp", JET_coltypLong, NOINDEX, FALSE, NULL }
|
|
};
|
|
|
|
/*
|
|
The index of various fields in a row of Name -- Add table
|
|
*/
|
|
|
|
#define NAM_ADD_NAME_INDEX 0
|
|
#define NAM_ADD_ADDRESS_INDEX 1
|
|
#define NAM_ADD_FLAGS_INDEX 2
|
|
#define NAM_ADD_OWNERID_INDEX 3
|
|
#define NAM_ADD_VERSIONNO_INDEX 4
|
|
#define NAM_ADD_TIMESTAMP_INDEX 5
|
|
|
|
/*
|
|
sOwnAddTblRow
|
|
|
|
Metadata about table that maps owner ids to addresses
|
|
*/
|
|
STATIC FLD_T sOwnAddTblRow[NO_COLS_OWN_ADD_TBL] =
|
|
{
|
|
#if NEW_OWID
|
|
{ "OwnerId", JET_coltypLong, CLUSTERED, TRUE, "OwnerId" },
|
|
#else
|
|
{ "OwnerId", JET_coltypUnsignedByte, CLUSTERED, TRUE, "OwnerId" },
|
|
#endif
|
|
{ "address", JET_coltypBinary, NOINDEX, 0, "Address" },
|
|
{ "state", JET_coltypUnsignedByte, NOINDEX, 0, "State" },
|
|
{ "versionno", JET_coltypCurrency, NOINDEX, FALSE, "Version"},
|
|
{ "uid", JET_coltypLong, NOINDEX, FALSE, "Uid"}
|
|
};
|
|
|
|
#ifdef WINSDBG
|
|
DWORD NmsDbDelDelDataRecs;
|
|
DWORD NmsDbDelQueryNUpdRecs;
|
|
#endif
|
|
|
|
/*
|
|
The index of various fields in a row of Owner Id -- Add table
|
|
*/
|
|
|
|
#define OWN_ADD_OWNERID_INDEX 0
|
|
#define OWN_ADD_ADDRESS_INDEX 1
|
|
#define OWN_ADD_STATE_INDEX 2
|
|
#define OWN_ADD_VERSIONNO_INDEX 3
|
|
#define OWN_ADD_UID_INDEX 4
|
|
|
|
|
|
#if DYNLOADJET
|
|
|
|
DYN_LOAD_JET_VERSION DynLoadJetVersion = DYN_LOAD_JET_600;
|
|
int NAM_ADD_OWNERID_SIZE;
|
|
int OWN_ADD_OWNERID_SIZE;
|
|
LPBYTE BASENAME;
|
|
|
|
|
|
NMSDB_JETFTBL_T NmsDbJetFTbl[] = {
|
|
#if _X86_
|
|
Init,
|
|
"JetInit@4", 145, NULL,
|
|
Term,
|
|
"JetTerm@4", 167, NULL,
|
|
Term2,
|
|
"JetTerm2@8", 167, NULL, //Jet200 does not have a JetTerm2
|
|
SetSystemParameter,
|
|
"JetSetSystemParameter@20", 165, NULL,
|
|
BeginSession,
|
|
"JetBeginSession@16", 104, NULL,
|
|
EndSession,
|
|
"JetEndSession@8", 124, NULL,
|
|
CreateDatabase,
|
|
"JetCreateDatabase@20", 112, NULL,
|
|
AttachDatabase,
|
|
"JetAttachDatabase@12", 102, NULL,
|
|
DetachDatabase,
|
|
"JetDetachDatabase@8", 121, NULL,
|
|
CreateTable,
|
|
"JetCreateTable@24", 115, NULL,
|
|
DeleteTable,
|
|
"JetDeleteTable@12", 120, NULL,
|
|
GetTableColumnInfo,
|
|
"JetGetTableColumnInfo@24", 137, NULL,
|
|
GetColumnInfo,
|
|
"JetGetColumnInfo@28", 127, NULL,
|
|
AddColumn,
|
|
"JetAddColumn@28", 101, NULL,
|
|
CreateIndex,
|
|
"JetCreateIndex@28", 113, NULL,
|
|
BeginTransaction,
|
|
"JetBeginTransaction@4", 105, NULL,
|
|
CommitTransaction,
|
|
"JetCommitTransaction@8", 109, NULL,
|
|
Rollback,
|
|
"JetRollback@8", 160, NULL,
|
|
CloseDatabase,
|
|
"JetCloseDatabase@12", 107, NULL,
|
|
CloseTable,
|
|
"JetCloseTable@8", 108, NULL,
|
|
OpenDatabase,
|
|
"JetOpenDatabase@20", 148, NULL,
|
|
OpenTable,
|
|
"JetOpenTable@28", 149, NULL,
|
|
Delete,
|
|
"JetDelete@8", 116, NULL,
|
|
Update,
|
|
"JetUpdate@20", 168, NULL,
|
|
RetrieveColumn,
|
|
"JetRetrieveColumn@32", 157, NULL,
|
|
SetColumn,
|
|
"JetSetColumn@28", 162, NULL,
|
|
PrepareUpdate,
|
|
"JetPrepareUpdate@12", 151, NULL,
|
|
GetCurrentIndex,
|
|
"JetGetCurrentIndex@16", 128, NULL,
|
|
SetCurrentIndex,
|
|
"JetSetCurrentIndex@12", 164, NULL,
|
|
Move,
|
|
"JetMove@16", 147, NULL,
|
|
MakeKey,
|
|
"JetMakeKey@20", 146, NULL,
|
|
Seek,
|
|
"JetSeek@12", 161, NULL,
|
|
Backup,
|
|
"JetBackup@12", 103, NULL,
|
|
Restore,
|
|
"JetRestore@8", 156, NULL
|
|
#else
|
|
Init,
|
|
"JetInit", 145, NULL,
|
|
Term,
|
|
"JetTerm", 167, NULL,
|
|
Term2,
|
|
"JetTerm2", 167, NULL, //Jet200 does not have a JetTerm2
|
|
SetSystemParameter,
|
|
"JetSetSystemParameter", 165, NULL,
|
|
BeginSession,
|
|
"JetBeginSession", 104, NULL,
|
|
EndSession,
|
|
"JetEndSession", 124, NULL,
|
|
CreateDatabase,
|
|
"JetCreateDatabase", 112, NULL,
|
|
AttachDatabase,
|
|
"JetAttachDatabase", 102, NULL,
|
|
DetachDatabase,
|
|
"JetDetachDatabase", 121, NULL,
|
|
CreateTable,
|
|
"JetCreateTable", 115, NULL,
|
|
DeleteTable,
|
|
"JetDeleteTable", 120, NULL,
|
|
GetTableColumnInfo,
|
|
"JetGetTableColumnInfo", 137, NULL,
|
|
GetColumnInfo,
|
|
"JetGetColumnInfo", 127, NULL,
|
|
AddColumn,
|
|
"JetAddColumn", 101, NULL,
|
|
CreateIndex,
|
|
"JetCreateIndex", 113, NULL,
|
|
BeginTransaction,
|
|
"JetBeginTransaction", 105, NULL,
|
|
CommitTransaction,
|
|
"JetCommitTransaction", 109, NULL,
|
|
Rollback,
|
|
"JetRollback", 160, NULL,
|
|
CloseDatabase,
|
|
"JetCloseDatabase", 107, NULL,
|
|
CloseTable,
|
|
"JetCloseTable", 108, NULL,
|
|
OpenDatabase,
|
|
"JetOpenDatabase", 148, NULL,
|
|
OpenTable,
|
|
"JetOpenTable", 149, NULL,
|
|
Delete,
|
|
"JetDelete", 116, NULL,
|
|
Update,
|
|
"JetUpdate", 168, NULL,
|
|
RetrieveColumn,
|
|
"JetRetrieveColumn", 157, NULL,
|
|
SetColumn,
|
|
"JetSetColumn", 162, NULL,
|
|
PrepareUpdate,
|
|
"JetPrepareUpdate", 151, NULL,
|
|
GetCurrentIndex,
|
|
"JetGetCurrentIndex", 128, NULL,
|
|
SetCurrentIndex,
|
|
"JetSetCurrentIndex", 164, NULL,
|
|
Move,
|
|
"JetMove", 147, NULL,
|
|
MakeKey,
|
|
"JetMakeKey", 146, NULL,
|
|
Seek,
|
|
"JetSeek", 161, NULL,
|
|
Backup,
|
|
"JetBackup", 103, NULL,
|
|
Restore,
|
|
"JetRestore", 156, NULL
|
|
#endif _X86_
|
|
};
|
|
|
|
|
|
#else
|
|
|
|
#if NEW_OWID
|
|
#define NAM_ADD_OWNERID_SIZE sizeof(DWORD)
|
|
#else
|
|
#define NAM_ADD_OWNERID_SIZE sizeof(BYTE)
|
|
#endif
|
|
#define OWN_ADD_OWNERID_SIZE NAM_ADD_OWNERID_SIZE
|
|
|
|
#endif //DYNLOADJET
|
|
/*
|
|
* Local Function Prototype Declarations
|
|
*/
|
|
|
|
/* prototypes for functions local to this module go here */
|
|
STATIC
|
|
STATUS
|
|
CreateTbl(
|
|
JET_DBID DbId,
|
|
JET_SESID SesId,
|
|
JET_TABLEID *pTblId,
|
|
NMSDB_TBL_NAM_E TblNam_e //enumerator value for table to create
|
|
);
|
|
|
|
STATIC
|
|
STATUS
|
|
InitColInfo (
|
|
JET_SESID SesId,
|
|
JET_TABLEID TblId,
|
|
NMSDB_TBL_NAM_E TblNam_e
|
|
);
|
|
|
|
STATIC
|
|
STATUS
|
|
ReadOwnAddTbl(
|
|
JET_SESID SesId,
|
|
JET_DBID DbId,
|
|
JET_TABLEID TblId
|
|
);
|
|
|
|
STATIC
|
|
JET_ERR
|
|
UpdateDb (
|
|
JET_SESID SesId,
|
|
JET_TABLEID TblId,
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
ULONG TypOfUpd
|
|
|
|
);
|
|
|
|
|
|
STATIC
|
|
STATUS
|
|
GetGrpMem (
|
|
IN JET_SESID SesId,
|
|
IN JET_TABLEID TblId,
|
|
IN PNMSDB_ROW_INFO_T pRowInfo,
|
|
IN DWORD_PTR CurrentTime,
|
|
IN OUT PNMSDB_STAT_INFO_T pStatInfo,
|
|
// IN OUT PNMSDB_NODE_ADDS_T pNodeAdds,
|
|
IN BOOL fIsStatic,
|
|
OUT LPBOOL pfIsMem
|
|
);
|
|
|
|
STATIC
|
|
STATUS
|
|
GetMaxVersNos(
|
|
JET_SESID SesId,
|
|
JET_TABLEID TblId
|
|
);
|
|
|
|
STATIC
|
|
__inline
|
|
VOID
|
|
StoreSpecVersNo(
|
|
VOID
|
|
);
|
|
STATIC
|
|
JET_ERR
|
|
InsertGrpMemsInCol(
|
|
JET_SESID SesId,
|
|
JET_TABLEID TblId,
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
ULONG TypeOfUpd
|
|
);
|
|
STATIC
|
|
VOID
|
|
StoreGrpMems(
|
|
IN PWINSTHD_TLS_T pTls,
|
|
IN WINS_CLIENT_E WinsClient_e,
|
|
IN LPBYTE pName,
|
|
IN int ThdPrLvl,
|
|
IN JET_SESID SesId,
|
|
IN JET_TABLEID TblId,
|
|
IN BOOL fIsStatic,
|
|
OUT PRPL_REC_ENTRY_T pRspBuff
|
|
);
|
|
|
|
STATIC
|
|
STATUS
|
|
SetSystemParams(
|
|
BOOL fBeforeInit
|
|
);
|
|
|
|
STATIC
|
|
VOID
|
|
UpdHighestVersNoRecIfReqd(
|
|
IN PWINSTHD_TLS_T pTls,
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
PNMSDB_STAT_INFO_T pStatInfo
|
|
);
|
|
|
|
STATIC
|
|
STATUS
|
|
InitializeJetDb(
|
|
PWINSTHD_TLS_T pTls,
|
|
LPBOOL pfInitCallSucc,
|
|
LPBOOL pfDatabaseOpened
|
|
);
|
|
|
|
STATIC
|
|
STATUS
|
|
AllocTls(
|
|
LPVOID *ppTls
|
|
);
|
|
|
|
STATUS
|
|
ObliterateWins(
|
|
DWORD i,
|
|
PCOMM_ADD_T pWinsAdd
|
|
);
|
|
|
|
#if DYNLOADJET
|
|
STATUS
|
|
SetForJet(
|
|
VOID
|
|
);
|
|
#endif //DYNLOADJET
|
|
|
|
STATUS
|
|
ConvertJetDb(
|
|
JET_ERR JetRetStat
|
|
);
|
|
/*
|
|
function definitions start here
|
|
*/
|
|
|
|
STATUS
|
|
NmsDbInit(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the database manager component of the Name
|
|
Space Manager Component
|
|
|
|
It does the following
|
|
|
|
calls _tzset to init global variables used by time(). These
|
|
global variables are set so that convertion of UST to local
|
|
time is done (for instance when time() is called)by
|
|
taking into account the timezone information.
|
|
|
|
Initialize the database engine
|
|
|
|
Start a session with the db engine
|
|
|
|
Create and attach to a database file
|
|
|
|
Create (and open) the name-address mapping table
|
|
Create (and open) the owner-address mapping table
|
|
|
|
create a clustered and primary index on the name-address table
|
|
create a clustered index on the owner-address table
|
|
|
|
|
|
Note: if the database already exists, it
|
|
|
|
Attaches to it
|
|
|
|
Opens the Name IP address Mapping table
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Externals Used:
|
|
NmsDbOwnAddTblCrtSec
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
called by:
|
|
main function of WINS
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
JET_ERR JetRetStat;
|
|
PWINSTHD_TLS_T pTls;
|
|
BOOL fFirstTime = TRUE;
|
|
|
|
if (AllocTls(&pTls) != WINS_SUCCESS)
|
|
{
|
|
return(WINS_FAILURE);
|
|
}
|
|
_tzset(); /*
|
|
func. uses TZ variable to assign values
|
|
to three global variables used by time(). This is
|
|
so that Universal Coordinated Time to may be
|
|
adjusted to local time (timezone correction)
|
|
*/
|
|
|
|
|
|
#if DYNLOADJET
|
|
if (SetForJet() != WINS_SUCCESS)
|
|
{
|
|
return(WINS_FAILURE);
|
|
}
|
|
#endif
|
|
//
|
|
// Set Jet System params (ignore return status)
|
|
//
|
|
(VOID)SetSystemParams(TRUE);
|
|
|
|
//
|
|
// Initialize the critical section for protecting the in-memory
|
|
//table NmsDbOwnAddTbl
|
|
//
|
|
// Note: This table is read and written to during stable state by
|
|
// the Pull thread and the RPC threads executing WinsStatus()
|
|
//
|
|
// Check out RplFindOwnerId in rplpull.c
|
|
//
|
|
InitializeCriticalSection(&NmsDbOwnAddTblCrtSec);
|
|
|
|
/*
|
|
Initialize the Jet engine. This must be the first call
|
|
unless JetSetSystemParameter is called to set system
|
|
parameters. In that case, this call should be after that
|
|
*/
|
|
while(TRUE)
|
|
{
|
|
BOOL fInitCallSucc;
|
|
BOOL fDatabaseOpened;
|
|
|
|
if (InitializeJetDb(pTls, &fInitCallSucc, &fDatabaseOpened) !=
|
|
WINS_SUCCESS)
|
|
{
|
|
DWORD NoOfRestoresDone = 0;
|
|
if (fFirstTime && !fDbIs200 && !fDbIs500)
|
|
{
|
|
//
|
|
// If we have a backup path, attempt to do a restore
|
|
//
|
|
if (WinsCnf.pBackupDirPath != NULL)
|
|
{
|
|
|
|
DBGPRINT1(DET, "NmsDbInit: Doing Restore from path (%s)\n", WinsCnf.pBackupDirPath);
|
|
//
|
|
// If session is active, terminate it since we need
|
|
// call JetInit again. That requires that first we
|
|
// call JetTerm which does not expect any session to
|
|
// be active
|
|
|
|
//
|
|
if (fNmsMainSessionActive)
|
|
{
|
|
//
|
|
// Close tables opened in the session
|
|
//
|
|
NmsDbCloseTables();
|
|
if (fDatabaseOpened)
|
|
{
|
|
CALL_M(JetCloseDatabase(
|
|
pTls->SesId,
|
|
pTls->DbId,
|
|
0 //find out what grbit can be
|
|
//used
|
|
)
|
|
);
|
|
}
|
|
CALL_M(JetEndSession(
|
|
pTls->SesId,
|
|
0
|
|
)
|
|
);
|
|
fNmsMainSessionActive = FALSE;
|
|
|
|
}
|
|
//
|
|
// if JetInit was successful, term jet activity
|
|
//
|
|
if (fInitCallSucc)
|
|
{
|
|
NmsDbRelRes();
|
|
}
|
|
|
|
//
|
|
// We will try JetRestore a max of two times.
|
|
//
|
|
while(NoOfRestoresDone++ < 2)
|
|
{
|
|
if (DynLoadJetVersion >= DYN_LOAD_JET_500)
|
|
{
|
|
JetRetStat = JetRestore(WinsCnf.pBackupDirPath, NULL);
|
|
|
|
}
|
|
else
|
|
{
|
|
JetRetStat = JetRestore(WinsCnf.pBackupDirPath, 0, NULL, 0);
|
|
|
|
}
|
|
if (JetRetStat != JET_errSuccess)
|
|
{
|
|
if ( (
|
|
(JetRetStat == JET_errBadLogVersion)
|
|
||
|
|
(JetRetStat == JET_errBadLogSignature)
|
|
||
|
|
(JetRetStat == JET_errInvalidLogSequence)
|
|
)
|
|
&&
|
|
(NoOfRestoresDone == 1)
|
|
)
|
|
{
|
|
TCHAR LogFilePath[WINS_MAX_FILENAME_SZ];
|
|
|
|
#define LOG_FILE_SUFFIX TEXT("jet*.log")
|
|
WinsMscConvertAsciiStringToUnicode(
|
|
WinsCnf.pLogFilePath, (LPBYTE)LogFilePath, sizeof(LogFilePath)/sizeof(TCHAR));
|
|
//
|
|
// Delete log files
|
|
//
|
|
WinsMscDelFiles(TRUE, LOG_FILE_SUFFIX, LogFilePath);
|
|
continue;
|
|
}
|
|
|
|
WinsMscPutMsg(WINS_EVT_DB_RESTORE_GUIDE);
|
|
CALL_M(JetRetStat);
|
|
|
|
}
|
|
WINSEVT_LOG_INFO_D_M(WINS_SUCCESS, WINS_EVT_DB_RESTORED);
|
|
break; // break out of while loop
|
|
} // end of while()
|
|
|
|
fFirstTime = FALSE;
|
|
PERF("remove if not required")
|
|
sJetInstance = 0; //defensive programming
|
|
|
|
|
|
#if 0
|
|
//
|
|
// Start a session again
|
|
//
|
|
if (AllocTls(&pTls) != WINS_SUCCESS)
|
|
{
|
|
return(WINS_FAILURE);
|
|
}
|
|
//
|
|
// Set Jet System params (ignore return status)
|
|
//
|
|
(VOID)SetSystemParams(TRUE);
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
WinsMscPutMsg(WINS_EVT_DB_RESTORE_GUIDE);
|
|
|
|
//
|
|
// There is no back up path specified in the registry. Return
|
|
//
|
|
return(WINS_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
if (!fDbIs200 && !fDbIs500)
|
|
{
|
|
WinsMscPutMsg(WINS_EVT_DB_RESTORE_GUIDE);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we are converting to NT 5.0, DynLoadJetVersion=DYN_LOAD_JET_600
|
|
//
|
|
if ( DynLoadJetVersion == DYN_LOAD_JET_600 ) {
|
|
|
|
//
|
|
// Put a pop-up and log an event based on which version
|
|
// of Jet database we are converting from.
|
|
//
|
|
if (!fConvJetDbCalled)
|
|
{
|
|
WINSEVT_LOG_INFO_D_M(
|
|
WINS_SUCCESS,
|
|
fDbIs200 ? WINS_EVT_DB_CONV_351_TO_5_GUIDE
|
|
: WINS_EVT_DB_CONV_4_TO_5_GUIDE);
|
|
// As per bug#339015 remove popups
|
|
// WinsMscPutMsg(
|
|
// fDbIs200 ? WINS_EVT_DB_CONV_351_TO_5_GUIDE
|
|
// : WINS_EVT_DB_CONV_4_TO_5_GUIDE);
|
|
}
|
|
else
|
|
{
|
|
//WinsMscPutMsg(WINS_EVT_TEMP_TERM_UNTIL_CONV_TO_5);
|
|
}
|
|
}
|
|
//
|
|
// If we are converting to NT 4.0, DynLoadJetVersion=DYN_LOAD_JET_500
|
|
//
|
|
else if(DynLoadJetVersion == DYN_LOAD_JET_500) {
|
|
if (!fConvJetDbCalled)
|
|
{
|
|
WINSEVT_LOG_INFO_D_M(WINS_SUCCESS, WINS_EVT_DB_CONV_GUIDE);
|
|
WinsMscPutMsg(WINS_EVT_DB_CONV_GUIDE);
|
|
}
|
|
else
|
|
{
|
|
WinsMscPutMsg(WINS_EVT_TEMP_TERM_UNTIL_CONV);
|
|
}
|
|
}else {
|
|
//
|
|
// We should never come here.
|
|
//
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// We got an error a second time. Return
|
|
//
|
|
return(WINS_FAILURE);
|
|
}
|
|
}
|
|
break; //break out of the while loop
|
|
} // end of while(TRUE)
|
|
|
|
//
|
|
// Init Push records if required
|
|
//
|
|
RPLPUSH_INIT_PUSH_RECS_M(&WinsCnf);
|
|
|
|
NMSNMH_DEC_VERS_NO_M(NmsNmhMyMaxVersNo, NmsDbStartVersNo);
|
|
|
|
//
|
|
// Set our UID to be the time when the db got initialized
|
|
//
|
|
{
|
|
time_t timeNow;
|
|
(void)time(&timeNow);
|
|
|
|
NmsDbUid = (DWORD)timeNow;
|
|
}
|
|
return(WINS_SUCCESS);
|
|
}
|
|
STATUS
|
|
AllocTls(
|
|
LPVOID *ppTls
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to allocate TLS
|
|
|
|
Arguments:
|
|
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbInit
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
|
|
--*/
|
|
|
|
{
|
|
PWINSTHD_TLS_T pTls;
|
|
WinsMscAlloc( sizeof(WINSTHD_TLS_T), ppTls);
|
|
pTls = *ppTls;
|
|
|
|
pTls->fNamAddTblOpen = FALSE;
|
|
pTls->fOwnAddTblOpen = FALSE;
|
|
|
|
/*
|
|
* Let us store the address in the TLS storage
|
|
*/
|
|
if (!TlsSetValue(WinsTlsIndex, pTls))
|
|
{
|
|
DWORD Error;
|
|
Error = GetLastError();
|
|
DBGPRINT1(ERR, "NmsDbAllocTlc: TlsSetValue returned error. Error = (%d)\n", Error);
|
|
WINSEVT_LOG_M(Error, WINS_EVT_CANT_INIT);
|
|
return(WINS_FAILURE);
|
|
}
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
STATUS
|
|
InitializeJetDb(
|
|
PWINSTHD_TLS_T pTls,
|
|
LPBOOL pfInitCallSucc,
|
|
LPBOOL pfDatabaseOpened
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function opens the Jet db and tables
|
|
|
|
Arguments:
|
|
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
|
|
{
|
|
JET_ERR JetRetStat;
|
|
JET_SESID SesId;
|
|
JET_DBID DbId;
|
|
BOOL fOwnAddTblCreated = FALSE; /*indicates whether the
|
|
owner id to address
|
|
mapping table was created
|
|
at init time
|
|
*/
|
|
|
|
*pfDatabaseOpened = FALSE;
|
|
|
|
JetRetStat = JetInit(&sJetInstance);
|
|
DBGPRINT1(ERR, "JetInit returning (%d)\n", (JetRetStat));
|
|
|
|
if (JetRetStat != JET_errSuccess)
|
|
{
|
|
*pfInitCallSucc = FALSE;
|
|
if ((JetRetStat == JET_errDatabase200Format) || (JetRetStat == JET_errDatabase500Format))
|
|
{
|
|
ConvertJetDb(JetRetStat);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// We could have got an error because the LogFilePath
|
|
// is messed up in the registry. We try again, this time using
|
|
// the default log file path.
|
|
//
|
|
// Most of the time, we get FileNotFound error. We have seen
|
|
// "bad signature" error once. Let us just do this for all
|
|
// errors. The situation will not be any worse than before if
|
|
// JetInit fails again.
|
|
//
|
|
// Set the default log path
|
|
//
|
|
SetSystemParams(FALSE);
|
|
JetRetStat = JetInit(&sJetInstance);
|
|
}
|
|
CALL_M(JetRetStat);
|
|
}
|
|
|
|
*pfInitCallSucc = TRUE;
|
|
|
|
/*
|
|
Start a session.
|
|
*/
|
|
CALL_M( JetBeginSession(
|
|
sJetInstance,
|
|
&pTls->SesId,
|
|
NAMUSR,
|
|
PASSWD )
|
|
);
|
|
|
|
fNmsMainSessionActive = TRUE;
|
|
SesId = pTls->SesId;
|
|
|
|
//
|
|
// Create/Open the database
|
|
//
|
|
if ((JetRetStat = JetCreateDatabase(
|
|
SesId,
|
|
// NmsDbDatabaseFileName,
|
|
WinsCnf.pWinsDb,
|
|
CONNECT_INFO,
|
|
&pTls->DbId,
|
|
0 //grbit; Don't want exclusive use
|
|
)) == JET_errDatabaseDuplicate
|
|
)
|
|
|
|
|
|
{
|
|
|
|
//
|
|
// let us attach to the database. This is required for
|
|
// opening databases that were created in a different
|
|
// directory (Ian -- 11/23/93). We will get a warning
|
|
// if the database was created in this very directory
|
|
//
|
|
JetRetStat = JetAttachDatabase( SesId, WinsCnf.pWinsDb/*NmsDbDatabaseFileName*/, 0 );
|
|
if (
|
|
(JetRetStat != JET_wrnDatabaseAttached)
|
|
&&
|
|
(JetRetStat != JET_errSuccess)
|
|
)
|
|
{
|
|
if ((JetRetStat == JET_errDatabase200Format) || (JetRetStat == JET_errDatabase500Format))
|
|
{
|
|
//
|
|
// Start the convert process
|
|
//
|
|
|
|
JetRetStat = ConvertJetDb(JetRetStat);
|
|
*pfInitCallSucc = TRUE;
|
|
|
|
}
|
|
|
|
CALL_M(JetRetStat);
|
|
}
|
|
|
|
//
|
|
// If JetRetStat is success, it means ...
|
|
//
|
|
// The new db path is different from the old one. We need
|
|
// to detach so that Jet forgets about the old one. We then
|
|
// attach to the new one again
|
|
//
|
|
if (JetRetStat == JET_errSuccess)
|
|
{
|
|
CALL_M(JetDetachDatabase(SesId, NULL));
|
|
CALL_M(JetAttachDatabase(SesId, WinsCnf.pWinsDb, 0 ));
|
|
}
|
|
CALL_M(JetOpenDatabase(
|
|
SesId,
|
|
//NmsDbDatabaseFileName,
|
|
WinsCnf.pWinsDb,
|
|
NULL, /*the default engine*/
|
|
&pTls->DbId,
|
|
0
|
|
)
|
|
);
|
|
*pfDatabaseOpened = TRUE;
|
|
|
|
DbId = pTls->DbId;
|
|
JetRetStat = JetOpenTable(
|
|
SesId,
|
|
DbId,
|
|
NMSDB_NAM_ADD_TBL_NM,
|
|
NULL, /*ptr to parameter list; should be
|
|
*non-NULL if a query is being
|
|
*opened*/
|
|
0, /*Length of above parameter list*/
|
|
0, //shared access (no bit set)
|
|
&pTls->NamAddTblId
|
|
);
|
|
|
|
|
|
//
|
|
// If the name-address mapping table was not found, create it
|
|
//
|
|
if (JetRetStat == JET_errObjectNotFound)
|
|
{
|
|
|
|
DBGPRINT0(INIT, "InitializeJetDb:Creating Name-Address table\n");
|
|
CALL_M(CreateTbl(
|
|
DbId,
|
|
SesId,
|
|
&pTls->NamAddTblId,
|
|
NMSDB_E_NAM_ADD_TBL_NM
|
|
)
|
|
);
|
|
//
|
|
// Set this so that we close the table when we end the
|
|
// session
|
|
//
|
|
pTls->fNamAddTblOpen = TRUE;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
CALL_M(JetRetStat);
|
|
pTls->fNamAddTblOpen = TRUE;
|
|
//
|
|
// get and store in in-memory data structure, the
|
|
// information about the columns of the name-address
|
|
// mapping table
|
|
//
|
|
CALL_M(InitColInfo(
|
|
SesId,
|
|
pTls->NamAddTblId,
|
|
NMSDB_E_NAM_ADD_TBL_NM
|
|
));
|
|
|
|
//
|
|
// get the max. version numbers of records owned
|
|
// by different owners. These will be stored in
|
|
// the RplPullOwnerVersNo table
|
|
//
|
|
CALL_M(GetMaxVersNos(
|
|
SesId,
|
|
pTls->NamAddTblId
|
|
));
|
|
|
|
}
|
|
|
|
//
|
|
// Open the owner-address mapping table
|
|
//
|
|
JetRetStat = JetOpenTable(
|
|
SesId,
|
|
DbId,
|
|
NMSDB_OWN_ADD_TBL_NM,
|
|
NULL, /*ptr to parameter list; should be
|
|
*non-NULL if a query is being
|
|
*opened*/
|
|
0, /*Length of above parameter list*/
|
|
0, //shared access (no bit set)
|
|
&pTls->OwnAddTblId
|
|
);
|
|
if (JetRetStat == JET_errObjectNotFound)
|
|
{
|
|
|
|
DBGPRINT0(INIT, "InitializeJetDb:Creating Owner-Address table\n");
|
|
//
|
|
// Create the ownerid-address mapping table
|
|
//
|
|
CALL_M(CreateTbl(
|
|
DbId,
|
|
SesId,
|
|
&pTls->OwnAddTblId,
|
|
NMSDB_E_OWN_ADD_TBL_NM
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set this so that we close the table when we
|
|
// end the session
|
|
//
|
|
pTls->fOwnAddTblOpen = TRUE;
|
|
fOwnAddTblCreated = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pTls->fOwnAddTblOpen = TRUE;
|
|
CALL_M(InitColInfo(
|
|
SesId,
|
|
pTls->OwnAddTblId,
|
|
NMSDB_E_OWN_ADD_TBL_NM
|
|
)
|
|
);
|
|
|
|
}
|
|
}
|
|
else //if database file was not existent and has now been created
|
|
{
|
|
if (JetRetStat == JET_errSuccess)
|
|
{
|
|
DBGPRINT0(INIT, "InitializeJetDb: Database file was not there. It has been created\n");
|
|
*pfDatabaseOpened = TRUE;
|
|
DbId = pTls->DbId;
|
|
|
|
//
|
|
// Create the name -address mapping table
|
|
//
|
|
CALL_M(CreateTbl(
|
|
DbId,
|
|
SesId,
|
|
&pTls->NamAddTblId,
|
|
NMSDB_E_NAM_ADD_TBL_NM
|
|
)
|
|
);
|
|
|
|
pTls->fNamAddTblOpen = TRUE;
|
|
//
|
|
// Create the ownerid-address mapping table
|
|
//
|
|
CALL_M(CreateTbl(
|
|
DbId,
|
|
SesId,
|
|
&pTls->OwnAddTblId,
|
|
NMSDB_E_OWN_ADD_TBL_NM
|
|
)
|
|
);
|
|
|
|
pTls->fOwnAddTblOpen = TRUE;
|
|
fOwnAddTblCreated = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pfDatabaseOpened = FALSE;
|
|
RET_M(JetRetStat);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate the NmsDbOwnAddTbl table in memory
|
|
//
|
|
WinsMscAlloc(
|
|
sizeof(NMSDB_ADD_STATE_T) * NmsDbTotNoOfSlots,
|
|
&pNmsDbOwnAddTbl
|
|
);
|
|
|
|
/*
|
|
If the Owner - Address table was there, read its contents into
|
|
an in-memory table
|
|
*/
|
|
|
|
FUTURES("Pass ptr to an in-memory table instead of having ReadOwnAddTbl")
|
|
FUTURES("assume that one is present")
|
|
|
|
if (!fOwnAddTblCreated)
|
|
{
|
|
ReadOwnAddTbl(
|
|
SesId,
|
|
DbId,
|
|
pTls->OwnAddTblId
|
|
);
|
|
}
|
|
|
|
//
|
|
// Set the current index on the name-address table to the
|
|
// clustered index
|
|
//
|
|
|
|
CALL_M(
|
|
JetSetCurrentIndex( SesId,
|
|
pTls->NamAddTblId,
|
|
NMSDB_NAM_ADD_CLUST_INDEX_NAME
|
|
)
|
|
);
|
|
|
|
return(WINS_SUCCESS);
|
|
|
|
} // end InitialiazeJetDb
|
|
|
|
|
|
STATUS
|
|
NmsDbInsertRowInd(
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
PNMSDB_STAT_INFO_T pStatusInfo
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This function inserts a unique name-IP address mapping row in the
|
|
name-IP address mapping table. In case of a conflict, it returns
|
|
an error status and information about the conflicting
|
|
record that includes
|
|
|
|
|
|
Status -- group/unique
|
|
IP address(es) of the conflicting record (one address if
|
|
it was a unique record, one or more if it was
|
|
a special group).
|
|
state -- the state of the record (active/released/tombstone)
|
|
|
|
|
|
|
|
Arguments:
|
|
pRowInfo - Info. about the row to insert
|
|
pStatusInfo - Contains status of the operation + info about the
|
|
conflicting record, if the registration conflicted
|
|
with an entry in the db.
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsNmhNamRegInd
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
|
|
DWORD FldNo = 0;
|
|
JET_ERR JetRetStat;
|
|
DWORD FlagVal = 0; //flag value of record
|
|
DWORD ActFldLen = 0; //length of fld retrieved
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
BOOL fWaitDone = FALSE;
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
pStatusInfo->StatCode = NMSDB_SUCCESS;
|
|
|
|
JetRetStat = UpdateDb(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
JET_prepInsert
|
|
);
|
|
|
|
if ( JetRetStat == JET_errKeyDuplicate )
|
|
{
|
|
pStatusInfo->StatCode = NMSDB_CONFLICT;
|
|
|
|
/*
|
|
* retrieve the conflicting record's
|
|
* flag byte.
|
|
*/
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo->pName,
|
|
pRowInfo->NameLen,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
if ((JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
)) == JET_errSuccess
|
|
)
|
|
{
|
|
|
|
// retrieve the flags column
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&FlagVal,
|
|
sizeof(FlagVal),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
|
|
pStatusInfo->EntTyp = (BYTE)NMSDB_ENTRY_TYPE_M(FlagVal);
|
|
pStatusInfo->fStatic = NMSDB_IS_ENTRY_STATIC_M(FlagVal);
|
|
pStatusInfo->EntryState_e =
|
|
NMSDB_ENTRY_STATE_M(FlagVal);
|
|
|
|
if (NMSDB_ENTRY_UNIQUE_M(pStatusInfo->EntTyp))
|
|
{
|
|
|
|
FUTURES("Remove this RETINFO thing. Presumably, it is not needed")
|
|
/* It is a unique entry*/
|
|
JET_RETINFO RetInfo;
|
|
|
|
RetInfo.itagSequence = 1;
|
|
RetInfo.cbStruct = sizeof(JET_RETINFO);
|
|
RetInfo.ibLongValue = 0;
|
|
|
|
// retrieve the ip address column
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&(pStatusInfo->NodeAdds.Mem[0].Add),
|
|
sizeof(COMM_ADD_T),
|
|
&ActFldLen,
|
|
0,
|
|
&RetInfo
|
|
)
|
|
);
|
|
pStatusInfo->NodeAdds.NoOfMems = 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (NMSDB_ENTRY_MULTIHOMED_M(pStatusInfo->EntTyp))
|
|
{
|
|
//
|
|
// If status is active, we get the
|
|
// group members
|
|
//
|
|
if (pStatusInfo->EntryState_e ==
|
|
NMSDB_E_ACTIVE)
|
|
{
|
|
BOOL fIsMem;
|
|
|
|
#if 0
|
|
//NOTE: No need to do the following, since we don't care about the value of
|
|
//fIsMem returned GetGrpMem()
|
|
pRowInfo->NodeAdds.NoOfMems = 1;
|
|
pRowInfo->NodeAdds.Mem[0].Add =
|
|
*(pRowInfo->pNodeAdd);
|
|
#endif
|
|
|
|
PERF("If entry in conflict is STATIC, we don't need to get grp members")
|
|
PERF("except maybe for multihomed entries. Checkout Clash functions (nmsnmh.c)")
|
|
if (GetGrpMem(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
pRowInfo->TimeStamp - ((pRowInfo->OwnerId == NMSDB_LOCAL_OWNER_ID) ? WinsCnf.RefreshInterval : WinsCnf.VerifyInterval) ,
|
|
pStatusInfo,
|
|
pStatusInfo->fStatic,
|
|
&fIsMem
|
|
) != WINS_SUCCESS)
|
|
{
|
|
|
|
return(WINS_FAILURE);
|
|
|
|
}
|
|
|
|
//
|
|
// If all members are expired, then
|
|
// mark entry in conflict as a
|
|
// TOMBSTONE (for the benefit of
|
|
// ClashAtRegInd and ClashAtReplUniqueR)
|
|
//
|
|
if (pStatusInfo->NodeAdds.NoOfMems == 0)
|
|
{
|
|
pStatusInfo->EntryState_e =
|
|
NMSDB_E_RELEASED;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
pStatusInfo->NodeAdds.NoOfMems = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !NEW_OWID
|
|
pStatusInfo->OwnerId = 0;
|
|
#endif
|
|
|
|
/*
|
|
* Retrieve the owner Id column.
|
|
*/
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&pStatusInfo->OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// Just in case we challenge this entry and it
|
|
// happens to be multihomed, we would need to add
|
|
// it as a member (see ProcAddList).
|
|
//
|
|
if (NMSDB_ENTRY_UNIQUE_M(pStatusInfo->EntTyp))
|
|
{
|
|
pStatusInfo->NodeAdds.Mem[0].OwnerId =
|
|
pStatusInfo->OwnerId;
|
|
//
|
|
// Put the current time stamp as the time
|
|
// stamp of the member. Though not strictly
|
|
// correct, it is ok. We don't
|
|
// need to retrieve the time stamp of the
|
|
// conflicting record this way.
|
|
//
|
|
if (pStatusInfo->OwnerId ==
|
|
NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
pStatusInfo->NodeAdds.Mem[0].TimeStamp
|
|
= pRowInfo->TimeStamp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the conflicting record is owned by the local
|
|
// WINS, we must retrieve the version number. This
|
|
// is used to determine whether the special record
|
|
// storing the highest version number of the
|
|
// local records should be updated (refer:
|
|
// NmsDbUpdateRow, NmsDbSeekNUpd, NmsScvDoScavenging,
|
|
// NmsDbUpdHighestVersNoRec)
|
|
//
|
|
if (pStatusInfo->OwnerId == NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
|
|
//
|
|
// Retrieve the version number
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&(pStatusInfo->VersNo),
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
else //could not seek to the record
|
|
{
|
|
#if 0
|
|
// use the following code only if there is a thread somewhere in WINS that
|
|
// updates the db without first entering the NmsNmhNamRegCrtSec critical
|
|
// section.
|
|
|
|
//
|
|
// For registration done by RPLPULL thread where the version number is not
|
|
// incremented, we do not have to enter the above critical section. Currently
|
|
// we do enter it. If in the future we stop doing so, we will uncomment the
|
|
// following code.
|
|
//
|
|
if (!fWaitDone)
|
|
{
|
|
WINSEVT_LOG_INFO_M(
|
|
WINS_SUCCESS,
|
|
WINS_EVT_CANT_FIND_REC
|
|
);
|
|
Sleep(10); //sleep for 10 msecs to let the other
|
|
//thread commit/rollback the transaction
|
|
//that is inserting a record that caused
|
|
//the conflict
|
|
|
|
|
|
//
|
|
// Set flag to TRUE so that if we get the same
|
|
// error again, we can log an error and raise
|
|
// an exception
|
|
//
|
|
fWaitDone = TRUE;
|
|
continue; //iterate one more time
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* We should never get here. Something major is wrong
|
|
* (probably with Jet)
|
|
*/
|
|
DBGPRINT1(EXC, "NmsDbInsertRowInd: Could not seek to conflicting record. WEIRD. Error is (%d)\n", JetRetStat);
|
|
WINSEVT_LOG_M(JetRetStat, WINS_EVT_F_CANT_FIND_REC);
|
|
ASSERTMSG(0, "SEEK ERROR");
|
|
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
|
|
|
|
} // end of else
|
|
|
|
} //no duplicate
|
|
|
|
CALL_M(JetRetStat);
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
|
|
STATUS
|
|
NmsDbInsertRowGrp(
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
PNMSDB_STAT_INFO_T pStatusInfo
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This function inserts a group name-IP address mapping row in the
|
|
name-IP address mapping table. It first seeks on the name to see
|
|
if there is an entry with that name. if yes, it retrieves the
|
|
information about the conflicting record for the benefit of the
|
|
calling function and returns.
|
|
|
|
Information retrieved includes
|
|
Status -- group/unique
|
|
IP addresses pertaining to the entry
|
|
state -- the state of the record (active/released/tombstone)
|
|
|
|
|
|
Arguments:
|
|
|
|
pRowInfo - Info. about the row to insert
|
|
pStatusInfo - Contains status of the operation + info about the
|
|
conflicting record, if the registration conflicted
|
|
with an entry in the db.
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsNmhNamRegGrp
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
|
|
DWORD FldNo = 0;
|
|
JET_ERR JetRetStat;
|
|
DWORD FlagVal = 0; //flag value of record that is
|
|
//retrieved
|
|
DWORD ActFldLen = 0; //length of fld retrieved
|
|
BOOL fFound = FALSE; //set to TRUE if Address is found in
|
|
//group
|
|
BOOL fWaitDone = FALSE;
|
|
|
|
JET_RETINFO RetInfo;
|
|
JET_SESID SesId;
|
|
JET_TABLEID TblId;
|
|
PWINSTHD_TLS_T pTls;
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
pStatusInfo->StatCode = NMSDB_SUCCESS;
|
|
|
|
//
|
|
// So that we repeat the whole while loop in case we are not
|
|
// able to seek after a conflict
|
|
//
|
|
JetRetStat = UpdateDb(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
JET_prepInsert
|
|
);
|
|
|
|
|
|
if ( JetRetStat == JET_errKeyDuplicate )
|
|
{
|
|
pStatusInfo->StatCode = NMSDB_CONFLICT;
|
|
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo->pName,
|
|
pRowInfo->NameLen,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
if ((JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
)) == JET_errSuccess
|
|
)
|
|
{
|
|
|
|
// retrieve the flags column
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&FlagVal,
|
|
sizeof(FlagVal),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
pStatusInfo->EntryState_e =
|
|
NMSDB_ENTRY_STATE_M(FlagVal);
|
|
pStatusInfo->EntTyp =
|
|
(BYTE)NMSDB_ENTRY_TYPE_M(FlagVal);
|
|
pStatusInfo->fStatic =
|
|
NMSDB_IS_ENTRY_STATIC_M(FlagVal);
|
|
|
|
if (pStatusInfo->EntTyp == NMSDB_UNIQUE_ENTRY)
|
|
{
|
|
/* It is a unique entry*/
|
|
|
|
|
|
FUTURES("Remove this RETINFO thing. Presumably, it is not needed")
|
|
/* It is a unique entry*/
|
|
|
|
RetInfo.itagSequence = 1;
|
|
RetInfo.cbStruct = sizeof(JET_RETINFO);
|
|
RetInfo.ibLongValue = 0;
|
|
|
|
// retrieve the ip address column
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&(pStatusInfo->NodeAdds.Mem[0].Add),
|
|
sizeof(COMM_ADD_T),
|
|
&ActFldLen,
|
|
0,
|
|
&RetInfo
|
|
)
|
|
);
|
|
|
|
pStatusInfo->NodeAdds.NoOfMems = 1;
|
|
}
|
|
else //it is a group entry or a multihomed entry
|
|
{
|
|
|
|
if (pStatusInfo->EntTyp != NMSDB_NORM_GRP_ENTRY)
|
|
{
|
|
//
|
|
// If status is active, we get the
|
|
// group members
|
|
//
|
|
if (pStatusInfo->EntryState_e ==
|
|
NMSDB_E_ACTIVE)
|
|
{
|
|
BOOL fIsMem;
|
|
PERF("If entry in conflict is STATIC, we don't need to get grp members")
|
|
PERF("except maybe for multihomed entries. Checkout Clash functions (nmsnmh.c)")
|
|
if (GetGrpMem(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
pRowInfo->TimeStamp - ((pRowInfo->OwnerId == NMSDB_LOCAL_OWNER_ID) ? WinsCnf.RefreshInterval : WinsCnf.VerifyInterval),
|
|
pStatusInfo,
|
|
pStatusInfo->fStatic,
|
|
&fIsMem
|
|
) != WINS_SUCCESS)
|
|
{
|
|
return(WINS_FAILURE);
|
|
}
|
|
//
|
|
// If all members are expired, then
|
|
// mark entry in conflict as a
|
|
// RELEASED (for the benefit of
|
|
// ClashAtRegGrp and ClashAtReplGrpMemR)
|
|
//
|
|
if (pStatusInfo->NodeAdds.NoOfMems == 0)
|
|
{
|
|
pStatusInfo->EntryState_e =
|
|
NMSDB_E_RELEASED;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
pStatusInfo->NodeAdds.NoOfMems = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#if !NEW_OWID
|
|
pStatusInfo->OwnerId = 0;
|
|
#endif
|
|
/*
|
|
Retrieve the owner Id column.
|
|
*/
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&pStatusInfo->OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// Just in case we challenge this entry and it
|
|
// happens to be multihomed, we would need to add
|
|
// it as a member (see ProcAddList).
|
|
//
|
|
if (NMSDB_ENTRY_UNIQUE_M(pStatusInfo->EntTyp))
|
|
{
|
|
pStatusInfo->NodeAdds.Mem[0].OwnerId =
|
|
pStatusInfo->OwnerId;
|
|
if (pStatusInfo->OwnerId ==
|
|
NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
//
|
|
// Put the current time stamp as the time
|
|
// stamp of the member. Though not strictly
|
|
// correct, it is ok. We don't
|
|
// need to retrieve the time stamp of the
|
|
// conflicting record this way.
|
|
//
|
|
pStatusInfo->NodeAdds.Mem[0].TimeStamp
|
|
= pRowInfo->TimeStamp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the conflicting record is owned by the local
|
|
// WINS, we must retrieve the version number. This
|
|
// is used to determine whether the special record
|
|
// storing the highest version number of the
|
|
// local records should be updated (refer:
|
|
// NmsDbUpdateRow, NmsDbSeekNUpd, NmsScvDoScavenging, // NmsDbUpdHighestVersNoRec)
|
|
//
|
|
if (pStatusInfo->OwnerId == NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
//
|
|
// Retrieve the version number
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&(pStatusInfo->VersNo),
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
|
|
}
|
|
// break; //break out of the while loop
|
|
}
|
|
else
|
|
{
|
|
#if 0
|
|
if (!fWaitDone)
|
|
{
|
|
WINSEVT_LOG_INFO_M(
|
|
WINS_SUCCESS,
|
|
WINS_EVT_CANT_FIND_REC
|
|
);
|
|
|
|
Sleep(10); //sleep for 10 msecs to let
|
|
//the other
|
|
//thread commit/rollback the
|
|
//transaction that is
|
|
//inserting a record that
|
|
//caused a conflict
|
|
|
|
|
|
//
|
|
// Set flag to TRUE so that if we get the same
|
|
// error again, we can log an error and raise
|
|
// an exception
|
|
//
|
|
fWaitDone = TRUE;
|
|
continue; //iterate one more time
|
|
}
|
|
#endif
|
|
/*
|
|
* We should never get here. Something major is wrong.
|
|
* Either our current index is not on the name column or
|
|
* there is something wrong with JET
|
|
*/
|
|
DBGPRINT1(EXC, "NmsDbInsertRowGrp: Could not seek to conflicting record. WEIRD. Error is (%d)\n", JetRetStat);
|
|
ASSERTMSG(0, "SEEK ERROR");
|
|
WINSEVT_LOG_M(JetRetStat, WINS_EVT_F_CANT_FIND_REC);
|
|
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
|
|
|
|
}
|
|
|
|
} // not a duplicate
|
|
|
|
CALL_M(JetRetStat);
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
|
|
STATUS
|
|
NmsDbRelRow(
|
|
IN PNMSDB_ROW_INFO_T pRowInfo,
|
|
OUT PNMSDB_STAT_INFO_T pStatusInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function releases a record in the database. Releasing
|
|
requires
|
|
mark state as released
|
|
update time stamp
|
|
mark self as owner
|
|
|
|
Arguments:
|
|
pRowInfo - Information about the record to release
|
|
pStatusInfo - Status of operation
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsNmhNamRelRow
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
|
|
DWORD FldNo = 0;
|
|
JET_ERR JetRetStat;
|
|
DWORD Ownerid = NMSDB_LOCAL_OWNER_ID;
|
|
#if NEW_OWID
|
|
DWORD OldOwnerId;
|
|
#else
|
|
DWORD OldOwnerId = 0;
|
|
#endif
|
|
DWORD FlagVal = 0; //flag value of record that is retrieved
|
|
DWORD ActFldLen = 0; //length of fld retrieved
|
|
BOOL fFound = FALSE; //set to TRUE if Address is found in group
|
|
BOOL fToRelease = TRUE; //will be changed to false only for
|
|
//a special group
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
JET_RETINFO RetInfo;
|
|
BYTE EntTyp;
|
|
#ifdef WINSDBG
|
|
BOOL fUpd = FALSE;
|
|
#endif
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
pStatusInfo->StatCode = NMSDB_SUCCESS;
|
|
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo->pName,
|
|
pRowInfo->NameLen,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
if ( (JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
)
|
|
) == JET_errRecordNotFound
|
|
)
|
|
{
|
|
/*
|
|
We return success, since the record is not there.
|
|
|
|
This situation can happen under the following
|
|
condition.
|
|
|
|
The client sends a name release to another WINS
|
|
which has not yet got the replica of the record.
|
|
|
|
|
|
In the second case above, returning a positive name release
|
|
request is ok even though the entry has not been released.
|
|
It will eventually get released as a result of it not being
|
|
refreshed or at the occurrence of a conflict.
|
|
|
|
*/
|
|
|
|
NOTE("Currently, NETBT always goes to the local WINS server for registrations")
|
|
NOTE("So, if a record is not in this db, it better not be in netbt tables too")
|
|
NOTE("If NETBT changes the above semantic in the future i.e. starts going")
|
|
NOTE("to a non-local WINS for reg., we should set pStatusInfo->fLocal to TRUE")
|
|
NOTE("here")
|
|
return(WINS_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
if (JetRetStat != JET_errSuccess)
|
|
{
|
|
DBGPRINT1(ERR,
|
|
"NmsDbRelRow: Seek returned Error (%d)\n",
|
|
JetRetStat);
|
|
return(WINS_FAILURE);
|
|
}
|
|
}
|
|
|
|
// retrieve the flags column
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&FlagVal,
|
|
sizeof(FlagVal),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the fLocal flag if this entry was registered by this node
|
|
//
|
|
pStatusInfo->fLocal = NMSDB_IS_ENTRY_LOCAL_M(FlagVal);
|
|
|
|
if (!NMSDB_ENTRY_ACT_M(FlagVal))
|
|
{
|
|
|
|
/*
|
|
The entry is already released. This can happen
|
|
because of the following reasons
|
|
|
|
--client sent a repeat name release since it did not
|
|
get the response to the earlier one (maybe it got
|
|
lost or maybe because of a timing window where WINS
|
|
has sent a response just around the time the client
|
|
does the retry
|
|
|
|
--entry got released due to no refresh (all refreshes got
|
|
lost.
|
|
|
|
|
|
Returning a positive name release is fine. If the client
|
|
has not got the first one (because it got lost, it will get
|
|
the second one). If it has now received the first response,
|
|
it will just ignore the second one
|
|
*/
|
|
|
|
CHECK("Make sure that NBT will ignore the second one")
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
|
|
EntTyp = (BYTE)NMSDB_ENTRY_TYPE_M(FlagVal);
|
|
//
|
|
// If we got a release for a unique entry but the entry
|
|
// we found is a group entry or vice-versa, return
|
|
// NO_SUCH_ROW status.
|
|
//
|
|
if (
|
|
(
|
|
NMSDB_ENTRY_UNIQUE_M(EntTyp)
|
|
&&
|
|
NMSDB_ENTRY_GRP_M(pRowInfo->EntTyp)
|
|
)
|
|
||
|
|
(
|
|
NMSDB_ENTRY_GRP_M(EntTyp)
|
|
&&
|
|
!NMSDB_ENTRY_GRP_M(pRowInfo->EntTyp)
|
|
)
|
|
)
|
|
{
|
|
DBGPRINT0(ERR, "NmsDbRelRow: Request to release a record with a type (unique/group) than the one for which the release was sent has been ignored\n");
|
|
PERF("Remove this logging to increase speed")
|
|
// per bug #336889 remove this
|
|
// WINSEVT_LOG_D_M(WINS_FAILURE, WINS_EVT_REL_TYP_MISMATCH);
|
|
pStatusInfo->StatCode = NMSDB_NO_SUCH_ROW;
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
pStatusInfo->EntTyp = (BYTE)NMSDB_ENTRY_TYPE_M(FlagVal);
|
|
|
|
//
|
|
// If it is a dynamic release request but the entry found is STATIC,
|
|
// we return SUCCESS.
|
|
//
|
|
// Note: Even though the address in the release request may be
|
|
// different from one in the STATIC record, we return SUCCESS.
|
|
//
|
|
// This is to save overhead for the majority of cases (99%) where
|
|
// the addresses are going to be the same.
|
|
//
|
|
if (!pRowInfo->fAdmin && (NMSDB_IS_ENTRY_STATIC_M(FlagVal) &&
|
|
!NMSDB_ENTRY_USER_SPEC_GRP_M(pRowInfo->pName, pStatusInfo->EntTyp)))
|
|
{
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
if (pStatusInfo->EntTyp == NMSDB_UNIQUE_ENTRY)
|
|
{
|
|
/* retrieve the ip address column*/
|
|
|
|
RetInfo.itagSequence = 1;
|
|
RetInfo.cbStruct = sizeof(JET_RETINFO);
|
|
RetInfo.ibLongValue = 0;
|
|
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&(pStatusInfo->NodeAdds.Mem[0].Add),
|
|
sizeof(COMM_ADD_T),
|
|
&ActFldLen,
|
|
0,
|
|
&RetInfo
|
|
)
|
|
);
|
|
|
|
|
|
pStatusInfo->NodeAdds.NoOfMems = 1;
|
|
|
|
//
|
|
// Extract the Node Type from the Flags byte
|
|
//
|
|
pStatusInfo->NodeTyp = (BYTE)NMSDB_NODE_TYPE_M(FlagVal);
|
|
|
|
//
|
|
// if the address of the entry to be released does not
|
|
// match the address of the client requesting the release
|
|
// and it is not an administrative action, we do not release
|
|
// the entry
|
|
//
|
|
if (
|
|
(pRowInfo->pNodeAdd->Add.IPAdd !=
|
|
pStatusInfo->NodeAdds.Mem[0].Add.Add.IPAdd)
|
|
&&
|
|
(!pRowInfo->fAdmin)
|
|
)
|
|
|
|
{
|
|
DBGPRINT3(ERR, "NmsDbRelRow: Request to release a record (%s) with a different IP address (%x) than that in the release request (%x) has been ignored\n", pRowInfo->pName, pRowInfo->pNodeAdd->Add.IPAdd, pStatusInfo->NodeAdds.Mem[0].Add.Add.IPAdd);
|
|
pStatusInfo->StatCode = NMSDB_NO_SUCH_ROW;
|
|
#if 0 //per bug #336875
|
|
if (WinsCnf.LogDetailedEvts)
|
|
{
|
|
WinsEvtLogDetEvt(TRUE, WINS_EVT_REL_ADD_MISMATCH, TEXT("nmsdb"), __LINE__, "sdd", pRowInfo->pName, pRowInfo->pNodeAdd->Add.IPAdd,
|
|
pStatusInfo->NodeAdds.Mem[0].Add.Add.IPAdd);
|
|
}
|
|
#endif
|
|
// WINSEVT_LOG_D_M(WINS_FAILURE, WINS_EVT_REL_ADD_MISMATCH);
|
|
return(WINS_SUCCESS);
|
|
}
|
|
}
|
|
else // it is a group entry (Normal or Special) or a multihomed entry
|
|
{
|
|
//
|
|
// if it is a special group/multihomed entry, we need to do a
|
|
// number of things
|
|
//
|
|
if (!NMSDB_ENTRY_NORM_GRP_M(pStatusInfo->EntTyp))
|
|
{
|
|
BOOL fIsMem;
|
|
|
|
//
|
|
// Init the following fields since they are used to
|
|
// by GetGrpMem (for determining fIsMem)
|
|
//
|
|
pRowInfo->NodeAdds.NoOfMems = 1;
|
|
pRowInfo->NodeAdds.Mem[0].Add = *(pRowInfo->pNodeAdd);
|
|
|
|
//
|
|
// get all non-expired group/multihomed members
|
|
//
|
|
if (GetGrpMem(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
pRowInfo->TimeStamp,
|
|
pStatusInfo,
|
|
NMSDB_IS_ENTRY_STATIC_M(FlagVal),
|
|
&fIsMem
|
|
) != WINS_SUCCESS)
|
|
{
|
|
return(WINS_FAILURE);
|
|
}
|
|
|
|
//
|
|
// If client is not a member of the group (maybe it
|
|
// never registered or if it did, maybe its entry
|
|
// has timed out.) We return SUCCESS
|
|
//
|
|
CHECK("Maybe we should return NO_SUCH_ROW here. This will then result")
|
|
CHECK("in a NAM_ERR being returned to the client. Also, is there any")
|
|
CHECK("need to keep members around even if they have timed out just so")
|
|
CHECK("that we don't release a spec. group due to a request from a client")
|
|
CHECK("that was never a member. ")
|
|
if ((!fIsMem) || (pStatusInfo->NodeAdds.NoOfMems == 0))
|
|
{
|
|
pStatusInfo->StatCode = NMSDB_SUCCESS;
|
|
return(WINS_SUCCESS);
|
|
}
|
|
else //client is a member of the group/multihomed list
|
|
{
|
|
DWORD i;
|
|
DWORD n = 0;
|
|
|
|
//
|
|
// Save the address of the client in a local
|
|
// var.
|
|
//
|
|
COMM_IP_ADD_T IPAdd =
|
|
pRowInfo->NodeAdds.Mem[0].
|
|
Add.Add.IPAdd;
|
|
//
|
|
// Init the no. of mems fields of the address
|
|
// structure to store to 0
|
|
//
|
|
pRowInfo->NodeAdds.NoOfMems = 0;
|
|
|
|
//
|
|
// remove the client from the active list by
|
|
// storing all other members in the NodeAdds
|
|
// field of ROW_INFO_T structure. Note:
|
|
// if there is an address match, we remove
|
|
// the member irrespective of its ownership.
|
|
// Also note: This code is not reachable
|
|
// for a static record (see above)
|
|
// unless it is an admin request.
|
|
//
|
|
for (i = 0;
|
|
i < pStatusInfo->NodeAdds.NoOfMems;
|
|
i++
|
|
)
|
|
{
|
|
if (
|
|
pStatusInfo->NodeAdds.Mem[i].Add.Add.IPAdd
|
|
!= IPAdd )
|
|
{
|
|
pRowInfo->NodeAdds.Mem[n++]
|
|
= pStatusInfo->NodeAdds.Mem[i];
|
|
pRowInfo->NodeAdds.NoOfMems++;
|
|
}
|
|
|
|
}
|
|
//
|
|
// If there is at least one group/multihomed
|
|
// member, we do not release the row
|
|
//
|
|
if (pRowInfo->NodeAdds.NoOfMems != 0)
|
|
{
|
|
fToRelease = FALSE;
|
|
}
|
|
} //end of else
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Retrieve the owner Id column.
|
|
*/
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&OldOwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
try {
|
|
JetRetStat = JetPrepareUpdate(
|
|
SesId,
|
|
TblId,
|
|
JET_prepReplace
|
|
);
|
|
if (
|
|
(JetRetStat != JET_errSuccess)
|
|
&&
|
|
(JetRetStat != JET_wrnNoWriteLock)
|
|
)
|
|
{
|
|
RET_M(JetRetStat);
|
|
}
|
|
|
|
|
|
//
|
|
// If we have to release a record not owned by us, let us change
|
|
// it into a tombstone. This will result in replication of the same.
|
|
// We want this to shorten the db inconsistency window between our
|
|
// db and the db of the WINS that owns this record.
|
|
//
|
|
// Consider the following situation: Client A registers AA at WINS A
|
|
// It then releases AA at WINS B. On a reboot, it registers at WINS A.
|
|
// Subsequent refreshes also go to WINS A. Since AA was active at WINS
|
|
// A when the registration after the release (at B) came in, the
|
|
// version number wouldn't be incremented and so the record will not
|
|
// replicate again. B will continue to have the released record
|
|
// until it becomes a tombstone and gets replicated.
|
|
//
|
|
if (fToRelease)
|
|
{
|
|
//
|
|
// Get rid of released state altogether
|
|
//
|
|
if (OldOwnerId != Ownerid)
|
|
{
|
|
FlagVal |= (NMSDB_E_TOMBSTONE << NMSDB_SHIFT_STATE);
|
|
//
|
|
// Strictly speaking, for a record that has been turned into
|
|
// a tombstone, we should be using the tombstonetimeout value,
|
|
// we don't do that here. Since such a record never went through
|
|
// the released state, we set the expiry to the aggregate of the
|
|
// tombstone interval and tombstone timeout (to doubly safeguard
|
|
// against it getting deleted prematurely - long weekend and
|
|
// everything).
|
|
//
|
|
pRowInfo->TimeStamp +=
|
|
WinsCnf.TombstoneInterval + WinsCnf.TombstoneTimeout;
|
|
DBGPRINT3(DET, "NmsDbRelRow: Changing from ACTIVE TO TOMBSTONE. Name = (%s),Old and new OwnerId (%d/%d)\n",
|
|
pRowInfo->pName, OldOwnerId,Ownerid);
|
|
FUTURES("Use macro in winevt.h. Make it a warning")
|
|
#if 0 //per bug #336889
|
|
if (WinsCnf.LogDetailedEvts > 0)
|
|
{
|
|
WinsEvtLogDetEvt(TRUE, WINS_EVT_REL_DIFF_OWN, NULL, __LINE__, "sd", pRowInfo->pName,
|
|
OldOwnerId);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
FlagVal |= (NMSDB_E_RELEASED << NMSDB_SHIFT_STATE);
|
|
pRowInfo->TimeStamp += WinsCnf.TombstoneInterval;
|
|
}
|
|
}
|
|
else //hit only for a special group/multihomed entry
|
|
{
|
|
|
|
|
|
pRowInfo->TimeStamp += WinsCnf.RefreshInterval;
|
|
//
|
|
//Set the address field with the new member list
|
|
//
|
|
CALL_M( InsertGrpMemsInCol(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
JET_prepReplace
|
|
)
|
|
);
|
|
|
|
}
|
|
/*
|
|
Set flags column
|
|
Even though not required for special groups, we set it
|
|
to save ourselves an if test (an if test will impact 99% of the
|
|
client releases).
|
|
*/
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&FlagVal,
|
|
sizeof(FlagVal),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
//
|
|
// Since we are taking over ownership of this record, we must
|
|
// update the version number also, else there can be a conflict
|
|
//
|
|
if (OldOwnerId != NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
/* Set the owner byte */
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&Ownerid,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
// set the the version number column
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&(pRowInfo->VersNo),
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
#ifdef WINSDBG
|
|
fUpd = TRUE;
|
|
pRowInfo->EntryState_e = NMSDB_E_RELEASED;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the timestamp column */
|
|
CALL_M( JetSetColumn(
|
|
pTls->SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&(pRowInfo->TimeStamp),
|
|
sizeof(DWORD), /*change type to TIME_STAMP_T
|
|
*later*/
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
|
|
#ifndef WINSDBG
|
|
CALL_M(JetUpdate (
|
|
SesId,
|
|
TblId,
|
|
NULL,
|
|
0L,
|
|
NULL
|
|
)
|
|
);
|
|
#else
|
|
JetRetStat = JetUpdate ( SesId, TblId, NULL, 0L, NULL);
|
|
ASSERT(JetRetStat != JET_errKeyDuplicate);
|
|
if (JetRetStat == JET_errKeyDuplicate)
|
|
{
|
|
WinsEvtLogDetEvt(FALSE, WINS_EVT_DATABASE_UPD_ERR, NULL, __LINE__,
|
|
"sdd", pRowInfo->pName, Ownerid, FlagVal);
|
|
}
|
|
CALL_M(JetRetStat);
|
|
#endif
|
|
|
|
} // end of try block
|
|
finally {
|
|
|
|
if (AbnormalTermination())
|
|
{
|
|
CALL_M(JetRollback(SesId, JET_bitRollbackAll));
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetCommitTransaction(SesId, JET_bitCommitFlush));
|
|
if (OldOwnerId != NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
//
|
|
// No need to send any push notification since we do
|
|
// not wish to replicate this change.
|
|
//
|
|
// Also, no need to call NMSNMH_INC_VERS_COUNTER_M since
|
|
// it is ok not to check against threshold if the
|
|
// version number got incremented because of a release.
|
|
//
|
|
NMSNMH_INC_VERS_NO_M(NmsNmhMyMaxVersNo, NmsNmhMyMaxVersNo);
|
|
}
|
|
}
|
|
}
|
|
NMSNMH_UPD_UPD_CTRS_M(fUpd, TRUE, pRowInfo);
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
STATUS
|
|
NmsDbQueryRow(
|
|
IN PNMSDB_ROW_INFO_T pRowInfo,
|
|
OUT PNMSDB_STAT_INFO_T pStatusInfo
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This function queries a record in the database.
|
|
|
|
Arguments:
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsNmhNamQuery
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
|
|
DWORD FldNo = 0;
|
|
DWORD FlagVal = 0; //flag value of record that is retrieved
|
|
DWORD ActFldLen = 0; //length of fld retrieved
|
|
BOOL fFound = FALSE; //set to TRUE if Address is found in group
|
|
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
|
|
pStatusInfo->NodeAdds.NoOfMems = 1;
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
pStatusInfo->StatCode = NMSDB_SUCCESS;
|
|
|
|
CALL_M(JetBeginTransaction(pTls->SesId));
|
|
try {
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo->pName,
|
|
pRowInfo->NameLen,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
if ( JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
) == JET_errSuccess
|
|
)
|
|
{
|
|
|
|
// retrieve the flags column
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&FlagVal,
|
|
sizeof(FlagVal),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
|
|
pStatusInfo->EntTyp = (BYTE)NMSDB_ENTRY_TYPE_M(FlagVal);
|
|
pStatusInfo->fLocal = NMSDB_IS_ENTRY_LOCAL_M(FlagVal);
|
|
pStatusInfo->NodeTyp = (BYTE)((FlagVal & NMSDB_BIT_NODE_TYP) >> NMSDB_SHIFT_NODE_TYP);
|
|
|
|
if (pStatusInfo->EntTyp == NMSDB_UNIQUE_ENTRY)
|
|
{
|
|
/* It is a unique entry*/
|
|
|
|
/*
|
|
* check the flag field to determine if it is
|
|
* released or a tombstone. Get the address if
|
|
* the entry is ACTIVE or if it is an admin query
|
|
*/
|
|
if ((NMSDB_ENTRY_ACT_M(FlagVal)) || pRowInfo->fAdmin)
|
|
{
|
|
|
|
JET_RETINFO RetInfo;
|
|
|
|
/* retrieve the ip address column*/
|
|
|
|
RetInfo.itagSequence = 1;
|
|
RetInfo.cbStruct = sizeof(JET_RETINFO);
|
|
RetInfo.ibLongValue = 0;
|
|
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&(pStatusInfo->NodeAdds.Mem[0].Add),
|
|
sizeof(COMM_ADD_T),
|
|
&ActFldLen,
|
|
0,
|
|
&RetInfo
|
|
)
|
|
);
|
|
|
|
pStatusInfo->NodeAdds.NoOfMems = 1;
|
|
|
|
}
|
|
else // the unique entry is released or a tombstone
|
|
{
|
|
/*
|
|
If the state is anything other than active, return
|
|
no such row
|
|
*/
|
|
pStatusInfo->StatCode = NMSDB_NO_SUCH_ROW;
|
|
|
|
}
|
|
|
|
}
|
|
else // it is a group/multihomed record
|
|
{
|
|
|
|
/*
|
|
Check whether this is a normal group or a special group.
|
|
|
|
For normal group, we return the subnet broadcast
|
|
address. This means that we have to find the subnet
|
|
mask for the network from which the request came.
|
|
|
|
For now, we return all 1s (-1). This
|
|
indicates the broadcast address on the
|
|
local subnet (Vol 1, 2, 3 of Comer for the naive)
|
|
*/
|
|
|
|
if (pStatusInfo->EntTyp == NMSDB_NORM_GRP_ENTRY)
|
|
{
|
|
|
|
DBGPRINT0(FLOW, "Record queried is a normal group record\n");
|
|
//
|
|
// If it is not a TOMBSTONE, return the subnet mask.
|
|
// We return the subnet mask even when the state is
|
|
// RELEASED because the group may be active at another
|
|
// WINS server
|
|
//
|
|
if (!(NMSDB_ENTRY_TOMB_M(FlagVal)) || pRowInfo->fAdmin)
|
|
{
|
|
pStatusInfo->NodeAdds.Mem[0].Add.Add.IPAdd = 0xFFFFFFFF;
|
|
}
|
|
else //state is tombstone
|
|
{
|
|
pStatusInfo->StatCode = NMSDB_NO_SUCH_ROW;
|
|
}
|
|
|
|
}
|
|
else // it is a special group/multihomed entry
|
|
{
|
|
|
|
BOOL fIsMem;
|
|
|
|
DBGPRINT1(FLOW, "Record queried is a %s record\n",
|
|
NMSDB_ENTRY_SPEC_GRP_M(pStatusInfo->EntTyp) ?
|
|
"SPECIAL GROUP" : "MULTIHOMED");
|
|
|
|
#if 0
|
|
//NOTE: No need to do the following, since we don't care about the value of
|
|
//fIsMem returned GetGrpMem()
|
|
pRowInfo->NodeAdds.NoOfMems = 1;
|
|
pRowInfo->NodeAdds.Mem[0].Add = *(pRowInfo->pNodeAdd);
|
|
#endif
|
|
|
|
//
|
|
// We return only the active members.
|
|
//
|
|
// Remember:
|
|
// A special group/multihomed entry is released when
|
|
// all its members have timed out. A member times out
|
|
// only if it is a non-STATIC entry, is owned by the
|
|
// local WINS, and has not been refreshed within the
|
|
// refresh time interval. All owned entries get
|
|
// released if they are not refreshed. A member also
|
|
// gets removed if a release is received for it.
|
|
// Now, an owned multihomed entry/special group can have
|
|
// members owned by other WINS servers. The only member we
|
|
// may get is one that belongs to the local WINS
|
|
// for which the WINS got a release earlier (but
|
|
// the member was not removed)
|
|
//
|
|
|
|
if (NMSDB_ENTRY_ACT_M(FlagVal) || pRowInfo->fAdmin )
|
|
{
|
|
//
|
|
// Get all non-expired members unless it is
|
|
// is a STATIC record in which case get all
|
|
// members regardless of whether or not they
|
|
// have expired.
|
|
//
|
|
// NOTE: For some cases we also want to return expired
|
|
// members. e.g WINSA has name FOO with members (A,B)
|
|
// and WINSB has name FOO with members B. WINSA owns the
|
|
// member B. When B is expired on WINSA and if the replication
|
|
// is broken for extended period of time, then we still
|
|
// want to return member B from WINSA. Consider passing TRUE
|
|
// for the fStatic parameter.
|
|
GetGrpMem(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
pRowInfo->TimeStamp,
|
|
pStatusInfo,
|
|
NMSDB_IS_ENTRY_STATIC_M(FlagVal),
|
|
&fIsMem
|
|
);
|
|
|
|
if ((pStatusInfo->NodeAdds.NoOfMems == 0)
|
|
&& !pRowInfo->fAdmin)
|
|
{
|
|
pStatusInfo->StatCode =
|
|
NMSDB_NO_SUCH_ROW;
|
|
}
|
|
}
|
|
else //special group/multihomed entry is a tombstone
|
|
{
|
|
pStatusInfo->NodeAdds.NoOfMems = 0;
|
|
pStatusInfo->StatCode = NMSDB_NO_SUCH_ROW;
|
|
}
|
|
|
|
//
|
|
// If the group/multihomed entry does not have any
|
|
// members (i.e. all members have timed out, change
|
|
// the state of the entry to RELEASED
|
|
//
|
|
FUTURES("Maybe change the state of the group to released now")
|
|
|
|
} // it is a special group or multihomed entry
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
|
|
//
|
|
// If this function was invoked in an RPC thread and all
|
|
// operation upto now have succeeded, let us get the owner Id and
|
|
// version number of the record
|
|
//
|
|
if ((pRowInfo->fAdmin) && (RetStat == WINS_SUCCESS))
|
|
{
|
|
|
|
pStatusInfo->EntryState_e = NMSDB_ENTRY_STATE_M(FlagVal);
|
|
pStatusInfo->fStatic = NMSDB_IS_ENTRY_STATIC_M(FlagVal);
|
|
|
|
#if !NEW_OWID
|
|
pStatusInfo->OwnerId = 0;
|
|
#endif
|
|
/*
|
|
* Retrieve the owner Id column.
|
|
*/
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&pStatusInfo->OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
//
|
|
// Retrieve the version number
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&(pStatusInfo->VersNo),
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// get the timestamp field
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&(pStatusInfo->TimeStamp),
|
|
sizeof(pStatusInfo->TimeStamp),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
}
|
|
}
|
|
finally {
|
|
CALL_M(JetRollback(pTls->SesId, JET_bitRollbackAll));
|
|
}
|
|
return(RetStat);
|
|
}
|
|
|
|
STATUS
|
|
NmsDbUpdateRow(
|
|
IN PNMSDB_ROW_INFO_T pRowInfo,
|
|
OUT PNMSDB_STAT_INFO_T pStatusInfo
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This function replaces a conflicting row in the database with the
|
|
row passed. It expects the currency to be on the record
|
|
|
|
Arguments:
|
|
pRowInfo - Information about the record to insert/replace
|
|
pStatusInfo - Status of operation and information about the conflicting
|
|
record if the update resulted in a conlfict (only for
|
|
an insert)
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NBT request thread -- NmsNmhNamRegInd()
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
#ifdef WINSDBG
|
|
JET_ERR JetRetStat;
|
|
#endif
|
|
|
|
pTls = TlsGetValue(WinsTlsIndex);
|
|
|
|
// No need to check whether pTls is NON-NULL. It has to be
|
|
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
pStatusInfo->StatCode = NMSDB_SUCCESS;
|
|
|
|
|
|
#ifndef WINSDBG
|
|
/*
|
|
* Replace the row
|
|
*/
|
|
CALL_M(
|
|
UpdateDb(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
JET_prepReplace
|
|
)
|
|
);
|
|
#else
|
|
|
|
JetRetStat = UpdateDb( SesId, TblId, pRowInfo, JET_prepReplace );
|
|
|
|
if (JetRetStat == JET_errKeyDuplicate)
|
|
{
|
|
BYTE Tmp[20];
|
|
WinsEvtLogDetEvt(FALSE, WINS_EVT_DB_ERR, NULL, __LINE__,
|
|
"sssdd", pRowInfo->pName, _itoa(pRowInfo->VersNo.LowPart, Tmp, 10), _itoa(pStatusInfo->VersNo.LowPart, Tmp, 10), pRowInfo->OwnerId, pStatusInfo->OwnerId);
|
|
DBGPRINT5(ERR, "NmsDbUpdateRow: Could not replace row\nName=(%s);Owner id = (%d);Vers. no = (%d)\nNew owner id = (%d); New Vers.No = (%d)\n",
|
|
pRowInfo->pName, pStatusInfo->OwnerId, pStatusInfo->VersNo.LowPart,
|
|
pRowInfo->OwnerId, pRowInfo->VersNo.LowPart);
|
|
|
|
return(WINS_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetRetStat);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// NOTE: This call must be made after the UpdateDb above
|
|
// because otherwise we will need to seek to the record
|
|
// to be replaced
|
|
//
|
|
UpdHighestVersNoRecIfReqd(pTls, pRowInfo, pStatusInfo);
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
STATUS
|
|
NmsDbSeekNUpdateRow(
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
PNMSDB_STAT_INFO_T pStatusInfo
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This function seeks to a conflicting record and then replaces it
|
|
in the database with the row passed.
|
|
|
|
|
|
Arguments:
|
|
pRowInfo - Contains name to query
|
|
pStatusInfo - Information about the name queried
|
|
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
ChlUpdDb (Name Challenge thread) in NmsChl.c
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
Currently, this function is called only by the Name Challenge manager.
|
|
When it starts getting called by another component, we would need
|
|
to make sure that comparison of the owner id. retrieved from the
|
|
row to be replaced with the one we retrieved prior to handing the
|
|
request to the name challenge manager is the correct action for all
|
|
situations.
|
|
--*/
|
|
{
|
|
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
#if NEW_OWID
|
|
DWORD OwnerId;
|
|
#else
|
|
DWORD OwnerId = 0;
|
|
#endif
|
|
DWORD ActFldLen;
|
|
JET_ERR JetRetStat;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
|
|
pTls = TlsGetValue(WinsTlsIndex);
|
|
|
|
//
|
|
// No need to check whether pTls is NON-NULL. It has to be
|
|
//
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
pStatusInfo->StatCode = NMSDB_SUCCESS;
|
|
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo->pName,
|
|
pRowInfo->NameLen,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
if ((JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
)) == JET_errSuccess
|
|
)
|
|
{
|
|
|
|
|
|
//
|
|
// Before replacing the row, let us check whether it is still
|
|
// owned by the same owner. We check this because during the
|
|
// window in which this challenge thread was working, the
|
|
// replicator might have pulled in records from another WINS
|
|
// server and updated the row with another row or a local
|
|
// nbt request might have resulted in the row getting updated
|
|
// (if it was a replica first). In either of the two cases
|
|
// above, we do not want to update the row.
|
|
//
|
|
|
|
/*
|
|
* Retrieve the owner Id column.
|
|
*/
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
if (OwnerId == pStatusInfo->OwnerId)
|
|
{
|
|
|
|
/*
|
|
* Replace the row
|
|
*/
|
|
CALL_M(
|
|
UpdateDb(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
JET_prepReplace
|
|
)
|
|
);
|
|
|
|
//
|
|
// NOTE: This call must be made after the UpdateDb above
|
|
// because otherwise we will need to seek to the record
|
|
// to be replaced
|
|
//
|
|
UpdHighestVersNoRecIfReqd(pTls, pRowInfo, pStatusInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Means that some other thread (other than challenger),
|
|
* deleted the record. It has to be an rpc thread since
|
|
* an NBT thread would release the record, not delete it
|
|
*/
|
|
WINSEVT_LOG_M(JetRetStat, WINS_EVT_F_CANT_FIND_REC);
|
|
RetStat = WINS_FAILURE;
|
|
// WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
|
|
}
|
|
|
|
return(RetStat);
|
|
}
|
|
|
|
|
|
|
|
STATUS
|
|
NmsDbGetDataRecs(
|
|
IN WINS_CLIENT_E Client_e,
|
|
IN OPTIONAL INT ThdPrLvl,
|
|
IN VERS_NO_T MinVersNo,
|
|
IN VERS_NO_T MaxVersNo,
|
|
IN DWORD MaxNoOfRecsReqd,
|
|
IN BOOL fUpToLimit,
|
|
IN BOOL fOnlyReplTomb OPTIONAL,
|
|
IN PNMSSCV_CLUT_T pClutter,
|
|
IN OUT PCOMM_ADD_T pWinsAdd,
|
|
IN BOOL fOnlyDynRecs,
|
|
IN DWORD RplType,
|
|
OUT LPVOID *ppRBuf,
|
|
OUT LPDWORD pRspBufLen,
|
|
OUT LPDWORD pNoOfRecs
|
|
)
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This function returns all the records in the range MinVersNo to
|
|
MaxVersNo that are owned by the WINS server at address pWinsAdd.
|
|
|
|
Arguments:
|
|
Client_e - id of client that called this function (Pull handler in
|
|
replicator or the scavenger thread)
|
|
ThdPrLvl - priority level of the scavenger thread
|
|
MinVersNo, MaxVersNo - range of version numbers to retrieve
|
|
MaxNoOfRecsReqd - Max. number of records required
|
|
fUpToLimit - Set to TRUE, if the max. version number arg is to
|
|
be ignored and records upto the last one in the db
|
|
have to be retrieved
|
|
fOnlyReplTomb - Only tombstones desired (valid if Client_e is NMSSCV)
|
|
pWinsAdd - Wins whose records need to be retrieved (owner WINS)
|
|
ppRbuf - Buffer to contain the records
|
|
pRspBufLen - size of the buffer
|
|
pNoOfRecs - No of records in the buffer
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
DoScavenging(), UpdDb in nmsscv.c,
|
|
HandleSndEntriesReq() in rplpush.c
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function changes the index on the name address table to
|
|
clustered index.
|
|
|
|
This function has grown over time. It needs to be streamlined.
|
|
|
|
--*/
|
|
{
|
|
JET_ERR JetRetStat;
|
|
DWORD OwnerId;
|
|
DWORD ActFldLen; //length of fld retrieved
|
|
VERS_NO_T VersNoDiff;
|
|
VERS_NO_T TmpNoOfEntries;
|
|
LPBYTE pStartBuff;
|
|
DWORD SaveBufLen;
|
|
BYTE EntTyp; //type of entry (unique/group/special group)
|
|
PRPL_REC_ENTRY_T pRspBuf;
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
#if NEW_OWID
|
|
DWORD RecordOwnerId;
|
|
#else
|
|
DWORD RecordOwnerId = 0;
|
|
#endif
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
VERS_NO_T DefNo;
|
|
BYTE Name[NMSDB_MAX_NAM_LEN];
|
|
DWORD InitHeapSize;
|
|
DWORD MemSize;
|
|
|
|
#ifdef WINSDBG
|
|
DWORD StartTime;
|
|
DWORD EndTime;
|
|
#endif
|
|
DWORD CommitCnt = 1; //do not set to any other value
|
|
BOOL fTransCommitted;
|
|
// LPVOID pCallersAdd, pCallersCaller;
|
|
|
|
DBGENTER("NmsDbGetDataRecs\n");
|
|
// RtlGetCallersAddress(&pCallersAdd, &pCallersCaller);
|
|
// DbgPrint("Callers Address = (%x)\nCallersCaller = (%x)\n", pCallersAdd, pCallersCaller);
|
|
|
|
#ifdef WINSDBG
|
|
if (!fOnlyReplTomb)
|
|
{
|
|
struct in_addr InAddr;
|
|
if (!fUpToLimit)
|
|
{
|
|
InAddr.s_addr = htonl(pWinsAdd->Add.IPAdd);
|
|
if (MaxNoOfRecsReqd == 0)
|
|
{
|
|
DBGPRINT5(DET, "NmsDbGetDataRecs:Will retrieve records in the range (%lu %lu) to (%lu %lu) of WINS having address = (%s)\n",
|
|
MinVersNo.HighPart,
|
|
MinVersNo.LowPart,
|
|
MaxVersNo.HighPart,
|
|
MaxVersNo.LowPart,
|
|
inet_ntoa(InAddr)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT4(DET, "NmsDbGetDataRecs:Will retrieve a max. of %d records starting from (%lu %lu) version number of WINS having address = (%s)\n",
|
|
MaxNoOfRecsReqd,
|
|
MinVersNo.HighPart,
|
|
MinVersNo.LowPart,
|
|
inet_ntoa(InAddr)
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pWinsAdd)
|
|
{
|
|
InAddr.s_addr = htonl(pWinsAdd->Add.IPAdd);
|
|
DBGPRINT3(DET, "NmsDbGetDataRecs: Will retrieve all records starting from version no (%d %d) for WINS (%s)\n", MinVersNo.HighPart, MinVersNo.LowPart, inet_ntoa(InAddr));
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// fToLimit = TRUE and fOnlyReplTomb = FALSE means we
|
|
// are interested only in (active) replicas
|
|
//
|
|
DBGPRINT1(DET, "NmsDbGetDataRecs: Will retrieve all active replica records older than verify interval for WINS with owner id = (%d)\n",
|
|
pClutter->OwnerId);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT1(DET, "NmsDbGetDataRecs: Will retrieve %s replica tombstones\n", fUpToLimit ? "all" : "specified range");
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// initialize the default no. that determines the size of the
|
|
// buffer to allocate in case the range specified by the Max and
|
|
// Min Vers. No args is > it
|
|
//
|
|
PERF("Move this to NmsDbInit")
|
|
WINS_ASSIGN_INT_TO_VERS_NO_M(DefNo, INIT_NO_OF_ENTRIES);
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
pTls->HeapHdl = NULL; //make it NULL so that the caller can determine
|
|
//whether this function allocated a heap
|
|
//before returning (normally/abnormally)
|
|
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
|
|
|
|
/*
|
|
allocate a buffer using some rough calculations. Note: The
|
|
calculations help only if the difference between MaxVersNo and
|
|
MinVersNo is less than the predefined number (of records) we use for
|
|
allocating a buffer. if the difference is > this predefined number,
|
|
we use the predefined number since it might still suffice considering
|
|
that there may be gaps between version numbers of records falling
|
|
in the Min-Max range
|
|
*/
|
|
if ((!fOnlyReplTomb) && (!fUpToLimit))
|
|
{
|
|
//
|
|
// If a max. number has been specified, use that one.
|
|
// Currently, only the scavenger thread specifies a non-zero
|
|
// value for MaxNoOfRecsReqd
|
|
//
|
|
if (MaxNoOfRecsReqd == 0)
|
|
{
|
|
|
|
VersNoDiff.QuadPart = LiSub(MaxVersNo,MinVersNo);
|
|
|
|
//
|
|
// If client is the push thread, since we will never send more
|
|
// than RPL_MAX_LIMIT_FOR_RPL records, do not allocate more
|
|
// memory than is required.
|
|
//
|
|
//
|
|
if (Client_e == WINS_E_RPLPUSH)
|
|
{
|
|
LARGE_INTEGER TmpNo;
|
|
WINS_ASSIGN_INT_TO_LI_M(TmpNo, RPL_MAX_LIMIT_FOR_RPL);
|
|
if (LiGtr(VersNoDiff, TmpNo))
|
|
{
|
|
VersNoDiff = TmpNo;
|
|
}
|
|
}
|
|
NMSNMH_INC_VERS_NO_M( VersNoDiff, VersNoDiff );
|
|
}
|
|
else
|
|
{
|
|
VersNoDiff.QuadPart = MaxNoOfRecsReqd;
|
|
}
|
|
|
|
TmpNoOfEntries = LiGtr(VersNoDiff, DefNo) ? DefNo : VersNoDiff;
|
|
}
|
|
else
|
|
{
|
|
TmpNoOfEntries = DefNo;
|
|
}
|
|
|
|
//
|
|
// Store the memory size for the records. Note: This
|
|
// does not contain the memory for the name and addresses
|
|
// (in case of a special group or a multihomed entry). The
|
|
// sizes for these will be added as we store each record.
|
|
//
|
|
MemSize = RPL_REC_ENTRY_SIZE * (TmpNoOfEntries.LowPart + 1);
|
|
*pRspBufLen = MemSize + 10000; //for good measure;
|
|
|
|
|
|
|
|
//
|
|
// We will create a heap with the above amount of memory plus a
|
|
// pad for heap overhead. We add TmpNoOfEntries.LowPart * 17
|
|
// since each record will have memory allocated for the name.
|
|
// Names in general will be 17 bytes long (we attach a NULL at the
|
|
// end when registering names).
|
|
//
|
|
if (Client_e == WINS_E_RPLPUSH)
|
|
{
|
|
InitHeapSize = (*pRspBufLen * 4) + (TmpNoOfEntries.LowPart * 17) + PAD_FOR_REC_HEAP;
|
|
}
|
|
else
|
|
{
|
|
InitHeapSize = *pRspBufLen + (TmpNoOfEntries.LowPart * 17)
|
|
+ PAD_FOR_REC_HEAP;
|
|
}
|
|
|
|
|
|
//
|
|
// Create the heap
|
|
//
|
|
pTls->HeapHdl = WinsMscHeapCreate(0, InitHeapSize);
|
|
|
|
pRspBuf = WinsMscHeapAlloc(pTls->HeapHdl, MemSize);
|
|
|
|
pStartBuff = (LPBYTE)pRspBuf; //save start of buffer
|
|
SaveBufLen = MemSize; //save size of buffer
|
|
*ppRBuf = pStartBuff;
|
|
*pNoOfRecs = 0;
|
|
|
|
//
|
|
// If we are not acquiring just tombstones
|
|
//
|
|
if (!fOnlyReplTomb)
|
|
{
|
|
//
|
|
// Actually, we can call RplFindOwnerId for Scavenger thread
|
|
// We choose not to do so to avoid some overhead -- see the
|
|
// comment in the else block.
|
|
//
|
|
if (Client_e != WINS_E_NMSSCV)
|
|
{
|
|
BOOL fAllocNew = FALSE;
|
|
#if 0
|
|
BOOL fAllocNew =
|
|
(Client_e == WINS_E_WINSRPC) ? FALSE : TRUE;
|
|
//
|
|
// The following function enters a critical section.
|
|
//
|
|
// We do not want this function to allocate an
|
|
// an entry in the OwnAddTbl table for the Wins if we
|
|
// are executing in a RPC thread. We want to add
|
|
// a WINS address - Owner Id mapping in the above table
|
|
// (if not existent) only as a result of normal (as versus
|
|
// administrator initiated) actions of the WINS.
|
|
//
|
|
// NOTE: if there is no entry for the WINS address in the
|
|
// in-memory owner address table, the administrative
|
|
// action to retrieve records for a non-existent WINS will
|
|
// fail later on (as it should). Check out WinsGetDbRecs
|
|
//
|
|
#endif
|
|
try {
|
|
if (RplFindOwnerId(
|
|
pWinsAdd,
|
|
&fAllocNew,
|
|
&OwnerId,
|
|
WINSCNF_E_IGNORE_PREC,
|
|
WINSCNF_LOW_PREC
|
|
) != WINS_SUCCESS
|
|
)
|
|
{
|
|
DBGPRINT1(ERR, "NmsDbGetDataRecs: Could not find owner id of address = (%x)\n", pWinsAdd->Add.IPAdd);
|
|
//
|
|
// The client may not look at the return value, but
|
|
// it will look at the *pNoOfRecs value and thus
|
|
// determine that there are no records.
|
|
//
|
|
return(WINS_FAILURE);
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DWORD ExcCode = GetExceptionCode();
|
|
DBGPRINT1(EXC, "NmsDbGetDataRecs: Got exception %x",
|
|
ExcCode);
|
|
WINSEVT_LOG_M(ExcCode, WINS_EVT_EXC_RETRIEVE_DATA_RECS);
|
|
return(WINS_FAILURE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Executed by scavenger thread. pClutter will not be NULL
|
|
// if we are verifying the validity of old replicas
|
|
//
|
|
if (!pClutter)
|
|
{
|
|
//
|
|
// The scavenger thread calls this function either to
|
|
// get all replica tombstones, to get records owned
|
|
// by the local WINS or verify the validity of old active
|
|
// replicas. We therefore do not need to call the
|
|
// RplFindOwnerId function (not calling it lets us avoid a
|
|
// executing a chunk of code and also saves us from entering
|
|
// a critical section)
|
|
//
|
|
OwnerId = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We are just interested in active replicas that are older
|
|
// than the verify interval
|
|
//
|
|
OwnerId = (BYTE)pClutter->OwnerId;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Tombstones are to be retrieved.
|
|
//
|
|
// Actually we should enter a critical section prior to
|
|
// retrieving the value of NmsDbNoOfOwners since it
|
|
// can be changed by the Pull thread. We choose not to
|
|
// do so in order to save some overhead. Even if we
|
|
// get the wrong value (very low probability), we will
|
|
// know of it when we do the seek. If we get <=1 when
|
|
// it is actually more than 1, it is still ok since we
|
|
// will get the right value next time (or next to next)
|
|
//
|
|
FUTURES("Enter critical section to get NmsDbNoOfOwners. Raise priority")
|
|
FUTURES("before doing so")
|
|
if (NmsDbNoOfOwners > 1)
|
|
{
|
|
//
|
|
// We are interested in getting tombstones of
|
|
// replicas only. Tombstones on entries owned
|
|
// by the local WINS will be retrieved separately
|
|
// (every time we check whether owned entries need
|
|
// to be released or made tombstones)
|
|
//
|
|
OwnerId = 1;
|
|
#if 0
|
|
MinVersNo.LowPart = 0;
|
|
MinVersNo.HighPart = 0;
|
|
#endif
|
|
MinVersNo.QuadPart = 0;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT0(FLOW, "NmsDbGetDataRecs: This DB HAS NO REPLICAS IN IT\n");
|
|
DBGLEAVE("NmsDbGetDataRecs\n");
|
|
|
|
//
|
|
// The buffer allocated above will get deallocated
|
|
// in UpdDb (in nmsscv.c)
|
|
//
|
|
//*ppRBuf = pStartBuff;
|
|
return(WINS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* start a transaction
|
|
*/
|
|
CALL_M( JetBeginTransaction(SesId) );
|
|
fTransCommitted = FALSE;
|
|
try {
|
|
/*
|
|
* Use primary index now
|
|
*/
|
|
CALL_M( JetSetCurrentIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_NAM_ADD_PRIM_INDEX_NAME
|
|
)
|
|
);
|
|
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
JET_bitNewKey //since this is the first
|
|
//data value of the key
|
|
)
|
|
);
|
|
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&MinVersNo,
|
|
sizeof(VERS_NO_T),
|
|
0 //0 for grbit since this is not the
|
|
//first component of the key
|
|
)
|
|
);
|
|
|
|
JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekGE
|
|
);
|
|
|
|
if (JetRetStat == JET_errRecordNotFound)
|
|
{
|
|
//
|
|
// This is an error only if the function was called in the
|
|
// PUSH thread (HandleSndEntriesRsp()). If it was called
|
|
// in the Scavenger thread (DoScavenging()), it may not be an
|
|
// error. This is because when scavenging, we start with
|
|
// the lowest version number possible (1) in specifying a
|
|
// range the size of WinsCnf.ScvChunk. We them make successive
|
|
// calls for getting the next batch of records in equal
|
|
// sized ranges that occur in tandem until we reach the
|
|
// highest version number of owned records as indicated
|
|
// by NmsNmhMyMaxVersNo. It is thus very much possible that
|
|
// the ranges specified at the lower end of the list of
|
|
// ranges are devoid of records
|
|
//
|
|
if (Client_e == WINS_E_RPLPUSH)
|
|
{
|
|
DBGPRINT5(ERR, "Weird. Could not locate even one record in the range (%d %d) - (%d %d) of owner with id (%d)\n",
|
|
MinVersNo.HighPart,
|
|
MinVersNo.LowPart,
|
|
MaxVersNo.HighPart,
|
|
MaxVersNo.LowPart,
|
|
OwnerId);
|
|
|
|
WINSEVT_LOG_M(
|
|
WINS_FAILURE,
|
|
WINS_EVT_CANT_FIND_ANY_REC_IN_RANGE
|
|
);
|
|
|
|
//
|
|
// Don't free memory. It will get freed later by
|
|
// HandleSndEntriesRsp/DoScavenging. In case the caller
|
|
// is HandleSndEntriesRsp(), what will happen is that
|
|
// it will send a response with 0
|
|
// records (i.e. no records). The Pull Pnr will
|
|
// find this out and will continue to function normally
|
|
//
|
|
// The response with 0 records is doing the work of a
|
|
// negative (error) response.
|
|
//
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
#ifdef WINSDBG
|
|
else // has to be WINS_E_NMSSCV or WINS_E_WINSRPC
|
|
{
|
|
DBGPRINT0(DET, "NmsDbGetDataRecs: Did not find even one record in the db. Maybe all got deleted\n");
|
|
}
|
|
#endif
|
|
}
|
|
else //JetSeek did not return JET_errRecordNotFound.
|
|
{
|
|
|
|
CHECK("It may be better to count the number of records first and allocate")
|
|
CHECK(" a buffer big enough to store all of them (i.e. take a hit once")
|
|
CHECK(" than a small hit of an if test in every iteration. ")
|
|
//
|
|
// Do until there are no more records in the database to retrieve
|
|
//
|
|
|
|
//
|
|
// We are assured of there being at least one record since the
|
|
// JetSeek succeeded (if not for the owner we are interested in
|
|
// then for the next one).
|
|
// We can therefore safely use the do .. while() construct
|
|
//
|
|
// *NOT REALLY. It seems that JetSeek can return JET_wrnSeekNE
|
|
// even when there are no records in the db. In such a case,
|
|
// our JetRetrieveColumn will fail with a CurrencyNot there error
|
|
//
|
|
CHECK("Check with IAN JOSE")
|
|
|
|
#ifdef WINSDBG
|
|
//(void)time(&StartTime);
|
|
StartTime = GetTickCount();
|
|
#endif
|
|
do
|
|
{
|
|
//
|
|
// If the number of records has exceeded what can be stored
|
|
// in our buffer, allocate another buffer of double the size
|
|
// and use that.
|
|
//
|
|
if (*pNoOfRecs > TmpNoOfEntries.LowPart)
|
|
{
|
|
UINT_PTR Offset = (LPBYTE)pRspBuf - pStartBuff;
|
|
|
|
//
|
|
// Not a bad place to check whether WINS has been
|
|
// terminated. Scavenger thread can take a long time
|
|
// to go through the entire db if it is large and so
|
|
// a net stop can take a long time to finish. This
|
|
// check here should speed up net stop.
|
|
//
|
|
if (Client_e == WINS_E_NMSSCV)
|
|
{
|
|
WinsMscChkTermEvt(
|
|
#ifdef WINSDBG
|
|
WINS_E_NMSSCV,
|
|
#endif
|
|
TRUE
|
|
);
|
|
|
|
}
|
|
DBGPRINT1(FLOW, "NmsDbGetDataRecs: No of Records (%d) are more than what we can store in our buffer. We will allocate a new one\n", *pNoOfRecs);
|
|
#if 0
|
|
TmpNoOfEntries = LiXMul(TmpNoOfEntries, 2);
|
|
#endif
|
|
|
|
TmpNoOfEntries.QuadPart = TmpNoOfEntries.QuadPart * 2;
|
|
ASSERT(!(TmpNoOfEntries.HighPart & 0x80000000));
|
|
ASSERT(TmpNoOfEntries.LowPart < 0xFFFFFFFF);
|
|
|
|
|
|
MemSize = RPL_REC_ENTRY_SIZE * ((DWORD)TmpNoOfEntries.QuadPart + 1);
|
|
pRspBuf = HeapReAlloc(pTls->HeapHdl,
|
|
HEAP_GENERATE_EXCEPTIONS |
|
|
HEAP_ZERO_MEMORY,
|
|
pStartBuff, MemSize);
|
|
|
|
|
|
DBGPRINT1(DET, "NmsDbGetDataRecs: Doing a realloc in thd\n", pTls->ThdName);
|
|
//
|
|
// Save the start position of the new buffer
|
|
//
|
|
pStartBuff = (LPBYTE)pRspBuf;
|
|
|
|
*ppRBuf = pStartBuff;
|
|
|
|
//
|
|
// Make pRspBuf point to just past the last record
|
|
// inserted
|
|
//
|
|
pRspBuf = (PRPL_REC_ENTRY_T)(pStartBuff + Offset);
|
|
|
|
//
|
|
// Add the length we incremented *pRspBufLen by to
|
|
// the new memory size
|
|
//
|
|
*pRspBufLen = (*pRspBufLen - SaveBufLen) + MemSize;
|
|
|
|
//
|
|
// Store the new length in SaveBufLen
|
|
//
|
|
SaveBufLen = MemSize;
|
|
|
|
}
|
|
|
|
JetRetStat = JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&RecordOwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
#if 0
|
|
//apparently with 118.6 we don't need to execute this code
|
|
//
|
|
// if currency is not on a record, then this means that
|
|
// this is our first iteration of the do loop. JetSeek above
|
|
// must have returned JET_wrnSeekNE. See comment above.
|
|
//
|
|
// A continue will result in us doing a JetMove and getting
|
|
// out of this loop. We do a 'continue' instead of a break
|
|
// since a break would involve a search of the termination
|
|
// handler which is an expensive operation (but then maybe
|
|
// JetMove may also be an expensive operation even though it is
|
|
// done in memory)
|
|
//
|
|
PERF("Is it better to break out of the loop. Check with Ian regarding JetMove")
|
|
if (JetRetStat == JET_errNoCurrentRecord)
|
|
{
|
|
ASSERT(*pNoOfRecs == 0);
|
|
continue;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
//
|
|
// check that we don't have some other error here
|
|
//
|
|
FUTURES("Yet another hack to workaround jet bugs = 7-11-94")
|
|
if (JetRetStat == JET_errRecordDeleted)
|
|
{
|
|
DBGPRINT2(ERR, "Jet Error: JetRetStat is (%d). Line is (%d)\n",
|
|
JetRetStat, __LINE__);
|
|
continue;
|
|
|
|
}
|
|
CALL_M(JetRetStat);
|
|
}
|
|
PERF("In case fOnlyReplTomb is true, retrieve the state field first")
|
|
//
|
|
// if only tombstones are required, it means that we need
|
|
// all tombstones irrespective of owner
|
|
//
|
|
if (!fOnlyReplTomb)
|
|
{
|
|
if (RecordOwnerId != OwnerId )
|
|
{
|
|
//
|
|
// We have exhausted all records for the owner. Break out
|
|
// of the loop
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieve the version number
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&(pRspBuf->VersNo),
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// if only tombstones are required, it means that we need
|
|
// all tombstones irrespective of version number
|
|
//
|
|
if (
|
|
(!fOnlyReplTomb)
|
|
&&
|
|
(!fUpToLimit)
|
|
&&
|
|
LiGtr(pRspBuf->VersNo, MaxVersNo)
|
|
)
|
|
{
|
|
//
|
|
// We have acquired records upto MaxVersNo. Break out
|
|
// of the loop
|
|
//
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieve the flags byte
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&(pRspBuf->Flag),
|
|
sizeof(pRspBuf->Flag),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// if we were asked to retrieve only dynamic records and
|
|
// this record is static, skip it.
|
|
//
|
|
if (fOnlyDynRecs && NMSDB_IS_ENTRY_STATIC_M(pRspBuf->Flag))
|
|
{
|
|
// DBGPRINT0(DET, "NmsDbGetDataRecs: Encountered a STATIC record but were asked to retrieve only dynamic records\n");
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// retrieve the name
|
|
//
|
|
CALL_M(JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_NAME_INDEX].Fid,
|
|
//pRspBuf->Name,
|
|
Name,
|
|
NMSDB_MAX_NAM_LEN,
|
|
&(pRspBuf->NameLen),
|
|
0,
|
|
NULL ) );
|
|
|
|
//
|
|
// if name length is > 255, jet is returning an invalid value.
|
|
// Make the length equal to the max. length we can have for
|
|
// a netbios name. Also, log an event
|
|
//
|
|
if (pRspBuf->NameLen > WINS_MAX_NAME_SZ)
|
|
{
|
|
WINSEVT_LOG_M(pRspBuf->NameLen, WINS_EVT_NAME_TOO_LONG);
|
|
DBGPRINT1(ERR, "NmsDbGetDataRecs: Name length is too long = (%x)\n", pRspBuf->NameLen);
|
|
pRspBuf->NameLen = WINS_MAX_NS_NETBIOS_NAME_LEN;
|
|
}
|
|
|
|
|
|
//
|
|
// This macro will allocate memory and store the name in it
|
|
//
|
|
NMSDB_STORE_NAME_M(pTls, pRspBuf, Name, pRspBuf->NameLen);
|
|
|
|
//
|
|
// We need to retrieve the address field if we are in the
|
|
// PUSH thread or an RPC thread
|
|
//
|
|
if (Client_e != WINS_E_NMSSCV)
|
|
{
|
|
//
|
|
// If the record is released, go to the next record
|
|
//
|
|
if(
|
|
(Client_e == WINS_E_RPLPUSH)
|
|
&&
|
|
(NMSDB_ENTRY_REL_M(pRspBuf->Flag))
|
|
|
|
)
|
|
{
|
|
DBGPRINT0(DET,
|
|
"NmsDbGetDataRecs: ENCOUNTERED A RECORD IN THE RELEASED STATE\n");
|
|
|
|
continue;
|
|
}
|
|
|
|
EntTyp = (BYTE)((pRspBuf->Flag & NMSDB_BIT_ENT_TYP));
|
|
if (
|
|
(EntTyp == NMSDB_UNIQUE_ENTRY)
|
|
||
|
|
(EntTyp == NMSDB_NORM_GRP_ENTRY)
|
|
)
|
|
{
|
|
/* It is a unique entry*/
|
|
pRspBuf->fGrp = (EntTyp == NMSDB_UNIQUE_ENTRY) ?
|
|
FALSE : TRUE;
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&pRspBuf->NodeAdd,
|
|
sizeof(COMM_ADD_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
}
|
|
else // it is a special group or a multihomed entry
|
|
{
|
|
|
|
|
|
//
|
|
// Even if the entry is a multihomed entry, we set the
|
|
// fGrp flag to TRUE so that the formatting function
|
|
// works properly (called by PUSH thread). The EntTyp
|
|
// will be used to decipher whether it is a multihomned
|
|
// entry or not
|
|
//
|
|
FUTURES("Remove this hacky mechanism")
|
|
pRspBuf->fGrp =
|
|
(EntTyp == NMSDB_SPEC_GRP_ENTRY) ? TRUE : FALSE;
|
|
|
|
/*
|
|
* get member addresses.
|
|
*
|
|
* If we are in an RPC thread, we want to get the members
|
|
* even if they are expired. We can do that by
|
|
* passing a TRUE value for the STATIC flag parameter.
|
|
*/
|
|
StoreGrpMems(
|
|
pTls,
|
|
Client_e,
|
|
pRspBuf->pName,
|
|
ThdPrLvl,
|
|
SesId,
|
|
TblId,
|
|
(WINS_E_WINSRPC == Client_e ? TRUE
|
|
: NMSDB_IS_ENTRY_STATIC_M(pRspBuf->Flag)),
|
|
pRspBuf
|
|
);
|
|
|
|
|
|
//
|
|
// if the record is active but has no members,
|
|
// don't send it. It is possible that all
|
|
// members of the group expired after the last scavenging
|
|
// cycle. This record will be marked RELEASED at the next
|
|
// scavenging cycle.
|
|
// For now ignore the record
|
|
//
|
|
if (
|
|
(pRspBuf->NoOfAdds == 0)
|
|
&&
|
|
(NMSDB_ENTRY_ACT_M(pRspBuf->Flag))
|
|
)
|
|
{
|
|
if (Client_e == WINS_E_RPLPUSH)
|
|
{
|
|
DBGPRINT2(FLOW, "NmsDbGetDataRecs: Active Group (Version # %d %d) has no members. So it is not being replicated\n", pRspBuf->VersNo.HighPart, pRspBuf->VersNo.LowPart/*pRspBuf->Name*/);
|
|
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//Must be an RPC thread.
|
|
//Change the state to released so that the
|
|
//record shows up as released when displayed
|
|
//
|
|
NMSDB_CLR_STATE_M(pRspBuf->Flag);
|
|
NMSDB_SET_STATE_M(pRspBuf->Flag, NMSDB_E_RELEASED);
|
|
|
|
}
|
|
}
|
|
} // end of else
|
|
|
|
|
|
//
|
|
// Adjust the size to be passed to the push thread
|
|
//
|
|
if (Client_e == WINS_E_RPLPUSH)
|
|
{
|
|
*pRspBufLen += pRspBuf->NameLen;
|
|
if ((EntTyp == NMSDB_MULTIHOMED_ENTRY) ||
|
|
(EntTyp == NMSDB_SPEC_GRP_ENTRY)
|
|
)
|
|
{
|
|
*pRspBufLen +=
|
|
(pRspBuf->NoOfAdds * sizeof(COMM_ADD_T) * 2);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If client is the RPC thread, retrieve the timestamp
|
|
//
|
|
if (Client_e == WINS_E_WINSRPC)
|
|
{
|
|
//
|
|
// get the timestamp field
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&(pRspBuf->TimeStamp),
|
|
sizeof(pRspBuf->TimeStamp),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
if (!fOnlyDynRecs && NMSDB_IS_ENTRY_STATIC_M(pRspBuf->Flag)
|
|
&& (OwnerId == NMSDB_LOCAL_OWNER_ID) && NMSDB_ENTRY_ACT_M(pRspBuf->Flag))
|
|
{
|
|
pRspBuf->TimeStamp = MAXLONG;
|
|
}
|
|
}
|
|
}
|
|
else //client is the scavenger thread
|
|
{
|
|
#if 0
|
|
//
|
|
// We don't scavenge STATIC records. This record will be
|
|
// static only if fOnlyDynRecs is FALSE. This means we
|
|
// should not skip it. VerifyClutter is taking place
|
|
//
|
|
if (NMSDB_IS_ENTRY_STATIC_M(pRspBuf->Flag))
|
|
{
|
|
|
|
DBGPRINT0(FLOW,
|
|
"NmsDbGetDataRecs: Encountered a STATIC record\n"
|
|
);
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If only tombstones are required and this record is not
|
|
// a tombstone, go to the next record
|
|
//
|
|
if (fOnlyReplTomb && !NMSDB_ENTRY_TOMB_M(pRspBuf->Flag))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// pClutter will not be NULL if this function was called
|
|
// by the scavenger thread to either retrieve replica
|
|
// tombstones or to retrieve replicas for consistency
|
|
// checking
|
|
//
|
|
if (pClutter && !fOnlyReplTomb)
|
|
{
|
|
//
|
|
// Want all replicas
|
|
// for consistency checking
|
|
//
|
|
if ( !pClutter->fAll)
|
|
{
|
|
//
|
|
// just interested in active records
|
|
//
|
|
if (!NMSDB_ENTRY_ACT_M(pRspBuf->Flag))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// get the timestamp field
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&(pRspBuf->TimeStamp),
|
|
sizeof(pRspBuf->TimeStamp),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
|
|
if (pClutter)
|
|
{
|
|
//
|
|
// if we are retrieving clutter, check the time stamp
|
|
// unless this is a static record
|
|
//
|
|
if( !fOnlyReplTomb)
|
|
{
|
|
FUTURES("We need to skip this for owned static records only, not for all")
|
|
// if (!NMSDB_IS_ENTRY_STATIC_M(pRspBuf->Flag))
|
|
{
|
|
//
|
|
// if this record is not old enough, we are not interested
|
|
//
|
|
if (
|
|
pClutter->Age &&
|
|
(pRspBuf->TimeStamp > (DWORD)pClutter->CurrentTime)
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We want replica tombstones.
|
|
//
|
|
if (NMSDB_ENTRY_TOMB_M(pRspBuf->Flag))
|
|
{
|
|
if (pClutter->CurrentTime < (time_t)pRspBuf->TimeStamp)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
} // end of else (Client is the scavenger thread)
|
|
|
|
#if 0
|
|
{
|
|
//if above jet call returns 1004, print this out - for debugging only
|
|
DBGPRINT4(ERR, "NmsDbGetDataRecs: ERROR 1004 OWNER ID=(%d); Version No = (%d %d); Flags = (%d)\n", OwnerId, pRspBuf->VersNo.HighPart, pRspBuf->VersNo.LowPart, pRspBuf->Flag);
|
|
DBGPRINT3(ERR, "NmsDbGetDataRecs: ERROR = 1004; Name = (%s); Len=(%d), Add=(%x)\n", Name, pRspBuf->NameLen, pRspBuf->NodeAdd[0].Add.IPAdd);
|
|
}
|
|
CALL_M(JetRetStat);
|
|
#endif
|
|
|
|
#if 0
|
|
//
|
|
// Apply the RplType filter on the record
|
|
// PDC names and special groups, check if it is a unique/mh
|
|
// non PDC name. If yes, skip it.
|
|
//
|
|
if (
|
|
(RplType & WINSCNF_RPL_SPEC_GRPS_N_PDC) &&
|
|
!NMSDB_ENTRY_SPEC_GRP_M(EntTyp) &&
|
|
!(NMSDB_IS_IT_PDC_NM_M(Name))
|
|
)
|
|
{
|
|
|
|
DBGPRINT2(RPLPUSH, "NmsDbGetDataRecs: non 1B unique record = (%s)[16th char = %x) being skipped\n", Name, Name[15]);
|
|
continue;
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// increment the counter and the pointer to past the last record.
|
|
//
|
|
pRspBuf = (PRPL_REC_ENTRY_T)((LPBYTE)pRspBuf + RPL_REC_ENTRY_SIZE);
|
|
(*pNoOfRecs)++;
|
|
|
|
if (Client_e == WINS_E_RPLPUSH)
|
|
{
|
|
if (*pNoOfRecs == RPL_MAX_LIMIT_FOR_RPL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we have retrieved the max. number asked for, break out of
|
|
// the loop
|
|
//
|
|
if ((MaxNoOfRecsReqd > 0) && (*pNoOfRecs >= MaxNoOfRecsReqd))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If this is the scavenger thread, let us give the version store
|
|
// a breather after a certain number of records have been retrieved
|
|
//
|
|
#if 0
|
|
if ((Client_e == WINS_E_NMSSCV) && (*pNoOfRecs/CommitCnt >= WINSCNF_SCV_CHUNK))
|
|
#endif
|
|
if (*pNoOfRecs/CommitCnt >= MAX_RECS_BEFORE_COMMIT)
|
|
{
|
|
|
|
//
|
|
// Let us commit the transaction to free up the version store
|
|
//
|
|
CALL_M(
|
|
JetCommitTransaction(SesId, JET_bitCommitFlush)
|
|
);
|
|
fTransCommitted = TRUE;
|
|
CommitCnt++;
|
|
CALL_M( JetBeginTransaction(SesId) );
|
|
fTransCommitted = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while(JetMove(SesId, TblId, JET_MoveNext, 0) >= 0);
|
|
#ifdef WINSDBG
|
|
EndTime = GetTickCount();
|
|
DBGPRINT2(TM, "NmsDbGetDataRecs: Retrieved %d records in %d secs\n",
|
|
*pNoOfRecs, StartTime - EndTime);
|
|
#endif
|
|
} // end of else
|
|
|
|
} // end of try {..}
|
|
finally {
|
|
if (AbnormalTermination())
|
|
{
|
|
DWORD EvtCode;
|
|
DBGPRINT0(ERR,
|
|
"NmsDbGetDataRecs: Terminating abnormally\n");
|
|
if (Client_e == WINS_E_WINSRPC)
|
|
{
|
|
EvtCode = WINS_EVT_RPC_EXC;
|
|
}
|
|
else
|
|
{
|
|
EvtCode = (Client_e == WINS_E_RPLPUSH) ?
|
|
WINS_EVT_RPLPUSH_EXC :
|
|
WINS_EVT_SCV_EXC;
|
|
}
|
|
WINSEVT_LOG_M(WINS_FAILURE, EvtCode);
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
//*ppRBuf = pStartBuff;
|
|
DBGPRINT1(FLOW, "NmsDbGetDataRecs:Retrieved %d records\n",
|
|
*pNoOfRecs);
|
|
|
|
//
|
|
// If the no of records retrieved is 0, log an informational
|
|
// message. The reason for 0 records being retrieved could
|
|
// be that all records are released
|
|
//
|
|
if (*pNoOfRecs == 0)
|
|
{
|
|
WINSEVT_STRS_T EvtStrs;
|
|
EvtStrs.NoOfStrs = 1;
|
|
if (Client_e == WINS_E_RPLPUSH)
|
|
{
|
|
//EvtStrs.pStr[0] = TEXT("Replicator Push");
|
|
if (WinsCnf.LogDetailedEvts > 0)
|
|
{
|
|
WinsEvtLogDetEvt(TRUE,
|
|
WINS_EVT_NO_RPL_RECS_RETRIEVED, NULL, __LINE__, "ddddd", pWinsAdd != NULL ? pWinsAdd->Add.IPAdd : 0, MinVersNo.LowPart, MinVersNo.HighPart, MaxVersNo.LowPart, MaxVersNo.HighPart);
|
|
//WINSEVT_LOG_INFO_STR_D_M( WINS_EVT_NO_RPL_RECS_RETRIEVED, &EvtStrs );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Per bug#339152 remove this.
|
|
//EvtStrs.pStr[0] = (Client_e == WINS_E_NMSSCV) ?TEXT("Scavenging") : TEXT("Client");
|
|
//WINSEVT_LOG_INFO_STR_D_M( WINS_EVT_NO_RECS_RETRIEVED, &EvtStrs );
|
|
}
|
|
|
|
}
|
|
//
|
|
// We are done. Let us commit the transaction
|
|
//
|
|
if (!fTransCommitted)
|
|
{
|
|
CALL_M(
|
|
JetCommitTransaction(SesId, JET_bitCommitFlush)
|
|
);
|
|
}
|
|
}
|
|
|
|
DBGLEAVE("NmsDbGetDataRecs\n");
|
|
return(RetStat);
|
|
}
|
|
|
|
|
|
VOID
|
|
StoreGrpMems(
|
|
IN PWINSTHD_TLS_T pTls,
|
|
IN WINS_CLIENT_E Client_e,
|
|
IN LPBYTE pName,
|
|
IN INT ThdPrLvl,
|
|
IN JET_SESID SesId,
|
|
IN JET_TABLEID TblId,
|
|
IN BOOL fStatic,
|
|
IN PRPL_REC_ENTRY_T pRspInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function retrieves all the addresses in the group record
|
|
and stores them in the data structure passed to it
|
|
|
|
Arguments:
|
|
Client_e - Client (indicates the thread) calling this function
|
|
ThdPrLvl - The normal priority level of thread (is looked at only
|
|
if the client is WINS_E_NMSSCV (scavenger thread)
|
|
SesId - Id of this thread's session with the db
|
|
TblId - Id of the name-address table
|
|
fStatic - indicates whether the entry is STATIC
|
|
RspInfo - Contains members of a special group (after this function
|
|
is done)
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
None
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbGetDataRecs
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function assumes that a heap has been created for use by this
|
|
thread. Currently, this function is called only by NmsDbGetDataRecs
|
|
--*/
|
|
|
|
{
|
|
BOOL fIsMem;
|
|
NMSDB_ROW_INFO_T RowInfo;
|
|
NMSDB_STAT_INFO_T StatusInfo;
|
|
DWORD i; //for loop counter
|
|
DWORD n = 0; //indexes NodeAdd array
|
|
PNMSDB_WINS_STATE_E pWinsState_e;
|
|
PCOMM_ADD_T pWinsAdd;
|
|
PVERS_NO_T pStartVersNo;
|
|
PWINS_UID_T pUid;
|
|
|
|
//
|
|
// init to 0
|
|
//
|
|
RowInfo.NodeAdds.Mem[0].Add.Add.IPAdd = 0;
|
|
RowInfo.pName = pName;
|
|
|
|
//
|
|
// Get and store the current time.
|
|
//
|
|
(void)time(&RowInfo.TimeStamp);
|
|
|
|
//
|
|
// get all active group members
|
|
//
|
|
GetGrpMem(
|
|
SesId,
|
|
TblId,
|
|
&RowInfo,
|
|
RowInfo.TimeStamp,
|
|
&StatusInfo,
|
|
fStatic,
|
|
&fIsMem
|
|
);
|
|
|
|
pRspInfo->NoOfAdds = StatusInfo.NodeAdds.NoOfMems;
|
|
|
|
//
|
|
// If we are in the scavenger thread, raise our priority level to
|
|
// normal before entering the critical section.
|
|
//
|
|
if (Client_e == WINS_E_NMSSCV)
|
|
{
|
|
WinsMscSetThreadPriority(
|
|
WinsThdPool.ScvThds[0].ThdHdl,
|
|
THREAD_PRIORITY_NORMAL
|
|
);
|
|
}
|
|
|
|
if (pRspInfo->NoOfAdds > 0)
|
|
{
|
|
//
|
|
// Allocate memory to store group members
|
|
//
|
|
pRspInfo->pNodeAdd = WinsMscHeapAlloc(
|
|
pTls->HeapHdl,
|
|
StatusInfo.NodeAdds.NoOfMems *
|
|
sizeof(COMM_ADD_T) * 2
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pRspInfo->pNodeAdd = NULL;
|
|
}
|
|
|
|
//
|
|
// This critical section guards us against simultaenous updates
|
|
// to the NmsDbOwnAddTbl (accessed by RPL_FIND_ADD_BY_OWNER_ID_M
|
|
// macro) by the PULL thread
|
|
//
|
|
EnterCriticalSection(&NmsDbOwnAddTblCrtSec);
|
|
try {
|
|
//
|
|
// Store the group members
|
|
//
|
|
for (i=0; i<StatusInfo.NodeAdds.NoOfMems; i++)
|
|
{
|
|
RPL_FIND_ADD_BY_OWNER_ID_M(
|
|
StatusInfo.NodeAdds.Mem[i].OwnerId,
|
|
pWinsAdd,
|
|
pWinsState_e,
|
|
pStartVersNo
|
|
);
|
|
//
|
|
// First address is the address of the owner WINS
|
|
// Second address is the address of the member
|
|
//
|
|
*(pRspInfo->pNodeAdd + n) = *pWinsAdd;
|
|
n++;
|
|
*(pRspInfo->pNodeAdd + n) = StatusInfo.NodeAdds.Mem[i].Add;
|
|
n++;
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DWORD ExcCode = GetExceptionCode();
|
|
DBGPRINT1(EXC, "StoreGrpMems. Got Exception %x", ExcCode);
|
|
WINSEVT_LOG_M(ExcCode, WINS_EVT_GRP_MEM_PROC_EXC);
|
|
}
|
|
|
|
LeaveCriticalSection(&NmsDbOwnAddTblCrtSec);
|
|
|
|
if (Client_e == WINS_E_NMSSCV)
|
|
{
|
|
//
|
|
// revert to old priority level
|
|
//
|
|
WinsMscSetThreadPriority(
|
|
WinsThdPool.ScvThds[0].ThdHdl,
|
|
ThdPrLvl
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
STATUS
|
|
CreateTbl(
|
|
JET_DBID DbId,
|
|
JET_SESID SesId,
|
|
JET_TABLEID *pTblId,
|
|
NMSDB_TBL_NAM_E TblNam_e //enumerator value for table to create
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function creates a table.
|
|
|
|
Arguments:
|
|
DbId - Database Id.
|
|
SesId - Session Id.
|
|
pTblId - Id of the table created
|
|
TblNm_e - Identifies the table to create
|
|
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbInit
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
#define LANGID 0x0409
|
|
#define CP 1252
|
|
|
|
BYTE TmpCol[MAX_FIXED_FLD_LEN];
|
|
DWORD FldNo; /*counter for fields */
|
|
JET_TABLEID TblId; /*id of table created*/
|
|
JET_COLUMNDEF columndef;
|
|
|
|
//
|
|
// Init fields of columndef that do not change between additions of
|
|
// columns
|
|
//
|
|
columndef.cbStruct = sizeof(columndef);
|
|
columndef.columnid = 0;
|
|
columndef.cp = CP;
|
|
columndef.langid = LANGID;
|
|
columndef.cbMax = 0;
|
|
columndef.grbit = 0;
|
|
|
|
/*
|
|
Switch on Table Name
|
|
*/
|
|
switch(TblNam_e)
|
|
{
|
|
|
|
/*
|
|
The Name to Address Mapping table needs to be created
|
|
*/
|
|
case(NMSDB_E_NAM_ADD_TBL_NM):
|
|
|
|
/*
|
|
Create the Nam IP address mapping table
|
|
*/
|
|
CALL_M( JetCreateTable(
|
|
SesId,
|
|
DbId,
|
|
NMSDB_NAM_ADD_TBL_NM,
|
|
NMSDB_NAM_ADD_TBL_PGS,
|
|
NMSDB_NAM_ADD_TBL_DENSITY,
|
|
&TblId
|
|
)
|
|
);
|
|
|
|
NOTE("DDL such as AddColumn and CreateIndex on a table in shared access mode")
|
|
NOTE("will return an error unless we are at transaction level 0 (i.e no Begin")
|
|
NOTE("transaction). If done on a table in exclusive mode, it is ok -- Ian ")
|
|
NOTE("10/16/93")
|
|
//
|
|
// In order to open the table with shared access, we need
|
|
// to close the handle returned from CreateTable (this
|
|
// one has deny read access flag set) and open the
|
|
// table for shared access
|
|
//
|
|
CALL_M(JetCloseTable(
|
|
SesId,
|
|
TblId
|
|
)
|
|
);
|
|
|
|
CALL_M(JetOpenTable(
|
|
SesId,
|
|
DbId,
|
|
NMSDB_NAM_ADD_TBL_NM,
|
|
NULL, /*ptr to parameter list; should be
|
|
*non-NULL if a query is being
|
|
*opened*/
|
|
0, /*Length of above parameter list*/
|
|
0, //shared access (no bit set)
|
|
&TblId
|
|
)
|
|
);
|
|
|
|
*pTblId = TblId;
|
|
|
|
/*
|
|
Add columns
|
|
*/
|
|
for ( FldNo=0 ; FldNo < NO_COLS_NAM_ADD_TBL ; ++FldNo )
|
|
{
|
|
|
|
columndef.coltyp = sNamAddTblRow[FldNo].FldTyp;
|
|
CALL_M( JetAddColumn (
|
|
SesId, // user
|
|
TblId, // table id
|
|
sNamAddTblRow[FldNo].pName, // fld name
|
|
&columndef, // columndef
|
|
NULL, // default value
|
|
0, // default value length
|
|
&sNamAddTblRow[FldNo].Fid // field id
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Create clustered index (in ascending order) on the name field.
|
|
*
|
|
* In NT5.0 (Jet600), we do not create the cluster key. The
|
|
* primary index is the one on which Jet clusters. The primary
|
|
* key should be smaller, because in Jet600 Jet uses primary key
|
|
* bookmarks, meaning that the bookmark length will be entirely
|
|
* dependent on the length of the primary key.Jonathan Liem (1/7/97)
|
|
*
|
|
* Rule for creating index:
|
|
*
|
|
* The index key contains a sequence of concatenated
|
|
* column names, in order of key significance, each
|
|
* of which is null terminated and prefixed with either
|
|
* '+' or '-', indicating ascending or descending. The
|
|
* entire sequence must be double null terminated.
|
|
*
|
|
*/
|
|
|
|
sprintf( TmpCol, "+%s",
|
|
sNamAddTblRow[NAM_ADD_NAME_INDEX].pName );
|
|
|
|
TmpCol[ 2 +
|
|
strlen( sNamAddTblRow[NAM_ADD_NAME_INDEX].pName )
|
|
] = '\0';
|
|
|
|
if (DynLoadJetVersion >= DYN_LOAD_JET_600) {
|
|
CALL_M(
|
|
JetCreateIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_NAM_ADD_CLUST_INDEX_NAME, // name of index
|
|
JET_bitIndexPrimary | JET_bitIndexUnique | JET_bitIndexDisallowNull,
|
|
TmpCol,
|
|
3 +
|
|
strlen( sNamAddTblRow[NAM_ADD_NAME_INDEX].pName),
|
|
NMSDB_NAM_ADD_CLUST_INDEX_DENSITY /*% space on each
|
|
page to be used*/
|
|
)
|
|
);
|
|
} else {
|
|
CALL_M(
|
|
JetCreateIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_NAM_ADD_CLUST_INDEX_NAME, // name of index
|
|
JET_bitIndexClustered | JET_bitIndexUnique | JET_bitIndexDisallowNull,
|
|
TmpCol,
|
|
3 +
|
|
strlen( sNamAddTblRow[NAM_ADD_NAME_INDEX].pName),
|
|
NMSDB_NAM_ADD_CLUST_INDEX_DENSITY /*% space on each
|
|
page to be used*/
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
CHECK("What exactly does DENSITY argument do for us")
|
|
|
|
/*
|
|
* Create Primary Index using the ownerid and the version cols
|
|
*/
|
|
sprintf( TmpCol, "+%s",
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].pName
|
|
);
|
|
|
|
sprintf(
|
|
&TmpCol[2 + strlen(sNamAddTblRow[NAM_ADD_OWNERID_INDEX].pName)],
|
|
"+%s", sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].pName
|
|
);
|
|
|
|
TmpCol[ 4 +
|
|
strlen( sNamAddTblRow[NAM_ADD_OWNERID_INDEX].pName ) +
|
|
strlen(sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].pName)
|
|
] = '\0';
|
|
|
|
|
|
if (DynLoadJetVersion >= DYN_LOAD_JET_600) {
|
|
CALL_M( JetCreateIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_NAM_ADD_PRIM_INDEX_NAME, // name of index
|
|
JET_bitIndexUnique, //in jet600 dont need primary index.
|
|
TmpCol,
|
|
5 +
|
|
strlen( sNamAddTblRow[NAM_ADD_OWNERID_INDEX].pName) +
|
|
strlen( sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].pName),
|
|
|
|
NMSDB_NAM_ADD_PRIM_INDEX_DENSITY /*% space on each
|
|
page to be used*/
|
|
)
|
|
);
|
|
|
|
} else {
|
|
CALL_M( JetCreateIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_NAM_ADD_PRIM_INDEX_NAME, // name of index
|
|
JET_bitIndexPrimary, //primary index is unique by def.
|
|
TmpCol,
|
|
5 +
|
|
strlen( sNamAddTblRow[NAM_ADD_OWNERID_INDEX].pName) +
|
|
strlen( sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].pName),
|
|
|
|
NMSDB_NAM_ADD_PRIM_INDEX_DENSITY /*% space on each
|
|
page to be used*/
|
|
)
|
|
);
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case(NMSDB_E_OWN_ADD_TBL_NM):
|
|
|
|
/*
|
|
Create the Owner address mapping table
|
|
*/
|
|
|
|
CALL_M( JetCreateTable(
|
|
SesId,
|
|
DbId,
|
|
NMSDB_OWN_ADD_TBL_NM,
|
|
NMSDB_OWN_ADD_TBL_PGS,
|
|
NMSDB_OWN_ADD_TBL_DENSITY,
|
|
&TblId
|
|
)
|
|
);
|
|
|
|
//
|
|
// In order to open the table with shared access, we need
|
|
// to close the handle returned from CreateTable (this
|
|
// one has deny read access flag set) and open the
|
|
// table for shared access
|
|
//
|
|
CALL_M(JetCloseTable(
|
|
SesId,
|
|
TblId
|
|
)
|
|
);
|
|
|
|
CALL_M(JetOpenTable(
|
|
SesId,
|
|
DbId,
|
|
NMSDB_OWN_ADD_TBL_NM,
|
|
NULL, /*ptr to parameter list; should be
|
|
*non-NULL if a query is being
|
|
*opened*/
|
|
0, /*Length of above parameter list*/
|
|
0, //shared access (no bit set)
|
|
&TblId
|
|
)
|
|
);
|
|
*pTblId = TblId;
|
|
|
|
/*
|
|
Add columns
|
|
*/
|
|
for ( FldNo=0 ; FldNo < NO_COLS_OWN_ADD_TBL ; ++FldNo )
|
|
{
|
|
JET_COLUMNDEF columndef;
|
|
|
|
columndef.cbStruct = sizeof(columndef);
|
|
columndef.columnid = 0;
|
|
columndef.coltyp = sOwnAddTblRow[FldNo].FldTyp;
|
|
columndef.cp = 1252;
|
|
columndef.langid = 0x0409;
|
|
columndef.cbMax = 0;
|
|
columndef.grbit = 0;
|
|
|
|
CALL_M( JetAddColumn(
|
|
SesId, // user
|
|
TblId, // table id
|
|
sOwnAddTblRow[FldNo].pName, // fld name
|
|
&columndef, // columndef
|
|
NULL, // default value
|
|
0, // default value lenght
|
|
&sOwnAddTblRow[FldNo].Fid // field id.
|
|
)
|
|
);
|
|
} //end of for loop
|
|
|
|
|
|
/*
|
|
|
|
|
|
Insertions into this table will be in the order of increasing
|
|
owner ids. with the owner id. 0 always referring to the local
|
|
WINS.
|
|
|
|
The state of an entry in the table can be active or down or
|
|
deleted.
|
|
|
|
As an aside (this comment is out of context here, but anyway..)
|
|
|
|
deleted entries are removed at boot time. Also, all records
|
|
owned by the WINS of a deleted entry are removed from the
|
|
Name Address table at boot time.i This functionality is a
|
|
future enhancement
|
|
|
|
*/
|
|
|
|
/*
|
|
* Create clustered index
|
|
*/
|
|
sprintf( TmpCol, "+%s",
|
|
sOwnAddTblRow[OWN_ADD_OWNERID_INDEX].pName
|
|
);
|
|
|
|
TmpCol[ 2 +
|
|
strlen( sOwnAddTblRow[OWN_ADD_OWNERID_INDEX].pName )] = '\0';
|
|
|
|
if (DynLoadJetVersion >= DYN_LOAD_JET_600) {
|
|
CALL_M( JetCreateIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_OWN_ADD_CLUST_INDEX_NAME, // name of index
|
|
JET_bitIndexPrimary | JET_bitIndexUnique,
|
|
TmpCol,
|
|
3 +
|
|
strlen( sOwnAddTblRow[OWN_ADD_OWNERID_INDEX].pName),
|
|
NMSDB_OWN_ADD_CLUST_INDEX_DENSITY /*% space on each
|
|
page to alloc
|
|
*/
|
|
)
|
|
);
|
|
} else{
|
|
CALL_M( JetCreateIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_OWN_ADD_CLUST_INDEX_NAME, // name of index
|
|
JET_bitIndexClustered | JET_bitIndexUnique,
|
|
TmpCol,
|
|
3 +
|
|
strlen( sOwnAddTblRow[OWN_ADD_OWNERID_INDEX].pName),
|
|
NMSDB_OWN_ADD_CLUST_INDEX_DENSITY /*% space on each
|
|
page to alloc
|
|
*/
|
|
)
|
|
);
|
|
}
|
|
|
|
CHECK("Do we need to set this")
|
|
/*
|
|
* Set the clustered index as the current index
|
|
*/
|
|
CALL_M(
|
|
JetSetCurrentIndex( SesId,
|
|
TblId,
|
|
NMSDB_OWN_ADD_CLUST_INDEX_NAME
|
|
)
|
|
);
|
|
|
|
break;
|
|
default:
|
|
DBGPRINT1(ERR, "CreateTbl: Invalid Tbl id (%d)\n",
|
|
TblNam_e);
|
|
WINSEVT_LOG_M(WINS_FATAL_ERR, WINS_EVT_SFT_ERR);
|
|
return(WINS_FAILURE);
|
|
break;
|
|
} //end of switch
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
STATUS
|
|
InitColInfo (
|
|
JET_SESID SesId,
|
|
JET_TABLEID TblId,
|
|
NMSDB_TBL_NAM_E TblNam_e
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to get information about the different
|
|
columns of a table
|
|
|
|
Arguments:
|
|
SesId - Session Id
|
|
TblId - Id. of open table
|
|
TblNam_e - Indicator or table
|
|
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbInit (Main Thread of the process)
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
JET_COLUMNDEF ColumnDef;
|
|
PFLD_T pRow = NULL;
|
|
DWORD FldNo = 0;
|
|
DWORD NoOfCols = 0;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
|
|
/*
|
|
Switch on Table Name
|
|
*/
|
|
switch(TblNam_e)
|
|
{
|
|
|
|
/*
|
|
The Name to Address Mapping table needs to be created
|
|
*/
|
|
case(NMSDB_E_NAM_ADD_TBL_NM):
|
|
|
|
pRow = sNamAddTblRow;
|
|
NoOfCols = NO_COLS_NAM_ADD_TBL;
|
|
break;
|
|
|
|
case(NMSDB_E_OWN_ADD_TBL_NM):
|
|
|
|
pRow = sOwnAddTblRow;
|
|
NoOfCols = NO_COLS_OWN_ADD_TBL;
|
|
break;
|
|
|
|
default:
|
|
|
|
DBGPRINT1(ERR, "InitColInfo: Invalid Tbl id (%d)\n",
|
|
TblNam_e);
|
|
WINSEVT_LOG_M(WINS_FATAL_ERR, WINS_EVT_SFT_ERR);
|
|
RetStat = WINS_FATAL_ERR;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Get info about columns
|
|
*/
|
|
for ( FldNo=0 ; FldNo < NoOfCols; ++FldNo )
|
|
{
|
|
|
|
CALL_M( JetGetTableColumnInfo (
|
|
SesId, // user session
|
|
TblId, // table id
|
|
pRow[FldNo].pName, // fld name
|
|
&ColumnDef, // columndef
|
|
sizeof(ColumnDef),
|
|
JET_ColInfo //info level 0
|
|
)
|
|
);
|
|
|
|
|
|
pRow[FldNo].Fid = ColumnDef.columnid; // field id
|
|
}
|
|
|
|
return(RetStat);
|
|
}
|
|
|
|
|
|
STATUS
|
|
ReadOwnAddTbl(
|
|
JET_SESID SesId,
|
|
JET_DBID DbId,
|
|
JET_TABLEID TblId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to read all the entries of the Owner - Address
|
|
mapping table into the in-memory data structure
|
|
|
|
It is called at init time
|
|
|
|
Arguments:
|
|
SesId
|
|
DbId
|
|
TblId
|
|
|
|
Externals Used:
|
|
NmsDbOwnAddTbl
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbInit()
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
No need to start a transaction in this since it is called only
|
|
by NmsDbInit (at initialization time)
|
|
--*/
|
|
{
|
|
|
|
|
|
PNMSDB_ADD_STATE_T pOwnAddTbl = NULL;
|
|
DWORD i, n;
|
|
LONG ActFldLen;
|
|
DWORD cOwners = 0;
|
|
JET_ERR JetRetStat;
|
|
#if NEW_OWID
|
|
DWORD OwnerId;
|
|
#else
|
|
DWORD OwnerId = 0;
|
|
#endif
|
|
|
|
DWORD LastOwnerId = 0;
|
|
BOOL fLogged = FALSE;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
|
|
DBGENTER("ReadOwnAddTbl\n");
|
|
|
|
|
|
pOwnAddTbl = pNmsDbOwnAddTbl;
|
|
|
|
/*
|
|
* Setting the index will move the database cursor to the first record
|
|
*in the table.
|
|
*/
|
|
CALL_M(
|
|
JetSetCurrentIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_OWN_ADD_CLUST_INDEX_NAME
|
|
)
|
|
);
|
|
|
|
/*
|
|
* Loop until the end of the table is reached. We are retrieving
|
|
* records in the order of increasing owner ids.
|
|
*/
|
|
do
|
|
{
|
|
|
|
//
|
|
// retrieve the OwnerId column
|
|
//
|
|
JetRetStat =
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_OWNERID_INDEX].Fid,
|
|
&OwnerId,
|
|
OWN_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
|
|
if (JetRetStat == JET_errNoCurrentRecord)
|
|
{
|
|
//
|
|
// If this is not the first iteration of the loop, then
|
|
// there is something seriously wrong. Log an error and
|
|
// raise exception
|
|
//
|
|
if (NmsDbNoOfOwners != 0)
|
|
{
|
|
DBGPRINT0(EXC,
|
|
"There is no current record to retrieve from\n");
|
|
WINSEVT_LOG_M(JetRetStat, WINS_EVT_SFT_ERR);
|
|
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT0(ERR,
|
|
"ReadOwnAddTbl: There are no records in this table.");
|
|
WINSEVT_LOG_INFO_M(WINS_SUCCESS, WINS_EVT_NO_RECS_IN_OWN_ADD_TBL);
|
|
}
|
|
|
|
break; //break out of the loop
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetRetStat);
|
|
}
|
|
|
|
// the (OwnerId<->Addr) table is not large enough to contain a slot at index OwnerId.
|
|
// the table has to be enlarged in order to cover this index.
|
|
if (NmsDbTotNoOfSlots <= OwnerId)
|
|
{
|
|
DWORD newNoOfSlots = max(NmsDbTotNoOfSlots*2, OwnerId+1);
|
|
|
|
WINSMSC_REALLOC_M(
|
|
sizeof(NMSDB_ADD_STATE_T) * newNoOfSlots,
|
|
&pOwnAddTbl);
|
|
|
|
pNmsDbOwnAddTbl = pOwnAddTbl;
|
|
NmsDbTotNoOfSlots = newNoOfSlots;
|
|
|
|
// Enlarge the (OwnerId<->VersNo) table if it is not at least as large as (OwnerId<->Addr) table.
|
|
if (RplPullMaxNoOfWins < NmsDbTotNoOfSlots)
|
|
{
|
|
RplPullAllocVersNoArray(&pRplPullOwnerVersNo, NmsDbTotNoOfSlots);
|
|
RplPullMaxNoOfWins = NmsDbTotNoOfSlots;
|
|
}
|
|
|
|
DBGPRINT2(
|
|
DET,
|
|
"ReadOwnAddTbl: Table sizes updated: (OwnerId<->Addr)[%d]; (OwnerId<->VersNo)[%d]\n",
|
|
NmsDbTotNoOfSlots,
|
|
RplPullMaxNoOfWins);
|
|
}
|
|
|
|
//
|
|
// If this is the first wins server's owner id then this has
|
|
// to be zero.
|
|
//
|
|
if (cOwners == 0)
|
|
{
|
|
ASSERT(OwnerId == 0);
|
|
if (OwnerId > 0)
|
|
{
|
|
DBGPRINT1(ERR, "Database error. The first owner in the owner-add table has owner id of (%d)\n", OwnerId);
|
|
|
|
WINSEVT_LOG_M(
|
|
WINS_FAILURE,
|
|
WINS_EVT_DB_INCONSISTENT
|
|
);
|
|
|
|
WINS_RAISE_EXC_M(WINS_EXC_DB_INCONSISTENT);
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Mark all entries in NmsDbOwnerAddTbl for which we did
|
|
// not find an owner id as deleted.
|
|
//
|
|
for (i = LastOwnerId + 1; i < OwnerId; i++)
|
|
{
|
|
(pNmsDbOwnAddTbl + i)->WinsState_e = NMSDB_E_WINS_DELETED;
|
|
}
|
|
}
|
|
|
|
// retrieve the address column
|
|
JetRetStat =
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_ADDRESS_INDEX].Fid,
|
|
&((pNmsDbOwnAddTbl + OwnerId)->WinsAdd),
|
|
sizeof(COMM_ADD_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
DBGPRINT2(INIT, "ReadOwnAddTable: Owner Id (%d) - Address (%x)\n",
|
|
OwnerId, (pNmsDbOwnAddTbl + OwnerId)->WinsAdd.Add.IPAdd);
|
|
|
|
// retrieve the state column
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_STATE_INDEX].Fid,
|
|
&((pNmsDbOwnAddTbl + OwnerId)->WinsState_e),
|
|
sizeof(BYTE),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
// retrieve the version number column
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_VERSIONNO_INDEX].Fid,
|
|
&((pNmsDbOwnAddTbl + OwnerId)->StartVersNo),
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
// retrieve the Uid column
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_UID_INDEX].Fid,
|
|
&((pNmsDbOwnAddTbl + OwnerId)->Uid),
|
|
sizeof(WINS_UID_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
// pOwnAddTbl++; //increment ptr to point to next array element
|
|
LastOwnerId = OwnerId;
|
|
cOwners++;
|
|
|
|
} while(
|
|
JetMove(
|
|
SesId,
|
|
TblId,
|
|
JET_MoveNext,
|
|
0 //grbit - use default (i.e. we want next record
|
|
) >= 0
|
|
);
|
|
|
|
//
|
|
// Compare the count of owners found in the Owner-Address mapping
|
|
// table with the count we determined from the Name-Address mapping
|
|
// table (see GetMaxVersNos()). If the count is less
|
|
// the database is in an inconsistent state. This can
|
|
// mean any of the following:
|
|
//
|
|
// 1) WINS crashed in the middle of replication and recovery was not
|
|
// done properly prior to this invocation
|
|
//
|
|
// 2) The database got trashed due to some other external factors.
|
|
//
|
|
// This error condition is serious enough to warrant an exception.
|
|
// This should terminate WINS.
|
|
//
|
|
// The count can be more but not less. This is because when a
|
|
// WINS comes up, it registers itself in the Owner-Address mapping
|
|
// table. So it is possible that it might have gone down before
|
|
// registering anything. Also, it is possible for all records owned
|
|
// by a WINS server to be deleted.
|
|
//
|
|
if (cOwners < NmsDbNoOfOwners)
|
|
{
|
|
DBGPRINT2(ERR, "Database is inconsistent. The number of owners in the nam-add table (%d) is > in the own-add table (%d)\n",
|
|
NmsDbNoOfOwners,
|
|
cOwners);
|
|
|
|
WINSEVT_LOG_M(
|
|
WINS_FAILURE,
|
|
WINS_EVT_DB_INCONSISTENT
|
|
);
|
|
|
|
WINS_RAISE_EXC_M(WINS_EXC_DB_INCONSISTENT);
|
|
}
|
|
|
|
//
|
|
// Set the global equal to the number of owner records found in
|
|
// the owner-address table. If the global is < Cowners it means that
|
|
// the records owned by one or more WINS servers whose addresses were
|
|
// found in the owner - address mapping table have expired in our
|
|
// name - address mapping table.
|
|
//
|
|
#if 0
|
|
FUTURES("Do not include the WINS server that have a non-active state in the")
|
|
FUTURES("cOwners count")
|
|
NmsDbNoOfOwners = cOwners;
|
|
#endif
|
|
//
|
|
// Set the global to 1 more than the highest owner id found. This
|
|
// is done because we use this global to go over all entries in
|
|
// the NmsDbOwnAddTbl table (at several places - for example,
|
|
// RplFindOwnerId)
|
|
//
|
|
NmsDbNoOfOwners = OwnerId + 1;
|
|
|
|
//
|
|
// Do a sanity check. Make sure that there is no owner id with address
|
|
// same as ours. If there is such an owner id, mark the state as
|
|
// deleted.
|
|
//
|
|
// If the db at WINS A is used at WINS B and WINS A was and is a
|
|
// a partner of WINS B, we will have this situation. WINS B will
|
|
// see its records that got replicated to WINS A in the table at
|
|
// a non-zero (i.e. non-local partner) index. The 0th index is
|
|
// always claimed by the local WINS (WINS B in this example), so
|
|
// we can not have another index with the same address. Having it
|
|
// will cause clutter and also some unnecessary overhead at replication
|
|
// where a partner that gets the mappings can ask for version numbers
|
|
// that don't exist (if highest version number of records at the
|
|
// non-zero index is > that at 0 index). Admitted that eventually,
|
|
// the prior stated situation will no longer exist since the max.
|
|
// version number at index 0 will become > that at the non-zero index.
|
|
//
|
|
DBGPRINT0(DET, "ReadOwnAddTbl: Do a sanity check on the list of owners\n");
|
|
for (i = 1; i < NmsDbNoOfOwners; i++)
|
|
{
|
|
//
|
|
// If address is same as ours and state is ACTIVE, mark it
|
|
// deleted and get rid of all the database records.
|
|
//
|
|
if (
|
|
(WINSMSC_COMPARE_MEMORY_M(&(pNmsDbOwnAddTbl+i)->WinsAdd,
|
|
&NmsLocalAdd, sizeof(COMM_ADD_T))
|
|
== sizeof(COMM_ADD_T))
|
|
&&
|
|
((pNmsDbOwnAddTbl+i)->WinsState_e == NMSDB_E_WINS_ACTIVE)
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
// Tell the sc. to wait since ObliterateWins can take
|
|
// a long time.
|
|
//
|
|
ENmsWinsUpdateStatus(MSECS_WAIT_WHEN_DEL_WINS);
|
|
RetStat = ObliterateWins(i, &(pNmsDbOwnAddTbl+i)->WinsAdd);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for other duplicates
|
|
//
|
|
for (i = 1; i < NmsDbNoOfOwners; i++)
|
|
{
|
|
DWORD OwnerIdToDel;
|
|
for (n = i + 1; n < NmsDbNoOfOwners; n++)
|
|
{
|
|
if ((WINSMSC_COMPARE_MEMORY_M(&(pNmsDbOwnAddTbl+i)->WinsAdd,
|
|
&(pNmsDbOwnAddTbl+n)->WinsAdd, sizeof(COMM_ADD_T))
|
|
== sizeof(COMM_ADD_T))
|
|
&&
|
|
((pNmsDbOwnAddTbl+i)->WinsState_e ==
|
|
(pNmsDbOwnAddTbl+n)->WinsState_e)
|
|
)
|
|
{
|
|
if ( (pNmsDbOwnAddTbl+i)->WinsState_e == NMSDB_E_WINS_ACTIVE)
|
|
{
|
|
if (!fLogged)
|
|
{
|
|
WINSEVT_LOG_M(WINS_FAILURE,
|
|
WINS_EVT_DUP_ENTRY_IN_DB);
|
|
fLogged = TRUE;
|
|
|
|
}
|
|
OwnerIdToDel =
|
|
LiLeq((pRplPullOwnerVersNo+i)->VersNo,
|
|
(pRplPullOwnerVersNo+n)->VersNo) ? i : n;
|
|
|
|
ENmsWinsUpdateStatus(MSECS_WAIT_WHEN_DEL_WINS);
|
|
RetStat = ObliterateWins(OwnerIdToDel,
|
|
&(pNmsDbOwnAddTbl+OwnerIdToDel)->WinsAdd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
DBGPRINT1(DET, "ReadOwnAddTbl. No of owners found = (%d)\n", NmsDbNoOfOwners);
|
|
return(RetStat);
|
|
}
|
|
|
|
STATUS
|
|
ObliterateWins(
|
|
DWORD OwnerToDel,
|
|
PCOMM_ADD_T pWinsAdd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function gets rid of all information pertaining to a WINS.
|
|
|
|
Arguments:
|
|
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function assumes that it is being called at init time. So, when
|
|
calling NmsDbDelDataRecs, it does not request the same to enter a
|
|
critical section
|
|
--*/
|
|
|
|
{
|
|
VERS_NO_T MinVersNo;
|
|
VERS_NO_T MaxVersNo;
|
|
WINS_ASSIGN_INT_TO_LI_M(MinVersNo, 0);
|
|
|
|
DBGENTER("ObliterateWins\n");
|
|
//
|
|
// Set MaxVersNo to 0 also so that all records get
|
|
// deleted
|
|
//
|
|
MaxVersNo = MinVersNo;
|
|
|
|
WinsEvtLogDetEvt(TRUE, WINS_EVT_DUP_ENTRY_DEL, NULL, __LINE__, "ds", OwnerToDel, pWinsAdd->Add.IPAdd);
|
|
|
|
(pNmsDbOwnAddTbl+OwnerToDel)->WinsState_e = NMSDB_E_WINS_DELETED;
|
|
NmsDbWriteOwnAddTbl(
|
|
NMSDB_E_DELETE_REC,
|
|
OwnerToDel,
|
|
NULL,
|
|
NMSDB_E_WINS_DELETED,
|
|
NULL, NULL
|
|
);
|
|
//
|
|
// delete all the records in the database.
|
|
//
|
|
if (NmsDbDelDataRecs( OwnerToDel, MinVersNo, MaxVersNo, FALSE, FALSE) != WINS_SUCCESS) {
|
|
return(WINS_FAILURE);
|
|
}
|
|
|
|
|
|
WINS_ASSIGN_INT_TO_VERS_NO_M((pRplPullOwnerVersNo+OwnerToDel)->VersNo, 0);
|
|
WINS_ASSIGN_INT_TO_VERS_NO_M((pRplPullOwnerVersNo+OwnerToDel)->StartVersNo, 0);
|
|
//(pRplPullOwnerVersNo+OwnerToDel)->OldUid = 0;
|
|
|
|
WINSEVT_LOG_INFO_M(WINS_SUCCESS, WINS_EVT_WINS_ENTRY_DELETED);
|
|
|
|
DBGLEAVE("ObliterateWins\n");
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
STATUS
|
|
NmsDbWriteOwnAddTbl (
|
|
IN NMSDB_TBL_ACTION_E TblAct_e,
|
|
IN DWORD OwnerId,
|
|
IN PCOMM_ADD_T pWinsAdd,
|
|
IN NMSDB_WINS_STATE_E WinsState_e,
|
|
IN PVERS_NO_T pStartVersNo,
|
|
IN PWINS_UID_T pUid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to insert or modify a record in the
|
|
owner id to address mapping table
|
|
|
|
Arguments:
|
|
TblAct_e - the action to perform (Insert, delete, modify)
|
|
OwnerId - id of owner
|
|
pWinsAdd - Address of owner (can be NULL when action is to delete)
|
|
WinsState_e - State of record in the table
|
|
pStartVersNo - version number this WINS started from
|
|
|
|
Externals Used:
|
|
NmsDbNoOfOwners
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
InitOwnAddTbl() in commapi.c, RplFindOwnerId
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
|
|
JET_ERR JetRetStat;
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
DBGPRINT2(FLOW, "ENTER: WriteOwnAddTbl. Action = (%d) for Owner id = (%d)\n", TblAct_e, OwnerId);
|
|
|
|
TblId = pTls->OwnAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
switch(TblAct_e)
|
|
{
|
|
case(NMSDB_E_INSERT_REC):
|
|
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
try {
|
|
CALL_M(JetPrepareUpdate(
|
|
SesId,
|
|
TblId,
|
|
JET_prepInsert
|
|
)
|
|
);
|
|
|
|
|
|
// add first column (ownerid field)
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_OWNERID_INDEX].Fid,
|
|
&OwnerId,
|
|
OWN_ADD_OWNERID_SIZE,
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
// add 2nd column (this is the address field)
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_ADDRESS_INDEX].Fid,
|
|
pWinsAdd,
|
|
sizeof(COMM_ADD_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
|
|
// add the 3rd column (this is the state byte
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_STATE_INDEX].Fid,
|
|
&WinsState_e,
|
|
sizeof(BYTE),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
// add the 4th column (this is the Vers. No
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_VERSIONNO_INDEX].Fid,
|
|
pStartVersNo,
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
// add the 5th column (this is the Uid)
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_UID_INDEX].Fid,
|
|
pUid,
|
|
sizeof(WINS_UID_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
CALL_M( JetUpdate (
|
|
SesId,
|
|
TblId,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
));
|
|
}
|
|
finally {
|
|
if (AbnormalTermination())
|
|
{
|
|
DBGPRINT0(ERR,
|
|
"NmsDbWriteOwnAddTbl: Could not insert record in Owner to Address Mapping Tbl\n");
|
|
WINSEVT_LOG_M(
|
|
WINS_FAILURE,
|
|
WINS_EVT_CONFLICT_OWN_ADD_TBL
|
|
);
|
|
CALL_M(JetRollback(SesId, JET_bitRollbackAll));
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
NmsDbNoOfOwners++;
|
|
CALL_M(JetCommitTransaction(SesId,
|
|
JET_bitCommitFlush));
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
//
|
|
// This case will be executed as a result of
|
|
// administrative actions or when the database (owner-address
|
|
// mapping table) shows that it was used earlier by a WINS
|
|
// at a different address (see ReadOwnAddTbl())
|
|
//
|
|
case(NMSDB_E_MODIFY_REC):
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&OwnerId,
|
|
OWN_ADD_OWNERID_SIZE,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
if ( JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
) == JET_errSuccess
|
|
)
|
|
{
|
|
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
|
|
try {
|
|
JetRetStat = JetPrepareUpdate(
|
|
SesId,
|
|
TblId,
|
|
JET_prepReplace
|
|
);
|
|
if (
|
|
(JetRetStat != JET_errSuccess)
|
|
&&
|
|
(JetRetStat != JET_wrnNoWriteLock)
|
|
)
|
|
{
|
|
RET_M(JetRetStat);
|
|
}
|
|
|
|
// add 2nd column (this is the address field)
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_ADDRESS_INDEX].Fid,
|
|
pWinsAdd,
|
|
sizeof(COMM_ADD_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
|
|
// add the 3rd column (this is the state byte
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_STATE_INDEX].Fid,
|
|
&WinsState_e,
|
|
sizeof(BYTE),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
// add the 4th column (this is the Vers. No
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_VERSIONNO_INDEX].Fid,
|
|
pStartVersNo,
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
// add the 5th column (this is the Uid)
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sOwnAddTblRow[OWN_ADD_UID_INDEX].Fid,
|
|
pUid,
|
|
sizeof(WINS_UID_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
CALL_M( JetUpdate (
|
|
SesId,
|
|
TblId,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
));
|
|
|
|
}
|
|
finally {
|
|
if (AbnormalTermination())
|
|
{
|
|
DBGPRINT0(ERR,
|
|
"NmsDbWriteOwnAddTbl: Could not modify record in Owner to Address Mapping Tbl\n");
|
|
WINSEVT_LOG_M(
|
|
WINS_FAILURE,
|
|
WINS_EVT_CONFLICT_OWN_ADD_TBL
|
|
);
|
|
CALL_M(JetRollback(SesId,
|
|
JET_bitRollbackAll));
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetCommitTransaction(SesId,
|
|
JET_bitCommitFlush));
|
|
|
|
}
|
|
}
|
|
}
|
|
else //did not find record
|
|
{
|
|
DBGPRINT0(EXC, "NmsDbOwnAddTbl: Weird: Could not seek to a record is to be modified\n");
|
|
WINSEVT_LOG_M(
|
|
WINS_FAILURE,
|
|
WINS_EVT_SFT_ERR
|
|
);
|
|
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
|
|
|
|
}
|
|
break;
|
|
|
|
case(NMSDB_E_DELETE_REC):
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&OwnerId,
|
|
OWN_ADD_OWNERID_SIZE,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
if ( JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
) == JET_errSuccess
|
|
)
|
|
{
|
|
try {
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
CALL_M(JetDelete(SesId, TblId));
|
|
DBGPRINT1(SCV, "WriteOwnAddTbl: Deleted owner id = (%d) from table\n", OwnerId);
|
|
}
|
|
finally {
|
|
if (AbnormalTermination())
|
|
{
|
|
DBGPRINT0(ERR,
|
|
"NmsDbWriteOwnAddTbl: Could not delete record in Owner to Address Mapping Tbl\n");
|
|
WINSEVT_LOG_M(
|
|
WINS_FAILURE,
|
|
WINS_EVT_CONFLICT_OWN_ADD_TBL
|
|
);
|
|
CALL_M(JetRollback(SesId,
|
|
JET_bitRollbackAll));
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// NOTE: Do not decrement
|
|
// NmsDbNoOfOwners since that indicates
|
|
// the number of WINS owners in the
|
|
// in-memory table (in all states)
|
|
//
|
|
CALL_M(JetCommitTransaction(SesId,
|
|
JET_bitCommitFlush));
|
|
|
|
}
|
|
} // end of finally
|
|
}
|
|
else //did not find record
|
|
{
|
|
DBGPRINT0(EXC, "NmsDbOwnAddTbl: Weird: Could not seek to a record to be deleted \n");
|
|
WINS_RAISE_EXC_M(WINS_EXC_FATAL_ERR);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGPRINT1(ERR, "Invalid Action Code - (%d)\n", TblAct_e);
|
|
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR);
|
|
RetStat = WINS_FAILURE;
|
|
break;
|
|
|
|
}
|
|
|
|
DBGLEAVE("WriteOwnAddTbl\n");
|
|
return(RetStat);
|
|
}
|
|
|
|
VOID
|
|
NmsDbThdInit(
|
|
WINS_CLIENT_E Client_e
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called by each thread that wishes to init with
|
|
the database.
|
|
|
|
Arguments:
|
|
Client_e - indicates which thread it is
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
The init functions of the various threads
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function is not to be called by the manin thread of the process
|
|
That thread calls the NmsDbInit function.
|
|
--*/
|
|
|
|
{
|
|
PWINSTHD_TLS_T pTls = NULL;
|
|
DWORD Error = 0;
|
|
BOOL fRetVal = TRUE;
|
|
|
|
WinsMscAlloc(sizeof(WINSTHD_TLS_T), &pTls);
|
|
#ifdef WINSDBG
|
|
pTls->Client_e = Client_e;
|
|
#endif
|
|
|
|
//
|
|
// Start a session.
|
|
//
|
|
FUTURES("When security story regarding JET is complete, we might want to")
|
|
FUTURES("change the following. Until then, this should do")
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetBeginSession(
|
|
sJetInstance,
|
|
&pTls->SesId,
|
|
NAMUSR,
|
|
PASSWD
|
|
)
|
|
);
|
|
|
|
//
|
|
// Open the database
|
|
//
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetOpenDatabase(
|
|
pTls->SesId,
|
|
//NmsDbDatabaseFileName,
|
|
WinsCnf.pWinsDb,
|
|
NULL, /*the default engine*/
|
|
&pTls->DbId,
|
|
0 //shared access
|
|
)
|
|
);
|
|
|
|
/*
|
|
* Let us set the TLS storage
|
|
*/
|
|
fRetVal = TlsSetValue(WinsTlsIndex, pTls);
|
|
|
|
if (!fRetVal)
|
|
{
|
|
Error = GetLastError();
|
|
WINSEVT_LOG_M(Error, WINS_EVT_CANT_INIT_W_DB);
|
|
WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// RPC threads come and go. Since the count NmsTotalTermThdCnt
|
|
// represents the number of threads that need to be terminated
|
|
// at process termination time we include only those threads
|
|
// that we are guaranteed to have in the process (with active
|
|
// db sessions).
|
|
//
|
|
// Also, the main thread is always accounted for in the
|
|
// NmsTotalTrmThdCnt counter.
|
|
//
|
|
if ((Client_e != WINS_E_WINSRPC) && (Client_e != WINS_E_NMS))
|
|
{
|
|
//
|
|
// Increment the count of threads that have initialized
|
|
// with the db engine. This count will be used by the
|
|
// main thread to determine the number of threads that
|
|
// must wait for prior to terminating the process.
|
|
//
|
|
EnterCriticalSection(&NmsTermCrtSec);
|
|
NmsTotalTrmThdCnt++;
|
|
LeaveCriticalSection(&NmsTermCrtSec);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
JET_ERR
|
|
UpdateDb (
|
|
JET_SESID SesId,
|
|
JET_TABLEID TblId,
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
ULONG TypOfUpd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to insert a record in the name - address
|
|
mapping table of the database
|
|
|
|
Arguments:
|
|
SesId - Session Id
|
|
TblId - Table Id
|
|
pRowInfo - Row to insert
|
|
TypOfUp - Type of Update (insertion or replacement)
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- JET_ErrSuccess
|
|
Error status codes -- Jet error status codes
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbInsertRowInd,
|
|
NmsDbUpdateRow
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
|
|
DWORD EntryFlag = 0;
|
|
JET_ERR JetRetStat;
|
|
//JET_SETINFO SetInfo;
|
|
#ifdef WINSDBG
|
|
BOOL fUpd = FALSE;
|
|
#endif
|
|
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
try {
|
|
JetRetStat = JetPrepareUpdate(
|
|
SesId,
|
|
TblId,
|
|
TypOfUpd
|
|
);
|
|
|
|
//
|
|
// Starting from rel118.0, JetPrepareUpdate can return
|
|
// JET_wrnNoWriteLock when called to replace a record at
|
|
// transaction level 0. We should just ignore it
|
|
//
|
|
if (JetRetStat != JET_errSuccess)
|
|
{
|
|
if (
|
|
!((JetRetStat == JET_wrnNoWriteLock)
|
|
&&
|
|
(TypOfUpd == JET_prepReplace))
|
|
)
|
|
{
|
|
RET_M(JetRetStat);
|
|
}
|
|
}
|
|
|
|
// add first column (clustered index)
|
|
if (TypOfUpd != JET_prepReplace)
|
|
{
|
|
JETRET_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_NAME_INDEX].Fid,
|
|
pRowInfo->pName,
|
|
pRowInfo->NameLen,
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
PERF("Make check for unique record first. Also, remove the shifting")
|
|
PERF("NodeType field for spec. grp. When we start doing this")
|
|
PERF("do not set NodeType to 0 in NmsNmhReplGrpMem in nmsnmh.c")
|
|
if (
|
|
NMSDB_ENTRY_SPEC_GRP_M(pRowInfo->EntTyp)
|
|
||
|
|
NMSDB_ENTRY_MULTIHOMED_M(pRowInfo->EntTyp)
|
|
)
|
|
{
|
|
EntryFlag = pRowInfo->EntTyp
|
|
|
|
|
(pRowInfo->NodeTyp << NMSDB_SHIFT_NODE_TYP)
|
|
|
|
|
(pRowInfo->fStatic << NMSDB_SHIFT_STATIC)
|
|
|
|
|
(pRowInfo->fLocal ? NMSDB_BIT_LOCAL : 0)
|
|
|
|
|
(pRowInfo->EntryState_e << NMSDB_SHIFT_STATE);
|
|
|
|
JETRET_M( InsertGrpMemsInCol(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo,
|
|
TypOfUpd
|
|
)
|
|
);
|
|
}
|
|
else // it is a unique entry or a normal group entry
|
|
{
|
|
if (NMSDB_ENTRY_NORM_GRP_M(pRowInfo->EntTyp))
|
|
{
|
|
EntryFlag = pRowInfo->EntTyp
|
|
|
|
|
(pRowInfo->fStatic << NMSDB_SHIFT_STATIC)
|
|
|
|
|
(pRowInfo->EntryState_e << NMSDB_SHIFT_STATE);
|
|
}
|
|
else // it is a Unique entry
|
|
{
|
|
EntryFlag =
|
|
pRowInfo->EntTyp
|
|
|
|
|
(pRowInfo->NodeTyp << NMSDB_SHIFT_NODE_TYP)
|
|
|
|
|
(pRowInfo->fLocal ? NMSDB_BIT_LOCAL : 0)
|
|
|
|
|
(pRowInfo->fStatic << NMSDB_SHIFT_STATIC)
|
|
|
|
|
(pRowInfo->EntryState_e << NMSDB_SHIFT_STATE);
|
|
|
|
}
|
|
FUTURES("If in the future, we support more than one address for a unique name")
|
|
FUTURES("we will check pRowInfo for the number of addresses (another field)")
|
|
FUTURES("and then specify the right size to JetSetColumn below")
|
|
|
|
//
|
|
// add second column (IP address)
|
|
//
|
|
// Note: Even though for Normal groups there is no need to
|
|
// set the address, we do it anyway. This is to save
|
|
// an if test which wlll slow down the registrations (inside
|
|
// a critical section) of unique entries (form the bulk
|
|
// of registration traffic).
|
|
//
|
|
FUTURES("Don't distinguish between unique and group entries. Store Time stamp")
|
|
FUTURES("and owner id along with address in case of unique entry. This will")
|
|
FUTURES("help get rid of some code from this function")
|
|
|
|
// JetRetStat = JetSetColumn(
|
|
JETRET_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
pRowInfo->pNodeAdd,
|
|
sizeof(COMM_ADD_T),
|
|
//Grbit,
|
|
0,
|
|
//pSetInfo
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
// add third column (this is the flag byte */
|
|
JETRET_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&EntryFlag,
|
|
sizeof(EntryFlag),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// If the version number is not to be incremented, there is no
|
|
// need to increment the owner id. It must remain the same.
|
|
//
|
|
if (pRowInfo->fUpdVersNo)
|
|
{
|
|
// add 4th column (this is the owner byte */
|
|
JETRET_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&pRowInfo->OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
|
|
// add 5th column (this is the version number long(DWORD)
|
|
JETRET_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&(pRowInfo->VersNo),
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
#ifdef WINSDBG
|
|
fUpd = TRUE;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// When the conflict is between two internet group entries,
|
|
// (replica -- Tombstone, database entry -- Active), we do
|
|
// not update the timestamp (Check out -- ClashAtReplGrpMems
|
|
// in nmsnmh.c to get a better insight into this).
|
|
//
|
|
if (pRowInfo->fUpdTimeStamp)
|
|
{
|
|
// add 6th column (this is the time stamp) */
|
|
JETRET_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&(pRowInfo->TimeStamp),
|
|
sizeof(DWORD),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
}
|
|
|
|
JetRetStat = JetUpdate (
|
|
SesId,
|
|
TblId,
|
|
NULL,
|
|
0L,
|
|
NULL
|
|
);
|
|
} // end of try block
|
|
finally {
|
|
if (AbnormalTermination() || JetRetStat != JET_errSuccess)
|
|
{
|
|
CALL_M(JetRollback(SesId, JET_bitRollbackAll));
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetCommitTransaction(SesId, /*CommitGrBit |*/ JET_bitCommitFlush));
|
|
}
|
|
}
|
|
|
|
#ifdef WINSDBG
|
|
if (JetRetStat == JET_errSuccess)
|
|
{
|
|
|
|
NMSNMH_UPD_UPD_CTRS_M(fUpd, TypOfUpd != JET_prepReplace ? FALSE : TRUE, pRowInfo);
|
|
}
|
|
#endif
|
|
|
|
return(JetRetStat);
|
|
}
|
|
|
|
|
|
STATUS
|
|
NmsDbUpdateVersNo (
|
|
BOOL fAfterClash,
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
PNMSDB_STAT_INFO_T pStatusInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to update a record in the name - address
|
|
mapping table of the database.
|
|
|
|
|
|
Arguments:
|
|
fAfterClash - indicates whether the update is being done after
|
|
a conflict resolution.
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsNmhReplRegInd,
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
JET_ERR JetRetStat;
|
|
DWORD ActFldLen;
|
|
|
|
DBGENTER("NmsDbUpdVersNo\n");
|
|
pTls = TlsGetValue(WinsTlsIndex);
|
|
|
|
// No need to check whether pTls is NON-NULL. It has to be
|
|
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
|
|
pStatusInfo->StatCode = NMSDB_SUCCESS;
|
|
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
pRowInfo->pName,
|
|
pRowInfo->NameLen,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
if ( (JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
)
|
|
) == JET_errRecordNotFound
|
|
)
|
|
{
|
|
if (fAfterClash)
|
|
{
|
|
/*
|
|
There is some serious error.
|
|
This condition should never occur because this thread
|
|
got a conflict on a record earlier while inside the
|
|
NmsNmhNamRegCrtSec. Since the thread never got out of the
|
|
critical section prior to calling this function, there is
|
|
no reason why we should now not be able to find the record
|
|
*/
|
|
DBGPRINT1(ERR,
|
|
"NmsDbUpdateVersNo: Could not find record (%s) -- WEIRD\n", pRowInfo->pName);
|
|
WINSEVT_LOG_M(JetRetStat, WINS_EVT_F_CANT_FIND_REC);
|
|
ASSERTMSG(0, "SEEK ERROR");
|
|
return(WINS_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT1(DET,
|
|
"NmsDbUpdateVersNo: Could not find record (%s). It might have been deleted\n", pRowInfo->pName);
|
|
WINSEVT_LOG_INFO_D_M(WINS_SUCCESS, WINS_EVT_CANT_FIND_REC);
|
|
return(WINS_SUCCESS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (JetRetStat != JET_errSuccess)
|
|
{
|
|
DBGPRINT1(ERR,
|
|
"NmsDbRelRow: Seek returned Error (%d)\n",
|
|
JetRetStat);
|
|
|
|
WINSEVT_LOG_M(JetRetStat, WINS_EVT_DATABASE_ERR);
|
|
return(WINS_FAILURE);
|
|
}
|
|
}
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
try {
|
|
JetRetStat = JetPrepareUpdate(
|
|
SesId,
|
|
TblId,
|
|
JET_prepReplace
|
|
);
|
|
if ((JetRetStat != JET_errSuccess) && (JetRetStat != JET_wrnNoWriteLock))
|
|
{
|
|
CALL_M(JetRetStat);
|
|
}
|
|
|
|
FUTURES("Remove adding of name")
|
|
#if 0
|
|
// add first column (clusterred index)
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_NAME_INDEX].Fid,
|
|
pRowInfo->pName,
|
|
pRowInfo->NameLen,
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
#endif
|
|
//
|
|
// retrieve the owner id field for doing sanity check
|
|
//
|
|
#if !NEW_OWID
|
|
pStatusInfo->OwnerId = 0;
|
|
#endif
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&pStatusInfo->OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// If this WINS does not own the record, raise an exception
|
|
//
|
|
// This should never happen since we never left the critical
|
|
// section after the clash.
|
|
//
|
|
if(pStatusInfo->OwnerId != NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
if (fAfterClash)
|
|
{
|
|
pStatusInfo->StatCode = NMSDB_NO_SUCH_ROW;
|
|
WINSEVT_LOG_M(pStatusInfo->OwnerId, WINS_EVT_RECORD_NOT_OWNED);
|
|
DBGPRINT1(EXC,
|
|
"NmsDbUpdVersNo: Record with name (%s) not owned by this WINS\n",
|
|
pRowInfo->pName);
|
|
WINS_RAISE_EXC_M(WINS_EXC_RECORD_NOT_OWNED);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT1(DET,
|
|
"NmsDbUpdateVersNo: The record with name (%s) is no longer owned by this WINS", pRowInfo->pName);
|
|
WINSEVT_LOG_INFO_D_M(WINS_SUCCESS, WINS_EVT_RECORD_NOT_OWNED);
|
|
return(WINS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
// add 5th column (this is the version number long(DWORD) */
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&(pRowInfo->VersNo),
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// determine if the time stamp needs to be updated
|
|
//
|
|
if (pRowInfo->fUpdTimeStamp)
|
|
{
|
|
// add 6th column (this is the time stamp) */
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&(pRowInfo->TimeStamp),
|
|
sizeof(DWORD),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
}
|
|
CALL_M( JetUpdate (
|
|
SesId,
|
|
TblId,
|
|
NULL,
|
|
0L,
|
|
NULL
|
|
)
|
|
);
|
|
} // end of try ..
|
|
finally {
|
|
if (AbnormalTermination())
|
|
{
|
|
CALL_M(JetRollback(SesId, JET_bitRollbackAll));
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetCommitTransaction(SesId, JET_bitCommitFlush));
|
|
}
|
|
}
|
|
|
|
|
|
DBGLEAVE("NmsDbUpdVersNo\n");
|
|
return(RetStat);
|
|
}
|
|
|
|
STATUS
|
|
NmsDbEndSession (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function closes the table, the database and ends the session
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Externals Used:
|
|
WinsTlsIndex
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
Errors are logged
|
|
|
|
Called by:
|
|
WaitUntilSignaled in nms.c (by an nbt thread when it is signaled by
|
|
the main thread for termination purposes)
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
|
|
PWINSTHD_TLS_T pTls;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
|
|
pTls = TlsGetValue(WinsTlsIndex);
|
|
if (pTls == NULL)
|
|
{
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (pTls->fNamAddTblOpen)
|
|
{
|
|
CALL_M(JetCloseTable(
|
|
pTls->SesId,
|
|
pTls->NamAddTblId
|
|
)
|
|
);
|
|
}
|
|
|
|
if (pTls->fOwnAddTblOpen)
|
|
{
|
|
CALL_M(JetCloseTable(
|
|
pTls->SesId,
|
|
pTls->OwnAddTblId
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
CALL_M(JetCloseDatabase(
|
|
pTls->SesId,
|
|
pTls->DbId,
|
|
0 //find out what grbit can be used for
|
|
)
|
|
);
|
|
|
|
|
|
CALL_M(JetEndSession(
|
|
pTls->SesId,
|
|
0 //find out what grbit can be used for
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// deallocate the TLS storage
|
|
//
|
|
WinsMscDealloc(pTls);
|
|
|
|
return(RetStat);
|
|
|
|
}
|
|
|
|
|
|
STATUS
|
|
GetGrpMem (
|
|
IN JET_SESID SesId,
|
|
IN JET_TABLEID TblId,
|
|
IN PNMSDB_ROW_INFO_T pRowInfo,
|
|
IN DWORD_PTR CurrentTime,
|
|
// IN OUT PNMSDB_NODE_ADDS_T pNodeAdds,
|
|
IN OUT PNMSDB_STAT_INFO_T pStatInfo,
|
|
IN BOOL fStatic,
|
|
OUT LPBOOL pfIsMem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to get all the active members of a
|
|
special group
|
|
|
|
Arguments:
|
|
SesId - Id of the session started with the db
|
|
TblId - Id of the name -address mapping table
|
|
pRowInfo - Used to pass current time and address of the client
|
|
(when the client sends the release request)
|
|
pNodeAdds - group memnbers that are still active
|
|
fStatic - indicates whether the record is STATIC or not.
|
|
pfIsMem - indicates whether the client is a member of the group
|
|
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbRelRow, NmsDbInsertRowGrp
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
DWORD i;
|
|
DWORD No = 0; //needs to be inited here
|
|
JET_RETINFO RetInfo;
|
|
DWORD ActFldLen = 0;
|
|
DWORD TimeToExpire;
|
|
NMSDB_GRP_MEM_ENTRY_T GrpMem;
|
|
JET_ERR JetRetStat;
|
|
|
|
*pfIsMem = FALSE; //Assume that the client is not a member
|
|
//of the group
|
|
|
|
/* retrieve the number of addresses info*/
|
|
RetInfo.itagSequence = 1;
|
|
RetInfo.cbStruct = sizeof(JET_RETINFO);
|
|
RetInfo.ibLongValue = 0;
|
|
|
|
JetRetStat = JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&pStatInfo->NodeAdds.NoOfMems,
|
|
sizeof(pStatInfo->NodeAdds.NoOfMems),
|
|
&ActFldLen,
|
|
0,
|
|
&RetInfo
|
|
);
|
|
|
|
if (
|
|
(JetRetStat != JET_errSuccess)
|
|
&&
|
|
(JetRetStat != JET_wrnBufferTruncated)
|
|
)
|
|
{
|
|
CALL_M(JetRetStat);
|
|
}
|
|
|
|
ASSERT(pStatInfo->NodeAdds.NoOfMems <= NMSDB_MAX_MEMS_IN_GRP);
|
|
|
|
DBGPRINT1(FLOW, "GetGrpMems: No Of members in group (expired and non-expired) are (%d)\n", pStatInfo->NodeAdds.NoOfMems);
|
|
|
|
NOTE("Remove this check once JET is error free")
|
|
if (pStatInfo->NodeAdds.NoOfMems > NMSDB_MAX_MEMS_IN_GRP)
|
|
{
|
|
WINSEVT_STRS_T EvtStrs;
|
|
WCHAR String[NMSDB_MAX_NAM_LEN];
|
|
EvtStrs.NoOfStrs = 1;
|
|
(VOID)WinsMscConvertAsciiStringToUnicode(
|
|
pRowInfo->pName,
|
|
(LPBYTE)String,
|
|
NMSDB_MAX_NAM_LEN);
|
|
EvtStrs.pStr[0] = String;
|
|
pStatInfo->NodeAdds.NoOfMems = 0;
|
|
WINSEVT_LOG_STR_M(WINS_EVT_DATABASE_CORRUPTION, &EvtStrs);
|
|
|
|
}
|
|
RetInfo.ibLongValue = sizeof(pStatInfo->NodeAdds.NoOfMems);
|
|
for (i=0; i < pStatInfo->NodeAdds.NoOfMems; i++)
|
|
{
|
|
JetRetStat = JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&GrpMem,
|
|
sizeof(GrpMem),
|
|
&ActFldLen,
|
|
0,
|
|
&RetInfo
|
|
);
|
|
if (
|
|
(JetRetStat != JET_errSuccess)
|
|
&&
|
|
(JetRetStat != JET_wrnBufferTruncated)
|
|
)
|
|
{
|
|
CALL_M(JetRetStat);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If the grp has expired, set TimeToExpire to 0
|
|
//
|
|
if (CurrentTime >= GrpMem.TimeStamp)
|
|
{
|
|
TimeToExpire = 0;
|
|
}
|
|
else
|
|
{
|
|
TimeToExpire = 1;
|
|
}
|
|
|
|
|
|
//
|
|
// If this is a STATIC record but not a user defined spec. grp or
|
|
// if the member was registered by another WINS or if
|
|
// the member is still active, keep it (i.e. return it
|
|
// in the NodeAdds array.) We drop all non-owned members which
|
|
// have expired.
|
|
//
|
|
// Note 1C groups are special even if user defines them in the
|
|
// lmhosts file
|
|
//
|
|
if (
|
|
(fStatic && (!(NMSDB_ENTRY_USER_SPEC_GRP_M(pRowInfo->pName, pStatInfo->EntTyp))))
|
|
||
|
|
(GrpMem.OwnerId != NMSDB_LOCAL_OWNER_ID)
|
|
||
|
|
TimeToExpire
|
|
)
|
|
{
|
|
|
|
pStatInfo->NodeAdds.Mem[No++] = GrpMem;
|
|
if (pRowInfo->NodeAdds.Mem[0].Add.Add.IPAdd
|
|
== GrpMem.Add.Add.IPAdd)
|
|
{
|
|
*pfIsMem = TRUE;
|
|
}
|
|
}
|
|
|
|
if (No == NMSDB_MAX_MEMS_IN_GRP)
|
|
{
|
|
/*
|
|
* Group limit reached
|
|
*/
|
|
break;
|
|
}
|
|
RetInfo.ibLongValue += sizeof(GrpMem);
|
|
|
|
} //end of for
|
|
|
|
pStatInfo->NodeAdds.NoOfMems = No;
|
|
|
|
DBGPRINT1(FLOW, "GetGrpMems: No Of non-expired members in group are (%d)\n", pStatInfo->NodeAdds.NoOfMems);
|
|
#ifdef WINSDBG
|
|
if (pStatInfo->NodeAdds.NoOfMems > NMSDB_MAX_MEMS_IN_GRP)
|
|
{
|
|
DBGPRINT4(SPEC, "GetGrpMems: No Of non-expired members in group %s are (%d). Vers. No to insert is (%d %d)\n", pRowInfo->pName, pStatInfo->NodeAdds.NoOfMems, pRowInfo->VersNo.HighPart, pRowInfo->VersNo.LowPart);
|
|
}
|
|
#endif
|
|
return(WINS_SUCCESS);
|
|
}
|
|
VOID
|
|
NmsDbRelRes(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function releases all the resources held by the Database Engine
|
|
(JET)
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
None
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
WinsMain
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function must be called by the thread that did the attach.
|
|
So, it has to be the main thread.
|
|
--*/
|
|
{
|
|
// PWINSTHD_TLS_T pTls;
|
|
JET_ERR JetRetStat = JET_errSuccess;
|
|
// JET_SESID SesId;
|
|
// BOOL fOutOfReck;
|
|
|
|
DBGENTER("NmsDbRelRes\n");
|
|
|
|
//
|
|
// Call JetTerm only if there is no abrupt termination. Currently,
|
|
// JetTerm will hang if it is called without all sessions being
|
|
// terminated. Terminating abruptly without calling JetTerm
|
|
// is sort of equivalent to power failure kind of situation.
|
|
// Recovery will happen under the covers the next time WINS server
|
|
// is invoked -- Ian Jose 10/18/93.
|
|
//
|
|
if (!fNmsAbruptTerm /*&& !fOutOfReck*/)
|
|
{
|
|
DBGPRINT0(DET, "NmsDbRelRes: JetTerm being called\n");
|
|
|
|
#if DYNLOADJET
|
|
if (DynLoadJetVersion >= DYN_LOAD_JET_500)
|
|
{
|
|
(VOID)JetTerm2(sJetInstance, JET_bitTermComplete);//no need to check to the return value
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
(VOID)JetTerm(sJetInstance);//no need to check to the return value
|
|
}
|
|
}
|
|
DBGLEAVE("NmsDbRelRes\n");
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATUS
|
|
GetMaxVersNos(
|
|
JET_SESID SesId,
|
|
JET_TABLEID TblId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called at initialization time to get the
|
|
max version number for records owned by different WINS servers
|
|
in the database.
|
|
|
|
Arguments:
|
|
SesId - Jet Session id
|
|
TblId - Table Id of the Name-Address Mapping table
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbInit
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function is called at initialization time. If in the future,
|
|
it gets called during stable state, we need to have a critical section
|
|
around the update of NmsDbNoOfOwners var.
|
|
--*/
|
|
|
|
{
|
|
#if NEW_OWID
|
|
DWORD OwnerId;
|
|
#else
|
|
DWORD OwnerId = 0;
|
|
#endif
|
|
DWORD ActFldLen;
|
|
JET_ERR JetRetStat;
|
|
BOOL fOnlyReplicas = FALSE;
|
|
BOOL fFirstIter = TRUE;
|
|
|
|
WINS_ASSIGN_INT_TO_VERS_NO_M(sHighestVersNoSaved, 0);
|
|
|
|
/*
|
|
* Set the primary index as the current index
|
|
*/
|
|
CALL_M( JetSetCurrentIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_NAM_ADD_PRIM_INDEX_NAME
|
|
)
|
|
);
|
|
|
|
PERF("Remove this Move since when we set the index, we are automatically")
|
|
PERF("positioned on the first row")
|
|
//
|
|
// Move to the first record in the name - address mapping table
|
|
//
|
|
JetRetStat = JetMove(
|
|
SesId,
|
|
TblId,
|
|
JET_MoveFirst,
|
|
0 //no grbit
|
|
);
|
|
//
|
|
// The following error indicates that either our database
|
|
// is empty or has garbage. I will assume for now that it
|
|
// is empty. If it contains garbage, we will know soon enough
|
|
//
|
|
if (JetRetStat == JET_errNoCurrentRecord)
|
|
{
|
|
FUTURES("Be more robust. Check if db contains garbage")
|
|
DBGPRINT0(ERR,
|
|
"GetMaxVersNos: There are no records in the db\n");
|
|
WINSEVT_LOG_INFO_M(WINS_SUCCESS, WINS_EVT_NO_RECS_IN_NAM_ADD_TBL);
|
|
NmsDbNoOfOwners = 0;
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
CALL_M(JetRetStat);
|
|
|
|
//
|
|
// The fact that we are here means that there is atleast one record
|
|
// in our db
|
|
//
|
|
|
|
//
|
|
// Get the owner id and max version numbers of all owners in the
|
|
// table
|
|
//
|
|
do
|
|
{
|
|
|
|
//
|
|
// Retrieve the owner Id column.
|
|
//
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
if (fFirstIter)
|
|
{
|
|
if (OwnerId != 0)
|
|
{
|
|
// The last owner id to be retrieved is not 0 means
|
|
// that there is no record owned by us
|
|
//
|
|
fOnlyReplicas = TRUE;
|
|
}
|
|
fFirstIter = FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Specify an owner id that is 1 more than what we retrieved
|
|
//
|
|
OwnerId += 1;
|
|
|
|
// in case this is not the special record...
|
|
if ((OwnerId - 1) != OWNER_ID_OF_SPEC_REC)
|
|
{
|
|
// ...expand the ownerid - versNo array to at least OwnerId slots
|
|
if (RplPullMaxNoOfWins < OwnerId)
|
|
{
|
|
DWORD newMaxNoOfWins = max(RplPullMaxNoOfWins * 2, OwnerId);
|
|
|
|
RplPullAllocVersNoArray(&pRplPullOwnerVersNo, newMaxNoOfWins);
|
|
RplPullMaxNoOfWins = newMaxNoOfWins;
|
|
DBGPRINT1(DET, "ReadOwnAddTbl: No of slots in RPL_OWNER_VERS_NO_ARRAY has been increased to %d\n", RplPullMaxNoOfWins);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Construct a partial key made of owner id.
|
|
//
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
JET_bitNewKey //since this is the first
|
|
//data value of the key
|
|
)
|
|
);
|
|
|
|
//
|
|
// Seek to the record that has a key that is Less than or
|
|
// Equal to the OwnerId value.
|
|
//
|
|
// Since we have specified a partial key (saying in effect
|
|
// that the other component of the key is NULL), JetSeek
|
|
// must return wrnSeekNotEqual since it will never find
|
|
// a record with NULL for the second component of the index
|
|
// -- Ian 7/13/93
|
|
//
|
|
JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekLE
|
|
);
|
|
ASSERT(JetRetStat == JET_wrnSeekNotEqual);
|
|
#ifdef WINSDBG
|
|
if (JetRetStat != JET_wrnSeekNotEqual)
|
|
{
|
|
DBGPRINT1(ERR, "GetMaxVersNos: JetSeek returned (%d)\n", JetRetStat);
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// retrieve the version number of the record on which we
|
|
// are positioned. This is the max vers. number pertaining
|
|
// to OwnerId. If the Owner Id is one more than the
|
|
// owner id. we have assigned to the special record,
|
|
// then store the version number retrieved into
|
|
// sHighestVersNoSaved
|
|
//
|
|
CALL_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
((OwnerId - 1 ) == OWNER_ID_OF_SPEC_REC) ?
|
|
&sHighestVersNoSaved :
|
|
&(pRplPullOwnerVersNo+OwnerId - 1)->VersNo,
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
if ((OwnerId - 1) == OWNER_ID_OF_SPEC_REC )
|
|
{
|
|
ASSERT(!sfHighestVersNoRecExists);
|
|
if (sfHighestVersNoRecExists)
|
|
{
|
|
DBGPRINT0(ERR, "GetMaxVersNo: ERROR: SOFTWARE BUG - Found both the old and new spec. owner id records. They are MUTUALLY EXCLUSIVE\n");
|
|
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR);
|
|
}
|
|
StoreSpecVersNo();
|
|
DBGPRINT3(INIT, "GetMaxVersNo: Owner Id - (%d) : Vers No. (%d %d)\n", (OwnerId - 1), sHighestVersNoSaved.HighPart, sHighestVersNoSaved.LowPart);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If the owner id is == what used to be the owner id.
|
|
// of the special record, it means that we have a pre-SUR
|
|
// beta2 db. We should delete this name to get rid of
|
|
// clutter. We should mark the pRplPullOwnerVersNo slot
|
|
// empty since it was initialized above.
|
|
//
|
|
if ((OwnerId - 1) == OWNER_ID_OF_SPEC_REC_OLD )
|
|
{
|
|
LPBYTE Name[NMSDB_MAX_NAM_LEN];
|
|
DWORD NameLen;
|
|
|
|
//
|
|
// If the name is == spHighestVersNoRecNameOld, delete
|
|
// this record. This is the old special record
|
|
// we had. Save the vers. no. in a local
|
|
//
|
|
// NOTE: the length of the spec. rec. name is < 16
|
|
// bytes so it is not a valid netbios name
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_NAME_INDEX].Fid,
|
|
Name,
|
|
NMSDB_MAX_NAM_LEN,
|
|
&NameLen,
|
|
0,
|
|
NULL));
|
|
|
|
if ((NameLen == sizeof(spHighestVersNoRecNameOld)) && RtlEqualMemory((PVOID)Name, spHighestVersNoRecNameOld, NameLen))
|
|
{
|
|
sHighestVersNoSaved =
|
|
(pRplPullOwnerVersNo+OwnerId - 1)->VersNo;
|
|
(pRplPullOwnerVersNo+OwnerId - 1)->VersNo.QuadPart = 0;
|
|
CALL_M(JetDelete(SesId, TblId));
|
|
StoreSpecVersNo();
|
|
DBGPRINT3(INIT, "GetMaxVersNo: Owner Id - (%d) : Vers No. (%d %d)\n", (OwnerId - 1), sHighestVersNoSaved.HighPart, sHighestVersNoSaved.LowPart);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
DBGPRINT3(INIT, "GetMaxVersNo: Owner Id - (%d) : Vers No. (%d %d)\n", (OwnerId - 1), (pRplPullOwnerVersNo+OwnerId - 1)->VersNo.HighPart,
|
|
(pRplPullOwnerVersNo+OwnerId - 1)->VersNo.LowPart);
|
|
|
|
|
|
NmsDbNoOfOwners++; //count of owners found in the db
|
|
|
|
} while(
|
|
JetMove(SesId, TblId, JET_MoveNext, 0) == JET_errSuccess
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Check if the version counter's value is < that of the highest
|
|
// version found for owned records
|
|
// (found when we did the search in the while loop above. Use
|
|
// whichever is higher as the version counter)
|
|
//
|
|
if (!fOnlyReplicas)
|
|
{
|
|
//
|
|
// We need to increment the Vers. No. Counter to point to the
|
|
// number to be given to the next record
|
|
//
|
|
if (LiGeq(
|
|
pRplPullOwnerVersNo->VersNo,
|
|
NmsNmhMyMaxVersNo
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// Initialize NmsNmhMyMaxVersNo. Remember this counter
|
|
// always contains the next version number to be given
|
|
// to a record. So, we must increment the count contained
|
|
// in RplPullOwnerVersNo[0] by 1
|
|
//
|
|
NMSNMH_INC_VERS_NO_M(
|
|
pRplPullOwnerVersNo->VersNo,
|
|
NmsNmhMyMaxVersNo
|
|
);
|
|
|
|
//
|
|
// Since we found records in the db, we take the conservative
|
|
// approach here, and set the Min Scv Vers. no to 1. If
|
|
// the first record has a very high version no. the scavenger
|
|
// thread will update the NmsScvMinVersNo to that value.
|
|
//
|
|
// We need to scavenge from this version onwards.
|
|
//
|
|
NmsScvMinScvVersNo.QuadPart = 1;
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Since we are here it means that when we searched for records
|
|
// belonging to the local WINS, we did not find any record
|
|
// We may or may not have found the special record. If we did find it
|
|
// it means that all the local records of the WINS were either
|
|
// deleted or replaced by replicas in its previous incarnation.
|
|
//
|
|
|
|
//
|
|
// If we found the special record, let us initialize RplPullOwnerVersNo
|
|
// entry for the local WINS
|
|
//
|
|
if (sfHighestVersNoRecExists)
|
|
{
|
|
pRplPullOwnerVersNo->VersNo = NmsNmhMyMaxVersNo;
|
|
|
|
//
|
|
// Increment the counter since it must always have a
|
|
// value to be given to the next local record we insert or
|
|
// update.
|
|
//
|
|
CHECK("May not be necessary")
|
|
NMSNMH_INC_VERS_NO_M(
|
|
NmsNmhMyMaxVersNo,
|
|
NmsNmhMyMaxVersNo
|
|
);
|
|
|
|
if (fOnlyReplicas)
|
|
{
|
|
//
|
|
// We need to scavenge from this version onwards.
|
|
//
|
|
NmsScvMinScvVersNo = NmsNmhMyMaxVersNo;
|
|
}
|
|
else
|
|
{
|
|
NmsScvMinScvVersNo.QuadPart = 1;
|
|
}
|
|
}
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
__inline
|
|
VOID
|
|
StoreSpecVersNo(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function conditionally updates NmsNmhMyMaxVersNo to a number that is 1
|
|
more than the version. no. found in the special owner id. record.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Externals Used:
|
|
|
|
NmsNmhMyMaxVersNo,
|
|
sfHighestVersNoExists
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
GetMaxVersNos()
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
sfHighestVersNoRecExists = TRUE;
|
|
|
|
//
|
|
// If the version counter's value is < that of
|
|
// the special record, update it.
|
|
//
|
|
//
|
|
// NOTE: If the registry specified a number for the
|
|
// version counter, then NmsNmhMyMaxVersNo would be
|
|
// having that value, else it would be 1
|
|
//
|
|
if (LiLtr(NmsNmhMyMaxVersNo, sHighestVersNoSaved))
|
|
{
|
|
NMSNMH_INC_VERS_NO_M( sHighestVersNoSaved, NmsNmhMyMaxVersNo);
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
JET_ERR
|
|
InsertGrpMemsInCol(
|
|
JET_SESID SesId,
|
|
JET_TABLEID TblId,
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
ULONG TypOfUpd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to insert members of a special group
|
|
in the address column field of the name - address mapping table
|
|
|
|
Arguments:
|
|
SesId - Session Id.
|
|
TblId - Table Id.
|
|
pRowInfo - Contains the member info
|
|
fOverwrite - Whether members in the list above would overwrite the
|
|
ones already there
|
|
|
|
|
|
Externals Used:
|
|
sNamAddTblRow
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- JET_errSuccess
|
|
Error status codes -- Jet error codes
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbRelRow, UpdateDb
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
|
|
JET_SETINFO SetInfo;
|
|
DWORD i;
|
|
JET_ERR JetRetStat = JET_errSuccess; //needs to be inited here
|
|
|
|
DBGENTER("InsertGrpMemsInCol\n");
|
|
SetInfo.itagSequence = 1; //has to be 1 always
|
|
SetInfo.ibLongValue = 0;
|
|
SetInfo.cbStruct = sizeof(JET_SETINFO);
|
|
|
|
ASSERT(pRowInfo->NodeAdds.NoOfMems <= NMSDB_MAX_MEMS_IN_GRP);
|
|
|
|
#ifdef WINSDBG
|
|
if (NMSDB_ENTRY_MULTIHOMED_M(pRowInfo->EntTyp) && pRowInfo->NodeAdds.NoOfMems > NMSDB_MAX_MEMS_IN_GRP)
|
|
{
|
|
DBGPRINT4(SPEC, "InsertGrpMemsInCol: Name is (%s); No Of Mems are (%d); Version number is (%d %d)\n", pRowInfo->pName, pRowInfo->NodeAdds.NoOfMems, pRowInfo->VersNo.HighPart, pRowInfo->VersNo.LowPart);
|
|
}
|
|
#endif
|
|
//
|
|
// Set the # of Members field. This is always the first
|
|
// field
|
|
//
|
|
if (TypOfUpd == JET_prepReplace)
|
|
{
|
|
|
|
// SetInfo.ibLongValue = sizeof(pRowInfo->NodeAdds.NoOfMems);
|
|
JETRET_M(JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
NULL,
|
|
0,
|
|
JET_bitSetSizeLV,
|
|
&SetInfo /*optional info */
|
|
)
|
|
);
|
|
// SetInfo.ibLongValue = 0;
|
|
}
|
|
JETRET_M(JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&pRowInfo->NodeAdds.NoOfMems,
|
|
sizeof(pRowInfo->NodeAdds.NoOfMems),
|
|
JET_bitSetAppendLV,
|
|
&SetInfo /*optional info */
|
|
)
|
|
);
|
|
for (
|
|
i=0;
|
|
i < pRowInfo->NodeAdds.NoOfMems && JetRetStat == JET_errSuccess;
|
|
i++
|
|
)
|
|
{
|
|
|
|
DBGPRINT3(DET, "InsertGrpMemsInCol: Inserted member (%d) with address (%X) and owner id (%d)\n", i, pRowInfo->NodeAdds.Mem[i].Add.Add.IPAdd,
|
|
pRowInfo->NodeAdds.Mem[i].OwnerId
|
|
);
|
|
|
|
CHECK("Check this on a MIPS machine")
|
|
//
|
|
// Set the GrpMem
|
|
//
|
|
JetRetStat = JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&pRowInfo->NodeAdds.Mem[i],
|
|
sizeof(NMSDB_GRP_MEM_ENTRY_T),
|
|
JET_bitSetAppendLV,
|
|
// TypOfUpd == JET_prepReplace ? JET_bitSetOverwriteLV : JET_bitSetAppendLV,
|
|
&SetInfo /*optional info */
|
|
);
|
|
|
|
} // end of for
|
|
DBGLEAVE("InsertGrpMemsInCol\n");
|
|
return(JetRetStat);
|
|
}
|
|
|
|
|
|
STATUS
|
|
NmsDbSetCurrentIndex(
|
|
IN NMSDB_TBL_NAM_E TblNm_e,
|
|
IN LPBYTE pIndexNam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to set the index on a table
|
|
|
|
Arguments:
|
|
TblNm_e - Identifies the table whose index needs to be set
|
|
pIndexNm - Name of index to be set
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
|
|
PWINSTHD_TLS_T pTls;
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
/*
|
|
* Use primary index now
|
|
*/
|
|
CALL_M( JetSetCurrentIndex(
|
|
pTls->SesId,
|
|
TblNm_e == NMSDB_E_NAM_ADD_TBL_NM ?
|
|
pTls->NamAddTblId :
|
|
pTls->OwnAddTblId,
|
|
pIndexNam
|
|
)
|
|
);
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
STATUS
|
|
NmsDbQueryNUpdIfMatch(
|
|
LPVOID pRecord,
|
|
int ThdPrLvl,
|
|
BOOL fChgPrLvl,
|
|
WINS_CLIENT_E Client_e
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to query a record and then update it only
|
|
if it matches the timestamp of the record supplied
|
|
|
|
Arguments:
|
|
pRecord - Record supplied
|
|
ThdPrLvl - Priority level of the thread
|
|
fChgPrLvl - TRUE, if priority level of the thread needs to be changed
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
UpdDb in nmsscv.c, WinsRecordAction in winsintf.c
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function must be called only when the index on the name
|
|
address table has been set to the clustered index column.
|
|
--*/
|
|
{
|
|
|
|
BYTE State;
|
|
DWORD TimeStamp = 0;
|
|
DWORD ActFldLen;
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
PRPL_REC_ENTRY_T pRec = pRecord;
|
|
JET_ERR JetRetStat;
|
|
BOOL fIncVersNo = FALSE;
|
|
#if NEW_OWID
|
|
DWORD OwnerId;
|
|
#else
|
|
DWORD OwnerId = 0;
|
|
#endif
|
|
BOOL fAbort = FALSE;
|
|
|
|
|
|
DBGENTER("NmsDbQueryNUpdIfMatch\n");
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
State = (BYTE)NMSDB_ENTRY_STATE_M(pRec->Flag);
|
|
|
|
#if 0
|
|
NmsDbSetCurrentIndex(
|
|
NMSDB_E_NAM_ADD_TBL_NM,
|
|
NMSDB_NAM_ADD_CLUST_INDEX_NAME
|
|
);
|
|
#endif
|
|
|
|
//
|
|
// Make sure you enter the critical section
|
|
// prior to deleting a record. This is because
|
|
// another thread may be seeking to it after
|
|
// conflicting with it. If we delete the
|
|
// record without entering the critical
|
|
// section, the thread may not
|
|
// find the record. This would cause it to
|
|
// raise an exception.
|
|
//
|
|
if (fChgPrLvl)
|
|
{
|
|
//
|
|
// Set the priority to NORMAL. We
|
|
// don't want to delay normal
|
|
// priority threads by getting
|
|
// starved of cpu time inside
|
|
// the critical section.
|
|
//
|
|
WinsMscSetThreadPriority(
|
|
WinsThdPool.ScvThds[0].ThdHdl,
|
|
THREAD_PRIORITY_NORMAL
|
|
);
|
|
}
|
|
EnterCriticalSection(&NmsNmhNamRegCrtSec);
|
|
try {
|
|
|
|
//
|
|
// Seek to the record
|
|
//
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
// pRec->Name,
|
|
pRec->pName,
|
|
pRec->NameLen,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
if (JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
) == JET_errSuccess
|
|
)
|
|
{
|
|
BOOL fUpdSpecRec = FALSE;
|
|
VERS_NO_T RecVersNo;
|
|
VERS_NO_T MyMaxVersNo;
|
|
|
|
//
|
|
// If we are doing scavenging, we need to make sure that
|
|
// while we were examining the records, the record that
|
|
// we want to update now, did not get updated. To check
|
|
// that we retrieve the timestamp of the record
|
|
//
|
|
if (Client_e == WINS_E_NMSSCV)
|
|
{
|
|
//
|
|
// retrieve the time stamp
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&TimeStamp,
|
|
sizeof(TimeStamp),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
}
|
|
|
|
//
|
|
// if timestamp is the same, we have our record.
|
|
// we don't need to check any other field. Exception: If we
|
|
// are an RPC thread, whether or not we update the
|
|
// record is independent of the timestamp that the
|
|
// record may have now
|
|
//
|
|
if (
|
|
(pRec->TimeStamp == TimeStamp)
|
|
||
|
|
(Client_e == WINS_E_WINSRPC)
|
|
)
|
|
{
|
|
//
|
|
// if state of the record is deleted, we need to
|
|
// delete it from the database.
|
|
//
|
|
if (State == NMSDB_E_DELETED)
|
|
{
|
|
|
|
//
|
|
// If Client is an RPC thread, first retrieve
|
|
// the owner id and version number of the
|
|
// record to delete
|
|
//
|
|
|
|
if (Client_e == WINS_E_WINSRPC)
|
|
{
|
|
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
if (OwnerId == NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
//
|
|
// Retrieve the version number
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&RecVersNo,
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// get the highest version number used
|
|
// up until now.
|
|
//
|
|
NMSNMH_DEC_VERS_NO_M(NmsNmhMyMaxVersNo,
|
|
MyMaxVersNo);
|
|
|
|
//
|
|
// If the record to be deleted has
|
|
// the this highest version number we
|
|
// must update the special record
|
|
//
|
|
if(LiEql(RecVersNo, MyMaxVersNo))
|
|
{
|
|
fUpdSpecRec = TRUE;
|
|
}
|
|
}
|
|
}
|
|
CALL_M(JetDelete(
|
|
SesId,
|
|
TblId
|
|
)
|
|
);
|
|
#ifdef WINSDBG
|
|
NmsDbDelQueryNUpdRecs++;
|
|
#endif
|
|
DBGPRINT2(SCV, "NmsDbQueryNUpdIfMatch: Deleted the record with name = (%s);16th char (%X)\n", pRec->pName, *(pRec->pName + 15));
|
|
|
|
//
|
|
// This can be TRUE only in an RPC thread
|
|
//
|
|
if (fUpdSpecRec)
|
|
{
|
|
NmsDbUpdHighestVersNoRec(
|
|
pTls,
|
|
MyMaxVersNo,
|
|
FALSE //don't enter Crt
|
|
//sec
|
|
);
|
|
}
|
|
}
|
|
else // we need to set the Flag field and in the
|
|
//case of a tombstone record update the version
|
|
//stamp
|
|
{
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
try {
|
|
JetRetStat = JetPrepareUpdate(
|
|
SesId,
|
|
TblId,
|
|
JET_prepReplace
|
|
);
|
|
if (
|
|
(JetRetStat != JET_errSuccess)
|
|
&&
|
|
(JetRetStat != JET_wrnNoWriteLock)
|
|
)
|
|
{
|
|
FUTURES("When Jet becomes stable, replace RET_M with a raise_exception")
|
|
//
|
|
// this should result in the execution
|
|
// of the finally clause
|
|
//
|
|
RET_M(JetRetStat);
|
|
}
|
|
|
|
if (Client_e == WINS_E_WINSRPC)
|
|
{
|
|
|
|
DWORD FlagVal;
|
|
BYTE EntryType;
|
|
BYTE NewEntryType;
|
|
|
|
//
|
|
// Retrieve the flags byte
|
|
//
|
|
// retrieve the flags column
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&FlagVal,
|
|
sizeof(FlagVal),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
EntryType = (BYTE)NMSDB_ENTRY_TYPE_M(FlagVal);
|
|
NewEntryType = (BYTE)NMSDB_ENTRY_TYPE_M(pRec->Flag);
|
|
|
|
//
|
|
// A unique/normal group record
|
|
// can not be changed to a multihomed/
|
|
// special group record unless the
|
|
// address column too is changed
|
|
//
|
|
if (
|
|
(
|
|
(
|
|
EntryType == NMSDB_UNIQUE_ENTRY
|
|
||
|
|
EntryType ==
|
|
NMSDB_NORM_GRP_ENTRY
|
|
)
|
|
&&
|
|
(
|
|
NewEntryType ==
|
|
NMSDB_SPEC_GRP_ENTRY
|
|
||
|
|
NewEntryType ==
|
|
NMSDB_MULTIHOMED_ENTRY
|
|
)
|
|
)
|
|
||
|
|
(
|
|
(
|
|
EntryType ==
|
|
NMSDB_SPEC_GRP_ENTRY
|
|
||
|
|
EntryType ==
|
|
NMSDB_MULTIHOMED_ENTRY
|
|
)
|
|
&&
|
|
(
|
|
NewEntryType == NMSDB_UNIQUE_ENTRY
|
|
||
|
|
NewEntryType ==
|
|
NMSDB_NORM_GRP_ENTRY
|
|
)
|
|
)
|
|
)
|
|
{
|
|
|
|
DBGPRINT0(ERR, "NmsDbQueryNUpdIfMatch: SORRY, Can not change to an incompatibe address format record. (Unique/Normal Group) to (Multihomed/Spec. Group) or vice-versa disallowed\n");
|
|
|
|
PERF("Do not return like this. finally block search is expensive")
|
|
fAbort = TRUE;
|
|
return(WINS_FAILURE);
|
|
|
|
}
|
|
|
|
} // end of if (client is RPC)
|
|
|
|
//
|
|
// Update the flags field
|
|
//
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&pRec->Flag,
|
|
sizeof(pRec->Flag),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
/* Update the timestamp column */
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&(pRec->NewTimeStamp),
|
|
sizeof(DWORD), /*change type
|
|
*to
|
|
*TIME_STAMP_T
|
|
*later
|
|
*/
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
//
|
|
// If the state of the record is a Tombstone
|
|
// or ACTIVE, we need to update the version
|
|
// number.
|
|
//
|
|
if (
|
|
(State == NMSDB_E_TOMBSTONE)
|
|
||
|
|
(State == NMSDB_E_ACTIVE)
|
|
)
|
|
{
|
|
|
|
VERS_NO_T VersNo;
|
|
|
|
VersNo = NmsNmhMyMaxVersNo;
|
|
|
|
|
|
//
|
|
// Make local WINS the owner if
|
|
// we are in an RPC thread. We
|
|
// have to make the local WINS the
|
|
// owner in order to update the
|
|
// version stamp. Also, note that if
|
|
// this is not the RPC thread then
|
|
// this has to be the scavenger thread
|
|
// (FYI: A scavenger thread never
|
|
// changes a replica into a tombstone)
|
|
//
|
|
if (Client_e == WINS_E_WINSRPC)
|
|
{
|
|
DWORD OwnerId=NMSDB_LOCAL_OWNER_ID;
|
|
|
|
/* Set the owner byte */
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
//
|
|
// Update the version number field
|
|
// so that this record gets
|
|
// propagated eventually
|
|
//
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[
|
|
NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&VersNo,
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
fIncVersNo = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is the scavenger thread.
|
|
// If the new state is not ACTIVE,
|
|
// update the version number since
|
|
// the state is TOMBSTONE.
|
|
// if the state is ACTIVE, then it
|
|
// means that we are doing a
|
|
// a revalidation of old replicas
|
|
// (i,e, the VerifyClutter() called
|
|
// us).
|
|
// The version number should stay
|
|
// the same.
|
|
//
|
|
if (State != NMSDB_E_ACTIVE)
|
|
{
|
|
// if the current record is replica dont touch
|
|
// the version #.
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL));
|
|
|
|
if (NMSDB_LOCAL_OWNER_ID == OwnerId) {
|
|
//
|
|
// Update the version number field
|
|
//
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[
|
|
NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&VersNo,
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
fIncVersNo = TRUE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
} // if (state is ACTIVE or TOMBSTONE)
|
|
|
|
//
|
|
// Update the record
|
|
//
|
|
CALL_M(JetUpdate (
|
|
SesId,
|
|
TblId,
|
|
NULL,
|
|
0L,
|
|
NULL
|
|
)
|
|
);
|
|
} // end of try block
|
|
finally {
|
|
if (AbnormalTermination())
|
|
{
|
|
CALL_M(JetRollback(SesId,
|
|
JET_bitRollbackAll));
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetCommitTransaction(SesId,
|
|
JET_bitCommitFlush));
|
|
}
|
|
}
|
|
if (fIncVersNo)
|
|
{
|
|
NMSNMH_INC_VERS_COUNTER_M(
|
|
NmsNmhMyMaxVersNo,
|
|
NmsNmhMyMaxVersNo
|
|
);
|
|
|
|
RPL_PUSH_NTF_M(
|
|
RPL_PUSH_NO_PROP, NULL, NULL, NULL);
|
|
}
|
|
|
|
} // New state is not DELETED
|
|
|
|
} // if (Timestamps equal or client is RPC)
|
|
#ifdef WINSDBG
|
|
else
|
|
{
|
|
|
|
DBGPRINT0(FLOW, "NmsDbQueryNUpdIfMatch: TimeStamp of record has changed\n");
|
|
}
|
|
#endif
|
|
}
|
|
else //seek failed
|
|
{
|
|
DBGPRINT3(FLOW, "NmsDbQueryNUpdIfMatch: Could not find record(%s[%x]) whose state has to be changed to (%d)\n",
|
|
pRec->pName, *(pRec->pName + 15),
|
|
NMSDB_ENTRY_STATE_M(pRec->Flag));
|
|
|
|
//
|
|
// Two different threads (RPC or Scavenger) can be calling
|
|
// this function. It is possible that either might have
|
|
// deleted the record. We should not raise an exception
|
|
// here
|
|
//
|
|
// WINS_RAISE_EXC_M(WINS_EXC_FAILURE);
|
|
|
|
}
|
|
|
|
} // end of try { ..}
|
|
|
|
finally {
|
|
if (AbnormalTermination() && !fAbort)
|
|
{
|
|
DBGPRINT0(ERR, "NmsDbQueryNUpdIfMatch: Abnormal Termination\n");
|
|
WINSEVT_LOG_M(WINS_FAILURE, WINS_EVT_SFT_ERR);
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&NmsNmhNamRegCrtSec);
|
|
if (fChgPrLvl)
|
|
{
|
|
WinsMscSetThreadPriority(
|
|
WinsThdPool.ScvThds[0].ThdHdl,
|
|
ThdPrLvl
|
|
);
|
|
}
|
|
|
|
} //end of finally
|
|
|
|
DBGLEAVE("NmsDbQueryNUpdIfMatch\n");
|
|
return(WINS_SUCCESS);
|
|
|
|
} // NmsDbQueryNUpdIfMatch
|
|
|
|
STATUS
|
|
SetSystemParamsJet600(
|
|
BOOL fBeforeInit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to set the system parameters for Jet
|
|
|
|
Arguments:
|
|
|
|
fBeforeInit - indicates whether this function has been called
|
|
prior to JetInit
|
|
Externals Used:
|
|
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
--*/
|
|
|
|
{
|
|
JET_ERR JetRetStat;
|
|
BOOL fFreeMem = TRUE;
|
|
CHAR DbFileDir[WINS_MAX_FILENAME_SZ]; //path to database file directory
|
|
DBGENTER("SetSystemParam600\n");
|
|
if (fBeforeInit)
|
|
{
|
|
CHAR *p;
|
|
|
|
// extract the directory path where database file will be created
|
|
strcpy(DbFileDir, WinsCnf.pWinsDb);
|
|
if (p = strrchr(DbFileDir, '\\')) {
|
|
p++ ;
|
|
*p = '\0';
|
|
} else {
|
|
return WINS_FAILURE;
|
|
}
|
|
|
|
//
|
|
// set this to enable version checking.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramCheckFormatWhenOpenFail,
|
|
1,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramExceptionAction,
|
|
JET_ExceptionMsgBox,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// Path for the checkpoint file jet.chk to be located
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramSystemPath,
|
|
0,
|
|
DbFileDir
|
|
)
|
|
);
|
|
//
|
|
// Basename to use for jet*.log and jet.chk
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramBaseName,
|
|
0,
|
|
BASENAME
|
|
)
|
|
);
|
|
//
|
|
// Max size of the log file in kb.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogFileSize,
|
|
1024, //set to one full meg (#96543)
|
|
NULL //ignored
|
|
)
|
|
);
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramTempPath,
|
|
0,
|
|
TEMP_DB_PATH //ignored
|
|
)
|
|
);
|
|
PERF("Check the following two things")
|
|
//
|
|
// We want some aggressive flushing. The performance impact
|
|
// is very trivial - Ian Jose 7/12/93
|
|
//
|
|
|
|
//
|
|
// The max number of buffers for database usage
|
|
//
|
|
// The default number is 500. 600 events are allocated
|
|
// for 500 buffers -- Ian 10/21/93. Each buffer is
|
|
// 4K. By keeping the number small, we impact performamce
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramCacheSizeMax, // JET_paramMaxBuffers,
|
|
WinsCnf.NoOfDbBuffers,//200,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
// Cheen: min cache size should be at-least 4 times the size of no of sessions
|
|
// o/w it can lead to deadlock.
|
|
ASSERT( WinsCnf.NoOfDbBuffers > MAX_NO_SESSIONS*4 );
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramCacheSizeMin,
|
|
MAX_NO_SESSIONS * 4,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// The max. number of buffers to store old version of a
|
|
// a record (snapshot at the start of a transaction)
|
|
// Each version store is 16k bytes. A version store
|
|
// stores structures that hold information derived from
|
|
// a snapshot of the database prior to an insert (20 bytes
|
|
// roughly) or update (size of the record + 20 bytes).
|
|
//
|
|
// For small transactions (i.e. a transaction around each
|
|
// update), this number should be >= the max. number of
|
|
// sessions that can be updating/inserting at the same time.
|
|
// Each session will have one version bucket. Since 16k of
|
|
// version bucket size can result in a lot of wastage per
|
|
// session (since each record is < .5k, and on the average
|
|
// around 50 bytes), it may be better to specify the
|
|
// max. size of the version bucket (<< 16k). Ian will
|
|
//provide a system param for this if we absolutely need it
|
|
//
|
|
// 3/4/93
|
|
//16kBytes should be enough for the transactions WINS does,
|
|
//but if all the sessions are in transactions at the same time
|
|
//and they all happen to have their small transactions traverse
|
|
//2 buckets then the peak requirement is 2 buckets per session.
|
|
//We could shorten the buckets to 8kBytes, or 4kBytes, and you
|
|
//could allocate 2 per session?
|
|
//
|
|
// 8/5/99
|
|
// The previous value was 16M (42x15 pages of 16K each ~= 16M).
|
|
// This value seems to be a bit too small so bump it to 32M.
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxVerPages,
|
|
MAX_NO_SESSIONS * 50, //number of 16K pages
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the File Control Block Param
|
|
//
|
|
// This is the max. number of tables that can be open
|
|
// at any time. If multiple threads open the same table
|
|
// they use the same FCB. FCB is 1 per table/index.
|
|
// Now, for a create database, we need atleast 18 FCBS
|
|
// and 18 IDBS. However apart from create database and
|
|
// ddl operations, we don't need to have these tables open.
|
|
// Default value is 300. Size of an FCB is 112 bytes.
|
|
//
|
|
// Jonathan Liem (1/6/97)
|
|
// JET_paramMaxOpenTableIndexes is removed. It is merged with
|
|
// JET_paramMaxOpenTables. So if you used to set JET_paramMaxOpenIndexes
|
|
// to be 2000 and JET_paramMaxOpenTables to be 1000, then for
|
|
// new Jet, you need to set JET_paramMaxOpenTables to 3000.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxOpenTables,
|
|
112, //was 56 //18 + 10,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// Set the File Usage Control Block to 100.
|
|
// This parameter indicates the max. number of cursors
|
|
// that can be open at any one time. This is
|
|
// therefore dependent on the the max. number of sessions
|
|
// that we can have running concurrently. For each session,
|
|
// there would be 4 cursors (for the two tables) + a certain
|
|
// number of internal cursors. For good measure we add
|
|
// a pad. Default value is 300. Size of each is 200 bytes.
|
|
// We use MAX_SESSIONS * 4 + pad
|
|
// (around 100)
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxCursors,
|
|
(MAX_NO_SESSIONS * 8 /*4*/) + 32,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the Sort Control block.
|
|
// This should be 1 per concurrent Create Index.
|
|
// Default value is 20. Size of each is 612 bytes.
|
|
// In the case of WINS, the main thread creates the
|
|
// indices. We should be setting it to 1. Let us
|
|
// however set it to 3.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxTemporaryTables ,
|
|
10, //1 + 2,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
//
|
|
// Set the Number for the Database Attribute Blocks
|
|
//
|
|
// This is max. number of Open Databases done. Since we
|
|
// can have a max of MAX_NO_SESSIONS at one time. This should
|
|
// be equal to that number (since we have just one database)
|
|
// Default number is 100. Size is 14 bytes
|
|
//
|
|
// JET_paramMaxOpenDatabase is removed. Jonathan Liem (1/6/97)
|
|
|
|
// Jonathan Liem (1/6/97)
|
|
// JET_paramBfThrshldLowPrcnt and JET_paramBfThrhldHighPrcnt are changed
|
|
// to JET_paramStartFlushThreshold and JET_paramStopFlushThreshold. The
|
|
// old ones are percent of given number of buffers (set through JET_paramMaxBuffer),
|
|
// the new ones are absolute value so that we can set low threshold less
|
|
// than 1 percent.
|
|
//
|
|
//
|
|
// The min number of buffers not yet dirtied before
|
|
// background flushing begins
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramStartFlushThreshold,
|
|
(WinsCnf.NoOfDbBuffers * 1)/100,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// The max number of buffers not yet dirtied before
|
|
// background flushing begins
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramStopFlushThreshold,
|
|
(WinsCnf.NoOfDbBuffers * 2)/100,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// The max. number of sessions that can be open at any time
|
|
//
|
|
// Note: Jet does not preallocate resources corresponding
|
|
// to the max. value. It allocates them dynamically upto
|
|
// the limit -- according to Ian Jose 7/12/93
|
|
//
|
|
// When checked with Ian again on 10/21, he said that they are
|
|
// allocated STATICally
|
|
//
|
|
CHECK("Make sure the comment above remains true")
|
|
FUTURES("Make sure the comment above remains true")
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxSessions,
|
|
MAX_NO_SESSIONS,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Turn on logging if not prohibited by administrator
|
|
//
|
|
if (WinsCnf.fLoggingOn)
|
|
{
|
|
|
|
FUTURES("Internationalize the following when jet is internationalized")
|
|
//
|
|
// Turn logging (recovery) on
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramRecovery,
|
|
0, //ignored
|
|
"on"
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// The number of log sectors. Each sector is
|
|
// 512 bytes. We should keep the size more than
|
|
// the threshold so that if the threshold is reached
|
|
// and flushing starts, Jet can still continue to
|
|
// log in the spare sectors. Point to note is that
|
|
// if the log rate is faster than the flush rate, then
|
|
// the Jet engine thread will not be able to log when
|
|
// the entire buffer is filled up. It will then wait
|
|
// until space becomes available.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogBuffers,
|
|
30, //30 sectors
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the number of log buffers dirtied before they
|
|
// are flushed. This number should always be less than
|
|
// the number for LogBuffers so that spare sectors
|
|
// are there for concurrent logging. Also, we should
|
|
// make this number high enough to handle burst of
|
|
// traffic.
|
|
//
|
|
// this is gone in jet600.dll cheen liao 1/6/96
|
|
|
|
//
|
|
// Set the wait time (in msecs) to wait prior to
|
|
// flushing the log on commit transaction to allow
|
|
// other users (sessions) to share the flush
|
|
//
|
|
//
|
|
// This is the time after which the user (a session)
|
|
// will ask the log manager to flush. If we specify
|
|
// 0 here than it means flush every time a transaction
|
|
// commits. In the WINS server case, every insertion
|
|
// or modification is done under an implicit
|
|
// transaction. So, it means that there will be
|
|
// a flush after every such transaction. It has
|
|
// been seen on a 486/66 (Cheen Liao) machine that
|
|
// it takes roughly 16 msecs to do the flush. The
|
|
// time it takes to do the flush is dependent upon
|
|
// the type of disk (how fast it is), the CPU speed,
|
|
// the type of file system etc. We can for now
|
|
// go with the assumption that it is in the range
|
|
// 15-25 msecs. I am pushing for this WaitTime to
|
|
// be made a session specific param so that it can
|
|
// be changed on the fly if the admin. finds that
|
|
// the WINS server is slow due to the WaitTime being
|
|
// very low or if it finds it to be so large that
|
|
// in case of a crash, there is possibility to loose
|
|
// a lot of data.
|
|
//
|
|
// Making this session specific is also very important
|
|
// for replication where we do want to set it to
|
|
// a high value (high enough to ensure that most
|
|
// of the records that need to be inserted are
|
|
// inserted before a flush action takes place. The
|
|
// wait time would be set every time a bunch of
|
|
// records are pulled in for replication. It will
|
|
// be computed based on the number of records pulled
|
|
// in and the time it takes to insert one record in the
|
|
// jet buffer. The wait time should preferably be < than
|
|
// the above computed time (it does not have to be).
|
|
//
|
|
// NOTE: In the Pull thread, I will need to start
|
|
// two sessions, one for updating the OwnerId-Version
|
|
// number table (0 wait time) and the other to
|
|
// update the name-address mapping table (wait time
|
|
// computed based on the factors mentioned above)
|
|
|
|
//
|
|
// The following will set the WaitLogFlush time for
|
|
// all sessions.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramWaitLogFlush,
|
|
0, //wait 0 msecs after commit
|
|
//before flushing
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// There does not seem to be any need to set
|
|
// Log Flush Period.
|
|
//
|
|
|
|
//
|
|
// set the log file path
|
|
//
|
|
if (WinsCnf.pLogFilePath == NULL)
|
|
{
|
|
//
|
|
// We should use the same directory as
|
|
// the one for system.mdb file
|
|
//
|
|
WinsCnf.pLogFilePath = LOGFILE_PATH;
|
|
fFreeMem = FALSE;
|
|
}
|
|
|
|
DBGPRINT1(FLOW, "SetSystemParam: LogFilePath = (%s)\n", WinsCnf.pLogFilePath);
|
|
//
|
|
// Set the log file path.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogFilePath,
|
|
0, //ignored
|
|
WinsCnf.pLogFilePath
|
|
//pLogFilePath
|
|
)
|
|
);
|
|
|
|
//
|
|
// Free this memory. It is not needed any more
|
|
//
|
|
if (fFreeMem)
|
|
{
|
|
WinsMscDealloc(WinsCnf.pLogFilePath);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
if (!RtlEqualMemory(WinsCnf.pLogFilePath, LOGFILE_PATH, sizeof(LOGFILE_PATH)))
|
|
{
|
|
DBGPRINT0(DET, "SetSystemParam: Setting Log file path again\n");
|
|
WinsCnf.pLogFilePath = LOGFILE_PATH;
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogFilePath,
|
|
0, //ignored
|
|
WinsCnf.pLogFilePath
|
|
)
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
return WINS_SUCCESS;
|
|
}
|
|
|
|
STATUS
|
|
SetSystemParamsJet500(
|
|
BOOL fBeforeInit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to set the system parameters for Jet
|
|
|
|
Arguments:
|
|
|
|
fBeforeInit - indicates whether this function has been called
|
|
prior to JetInit
|
|
Externals Used:
|
|
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
--*/
|
|
|
|
{
|
|
JET_ERR JetRetStat;
|
|
DBGENTER("SetSystemParam500\n");
|
|
if (fBeforeInit)
|
|
{
|
|
//
|
|
// Path for the checkpoint file jet.chk to be located
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramSystemPath_OLD,
|
|
0,
|
|
CHKPOINT_PATH
|
|
)
|
|
);
|
|
//
|
|
// Basename to use for jet*.log and jet.chk
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramBaseName_OLD,
|
|
0,
|
|
BASENAME
|
|
)
|
|
);
|
|
|
|
//
|
|
// Max size of the log file in kb.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogFileSize_OLD,
|
|
1024, //set to one full meg (#96543)
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramTempPath_OLD,
|
|
0,
|
|
TEMP_DB_PATH //ignored
|
|
)
|
|
);
|
|
|
|
PERF("Check the following two things")
|
|
//
|
|
// We want some aggressive flushing. The performance impact
|
|
// is very trivial - Ian Jose 7/12/93
|
|
//
|
|
|
|
//
|
|
// The max number of buffers for database usage
|
|
//
|
|
// The default number is 500. 600 events are allocated
|
|
// for 500 buffers -- Ian 10/21/93. Each buffer is
|
|
// 4K. By keeping the number small, we impact performamce
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxBuffers_OLD,
|
|
WinsCnf.NoOfDbBuffers,//200,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
//
|
|
// The max. number of buffers to store old version of a
|
|
// a record (snapshot at the start of a transaction)
|
|
// Each version store is 16k bytes. A version store
|
|
// stores structures that hold information derived from
|
|
// a snapshot of the database prior to an insert (20 bytes
|
|
// roughly) or update (size of the record + 20 bytes).
|
|
//
|
|
// For small transactions (i.e. a transaction around each
|
|
// update), this number should be >= the max. number of
|
|
// sessions that can be updating/inserting at the same time.
|
|
// Each session will have one version bucket. Since 16k of
|
|
// version bucket size can result in a lot of wastage per
|
|
// session (since each record is < .5k, and on the average
|
|
// around 50 bytes), it may be better to specify the
|
|
// max. size of the version bucket (<< 16k). Ian will
|
|
//provide a system param for this if we absolutely need it
|
|
//
|
|
// 3/4/93
|
|
//16kBytes should be enough for the transactions WINS does,
|
|
//but if all the sessions are in transactions at the same time
|
|
//and they all happen to have their small transactions traverse
|
|
//2 buckets then the peak requirement is 2 buckets per session.
|
|
//We could shorten the buckets to 8kBytes, or 4kBytes, and you
|
|
//could allocate 2 per session?
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxVerPages_OLD,
|
|
MAX_NO_SESSIONS * 6, //10-4-95 Bump it up more
|
|
//MAX_NO_SESSIONS * 2,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the File Control Block Param
|
|
//
|
|
// This is the max. number of tables that can be open
|
|
// at any time. If multiple threads open the same table
|
|
// they use the same FCB. FCB is 1 per table/index.
|
|
// Now, for a create database, we need atleast 18 FCBS
|
|
// and 18 IDBS. However apart from create database and
|
|
// ddl operations, we don't need to have these tables open.
|
|
// Default value is 300. Size of an FCB is 112 bytes.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxOpenTables_OLD,
|
|
56, //18 + 10,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// Set the File Usage Control Block to 100.
|
|
// This parameter indicates the max. number of cursors
|
|
// that can be open at any one time. This is
|
|
// therefore dependent on the the max. number of sessions
|
|
// that we can have running concurrently. For each session,
|
|
// there would be 4 cursors (for the two tables) + a certain
|
|
// number of internal cursors. For good measure we add
|
|
// a pad. Default value is 300. Size of each is 200 bytes.
|
|
// We use MAX_SESSIONS * 4 + pad
|
|
// (around 100)
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxCursors_OLD,
|
|
(MAX_NO_SESSIONS * 8 /*4*/) + 32,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the number of index description blocks
|
|
// This is one per table/index. We have two tables
|
|
// each with two indices. We use 9 (see comment for
|
|
// FCBs above). Default value is 300.
|
|
// Size of each is 128 bytes.
|
|
//
|
|
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxOpenTableIndexes_OLD,
|
|
56, //18 + 10,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the Sort Control block.
|
|
// This should be 1 per concurrent Create Index.
|
|
// Default value is 20. Size of each is 612 bytes.
|
|
// In the case of WINS, the main thread creates the
|
|
// indices. We should be setting it to 1. Let us
|
|
// however set it to 3.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxTemporaryTables_OLD ,
|
|
10, //1 + 2,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
//
|
|
// Set the Number for the Database Attribute Blocks
|
|
//
|
|
// This is max. number of Open Databases done. Since we
|
|
// can have a max of MAX_NO_SESSIONS at one time. This should
|
|
// be equal to that number (since we have just one database)
|
|
// Default number is 100. Size is 14 bytes
|
|
//
|
|
// JET_paramMaxOpenDatabase is removed. Jonathan Liem (1/6/97)
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxOpenDatabases_OLD,
|
|
MAX_NO_SESSIONS * 4, //*2,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// The min percentage of buffers not yet dirtied before
|
|
// background flushing begins
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramBfThrshldLowPrcnt_OLD,
|
|
80,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// The max percentage of buffers not yet dirtied before
|
|
// background flushing begins
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramBfThrshldHighPrcnt_OLD,
|
|
100,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// The max. number of sessions that can be open at any time
|
|
//
|
|
// Note: Jet does not preallocate resources corresponding
|
|
// to the max. value. It allocates them dynamically upto
|
|
// the limit -- according to Ian Jose 7/12/93
|
|
//
|
|
// When checked with Ian again on 10/21, he said that they are
|
|
// allocated STATICally
|
|
//
|
|
CHECK("Make sure the comment above remains true")
|
|
FUTURES("Make sure the comment above remains true")
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxSessions_OLD,
|
|
MAX_NO_SESSIONS,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Turn on logging if not prohibited by administrator
|
|
//
|
|
if (WinsCnf.fLoggingOn)
|
|
{
|
|
|
|
FUTURES("Internationalize the following when jet is internationalized")
|
|
//
|
|
// Turn logging (recovery) on
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
30, // JET_paramRecovery not available,
|
|
0, //ignored
|
|
"on"
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// The number of log sectors. Each sector is
|
|
// 512 bytes. We should keep the size more than
|
|
// the threshold so that if the threshold is reached
|
|
// and flushing starts, Jet can still continue to
|
|
// log in the spare sectors. Point to note is that
|
|
// if the log rate is faster than the flush rate, then
|
|
// the Jet engine thread will not be able to log when
|
|
// the entire buffer is filled up. It will then wait
|
|
// until space becomes available.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogBuffers_OLD,
|
|
30, //30 sectors
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the number of log buffers dirtied before they
|
|
// are flushed. This number should always be less than
|
|
// the number for LogBuffers so that spare sectors
|
|
// are there for concurrent logging. Also, we should
|
|
// make this number high enough to handle burst of
|
|
// traffic.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
18, //JET_paramLogFlushThreshold,
|
|
20, //20 sectors dirtied causes
|
|
//flush
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// Set the wait time (in msecs) to wait prior to
|
|
// flushing the log on commit transaction to allow
|
|
// other users (sessions) to share the flush
|
|
//
|
|
//
|
|
// This is the time after which the user (a session)
|
|
// will ask the log manager to flush. If we specify
|
|
// 0 here than it means flush every time a transaction
|
|
// commits. In the WINS server case, every insertion
|
|
// or modification is done under an implicit
|
|
// transaction. So, it means that there will be
|
|
// a flush after every such transaction. It has
|
|
// been seen on a 486/66 (Cheen Liao) machine that
|
|
// it takes roughly 16 msecs to do the flush. The
|
|
// time it takes to do the flush is dependent upon
|
|
// the type of disk (how fast it is), the CPU speed,
|
|
// the type of file system etc. We can for now
|
|
// go with the assumption that it is in the range
|
|
// 15-25 msecs. I am pushing for this WaitTime to
|
|
// be made a session specific param so that it can
|
|
// be changed on the fly if the admin. finds that
|
|
// the WINS server is slow due to the WaitTime being
|
|
// very low or if it finds it to be so large that
|
|
// in case of a crash, there is possibility to loose
|
|
// a lot of data.
|
|
//
|
|
// Making this session specific is also very important
|
|
// for replication where we do want to set it to
|
|
// a high value (high enough to ensure that most
|
|
// of the records that need to be inserted are
|
|
// inserted before a flush action takes place. The
|
|
// wait time would be set every time a bunch of
|
|
// records are pulled in for replication. It will
|
|
// be computed based on the number of records pulled
|
|
// in and the time it takes to insert one record in the
|
|
// jet buffer. The wait time should preferably be < than
|
|
// the above computed time (it does not have to be).
|
|
//
|
|
// NOTE: In the Pull thread, I will need to start
|
|
// two sessions, one for updating the OwnerId-Version
|
|
// number table (0 wait time) and the other to
|
|
// update the name-address mapping table (wait time
|
|
// computed based on the factors mentioned above)
|
|
|
|
//
|
|
// The following will set the WaitLogFlush time for
|
|
// all sessions.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramWaitLogFlush_OLD,
|
|
0, //wait 0 msecs after commit
|
|
//before flushing
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// There does not seem to be any need to set
|
|
// Log Flush Period.
|
|
//
|
|
|
|
//
|
|
// set the log file path
|
|
//
|
|
FUTURES("Use DEFAULT_LOG_PATH after putting it in a header file")
|
|
if (WinsCnf.pLogFilePath == NULL)
|
|
{
|
|
//
|
|
// We should use the same directory as
|
|
// the one for system.mdb file
|
|
//
|
|
|
|
// pLogFilePath = ".\\wins";
|
|
WinsCnf.pLogFilePath = LOGFILE_PATH;
|
|
}
|
|
else
|
|
{
|
|
#if 0
|
|
#ifdef UNICODE
|
|
CHAR AsciiLogFilePath[WINS_MAX_FILENAME_SZ];
|
|
WinsMscConvertUnicodeStringToAscii(
|
|
(LPBYTE)WinsCnf.pLogFilePath,
|
|
AsciiLogFilePath,
|
|
WINS_MAX_FILENAME_SZ
|
|
);
|
|
pLogFilePath = (LPBYTE)AsciiLogFilePath;
|
|
#else
|
|
pLogFilePath = (LPBYTE)WinsCnf.pLogFilePath;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
DBGPRINT1(FLOW, "SetSystemParam: LogFilePath = (%s)\n", WinsCnf.pLogFilePath);
|
|
//
|
|
// Set the log file path.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogFilePath_OLD,
|
|
0, //ignored
|
|
WinsCnf.pLogFilePath
|
|
//pLogFilePath
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
if (!RtlEqualMemory(WinsCnf.pLogFilePath, LOGFILE_PATH, sizeof(LOGFILE_PATH)))
|
|
{
|
|
DBGPRINT0(DET, "SetSystemParam: Setting Log file path again\n");
|
|
WinsCnf.pLogFilePath = LOGFILE_PATH;
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogFilePath_OLD,
|
|
0, //ignored
|
|
WinsCnf.pLogFilePath
|
|
)
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
return WINS_SUCCESS;
|
|
}
|
|
|
|
STATUS
|
|
SetSystemParamsJet200(
|
|
BOOL fBeforeInit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to set the system parameters for Jet
|
|
|
|
Arguments:
|
|
|
|
fBeforeInit - indicates whether this function has been called
|
|
prior to JetInit
|
|
Externals Used:
|
|
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
--*/
|
|
|
|
{
|
|
JET_ERR JetRetStat;
|
|
BOOL fFreeMem = TRUE;
|
|
DBGENTER("SetSystemParam200\n");
|
|
if (fBeforeInit)
|
|
{
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramSysDbPath_OLD,
|
|
0,
|
|
SYS_DB_PATH //ignored
|
|
)
|
|
);
|
|
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramTempPath_OLD,
|
|
0,
|
|
TEMP_DB_PATH //ignored
|
|
)
|
|
);
|
|
PERF("Check the following two things")
|
|
//
|
|
// We want some aggressive flushing. The performance impact
|
|
// is very trivial - Ian Jose 7/12/93
|
|
//
|
|
|
|
//
|
|
// The max number of buffers for database usage
|
|
//
|
|
// The default number is 500. 600 events are allocated
|
|
// for 500 buffers -- Ian 10/21/93. Each buffer is
|
|
// 4K. By keeping the number small, we impact performamce
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxBuffers_OLD,
|
|
WinsCnf.NoOfDbBuffers,//200,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
//
|
|
// The max. number of buffers to store old version of a
|
|
// a record (snapshot at the start of a transaction)
|
|
// Each version store is 16k bytes. A version store
|
|
// stores structures that hold information derived from
|
|
// a snapshot of the database prior to an insert (20 bytes
|
|
// roughly) or update (size of the record + 20 bytes).
|
|
//
|
|
// For small transactions (i.e. a transaction around each
|
|
// update), this number should be >= the max. number of
|
|
// sessions that can be updating/inserting at the same time.
|
|
// Each session will have one version bucket. Since 16k of
|
|
// version bucket size can result in a lot of wastage per
|
|
// session (since each record is < .5k, and on the average
|
|
// around 50 bytes), it may be better to specify the
|
|
// max. size of the version bucket (<< 16k). Ian will
|
|
//provide a system param for this if we absolutely need it
|
|
//
|
|
// 3/4/93
|
|
//16kBytes should be enough for the transactions WINS does,
|
|
//but if all the sessions are in transactions at the same time
|
|
//and they all happen to have their small transactions traverse
|
|
//2 buckets then the peak requirement is 2 buckets per session.
|
|
//We could shorten the buckets to 8kBytes, or 4kBytes, and you
|
|
//could allocate 2 per session?
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxVerPages_OLD,
|
|
MAX_NO_SESSIONS * 6, //10-4-95 Bump it up more
|
|
//MAX_NO_SESSIONS * 2,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the File Control Block Param
|
|
//
|
|
// This is the max. number of tables that can be open
|
|
// at any time. If multiple threads open the same table
|
|
// they use the same FCB. FCB is 1 per table/index.
|
|
// Now, for a create database, we need atleast 18 FCBS
|
|
// and 18 IDBS. However apart from create database and
|
|
// ddl operations, we don't need to have these tables open.
|
|
// Default value is 300. Size of an FCB is 112 bytes.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxOpenTables_OLD,
|
|
56, //18 + 10,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// Set the File Usage Control Block to 100.
|
|
// This parameter indicates the max. number of cursors
|
|
// that can be open at any one time. This is
|
|
// therefore dependent on the the max. number of sessions
|
|
// that we can have running concurrently. For each session,
|
|
// there would be 4 cursors (for the two tables) + a certain
|
|
// number of internal cursors. For good measure we add
|
|
// a pad. Default value is 300. Size of each is 200 bytes.
|
|
// We use MAX_SESSIONS * 4 + pad
|
|
// (around 100)
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxCursors_OLD,
|
|
(MAX_NO_SESSIONS * 8 /*4*/) + 32,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the number of index description blocks
|
|
// This is one per table/index. We have two tables
|
|
// each with two indices. We use 9 (see comment for
|
|
// FCBs above). Default value is 300.
|
|
// Size of each is 128 bytes.
|
|
//
|
|
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxOpenTableIndexes_OLD,
|
|
56, //18 + 10,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the Sort Control block.
|
|
// This should be 1 per concurrent Create Index.
|
|
// Default value is 20. Size of each is 612 bytes.
|
|
// In the case of WINS, the main thread creates the
|
|
// indices. We should be setting it to 1. Let us
|
|
// however set it to 3.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxTemporaryTables_OLD ,
|
|
10, //1 + 2,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
//
|
|
// Set the Number for the Database Attribute Blocks
|
|
//
|
|
// This is max. number of Open Databases done. Since we
|
|
// can have a max of MAX_NO_SESSIONS at one time. This should
|
|
// be equal to that number (since we have just one database)
|
|
// Default number is 100. Size is 14 bytes
|
|
//
|
|
// JET_paramMaxOpenDatabase is removed. Jonathan Liem (1/6/97)
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxOpenDatabases_OLD,
|
|
MAX_NO_SESSIONS * 4, //*2,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// The min percentage of buffers not yet dirtied before
|
|
// background flushing begins
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramBfThrshldLowPrcnt_OLD,
|
|
80,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// The max percentage of buffers not yet dirtied before
|
|
// background flushing begins
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramBfThrshldHighPrcnt_OLD,
|
|
100,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// The max. number of sessions that can be open at any time
|
|
//
|
|
// Note: Jet does not preallocate resources corresponding
|
|
// to the max. value. It allocates them dynamically upto
|
|
// the limit -- according to Ian Jose 7/12/93
|
|
//
|
|
// When checked with Ian again on 10/21, he said that they are
|
|
// allocated STATICally
|
|
//
|
|
CHECK("Make sure the comment above remains true")
|
|
FUTURES("Make sure the comment above remains true")
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramMaxSessions_OLD,
|
|
MAX_NO_SESSIONS,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Turn on logging if not prohibited by administrator
|
|
//
|
|
if (WinsCnf.fLoggingOn)
|
|
{
|
|
|
|
FUTURES("Internationalize the following when jet is internationalized")
|
|
//
|
|
// Turn logging (recovery) on
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
30, // JET_paramRecovery_OLD not available,
|
|
0, //ignored
|
|
"on"
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// The number of log sectors. Each sector is
|
|
// 512 bytes. We should keep the size more than
|
|
// the threshold so that if the threshold is reached
|
|
// and flushing starts, Jet can still continue to
|
|
// log in the spare sectors. Point to note is that
|
|
// if the log rate is faster than the flush rate, then
|
|
// the Jet engine thread will not be able to log when
|
|
// the entire buffer is filled up. It will then wait
|
|
// until space becomes available.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogBuffers_OLD,
|
|
30, //30 sectors
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the number of log buffers dirtied before they
|
|
// are flushed. This number should always be less than
|
|
// the number for LogBuffers so that spare sectors
|
|
// are there for concurrent logging. Also, we should
|
|
// make this number high enough to handle burst of
|
|
// traffic.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
18, // JET_paramLogFlushThreshold,
|
|
20, //20 sectors dirtied causes
|
|
//flush
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
|
|
//
|
|
// Set the wait time (in msecs) to wait prior to
|
|
// flushing the log on commit transaction to allow
|
|
// other users (sessions) to share the flush
|
|
//
|
|
//
|
|
// This is the time after which the user (a session)
|
|
// will ask the log manager to flush. If we specify
|
|
// 0 here than it means flush every time a transaction
|
|
// commits. In the WINS server case, every insertion
|
|
// or modification is done under an implicit
|
|
// transaction. So, it means that there will be
|
|
// a flush after every such transaction. It has
|
|
// been seen on a 486/66 (Cheen Liao) machine that
|
|
// it takes roughly 16 msecs to do the flush. The
|
|
// time it takes to do the flush is dependent upon
|
|
// the type of disk (how fast it is), the CPU speed,
|
|
// the type of file system etc. We can for now
|
|
// go with the assumption that it is in the range
|
|
// 15-25 msecs. I am pushing for this WaitTime to
|
|
// be made a session specific param so that it can
|
|
// be changed on the fly if the admin. finds that
|
|
// the WINS server is slow due to the WaitTime being
|
|
// very low or if it finds it to be so large that
|
|
// in case of a crash, there is possibility to loose
|
|
// a lot of data.
|
|
//
|
|
// Making this session specific is also very important
|
|
// for replication where we do want to set it to
|
|
// a high value (high enough to ensure that most
|
|
// of the records that need to be inserted are
|
|
// inserted before a flush action takes place. The
|
|
// wait time would be set every time a bunch of
|
|
// records are pulled in for replication. It will
|
|
// be computed based on the number of records pulled
|
|
// in and the time it takes to insert one record in the
|
|
// jet buffer. The wait time should preferably be < than
|
|
// the above computed time (it does not have to be).
|
|
//
|
|
// NOTE: In the Pull thread, I will need to start
|
|
// two sessions, one for updating the OwnerId-Version
|
|
// number table (0 wait time) and the other to
|
|
// update the name-address mapping table (wait time
|
|
// computed based on the factors mentioned above)
|
|
|
|
//
|
|
// The following will set the WaitLogFlush time for
|
|
// all sessions.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramWaitLogFlush_OLD,
|
|
0, //wait 0 msecs after commit
|
|
//before flushing
|
|
NULL //ignored
|
|
)
|
|
);
|
|
|
|
//
|
|
// There does not seem to be any need to set
|
|
// Log Flush Period.
|
|
//
|
|
|
|
//
|
|
// set the log file path
|
|
//
|
|
FUTURES("Use DEFAULT_LOG_PATH after putting it in a header file")
|
|
if (WinsCnf.pLogFilePath == NULL)
|
|
{
|
|
//
|
|
// We should use the same directory as
|
|
// the one for system.mdb file
|
|
//
|
|
|
|
// pLogFilePath = ".\\wins";
|
|
WinsCnf.pLogFilePath = LOGFILE_PATH;
|
|
fFreeMem = FALSE;
|
|
}
|
|
else
|
|
{
|
|
#if 0
|
|
#ifdef UNICODE
|
|
CHAR AsciiLogFilePath[WINS_MAX_FILENAME_SZ];
|
|
WinsMscConvertUnicodeStringToAscii(
|
|
(LPBYTE)WinsCnf.pLogFilePath,
|
|
AsciiLogFilePath,
|
|
WINS_MAX_FILENAME_SZ
|
|
);
|
|
pLogFilePath = (LPBYTE)AsciiLogFilePath;
|
|
#else
|
|
pLogFilePath = (LPBYTE)WinsCnf.pLogFilePath;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
DBGPRINT1(FLOW, "SetSystemParam: LogFilePath = (%s)\n", WinsCnf.pLogFilePath);
|
|
//
|
|
// Set the log file path.
|
|
//
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogFilePath_OLD,
|
|
0, //ignored
|
|
WinsCnf.pLogFilePath
|
|
//pLogFilePath
|
|
)
|
|
);
|
|
|
|
//
|
|
// Free this memory. It is not needed any more
|
|
//
|
|
if (fFreeMem)
|
|
{
|
|
WinsMscDealloc(WinsCnf.pLogFilePath);
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
if (!RtlEqualMemory(WinsCnf.pLogFilePath, LOGFILE_PATH, sizeof(LOGFILE_PATH)))
|
|
{
|
|
DBGPRINT0(DET, "SetSystemParam: Setting Log file path again\n");
|
|
WinsCnf.pLogFilePath = LOGFILE_PATH;
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
(JET_SESID)0, //SesId - ignored
|
|
JET_paramLogFilePath_OLD,
|
|
0, //ignored
|
|
WinsCnf.pLogFilePath
|
|
)
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
return WINS_SUCCESS;
|
|
|
|
}
|
|
|
|
STATUS
|
|
SetSystemParams(
|
|
BOOL fBeforeInit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to set the system parameters for Jet
|
|
|
|
Arguments:
|
|
|
|
fBeforeInit - indicates whether this function has been called
|
|
prior to JetInit
|
|
Externals Used:
|
|
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
if (DynLoadJetVersion >= DYN_LOAD_JET_600) {
|
|
|
|
return SetSystemParamsJet600( fBeforeInit );
|
|
}
|
|
else if (DynLoadJetVersion == DYN_LOAD_JET_500)
|
|
{
|
|
|
|
return SetSystemParamsJet500( fBeforeInit );
|
|
|
|
} else {
|
|
|
|
return SetSystemParamsJet200( fBeforeInit );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdHighestVersNoRecIfReqd(
|
|
PWINSTHD_TLS_T pTls,
|
|
PNMSDB_ROW_INFO_T pRowInfo,
|
|
PNMSDB_STAT_INFO_T pStatInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to check if the record being replaced is
|
|
the highest version number record owned by the local WINS. If so,
|
|
the special record that records the highest version number reached
|
|
for local records is updated to reflect the version number of the
|
|
record to be replaced
|
|
|
|
Arguments:
|
|
|
|
pTls - ptr to thread local storage,
|
|
pRowInfo - ptr to info of record to store in db
|
|
pStatInfo - ptr to info of record to replace in db
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
NONE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbUpdateRow, NmsDbSeekNUpdate
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function is always called from inside the NmsNmhNamRegCrtSec
|
|
--*/
|
|
{
|
|
VERS_NO_T MyMaxVersNo;
|
|
|
|
//
|
|
// Decrement the value of the vers. no. counter by 1
|
|
//
|
|
NMSNMH_DEC_VERS_NO_M(NmsNmhMyMaxVersNo,
|
|
MyMaxVersNo
|
|
);
|
|
|
|
//
|
|
// If a local record is being replaced by a replica, then only are we
|
|
// interested in updating the special record
|
|
//
|
|
if ((pStatInfo->OwnerId == NMSDB_LOCAL_OWNER_ID) && (pRowInfo->OwnerId
|
|
!= NMSDB_LOCAL_OWNER_ID))
|
|
{
|
|
//
|
|
// Check if the local record to be replaced has the highest
|
|
// version number that we know of for local records
|
|
//
|
|
if (LiEql(pStatInfo->VersNo, MyMaxVersNo))
|
|
{
|
|
//
|
|
// Update (or insert) the special record that records
|
|
// the highest version number reached
|
|
//
|
|
NmsDbUpdHighestVersNoRec(pTls, MyMaxVersNo, FALSE);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
STATUS
|
|
NmsDbUpdHighestVersNoRec(
|
|
IN PWINSTHD_TLS_T pTls,
|
|
IN VERS_NO_T MyMaxVersNo,
|
|
IN BOOL fEnterCrtSec
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called to update the record that stores the
|
|
highest version number reached for entries owned by the local WINS.
|
|
|
|
Arguments:
|
|
pTls - Thread local storage
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
NONE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbDoScavenging, UpdHighestVersNoRecIfReqd() in nmsdb.c
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
DWORD OwnerId = OWNER_ID_OF_SPEC_REC;
|
|
DWORD FldNo = 0;
|
|
JET_ERR JetRetStat;
|
|
DWORD ActFldLen = 0; //length of fld retrieved
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
DWORD FlagVal = 0;
|
|
COMM_ADD_T Add;
|
|
|
|
DBGENTER("NmsDbUpdHighestVersNoRec\n");
|
|
|
|
//
|
|
// pTls should be non NULL if this function was called by
|
|
// UpdHighestVersNoRecIfReqd()
|
|
//
|
|
if (pTls == NULL)
|
|
{
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
}
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
|
|
/*
|
|
* Set the clustered index as the current index
|
|
*/
|
|
CALL_M(
|
|
JetSetCurrentIndex( SesId,
|
|
TblId,
|
|
NMSDB_NAM_ADD_CLUST_INDEX_NAME
|
|
)
|
|
);
|
|
//
|
|
//
|
|
//if called by UpdHighestVersNoRecIfReqd(), fEnterCrtSec should be
|
|
// FALSE
|
|
//
|
|
if (fEnterCrtSec)
|
|
{
|
|
EnterCriticalSection(&NmsNmhNamRegCrtSec);
|
|
}
|
|
|
|
try {
|
|
|
|
|
|
//
|
|
// If the special record exists in the db, seek to it
|
|
//
|
|
if (sfHighestVersNoRecExists)
|
|
{
|
|
DBGPRINT2(DET, "NmsDbUpdHighestVersNoRec: REPLACING SPECIAL OWNER ID RECORD. New Version # = (%d %d)\n", MyMaxVersNo.HighPart, MyMaxVersNo.LowPart);
|
|
//
|
|
// If the special record's version number is less than the
|
|
// version number passed to us, replace it with the new one
|
|
//
|
|
if (
|
|
(fEnterCrtSec == FALSE) ||
|
|
(LiGtr(MyMaxVersNo, sHighestVersNoSaved))
|
|
)
|
|
{
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
spHighestVersNoRecName,
|
|
sizeof(spHighestVersNoRecName),
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
CALL_M(JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekEQ
|
|
)
|
|
);
|
|
|
|
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
try{
|
|
JetRetStat = JetPrepareUpdate(
|
|
SesId,
|
|
TblId,
|
|
JET_prepReplace
|
|
);
|
|
|
|
if (
|
|
(JetRetStat != JET_errSuccess)
|
|
&&
|
|
(JetRetStat != JET_wrnNoWriteLock)
|
|
)
|
|
{
|
|
RET_M(JetRetStat);
|
|
}
|
|
|
|
//
|
|
// Update the version number
|
|
//
|
|
// add 5th column (this is the version number long(DWORD)
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&MyMaxVersNo,
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
//
|
|
// Update the record
|
|
//
|
|
CALL_M(JetUpdate (
|
|
SesId,
|
|
TblId,
|
|
NULL,
|
|
0L,
|
|
NULL
|
|
)
|
|
);
|
|
}
|
|
finally {
|
|
if (AbnormalTermination())
|
|
{
|
|
CALL_M(JetRollback(SesId, JET_bitRollbackAll));
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetCommitTransaction(SesId, JET_bitCommitFlush));
|
|
sHighestVersNoSaved = MyMaxVersNo;
|
|
}
|
|
}
|
|
}
|
|
#ifdef WINSDBG
|
|
else
|
|
{
|
|
DBGPRINT0(DET, "NmsDbUpdHighestVersNoRec: The record has a higher version number the one we wish to store. NO UPDATE IS BEING MADE\n");
|
|
}
|
|
#endif
|
|
}
|
|
else // special record not there
|
|
{
|
|
DWORD TimeStamp = MAXLONG;
|
|
DBGPRINT2(DET, "NmsDbUpdHighestVersNoRec: INSERTING SPECIAL OWNER ID RECORD. Version # = (%d %d)\n", MyMaxVersNo.HighPart, MyMaxVersNo.LowPart);
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
try {
|
|
JetRetStat = JetPrepareUpdate(
|
|
SesId,
|
|
TblId,
|
|
JET_prepInsert
|
|
);
|
|
|
|
if (
|
|
(JetRetStat != JET_errSuccess)
|
|
&&
|
|
(JetRetStat != JET_wrnNoWriteLock)
|
|
)
|
|
{
|
|
RET_M(JetRetStat);
|
|
}
|
|
|
|
//
|
|
// Set the name
|
|
//
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_NAME_INDEX].Fid,
|
|
spHighestVersNoRecName,
|
|
sizeof(spHighestVersNoRecName),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
|
|
|
|
/* Set the owner byte */
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the version number
|
|
//
|
|
// add 5th column (this is the version number long(DWORD)
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&MyMaxVersNo,
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
//
|
|
// Set the flags column. We mark it STATIC so that
|
|
// the scavenger thread does not pick it up for // scavenging. Even if that were not the case, we still need
|
|
// to set this column to avoid a JET_wrnColumnNull from
|
|
// JetRetrieveColumn (in NmsDbGetDataRecs).
|
|
//
|
|
NMSDB_SET_STATIC_M(FlagVal);
|
|
NMSDB_SET_STATE_M(FlagVal, NMSDB_E_ACTIVE);
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&FlagVal,
|
|
sizeof(FlagVal),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
//
|
|
// set the timestamp column to avoid getting a
|
|
// JET_wrnColumnNull from
|
|
// JetRetrieveColumn (in NmsDbGetDataRecsByName).
|
|
//
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&TimeStamp,
|
|
sizeof(DWORD), /*change type to TIME_STAMP_T
|
|
*later*/
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
//
|
|
// set this address column to avoid a JET_wrnColumnNull from
|
|
// JetRetrieveColumn (in NmsDbGetDataRecsByName).
|
|
//
|
|
CALL_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&Add,
|
|
sizeof(Add),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
//
|
|
// Update the record
|
|
//
|
|
JetRetStat = JetUpdate (
|
|
SesId,
|
|
TblId,
|
|
NULL,
|
|
0L,
|
|
NULL
|
|
);
|
|
|
|
} // end of try block
|
|
finally {
|
|
|
|
if (AbnormalTermination())
|
|
{
|
|
CALL_M(JetRollback(SesId, JET_bitRollbackAll));
|
|
}
|
|
else
|
|
{
|
|
if (JetRetStat == JET_errSuccess)
|
|
{
|
|
CALL_M(JetCommitTransaction(SesId, JET_bitCommitFlush));
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetRollback(SesId, JET_bitRollbackAll));
|
|
}
|
|
|
|
//
|
|
// The only time we will get KeyDuplicate is if somebody
|
|
// entered the special name in the db. In such a
|
|
// situation, we should mark the record as existent
|
|
// such that next time we end up replacing the
|
|
// offensive record. Replacing this record can be
|
|
// done right now but at this stage it is not worth
|
|
// the time required to test it. In any case, the
|
|
// probability of problems due to this are miniscule.
|
|
//
|
|
if ( (JetRetStat == JET_errSuccess) ||
|
|
(JetRetStat == JET_errKeyDuplicate))
|
|
{
|
|
#ifdef WINSDBG
|
|
if (JetRetStat == JET_errKeyDuplicate)
|
|
{
|
|
DBGPRINT0(ERR, "NmsDbUpdHighestVersNoRec: DUPLICATE SPECIAL OWNER ID RECORD\n");
|
|
}
|
|
#endif
|
|
sHighestVersNoSaved = MyMaxVersNo;
|
|
sfHighestVersNoRecExists = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} // end of try { .. }
|
|
finally {
|
|
if (fEnterCrtSec)
|
|
{
|
|
LeaveCriticalSection(&NmsNmhNamRegCrtSec);
|
|
}
|
|
|
|
}
|
|
DBGLEAVE("NmsDbUpdHighestVersNoRec\n");
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
STATUS
|
|
NmsDbDelDataRecs(
|
|
DWORD dwOwnerId,
|
|
VERS_NO_T MinVersNo,
|
|
VERS_NO_T MaxVersNo,
|
|
BOOL fEnterCrtSec,
|
|
BOOL fFragmentedDel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to delete a specified range of records
|
|
of a WINS from the local db
|
|
|
|
Arguments:
|
|
pWinsAdd - Address of owner WINS
|
|
MinVersNo - Min. Vers. No
|
|
MaxVersNo = Max. Vers. No
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
WinsDelDbRecs
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function is called in the Pull thread or an RPC thread.
|
|
On exit, it sets the index to the clustered index on the
|
|
name-address table
|
|
--*/
|
|
|
|
{
|
|
JET_ERR JetRetStat;
|
|
DWORD ActFldLen; //length of fld retrieved
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
VERS_NO_T VersNo;
|
|
#if NEW_OWID
|
|
DWORD RecordOwnerId;
|
|
#else
|
|
DWORD RecordOwnerId = 0;
|
|
#endif
|
|
|
|
DWORD NoOfRecsUpd = 0;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
BOOL fAllToBeDeleted = FALSE;
|
|
//BOOL fTransActive = FALSE;
|
|
BOOL fEntered = FALSE;
|
|
DWORD Count = 0;
|
|
LONG RetVal;
|
|
|
|
DBGENTER("NmsDbDelDataRecs\n");
|
|
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
if (fEnterCrtSec)
|
|
{
|
|
EnterCriticalSection(&NmsNmhNamRegCrtSec);
|
|
fEntered = TRUE;
|
|
}
|
|
if (dwOwnerId == NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
NMSNMH_DEC_VERS_NO_M(NmsNmhMyMaxVersNo, VersNo);
|
|
}
|
|
else
|
|
{
|
|
if (WinsCnf.State_e != WINSCNF_E_INITING)
|
|
{
|
|
EnterCriticalSection(&RplVersNoStoreCrtSec);
|
|
VersNo = (pRplPullOwnerVersNo+dwOwnerId)->VersNo;
|
|
LeaveCriticalSection(&RplVersNoStoreCrtSec);
|
|
}
|
|
else
|
|
{
|
|
VersNo = (pRplPullOwnerVersNo+dwOwnerId)->VersNo;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If both minimum and maximum version numbers specified are 0,
|
|
// it means all the records of the WINS need to be deleted
|
|
//
|
|
if (LiEqlZero(MinVersNo) && LiEqlZero(MaxVersNo))
|
|
{
|
|
fAllToBeDeleted = TRUE;
|
|
}
|
|
#if 0
|
|
else
|
|
{
|
|
if (LiGtr(MinVersNo, VersNo))
|
|
{
|
|
DBGPRINT4(DET, "NmsDbDelDataRecs: Wrong range to delete. Min. Vers. no (%d %d) is > the max. (%d %d) that this WINS server knows of.\n",
|
|
MinVersNo.HighPart, MinVersNo.LowPart,
|
|
VersNo.HighPart, VersNo.LowPart,
|
|
);
|
|
|
|
LeaveCriticalSection(&NmsNmhNamRegCrtSec);
|
|
return(WINS_FAILURE);
|
|
}
|
|
|
|
//
|
|
// We should never attempt to delete a record that is not in
|
|
// our database currently
|
|
//
|
|
MaxVersNo = LiGtr(MaxVersNo, VersNo) ? VersNo : MaxVersNo;
|
|
}
|
|
#endif
|
|
|
|
try {
|
|
|
|
//
|
|
// Let us make sure that the special record points to the highest
|
|
// version number that we know of for local records. Note:
|
|
// When there is atleast one record of a higher version number
|
|
// than the highest version numbered record to be deleted,
|
|
// there is no need to update the special record. Checking
|
|
// whether this is the case would be more overhead (in general).
|
|
//We therefore use the strategem of always updating the special record.
|
|
//
|
|
if (dwOwnerId == NMSDB_LOCAL_OWNER_ID)
|
|
{
|
|
NmsDbUpdHighestVersNoRec(pTls, VersNo, FALSE);
|
|
}
|
|
|
|
//
|
|
// Don't start a transaction since if the number of records are
|
|
// huge, the transaction can become long in duration and JetDelete
|
|
// may return an "out of Memory" error.
|
|
//
|
|
// Ian's comments on 8/26/94
|
|
//
|
|
// If you call JetDelete outside of any transaction, then JET
|
|
// internally wraps a begin transction/commit trnasaction around the
|
|
// delete. Another user at transaction level 0 will immediately see
|
|
// this change, but another user in a transction, i.e. at transaction
|
|
// level 1 or greater, will not see this change until they return to
|
|
// transaction level 0.
|
|
|
|
//
|
|
// Thus, you do not have to delete records in a transaction, unless
|
|
// you are deleting multiple records which must be deleted atomically,
|
|
// or which must be seen to be deleted atomically.
|
|
//
|
|
|
|
//CALL_M(JetBeginTransaction(SesId));
|
|
//fTransActive = TRUE;
|
|
|
|
do {
|
|
|
|
if (fFragmentedDel && fEnterCrtSec && !fEntered)
|
|
{
|
|
EnterCriticalSection(&NmsNmhNamRegCrtSec);
|
|
fEntered = TRUE;
|
|
}
|
|
|
|
CALL_M( JetSetCurrentIndex(
|
|
pTls->SesId,
|
|
pTls->NamAddTblId,
|
|
NMSDB_NAM_ADD_PRIM_INDEX_NAME
|
|
)
|
|
);
|
|
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&dwOwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
JET_bitNewKey //since this is the first
|
|
//data value of the key
|
|
)
|
|
);
|
|
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&MinVersNo,
|
|
sizeof(VERS_NO_T),
|
|
0 //0 for grbit since this is not the
|
|
//first component of the key
|
|
)
|
|
);
|
|
|
|
JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekGE
|
|
);
|
|
|
|
if (JetRetStat != JET_errRecordNotFound)
|
|
{
|
|
do {
|
|
|
|
CALL_M(JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&RecordOwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// if only tombstones are required, it means that we need
|
|
// all tombstones irrespective of owner
|
|
//
|
|
if (RecordOwnerId != dwOwnerId )
|
|
{
|
|
//
|
|
// We have exhausted all records for the owner. Break out
|
|
// of the loop
|
|
//
|
|
RetVal = -1; //to break out of the out loop
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieve the version number
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&VersNo,
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// If MaxVersNo is not zero and VersNo retrieved is
|
|
// greater than it, break out of the loop.
|
|
//
|
|
// NOTE: fAllToBeDeleted is used instead of LiEqlZero()
|
|
// since the latter is a function call and would be
|
|
// costlier (this is the reason, why fAllToBeDeleted exists)
|
|
//
|
|
if (!fAllToBeDeleted && LiGtr(VersNo, MaxVersNo))
|
|
{
|
|
//
|
|
// We have acquired records upto MaxVersNo. Break out
|
|
// of the loop
|
|
//
|
|
RetVal = -1; // to break out of the outer loop
|
|
break;
|
|
}
|
|
|
|
CALL_M(JetDelete(
|
|
SesId,
|
|
TblId
|
|
)
|
|
);
|
|
|
|
#ifdef WINSDBG
|
|
NmsDbDelDelDataRecs++;
|
|
#endif
|
|
NoOfRecsUpd++;
|
|
} while(
|
|
((RetVal = JetMove(SesId, TblId, JET_MoveNext, 0)) >= 0)
|
|
&&
|
|
(++Count < 50)
|
|
);
|
|
|
|
if (fFragmentedDel && fEntered)
|
|
{
|
|
LeaveCriticalSection(&NmsNmhNamRegCrtSec);
|
|
fEntered = FALSE;
|
|
MinVersNo = VersNo;
|
|
Count = 0;
|
|
}
|
|
if (RetVal < 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT0(DET, "NmsDbDelDataRecs: There are no records to delete\n");
|
|
RetStat = WINS_SUCCESS;
|
|
break;
|
|
}
|
|
} while (TRUE);
|
|
} // end of try
|
|
finally {
|
|
#if 0
|
|
if (AbnormalTermination())
|
|
{
|
|
if (fTransActive)
|
|
{
|
|
CALL_M(JetRollback(SesId, JET_bitRollbackAll));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetCommitTransaction(SesId, JET_bitCommitFlush));
|
|
}
|
|
#endif
|
|
DBGPRINT3(SCV, "NmsDbDelDataRecs: Deleted records of owner id = (%d) in the range (%x - %x)\n", dwOwnerId, MinVersNo, VersNo);
|
|
if (fEntered)
|
|
{
|
|
LeaveCriticalSection(&NmsNmhNamRegCrtSec);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Change the index to clustered
|
|
//
|
|
CALL_M( JetSetCurrentIndex(
|
|
pTls->SesId,
|
|
pTls->NamAddTblId,
|
|
NMSDB_NAM_ADD_CLUST_INDEX_NAME
|
|
)
|
|
);
|
|
} // end of finally
|
|
|
|
WinsEvtLogDetEvt(TRUE, WINS_EVT_DEL_RECS, NULL, __LINE__, "ddddd",
|
|
dwOwnerId, MinVersNo.LowPart, MinVersNo.HighPart,
|
|
VersNo.LowPart, VersNo.HighPart);
|
|
|
|
DBGPRINT1(DET, "NmsDbDelDataRecs: No. Of. records deleted = (%d)\n", NoOfRecsUpd);
|
|
DBGLEAVE("NmsDbDelDataRecs\n");
|
|
return(RetStat);
|
|
}
|
|
|
|
STATUS
|
|
NmsDbTombstoneDataRecs(
|
|
DWORD dwOwnerId,
|
|
VERS_NO_T MinVersNo,
|
|
VERS_NO_T MaxVersNo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to tombstone a specified range of records
|
|
of a WINS from the local db
|
|
|
|
Arguments:
|
|
pWinsAdd - Address of owner WINS
|
|
MinVersNo - Min. Vers. No
|
|
MaxVersNo = Max. Vers. No
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Called by:
|
|
WinsTombstoneDbRecs
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function is called on RPC thread.
|
|
On exit, it sets the index to the clustered index on the
|
|
name-address table
|
|
--*/
|
|
|
|
{
|
|
JET_ERR JetRetStat;
|
|
DWORD ActFldLen; //length of fld retrieved
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
DWORD RecordOwnerId = 0;
|
|
DWORD NoOfRecsUpd = 0;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
BOOL fAllToBeTombstoned = FALSE;
|
|
DWORD Count = 0;
|
|
LONG RetVal;
|
|
BOOL fIncVersNo;
|
|
VERS_NO_T VersNo;
|
|
DWORD FlagVal;
|
|
BOOL LockHeld = FALSE;
|
|
BOOL UpdateOwnerId = FALSE;
|
|
DWORD_PTR NewTimeStamp;
|
|
time_t CurrentTime;
|
|
DWORD dwNewOwnerId;
|
|
|
|
DBGENTER("NmsDbTombstoneDataRecs\n");
|
|
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
|
|
(void)time(&CurrentTime);
|
|
NewTimeStamp = CurrentTime + WinsCnf.TombstoneTimeout;
|
|
DBGPRINT1(DET, "NmsDbTombstoneDataRecs: The new tombstone Time is %.19s\n",
|
|
asctime(localtime(&NewTimeStamp)));
|
|
|
|
if (NMSDB_LOCAL_OWNER_ID != dwOwnerId) {
|
|
UpdateOwnerId = TRUE;
|
|
dwNewOwnerId = NMSDB_LOCAL_OWNER_ID;
|
|
}
|
|
|
|
// If both minimum and maximum version numbers specified are 0,
|
|
// it means all the records of the WINS need to be deleted
|
|
if (LiEqlZero(MinVersNo) && LiEqlZero(MaxVersNo)){
|
|
if (NMSDB_LOCAL_OWNER_ID == dwOwnerId) {
|
|
MaxVersNo = NmsNmhMyMaxVersNo;
|
|
} else {
|
|
fAllToBeTombstoned = TRUE;
|
|
}
|
|
}
|
|
|
|
CALL_N_JMP_M( JetSetCurrentIndex(
|
|
pTls->SesId,
|
|
pTls->NamAddTblId,
|
|
NMSDB_NAM_ADD_PRIM_INDEX_NAME
|
|
),
|
|
Cleanup
|
|
);
|
|
CALL_N_JMP_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&dwOwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
JET_bitNewKey //since this is the first
|
|
),
|
|
Cleanup
|
|
);
|
|
CALL_N_JMP_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&MinVersNo,
|
|
sizeof(VERS_NO_T),
|
|
0 //0 for grbit since this is not the
|
|
),
|
|
Cleanup
|
|
);
|
|
JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekGE
|
|
);
|
|
if (JetRetStat == JET_errRecordNotFound) {
|
|
DBGPRINT0(DET, "NmsDbTombstoneDataRecs: There are no records to tombstone\n");
|
|
RetStat = WINS_FAILURE;
|
|
goto Cleanup;
|
|
}
|
|
if (JetRetStat != JET_errSuccess && JetRetStat != JET_wrnSeekNotEqual) {
|
|
DBGPRINT1(ERR, "NmsDbTombstoneDataRecs: JetSeek failed with %ld\n",JetRetStat);
|
|
RetStat = WINS_FAILURE;
|
|
goto Cleanup;
|
|
}
|
|
while (TRUE) {
|
|
// tombstone 50 recs at a time so that we dont hold crit section
|
|
// for long time.
|
|
EnterCriticalSection(&NmsNmhNamRegCrtSec);
|
|
LockHeld = TRUE;
|
|
|
|
do {
|
|
CALL_N_JMP_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&RecordOwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL),
|
|
Cleanup
|
|
);
|
|
if (RecordOwnerId != dwOwnerId ){
|
|
// We have exhausted all records for the owner. Break of the loop
|
|
goto Cleanup;
|
|
}
|
|
// Retrieve the version number
|
|
CALL_N_JMP_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&VersNo,
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL),
|
|
Cleanup
|
|
);
|
|
|
|
DBGPRINT2(DET, "NmsDbTombstoneDataRecs: tombstone record - (%lx - %lx)\n", VersNo.HighPart, VersNo.LowPart);
|
|
// If MaxVersNo is not zero and VersNo retrieved is
|
|
// greater than it, break out of the loop.
|
|
if (!fAllToBeTombstoned && LiGtr(VersNo, MaxVersNo)){
|
|
// We have acquired records upto MaxVersNo. Break of the loop
|
|
goto Cleanup;
|
|
}
|
|
// retrieve the flags column
|
|
CALL_N_JMP_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&FlagVal,
|
|
sizeof(FlagVal),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
),
|
|
Cleanup
|
|
);
|
|
|
|
CALL_N_JMP_M(JetBeginTransaction(SesId),Cleanup);
|
|
try {
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetPrepareUpdate(
|
|
SesId,
|
|
TblId,
|
|
JET_prepReplace
|
|
)
|
|
);
|
|
|
|
// make it tombstone.
|
|
NMSDB_SET_STATE_M(FlagVal,NMSDB_E_TOMBSTONE);
|
|
|
|
// Update the flags field
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&FlagVal,
|
|
sizeof(FlagVal),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
VersNo = NmsNmhMyMaxVersNo;
|
|
|
|
// Update the version number field so that this record gets
|
|
// propagated eventually
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&VersNo,
|
|
sizeof(VERS_NO_T),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
if (UpdateOwnerId) {
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&dwNewOwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetSetColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&NewTimeStamp,
|
|
sizeof(DWORD),
|
|
0,
|
|
NULL /*optional info */
|
|
)
|
|
);
|
|
|
|
// Update the record
|
|
CALL_N_RAISE_EXC_IF_ERR_M(JetUpdate (
|
|
SesId,
|
|
TblId,
|
|
NULL,
|
|
0L,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
} // end of try block
|
|
finally {
|
|
if (AbnormalTermination()){
|
|
CALL_N_JMP_M(JetRollback(SesId,JET_bitRollbackAll), Cleanup);
|
|
}else{
|
|
CALL_N_JMP_M(JetCommitTransaction(SesId,JET_bitCommitFlush), Cleanup);
|
|
NMSNMH_INC_VERS_COUNTER_M(NmsNmhMyMaxVersNo,NmsNmhMyMaxVersNo);
|
|
NoOfRecsUpd++;
|
|
}
|
|
}
|
|
} while(((RetVal = JetMove(SesId, TblId, JET_MoveNext, 0)) >= 0)&&(++Count < 50));
|
|
|
|
LeaveCriticalSection(&NmsNmhNamRegCrtSec);
|
|
LockHeld = FALSE;
|
|
DBGPRINT2(SCV, "NmsDbTombstoneDataRecs: tombstoned records %ld, RetVal %ld, \n", Count, RetVal);
|
|
Count = 0;
|
|
if (RetVal < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
DBGPRINT3(SCV, "NmsDbTombstoneDataRecs: tombstone records of owner id = (%d) in the range (%x - %x)\n", dwOwnerId, MinVersNo, VersNo);
|
|
WinsEvtLogDetEvt(TRUE, WINS_EVT_DEL_RECS, NULL, __LINE__, "ddddd",
|
|
dwOwnerId, MinVersNo.LowPart, MinVersNo.HighPart,
|
|
VersNo.LowPart, VersNo.HighPart);
|
|
|
|
|
|
Cleanup:
|
|
// Change the index to clustered
|
|
JetSetCurrentIndex(
|
|
pTls->SesId,
|
|
pTls->NamAddTblId,
|
|
NMSDB_NAM_ADD_CLUST_INDEX_NAME
|
|
);
|
|
|
|
|
|
if (!LockHeld) {
|
|
EnterCriticalSection(&NmsNmhNamRegCrtSec);
|
|
}
|
|
if (NoOfRecsUpd) RPL_PUSH_NTF_M(RPL_PUSH_NO_PROP, NULL, NULL, NULL);
|
|
LeaveCriticalSection(&NmsNmhNamRegCrtSec);
|
|
|
|
|
|
DBGPRINT1(DET, "NmsDbTombstoneDataRecs: No. Of. records tombstoned = (%d)\n",NoOfRecsUpd);
|
|
DBGLEAVE("NmsDbTombstoneDataRecs\n");
|
|
return(RetStat);
|
|
}
|
|
|
|
|
|
STATUS
|
|
NmsDbSetFlushTime(
|
|
DWORD WaitTime
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to set a session specific flush time
|
|
|
|
Arguments:
|
|
WaitTime - Time in msecs to wait after a commit
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
None
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
RplPullInit
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
PWINSTHD_TLS_T pTls;
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
|
|
if (DynLoadJetVersion >= DYN_LOAD_JET_600) {
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
pTls->SesId,
|
|
JET_paramWaitLogFlush,
|
|
WaitTime,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
} else {
|
|
CALL_M(JetSetSystemParameter(
|
|
&sJetInstance,
|
|
pTls->SesId,
|
|
JET_paramWaitLogFlush_OLD,
|
|
WaitTime,
|
|
NULL //ignored
|
|
)
|
|
);
|
|
}
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
STATUS
|
|
NmsDbOpenTables(
|
|
WINS_CLIENT_E Client_e //client
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function opens one or both of name-address mapping and
|
|
owner-address mapping tables. It further starts a transaction
|
|
|
|
Arguments:
|
|
|
|
Client_e - Client
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
PWINSTHD_TLS_T pTls;
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
//
|
|
// Open the name to address mapping table
|
|
//
|
|
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetOpenTable(
|
|
pTls->SesId,
|
|
pTls->DbId,
|
|
NMSDB_NAM_ADD_TBL_NM,
|
|
NULL, /*ptr to parameter list; should be
|
|
*non-NULL only if a query is being
|
|
*opened (not the case here)*/
|
|
0, /*Length of above parameter list*/
|
|
0, //shared access
|
|
&pTls->NamAddTblId
|
|
)
|
|
);
|
|
|
|
// DBGPRINT2(SPEC, "NmsDbOpenTables: OPENED NAME-ADD table for client = (%d). Table id is (%x)\n", Client_e, pTls->NamAddTblId);
|
|
|
|
pTls->fNamAddTblOpen = TRUE;
|
|
|
|
/*
|
|
* If the client is not the replicator (i.e. it is the Name Space
|
|
* Manager (Nbt thread) or an RPC thread, we want to set the current
|
|
* index on the Name Address Mapping table to the clustered index.
|
|
* We are not interested in the Owner to Address Mapping table in the
|
|
* database (it has already been read into the in-memory table
|
|
* NmsDbOwnAddTbl which is what we are interested in).
|
|
*/
|
|
if (
|
|
(Client_e != WINS_E_RPLPULL)
|
|
&&
|
|
(Client_e != WINS_E_RPLPUSH)
|
|
&&
|
|
(Client_e != WINS_E_NMSSCV)
|
|
)
|
|
{
|
|
/*
|
|
Set the clustered index as the current index
|
|
*/
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetSetCurrentIndex(
|
|
pTls->SesId,
|
|
pTls->NamAddTblId,
|
|
NMSDB_NAM_ADD_CLUST_INDEX_NAME
|
|
)
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The client is a replicator thread.
|
|
*/
|
|
if (Client_e == WINS_E_RPLPUSH)
|
|
{
|
|
/*
|
|
* Set the primary index as the current index
|
|
*/
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetSetCurrentIndex(
|
|
pTls->SesId,
|
|
pTls->NamAddTblId,
|
|
NMSDB_NAM_ADD_PRIM_INDEX_NAME
|
|
)
|
|
|
|
);
|
|
}
|
|
else // it is the PULL thread
|
|
{
|
|
|
|
/*
|
|
*Set the clustered index as the current index
|
|
*/
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetSetCurrentIndex(
|
|
pTls->SesId,
|
|
pTls->NamAddTblId,
|
|
NMSDB_NAM_ADD_CLUST_INDEX_NAME
|
|
)
|
|
);
|
|
}
|
|
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetOpenTable(
|
|
pTls->SesId,
|
|
pTls->DbId,
|
|
NMSDB_OWN_ADD_TBL_NM,
|
|
NULL, /*ptr to parameter list; should be
|
|
*non-NULL only if a query is being
|
|
*opened*/
|
|
0, /*Length of above parameter list*/
|
|
0, //shared access
|
|
&pTls->OwnAddTblId
|
|
)
|
|
);
|
|
|
|
// DBGPRINT2(SPEC, "NmsDbOpenTables: Opened OWN-ADD table for client = (%d). Table id is (%x)\n", Client_e, pTls->OwnAddTblId);
|
|
|
|
pTls->fOwnAddTblOpen = TRUE;
|
|
|
|
/*
|
|
Set the clustered index as the current index
|
|
*/
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetSetCurrentIndex(
|
|
pTls->SesId,
|
|
pTls->OwnAddTblId,
|
|
NMSDB_OWN_ADD_CLUST_INDEX_NAME
|
|
)
|
|
|
|
);
|
|
}
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
STATUS
|
|
NmsDbCloseTables(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to close the tables that were opened
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
None
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
|
|
PWINSTHD_TLS_T pTls;
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
if (pTls->fNamAddTblOpen)
|
|
{
|
|
CALL_N_RAISE_EXC_IF_ERR_M(JetCloseTable(
|
|
pTls->SesId,
|
|
pTls->NamAddTblId
|
|
)
|
|
);
|
|
|
|
// DBGPRINT1(SPEC, "NmsDbCloseTables: CLOSED NAME-ADD table. Table id is (%x)\n", pTls->NamAddTblId);
|
|
pTls->fNamAddTblOpen = FALSE;
|
|
}
|
|
|
|
if (pTls->fOwnAddTblOpen)
|
|
{
|
|
CALL_N_RAISE_EXC_IF_ERR_M(JetCloseTable(
|
|
pTls->SesId,
|
|
pTls->OwnAddTblId
|
|
)
|
|
);
|
|
// DBGPRINT1(SPEC, "NmsDbCloseTables: CLOSED NAME-ADD table. Table id is (%x)\n", pTls->OwnAddTblId);
|
|
pTls->fOwnAddTblOpen = FALSE;
|
|
}
|
|
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
STATUS
|
|
NmsDbGetNamesWPrefixChar(
|
|
IN BYTE PrefixChar,
|
|
OUT PWINSINTF_BROWSER_INFO_T *ppInfo,
|
|
OUT LPDWORD pEntriesRead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function retrieves all records starting with PrefixChar
|
|
|
|
Arguments:
|
|
PrefixChar - Prefix character
|
|
ppInfo - address of pointer to info structure
|
|
pEntriesRead - Entries read
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes -- WINS_SUCCESS
|
|
Error status codes -- WINS_FAILURE
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
WinsGetNames
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
PWINSTHD_TLS_T pTls;
|
|
volatile DWORD Iter = 0;
|
|
JET_SESID SesId;
|
|
JET_TABLEID TblId;
|
|
JET_ERR JetRetStat;
|
|
DWORD Flag;
|
|
DWORD ActFldLen; //length of fld retrieved
|
|
PWINSINTF_BROWSER_INFO_T pInfo;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
|
|
DWORD CommitCnt = 1; //the number of commits already done - do not change
|
|
BOOL fTransCommitted = TRUE; // says whether the last commit should be done or not
|
|
DWORD dwEntriesAvailable; // number of records for which storage is available
|
|
|
|
|
|
DBGENTER("NmsDbGetNamesWPrefixChar\n");
|
|
|
|
//
|
|
// Initialize the out args to default values
|
|
//
|
|
*pEntriesRead = 0;
|
|
*ppInfo = NULL;
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
SesId = pTls->SesId;
|
|
TblId = pTls->NamAddTblId;
|
|
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
fTransCommitted = FALSE;
|
|
try {
|
|
|
|
// dwEntriesAvailable shows how many records were found during the first iteration
|
|
// (when it is incremented) and how many records are to be read during the second
|
|
// iteration (when it is decremented)
|
|
dwEntriesAvailable = 0;
|
|
|
|
//
|
|
// We iterate a max of two times, the first time to get the
|
|
// count of records and the second time to get the records
|
|
//
|
|
while(Iter < 2)
|
|
{
|
|
//
|
|
// Seek to the first record starting with 1B character
|
|
//
|
|
CALL_N_JMP_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&PrefixChar,
|
|
sizeof(BYTE),
|
|
JET_bitNewKey
|
|
), ErrorProc
|
|
);
|
|
if ((JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekGE
|
|
)) != JET_errRecordNotFound)
|
|
{
|
|
|
|
BYTE Name[NMSDB_MAX_NAM_LEN];
|
|
DWORD NameLen;
|
|
|
|
if (JetRetStat != JET_wrnSeekNotEqual)
|
|
{
|
|
CALL_N_JMP_M(JetRetStat, ErrorProc);
|
|
}
|
|
|
|
//
|
|
// Move one record at a time until we get to a record that
|
|
// does not have 1B as the starting prefix.
|
|
//
|
|
do
|
|
{
|
|
BOOL bFiltered;
|
|
|
|
//
|
|
// retrieve the name
|
|
//
|
|
CALL_N_JMP_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_NAME_INDEX].Fid,
|
|
Name,
|
|
NMSDB_MAX_NAM_LEN,
|
|
&NameLen,
|
|
0,
|
|
NULL
|
|
), ErrorProc
|
|
);
|
|
|
|
//
|
|
// Check if the first character is 1B
|
|
//
|
|
if (Name[0] != PrefixChar)
|
|
{
|
|
break;
|
|
}
|
|
if ((NameLen < WINS_MAX_NS_NETBIOS_NAME_LEN) || (Name[NameLen - 2] == 0))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// --ft:10/18/00
|
|
// Add 1B name filtering here, if there is a filter specified for 1B names
|
|
//
|
|
EnterCriticalSection(&g_cs1BFilter);
|
|
bFiltered = IsNmInFilter(g_p1BFilter, Name, WINS_MAX_NS_NETBIOS_NAME_LEN-1);
|
|
LeaveCriticalSection(&g_cs1BFilter);
|
|
|
|
if (!bFiltered)
|
|
continue;
|
|
//
|
|
// --tf
|
|
|
|
if (Iter == 1)
|
|
{
|
|
|
|
//
|
|
// Retrieve the flag byte
|
|
//
|
|
CALL_N_JMP_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&Flag,
|
|
sizeof(Flag),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
), ErrorProc
|
|
);
|
|
|
|
|
|
if (!NMSDB_ENTRY_ACT_M(Flag))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// specify the length of the string otherwise RPC
|
|
// will transport up to the first '\0'. For shorter names this would lead
|
|
// to loosing the record type on the way..
|
|
pInfo->dwNameLen = NameLen;
|
|
pInfo->pName = midl_user_allocate(NameLen + 1);
|
|
RtlMoveMemory(pInfo->pName, Name, NameLen);
|
|
|
|
// add this to make sure RPC doesn't go over limits.
|
|
// RPC is seeing pName as 'string' which makes it to pick up bytes
|
|
// up to the first '\0'.
|
|
// This hides a bug for names that contain extended chars (with '\0'
|
|
// somewhere in the middle) but fixing this breaks compatibility with
|
|
// Win2K (querying Win2K results in RPC not being able to unmarshall
|
|
// the responses and causing WinsGetBrowser to fail entirely).
|
|
pInfo->pName[NameLen] = '\0';
|
|
|
|
//
|
|
// Swap the first and 16th byte
|
|
//
|
|
WINS_SWAP_BYTES_M(pInfo->pName,
|
|
pInfo->pName + 15
|
|
);
|
|
pInfo++;
|
|
|
|
// increment the number of records that have been retrieved
|
|
(*pEntriesRead)++;
|
|
|
|
// check if there remains storage for more entries
|
|
dwEntriesAvailable--;
|
|
|
|
// if no memory available, break the loop
|
|
if (dwEntriesAvailable == 0)
|
|
break;
|
|
|
|
}
|
|
else
|
|
{
|
|
dwEntriesAvailable++;
|
|
// increment pEntriesRead here just to be able to control
|
|
// the granularity of [BeginTransaction()..CommitTransaction()] during both
|
|
// iterations
|
|
(*pEntriesRead)++;
|
|
}
|
|
|
|
//
|
|
// decrease the granularity of [BeginTransaction()..CommitTransaction()] intervals
|
|
//
|
|
if (*pEntriesRead/CommitCnt >= MAX_RECS_BEFORE_COMMIT)
|
|
{
|
|
CALL_M(
|
|
JetCommitTransaction(SesId, JET_bitCommitFlush)
|
|
);
|
|
fTransCommitted = TRUE;
|
|
CommitCnt++;
|
|
CALL_M( JetBeginTransaction(SesId) );
|
|
fTransCommitted = FALSE;
|
|
}
|
|
|
|
} while(JetMove(SesId, TblId, JET_MoveNext, 0) >= 0);
|
|
|
|
//
|
|
// If we found records, allocate memory to store them
|
|
//
|
|
if ((Iter == 0) && (dwEntriesAvailable != 0))
|
|
{
|
|
*ppInfo = midl_user_allocate(dwEntriesAvailable *
|
|
sizeof(WINSINTF_BROWSER_INFO_T));
|
|
pInfo = *ppInfo;
|
|
// reset the pEntriesRead, as from now on it will really count the records that have been retrieved.
|
|
*pEntriesRead = 0;
|
|
}
|
|
else
|
|
{
|
|
// either two iterations already done, or no entries detected during the first iteration.
|
|
// break the loop in either case, otherwise AV could happen or even worse, other locations
|
|
// from the memory space of the same process might get overwritten.
|
|
break;
|
|
}
|
|
|
|
Iter++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we failed in the first seek, initialize the out vars
|
|
// to indicate that there are no records. If we failed in
|
|
// the second seek, set return status to WINS_FAILURE, so
|
|
// that we do any cleanup that is required
|
|
//
|
|
if (Iter == 0)
|
|
{
|
|
*pEntriesRead = 0;
|
|
*ppInfo = NULL;
|
|
}
|
|
else
|
|
{
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
break; //break out of the while loop
|
|
}
|
|
|
|
//
|
|
// if no entries were read from the db, break;
|
|
//
|
|
if (dwEntriesAvailable == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
} // end of while
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DWORD ExcCode = GetExceptionCode();
|
|
DBGPRINT1(EXC, "NmsDbGetNamesWPrefixChar. Got Exception (%x)",
|
|
ExcCode);
|
|
WINSEVT_LOG_M(ExcCode, WINS_EVT_BROWSER_NAME_EXC);
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
|
|
if (RetStat == WINS_SUCCESS)
|
|
{
|
|
goto Done;
|
|
}
|
|
ErrorProc:
|
|
//
|
|
// if memory was allocated, do cleanup
|
|
//
|
|
if (*ppInfo != NULL)
|
|
{
|
|
//
|
|
// If any memory was allocated for names, free it
|
|
//
|
|
pInfo = *ppInfo;
|
|
while (*pEntriesRead > 0)
|
|
{
|
|
midl_user_free(pInfo++->pName);
|
|
(*pEntriesRead)--;
|
|
}
|
|
//
|
|
// Free the main block
|
|
//
|
|
midl_user_free(*ppInfo);
|
|
|
|
//
|
|
// Reinit the out args to indicate no records to the client
|
|
//
|
|
*ppInfo = NULL;
|
|
*pEntriesRead = 0;
|
|
}
|
|
RetStat = WINS_FAILURE;
|
|
|
|
Done:
|
|
|
|
if (!fTransCommitted)
|
|
CALL_M(JetCommitTransaction(SesId, JET_bitCommitFlush));
|
|
DBGLEAVE("NmsDbGetNamesWPrefixChar\n");
|
|
return(RetStat);
|
|
|
|
} // NmsDbGetNamesWPrefixChar
|
|
|
|
STATUS
|
|
NmsDbCleanupOwnAddTbl(
|
|
LPDWORD pNoOfOwners
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called by the scavenger thread to cleanup
|
|
the OwnAdd Table
|
|
Arguments:
|
|
SesId - Jet Session id
|
|
TblId - Table Id of the Name-Address Mapping table
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
NmsDbInit
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
This function returns the highest owner id found.
|
|
--*/
|
|
|
|
{
|
|
DWORD OwnerId;
|
|
#if NEW_OWID
|
|
DWORD TmpOwnerId;
|
|
#else
|
|
DWORD TmpOwnerId = 0;
|
|
#endif
|
|
DWORD ActFldLen;
|
|
JET_ERR JetRetStat;
|
|
PWINSTHD_TLS_T pTls;
|
|
JET_SESID SesId;
|
|
JET_TABLEID TblId;
|
|
BOOL fNoOfOwnersInited = FALSE;
|
|
DWORD No;
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
|
|
DBGENTER("NmsDbCleanupOwnAddTbl\n");
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
SesId = pTls->SesId;
|
|
TblId = pTls->NamAddTblId;
|
|
|
|
/*
|
|
* Set the primary index as the current index
|
|
*/
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetSetCurrentIndex(
|
|
SesId,
|
|
TblId,
|
|
NMSDB_NAM_ADD_PRIM_INDEX_NAME
|
|
)
|
|
);
|
|
|
|
EnterCriticalSection(&NmsDbOwnAddTblCrtSec);
|
|
*pNoOfOwners = NmsDbNoOfOwners;
|
|
try {
|
|
|
|
OwnerId = NmsDbNoOfOwners;
|
|
do
|
|
{
|
|
|
|
DBGPRINT1(FLOW, "NmsDbCleanupOwnAddTbl: will seek for owner less than = (%d)\n", OwnerId);
|
|
//
|
|
// Construct a partial key made of owner id.
|
|
//
|
|
CALL_N_RAISE_EXC_IF_ERR_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
&OwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
JET_bitNewKey //since this is the first
|
|
//data value of the key
|
|
)
|
|
);
|
|
|
|
//
|
|
// Seek to the record that has a key that is Less than or
|
|
// Equal to the OwnerId value.
|
|
//
|
|
// Since we have specified a partial key (saying in effect
|
|
// that the other component of the key is NULL), JetSeek
|
|
// must return wrnSeekNotEqual since it will never find
|
|
// a record with NULL for the second component of the index
|
|
// -- Ian 7/13/93
|
|
//
|
|
JetRetStat = JetSeek(
|
|
SesId,
|
|
TblId,
|
|
JET_bitSeekLE
|
|
);
|
|
|
|
//
|
|
// If we found a record
|
|
//
|
|
if (JetRetStat != JET_errRecordNotFound)
|
|
{
|
|
ASSERT(JetRetStat == JET_wrnSeekNotEqual);
|
|
|
|
/*
|
|
* Retrieve the owner Id column.
|
|
*/
|
|
CALL_N_RAISE_EXC_IF_ERR_M(
|
|
JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&TmpOwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
JET_bitRetrieveFromIndex,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
if(!fNoOfOwnersInited)
|
|
{
|
|
//
|
|
// We want to return the highest owner id that we find.
|
|
// not the number of owners. The param. name is
|
|
// misleading
|
|
//
|
|
*pNoOfOwners = TmpOwnerId;
|
|
fNoOfOwnersInited = TRUE;
|
|
}
|
|
|
|
DBGPRINT1(FLOW, "NmsDbCleanupOwnAddTbl: records found for owner id = (%d)\n", TmpOwnerId);
|
|
//
|
|
// Mark all those records in the owner-address table
|
|
// that don't have corresponding records in the
|
|
// name - address table
|
|
//
|
|
if (OwnerId >= 1)
|
|
{
|
|
for (No = OwnerId - 1; No > TmpOwnerId; No--)
|
|
{
|
|
|
|
if ((pNmsDbOwnAddTbl+No)->WinsState_e ==
|
|
NMSDB_E_WINS_ACTIVE)
|
|
{
|
|
//
|
|
// We may have deleted this entry in an earlier
|
|
// invocation. If so, we bypass the deletion here.
|
|
//
|
|
if ((pNmsDbOwnAddTbl+No)->WinsState_e !=
|
|
NMSDB_E_WINS_DELETED)
|
|
{
|
|
DBGPRINT1(FLOW, "NmsDbCleanupOwnAddTbl: Deleting WINS with owner id = (%d)\n", No);
|
|
(pNmsDbOwnAddTbl+No)->WinsState_e = NMSDB_E_WINS_DELETED;
|
|
NmsDbWriteOwnAddTbl(
|
|
NMSDB_E_DELETE_REC,
|
|
No,
|
|
NULL,
|
|
NMSDB_E_WINS_DELETED,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT1(DET, "NmsDbCleanupOwnAddTbl: Owner Id (%d) is already in DELETED state\n", OwnerId);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Make OwnerId = the max owner id that we found.
|
|
//
|
|
OwnerId = TmpOwnerId;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Owner Id is 0, our job is done
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
else //record not found
|
|
{
|
|
if(!fNoOfOwnersInited)
|
|
{
|
|
//
|
|
// Since fNoOfOwnersInited is FALSE, we
|
|
// did not find even one record
|
|
//
|
|
DBGPRINT1(FLOW, "NmsDbCleanupOwnAddTbl: THERE IS NOT EVEN ONE REPLICA RECORD IN THE DB. No of owners in Own-Add Tbl are (%d)\n",
|
|
NmsDbNoOfOwners
|
|
)
|
|
*pNoOfOwners = 0;
|
|
}
|
|
if (OwnerId > 0)
|
|
{
|
|
for (No = OwnerId - 1; No > 0; No--)
|
|
{
|
|
|
|
DBGPRINT1(FLOW, "NmsDbCleanupOwnAddTbl: Deleting WINS with owner id = (%d)\n", No);
|
|
if ((pNmsDbOwnAddTbl+No)->WinsState_e ==
|
|
NMSDB_E_WINS_ACTIVE)
|
|
{
|
|
(pNmsDbOwnAddTbl+No)->WinsState_e =
|
|
NMSDB_E_WINS_DELETED;
|
|
NmsDbWriteOwnAddTbl(
|
|
NMSDB_E_DELETE_REC,
|
|
No,
|
|
NULL,
|
|
NMSDB_E_WINS_DELETED,
|
|
NULL,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
} // end of for
|
|
}
|
|
//
|
|
// No more records in the db. Break out of the loop
|
|
//
|
|
break;
|
|
}
|
|
|
|
} while (TRUE);
|
|
} // end of try
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DWORD ExcCode = GetExceptionCode();
|
|
DBGPRINT1(EXC, "NmsDbCleanupOwnAddTbl: Got exception (%x)\n", ExcCode);
|
|
WINSEVT_LOG_M(ExcCode, WINS_EVT_CLEANUP_OWNADDTBL_EXC);
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
|
|
LeaveCriticalSection(&NmsDbOwnAddTblCrtSec);
|
|
DBGLEAVE("NmsDbCleanupOwnAddTbl\n");
|
|
return(RetStat);
|
|
}
|
|
|
|
|
|
STATUS
|
|
NmsDbBackup(
|
|
LPBYTE pBackupPath,
|
|
DWORD TypeOfBackup
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function is called to backup the jet db
|
|
|
|
Arguments:
|
|
pBackupPath - backup dir
|
|
fIncremental - indicates whether the backup is incremental/full
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
{
|
|
JET_ERR JetRetStat;
|
|
DWORD RetStat = WINS_SUCCESS;
|
|
static BOOL sFullBackupDone = FALSE;
|
|
BOOL fBackupChanged = FALSE;
|
|
|
|
DBGENTER("NmsDbBackup\n");
|
|
if (pBackupPath != NULL)
|
|
{
|
|
DBGPRINT2(FLOW, "NmsDbBackup:Backup path = (%s).\n Type of Backup = (%s)\n", pBackupPath, TypeOfBackup == NMSDB_FULL_BACKUP ? "FULL" : "INCREMENTAL");
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT0(FLOW, "NmsDbBackup. Null Backup path\n");
|
|
}
|
|
|
|
//
|
|
// If we have to do an incremental backup to a non-null directory and we
|
|
// haven't ever done a full back in this instance of WINS, we do a full
|
|
// backup
|
|
//
|
|
if ((pBackupPath != NULL) && (TypeOfBackup != NMSDB_FULL_BACKUP) && !sFullBackupDone)
|
|
{
|
|
TypeOfBackup = NMSDB_FULL_BACKUP;
|
|
fBackupChanged = TRUE;
|
|
}
|
|
if (DynLoadJetVersion >= DYN_LOAD_JET_500)
|
|
{
|
|
JetRetStat = JetBackup(pBackupPath, (ULONG)TypeOfBackup, NULL);
|
|
|
|
}
|
|
else
|
|
{
|
|
JetRetStat = JetBackup(pBackupPath, (ULONG)TypeOfBackup);
|
|
|
|
}
|
|
if (JetRetStat != JET_errSuccess)
|
|
|
|
{
|
|
|
|
DBGPRINT3(ERR, "NmsDbBackup: Could not do %s backup to dir (%s). Error from JetBackup is (%d)\n", TypeOfBackup == NMSDB_FULL_BACKUP ? "FULL" : "INCREMENTAL", pBackupPath, JetRetStat);
|
|
|
|
WinsEvtLogDetEvt(FALSE, WINS_EVT_BACKUP_ERR, NULL, __LINE__,
|
|
"sd", pBackupPath, JetRetStat);
|
|
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Backup was successful. Let us set the static flag to indicate that.
|
|
//
|
|
if (fBackupChanged)
|
|
{
|
|
sFullBackupDone = TRUE;
|
|
fBackupChanged = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
DBGLEAVE("NmsDbBackup\n");
|
|
return(RetStat);
|
|
}
|
|
|
|
|
|
STATUS
|
|
NmsDbGetDataRecsByName(
|
|
LPBYTE pName,
|
|
DWORD NameLen,
|
|
DWORD Location,
|
|
DWORD NoOfRecsDesired,
|
|
PCOMM_ADD_T pWinsAdd,
|
|
DWORD TypeOfRecs,
|
|
LPVOID *ppRBuf,
|
|
LPDWORD pRspBuffLen,
|
|
LPDWORD pNoOfRecsRet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
JET_ERR JetRetStat = JET_errSuccess;
|
|
DWORD OwnerId;
|
|
DWORD ActFldLen; //length of fld retrieved
|
|
VERS_NO_T TmpNoOfEntries;
|
|
LPBYTE pStartBuff;
|
|
DWORD SaveBufLen;
|
|
BYTE EntTyp; //type of entry (unique/group/special group)
|
|
PRPL_REC_ENTRY2_T pRspBuf;
|
|
JET_TABLEID TblId;
|
|
JET_SESID SesId;
|
|
PWINSTHD_TLS_T pTls;
|
|
#if NEW_OWID
|
|
DWORD RecordOwnerId;
|
|
#else
|
|
DWORD RecordOwnerId = 0;
|
|
#endif
|
|
STATUS RetStat = WINS_SUCCESS;
|
|
BYTE Name[NMSDB_MAX_NAM_LEN];
|
|
DWORD InitHeapSize;
|
|
LONG MoveDir = JET_MoveNext;
|
|
DWORD MemSize;
|
|
|
|
#ifdef WINSDBG
|
|
DWORD StartTime;
|
|
DWORD EndTime;
|
|
#endif
|
|
|
|
BOOL fAllocNew;
|
|
BOOL fTransCommitted = TRUE; // says whether the last commit should be done or not
|
|
DWORD CommitCnt = 1; //the number of commits already done - do not change
|
|
|
|
DBGENTER("NmsDbGetDataRecsByName\n");
|
|
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
|
|
TblId = pTls->NamAddTblId;
|
|
SesId = pTls->SesId;
|
|
|
|
|
|
#ifdef WINSDBG
|
|
if (pWinsAdd != NULL)
|
|
{
|
|
struct in_addr InAddr;
|
|
InAddr.s_addr = htonl(pWinsAdd->Add.IPAdd);
|
|
DBGPRINT3(DET, "NmsDbGetDataRecsByName:Will retrieve %d records starting from record with name (%s) of WINS having address = (%s)\n",
|
|
NoOfRecsDesired, pName, inet_ntoa(InAddr) );
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT2(DET, "NmsDbGetDataRecsByName:Will retrieve %d records starting from record with name (%s)\n", NoOfRecsDesired, pName);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// initialize the default no. that determines the size of the
|
|
// buffer to allocate in case the range specified by the Max and
|
|
// Min Vers. No args is > it
|
|
//
|
|
PERF("Move this to NmsDbInit")
|
|
WINS_ASSIGN_INT_TO_VERS_NO_M(TmpNoOfEntries, NoOfRecsDesired);
|
|
pTls->HeapHdl = NULL; //make it NULL so that the caller can determine
|
|
//whether this function allocated a heap
|
|
//before returning (normally/abnormally)
|
|
|
|
|
|
//
|
|
// Store the memory size for the records. Note: This
|
|
// does not contain the memory for the name and addresses
|
|
// (in case of a special group or a multihomed entry). The
|
|
// sizes for these will be added as we store each record.
|
|
//
|
|
// MemSize = RPL_REC_ENTRY_SIZE * (TmpNoOfEntries.LowPart + 1);
|
|
MemSize = RPL_REC_ENTRY2_SIZE * (DWORD)(TmpNoOfEntries.QuadPart + 1);
|
|
*pRspBuffLen = MemSize + 10000; //for good measure;
|
|
|
|
|
|
//
|
|
// We will create a heap with the above amount of memory plus a
|
|
// pad for heap overhead. We add TmpNoOfEntries.LowPart * 17
|
|
// since each record will have memory allocated for the name.
|
|
// Names in general will be 17 bytes long (we attach a NULL at the
|
|
// end when registering names).
|
|
//
|
|
// InitHeapSize = *pRspBuffLen + (TmpNoOfEntries.LowPart * 17)
|
|
InitHeapSize = *pRspBuffLen + ((DWORD)(TmpNoOfEntries.QuadPart * 17)
|
|
+ PAD_FOR_REC_HEAP);
|
|
|
|
//
|
|
// Create the heap
|
|
//
|
|
pTls->HeapHdl = WinsMscHeapCreate(0, InitHeapSize);
|
|
|
|
pRspBuf = WinsMscHeapAlloc(pTls->HeapHdl, MemSize);
|
|
|
|
pStartBuff = (LPBYTE)pRspBuf; //save start of buffer
|
|
SaveBufLen = MemSize; //save size of buffer
|
|
*pNoOfRecsRet = 0;
|
|
|
|
*ppRBuf = pStartBuff;
|
|
|
|
//
|
|
// Actually, we can call RplFindOwnerId for Scavenger thread
|
|
// We choose not to do so to avoid some overhead -- see the
|
|
// comment in the else block.
|
|
//
|
|
if (pWinsAdd != NULL)
|
|
{
|
|
fAllocNew = FALSE;
|
|
try {
|
|
if (RplFindOwnerId(
|
|
pWinsAdd,
|
|
&fAllocNew,
|
|
&OwnerId,
|
|
WINSCNF_E_IGNORE_PREC,
|
|
WINSCNF_LOW_PREC
|
|
) != WINS_SUCCESS
|
|
)
|
|
{
|
|
//
|
|
// The client may not look at the return value, but
|
|
// it will look at the *pNoOfRecs value and thus
|
|
// determine that there are no records.
|
|
//
|
|
return(WINS_FAILURE);
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DWORD ExcCode = GetExceptionCode();
|
|
DBGPRINT1(EXC,
|
|
"NmsDbGetDataRecsByName: Got exception %x",
|
|
ExcCode);
|
|
WINSEVT_LOG_M(ExcCode, WINS_EVT_EXC_RETRIEVE_DATA_RECS);
|
|
return(WINS_FAILURE);
|
|
}
|
|
|
|
//
|
|
//It is ok not to enter a critical section here since even if
|
|
//the array entry is being changed at this time, the repercussion
|
|
// of us seeing the old value is insignificant
|
|
//
|
|
if ((OwnerId != NMSDB_LOCAL_OWNER_ID) && LiEqlZero((pRplPullOwnerVersNo+OwnerId)->VersNo))
|
|
{
|
|
DBGPRINT2(DET, "NmsDbGetDataRecsByName: WINS with address = (%x) and owner id = (%d) has 0 records in the db\n", pWinsAdd->Add.IPAdd, OwnerId);
|
|
return(WINS_SUCCESS);
|
|
}
|
|
}
|
|
/*
|
|
* start a transaction
|
|
*/
|
|
|
|
CALL_M(JetBeginTransaction(pTls->SesId));
|
|
fTransCommitted = FALSE;
|
|
try {
|
|
if ((pName != NULL) || ((pName == NULL) && (Location != WINSINTF_END)))
|
|
{
|
|
CALL_M( JetMakeKey(
|
|
SesId,
|
|
TblId,
|
|
pName,
|
|
NameLen,
|
|
JET_bitNewKey
|
|
)
|
|
);
|
|
|
|
JetRetStat = JetSeek( SesId, TblId, JET_bitSeekGE);
|
|
if (
|
|
(JetRetStat == JET_errRecordNotFound)
|
|
||
|
|
((JetRetStat != JET_errSuccess) && (JetRetStat != JET_wrnSeekNotEqual))
|
|
)
|
|
{
|
|
//DBGPRINT0(ERR, "Weird. Could not locate even one record\n");
|
|
|
|
//WINSEVT_LOG_M(WINS_FAILURE,WINS_EVT_CANT_FIND_ANY_REC_IN_RANGE);
|
|
|
|
//
|
|
// Don't free memory. It will get freed later.
|
|
//
|
|
|
|
//
|
|
// Don't use macro CALL_M since that will call return
|
|
// which will cause overhead since the system will
|
|
// search for a termination handler. We don't want
|
|
// that for the case where there are no records in the db
|
|
//
|
|
if (JetRetStat != JET_errRecordNotFound)
|
|
{
|
|
#ifdef WINSDBG
|
|
DBGPRINT2(ERR, "Jet Error: JetRetStat is (%d). Line is (%d)\n",
|
|
JetRetStat, __LINE__);
|
|
#endif
|
|
WINSEVT_LOG_D_M(JetRetStat, WINS_EVT_DATABASE_ERR);
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CALL_M(JetMove(
|
|
SesId,
|
|
TblId,
|
|
JET_MoveLast,
|
|
//Location == WINSINTF_END ? JET_MoveLast : JET_MoveFirst,
|
|
0)
|
|
);
|
|
|
|
|
|
}
|
|
CHECK("Check with IAN JOSE")
|
|
//
|
|
// We are assured of there being at least one record since the
|
|
// JetSeek succeeded (if not for the owner we are interested in
|
|
// then for the next one).
|
|
// We can therefore safely use the do .. while() construct
|
|
//
|
|
// *NOT REALLY. It seems that JetSeek can return JET_wrnSeekNE
|
|
// even when there are no records in the db. In such a case,
|
|
// our JetRetrieveColumn will fail with a CurrencyNot there error
|
|
//
|
|
|
|
//
|
|
// If we found an exact match or a name greater than the search string,
|
|
// retrieve the record.
|
|
//
|
|
if ((RetStat == WINS_SUCCESS) && (JetRetStat != JET_errRecordNotFound))
|
|
{
|
|
|
|
UINT nLoops = 0;
|
|
|
|
if (Location == WINSINTF_END)
|
|
{
|
|
MoveDir = JET_MovePrevious;
|
|
|
|
}
|
|
#ifdef WINSDBG
|
|
//(void)time(&StartTime);
|
|
StartTime = GetTickCount();
|
|
#endif
|
|
do
|
|
{
|
|
nLoops++;
|
|
|
|
CALL_M(JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_OWNERID_INDEX].Fid,
|
|
&RecordOwnerId,
|
|
NAM_ADD_OWNERID_SIZE,
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
));
|
|
|
|
if ((pWinsAdd != NULL) && (RecordOwnerId != OwnerId))
|
|
{
|
|
//
|
|
// We have exhausted all records for the owner. Break out
|
|
// of the loop
|
|
//
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (RecordOwnerId == OWNER_ID_OF_SPEC_REC)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
pRspBuf->OwnerId = RecordOwnerId;
|
|
//
|
|
// Retrieve the version number
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_VERSIONNO_INDEX].Fid,
|
|
&(pRspBuf->VersNo),
|
|
sizeof(VERS_NO_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// retrieve the name
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_NAME_INDEX].Fid,
|
|
Name,
|
|
NMSDB_MAX_NAM_LEN,
|
|
&(pRspBuf->NameLen),
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// if name length is > 255, jet is returning an invalid value.
|
|
// Make the length equal to the max. length we can have for
|
|
// a netbios name. Also, log an event
|
|
//
|
|
if (pRspBuf->NameLen > WINS_MAX_NAME_SZ)
|
|
{
|
|
WINSEVT_LOG_M(pRspBuf->NameLen, WINS_EVT_NAME_TOO_LONG);
|
|
DBGPRINT1(ERR, "NmsDbGetDataRecsByName: Name length is too long = (%x)\n", pRspBuf->NameLen);
|
|
pRspBuf->NameLen = WINS_MAX_NS_NETBIOS_NAME_LEN;
|
|
}
|
|
//
|
|
// This macro will allocate memory for the name
|
|
//
|
|
NMSDB_STORE_NAME_M(pTls, pRspBuf, Name, pRspBuf->NameLen);
|
|
|
|
//
|
|
// Adjust the size to be passed to the push thread
|
|
//
|
|
*pRspBuffLen += pRspBuf->NameLen;
|
|
|
|
//
|
|
// Retrieve the flags byte
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_FLAGS_INDEX].Fid,
|
|
&(pRspBuf->Flag),
|
|
sizeof(pRspBuf->Flag),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
//
|
|
// if we were asked to retrieve only static records and
|
|
// this record is not a static record, skip it.
|
|
//
|
|
if ((TypeOfRecs & WINSINTF_STATIC) && !NMSDB_IS_ENTRY_STATIC_M(pRspBuf->Flag))
|
|
{
|
|
// DBGPRINT0(DET, "NmsDbGetDataRecs: Encountered a dynamic record but were asked to retrieve only static records\n");
|
|
continue;
|
|
}
|
|
if ((TypeOfRecs & WINSINTF_DYNAMIC) && NMSDB_IS_ENTRY_STATIC_M(pRspBuf->Flag))
|
|
{
|
|
// DBGPRINT0(DET, "NmsDbGetDataRecs: Encountered a static record but were asked to retrieve only dynamic records\n");
|
|
continue;
|
|
}
|
|
|
|
EntTyp = (BYTE)((pRspBuf->Flag & NMSDB_BIT_ENT_TYP));
|
|
if (
|
|
(EntTyp == NMSDB_UNIQUE_ENTRY)
|
|
||
|
|
(EntTyp == NMSDB_NORM_GRP_ENTRY)
|
|
)
|
|
{
|
|
/* It is a unique entry*/
|
|
pRspBuf->fGrp = (EntTyp == NMSDB_UNIQUE_ENTRY) ?
|
|
FALSE : TRUE;
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_ADDRESS_INDEX].Fid,
|
|
&pRspBuf->NodeAdd,
|
|
sizeof(COMM_ADD_T),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
}
|
|
else // it is a special group or a multihomed entry
|
|
{
|
|
|
|
//
|
|
// Even if the entry is a multihomed entry, we set the
|
|
// fGrp flag to TRUE so that the formatting function
|
|
// works properly (called by PUSH thread). The EntTyp
|
|
// will be used to decipher whether it is a multihomned
|
|
// entry or not
|
|
//
|
|
FUTURES("Remove this hacky mechanism")
|
|
pRspBuf->fGrp =
|
|
(EntTyp == NMSDB_SPEC_GRP_ENTRY) ? TRUE : FALSE;
|
|
|
|
/*
|
|
* get member addresses.
|
|
*
|
|
* This function is only called on RPC thread. We want to get
|
|
* the members, even if they are expired. We can do that by
|
|
* passing a TRUE value for the STATIC flag parameter.
|
|
* NmsDbGetDataRecsByName is the only way to get all the members
|
|
* including the expired ones.
|
|
*/
|
|
StoreGrpMems(
|
|
pTls,
|
|
WINS_E_WINSRPC,
|
|
pRspBuf->pName,
|
|
0, //not accessed by StoreGrpMems if Client_e
|
|
//is not WINS_E_NMSSCV
|
|
SesId,
|
|
TblId,
|
|
TRUE, // NMSDB_IS_ENTRY_STATIC_M(pRspBuf->Flag),
|
|
(PRPL_REC_ENTRY_T)pRspBuf
|
|
);
|
|
|
|
if (
|
|
(pRspBuf->NoOfAdds == 0)
|
|
&&
|
|
(NMSDB_ENTRY_ACT_M(pRspBuf->Flag))
|
|
)
|
|
{
|
|
//
|
|
//change the state to released so that the
|
|
//record shows up as released when displayed
|
|
//
|
|
NMSDB_CLR_STATE_M(pRspBuf->Flag);
|
|
NMSDB_SET_STATE_M(pRspBuf->Flag, NMSDB_E_RELEASED);
|
|
}
|
|
|
|
*pRspBuffLen +=
|
|
(pRspBuf->NoOfAdds * sizeof(COMM_ADD_T) * 2);
|
|
|
|
}
|
|
|
|
//
|
|
// get the timestamp field
|
|
//
|
|
CALL_M( JetRetrieveColumn(
|
|
SesId,
|
|
TblId,
|
|
sNamAddTblRow[NAM_ADD_TIMESTAMP_INDEX].Fid,
|
|
&(pRspBuf->TimeStamp),
|
|
sizeof(pRspBuf->TimeStamp),
|
|
&ActFldLen,
|
|
0,
|
|
NULL
|
|
)
|
|
);
|
|
|
|
if (NMSDB_IS_ENTRY_STATIC_M(pRspBuf->Flag) &&
|
|
(RecordOwnerId == NMSDB_LOCAL_OWNER_ID) &&
|
|
NMSDB_ENTRY_ACT_M(pRspBuf->Flag))
|
|
{
|
|
pRspBuf->TimeStamp = MAXLONG;
|
|
}
|
|
|
|
|
|
//
|
|
// increment the counter and the pointer to past the last record.
|
|
//
|
|
pRspBuf = (PRPL_REC_ENTRY2_T)((LPBYTE)pRspBuf + RPL_REC_ENTRY2_SIZE);
|
|
(*pNoOfRecsRet)++;
|
|
|
|
//
|
|
// if we have retrieved the max. number asked for, break out of
|
|
// the loop
|
|
//
|
|
if (*pNoOfRecsRet == NoOfRecsDesired)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// decrease the granularity of [BeginTransaction()..CommitTransaction()] intervals
|
|
//
|
|
if (*pNoOfRecsRet/CommitCnt >= MAX_RECS_BEFORE_COMMIT)
|
|
{
|
|
nLoops = 0;
|
|
CALL_M(JetCommitTransaction(SesId, JET_bitCommitFlush));
|
|
fTransCommitted = TRUE;
|
|
CommitCnt++;
|
|
CALL_M(JetBeginTransaction(SesId));
|
|
fTransCommitted = FALSE;
|
|
}
|
|
|
|
} while(JetMove(SesId, TblId, MoveDir/*JET_MoveNext*/, 0) >= 0);
|
|
#ifdef WINSDBG
|
|
EndTime = GetTickCount();
|
|
DBGPRINT2(TM, "NmsDbGetDataRecs: Retrieved %d records in %d secs\n",
|
|
*pNoOfRecsRet, StartTime - EndTime);
|
|
#endif
|
|
} // if RetStat == WINS_SUCCESS
|
|
} // end of try {..}
|
|
finally {
|
|
if (AbnormalTermination())
|
|
{
|
|
DBGPRINT0(ERR,
|
|
"NmsDbGetDataRecsByName: Terminating abnormally\n");
|
|
WINSEVT_LOG_D_M(WINS_FAILURE, WINS_EVT_RPC_EXC);
|
|
RetStat = WINS_FAILURE;
|
|
}
|
|
DBGPRINT1(FLOW, "NmsDbGetDataRecsByName:Retrieved %d records\n",
|
|
*pNoOfRecsRet);
|
|
|
|
//
|
|
//
|
|
// We are done. Let us commit the transaction if it is not yet committed
|
|
//
|
|
if (!fTransCommitted)
|
|
CALL_M(JetCommitTransaction(SesId, JET_bitCommitFlush));
|
|
}
|
|
|
|
DBGLEAVE("NmsDbGetDataRecsByName\n");
|
|
return(RetStat);
|
|
}
|
|
|
|
|
|
STATUS
|
|
NmsDbEndTransaction(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Externals Used:
|
|
None
|
|
|
|
|
|
Return Value:
|
|
|
|
Success status codes --
|
|
Error status codes --
|
|
|
|
Error Handling:
|
|
|
|
Called by:
|
|
WinsMscChkTermEvt
|
|
Side Effects:
|
|
|
|
Comments:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
PWINSTHD_TLS_T pTls;
|
|
DBGENTER("NmsDbEndTransaction\n");
|
|
GET_TLS_M(pTls);
|
|
ASSERT(pTls != NULL);
|
|
CALL_M(
|
|
JetCommitTransaction(pTls->SesId, JET_bitCommitFlush)
|
|
);
|
|
DBGLEAVE("NmsDbEndTransaction\n");
|
|
return(WINS_SUCCESS);
|
|
}
|
|
|
|
#if DYNLOADJET
|
|
STATUS
|
|
SetForJet(
|
|
VOID
|
|
)
|
|
{
|
|
HMODULE DllHandle;
|
|
DWORD Error;
|
|
LPTSTR pDllName;
|
|
|
|
#ifdef WINS_INTERACTIVE
|
|
DynLoadJetVersion = getenv("JET500") ? DYN_LOAD_JET_500
|
|
: (getenv("JET200") ? DYN_LOAD_JET_200 : DYN_LOAD_JET_600);
|
|
#endif
|
|
|
|
DBGENTER("SetForJet\n");
|
|
|
|
if (DynLoadJetVersion == DYN_LOAD_JET_500)
|
|
{
|
|
pDllName = TEXT("jet500.dll");
|
|
NAM_ADD_OWNERID_SIZE = sizeof(DWORD);
|
|
BASENAME = "j50";
|
|
sNamAddTblRow[3].FldTyp = JET_coltypLong;
|
|
sOwnAddTblRow[0].FldTyp = JET_coltypLong;
|
|
}
|
|
else if (DynLoadJetVersion == DYN_LOAD_JET_600 ) {
|
|
// jet600.dll is now called esent.dll!
|
|
pDllName = TEXT("esent.dll");
|
|
NAM_ADD_OWNERID_SIZE = sizeof(DWORD);
|
|
BASENAME = "j50";
|
|
sNamAddTblRow[3].FldTyp = JET_coltypLong;
|
|
sOwnAddTblRow[0].FldTyp = JET_coltypLong;
|
|
|
|
}
|
|
else
|
|
{
|
|
pDllName = TEXT("jet.dll");
|
|
NAM_ADD_OWNERID_SIZE = sizeof(BYTE);
|
|
BASENAME = "jet";
|
|
sNamAddTblRow[3].FldTyp = JET_coltypUnsignedByte;
|
|
sOwnAddTblRow[0].FldTyp = JET_coltypUnsignedByte;
|
|
}
|
|
|
|
|
|
DBGPRINT2(ERR,"SetForJet: loading DLL %ws: version %ld\n", pDllName, DynLoadJetVersion);
|
|
|
|
OWN_ADD_OWNERID_SIZE = NAM_ADD_OWNERID_SIZE;
|
|
|
|
//
|
|
// Load the DLL that contains the service.
|
|
//
|
|
|
|
DllHandle = LoadLibrary( pDllName );
|
|
if ( DllHandle == NULL )
|
|
{
|
|
Error = GetLastError();
|
|
DBGPRINT2(ERR,"SetForJet: Failed to load DLL %ws: %ld\n", pDllName, Error);
|
|
return(WINS_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
DWORD i;
|
|
for (i=0; i < NMSDB_SIZEOFJETFTBL; i++)
|
|
{
|
|
CHAR chFnName[64];
|
|
LPSTR pAt;
|
|
LPSTR pFnName;
|
|
|
|
pFnName = (LPSTR)NmsDbJetFTbl[i].pFName;
|
|
#if _X86_
|
|
if ( DynLoadJetVersion != DYN_LOAD_JET_200) {
|
|
strcpy(chFnName,NmsDbJetFTbl[i].pFName);
|
|
pAt = strrchr(chFnName,'@');
|
|
if (pAt != NULL)
|
|
{
|
|
*pAt = '\0';
|
|
pFnName = chFnName;
|
|
}
|
|
}
|
|
#endif
|
|
if ((NmsDbJetFTbl[i].pFAdd = (JETPROC)GetProcAddress(DllHandle,
|
|
(DynLoadJetVersion >= DYN_LOAD_JET_500) ? pFnName : ULongToPtr(NmsDbJetFTbl[i].FIndex))) == NULL)
|
|
{
|
|
DBGPRINT2(ERR, "SetForJet: Failed to get address of function %s: %ld\n", NmsDbJetFTbl[i].pFName, GetLastError());
|
|
return(WINS_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT3(DET, "SetForJet: Got address of function %s (%d): %p\n", NmsDbJetFTbl[i].pFName, i, NmsDbJetFTbl[i].pFAdd);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
return(WINS_SUCCESS);
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// Name of the process that converts jet200 db to jet500 db format
|
|
//
|
|
CHECK("Unicode from results in an exception from CreateProcess")
|
|
//#define JETCONVDB TEXT("jetconv WINS /@")
|
|
|
|
VOID
|
|
RecoverJetDb(
|
|
DYN_LOAD_JET_VERSION JetVersion
|
|
)
|
|
/*++
|
|
This routine recovers the database by calling JetInit/JetTerm on
|
|
the database.
|
|
Argument:
|
|
JetVersion - The version of the jet to use when recovering the db.
|
|
--*/
|
|
{
|
|
DYN_LOAD_JET_VERSION JetVersionSv = DynLoadJetVersion;
|
|
|
|
ASSERT(DYN_LOAD_JET_500 <= JetVersion );
|
|
|
|
//
|
|
// First JetTerm the current jet engine.
|
|
//
|
|
NmsDbRelRes();
|
|
|
|
//
|
|
// now load the appropriate version jet dll.
|
|
//
|
|
DynLoadJetVersion = JetVersion;
|
|
|
|
SetForJet();
|
|
|
|
|
|
//
|
|
// set system params and jetinit.
|
|
//
|
|
SetSystemParams(TRUE);
|
|
|
|
JetInit(&sJetInstance);
|
|
|
|
//
|
|
// finally, JetTerm this jet dll.
|
|
//
|
|
NmsDbRelRes();
|
|
|
|
DynLoadJetVersion = JetVersionSv;
|
|
return;
|
|
}
|
|
|
|
#define JETCONVDB200 "jetconv WINS /200 /@"
|
|
#define JETCONVDB500 "jetconv WINS /500 /@"
|
|
|
|
STATUS
|
|
ConvertJetDb(
|
|
JET_ERR JetRetStat
|
|
)
|
|
{
|
|
BOOL RetVal;
|
|
PROCESS_INFORMATION ProcInfo = {0};
|
|
STARTUPINFOA StartInfo = {0};
|
|
LPSTR pArg;
|
|
|
|
|
|
|
|
DBGPRINT1(DET, "ConvertJetDb: Converting %s\n", (JetRetStat == JET_errDatabase200Format)
|
|
? JETCONVDB200 : JETCONVDB500);
|
|
|
|
if (JetRetStat == JET_errDatabase200Format)
|
|
{
|
|
|
|
fDbIs200 = TRUE;
|
|
if (DynLoadJetVersion == DYN_LOAD_JET_500)
|
|
{
|
|
//
|
|
// Can not run jet200 using jet500.dll on NT5.0
|
|
//
|
|
|
|
DBGPRINT0(ERR, "Can not run jet200 using jet500.dll on NT5.0\n");
|
|
return WINS_FAILURE;
|
|
|
|
} else if (DynLoadJetVersion == DYN_LOAD_JET_600){
|
|
pArg = JETCONVDB200;
|
|
} else {
|
|
ASSERT(FALSE);
|
|
return WINS_FAILURE;
|
|
}
|
|
|
|
} else if ( JetRetStat == JET_errDatabase500Format ) {
|
|
|
|
if (DynLoadJetVersion == DYN_LOAD_JET_600)
|
|
{
|
|
// before we start the conversion, we need to bring the db to
|
|
// consistent state. The 351 to 4.0 conversion tool (upg351db.exe)
|
|
// did this from within the tool but the 4.0 to 5.0 tool
|
|
// does not do this from within the tool so we need to do it here.
|
|
RecoverJetDb( DYN_LOAD_JET_500 );
|
|
|
|
// Start the convert process
|
|
//
|
|
pArg = JETCONVDB500;
|
|
fDbIs500 = TRUE;
|
|
|
|
} else {
|
|
ASSERT(FALSE);
|
|
return WINS_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//return WINS_FAILURE;
|
|
|
|
StartInfo.cb = sizeof(StartInfo);
|
|
//
|
|
|
|
// Create the convert process to do the conversion. This process
|
|
//
|
|
DBGPRINT0(DET, "ConvertJetDb - creating convert process\n");
|
|
RetVal = CreateProcessA(
|
|
NULL, //
|
|
pArg,
|
|
NULL, //default proc. sec.
|
|
NULL, //default thread. sec.
|
|
FALSE, //don't inherit handles
|
|
DETACHED_PROCESS, //no creation flags
|
|
NULL, //default env.
|
|
NULL, //current drv/dir. same as creator
|
|
&StartInfo, //no startup info
|
|
&ProcInfo //no process info.
|
|
);
|
|
if (!RetVal)
|
|
{
|
|
DBGPRINT1(ERR, "ConvertJetDb: Create process failed with error (%x)\n", GetLastError());
|
|
return(WINS_FAILURE);
|
|
}
|
|
|
|
fConvJetDbCalled = TRUE;
|
|
|
|
//
|
|
// Log an event.
|
|
//
|
|
DBGPRINT0(DET, "ConvertJetDb - returning\n");
|
|
|
|
// WINSEVT_LOG_M(WINS_SUCCESS, WINS_EVT_TEMP_TERM_UNTIL_CONV);
|
|
return(WINS_SUCCESS);
|
|
|
|
}
|
|
|
|
|