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.
1556 lines
42 KiB
1556 lines
42 KiB
/*++
|
|
|
|
Copyright (c) 1987-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
pulser.c
|
|
|
|
Abstract:
|
|
Contains Pulse thread - does actual work for REPL Master.
|
|
|
|
Author:
|
|
Ported from Lan Man 2.x
|
|
|
|
Environment:
|
|
Contains NT-specific code.
|
|
Requires ANSI C extensions: slash-slash comments, long external names.
|
|
|
|
Revision History:
|
|
04/11/89 (yuv)
|
|
initial coding
|
|
|
|
10/07/91 (madana)
|
|
ported to NT. Converted to NT style.
|
|
26-Dec-1991 JohnRo
|
|
Added equate for unguarded value of master_list_rec.grd_count.
|
|
20-Jan-1992 JohnRo
|
|
Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
|
|
Changed which routines lock master list to allow duplicate checks.
|
|
Added debug print of thread ID.
|
|
Changed to use NetLock.h (allow shared locks, for one thing).
|
|
Added RemoveMasterRecForDirName() for use by NetrReplExportDirDel().
|
|
Added locktime and time_of_first_lock fields to master record.
|
|
Added RMGlobalMasterListHeader (was master_list_header) and
|
|
RMGlobalMasterListCount.
|
|
Made changes suggested by PC-LINT.
|
|
24-Jan-1992 JohnRo
|
|
Use ReplConfigRead() instead of GetReplIni().
|
|
P_ globals are now called ReplGlobal variables in replgbl.h.
|
|
Changed to use LPTSTR etc.
|
|
09-Feb-1992 JohnRo
|
|
Set up to dynamically change role.
|
|
Use FORMAT equates.
|
|
17-Feb-1992 JohnRo
|
|
CheckForDelDirs() should get a shared lock on master list.
|
|
Added some debug messages.
|
|
05-Mar-1992 JohnRo
|
|
Fix locking of master list.
|
|
Lock global copy of config data.
|
|
Minor source cleanup.
|
|
15-Mar-1992 JohnRo
|
|
Change RemoveMasterRecForDirName()'s lock requirements to allow updating
|
|
registry with new values.
|
|
23-Mar-1992 JohnRo
|
|
Renamed many ReplGlobal vars to ReplConfig vars.
|
|
Avoid RemoveFromList() vs. CheckForDelDirs() lock conflict.
|
|
Use integrity and extent equates in <lmrepl.h>.
|
|
19-Aug-1992 JohnRo
|
|
RAID 2115: repl svc should wait while stopping or changing role.
|
|
Use PREFIX_ equates.
|
|
20-Nov-1992 JohnRo
|
|
RAID 1537: Repl APIs in wrong role kill svc.
|
|
Fix remote repl admin.
|
|
Added some debug checks.
|
|
28-Nov-1992 JohnRo
|
|
Repl should use filesystem change notify.
|
|
12-Jan-1993 JohnRo
|
|
RAID 7064: replicator exporter skips new data (change notify while
|
|
dir locked is lost).
|
|
14-Jan-1993 JohnRo
|
|
RAID 7053: locked trees added to pulse msg. (Actually fix all
|
|
kinds of remote lock handling.)
|
|
12-Feb-1993 JohnRo
|
|
RAID 10716: msg timing and checksum problems (also downlevel msg bug).
|
|
RAID 7988: Downlevel importer might not see lock file.
|
|
Made changes suggested by PC-LINT 5.0
|
|
25-Jan-1993 JohnRo
|
|
RAID 12237: replicator tree depth exceeded.
|
|
08-Mar-1993 JohnRo
|
|
RAID 7988: downlevel repl importer might not see lock file
|
|
for new first-level dir.
|
|
25-Mar-1993 JohnRo
|
|
RAID 4267: Replicator has problems when work queue gets large.
|
|
09-Apr-1993 JohnRo
|
|
RAID 5959: export side gets long hourglass too.
|
|
Use NetpKdPrint() where possible.
|
|
26-Apr-1993 JimKel
|
|
RAID 7298: repl stops when it didn't get _access to an export dir.
|
|
27-May-1993 JohnRo
|
|
RAID 11103: repl svc doesn't handle time being set back.
|
|
10-Jun-1993 JohnRo
|
|
RAID 13080: Allow repl between different timezones.
|
|
18-Jun-1993 JohnRo
|
|
RAID 13594: repl exporter does not handle timezone changes (must
|
|
recompute all checksums).
|
|
22-Nov-1994 JonN
|
|
RAID 27287: Memory leak in Replicator (LMREPL.EXE)
|
|
ReplEnableChangeNotify is being called even though there is still
|
|
an outstanding notify request on ChangeHandle, this causes multiple
|
|
requests to build up and leaks paged pool. Added boolean
|
|
NotifyPending to keep this at only a single request at a time.
|
|
|
|
|
|
--*/
|
|
|
|
#include <nt.h> // NT definitions (needed by chngnot.h)
|
|
#include <ntrtl.h> // NT runtime library definitions (needed by nturtl.h)
|
|
#include <nturtl.h> // Needed to have windows.h and nt.h co-exist.
|
|
#include <windows.h> // DWORD, IN, File APIs, etc.
|
|
|
|
#include <lmcons.h>
|
|
#include <lmerr.h>
|
|
#include <netlib.h>
|
|
#include <lmerrlog.h>
|
|
#include <alertmsg.h>
|
|
#include <lmrepl.h> // REPL_EXTENT_FILE, etc.
|
|
#include <netlib.h>
|
|
#include <netdebug.h> // NetpKdPrint(), FORMAT_ equates.
|
|
#include <netlock.h> // Lock data types, functions, and macros.
|
|
#include <prefix.h> // PREFIX_ equates.
|
|
#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
|
|
#include <timelib.h> // NetpLocalTimeZoneOffset().
|
|
#include <tstr.h> // TCHAR_FWDSLASH, etc.
|
|
|
|
// repl headers
|
|
|
|
#include <chngnot.h> // ReplSetupChangeNotify, REPL_CHANGE_NOTIFY_HANDLE, etc
|
|
#include <expdir.h> // ExportDirWriteConfigData(), etc.
|
|
#include <repldefs.h> // IF_DEBUG(), etc.
|
|
#include <replgbl.h> // ReplGlobal and ReplConfig variables.
|
|
#include <iniparm.h> // DEFAULT_INTEGRITY, etc.
|
|
#include <master.h>
|
|
#include <pulser.h> // PulserTimeOfLastNotifyOrUnlock, etc.
|
|
#include <masproto.h>
|
|
#include <replp.h>
|
|
|
|
#define FOREVER 1
|
|
|
|
// G L O B A L S:
|
|
|
|
// lists initially empty
|
|
LPMASTER_LIST_REC RMGlobalMasterListHeader = NULL; // Locked by RMGlobalListLock
|
|
DWORD RMGlobalMasterListCount = 0; // Locked by RMGlobalListLock.
|
|
|
|
//
|
|
// Time (in secs since 1970) of last export tree chng or unlock.
|
|
// (We can't checksum a tree while it is locked.)
|
|
// This is set and used by PulserThread(), and set by NetrReplExportDirUnlock().
|
|
//
|
|
DWORD PulserTimeOfLastNotifyOrUnlock = 0; // Locked by RMGlobalListLock.
|
|
|
|
|
|
DBGSTATIC BOOL guard_needed;
|
|
DBGSTATIC DWORD PulserTimeOfSyncStart;
|
|
DBGSTATIC DWORD sync_time;
|
|
DBGSTATIC DWORD guard_time;
|
|
DBGSTATIC TCHAR path_buf[PATHLEN+1];
|
|
|
|
|
|
#define WAIT_N_SECONDS_AND_CATCH_CHANGES( totalSecondsToWait ) \
|
|
{ \
|
|
DWORD CycleStart = NetpReplTimeNow(); \
|
|
DWORD CycleEnd = CycleStart + totalSecondsToWait; \
|
|
DWORD SecondsLeftToWait = totalSecondsToWait; \
|
|
NetpAssert( totalSecondsToWait > 0 ); \
|
|
NetpAssert( CycleEnd > CycleStart ); \
|
|
/*lint -save -e716 */ /* disable warnings for while(TRUE) */ \
|
|
while (TRUE) { \
|
|
DWORD TimeWaitEnded; \
|
|
\
|
|
if (!NotifyPending) { \
|
|
/* Enable this pass of change notify... */ \
|
|
\
|
|
ApiStatus = ReplEnableChangeNotify( ChangeHandle ); \
|
|
if (ApiStatus != NO_ERROR) { \
|
|
NetpKdPrint(( PREFIX_REPL_MASTER \
|
|
"PulserThread got " FORMAT_API_STATUS \
|
|
" from ReplEnableChangeNotify.\n", ApiStatus )); \
|
|
goto Cleanup; \
|
|
} \
|
|
NotifyPending = TRUE; \
|
|
} \
|
|
\
|
|
NetpAssert( SecondsLeftToWait > 0 ); \
|
|
WaitStatus = WaitForMultipleObjects( \
|
|
2, /* handle count */ \
|
|
WaitHandles, \
|
|
FALSE, /* don't wait for all handles */ \
|
|
SecondsLeftToWait * 1000); /* timeout left (millisecs) */ \
|
|
\
|
|
/* Check if this thread should exit. */ \
|
|
\
|
|
TimeWaitEnded = NetpReplTimeNow(); \
|
|
if ( WaitStatus == 0 ) { /* termination handle */ \
|
|
Terminating = TRUE; \
|
|
break; /* exit do-while timeout loop */ \
|
|
} else if (WaitStatus == WAIT_TIMEOUT) { \
|
|
if (TimeWaitEnded < \
|
|
(CycleStart-BACKWARDS_TIME_CHNG_ALLOWED_SECS) ) { \
|
|
/* Time moved back a bunch, maybe a month. */ \
|
|
/* Cause first-time resync, reset checksum times, etc */ \
|
|
FirstTime = TRUE; \
|
|
LastTimeZoneOffsetSecs = NetpLocalTimeZoneOffset(); \
|
|
goto RestartCycle; \
|
|
} \
|
|
break; /* exit do-while timeout loop */ \
|
|
} \
|
|
NetpAssert( WaitStatus == 1 ); /* change notify handle */ \
|
|
NotifyPending = FALSE; \
|
|
ACQUIRE_LOCK( RMGlobalListLock ); \
|
|
/* BUGBUG: set all locks_fixed to FALSE? */ \
|
|
PulserTimeOfLastNotifyOrUnlock = TimeWaitEnded; \
|
|
RELEASE_LOCK( RMGlobalListLock ); \
|
|
\
|
|
if (TimeWaitEnded >= CycleEnd) { \
|
|
break; /* waited too long or clock moved forward */ \
|
|
} else if (TimeWaitEnded < \
|
|
(CycleStart-BACKWARDS_TIME_CHNG_ALLOWED_SECS) ) { \
|
|
/* Time moved back a bunch, maybe a month. */ \
|
|
/* Cause first-time resync, reset checksum times, etc */ \
|
|
FirstTime = TRUE; \
|
|
LastTimeZoneOffsetSecs = NetpLocalTimeZoneOffset(); \
|
|
goto RestartCycle; \
|
|
} \
|
|
SecondsLeftToWait = TimeWaitEnded - CycleStart; \
|
|
NetpAssert( SecondsLeftToWait > 0 ); \
|
|
/* do it again */ \
|
|
\
|
|
} /* while TRUE */ \
|
|
/*lint -restore */ /* re-enable warnings for while(TRUE) */ \
|
|
}
|
|
|
|
|
|
DWORD
|
|
PulserThread(
|
|
IN LPVOID parm
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Spawned by Master process, takes care of all replication master's timing.
|
|
|
|
Arguments:
|
|
parm : never used here.
|
|
|
|
Return Value:
|
|
does not return at all.
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
{
|
|
|
|
NET_API_STATUS ApiStatus;
|
|
LPREPL_CHANGE_NOTIFY_HANDLE ChangeHandle = NULL;
|
|
BOOL ConfigLocked = FALSE;
|
|
BOOL FirstTime = TRUE;
|
|
BOOL NotifyPending = FALSE;
|
|
LONG LastTimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
|
|
DWORD pulse_count = 0;
|
|
LPTSTR path_p;
|
|
DWORD path_len;
|
|
DWORD SyncTimeUsed;
|
|
BOOL Terminating = FALSE;
|
|
DWORD TimeOfLastChecksum = 0;// Time (secs since 1970) when we summed tree
|
|
HANDLE WaitHandles[2];
|
|
|
|
UNREFERENCED_PARAMETER(parm);
|
|
|
|
ACQUIRE_LOCK_SHARED( ReplConfigLock );
|
|
ConfigLocked = TRUE;
|
|
|
|
IF_DEBUG( PULSER ) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"PulserThread: thread ID is "
|
|
FORMAT_NET_THREAD_ID ", max pulse is " FORMAT_DWORD ".\n",
|
|
NetpCurrentThread(), ReplConfigPulse ));
|
|
}
|
|
NetpAssert( ReplIsPulseValid( ReplConfigPulse ) );
|
|
|
|
//
|
|
// Arrange to use change notify on export path.
|
|
//
|
|
ApiStatus = ReplSetupChangeNotify(
|
|
ReplConfigExportPath,
|
|
&ChangeHandle );
|
|
if (ApiStatus != NO_ERROR) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// converts times from minutes to seconds.
|
|
//
|
|
|
|
guard_time = ReplConfigGuardTime * 60;
|
|
sync_time = ReplConfigInterval * 60;
|
|
|
|
path_p = path_buf;
|
|
(void) STRCPY(path_p, ReplConfigExportPath);
|
|
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
ConfigLocked = FALSE;
|
|
|
|
//
|
|
// append a slash except for the case of "c:\" or "c:/".
|
|
//
|
|
|
|
path_len = STRLEN(path_p);
|
|
if ((path_p[path_len - 1] != TCHAR_BACKSLASH)
|
|
&& (path_p[path_len - 1] != TCHAR_FWDSLASH))
|
|
|
|
(void) STRCPY(path_p + path_len, SLASH);
|
|
|
|
WaitHandles[0] = ReplGlobalMasterTerminateEvent;
|
|
WaitHandles[1] = ChangeHandle->WaitableHandle;
|
|
|
|
/*lint -save -e716 */ // disable warnings for while(TRUE)
|
|
while (FOREVER) {
|
|
|
|
DWORD CurrentTime;
|
|
DWORD WaitStatus;
|
|
|
|
RestartCycle:
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"Pulser is starting its cycle..., "
|
|
"pulse_count is " FORMAT_DWORD "\n",
|
|
pulse_count ));
|
|
|
|
}
|
|
|
|
guard_needed = FALSE;
|
|
|
|
PulserTimeOfSyncStart = NetpReplTimeNow();
|
|
|
|
if (FirstTime) {
|
|
TimeOfLastChecksum = PulserTimeOfSyncStart;
|
|
}
|
|
|
|
ACQUIRE_LOCK_SHARED( RMGlobalListLock );
|
|
if ( (TimeOfLastChecksum <= PulserTimeOfLastNotifyOrUnlock)
|
|
|| (PulserTimeOfLastNotifyOrUnlock > PulserTimeOfSyncStart)
|
|
|| FirstTime ) {
|
|
|
|
RELEASE_LOCK( RMGlobalListLock );
|
|
TimeOfLastChecksum = PulserTimeOfSyncStart;
|
|
SyncUpdate(); // Checks for changes, if guard needed raises
|
|
// guard_needed
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"Pulser completed SyncUpdate.\n" ));
|
|
|
|
}
|
|
} else {
|
|
RELEASE_LOCK( RMGlobalListLock );
|
|
}
|
|
|
|
if (FirstTime) {
|
|
FirstTime = FALSE;
|
|
}
|
|
|
|
ACQUIRE_LOCK_SHARED( ReplConfigLock );
|
|
ConfigLocked = TRUE;
|
|
if (( ReplConfigPulse != 0) && ( ++pulse_count == ReplConfigPulse)) {
|
|
pulse_count = 0;
|
|
SendPulse(); // Sends Pulse.
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"Pulser sent pulse.\n" ));
|
|
|
|
}
|
|
|
|
}
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
ConfigLocked = FALSE;
|
|
|
|
|
|
//
|
|
// Wait for guard stuff to become stable, or another change to occur.
|
|
//
|
|
|
|
if ((guard_needed) && (guard_time != 0)) {
|
|
|
|
WAIT_N_SECONDS_AND_CATCH_CHANGES( guard_time );
|
|
|
|
// Check if this thread should exit.
|
|
if (Terminating) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We need to re-scan the tree, even if we didn't get a change
|
|
// notify this time around. (Actually,
|
|
// We need to send a guard message if no changes occurred.
|
|
// GuardUpdate will do another checksum and decide whether or
|
|
// not to do the guard msg for each directory. BUGBUG: We could
|
|
// optimize this if no change notify occurred. We would still
|
|
// need to send the message, but the extra checksum is unneeded.
|
|
//
|
|
|
|
TimeOfLastChecksum = NetpReplTimeNow();
|
|
GuardUpdate(); // Check if those INTEGRITY snobs have
|
|
// stopped fingering their files.
|
|
// We checksum entire tree to find out.
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"Pulser completed GuardUpdate.\n" ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check for reasons to restart cycle:
|
|
// time (or date) set back
|
|
// timezone changed (e.g. to daylight savings time)
|
|
//
|
|
|
|
CurrentTime = NetpReplTimeNow();
|
|
if (CurrentTime < PulserTimeOfSyncStart) { // was time set back?
|
|
// Cause first-time resync, reset checksum times, etc.
|
|
FirstTime = TRUE;
|
|
LastTimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
|
|
goto RestartCycle;
|
|
}
|
|
|
|
{
|
|
LONG NewTimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
|
|
if (NewTimeZoneOffsetSecs != LastTimeZoneOffsetSecs) {
|
|
FirstTime = TRUE;
|
|
LastTimeZoneOffsetSecs = NewTimeZoneOffsetSecs;
|
|
goto RestartCycle;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if sync_time gone, or we need some sleeping.
|
|
//
|
|
|
|
SyncTimeUsed = CurrentTime - PulserTimeOfSyncStart;
|
|
if (SyncTimeUsed < sync_time) {
|
|
|
|
WAIT_N_SECONDS_AND_CATCH_CHANGES( sync_time - SyncTimeUsed );
|
|
|
|
// Check if this thread should exit.
|
|
|
|
if ( Terminating ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"Pulser completed one cycle.\n" ));
|
|
|
|
}
|
|
|
|
} // while
|
|
/*lint -restore */ // re-enable warnings for while(TRUE)
|
|
|
|
Cleanup:
|
|
|
|
if (ConfigLocked) {
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
}
|
|
|
|
if (ChangeHandle != NULL) {
|
|
(VOID) ReplCloseChangeNotify( ChangeHandle );
|
|
}
|
|
|
|
// get rid of master_rec_list
|
|
|
|
ReplMasterFreeList();
|
|
|
|
// get rid of message buffer
|
|
|
|
ReplMasterFreeMsgBuf();
|
|
|
|
IF_DEBUG( PULSER ) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"PulserThread: exiting thread " FORMAT_NET_THREAD_ID ".\n",
|
|
NetpCurrentThread() ));
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
} // PulserThread
|
|
|
|
|
|
VOID
|
|
SyncUpdate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
Does a complete scan of participating directories, creates the update msg
|
|
and if needed sets up the guard list
|
|
|
|
Arguments :
|
|
none
|
|
|
|
Return Value :
|
|
none
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus = NO_ERROR, TmpStatus = NO_ERROR;
|
|
BOOL ConfigLocked = FALSE,
|
|
Update = FALSE;
|
|
HANDLE shan = INVALID_HANDLE_VALUE;
|
|
WIN32_FIND_DATA search_buf;
|
|
|
|
ACQUIRE_LOCK_SHARED( ReplConfigLock );
|
|
ConfigLocked = TRUE;
|
|
|
|
|
|
//
|
|
// Change to Export directory - note - it might be locked by
|
|
// another process
|
|
//
|
|
|
|
|
|
if (!SetCurrentDirectory(ReplConfigExportPath)) {
|
|
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"SyncUpdate() set current directory to " FORMAT_LPTSTR "\n",
|
|
ReplConfigExportPath ));
|
|
|
|
}
|
|
|
|
//
|
|
// reset exists for every entry in master list,
|
|
// so when we complete this sync we can know
|
|
// which dirs have been deleted
|
|
//
|
|
|
|
SetForDelDirs();
|
|
|
|
InitMsgSend(SYNC_MSG); // creates message buffer, header etc to be used
|
|
// for subsequent update
|
|
|
|
//
|
|
// Go thru all SubDirectories
|
|
//
|
|
|
|
shan = FindFirstFile(STAR_DOT_STAR, & search_buf);
|
|
|
|
|
|
if(shan != INVALID_HANDLE_VALUE) {
|
|
|
|
//
|
|
// on valid handle
|
|
//
|
|
|
|
do
|
|
{
|
|
|
|
//
|
|
// make sure it's a dir and not '.' or '..'
|
|
//
|
|
|
|
if ((search_buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
|
(STRCMP(search_buf.cFileName , DOT)) &&
|
|
(STRCMP(search_buf.cFileName , DOT_DOT))) {
|
|
|
|
if (!SetCurrentDirectory(search_buf.cFileName)) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
|
|
|
|
//
|
|
// only keep an error for the first dir that failed
|
|
// so that we don't fill up the error log or message buffers
|
|
//
|
|
if ( ApiStatus == NO_ERROR )
|
|
ApiStatus = TmpStatus;
|
|
|
|
//
|
|
// for the contunue statement we want to get the
|
|
// next dir in the export path and try that one.
|
|
// We are assuming that the last SetCurrentDirectory
|
|
// call which failed left us at the ReplConfigExportPath
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// does the work
|
|
//
|
|
|
|
ACQUIRE_LOCK( RMGlobalListLock );
|
|
|
|
// Note: ScanObject needs shared lock on ReplConfigLock and
|
|
// any lock on RMGlobalListLock.
|
|
ScanObject(
|
|
search_buf.cFileName,
|
|
SYNC_SCAN,
|
|
TRUE ); // yes, caller has excl lock
|
|
// (ScanObject converted excl lock to shared.)
|
|
|
|
RELEASE_LOCK( RMGlobalListLock );
|
|
Update = TRUE; // possible Update to this dir
|
|
|
|
if (!SetCurrentDirectory(DOT_DOT)) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
|
|
|
|
//
|
|
// Can't get back to parent dir, something is messed up.
|
|
// Go log error and return.
|
|
// this is a more serious error and indicates that we could
|
|
// not continue with the next find next call so exit and
|
|
// send what we have insead of continuing.
|
|
//
|
|
goto Cleanup;
|
|
|
|
}
|
|
}
|
|
} while (FindNextFile(shan, &search_buf));
|
|
|
|
} else {
|
|
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"SyncUpdate() is in error "
|
|
"when calling FindFirstFile(), " FORMAT_DWORD "\n",
|
|
ApiStatus ));
|
|
|
|
}
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// See if an existing directory (i.e it has a record on the Master List)
|
|
// has been deleted then send a message for the rest
|
|
//
|
|
if ( Update )
|
|
{
|
|
CheckForDelDirs();
|
|
MsgSend();
|
|
}
|
|
|
|
if (ConfigLocked) {
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
}
|
|
|
|
if (shan != INVALID_HANDLE_VALUE) {
|
|
(VOID) FindClose(shan);
|
|
}
|
|
|
|
if (ApiStatus != NO_ERROR) { // log the first error that occured if any
|
|
AlertLogExit(ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
}
|
|
|
|
|
|
} // SyncUpdate
|
|
|
|
|
|
|
|
VOID
|
|
GuardUpdate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
Scans TREE INTEGRITY trees found unstable on last update. For those
|
|
stable since then, sends update to clients and updates Master List
|
|
|
|
Arguments :
|
|
none
|
|
|
|
Return Value :
|
|
none
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus = NO_ERROR, TmpStatus = NO_ERROR;
|
|
BOOL ConfigLocked = FALSE,
|
|
Update = FALSE;
|
|
PMASTER_LIST_REC cur_rec;
|
|
BOOL ListLocked = FALSE;
|
|
|
|
ACQUIRE_LOCK_SHARED( ReplConfigLock );
|
|
ConfigLocked = TRUE;
|
|
|
|
//
|
|
// Change to Export directory - note - it might be locked by
|
|
// another process
|
|
//
|
|
|
|
if (!SetCurrentDirectory(ReplConfigExportPath)) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
InitMsgSend(GUARD_MSG); // creates message buffer, header
|
|
// etc to be used for update
|
|
|
|
//
|
|
// Go thru all SubDirectories in Guard list
|
|
//
|
|
|
|
ACQUIRE_LOCK_SHARED( RMGlobalListLock ); // ScanObject() needs any lock.
|
|
ListLocked = TRUE;
|
|
|
|
cur_rec = RMGlobalMasterListHeader;
|
|
|
|
while (cur_rec != NULL) {
|
|
if (cur_rec->grd_count != MASTER_GUARD_NOT_NEEDED) {
|
|
|
|
if (!SetCurrentDirectory(cur_rec->dir_name)) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
|
|
//
|
|
// only keep an error for the first dir that failed
|
|
// so that we don't fill up the error log or message buffers
|
|
//
|
|
if ( ApiStatus == NO_ERROR )
|
|
ApiStatus = TmpStatus;
|
|
|
|
//
|
|
// we want to get the next dir record and try that one.
|
|
// We are assuming that the last SetCurrentDirectory
|
|
// call which failed left us at the ReplConfigExportPath
|
|
// therefor skip the else and pick up the next record
|
|
//
|
|
}
|
|
else {
|
|
|
|
//
|
|
// does the work
|
|
// Note: ScanObject needs shared lock on ReplConfigLock and
|
|
// excl lock on RMGlobalListLock.
|
|
//
|
|
|
|
ScanObject( cur_rec->dir_name,
|
|
GUARD_SCAN,
|
|
FALSE
|
|
); // no, caller does not have excl lock
|
|
|
|
Update = TRUE; // possible Update to this dir
|
|
|
|
if (!SetCurrentDirectory(DOT_DOT)) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
NetpAssert( ApiStatus != ERROR_INVALID_FUNCTION );
|
|
|
|
//
|
|
// Can't get back to parent dir, something is messed up.
|
|
// Go log error and return.
|
|
// this is a more serious error and indicates that we could
|
|
// not continue with the next find next call so exit and
|
|
// send what we have insead of continuing.
|
|
//
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
cur_rec = cur_rec->next_p;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (ListLocked) { // MsgSend gets into trouble if master
|
|
RELEASE_LOCK( RMGlobalListLock ); // list is locked, so avoid it.
|
|
ListLocked = FALSE;
|
|
}
|
|
|
|
if ( Update )
|
|
{
|
|
MsgSend(); // Let the world know the difference ..
|
|
}
|
|
|
|
if (ConfigLocked) {
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
}
|
|
|
|
if (ApiStatus != NO_ERROR) { // log the first error that is found if any
|
|
AlertLogExit(ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
}
|
|
|
|
} // GuardUpdate
|
|
|
|
|
|
VOID
|
|
SendPulse(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
Sends all entries in Master List that have a timestamp earlier then
|
|
previous sync update
|
|
|
|
Arguments :
|
|
|
|
Return Value :
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
{
|
|
DWORD CurrentTime = NetpReplTimeNow();
|
|
PMASTER_LIST_REC cur_rec;
|
|
|
|
InitMsgSend(PULSE_MSG); // creates message buffer, header etc
|
|
// to be used for subsequent update
|
|
|
|
ACQUIRE_LOCK_SHARED( RMGlobalListLock );
|
|
|
|
cur_rec = RMGlobalMasterListHeader;
|
|
|
|
while (cur_rec != NULL) {
|
|
IF_DEBUG( PULSER ) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"SendPulse: found '" FORMAT_LPTSTR "', timestamp "
|
|
FORMAT_DWORD ".\n",
|
|
cur_rec->dir_name, cur_rec->timestamp ));
|
|
}
|
|
if ((cur_rec->timestamp < (PulserTimeOfSyncStart - sync_time))
|
|
&& (cur_rec->count != (DWORD) -1)) {
|
|
|
|
IF_DEBUG(PULSER) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"SendPulse: calling AddToMsg with dir name '"
|
|
FORMAT_LPTSTR "'.\n", cur_rec->dir_name ));
|
|
}
|
|
|
|
AddToMsg(cur_rec, PULSE);
|
|
|
|
} else if (CurrentTime < (cur_rec->timestamp)) { // clock set back?
|
|
|
|
IF_DEBUG(PULSER) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"SendPulse: calling AddToMsg with dir name '"
|
|
FORMAT_LPTSTR "' (TIME WENT BACK).\n",
|
|
cur_rec->dir_name ));
|
|
}
|
|
|
|
AddToMsg(cur_rec, PULSE);
|
|
}
|
|
|
|
cur_rec = cur_rec->next_p;
|
|
|
|
}
|
|
RELEASE_LOCK( RMGlobalListLock );
|
|
|
|
MsgSend();
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ScanObject(
|
|
IN LPTSTR dir_name,
|
|
IN DWORD mode,
|
|
IN BOOL CallerHasExclLock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Scans a complete object (TREE or DIRECTORY) counting files and checksumming
|
|
and updates Master List and adds entries to msg_buf according to mode,
|
|
change detected and guard / passed status.
|
|
|
|
Note that the caller must have a lock (any kind) on RMGlobalListLock.
|
|
This routine will convert it to a shared lock if needed.
|
|
|
|
Caller must also have lock (shared will do) on ReplConfigLock.
|
|
|
|
Arguments:
|
|
dir_name - Name of dir (tree) to be scanned.
|
|
|
|
mode - scan mode: SYNC_SCAN or GUARD_SCAN.
|
|
|
|
CallerHasExclLock - TRUE iff caller has exclusive lock on RMGlobaListLock,
|
|
in which case this routine will convert to shared lock.
|
|
|
|
Return Value:
|
|
If encounters and error simply returns - doing nothing.
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
CHECKSUM_REC tmp;
|
|
PCHECKSUM_REC tmp_rec;
|
|
PMASTER_LIST_REC cur_rec;
|
|
LONG MasterTimeZoneOffsetSecs; // exporter offset from GMT
|
|
DWORD msg_code, path_index;
|
|
LPTSTR path;
|
|
|
|
IF_DEBUG(PULSER) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"ScanObject: called with dir name '" FORMAT_LPTSTR
|
|
"', mode " FORMAT_DWORD ".\n", dir_name, mode ));
|
|
}
|
|
|
|
path = (LPTSTR)path_buf;
|
|
path_index = STRLEN(path);
|
|
|
|
tmp_rec = &tmp;
|
|
|
|
msg_code = UPDATE;
|
|
|
|
cur_rec = GetMasterRec(dir_name); // Gets cur record for dir, returns
|
|
// NULL if none exists.
|
|
|
|
// it's not in the current list maby its in the regestry?
|
|
|
|
if (cur_rec == NULL) {
|
|
ApiStatus = ExportDirGetRegistryValues( NULL, dir_name );
|
|
if ( ApiStatus == NO_ERROR ) {
|
|
|
|
// name found in registry and placed in the MasterList
|
|
// so now get the record
|
|
|
|
cur_rec = GetMasterRec(dir_name);
|
|
NetpAssert( cur_rec != NULL );
|
|
} else {
|
|
|
|
//
|
|
// no record in registry or could not access or other error so
|
|
// make new entry and continue.
|
|
//
|
|
|
|
cur_rec = NewMasterRecord(
|
|
dir_name,
|
|
DEFAULT_INTEGRITY,
|
|
DEFAULT_EXTENT);
|
|
if (cur_rec == NULL) {
|
|
|
|
IF_DEBUG(PULSER) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"ScanObject() can't create a new directory record.\n"
|
|
));
|
|
}
|
|
|
|
ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
msg_code = START;
|
|
|
|
// Write new entry to registry...
|
|
ApiStatus = ExportDirWriteConfigData (
|
|
NULL, // server name
|
|
dir_name,
|
|
DEFAULT_INTEGRITY,
|
|
DEFAULT_EXTENT,
|
|
0, // lock count
|
|
0 ); // Seconds since 1970.
|
|
if (ApiStatus != NO_ERROR) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
// At this point we know we've got a record in the client list.
|
|
NetpAssert( cur_rec != NULL );
|
|
|
|
if (CallerHasExclLock) {
|
|
//
|
|
// Let's change our exclusive lock to a shared one, as we may be in here
|
|
// for quite a long time, maybe even hours.
|
|
//
|
|
CONVERT_EXCLUSIVE_LOCK_TO_SHARED( RMGlobalListLock );
|
|
}
|
|
|
|
//
|
|
// Fix user lock files if necessary. (We would have to do this if
|
|
// this is a new first-level directory. Fixing the lock files may
|
|
// create a lock file for the replicator, which a downlevel importer would
|
|
// need to see.)
|
|
//
|
|
if ( ! (cur_rec->locks_fixed) ) {
|
|
IF_DEBUG( PULSER ) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"ScanObject: fixing user locks for new first-level '"
|
|
FORMAT_LPTSTR "'\n", dir_name ));
|
|
}
|
|
|
|
// Fix UserLock file(s) to match lock count.
|
|
ApiStatus = ExportDirFixUserLockFiles(
|
|
(LPCTSTR) ReplConfigExportPath,
|
|
(LPCTSTR) dir_name,
|
|
cur_rec->lockcount );
|
|
// BUGBUG: unfriendly on CD-ROM!
|
|
if (ApiStatus != NO_ERROR) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
cur_rec->locks_fixed = TRUE;
|
|
}
|
|
|
|
//
|
|
// The checksums we send will exporter's local time, which may different
|
|
// than importer's, so compute offset to use for the checksums.
|
|
//
|
|
MasterTimeZoneOffsetSecs = NetpLocalTimeZoneOffset();
|
|
|
|
//
|
|
// Done fixing user locks. Continue with normal stuff.
|
|
//
|
|
|
|
cur_rec->exists = TRUE; // to enable detection of dir deletion.
|
|
|
|
(void) STRCPY(path + path_index, dir_name);
|
|
|
|
if (cur_rec->extent == REPL_EXTENT_FILE) {
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"ScanObject() called ScanDir.\n" ));
|
|
|
|
}
|
|
|
|
// scans dir, checksums and counts files
|
|
ScanDir(
|
|
MasterTimeZoneOffsetSecs, // exporter offset from GMT
|
|
path,
|
|
tmp_rec,
|
|
0);
|
|
}
|
|
else {
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"ScanObject() called ScanTree.\n" ));
|
|
|
|
}
|
|
|
|
// as above but on a tree
|
|
ScanTree(
|
|
MasterTimeZoneOffsetSecs, // exporter offset from GMT
|
|
path,
|
|
tmp_rec);
|
|
}
|
|
|
|
*(path + path_index) = '\0';
|
|
|
|
if (tmp_rec->count == (DWORD)-1) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"ScanObject ERROR! GOT COUNT -1 FROM ScanTree/ScanDir!!!\n" ));
|
|
// BUGBUG: log this?
|
|
return;
|
|
}
|
|
|
|
if (mode == SYNC_SCAN) {
|
|
|
|
//
|
|
// Change ?
|
|
//
|
|
|
|
BOOL NeedMsg;
|
|
NeedMsg = ( (tmp_rec->count != cur_rec->count)
|
|
|| (tmp_rec->checksum != cur_rec->checksum) );
|
|
if (cur_rec->timestamp > NetpReplTimeNow()) {
|
|
// Time went backwards, e.g. a month, so force message to get sent
|
|
// for this dir even though checksums match.
|
|
NeedMsg = TRUE;
|
|
}
|
|
|
|
if (NeedMsg) {
|
|
if ((cur_rec->integrity == REPL_INTEGRITY_TREE) && (guard_time != 0)) {
|
|
|
|
//
|
|
// wait GUARDTIME
|
|
//
|
|
|
|
guard_needed = TRUE; // raise flag
|
|
|
|
cur_rec->grd_count = tmp_rec->count;
|
|
cur_rec->grd_checksum = tmp_rec->checksum;
|
|
} else // integrity = FILE
|
|
UpdateRecAndMsg(cur_rec, tmp_rec, msg_code);
|
|
|
|
}
|
|
|
|
} // mode = SYNC_SCAN
|
|
|
|
if (mode == GUARD_SCAN) {
|
|
|
|
//
|
|
// Change during GUARDTIME ?
|
|
//
|
|
|
|
if ((tmp_rec->count == cur_rec->grd_count)
|
|
&& (tmp_rec->checksum == cur_rec->grd_checksum)) {
|
|
|
|
//
|
|
// tree is stable so just update
|
|
//
|
|
|
|
if (cur_rec->count == (DWORD) -1) // see if we havent just started?
|
|
|
|
msg_code = START;
|
|
|
|
UpdateRecAndMsg(cur_rec, tmp_rec, msg_code);
|
|
}
|
|
}
|
|
|
|
ApiStatus = NO_ERROR;
|
|
|
|
Cleanup:
|
|
|
|
if (ApiStatus != NO_ERROR) {
|
|
AlertLogExit(ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
}
|
|
|
|
IF_DEBUG(PULSER) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"ScanObject() is done.\n" ));
|
|
|
|
}
|
|
|
|
|
|
} // End of ScanObject
|
|
|
|
|
|
VOID
|
|
SetForDelDirs(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Inits Checks for directory under EXPORT that have been deleted.
|
|
Before SYNC for each entry in the master list the exists flag is reset,
|
|
it is set when the directory is scanned ( a good sign for it's existence).
|
|
CheckForDelDirs below goes again thru master list for those entries not set.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Return Value:
|
|
none
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
{
|
|
PMASTER_LIST_REC cur_rec;
|
|
|
|
ACQUIRE_LOCK( RMGlobalListLock );
|
|
|
|
cur_rec = RMGlobalMasterListHeader;
|
|
|
|
while (cur_rec != NULL) {
|
|
cur_rec->exists = FALSE;
|
|
cur_rec = cur_rec->next_p;
|
|
}
|
|
|
|
RELEASE_LOCK( RMGlobalListLock );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
CheckForDelDirs(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Checks for directory under EXPORT that have been deleted.
|
|
The check is done by going thru entire list for entries whose exists flag
|
|
is not set.
|
|
|
|
For every deleted directory, it sends an END update message and removes
|
|
the dir's entry in the master list.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
{
|
|
PMASTER_LIST_REC cur_rec;
|
|
PMASTER_LIST_REC next_rec;
|
|
|
|
ACQUIRE_LOCK( RMGlobalListLock ); // RemoveFromList() needs excl. lock.
|
|
cur_rec = RMGlobalMasterListHeader;
|
|
|
|
while (cur_rec != NULL) {
|
|
next_rec = cur_rec->next_p;
|
|
if ( ! (cur_rec->exists) ) {
|
|
|
|
IF_DEBUG(PULSER) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"CheckForDelDirs: calling AddToMsg "
|
|
"with dir name '" FORMAT_LPTSTR
|
|
"'.\n", cur_rec->dir_name ));
|
|
}
|
|
|
|
AddToMsg(cur_rec, END); // Send an EndOfRepl msg
|
|
|
|
RemoveFromList(cur_rec); // Remove from MasterList
|
|
}
|
|
cur_rec = next_rec;
|
|
}
|
|
RELEASE_LOCK( RMGlobalListLock );
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdateRecAndMsg(
|
|
IN OUT PMASTER_LIST_REC dir_rec,
|
|
IN PCHECKSUM_REC new_check,
|
|
IN DWORD msg_opcode)
|
|
/*++
|
|
|
|
Routine Description :
|
|
Updates entry in Master List and adds to msg_buf
|
|
|
|
Arguments :
|
|
dir_rec tree's record in master list. Must be excl locked by caller.
|
|
new_check New checksum and count for tree.
|
|
msg_opcode code for update msg: START or UPDATE
|
|
|
|
Return Value :
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
{
|
|
NetpAssert( new_check != NULL );
|
|
|
|
dir_rec->count = new_check->count; // update old record
|
|
dir_rec->checksum = new_check->checksum;
|
|
dir_rec->timestamp = NetpReplTimeNow(); // .. and timestamp it
|
|
dir_rec->grd_count = MASTER_GUARD_NOT_NEEDED; // mark it as unguarded
|
|
|
|
IF_DEBUG(PULSER) {
|
|
NetpKdPrint(( PREFIX_REPL_MASTER
|
|
"UpdateRecAndMsg: calling AddToMsg with dir name '"
|
|
FORMAT_LPTSTR "'.\n", dir_rec->dir_name ));
|
|
}
|
|
|
|
AddToMsg(dir_rec, msg_opcode);
|
|
|
|
}
|
|
|
|
|
|
PMASTER_LIST_REC
|
|
NewMasterRecord(
|
|
IN LPTSTR dir_name,
|
|
IN DWORD integrity,
|
|
IN DWORD extent
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
Allocates new record links it to master list, and inits the members.
|
|
Caller must have exclusive lock for the master list before calling
|
|
NewMasterRecord.
|
|
|
|
Arguments :
|
|
dir_name - new dir name
|
|
integrity - new dir's integrity
|
|
extent - bew dir's extent
|
|
|
|
Return Value :
|
|
returns - pointer to new record.
|
|
- NULL if fails.
|
|
|
|
--*/
|
|
{
|
|
PMASTER_LIST_REC rec;
|
|
|
|
rec = (PMASTER_LIST_REC)NetpMemoryAllocate(sizeof(MASTER_LIST_REC));
|
|
|
|
if(rec == NULL) {
|
|
|
|
AlertLogExit(ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
ERROR_NOT_ENOUGH_MEMORY,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// put values in
|
|
//
|
|
|
|
rec->extent = extent;
|
|
rec->integrity = integrity;
|
|
rec->checksum = 0;
|
|
rec->count = (DWORD) -1;
|
|
rec->grd_checksum = 0;
|
|
rec->grd_count = MASTER_GUARD_NOT_NEEDED;
|
|
rec->lockcount = 0;
|
|
rec->locks_fixed = FALSE;
|
|
rec->time_of_first_lock = 0;
|
|
(void) STRCPY(rec->dir_name, dir_name);
|
|
|
|
//
|
|
// Chain record into master list.
|
|
// Caller already has exclusive lock for master list, so it's OK.
|
|
//
|
|
|
|
rec->next_p = RMGlobalMasterListHeader;
|
|
rec->prev_p = NULL;
|
|
RMGlobalMasterListHeader = rec;
|
|
|
|
if (rec->next_p != NULL) // when inserting the very first record,
|
|
// there is no next
|
|
|
|
(rec->next_p)->prev_p = rec;
|
|
|
|
++RMGlobalMasterListCount;
|
|
|
|
return rec;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
RemoveFromList(
|
|
IN PMASTER_LIST_REC rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Removes record from master list (doubly linked) and
|
|
free the block of memory.
|
|
|
|
Note that the caller must have an exclusive lock on RMGlobalListLock.
|
|
|
|
Arguments:
|
|
rec : pointer to the record that to be removed.
|
|
|
|
Return Value:
|
|
none
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
{
|
|
|
|
NetpAssert( rec != NULL );
|
|
|
|
NetpAssert( RMGlobalMasterListCount > 0 );
|
|
|
|
if (rec->prev_p != NULL)
|
|
(rec->prev_p)->next_p = rec->next_p;
|
|
else // it's the first record
|
|
RMGlobalMasterListHeader = rec->next_p;
|
|
|
|
if (rec->next_p != NULL) // if it's not the last record
|
|
(rec->next_p)->prev_p = rec->prev_p;
|
|
|
|
--RMGlobalMasterListCount;
|
|
|
|
NetpMemoryFree( rec );
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
RemoveMasterRecForDirName(
|
|
IN LPTSTR DirName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Removes record from master list (doubly linked) and
|
|
free the block of memory.
|
|
|
|
The caller must have an exclusive lock on RMGlobalListLock.
|
|
|
|
Arguments:
|
|
DirName : Name of directory whose master record is to be deleted.
|
|
|
|
Return Value:
|
|
NET_API_STATUS (NO_ERROR or NERR_UnknownDevDir)
|
|
|
|
Threads:
|
|
Only called by API threads.
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus = NERR_UnknownDevDir; // assume guilty until...
|
|
PMASTER_LIST_REC Rec;
|
|
|
|
Rec = RMGlobalMasterListHeader;
|
|
|
|
while (Rec != NULL) {
|
|
if (STRICMP(Rec->dir_name, DirName))
|
|
Rec = Rec->next_p;
|
|
else {
|
|
|
|
NetpAssert( RMGlobalMasterListCount > 0 );
|
|
|
|
if (Rec->prev_p != NULL)
|
|
(Rec->prev_p)->next_p = Rec->next_p;
|
|
else // it's the first record
|
|
RMGlobalMasterListHeader = Rec->next_p;
|
|
|
|
if (Rec->next_p != NULL) // if it's not the last record
|
|
(Rec->next_p)->prev_p = Rec->prev_p;
|
|
|
|
--RMGlobalMasterListCount;
|
|
|
|
NetpMemoryFree( Rec );
|
|
ApiStatus = NO_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (ApiStatus);
|
|
|
|
} // RemoveMasterRecForDirName
|
|
|
|
|
|
PMASTER_LIST_REC
|
|
GetMasterRec(
|
|
IN LPTSTR dir_name
|
|
)
|
|
/*++
|
|
|
|
Routine Description :
|
|
Searches master list for record with dir_name.
|
|
Caller must lock the master list before calling GetMasterRec.
|
|
|
|
Arguments :
|
|
dir_name - the required dir_name
|
|
|
|
Return Value :
|
|
returns a pointer to the required record or NULL if not found
|
|
|
|
--*/
|
|
{
|
|
PMASTER_LIST_REC Rec;
|
|
|
|
Rec = RMGlobalMasterListHeader;
|
|
|
|
while (Rec != NULL) {
|
|
if (STRICMP(Rec->dir_name, dir_name))
|
|
Rec = Rec->next_p;
|
|
else
|
|
break;
|
|
}
|
|
|
|
return Rec;
|
|
}
|
|
|
|
VOID
|
|
ReplMasterFreeList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Free RMGlobalMasterListHeader. called from pulser thread main routine.
|
|
|
|
Arguments:
|
|
none
|
|
|
|
Return Value:
|
|
none
|
|
|
|
Threads:
|
|
Only called by pulser thread.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMASTER_LIST_REC Rec, NextRec;
|
|
|
|
ACQUIRE_LOCK( RMGlobalListLock );
|
|
|
|
Rec = RMGlobalMasterListHeader;
|
|
|
|
while (Rec != NULL) {
|
|
NextRec = Rec->next_p;
|
|
NetpMemoryFree( Rec );
|
|
Rec = NextRec;
|
|
}
|
|
|
|
RMGlobalMasterListHeader = NULL;
|
|
RMGlobalMasterListCount = 0;
|
|
|
|
RELEASE_LOCK( RMGlobalListLock );
|
|
}
|