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.
1664 lines
46 KiB
1664 lines
46 KiB
/*++
|
|
|
|
Copyright (c) 1987-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
syncer.c
|
|
|
|
Abstract:
|
|
|
|
Contains Syncer thread - does actual work for REPL client.
|
|
|
|
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.
|
|
|
|
21-Oct-1991 (cliffv)
|
|
Ported to NT. Converted to NT style.
|
|
|
|
30-Dec-1991 JohnRo
|
|
Made some changes suggested by PC-LINT.
|
|
16-Jan-1992 JohnRo
|
|
Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
|
|
Added debug print of thread ID.
|
|
Changed to use NetLock.h (allow shared locks, for one thing).
|
|
Use REPL_STATE_ equates for client_list_rec.state values.
|
|
24-Jan-1992 JohnRo
|
|
P_ globals are now called ReplGlobal variables in replgbl.h.
|
|
Changed to use LPTSTR etc.
|
|
24-Mar-1992 JohnRo
|
|
Renamed many ReplGlobal vars to ReplConfig vars.
|
|
Added/corrected some lock handling.
|
|
Added a little more debug output.
|
|
25-Mar-1992 JohnRo
|
|
Added some thread comments.
|
|
27-Mar-1992 JohnRo
|
|
Just a debug output adjustment.
|
|
29-Jul-1992 JohnRo
|
|
RAID 2650: repl svc should handle new subdirs.
|
|
19-Aug-1992 JohnRo
|
|
RAID 2115: repl svc should wait while stopping or changing role.
|
|
Use PREFIX_ equates.
|
|
05-Nov-1992 JohnRo
|
|
RAID 5496: old fields not maintained in registry.
|
|
Added some debug output when setting no sync state.
|
|
17-Jan-1993 JohnRo
|
|
RAID 7053: locked trees added to pulse msg. (Actually fix all
|
|
kinds of remote lock handling.)
|
|
Made changes suggested by PC-LINT 5.0
|
|
Added some debug checks.
|
|
01-Feb-1993 JohnRo
|
|
RAID 7983: Repl svc needs to change process token to really copy ACLs.
|
|
15-Feb-1993 JohnRo
|
|
RAID 8355: setting repl lock when registry doesn't exist causes assert.
|
|
11-Mar-1993 JohnRo
|
|
RAID 14144: avoid very long hourglass in repl UI.
|
|
26-Mar-1993 JohnRo
|
|
RAID 4267: Replicator has problems when work queue gets large.
|
|
We didn't really need excl lock on client list when syncing with tree
|
|
integrity.
|
|
Also, prepare for >32 bits someday.
|
|
01-Apr-1993 JohnRo
|
|
Reduce checksum frequency by using change notify on import tree.
|
|
Don't generate repl sys err popup for access denied for every cycle.
|
|
05-Apr-1993 JohnRo
|
|
RAID 5091: directory state not reflected accurately (in registry during
|
|
sync).
|
|
21-Apr-1993 JohnRo
|
|
RAID 7298: repl stops when it didn't get _access to an import dir.
|
|
23-Apr-1993 JohnRo
|
|
RAID 7157: fix setting ACLs, etc., on dirs themselves.
|
|
28-Apr-1993 JohnRo
|
|
Use NetpKdPrint() where possible.
|
|
25-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.
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <lmcons.h>
|
|
|
|
#include <alertmsg.h> // ALERT_* defines
|
|
#include <icanon.h> // I_NetPathCompare
|
|
#include <impdir.h> // ImportDirWriteConfigData().
|
|
#include <lmerr.h> // NERR_* defines
|
|
#include <lmerrlog.h> // NELOG_* defines
|
|
#include <lmrepl.h> // REPL_STATE_ equates.
|
|
#include <masproto.h> // ReplCheckExportLocks().
|
|
#include <netdebug.h> // DBGSTATIC, NetDbgHexDump(), FORMAT_ equates, etc.
|
|
#include <netlock.h> // Lock data types, functions, and macros.
|
|
#include <prefix.h> // PREFIX_ equates.
|
|
#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
|
|
#include <time.h> // ctime(), time(), etc.
|
|
#include <tstr.h> // STRLEN(), TCHAR_EOS, etc.
|
|
|
|
//
|
|
// Local include files
|
|
//
|
|
#include <repldefs.h> // IF_DEBUG(), etc.
|
|
#include <replgbl.h> // ReplGlobal and ReplConfig variables.
|
|
#include <client.h>
|
|
#include <replp.h>
|
|
|
|
// G L O B A L S:
|
|
#if DBG
|
|
DBGSTATIC int RCGlobalSyncReceived[NEXT_AVAILABLE_MESSAGE_NUMBER] = {
|
|
0};
|
|
#endif
|
|
|
|
//
|
|
// Global variable indicating whether logon is needed.
|
|
//
|
|
//
|
|
// logged_on is TRUE iff "someone" is logged onto the workstation.
|
|
// (i.e. after checked someone is logged on)
|
|
//
|
|
|
|
#ifdef LOGON_DEFINED
|
|
|
|
DBGSTATIC BOOL logged_on = FALSE;
|
|
|
|
//
|
|
// need_logoff is TRUE iff we have explictly logged on and must logoff.
|
|
//
|
|
DBGSTATIC BOOL need_logoff = FALSE;
|
|
|
|
#endif // LOGON_DEFINED
|
|
|
|
|
|
|
|
DBGSTATIC NET_API_STATUS
|
|
ReplLogon(
|
|
IN PCLIENT_LIST_REC cur_rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes care of logging on - Client repl can't _access master when no logon.
|
|
|
|
Assumes that caller has a lock (any kind) on RCGlobalClientListLock.
|
|
|
|
if (USER LOGGED ON)
|
|
if(ReplConfigTryUser = NO)
|
|
STOP (client service cannot proceed).
|
|
else
|
|
OK (just take advantage of the user's logon)
|
|
|
|
else (USER NOT LOGGED ON)
|
|
if (ReplConfigLogonUserName != NULL)
|
|
TRY LOGGING ON USING ReplConfigLogonUserName and P_passwd.....
|
|
|
|
else (ReplConfigLogonUserName == NULL)
|
|
TRY LOGGONG ON USING the local COMPUTERNAME and a NULL password.
|
|
if fails try establishing a new password.
|
|
|
|
Arguments:
|
|
|
|
cur_rec - The client tree record for the directory being replicated.
|
|
(Used only to determine who the master is).
|
|
|
|
Return Value:
|
|
|
|
0 - if succesfull.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
|
|
#ifdef LOGON_DEFINED
|
|
|
|
//
|
|
// If we've already successfully logged on, don't do it again.
|
|
//
|
|
|
|
if ( logged_on ) {
|
|
return (NO_ERROR);
|
|
}
|
|
char buf[MAX_WKSTA_INFO_SIZE_10];
|
|
struct wksta_info_10 *info;
|
|
unsigned short avail;
|
|
NET_API_STATUS NetStatus;
|
|
|
|
info = (struct wksta_info_10 * )buf;
|
|
|
|
if (NetStatus = NetWkstaGetInfo(NULL, 10, info,
|
|
MAX_WKSTA_INFO_SIZE_10, (unsigned short * ) & avail)) {
|
|
AlertLogExit(ALERT_ReplNetErr, NELOG_ReplNetErr, NetStatus,
|
|
NULL, NULL, NO_EXIT);
|
|
return (NetStatus);
|
|
}
|
|
|
|
//
|
|
// a user is logged on.
|
|
//
|
|
|
|
ACQUIRE_LOCK_SHARED( ReplConfigLock );
|
|
if (info->wki10_username[0] != 0) {
|
|
if (ReplConfigTryUser) {
|
|
|
|
//
|
|
// all is well a user is logged on and client is permitted
|
|
// to piggyback.
|
|
//
|
|
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
return (NO_ERROR);
|
|
} else {
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplLogon: setting no sync (user logged on)\n" ));
|
|
}
|
|
|
|
// Note: ReplSetSignalFile needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
|
|
if ((cur_rec->alerts & USER_LOGGED_ALERT) == 0) {
|
|
AlertLogExit(ALERT_ReplUserLoged, NELOG_ReplUserLoged, 0,
|
|
NULL, NULL, NO_EXIT);
|
|
cur_rec->alerts |= USER_LOGGED_ALERT;
|
|
}
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
return (BUGBUG);
|
|
}
|
|
}
|
|
|
|
//
|
|
// user is not logged on.
|
|
//
|
|
|
|
//
|
|
// is ReplConfigLogonUserName specified.
|
|
//
|
|
|
|
if (ReplConfigLogonUserName != NULL) {
|
|
|
|
//
|
|
// Try logon using ReplConfigLogonUserName and P_asswd.
|
|
//
|
|
// NERR_UnableToAddName_W error means messanger couldn't add the
|
|
// username which is always the case when loggong on with the
|
|
// computername
|
|
//
|
|
|
|
NetStatus = NetWkstaSetUID(NULL,
|
|
ReplConfigLogonUserName, P_passwd, NULL, 0);
|
|
if ((NetStatus != NO_ERROR) && (NetStatus != NERR_UnableToAddName_W)) {
|
|
|
|
if ((cur_rec->alerts & LOGON_FAILED_ALERT) == 0) {
|
|
AlertLogExit(
|
|
ALERT_ReplLogonFailed,
|
|
NELOG_ReplLogonFailed,
|
|
NetStatus,
|
|
ReplConfigLogonUserName,
|
|
ReplGlobalUnicodeComputerName,
|
|
NO_EXIT);
|
|
cur_rec->alerts |= LOGON_FAILED_ALERT;
|
|
}
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
return (NetStatus);
|
|
} else {
|
|
|
|
//
|
|
// succeeded in loging on.
|
|
//
|
|
|
|
need_logoff = TRUE;
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
return (NO_ERROR);
|
|
}
|
|
}
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
|
|
//
|
|
// user is not logged on and ReplConfigLogonUserName specified.
|
|
//
|
|
|
|
//
|
|
// try logging on using the computername and a NULL password.
|
|
//
|
|
|
|
NetStatus = NetWkstaSetUID(
|
|
NULL, ReplGlobalUnicodeComputerName,
|
|
"", NULL, 0);
|
|
if ( (NetStatus != NO_ERROR) && (NetStatus != NERR_UnableToAddName_W)) {
|
|
// This error means messanger couldn't add the username
|
|
// which is always the case when logging on with the computername.
|
|
|
|
if ((cur_rec->alerts & LOGON_FAILED_ALERT) == 0) {
|
|
AlertLogExit(ALERT_ReplLogonFailed, NELOG_ReplLogonFailed,
|
|
NetStatus,
|
|
ReplGlobalUnicodeComputerName,
|
|
ReplGlobalUnicodeComputerName,
|
|
NO_EXIT);
|
|
cur_rec->alerts |= LOGON_FAILED_ALERT;
|
|
}
|
|
|
|
return (NetStatus);
|
|
} else {
|
|
|
|
//
|
|
// succeeded in logging on.
|
|
//
|
|
|
|
need_logoff = TRUE;
|
|
return (NO_ERROR);
|
|
}
|
|
#else
|
|
|
|
return (NO_ERROR);
|
|
|
|
#endif // LOGON_DEFINED
|
|
|
|
DBG_UNREFERENCED_PARAMETER(cur_rec);
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
|
|
|
|
DBGSTATIC VOID
|
|
ReplLogoff(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Logs off from the current master.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
|
|
#ifdef LOGON_DEFINED
|
|
|
|
//
|
|
// Only physically logoff if it is needed.
|
|
//
|
|
|
|
if ( need_logoff ) {
|
|
NetWkstaSetUID(NULL, NULL, NULL, NULL, WKSTA_MAX_FORCE);
|
|
need_logoff = FALSE;
|
|
}
|
|
|
|
//
|
|
// Always indicate that we've logged off.
|
|
//
|
|
|
|
logged_on = FALSE;
|
|
|
|
#endif // LOGON_DEFINED
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DBGSTATIC VOID
|
|
ReplDoUpdate(
|
|
IN LPTSTR dir,
|
|
IN PSYNCMSG master_info,
|
|
IN PMSG_STATUS_REC status_rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does a normal update:
|
|
1. Checks if dir is in client_list (i.e. is it a new dir) and
|
|
adds it if not.
|
|
|
|
2. Checks if update is from dir's master, if not call
|
|
ReplMultiMaster function.
|
|
|
|
3. Checks for change (i.e. has status vars changed), if so call
|
|
ReplSyncTree.
|
|
|
|
Arguments:
|
|
|
|
dir - directory name.
|
|
|
|
master_info - master name, and some other parms.
|
|
|
|
status_rec - new checksum, count and timestamp.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
TCHAR master_path[MAX_PATH];
|
|
TCHAR client_path[MAX_PATH];
|
|
DWORD Master_Attributes, Client_Attributes, ApiStatus;
|
|
|
|
CHECKSUM_REC ck_rec;
|
|
PCLIENT_LIST_REC cur_rec;
|
|
BOOL ListLocked = FALSE;
|
|
NET_API_STATUS NetStatus = NERR_Success;
|
|
BOOL PermissionEnabled = FALSE;
|
|
BOOL SyncNeeded = FALSE;
|
|
TCHAR UncMaster[UNCLEN+1];
|
|
|
|
|
|
NetpAssert( dir != NULL );
|
|
NetpAssert( (*dir) != TCHAR_EOS );
|
|
NetpAssert( master_info != NULL );
|
|
NetpAssert( status_rec != NULL );
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
time_t Now = (time_t) NetpReplTimeNow();
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"%24s "
|
|
"Syncer processing msg with '" FORMAT_LPTSTR "'\n",
|
|
(LPSTR) ctime( &Now ),
|
|
dir ));
|
|
}
|
|
|
|
//
|
|
// Form master's and client's path UNC.
|
|
//
|
|
|
|
ACQUIRE_LOCK_SHARED( ReplConfigLock );
|
|
|
|
(void) STRCPY(master_path, SLASH_SLASH);
|
|
(void) STRCAT(master_path, master_info->header.sender);
|
|
(void) STRCAT(master_path, SLASH);
|
|
(void) STRCAT(master_path, REPL_SHARE);
|
|
(void) STRCAT(master_path, SLASH);
|
|
(void) STRCAT(master_path, dir);
|
|
|
|
(void) STRCPY(client_path, ReplConfigImportPath);
|
|
(void) STRCAT(client_path, SLASH);
|
|
(void) STRCAT(client_path, dir);
|
|
|
|
RELEASE_LOCK( ReplConfigLock );
|
|
|
|
//
|
|
// We'll also need the UNC name of the master computer itself.
|
|
//
|
|
|
|
if (STRLEN( master_info->header.sender ) > CNLEN) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: sender name in message is too long.\n" ));
|
|
// BUGBUG: log this.
|
|
goto Cleanup;
|
|
}
|
|
|
|
(VOID) STRCPY( UncMaster, (LPTSTR) SLASH_SLASH );
|
|
(VOID) STRCAT( UncMaster, master_info->header.sender );
|
|
NetpAssert( STRLEN( UncMaster ) <= UNCLEN );
|
|
|
|
//
|
|
// We're going to need backup and restore permissions to do stuff,
|
|
// so enable them here.
|
|
//
|
|
NetStatus = ReplEnableBackupPermission( ); // Enable both.
|
|
if (NetStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: unexpected status " FORMAT_API_STATUS
|
|
" from enable permission...\n", NetStatus ));
|
|
AlertLogExit( ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
NetStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
// Continue... Maybe we'll get lucky and still be able to do work.
|
|
} else {
|
|
PermissionEnabled = TRUE;
|
|
}
|
|
|
|
//
|
|
// Find or create client record first.
|
|
//
|
|
|
|
ACQUIRE_LOCK( RCGlobalClientListLock ); // Need excl in case we add below.
|
|
ListLocked = TRUE;
|
|
|
|
cur_rec = ReplGetClientRec(dir, NULL );
|
|
|
|
if (cur_rec == NULL) {
|
|
|
|
//
|
|
// add to client list.
|
|
//
|
|
|
|
if ((cur_rec = ReplAddClientRec(dir, master_info, status_rec)) == NULL) {
|
|
|
|
//
|
|
// might fail due to memory problem.
|
|
//
|
|
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
} else {
|
|
// Add equivalent info to registry, so ReplCreateReplLock doesn't
|
|
// get confused.
|
|
ApiStatus = ImportDirWriteConfigData(
|
|
NULL, // no server name (local).
|
|
dir, // dir name
|
|
REPL_STATE_NEVER_REPLICATED,
|
|
UncMaster, // master computer name
|
|
0, // no last update time
|
|
0, // no locks
|
|
0 ); // no lock time
|
|
if (ApiStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: unexpected ret code from "
|
|
"ImportDirWriteConfigData: " FORMAT_API_STATUS ".\n",
|
|
ApiStatus ));
|
|
ReplErrorLog(
|
|
NULL, // local (no server name)
|
|
NELOG_ReplSysErr, // log code
|
|
ApiStatus, // the unexpected error code
|
|
NULL, // no optional str1
|
|
NULL ); // no optional str2
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
}
|
|
SyncNeeded = TRUE; // don't even try ChecksumEqual below.
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// When the current record is created before a message is sent
|
|
// from the exporter, the master name is set to null. This causes
|
|
// ReplNetNameCompare to fail and forces a Duplicate master situation
|
|
// To prevent this, fill in the master name at this time since we
|
|
// know it.
|
|
//
|
|
|
|
if ( *(cur_rec->master) == '\0' ) {
|
|
(VOID) STRCPY( cur_rec->master, master_info->header.sender );
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point we know we've got a record in the client list.
|
|
// Let's change our exclusive lock to a shared one, as we may be in here
|
|
// for quite a long time, maybe even hours.
|
|
//
|
|
NetpAssert( cur_rec != NULL );
|
|
NetpAssert( ListLocked );
|
|
CONVERT_EXCLUSIVE_LOCK_TO_SHARED( RCGlobalClientListLock );
|
|
|
|
//
|
|
// Check if dir exists on client.
|
|
//
|
|
//
|
|
// The next chunk of code queries the client's first level directory.
|
|
// If one exists its attributes are compared against the masters. If
|
|
// one does not exist a directory is created with matching atributes.
|
|
// If both directories exist but the attributes do not match, the client's
|
|
// attributes are set to the master's.
|
|
//
|
|
// BUGBUG: What is not compared and updated is the security attributes
|
|
// of the client directory. Could the master directory contain acls that
|
|
// would be bad for the client to have?
|
|
//
|
|
|
|
|
|
Client_Attributes = GetFileAttributes( client_path );
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: attr for '" FORMAT_LPTSTR "' is "
|
|
FORMAT_HEX_DWORD ".\n", client_path, Client_Attributes ));
|
|
}
|
|
|
|
if ( Client_Attributes == (DWORD) -1 ) {
|
|
|
|
NetStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( NetStatus != NO_ERROR );
|
|
|
|
//
|
|
// If the import path cannot be found,
|
|
// alert someone.
|
|
//
|
|
|
|
if (NetStatus == ERROR_PATH_NOT_FOUND) {
|
|
|
|
AlertLogExit( ALERT_ReplBadImport,
|
|
NELOG_ReplBadImport,
|
|
0,
|
|
client_path,
|
|
NULL,
|
|
NO_EXIT);
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
|
|
//
|
|
// If access denied, don't exit; try next dir.
|
|
//
|
|
|
|
} else if ((NetStatus == ERROR_ACCESS_DENIED)
|
|
|| (NetStatus == ERROR_NETWORK_ACCESS_DENIED)) {
|
|
|
|
if ((cur_rec->alerts & ACCESS_DENIED_ALERT) == 0) {
|
|
AlertLogExit( ALERT_ReplAccessDenied,
|
|
NELOG_ReplAccessDenied,
|
|
NetStatus,
|
|
client_path,
|
|
UncMaster, // need my own server name here.
|
|
NO_EXIT);
|
|
cur_rec->alerts |= ACCESS_DENIED_ALERT;
|
|
}
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
|
|
//
|
|
// If this is any error other than the directory not existing,
|
|
// alert someone and continue.
|
|
//
|
|
|
|
} else if (NetStatus != ERROR_FILE_NOT_FOUND) {
|
|
|
|
AlertLogExit( ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
NetStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
}
|
|
|
|
//
|
|
// If the dir does not exist at client, create it now.
|
|
// Make sure it has the right ACL, attributes, etc.
|
|
//
|
|
|
|
NetStatus = ReplCopyDirectoryItself(
|
|
master_path, // src
|
|
client_path, // dest
|
|
FALSE ); // don't fail if already exists.
|
|
if (NetStatus != NO_ERROR) {
|
|
|
|
AlertLogExit( ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
NetStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
}
|
|
|
|
Client_Attributes = FILE_ATTRIBUTE_DIRECTORY;
|
|
}
|
|
|
|
//
|
|
// If the name exists at client as a file - no replication takes place.
|
|
//
|
|
|
|
if ( (Client_Attributes & FILE_ATTRIBUTE_DIRECTORY) == 0 ) {
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
}
|
|
|
|
//
|
|
// Get Attributes at master
|
|
//
|
|
|
|
Master_Attributes = GetFileAttributes( master_path );
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: attr for '" FORMAT_LPTSTR "' is "
|
|
FORMAT_HEX_DWORD ".\n", master_path, Master_Attributes ));
|
|
}
|
|
|
|
if ( Master_Attributes == (DWORD) -1 ) {
|
|
|
|
NetStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( NetStatus != NO_ERROR );
|
|
NetpAssert( NetStatus != ERROR_INVALID_FUNCTION );
|
|
|
|
//
|
|
// If the export path cannot be found,
|
|
// alert someone.
|
|
//
|
|
|
|
if (NetStatus == ERROR_PATH_NOT_FOUND) {
|
|
|
|
AlertLogExit( ALERT_ReplBadExport,
|
|
NELOG_ReplBadExport,
|
|
0,
|
|
master_path,
|
|
NULL,
|
|
NO_EXIT);
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
|
|
} else if ((NetStatus == ERROR_ACCESS_DENIED)
|
|
|| (NetStatus == ERROR_NETWORK_ACCESS_DENIED)) {
|
|
|
|
if ((cur_rec->alerts & ACCESS_DENIED_ALERT) == 0) {
|
|
AlertLogExit( ALERT_ReplAccessDenied,
|
|
NELOG_ReplAccessDenied,
|
|
NetStatus,
|
|
master_path,
|
|
cur_rec->master,
|
|
NO_EXIT);
|
|
cur_rec->alerts |= ACCESS_DENIED_ALERT;
|
|
}
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
|
|
} else {
|
|
|
|
AlertLogExit( ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
NetStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the user has locked us from replication,
|
|
// don't sync now.
|
|
//
|
|
|
|
if (cur_rec->lockcount > 0) {
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: Tree Integrity setting no sync"
|
|
" (locked)\n" ));
|
|
}
|
|
|
|
// Note: ReplSetSignalFile needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Is it our true master ?
|
|
//
|
|
|
|
if (ReplNetNameCompare( NULL,
|
|
cur_rec->master,
|
|
master_info->header.sender,
|
|
NAMETYPE_COMPUTER,
|
|
0L ) == 0) {
|
|
|
|
//
|
|
// Is there a more recent message for this directory?
|
|
//
|
|
|
|
if ( ReplScanQueuesForMoreRecentMsg( dir ) ) {
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: skipping message for '" FORMAT_LPTSTR
|
|
"' in favor of more recent message.\n", dir ));
|
|
}
|
|
goto Cleanup; // don't forget to unlock...
|
|
}
|
|
|
|
//
|
|
// Always update integrity and extent - in case
|
|
// changed at master. Same goes for the timing variables.
|
|
//
|
|
|
|
cur_rec->integrity = status_rec->integrity;
|
|
cur_rec->extent = status_rec->extent;
|
|
cur_rec->pulse_time = master_info->info.pulse_rate * 60;
|
|
cur_rec->guard_time = master_info->info.guard_time * 60;
|
|
cur_rec->rand_time = master_info->info.random;
|
|
|
|
//
|
|
// If client dir attributes are not the same as the master's
|
|
// assign the master attributes to the client
|
|
//
|
|
|
|
if ( Client_Attributes != Master_Attributes ) {
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Setting client's attributes (first level) to "
|
|
FORMAT_HEX_DWORD " for '" FORMAT_LPTSTR "'.\n",
|
|
Master_Attributes, client_path ));
|
|
}
|
|
|
|
if ( !SetFileAttributes(
|
|
(LPTSTR) client_path,
|
|
Master_Attributes ) ) {
|
|
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: unexpected ret code from "
|
|
"SetFileAttributes(" FORMAT_LPTSTR
|
|
", " FORMAT_HEX_DWORD
|
|
"): " FORMAT_API_STATUS ".\n",
|
|
client_path, Master_Attributes, ApiStatus ));
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
goto Cleanup; // Don't forget to unlock stuff.
|
|
}
|
|
}
|
|
|
|
|
|
if ( !SyncNeeded ) { // not first time we've seen this dir...
|
|
|
|
if ((cur_rec->checksum != status_rec->checksum)
|
|
|| (((DWORD)cur_rec->count) != status_rec->count)) {
|
|
|
|
//
|
|
// Walk import tree and compute. Could be first time, in
|
|
// which case we just have old checksum.
|
|
//
|
|
if ( !ChecksumEqual(
|
|
UncMaster, client_path, status_rec, &ck_rec) ) {
|
|
|
|
//
|
|
// Yes, the checksum changed on master, so sync it.
|
|
//
|
|
SyncNeeded = TRUE; // must do sync and checksum again.
|
|
|
|
} else {
|
|
|
|
//
|
|
// Was probably just first time. Update checksum in
|
|
// client rec. Also update timestamp, so we don't keep
|
|
// recomputing this checksum.
|
|
//
|
|
cur_rec->checksum = ck_rec.checksum;
|
|
cur_rec->count = ck_rec.count;
|
|
cur_rec->timestamp = NetpReplTimeNow();
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ( !SyncNeeded ) {
|
|
|
|
//
|
|
// Have we gotten a change notify, which might mean we just need to
|
|
// do another checksum?
|
|
//
|
|
if (RCGlobalTimeOfLastChangeNotify >= (cur_rec->timestamp)) {
|
|
|
|
//
|
|
// Got change notify since last checksum.
|
|
//
|
|
if ( !ChecksumEqual(
|
|
UncMaster, client_path, status_rec, &ck_rec) ) {
|
|
|
|
SyncNeeded = TRUE; // must do sync and checksum again.
|
|
|
|
} else {
|
|
|
|
//
|
|
// There was a change notify but the checksum still
|
|
// matches. Must have been another tree that changed.
|
|
// Update timestamp in client rec, so we don't keep
|
|
// recomputing this checksum.
|
|
//
|
|
cur_rec->timestamp = NetpReplTimeNow();
|
|
|
|
}
|
|
} else if ( NetpReplTimeNow()
|
|
< ((cur_rec->timestamp) - BACKWARDS_TIME_CHNG_ALLOWED_SECS)
|
|
) {
|
|
|
|
//
|
|
// Time went backwards, perhaps a month. Can't trust time of
|
|
// last change notify or timestamp in record.
|
|
//
|
|
|
|
if ( !ChecksumEqual(
|
|
UncMaster, client_path, status_rec, &ck_rec) ) {
|
|
|
|
SyncNeeded = TRUE;
|
|
} else {
|
|
cur_rec->timestamp = NetpReplTimeNow();
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// if out of Sync ?
|
|
//
|
|
|
|
if (SyncNeeded) {
|
|
|
|
//
|
|
// This sync may take a while, and somebody may look at status
|
|
// during the sync, so set status to no sync in the meantime.
|
|
//
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: setting no sync"
|
|
" (about to begin update)\n" ));
|
|
}
|
|
|
|
// Note: ReplSetSignalFile needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
|
|
|
|
|
|
//
|
|
// If we can logon, sync the tree.
|
|
//
|
|
|
|
// Note: ReplLogon needs lock (any kind) on RCGlobalClientListLock.
|
|
if ((ReplLogon(cur_rec)) == NO_ERROR) {
|
|
|
|
//
|
|
// S Y N C T H E T R E E (at last!)
|
|
//
|
|
// This will sync the specified directory, do another checksum,
|
|
// and update state and checksum in client record.
|
|
//
|
|
// Note: ReplSyncTree needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplSyncTree(status_rec, cur_rec);
|
|
}
|
|
|
|
} else {
|
|
|
|
BOOL ExportLocked;
|
|
|
|
//
|
|
// See if exporter is locked; set no sync if it is.
|
|
//
|
|
ApiStatus = ReplCheckExportLocks(
|
|
UncMaster,
|
|
(LPCTSTR) dir,
|
|
&ExportLocked );
|
|
|
|
if ( ( !ExportLocked ) && (ApiStatus == NO_ERROR) ) {
|
|
//
|
|
// tree in sync so update signal file.
|
|
//
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
if (SyncNeeded) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: setting in sync (sync anyway)\n" ));
|
|
} else {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: setting in sync (not sync anyway)\n"
|
|
));
|
|
}
|
|
}
|
|
// Note: ReplSetSignalFile needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplSetSignalFile(cur_rec, REPL_STATE_OK);
|
|
} else {
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplDoUpdate: setting NO sync "
|
|
"(exporter locked or down)\n" ));
|
|
}
|
|
// Note: ReplSetSignalFile needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
|
|
}
|
|
}
|
|
|
|
// Note: ReplSetTimeOut needs shared RCGlobalClientListLock.
|
|
ReplSetTimeOut(PULSE_1_TIMEOUT, cur_rec); // reset timeout anyway.
|
|
|
|
//
|
|
// If this pulser is not our current master,
|
|
// handle it.
|
|
//
|
|
|
|
} else {
|
|
|
|
// Note: ReplMultiMaster needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplMultiMaster( DUPL_MASTER_UPDATE,
|
|
cur_rec,
|
|
master_info->header.sender);
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (ListLocked) {
|
|
RELEASE_LOCK( RCGlobalClientListLock );
|
|
}
|
|
if (PermissionEnabled) {
|
|
(VOID) ReplDisableBackupPermission();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
DBGSTATIC VOID
|
|
ReplGuardCheck(
|
|
IN PCLIENT_LIST_REC tree_rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called after GUARD_TIMEOUT - tries to sync the tree again. Uses
|
|
ReplSyncTree.
|
|
|
|
Fakes a MSG_STATUS_REC record, so ReplSyncTree
|
|
can't tell the difference. The Guard checksum and count appear as the
|
|
status_rec, i.e. as though it is part of an update message.
|
|
|
|
Assumes that caller has lock (any kind) on RCGlobalClientListLock.
|
|
|
|
Arguments:
|
|
|
|
tree_rec - The client record for the directory to check.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
MSG_STATUS_REC status_rec;
|
|
|
|
|
|
//
|
|
// Setup the Faked status_rec.
|
|
//
|
|
|
|
status_rec.opcode = GUARD;
|
|
status_rec.checksum = tree_rec->timer.grd_checksum;
|
|
status_rec.count = tree_rec->timer.grd_count;
|
|
status_rec.integrity = tree_rec->integrity;
|
|
status_rec.extent = tree_rec->extent;
|
|
|
|
//
|
|
// Leave guard status vars on (i.e grd_count != -1 so ReplSyncTree knows
|
|
// this is a guard check, and does not setup another guard timeout
|
|
//
|
|
|
|
//
|
|
// This sync may take a while, and somebody may look at status
|
|
// during the sync, so set status to no sync in the meantime.
|
|
//
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplGuardCheck: setting no sync"
|
|
" (about to begin update)\n" ));
|
|
}
|
|
|
|
// Note: ReplSetSignalFile needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplSetSignalFile(tree_rec, REPL_STATE_NO_SYNC);
|
|
|
|
|
|
//
|
|
// If we can logon, sync the tree.
|
|
//
|
|
|
|
// Note: ReplLogon needs lock (any kind) on RCGlobalClientListLock.
|
|
if ((ReplLogon(tree_rec)) == NO_ERROR) {
|
|
|
|
// Note: ReplSyncTree needs lock (any kind) on RCGlobalClientListLock.
|
|
ReplSyncTree((PMSG_STATUS_REC ) & status_rec, tree_rec);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBGSTATIC DWORD
|
|
ReplTimeSince(
|
|
DWORD check_time
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the time difference between check_time and current time, in seconds.
|
|
|
|
Arguments:
|
|
|
|
check_time - The time to compare against (in seconds since Jan 1, 1970).
|
|
|
|
Return Value:
|
|
|
|
Returns the number of seconds that have elapsed since check_time.
|
|
|
|
--*/
|
|
{
|
|
DWORD tmp;
|
|
|
|
tmp = NetpReplTimeNow();
|
|
if (tmp > check_time) {
|
|
return (tmp - check_time);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
ReplSyncerThread(
|
|
LPVOID Parameter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Spawned by Client process, takes care of all replication client's timing.
|
|
|
|
BUGBUG Comment is bad.
|
|
|
|
Arguments:
|
|
|
|
Parameter - Not used.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
DWORD MessageType;
|
|
PSYNCMSG sync_p;
|
|
PQUERY_MSG query_p;
|
|
PMSG_STATUS_REC status_p;
|
|
PCLIENT_LIST_REC cur_rec;
|
|
PBIGBUF_REC que_buf;
|
|
|
|
UNREFERENCED_PARAMETER( Parameter );
|
|
|
|
IF_DEBUG( REPL ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplSyncerThread: thread ID is "
|
|
FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
|
|
}
|
|
|
|
//
|
|
// Init backup and restore privs so we can copy ACLs.
|
|
// Don't enable them yet, though.
|
|
//
|
|
ApiStatus = ReplInitBackupPermission();
|
|
if (ApiStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplSyncerThread: FAILED init backup permission, status "
|
|
FORMAT_API_STATUS ".\n", ApiStatus ));
|
|
|
|
ReplErrorLog(
|
|
NULL, // no server name (local)
|
|
NELOG_ReplSysErr, // error log code
|
|
ApiStatus, // status to log
|
|
NULL, // no optional str1
|
|
NULL ); // no optional str2
|
|
|
|
// Continue, maybe we can still get some work done.
|
|
}
|
|
|
|
//
|
|
// Loop forever taking entries off the Work Queue and doing them.
|
|
//
|
|
|
|
for (;;) {
|
|
BOOL ClientTerminating;
|
|
|
|
//
|
|
// Get the next message from the queue.
|
|
//
|
|
// Also, awaken if the repl service is to be terminated.
|
|
//
|
|
|
|
que_buf = ReplGetWorkQueue( &ClientTerminating );
|
|
|
|
if ( ClientTerminating ) {
|
|
IF_DEBUG(SYNC) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplSyncerThread: TERMINATING...\n" ));
|
|
}
|
|
break; // exit.
|
|
}
|
|
|
|
if ( que_buf == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
query_p = (PQUERY_MSG ) (que_buf->data);
|
|
MessageType = query_p->header.msg_type;
|
|
|
|
#if DBG
|
|
{
|
|
if (MessageType < NEXT_AVAILABLE_MESSAGE_NUMBER) {
|
|
RCGlobalSyncReceived[ MessageType ] ++;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
IF_DEBUG(SYNC) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"worker queue received a message "
|
|
"(type = " FORMAT_DWORD ").\n", MessageType ));
|
|
|
|
}
|
|
|
|
switch (MessageType) {
|
|
|
|
case DIR_SUPPORTED: /*FALLTHROUGH*/
|
|
case DIR_NOT_SUPPORTED: /*FALLTHROUGH*/
|
|
case MASTER_DIR: /*FALLTHROUGH*/
|
|
case NOT_MASTER_DIR:
|
|
|
|
//
|
|
// Handshake messages are now handled in client thread!
|
|
//
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplSyncerThread got handshake message in queue!\n" ));
|
|
NetpAssert( FALSE );
|
|
|
|
ReplClientFreePoolEntry( QUEBIG_POOL, que_buf);
|
|
|
|
break;
|
|
|
|
|
|
//
|
|
// Handle a PULSE_MSG from the master - check if we have all synced.
|
|
//
|
|
|
|
case PULSE_MSG:
|
|
|
|
sync_p = (PSYNCMSG) (LPVOID) query_p;
|
|
status_p = (PMSG_STATUS_REC) (LPVOID) (sync_p + 1);
|
|
|
|
//
|
|
// Loop for each directory in this pulse message.
|
|
//
|
|
|
|
while ((sync_p->update_count)--) {
|
|
LPTSTR dir_name;
|
|
|
|
dir_name = (LPTSTR)
|
|
(((PCHAR)sync_p) + status_p->dir_name_offset);
|
|
|
|
ReplDoUpdate(dir_name, sync_p, status_p);
|
|
|
|
++status_p; // increment status pointer.
|
|
}
|
|
|
|
|
|
//
|
|
// Log off from this master.
|
|
//
|
|
|
|
ReplLogoff();
|
|
|
|
|
|
ReplClientFreePoolEntry( QUEBIG_POOL, que_buf);
|
|
break;
|
|
|
|
|
|
//
|
|
// Handle a SYNC_MSG or GUARD_MSG from the master.
|
|
//
|
|
|
|
case SYNC_MSG:
|
|
case GUARD_MSG:
|
|
|
|
sync_p = (PSYNCMSG) (LPVOID) query_p;
|
|
status_p = (PMSG_STATUS_REC) (LPVOID) (sync_p + 1);
|
|
|
|
//
|
|
// Loop for each directory in the message.
|
|
//
|
|
|
|
while ((sync_p->update_count)--) {
|
|
LPTSTR dir_name;
|
|
dir_name = (LPTSTR)
|
|
(((PCHAR)sync_p) + status_p->dir_name_offset);
|
|
|
|
switch (status_p->opcode) {
|
|
|
|
case START:
|
|
case UPDATE:
|
|
|
|
ReplDoUpdate(dir_name, sync_p, status_p);
|
|
break;
|
|
|
|
//
|
|
// If the master is asking us to stop syncing this directory,
|
|
// do so.
|
|
//
|
|
|
|
case END:
|
|
|
|
//
|
|
// If we still support this directory from this master,
|
|
// set signal to NO_MASTER and remove dir from client_list.
|
|
//
|
|
|
|
ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
|
|
if ((cur_rec = ReplGetClientRec(
|
|
dir_name,
|
|
sync_p->header.sender )) != NULL) {
|
|
|
|
// Note: ReplMasterDead needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplMasterDead(cur_rec);
|
|
}
|
|
RELEASE_LOCK( RCGlobalClientListLock );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
++status_p; // increment status pointer.
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Log off from this master.
|
|
//
|
|
|
|
ReplLogoff();
|
|
|
|
ReplClientFreePoolEntry( QUEBIG_POOL, que_buf);
|
|
break;
|
|
|
|
|
|
|
|
//
|
|
// Handle GUARD_TIMEOUT:
|
|
//
|
|
// A directory changed while we were syncing. Since then, the
|
|
// guard time on the directory has elapsed, so we will try again.
|
|
//
|
|
|
|
case GUARD_TIMEOUT:
|
|
|
|
//
|
|
// make sure it wasn't deleted while guarding.
|
|
//
|
|
|
|
|
|
//
|
|
// If we still support this directory from this master,
|
|
// try to sync again.
|
|
//
|
|
|
|
ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
|
|
if ((cur_rec = ReplGetClientRec(query_p->dir_name,
|
|
query_p->header.sender )) != NULL) {
|
|
|
|
//
|
|
// make sure no update was received while this timeout was
|
|
// on it's way
|
|
//
|
|
|
|
if (ReplTimeSince(cur_rec->timestamp) > cur_rec->guard_time) {
|
|
|
|
// Note: ReplGuardCheck needs any RCGlobalClientListLock.
|
|
ReplGuardCheck(cur_rec);
|
|
|
|
}
|
|
}
|
|
RELEASE_LOCK( RCGlobalClientListLock );
|
|
|
|
ReplClientFreePoolEntry( QUESML_POOL, que_buf);
|
|
break;
|
|
|
|
|
|
//
|
|
// We asked our master to send us a MASTER_DIR or a NOT_MASTER_DIR.
|
|
// Our master did not respond.
|
|
//
|
|
|
|
case DUPL_TIMEOUT:
|
|
|
|
ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
|
|
if ((cur_rec = ReplGetClientRec(query_p->dir_name, NULL )) != NULL) {
|
|
|
|
//
|
|
// the timeout sender field is used to store the dupl_master.
|
|
//
|
|
|
|
// Note: ReplMultiMaster needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplMultiMaster( DUPL_MASTER_TIMEOUT,
|
|
cur_rec,
|
|
query_p->header.sender);
|
|
}
|
|
RELEASE_LOCK( RCGlobalClientListLock );
|
|
|
|
ReplClientFreePoolEntry( QUESML_POOL, que_buf);
|
|
break;
|
|
|
|
|
|
|
|
//
|
|
// Handle when our master has not sent us a pulse in
|
|
// pulse time + 1 minute.
|
|
//
|
|
|
|
case PULSE_1_TIMEOUT:
|
|
|
|
|
|
//
|
|
// If we still support this directory from this master,
|
|
// indicate that we have no sync signal from the master.
|
|
// prepare to timeout again.
|
|
//
|
|
|
|
ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
|
|
if ((cur_rec = ReplGetClientRec(query_p->dir_name,
|
|
query_p->header.sender )) != NULL) {
|
|
|
|
//
|
|
// make sure no update was received while this timeout was
|
|
// on it's way
|
|
//
|
|
|
|
if (ReplTimeSince(cur_rec->timestamp) >=
|
|
(cur_rec->pulse_time + ONE_MINUTE)) {
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplSyncerThread: setting no sync"
|
|
" (pulse 1 timeout)\n" ));
|
|
}
|
|
|
|
// Note: ReplSetSignalFile needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplSetSignalFile(cur_rec, REPL_STATE_NO_SYNC);
|
|
|
|
// Note: ReplSetTimeOut needs shared RCGlobalClientListLock.
|
|
ReplSetTimeOut(PULSE_2_TIMEOUT, cur_rec);
|
|
}
|
|
}
|
|
RELEASE_LOCK( RCGlobalClientListLock );
|
|
|
|
ReplClientFreePoolEntry( QUESML_POOL, que_buf);
|
|
break;
|
|
|
|
|
|
//
|
|
// Handle when our master has not sent us a pulse in
|
|
// two times the pulse time + one minute.
|
|
//
|
|
|
|
case PULSE_2_TIMEOUT:
|
|
|
|
//
|
|
// If we still support this directory from this master,
|
|
// ask the master if it still supports this directory.
|
|
//
|
|
|
|
ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
|
|
if ((cur_rec = ReplGetClientRec(query_p->dir_name,
|
|
query_p->header.sender )) != NULL) {
|
|
|
|
//
|
|
// make sure tree is in right state.
|
|
//
|
|
|
|
if (cur_rec->state == REPL_STATE_NO_SYNC) {
|
|
|
|
ReplClientSendMessage( IS_DIR_SUPPORTED,
|
|
cur_rec->master,
|
|
cur_rec->dir_name);
|
|
|
|
// Note: ReplSetTimeOut needs shared RCGlobalClientListLock.
|
|
ReplSetTimeOut(PULSE_3_TIMEOUT, cur_rec);
|
|
}
|
|
}
|
|
RELEASE_LOCK( RCGlobalClientListLock );
|
|
|
|
ReplClientFreePoolEntry( QUESML_POOL, que_buf);
|
|
break;
|
|
|
|
|
|
//
|
|
// Handle when our master has not sent us a pulse in
|
|
// three times the pulse time + one minute.
|
|
//
|
|
// The master should have at least replied to our IS_DIRSUPPORTED query.
|
|
//
|
|
|
|
case PULSE_3_TIMEOUT:
|
|
|
|
//
|
|
// If we still support this directory from this master,
|
|
// alert that the master is really dead.
|
|
//
|
|
|
|
ACQUIRE_LOCK_SHARED( RCGlobalClientListLock );
|
|
if ((cur_rec = ReplGetClientRec(query_p->dir_name,
|
|
query_p->header.sender )) != NULL) {
|
|
|
|
//
|
|
// make sure tree is in right state.
|
|
//
|
|
|
|
if (cur_rec->state == REPL_STATE_NO_SYNC) {
|
|
|
|
AlertLogExit(ALERT_ReplLostMaster,
|
|
NELOG_ReplLostMaster,
|
|
0,
|
|
cur_rec->master,
|
|
cur_rec->dir_name,
|
|
NO_EXIT);
|
|
|
|
|
|
//
|
|
// Set signal to NO_MASTER and remove dir from client_list.
|
|
//
|
|
|
|
// Note: ReplMasterDead needs lock (any kind) on
|
|
// RCGlobalClientListLock.
|
|
ReplMasterDead(cur_rec);
|
|
}
|
|
}
|
|
RELEASE_LOCK( RCGlobalClientListLock );
|
|
|
|
ReplClientFreePoolEntry( QUESML_POOL, que_buf);
|
|
break;
|
|
|
|
default:
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplSyncerThread: UNEXPECTED MESSAGE TYPE IN QUEUE\n" ));
|
|
|
|
NetpAssert( FALSE );
|
|
|
|
ReplClientFreePoolEntry( QUEBIG_POOL, que_buf);
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Exit the syncer thread.
|
|
//
|
|
|
|
IF_DEBUG( REPL ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplSyncerThread: exiting thread "
|
|
FORMAT_NET_THREAD_ID ".\n", NetpCurrentThread() ));
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
ReplSetTimeOut(
|
|
IN DWORD timeout_type,
|
|
IN PCLIENT_LIST_REC tree_rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set up a timeout for this particular directory.
|
|
|
|
Note: all delays are in units of 10 seconds, rounded up.
|
|
|
|
Assumes that caller has shared lock on RCGlobalClientListLock.
|
|
|
|
Arguments:
|
|
|
|
timeout_type - type of timeout.
|
|
|
|
tree_rec - The client tree record for this directory.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
Threads:
|
|
|
|
Called by client and syncer threads.
|
|
|
|
--*/
|
|
{
|
|
|
|
switch (timeout_type) {
|
|
case PULSE_1_TIMEOUT:
|
|
|
|
tree_rec->timer.timeout = (tree_rec->pulse_time + ONE_MINUTE) / 10 + 1;
|
|
tree_rec->timer.type = PULSE_1_TIMEOUT;
|
|
break;
|
|
|
|
case PULSE_2_TIMEOUT:
|
|
|
|
tree_rec->timer.timeout = tree_rec->pulse_time / 10 + 1;
|
|
tree_rec->timer.type = PULSE_2_TIMEOUT;
|
|
break;
|
|
|
|
case PULSE_3_TIMEOUT:
|
|
|
|
tree_rec->timer.timeout = tree_rec->pulse_time / 10 + 1;
|
|
tree_rec->timer.type = PULSE_3_TIMEOUT;
|
|
break;
|
|
|
|
case GUARD_TIMEOUT:
|
|
|
|
tree_rec->timer.grd_timeout = tree_rec->guard_time / 10 + 1;
|
|
break;
|
|
default:
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplSetTimeOut: got INVALID timeout type!\n" ));
|
|
}
|
|
|
|
}
|