mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
850 lines
19 KiB
850 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1987-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cli_list.c
|
|
|
|
Abstract:
|
|
|
|
Contains function that manage all client lists.
|
|
|
|
Author:
|
|
|
|
Ported from Lan Man 2.1
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
Contains NT-specific code.
|
|
Requires ANSI C extensions: slash-slash comments, long external names.
|
|
|
|
Revision History:
|
|
|
|
11-Apr-1989 (yuv)
|
|
Initial Coding
|
|
|
|
03-Oct-1991 (cliffv)
|
|
Ported to NT. Converted to NT style.
|
|
14-Jan-1992 JohnRo
|
|
Made changes suggested by PC-LINT.
|
|
Changed to use NetLock.h (allow shared locks, for one thing).
|
|
Added lockcount and time_of_first_lock fields to client list record.
|
|
Use REPL_STATE_ equates for client_list_rec.state values.
|
|
Added RemoveClientRecForDirName() for use by NetrReplImportDirDel().
|
|
Maintain RCGlobalClientListCount for use by NetrReplImportDirEnum().
|
|
Added some more debug code.
|
|
28-Jan-1992 JohnRo
|
|
ReplInitSetSignalFile() is obsolete.
|
|
P_ globals are now named ReplGlobal (and declared in replgbl.h).
|
|
Changed to use LPTSTR etc.
|
|
04-Mar-1992 JohnRo
|
|
Changed ReplMain's interface to match new service controller.
|
|
15-Mar-1992 JohnRo
|
|
Update registry with new values.
|
|
24-Mar-1992 JohnRo
|
|
Renamed many ReplGlobal vars to ReplConfig vars.
|
|
Added/corrected some lock handling.
|
|
25-Mar-1992 JohnRo
|
|
Avoid obsolete state values.
|
|
26-Mar-1992 JohnRo
|
|
Fixed call to ImportDirSetState() in ReplClientInitLists().
|
|
Added more assertion checking in ReplAddClientRec().
|
|
27-Mar-1992 JohnRo
|
|
Allow use of ReplAddClientRec() without some parameters.
|
|
RCGlobalClientListCount is sometimes trashed by
|
|
ReplRemoveClientRecForDirName().
|
|
19-Aug-1992 JohnRo
|
|
RAID 3603: import tree (TMPREE.RP$) generated at startup.
|
|
25-Sep-1992 JohnRo
|
|
RAID 5494: repl svc does not maintain time stamp on import startup.
|
|
06-Nov-1992 JohnRo
|
|
RAID 5496: old fields not maintained in registry.
|
|
Minor debug and comment enhancements.
|
|
11-Jan-1993 JohnRo
|
|
RAID 6710: repl cannot manage dir with 2048 files.
|
|
Made changes suggested by PC-LINT 5.0
|
|
15-Jan-1993 JohnRo
|
|
RAID 7717: Repl assert if not logged on correctly. (Also do event
|
|
logging for real.)
|
|
02-Apr-1993 JohnRo
|
|
Use NetpKdPrint() where possible.
|
|
Added more comments about which thread is which.
|
|
|
|
--*/
|
|
|
|
|
|
#include <windows.h> // IN, DWORD, etc.
|
|
#include <lmcons.h>
|
|
|
|
#include <alertmsg.h> // ALERT_* defines
|
|
#include <align.h> // ALIGN_* defines
|
|
#include <dirname.h> // ReplIsDirNameValid().
|
|
#include <icanon.h> // I_NetPathCompare
|
|
#include <impdir.h> // ImportDirSetState().
|
|
#include <iniparm.h> // DEFAULT_ equates.
|
|
#include <lmerr.h> // NERR_* defines
|
|
#include <lmerrlog.h> // NELOG_* defines
|
|
#include <lmrepl.h> // REPL_STATE_ equates.
|
|
#include <names.h> // NetpIsComputerNameValid().
|
|
#include <netdebug.h> // DBGSTATIC ..
|
|
#include <netlib.h> // NetpMemoryAllocate
|
|
#include <netlock.h> // Lock data types, functions, and macros.
|
|
#include <prefix.h> // PREFIX_ equates.
|
|
#include <tstr.h> // STRLEN(), etc.
|
|
|
|
//
|
|
// Local include files
|
|
//
|
|
#include <repldefs.h>
|
|
#include <client.h>
|
|
#include <replgbl.h> // ReplGlobal and ReplConfig variables.
|
|
#include <replp.h>
|
|
|
|
//
|
|
// G L O B A L S:
|
|
//
|
|
|
|
|
|
|
|
DBGSTATIC VOID
|
|
ReplClientInitPool(
|
|
IN DWORD PoolNumber,
|
|
IN DWORD EntryCount,
|
|
IN DWORD EntrySize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates initial (guessed) memory for the Big Buffer pool.
|
|
If needed, other entries will be allocated dynamically.
|
|
|
|
Guess is = MAX(count, POOL_MIN_ENTRY_COUNT )
|
|
|
|
From here the linked list memory is managed as follows:
|
|
|
|
If an entry is deleted (FREE)
|
|
add record to free_list.
|
|
|
|
If a new entry is required (ALLOC)
|
|
if NOTEMPTY(free_list)
|
|
grab a record from free_list
|
|
else
|
|
Allocate new entry from the heap.
|
|
|
|
Arguments:
|
|
|
|
PoolNumber - Which pool to initialize
|
|
|
|
EntryCount - Initial guess of number of entries to allocate.
|
|
|
|
EntrySize - Size in byte of each entry in the pool.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
#ifdef notdef
|
|
DWORD i;
|
|
NET_API_STATUS NetStatus;
|
|
#endif // notdef
|
|
|
|
//
|
|
// Ensure the EntrySize is a multiple of pointer size to ensure
|
|
// each entry allocated here is pointer size aligned.
|
|
//
|
|
|
|
EntrySize = ROUND_UP_COUNT( EntrySize, ALIGN_LPBYTE );
|
|
|
|
RCGlobalPoolEntrySize[PoolNumber] = EntrySize;
|
|
RCGlobalPoolHeader[PoolNumber] = NULL;
|
|
|
|
|
|
#ifdef notdef
|
|
|
|
//
|
|
// Always allocate a minimum number of entries.
|
|
//
|
|
|
|
if (EntryCount < POOL_MIN_ENTRY_COUNT ) {
|
|
EntryCount = POOL_MIN_ENTRY_COUNT ;
|
|
}
|
|
|
|
//
|
|
// Allocate all the appropiate entries.
|
|
//
|
|
// It is better to detect a lack of resources at service initialization
|
|
// rather than later. That is why the pool is primed, otherwise we
|
|
// could just let the pools prime themselves.
|
|
//
|
|
|
|
for (i = 0; i < (EntryCount - 1); i++) {
|
|
PUCHAR rec;
|
|
|
|
rec = NetpMemoryAllocate( RCGlobalPoolEntrySize[PoolNumber] );
|
|
|
|
if ( rec == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
AlertLogExit( ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
NetStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
BUGBUG; // Hey, this will cause ReplClientFreePoolEntry to
|
|
// fault on a NULL pointer! --JR
|
|
}
|
|
|
|
ReplClientFreePoolEntry( PoolNumber, rec );
|
|
|
|
}
|
|
#endif // notdef
|
|
|
|
DBG_UNREFERENCED_PARAMETER (EntryCount);
|
|
}
|
|
|
|
VOID
|
|
ReplClientFreePools(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
free up all memory that consumed in pool
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD i;
|
|
PUCHAR Rec, NextRec;
|
|
|
|
for(i = 0; i < POOL_COUNT; i++) {
|
|
|
|
NextRec = RCGlobalPoolHeader[i];
|
|
|
|
while(NextRec != NULL) {
|
|
|
|
Rec = NextRec;
|
|
NextRec = *((PUCHAR *)Rec);
|
|
|
|
NetpMemoryFree((LPVOID) Rec);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
PVOID
|
|
ReplClientGetPoolEntry(
|
|
IN DWORD PoolNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a pointer to an entry from the appropriate pool
|
|
|
|
Arguments:
|
|
|
|
PoolNumber - Which pool to allocate from
|
|
|
|
Return Value:
|
|
|
|
Pointer to a buffer of the appropriate size.
|
|
This will be NULL is the memory is not available.
|
|
|
|
Threads:
|
|
|
|
Called by API, client, and syncer threads.
|
|
|
|
--*/
|
|
{
|
|
PUCHAR rec;
|
|
NET_API_STATUS NetStatus;
|
|
|
|
//
|
|
// Take first record on free_list, make header point to next one.
|
|
//
|
|
|
|
ACQUIRE_LOCK( RCGlobalPoolLock );
|
|
|
|
if ( RCGlobalPoolHeader[PoolNumber] != NULL) {
|
|
|
|
rec = RCGlobalPoolHeader[PoolNumber];
|
|
RCGlobalPoolHeader[PoolNumber] =
|
|
*( (PUCHAR *) RCGlobalPoolHeader[PoolNumber]);
|
|
|
|
RELEASE_LOCK( RCGlobalPoolLock );
|
|
return rec;
|
|
}
|
|
|
|
RELEASE_LOCK( RCGlobalPoolLock );
|
|
|
|
|
|
//
|
|
// If pool is empty, try to allocate an entry.
|
|
//
|
|
|
|
rec = NetpMemoryAllocate( RCGlobalPoolEntrySize[PoolNumber] );
|
|
|
|
if ( rec == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
AlertLogExit( ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
NetStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
}
|
|
|
|
return rec;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ReplClientFreePoolEntry(
|
|
IN DWORD PoolNumber,
|
|
IN PVOID Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees an entry back to the appropriate pool
|
|
|
|
Arguments:
|
|
|
|
PoolNumber - Which pool to free to.
|
|
|
|
Buffer - Pointer to the buffer to free.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
Threads:
|
|
|
|
Called by API, client, and syncer threads.
|
|
|
|
--*/
|
|
{
|
|
NetpAssert( Buffer != NULL );
|
|
|
|
//
|
|
// Link this buffer at the front of the pool.
|
|
//
|
|
|
|
ACQUIRE_LOCK( RCGlobalPoolLock );
|
|
|
|
* ((PUCHAR *)(Buffer)) = RCGlobalPoolHeader[PoolNumber];
|
|
RCGlobalPoolHeader[PoolNumber] = (PUCHAR) Buffer;
|
|
|
|
RELEASE_LOCK( RCGlobalPoolLock );
|
|
}
|
|
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
ReplClientInitLists(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Counts initial # of directories under IMPORT path and allocates
|
|
memory for client_list. Reads config data into the client list.
|
|
Also initializes various pools.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
NET_API_STATUS.
|
|
|
|
Threads:
|
|
|
|
Only called by client thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
DWORD dir_count = 0;
|
|
|
|
TCHAR PathName[MAX_PATH];
|
|
DWORD PathIndex;
|
|
WIN32_FIND_DATA FindData;
|
|
HANDLE FindHandle;
|
|
|
|
//
|
|
// Build the path name <ImportPath>\*.*
|
|
//
|
|
|
|
NetpAssert( ReplConfigImportPath != NULL );
|
|
(void) STRCPY(PathName, ReplConfigImportPath);
|
|
PathIndex = STRLEN(PathName);
|
|
(void) STRCPY(PathName + PathIndex++, SLASH);
|
|
(void) STRCPY(PathName + PathIndex, STAR_DOT_STAR);
|
|
|
|
//
|
|
// Go thru all SubDirectories counting them and Setting the signal file.
|
|
//
|
|
|
|
FindHandle = FindFirstFile( PathName, &FindData );
|
|
|
|
if ( FindHandle != INVALID_HANDLE_VALUE ) {
|
|
do {
|
|
|
|
if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
|
|
|
|
//
|
|
// Make sure it's not ".", "..", "TMPTREE.RP$", etc.
|
|
//
|
|
|
|
if (ReplIgnoreDirOrFileName(FindData.cFileName)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set the state to indicate there is no master.
|
|
//
|
|
|
|
(void) STRCPY( PathName + PathIndex, FindData.cFileName );
|
|
|
|
ApiStatus = ImportDirSetState(
|
|
FindData.cFileName,
|
|
REPL_STATE_NO_MASTER );
|
|
if (ApiStatus != NO_ERROR) {
|
|
//
|
|
// One very likely reason for that to fail is if we're not
|
|
// running in the Replicator Local Group, so we would get
|
|
// access denied when trying to write to the registry.
|
|
// Log this and continue.
|
|
//
|
|
ReplErrorLog(
|
|
NULL, // local (no server name)
|
|
NELOG_ReplSysErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
|
|
++dir_count;
|
|
}
|
|
} while ( FindNextFile( FindHandle, &FindData ) );
|
|
|
|
(VOID) FindClose( FindHandle );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Initialize each of the memory pools
|
|
//
|
|
|
|
ReplClientInitPool( CLIENT_LIST_POOL, dir_count, sizeof( CLIENT_LIST_REC ) );
|
|
ReplClientInitPool( QUEBIG_POOL, dir_count, sizeof( BIGBUF_REC ) );
|
|
ReplClientInitPool( QUESML_POOL, dir_count, sizeof( SMLBUF_REC ) );
|
|
ReplClientInitPool( DUPLMA_POOL, dir_count, sizeof( DUPL_MASTERS_REC ) );
|
|
|
|
//
|
|
// Initialize values in client list to reflect registry updates above.
|
|
// BUGBUG: if dir exists in registry but not on disk, state is not
|
|
// reset to REPL_STATE_NO_MASTER.
|
|
//
|
|
ApiStatus = ImportDirReadClientList( );
|
|
|
|
if (ApiStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL
|
|
"ReplClientInitLists: ImportDirReadClientList failed, status "
|
|
FORMAT_API_STATUS ".\n", ApiStatus ));
|
|
}
|
|
|
|
return (ApiStatus);
|
|
|
|
} // ReplClientInitLists
|
|
|
|
|
|
|
|
|
|
PCLIENT_LIST_REC
|
|
ReplAddClientRec(
|
|
IN LPTSTR dir_name,
|
|
IN PSYNCMSG master_info OPTIONAL,
|
|
IN PMSG_STATUS_REC dir_info OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocated a new client list entry, fills it in, and links it onto
|
|
the front of the client list.
|
|
|
|
Arguments:
|
|
|
|
dir_name - new dir name
|
|
|
|
master_info - pointer to sync_msg_structure with master's info
|
|
|
|
dir_info - pointer to sync_msg_structure with dir's info
|
|
|
|
Return Value:
|
|
|
|
Returns pointer to newly allocated entry. Returns NULL if not enough
|
|
memory for new entry.
|
|
|
|
Threads:
|
|
|
|
Only called by API and syncer threads.
|
|
|
|
--*/
|
|
{
|
|
PCLIENT_LIST_REC rec;
|
|
|
|
//
|
|
// Check for errors by caller.
|
|
//
|
|
|
|
NetpAssert( ReplIsDirNameValid( dir_name ) );
|
|
if (master_info != NULL) {
|
|
// BUGBUG: Is this UNC name? What about null name?
|
|
NetpAssert( NetpIsComputerNameValid( master_info->header.sender ) );
|
|
}
|
|
if (dir_info != NULL) {
|
|
NetpAssert( ReplIsIntegrityValid( dir_info->integrity ) );
|
|
NetpAssert( ReplIsExtentValid( dir_info->extent ) );
|
|
// BUGBUG: check other fields while we're at it?
|
|
}
|
|
|
|
//
|
|
// Get an entry from the pool.
|
|
//
|
|
|
|
rec = ReplClientGetPoolEntry( CLIENT_LIST_POOL );
|
|
|
|
if ( rec == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// put values in.
|
|
//
|
|
|
|
(void) STRCPY( rec->dir_name, dir_name);
|
|
if (master_info != NULL) {
|
|
(void) STRCPY( rec->master, master_info->header.sender);
|
|
} else {
|
|
(rec->master)[0] = TCHAR_EOS;
|
|
}
|
|
if (dir_info != NULL) {
|
|
rec->integrity = dir_info->integrity;
|
|
rec->extent = dir_info->extent;
|
|
} else {
|
|
rec->integrity = DEFAULT_INTEGRITY;
|
|
rec->extent = DEFAULT_EXTENT;
|
|
}
|
|
|
|
//
|
|
// Convert times to seconds.
|
|
//
|
|
|
|
if (master_info != NULL) {
|
|
rec->sync_time = master_info->info.sync_rate * 60;
|
|
rec->pulse_time = master_info->info.pulse_rate * 60;
|
|
rec->guard_time = master_info->info.guard_time * 60;
|
|
rec->rand_time = master_info->info.random;
|
|
} else {
|
|
|
|
// BUGBUG; // should acquire shared ReplConfigLock here!
|
|
rec->sync_time = ReplConfigInterval * 60;
|
|
rec->pulse_time = ReplConfigPulse * 60;
|
|
rec->guard_time = ReplConfigGuardTime * 60;
|
|
rec->rand_time = ReplConfigRandom; // already in seconds.
|
|
// BUGBUG; // should free ReplConfigLock here!
|
|
}
|
|
|
|
//
|
|
// Set other values to defaults.
|
|
//
|
|
rec->checksum = 0;
|
|
rec->count = (DWORD) -1;
|
|
rec->est_max_dir_entry_count = 1; // Estimated max entries (so far): 1.
|
|
rec->timestamp = NetpReplTimeNow();
|
|
rec->alerts = 0;
|
|
rec->state = REPL_STATE_NEVER_REPLICATED;
|
|
|
|
rec->lockcount = 0;
|
|
rec->time_of_first_lock = 0;
|
|
|
|
rec->timer.timeout = 0;
|
|
rec->timer.type = 0;
|
|
rec->timer.grd_timeout = 0;
|
|
rec->timer.grd_checksum = 0;
|
|
rec->timer.grd_count = (DWORD) -1;
|
|
|
|
rec->dupl.master[0] = 0;
|
|
rec->dupl.count = 0;
|
|
rec->dupl.sleep_time = 0;
|
|
rec->dupl.next_p = NULL;
|
|
|
|
|
|
//
|
|
// Chain record into the front of the client_list.
|
|
//
|
|
|
|
ACQUIRE_LOCK( RCGlobalClientListLock );
|
|
|
|
rec->next_p = RCGlobalClientListHeader;
|
|
rec->prev_p = NULL;
|
|
RCGlobalClientListHeader = rec;
|
|
|
|
//
|
|
// When inserting the very first record, there is no next.
|
|
//
|
|
|
|
if (rec->next_p != NULL)
|
|
(rec->next_p)->prev_p = rec;
|
|
|
|
++RCGlobalClientListCount;
|
|
|
|
RELEASE_LOCK( RCGlobalClientListLock );
|
|
|
|
//
|
|
// Return a pointer to the newly initialized entry.
|
|
//
|
|
|
|
return rec;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ReplRemoveClientRec(
|
|
IN OUT PCLIENT_LIST_REC rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes record from client_list (doubly linked) and puts record
|
|
on free_list (singly linked)
|
|
|
|
Assumes that caller has an exclusive lock on RCGlobalClientListLock.
|
|
This prevents this from being called while another thread has a
|
|
pointer to this entry.
|
|
|
|
Arguments:
|
|
|
|
rec - Pointer to record to delink.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
PDUPL_MASTERS_REC dupl;
|
|
PDUPL_MASTERS_REC next;
|
|
|
|
NetpAssert( rec != NULL );
|
|
|
|
//
|
|
// First free all elements in dupl list.
|
|
//
|
|
|
|
dupl = rec->dupl.next_p;
|
|
|
|
while (dupl != NULL) {
|
|
next = dupl->next_p;
|
|
ReplClientFreePoolEntry( DUPLMA_POOL, dupl);
|
|
dupl = next;
|
|
}
|
|
|
|
//
|
|
// Delink the record from the client list.
|
|
//
|
|
|
|
if (rec->prev_p != NULL) {
|
|
(rec->prev_p)->next_p = rec->next_p;
|
|
} else { // it's the first record.
|
|
RCGlobalClientListHeader = rec->next_p;
|
|
}
|
|
|
|
if (rec->next_p != NULL) { // if it's not the last record.
|
|
(rec->next_p)->prev_p = rec->prev_p;
|
|
}
|
|
|
|
//
|
|
// Free the record into the pool.
|
|
//
|
|
|
|
ReplClientFreePoolEntry( CLIENT_LIST_POOL, rec );
|
|
|
|
--RCGlobalClientListCount;
|
|
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
ReplRemoveClientRecForDirName (
|
|
IN LPTSTR DirName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes record from client_list (doubly linked) and puts record
|
|
on free_list (singly linked)
|
|
|
|
Assumes that caller has an exclusive lock on RCGlobalClientListLock.
|
|
|
|
Arguments:
|
|
|
|
DirName - name of entry to search for and delete.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Threads:
|
|
|
|
API threads only.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
PCLIENT_LIST_REC rec;
|
|
|
|
NetpAssert( DirName != NULL );
|
|
|
|
//
|
|
// Scan the list for a match on this DirName...
|
|
// BUGBUG: Should we check MasterName too?
|
|
//
|
|
for ( rec = RCGlobalClientListHeader; rec != NULL; rec = rec->next_p ) {
|
|
if ( (STRICMP( rec->dir_name, DirName ) == 0) ) {
|
|
// BUGBUG: Should we check MasterName too?
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rec == NULL) {
|
|
ApiStatus = NERR_UnknownDevDir; // entry not found
|
|
} else {
|
|
PDUPL_MASTERS_REC dupl;
|
|
PDUPL_MASTERS_REC next;
|
|
|
|
ApiStatus = NO_ERROR; // entry found, so let's delete it...
|
|
|
|
//
|
|
// First free all elements in dupl list.
|
|
//
|
|
|
|
dupl = rec->dupl.next_p;
|
|
|
|
while (dupl != NULL) {
|
|
next = dupl->next_p;
|
|
ReplClientFreePoolEntry( DUPLMA_POOL, dupl);
|
|
dupl = next;
|
|
}
|
|
|
|
//
|
|
// Delink the record from the client list.
|
|
//
|
|
|
|
if (rec->prev_p != NULL) {
|
|
(rec->prev_p)->next_p = rec->next_p;
|
|
} else { // it's the first record.
|
|
RCGlobalClientListHeader = rec->next_p;
|
|
}
|
|
|
|
if (rec->next_p != NULL) { // if it's not the last record.
|
|
(rec->next_p)->prev_p = rec->prev_p;
|
|
}
|
|
|
|
//
|
|
// Free the record into the pool.
|
|
//
|
|
|
|
ReplClientFreePoolEntry( CLIENT_LIST_POOL, rec );
|
|
|
|
NetpAssert( RCGlobalClientListCount >= 1 );
|
|
--RCGlobalClientListCount;
|
|
|
|
}
|
|
|
|
return (ApiStatus);
|
|
|
|
} // ReplRemoveClientRecForDirName
|
|
|
|
|
|
|
|
PCLIENT_LIST_REC
|
|
ReplGetClientRec(
|
|
IN LPTSTR dir_name,
|
|
IN LPTSTR MasterName OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches client_list for record with dir_name.
|
|
|
|
Assumes that caller has a shared lock on RCGlobalClientListLock.
|
|
|
|
Arguments:
|
|
|
|
dir_name - The directory name to look for.
|
|
|
|
MasterName - If not NULL, specifies the name of the master server for
|
|
this directory. If specified, this name must match the one in the
|
|
client record, otherwise this routine merely indicates that the
|
|
record was not found.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the required record or NULL if not found
|
|
|
|
--*/
|
|
{
|
|
PCLIENT_LIST_REC rec;
|
|
|
|
for ( rec = RCGlobalClientListHeader; rec != NULL; rec = rec->next_p ) {
|
|
if ( (STRICMP( rec->dir_name, dir_name ) == 0) ) {
|
|
if ( MasterName == NULL ||
|
|
ReplNetNameCompare( NULL,
|
|
rec->master,
|
|
MasterName,
|
|
NAMETYPE_COMPUTER,
|
|
0 ) == 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rec;
|
|
}
|