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.
1214 lines
30 KiB
1214 lines
30 KiB
/*++
|
|
|
|
Copyright (c) 1987-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
SyncMisc.c
|
|
|
|
Abstract:
|
|
|
|
Contains miscellaneous sync functions.
|
|
|
|
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:
|
|
|
|
09-May-1989 (yuv)
|
|
Initial Coding.
|
|
11-Oct-1991 (cliffv)
|
|
Ported to NT. Converted to NT style.
|
|
13-Dec-1991 JohnRo
|
|
Avoid nonstandard dollar sign in C source code.
|
|
16-Jan-1992 JohnRo
|
|
Avoid using private logon functions.
|
|
Changed file name from repl.h to replgbl.h to avoid MIDL conflict.
|
|
Changed to use NetLock.h (allow shared locks, for one thing).
|
|
Use REPL_STATE_ equates for client_list_rec.state values.
|
|
Changed _RP equates to be LPTSTR instead of LPWSTR type.
|
|
27-Jan-1992 JohnRo
|
|
Use config data instead of state files.
|
|
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.
|
|
25-Mar-1992 JohnRo
|
|
Avoid obsolete state values.
|
|
Added/corrected some lock handling.
|
|
Fixed bug in ReplSetSignalFile.
|
|
Added more debug output.
|
|
25-Mar-1992 JohnRo
|
|
Win32 CopyFile() API doesn't copy directories, dammit!
|
|
Warn about files being deleted.
|
|
26-Mar-1992 JohnRo
|
|
Fixed bug handling empty directory in ReplTreeDelete().
|
|
Added more debug output and assertion checking.
|
|
11-Aug-1992 JohnRo
|
|
RAID 3288: repl svc should preserve ACLs on copy.
|
|
Avoid compiler warnings.
|
|
Use PREFIX_ equates.
|
|
17-Aug-1992 JohnRo
|
|
RAID 3607: REPLLOCK.RP$ is being created during tree copy.
|
|
27-Oct-1992 jimkel
|
|
Changed the error message to read correctly. when file not copyied.
|
|
11-Nov-1992 JohnRo
|
|
Fix remote repl admin.
|
|
Added some debug checks.
|
|
15-Jan-1993 JohnRo
|
|
RAID 7717: Repl assert if not logged on correctly. (Also do event
|
|
logging for real.)
|
|
Made some changes suggested by PC-LINT 5.0
|
|
Added some IN and OUT keywords.
|
|
11-Mar-1993 JohnRo
|
|
RAID 14144: avoid very long hourglass in repl UI.
|
|
Make sure handle is closed in ReplTreeDelete().
|
|
26-Mar-1993 JohnRo
|
|
RAID 4267: Replicator has problems when work queue gets large.
|
|
13-Apr-1993 JohnRo
|
|
RAID 3107: locking directory over the net gives network path not found.
|
|
Lots of debug changes.
|
|
19-Apr-1993 JohnRo
|
|
RAID 829: replication giving system error 38 (ERROR_HANDLE_EOF).
|
|
26-Apr-1993 JohnRo
|
|
RAID 7313: repl needs change permission to work on NTFS,
|
|
or we need to delete files differently.
|
|
|
|
--*/
|
|
|
|
#include <windows.h> // IN, DWORD, etc.
|
|
#include <lmcons.h>
|
|
|
|
#include <alertmsg.h> // ALERT_* defines
|
|
#include <config.h> // NetpOpenConfigData(), LPNET_CONFIG_HANDLE, etc.
|
|
#include <confname.h> // SECT_ and REPL_KEYWORD_ equates.
|
|
#include <dirname.h> // ReplIsDirNameValid().
|
|
#include <impdir.h> // ImportDirWriteConfigData().
|
|
#include <lmapibuf.h> // NetApiBufferFree()
|
|
#include <lmerrlog.h> // NELOG_* defines
|
|
#include <lmrepl.h> // REPL_STATE_ equates.
|
|
#include <lmshare.h> // NetFileEnum
|
|
#include <names.h> // NetpIsUncComputerNameValid(), etc.
|
|
#include <netdebug.h> // DBGSTATIC, NetpDbgDisplayReplState(), etc.
|
|
#include <netlib.h> // NetpMemoryAllocate
|
|
#include <prefix.h> // PREFIX_ equates.
|
|
#include <tstr.h> // TCHAR_EOS, etc.
|
|
#include <winerror.h> // NO_ERROR and ERROR_ equates.
|
|
|
|
//
|
|
// Local include files
|
|
//
|
|
#include <repldefs.h> // IF_DEBUG(), etc.
|
|
#include <replgbl.h> // ReplGlobal variables.
|
|
#include <client.h>
|
|
|
|
|
|
|
|
VOID
|
|
ReplSetSignalFile(
|
|
IN OUT PCLIENT_LIST_REC tree_rec,
|
|
IN DWORD signal
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine used to write a signal file in a directory.
|
|
Nowadays, it really writes state in the registry.
|
|
|
|
Doesn't do anything if requested signal is same as current.
|
|
|
|
Assumes that caller has an lock (of any kind) on RCGlobalClientListLock.
|
|
|
|
Arguments:
|
|
|
|
tree_rec - Pointer to the directory's client list record.
|
|
|
|
signal - Indicates which file to create.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Threads:
|
|
|
|
Called by client and syncer threads.
|
|
|
|
--*/
|
|
{
|
|
NetpAssert( tree_rec != NULL );
|
|
NetpAssert( ReplIsStateValid( signal ) );
|
|
if (tree_rec->master[0] != TCHAR_EOS) {
|
|
NetpAssert( NetpIsComputerNameValid( tree_rec->master ) );
|
|
}
|
|
|
|
//
|
|
// If the state requested is same as the existing one then just return.
|
|
//
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Setting new state for " FORMAT_LPTSTR ":\n",
|
|
tree_rec->dir_name ));
|
|
NetpDbgDisplayReplState( signal );
|
|
}
|
|
|
|
if ((tree_rec->state != signal) || (signal == REPL_STATE_OK)) {
|
|
NET_API_STATUS ApiStatus;
|
|
TCHAR UncMasterBuffer[UNCLEN+1];
|
|
LPTSTR UncMaster;
|
|
|
|
NetpAssert( ReplIsDirNameValid( tree_rec->dir_name ) );
|
|
|
|
//
|
|
// Build a UNC master name if necessary.
|
|
//
|
|
if (tree_rec->master[0] != TCHAR_EOS) {
|
|
(void) STRCPY( UncMasterBuffer, SLASH_SLASH );
|
|
(void) STRCAT( UncMasterBuffer, tree_rec->master );
|
|
NetpAssert( NetpIsUncComputerNameValid( UncMasterBuffer ) );
|
|
UncMaster = &UncMasterBuffer[0];
|
|
} else {
|
|
UncMaster = NULL;
|
|
}
|
|
|
|
//
|
|
// Write config data for a single import directory.
|
|
//
|
|
|
|
tree_rec->state = signal;
|
|
|
|
ApiStatus = ImportDirWriteConfigData (
|
|
NULL, // server name
|
|
tree_rec->dir_name,
|
|
tree_rec->state,
|
|
UncMaster,
|
|
tree_rec->timestamp, // Last update time, secs since 1970.
|
|
tree_rec->lockcount,
|
|
tree_rec->time_of_first_lock); // Seconds since 1970.
|
|
|
|
if (ApiStatus != NO_ERROR) {
|
|
|
|
// Log error. Maybe we're running as wrong user...
|
|
AlertLogExit( ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
ReplSyncCopy(
|
|
IN LPTSTR source_path,
|
|
IN LPTSTR dest_path,
|
|
IN OUT PCLIENT_LIST_REC tree_rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies source_path to dest_path and handles error conditions.
|
|
|
|
Arguments:
|
|
|
|
source_path - UNC source path.
|
|
|
|
dest_path - UNC destination path.
|
|
|
|
tree_rec - pointer to CLIENT_LIST_REC for current dir.
|
|
|
|
Return Value:
|
|
|
|
Net status code of the copy.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
|
|
//
|
|
// Copy the file/tree and just return if there is no error.
|
|
//
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplSyncCopy: ***** copying file/tree '"
|
|
FORMAT_LPTSTR "' to '" FORMAT_LPTSTR "'.\n",
|
|
source_path, dest_path ));
|
|
}
|
|
|
|
ApiStatus = ReplCopyTree( source_path, dest_path );
|
|
if (ApiStatus == NO_ERROR) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// If access is denied, raise that alert.
|
|
//
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT "ReplSyncCopy: Copy failed, status="
|
|
FORMAT_API_STATUS ".\n", ApiStatus ));
|
|
|
|
if ((ApiStatus == ERROR_ACCESS_DENIED)
|
|
|| (ApiStatus == ERROR_NETWORK_ACCESS_DENIED)) {
|
|
|
|
if ((tree_rec->alerts & ACCESS_DENIED_ALERT) == 0) {
|
|
AlertLogExit( ALERT_ReplAccessDenied,
|
|
NELOG_ReplAccessDenied,
|
|
ApiStatus,
|
|
source_path,
|
|
tree_rec->master,
|
|
NO_EXIT);
|
|
tree_rec->alerts |= ACCESS_DENIED_ALERT;
|
|
}
|
|
|
|
|
|
//
|
|
// Otherwise, raise a less specific alert.
|
|
//
|
|
|
|
} else if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
|
|
AlertLogExit( ALERT_ReplUpdateError,
|
|
NELOG_ReplUpdateError,
|
|
ApiStatus,
|
|
dest_path, // changed from source_path,
|
|
tree_rec->master,
|
|
NO_EXIT);
|
|
tree_rec->alerts |= UPDATE_ERROR_ALERT;
|
|
}
|
|
|
|
return (ApiStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
ReplFileIntegrityDel(
|
|
IN LPTSTR path,
|
|
IN DWORD attrib,
|
|
IN OUT PCLIENT_LIST_REC tree_rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes the file or directory specfied by path.
|
|
|
|
Arguments:
|
|
|
|
path - UNC file or directory to delete. If a directory is specified,
|
|
the entire directory tree is deleted.
|
|
|
|
attrib - The file attributes of path.
|
|
|
|
tree_rec - Tree record for the directory containing path.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
|
|
//
|
|
// Avoid -1, which means "file not found".
|
|
//
|
|
|
|
NetpAssert( attrib != (DWORD)(-1) );
|
|
|
|
//
|
|
// If 'path' is a directory, delete the entire tree.
|
|
//
|
|
|
|
if (attrib & FILE_ATTRIBUTE_DIRECTORY) {
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplFileIntegrityDel: *** deleting dir/tree *** '"
|
|
FORMAT_LPTSTR "'.\n", path ));
|
|
}
|
|
NetStatus = ReplTreeDelete(path);
|
|
if ( NetStatus == NO_ERROR || NetStatus == ERROR_FILE_NOT_FOUND ) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// If 'path' is a file, force the file to be deleted.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Ensure the file is not read-only so we can delete it.
|
|
//
|
|
|
|
(VOID) SetFileAttributes( path, FILE_ATTRIBUTE_NORMAL );
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplFileIntegrityDel: *** deleting file *** '"
|
|
FORMAT_LPTSTR "'.\n", path ));
|
|
}
|
|
|
|
NetStatus = ReplDeleteFile( path );
|
|
if ( (NetStatus==NO_ERROR) || (NetStatus==ERROR_FILE_NOT_FOUND) ) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Log the error.
|
|
//
|
|
// No AlertLogging for sharing violation ? BUGBUG
|
|
//
|
|
|
|
if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
|
|
AlertLogExit( ALERT_ReplUpdateError,
|
|
NELOG_ReplUpdateError,
|
|
NetStatus,
|
|
path,
|
|
tree_rec->master,
|
|
NO_EXIT);
|
|
|
|
tree_rec->alerts |= UPDATE_ERROR_ALERT;
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
ReplFileIntegrityCopy(
|
|
IN LPTSTR source_path,
|
|
IN LPTSTR dest_path,
|
|
IN LPTSTR tmp_path,
|
|
IN OUT PCLIENT_LIST_REC tree_rec,
|
|
IN DWORD src_attr,
|
|
IN DWORD dest_attr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies source_path to temp_path and destination_path. Cleans up after
|
|
itself upon failure. Handles error conditions.
|
|
|
|
Arguments:
|
|
|
|
source_path - UNC source path.
|
|
|
|
dest_path - UNC destination path.
|
|
|
|
tmp_path - UNC path to IMPORT\TMPFILE.RP$.
|
|
|
|
tree_rec - pointer to CLIENT_LIST_REC for current dir.
|
|
|
|
src_attr - Attributes of the source path.
|
|
|
|
dest_attr - Attributes of the destination path.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus = NO_ERROR;
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplFileIntegrityCopy: ***** copying dir/file '"
|
|
FORMAT_LPTSTR "' to (temp) '" FORMAT_LPTSTR "'.\n",
|
|
source_path, tmp_path ));
|
|
}
|
|
|
|
//
|
|
// If we're copying a file, first copy the source to tmp_path.
|
|
//
|
|
|
|
if ((src_attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
|
NetStatus = ReplCopyFile( source_path, tmp_path, FALSE );
|
|
}
|
|
|
|
|
|
//
|
|
// Continue if we're successful so far.
|
|
//
|
|
|
|
if ( NetStatus == NO_ERROR ) {
|
|
|
|
//
|
|
// Try to delete existing file - if succeeds it must mean
|
|
// exclusive access (otherwise del would fail)
|
|
//
|
|
// if fails, error reporting was already done so may just return.
|
|
//
|
|
|
|
NetStatus = ReplFileIntegrityDel(dest_path, dest_attr, tree_rec);
|
|
if ( NetStatus != NO_ERROR ) {
|
|
(VOID) ReplFileIntegrityDel(tmp_path, src_attr, tree_rec);
|
|
return NetStatus;
|
|
}
|
|
|
|
//
|
|
// If source is a file, simply copy tmp_path to dest_path.
|
|
//
|
|
|
|
if ((src_attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplFileIntegrityCopy: ***** copying file '"
|
|
FORMAT_LPTSTR "' to file '" FORMAT_LPTSTR "'.\n",
|
|
source_path, tmp_path ));
|
|
}
|
|
|
|
NetStatus = ReplCopyFile( tmp_path, dest_path, FALSE );
|
|
if (NetStatus == NO_ERROR) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// If the source is a directory,
|
|
// create a new dir and make sure attributes are same.
|
|
//
|
|
// Errors are reported by ReplSyncCopy
|
|
//
|
|
|
|
} else {
|
|
|
|
return (ReplSyncCopy(source_path, dest_path, tree_rec));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Failure, delete any file we've created and report the error.
|
|
//
|
|
|
|
(VOID) ReplFileIntegrityDel(tmp_path, src_attr, tree_rec);
|
|
|
|
//
|
|
// Take care of error reporting.
|
|
//
|
|
// Sharing violations are simply ignored BUGBUG
|
|
//
|
|
|
|
if ((NetStatus == ERROR_ACCESS_DENIED)
|
|
|| (NetStatus == ERROR_NETWORK_ACCESS_DENIED)) {
|
|
|
|
if ((tree_rec->alerts & ACCESS_DENIED_ALERT) == 0) {
|
|
AlertLogExit( ALERT_ReplAccessDenied,
|
|
NELOG_ReplAccessDenied,
|
|
NetStatus,
|
|
source_path,
|
|
tree_rec->master,
|
|
NO_EXIT);
|
|
tree_rec->alerts |= ACCESS_DENIED_ALERT;
|
|
}
|
|
|
|
|
|
//
|
|
// Otherwise, raise a less specific alert.
|
|
//
|
|
|
|
} else if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
|
|
AlertLogExit( ALERT_ReplUpdateError,
|
|
NELOG_ReplUpdateError,
|
|
NetStatus,
|
|
dest_path, // changed from source_path,
|
|
tree_rec->master,
|
|
NO_EXIT);
|
|
tree_rec->alerts |= UPDATE_ERROR_ALERT;
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
ReplCreateTempDir(
|
|
IN LPTSTR tmp_path,
|
|
IN PCLIENT_LIST_REC tree_rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates dir TMPTREE.RP$ under client's IMPORT path.
|
|
|
|
Arguments:
|
|
|
|
tmp_path - Name of the directory to create.
|
|
|
|
tree_rec - Tree record of the directory being replicated.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
|
|
//
|
|
// Try to simply create the directory.
|
|
//
|
|
// BUGBUG Better security descriptor
|
|
//
|
|
|
|
if ( CreateDirectory( tmp_path, NULL ) ) {
|
|
return NO_ERROR;
|
|
}
|
|
NetStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( NetStatus != NO_ERROR );
|
|
|
|
//
|
|
// If dir exists - remove it and try again.
|
|
//
|
|
|
|
if ( ReplTreeDelete(tmp_path) == NO_ERROR ) {
|
|
if ( CreateDirectory( tmp_path, NULL ) ) {
|
|
return NO_ERROR;
|
|
}
|
|
NetStatus = (NET_API_STATUS) GetLastError();
|
|
}
|
|
|
|
if ((tree_rec->alerts & UPDATE_ERROR_ALERT) == 0) {
|
|
AlertLogExit( ALERT_ReplUpdateError,
|
|
NELOG_ReplUpdateError,
|
|
NetStatus,
|
|
tmp_path,
|
|
NULL,
|
|
NO_EXIT);
|
|
|
|
tree_rec->alerts |= UPDATE_ERROR_ALERT;
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
ReplCreateReplLock(
|
|
IN OUT PCLIENT_LIST_REC tree_rec
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a REPLLOCK.RP$ file in Pathname. If Succesful, checks for
|
|
presence of USERLOCKs, if present surrenders REPLLOCK and fails.
|
|
|
|
Assumes that caller has a lock (any kind) on RCGlobalClientListLock.
|
|
|
|
Arguments:
|
|
|
|
tree_rec - Tree record of the directory being replicated.
|
|
The lock count will be updated by this routine on exit.
|
|
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - everything worked.
|
|
ERROR_LOCKED - dir is already locked (e.g. by an app).
|
|
other status codes - some API failed.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus;
|
|
LPNET_CONFIG_HANDLE ConfigHandle = NULL;
|
|
|
|
NetpAssert( tree_rec != NULL );
|
|
NetpAssert( ReplIsDirNameValid( tree_rec->dir_name ) );
|
|
|
|
//
|
|
// Are there are user locks?
|
|
//
|
|
if (tree_rec->lockcount > 0) {
|
|
return (ERROR_LOCKED);
|
|
}
|
|
|
|
//
|
|
// Record our lock in client list. (We've got a lock on list.)
|
|
//
|
|
ApiStatus = ReplIncrLockFields(
|
|
& (tree_rec->lockcount),
|
|
& (tree_rec->time_of_first_lock) );
|
|
NetpAssert( ApiStatus == NO_ERROR );
|
|
NetpAssert( tree_rec->lockcount == 1 );
|
|
|
|
//
|
|
// Open the right section of the config file/whatever.
|
|
//
|
|
ApiStatus = NetpOpenConfigData(
|
|
& ConfigHandle,
|
|
NULL, // local (no server name)
|
|
(LPTSTR) SECT_NT_REPLICATOR,
|
|
FALSE); // not read-only (we'll delete later)
|
|
|
|
if (ApiStatus != NO_ERROR) {
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCreateReplLock: UNEXPECTED ERROR " FORMAT_API_STATUS
|
|
" from NetpOpenConfigData.\n", ApiStatus ));
|
|
goto Cleanup; // go log error
|
|
}
|
|
|
|
//
|
|
// Write the dir name out to registry (as CrashDir).
|
|
//
|
|
ApiStatus = NetpSetConfigValue(
|
|
ConfigHandle,
|
|
(LPTSTR) REPL_KEYWORD_CRASHDIR,
|
|
tree_rec->dir_name );
|
|
if (ApiStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCreateReplLock: UNEXPECTED ERROR " FORMAT_API_STATUS
|
|
" from NetpSetConfigValue.\n", ApiStatus ));
|
|
goto Cleanup; // go log error
|
|
}
|
|
|
|
//
|
|
// Increment number of locks in registry too.
|
|
//
|
|
ApiStatus = ImportDirLockInRegistry(
|
|
NULL, // no server name
|
|
tree_rec->dir_name );
|
|
if (ApiStatus != NO_ERROR) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCreateReplLock: UNEXPECTED ERROR " FORMAT_API_STATUS
|
|
" from ImportDirLockInRegistry.\n", ApiStatus ));
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (ConfigHandle != NULL) {
|
|
(VOID) NetpCloseConfigData( ConfigHandle );
|
|
}
|
|
if (ApiStatus != NO_ERROR) {
|
|
|
|
//
|
|
// Undo client list update.
|
|
//
|
|
NetpAssert( tree_rec->lockcount == 1 );
|
|
ApiStatus = ReplDecrLockFields(
|
|
& (tree_rec->lockcount),
|
|
& (tree_rec->time_of_first_lock),
|
|
REPL_UNLOCK_NOFORCE );
|
|
NetpAssert( tree_rec->lockcount == 0 );
|
|
|
|
}
|
|
if (ApiStatus != NO_ERROR) {
|
|
|
|
AlertLogExit(
|
|
ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
ApiStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT );
|
|
}
|
|
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplCreateReplLock: returning " FORMAT_API_STATUS ".\n",
|
|
ApiStatus ));
|
|
}
|
|
return (ApiStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
ReplAnyRemoteFilesOpen(
|
|
IN LPTSTR path
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if a remote user is accessing a file in dir we are working on.
|
|
|
|
Arguments:
|
|
|
|
path - A local path to check.
|
|
|
|
Return Value:
|
|
|
|
Returns FALSE if no files are opened, or if server not started.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
PFILE_INFO_2 FileInfo2;
|
|
DWORD EntriesRead;
|
|
DWORD TotalEntries;
|
|
BOOL ret;
|
|
LPTSTR Pathname;
|
|
|
|
#ifdef UNICODE
|
|
Pathname = path;
|
|
#else // UNICODE
|
|
|
|
Pathname = NetpAllocStrFromTStr( path );
|
|
|
|
if ( Pathname == NULL ) {
|
|
return FALSE;
|
|
}
|
|
#endif // UNICODE
|
|
|
|
|
|
//
|
|
// Ask server if any remote system is accessing any file in this tree.
|
|
//
|
|
|
|
NetStatus = NetFileEnum( NULL, // This machine
|
|
Pathname,
|
|
NULL, // No user name
|
|
2, // Level
|
|
(LPBYTE *) (LPVOID) &FileInfo2,
|
|
2*sizeof(FILE_INFO_2), // PrefMaxLen
|
|
&EntriesRead,
|
|
&TotalEntries,
|
|
NULL );
|
|
|
|
#ifndef UNICODE
|
|
NetpMemoryFree( Pathname );
|
|
#endif // UNICODE
|
|
|
|
|
|
//
|
|
// On success, just indicate whether any file were enumerated.
|
|
//
|
|
|
|
if ( NetStatus == NO_ERROR || NetStatus == ERROR_MORE_DATA ) {
|
|
if ( TotalEntries == 0 ) {
|
|
ret = FALSE;
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT "No remote files open.\n" ));
|
|
}
|
|
} else {
|
|
ret = TRUE;
|
|
IF_DEBUG( SYNC ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT "Some remote files open.\n" ));
|
|
}
|
|
}
|
|
|
|
(VOID) NetApiBufferFree( FileInfo2 );
|
|
|
|
//
|
|
// If we can't contact the server, obviously no system has files open.
|
|
//
|
|
|
|
} else {
|
|
ret = FALSE;
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Can't determine if remote files open "
|
|
FORMAT_API_STATUS ".\n", NetStatus ));
|
|
// BUGBUG: log this?
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
ReplTreeDelete(
|
|
IN LPTSTR dir
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Delete a directory tree with extreme prejudice
|
|
|
|
|
|
ReplTreeDelete deletes the directory specified by <dir>, and if
|
|
<dir> is not empty, it removes its contents. <dir> cannot
|
|
be the root directory of a device.
|
|
|
|
ReplTreeDelete calls itself recursively.
|
|
|
|
Since ReplTreeDelete fails as soon as one of the functions
|
|
which it invokes fails, a failure will leave some but
|
|
not all of the tree deleted.
|
|
|
|
|
|
Arguments:
|
|
|
|
dir - Specifies the root of the tree to be deleted (a UNC name).
|
|
This buffer is modified by ReplTreeDelete and although it is restored
|
|
to its original value by ReplTreeDelete before it returns, it
|
|
nevertheless must point to a buffer large enough to hold any file name.
|
|
|
|
Return Value:
|
|
|
|
Threads:
|
|
|
|
Called by client and syncer threads.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS ApiStatus = NO_ERROR;
|
|
HANDLE FindHandle = INVALID_HANDLE_VALUE;
|
|
WIN32_FIND_DATA FindData;
|
|
|
|
DWORD dir_index;
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplTreeDelete: *** deleting tree *** '"
|
|
FORMAT_LPTSTR "'.\n", dir ));
|
|
}
|
|
|
|
dir_index = STRLEN(dir);
|
|
(void) STRCPY(dir + dir_index++, SLASH);
|
|
(void) STRCPY(dir + dir_index, STAR_DOT_STAR);
|
|
|
|
//
|
|
// Enumerate all files/directories within the current directory
|
|
//
|
|
|
|
FindHandle = FindFirstFile( dir, &FindData );
|
|
|
|
if ( FindHandle != INVALID_HANDLE_VALUE ) {
|
|
do {
|
|
|
|
//
|
|
// Build the full pathname of the file/directory.
|
|
//
|
|
|
|
(void) STRCPY(dir + dir_index, FindData.cFileName );
|
|
|
|
//
|
|
// Recursively call ReplTreeDelete on all directories.
|
|
//
|
|
|
|
if ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
|
|
|
|
//
|
|
// make sure it's not '.' or '..'.
|
|
//
|
|
|
|
if ( STRCMP(FindData.cFileName , DOT) == 0) {
|
|
continue;
|
|
}
|
|
if ( STRCMP(FindData.cFileName , DOT_DOT) == 0) {
|
|
continue;
|
|
}
|
|
|
|
|
|
if ((ApiStatus = ReplTreeDelete(dir)) != NO_ERROR ) {
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Simply force files to be deleted.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// change the file's mode so it can be deleted.
|
|
//
|
|
|
|
if ( !SetFileAttributes( dir, FILE_ATTRIBUTE_NORMAL ) ) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
|
|
if (ApiStatus == ERROR_HANDLE_EOF) {
|
|
// BUGBUG: quiet this debug output eventually.
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplTreeDelete (after SetFileAttributes): "
|
|
"GOT HANDLE EOF!\n" ));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Remove the file.
|
|
//
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT "ReplTreeDelete: deleting "
|
|
FORMAT_LPTSTR ".\n", dir ));
|
|
}
|
|
ApiStatus = ReplDeleteFile( dir );
|
|
if (ApiStatus != NO_ERROR) {
|
|
// BUGBUG; // log and debug dump this!
|
|
NetpKdPrint(( "ReplTreeDelete: ReplDeleteFile failed"
|
|
", status " FORMAT_API_STATUS "\n", ApiStatus ));
|
|
break;
|
|
}
|
|
|
|
}
|
|
} while ( FindNextFile( FindHandle, &FindData ) );
|
|
|
|
}
|
|
|
|
// BUGBUG: log error if FindFirstFile failed.
|
|
|
|
if (FindHandle != INVALID_HANDLE_VALUE) {
|
|
(VOID) FindClose( FindHandle );
|
|
FindHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
|
|
//
|
|
// restore old directory path.
|
|
//
|
|
|
|
*(dir + --dir_index) = TCHAR_EOS;
|
|
|
|
if (ApiStatus) {
|
|
goto LogErrorAndExit;
|
|
}
|
|
|
|
//
|
|
// Allow the directory itself to be deleted
|
|
//
|
|
|
|
if ( !SetFileAttributes( dir, FILE_ATTRIBUTE_NORMAL ) ) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
goto LogErrorAndExit;
|
|
}
|
|
|
|
//
|
|
// Remove the directory itself.
|
|
//
|
|
|
|
IF_DEBUG( MAJOR ) {
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplTreeDelete: ***deleting directory*** "
|
|
FORMAT_LPTSTR ".\n", dir ));
|
|
}
|
|
if ( !RemoveDirectory( dir ) ) {
|
|
ApiStatus = (NET_API_STATUS) GetLastError();
|
|
goto LogErrorAndExit;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
|
|
LogErrorAndExit:
|
|
|
|
NetpAssert( ApiStatus != NO_ERROR );
|
|
|
|
if (ApiStatus == ERROR_HANDLE_EOF) {
|
|
// BUGBUG: quiet this debug output eventually.
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"ReplTreeDelete(at end): GOT HANDLE EOF!\n" ));
|
|
}
|
|
|
|
if (FindHandle != INVALID_HANDLE_VALUE) {
|
|
(VOID) FindClose( FindHandle );
|
|
}
|
|
|
|
ReplErrorLog(
|
|
NULL, // no server name (local)
|
|
NELOG_ReplSysErr, // log code
|
|
ApiStatus, // error code we got
|
|
NULL, // no optional str 1
|
|
NULL ); // no optional str 2
|
|
return ApiStatus;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
ReplInsertWorkQueue(
|
|
IN PBIGBUF_REC Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Inserts the specified buffer at the tail of the work queue.
|
|
|
|
Arguments:
|
|
|
|
Buffer -- Address of buffer to insert. The first four bytes of the
|
|
buffer will be overwritten.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Threads:
|
|
|
|
Only called by client and watchd threads.
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Serialize access to the global list head and tail.
|
|
//
|
|
|
|
ACQUIRE_LOCK( RCGlobalWorkQueueLock );
|
|
|
|
//
|
|
// Put the buffer at the tail of the list.
|
|
//
|
|
|
|
Buffer->next_p = NULL;
|
|
if ( RCGlobalWorkQueueHead == NULL ) {
|
|
RCGlobalWorkQueueHead = Buffer;
|
|
} else {
|
|
RCGlobalWorkQueueTail->next_p = Buffer;
|
|
}
|
|
RCGlobalWorkQueueTail = Buffer;
|
|
|
|
//
|
|
// Allow others to access the list.
|
|
//
|
|
|
|
RELEASE_LOCK( RCGlobalWorkQueueLock );
|
|
|
|
//
|
|
// Indicate that there is one more entry on the list.
|
|
//
|
|
|
|
if ( !ReleaseSemaphore( RCGlobalWorkQueueSemaphore, 1, NULL ) ) {
|
|
NET_API_STATUS NetStatus;
|
|
|
|
NetStatus = (NET_API_STATUS) GetLastError();
|
|
NetpAssert( NetStatus != NO_ERROR );
|
|
NetpAssert( NetStatus != ERROR_INVALID_FUNCTION );
|
|
|
|
AlertLogExit( ALERT_ReplSysErr,
|
|
NELOG_ReplSysErr,
|
|
NetStatus,
|
|
NULL,
|
|
NULL,
|
|
NO_EXIT);
|
|
|
|
NetpKdPrint(( PREFIX_REPL_CLIENT
|
|
"Insert Work Queue couldn't release "
|
|
"sem " FORMAT_API_STATUS "\n", NetStatus ));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PBIGBUF_REC
|
|
ReplGetWorkQueue(
|
|
OUT PBOOL ClientTerminating
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes an entry from the head of the work queue. Waits until there
|
|
is a buffer to remove or until the REPL service should terminate.
|
|
|
|
Note that the RCGlobalWorkQueueSemaphore is a counting semaphore which
|
|
is signalled as many times as there are items in the work queue. It is
|
|
important to read the semaphore every time something is removed
|
|
from the queue, otherwise the two will become out of sync.
|
|
|
|
Arguments:
|
|
|
|
ClientTerminating - set to TRUE (on output) if client is terminating;
|
|
set to FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
Returns the address of a buffer from the head of the queue.
|
|
Return NULL if awoken and there are no entries at the head of the queue.
|
|
|
|
Threads:
|
|
|
|
Only called by syncer thread.
|
|
|
|
--*/
|
|
{
|
|
PBIGBUF_REC Buffer;
|
|
HANDLE WaitHandles[2];
|
|
DWORD WaitStatus;
|
|
|
|
//
|
|
// Wait for an entry to be place into the work queue.
|
|
// (Or for the REPL service to be terminated).
|
|
//
|
|
|
|
WaitHandles[0] = ReplGlobalClientTerminateEvent;
|
|
WaitHandles[1] = RCGlobalWorkQueueSemaphore;
|
|
|
|
WaitStatus = WaitForMultipleObjects( 2, WaitHandles, FALSE, (DWORD) -1 );
|
|
if (WaitStatus == 0) {
|
|
*ClientTerminating = TRUE;
|
|
return (NULL);
|
|
} else {
|
|
*ClientTerminating = FALSE;
|
|
}
|
|
|
|
//
|
|
// Remove an entry from the queue if there is one.
|
|
//
|
|
|
|
ACQUIRE_LOCK( RCGlobalWorkQueueLock );
|
|
|
|
Buffer = RCGlobalWorkQueueHead;
|
|
|
|
if ( Buffer != NULL ) {
|
|
RCGlobalWorkQueueHead = Buffer->next_p;
|
|
if ( RCGlobalWorkQueueTail == Buffer ) {
|
|
RCGlobalWorkQueueTail = NULL;
|
|
}
|
|
}
|
|
|
|
RELEASE_LOCK( RCGlobalWorkQueueLock );
|
|
|
|
return Buffer;
|
|
|
|
}
|