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.
6200 lines
166 KiB
6200 lines
166 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
reint.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the fill and merge functions necessary for bot inward and outward
|
|
synchronization of files and directories. It also contains generic database tree-traversal
|
|
code. This is used by merge as well as DoLocalRename API.
|
|
|
|
|
|
Tree Traversal:
|
|
|
|
TraverseOneDirectory // traverses a subtree in the database
|
|
|
|
Fill Functions:
|
|
|
|
AttemptCacheFill Fills the entire cache or a particular share
|
|
DoSparseFill Fills a particular file
|
|
|
|
Merge Functions:
|
|
|
|
ReintOneShare // reintegrate one share
|
|
ReintAllShares // reintegrate all shares
|
|
|
|
|
|
|
|
Author:
|
|
Trent-Gray-Donald/Shishir Pardikar
|
|
|
|
|
|
Environment:
|
|
|
|
Win32 (user-mode) DLL
|
|
|
|
Revision History:
|
|
|
|
1-1-94 original
|
|
4-4-97 removed all the entry points to exports.c
|
|
reint.c
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#pragma hdrstop
|
|
#define UNICODE
|
|
|
|
#ifndef DEBUG
|
|
#undef VERBOSE
|
|
#else
|
|
#undef VERBOSE
|
|
#define VERBOSE 1
|
|
#endif
|
|
|
|
#include <winioctl.h>
|
|
|
|
#include "lib3.h"
|
|
#include "shdsys.h"
|
|
#include "reint.h"
|
|
#include "utils.h"
|
|
#include "resource.h"
|
|
#include "traynoti.h"
|
|
#include <dbt.h>
|
|
#include "strings.h"
|
|
|
|
#include "csc_bmpu.h"
|
|
|
|
// this sets flags in a couple of headers to not include some defs.
|
|
#define REINT
|
|
#include "list.h"
|
|
#include "merge.h"
|
|
#include "recact.h"
|
|
#include "recon.h" // reconnect shares dialog
|
|
#include "reint.h"
|
|
#include "aclapi.h"
|
|
|
|
//
|
|
// Defines
|
|
//
|
|
|
|
#define chNull '\0' // terminator
|
|
|
|
#define BUFF_SIZE 64
|
|
|
|
|
|
#define MAX_WAIT_PERIODS 1
|
|
#define MAX_ATTEMPTS 1
|
|
|
|
#define MAX_ATTEMPTS_SHARE 1
|
|
#define MAX_ATTEMPTS_SHADOW 5
|
|
#define MAX_SPARSE_FILL_RETRIES 4
|
|
|
|
|
|
|
|
#define STATE_REINT_BEGIN 0
|
|
|
|
#define STATE_REINT_CREATE_DIRS STATE_REINT_BEGIN
|
|
#define STATE_REINT_FILES (STATE_REINT_CREATE_DIRS+1)
|
|
#define STATE_REINT_END (STATE_REINT_FILES+1)
|
|
|
|
#define MAX_EXCLUSION_STRING 1024
|
|
|
|
|
|
// for some Registry queries, this is the max len buffer that I want back
|
|
#define MAX_NAME_LEN 100
|
|
|
|
#define MY_SZ_TRUE _TEXT("true")
|
|
#define MY_SZ_FALSE _TEXT("false")
|
|
#define SLOWLINK_SPEED 400 // in 100 bitspersec units
|
|
// NB!!! these defines cannot be changed they are in the order in which
|
|
// reintegration must proceed
|
|
|
|
#define REINT_DELETE_FILES 0
|
|
#define REINT_DELETE_DIRS 1
|
|
#define REINT_CREATE_UPDATE_DIRS 2
|
|
#define REINT_CREATE_UPDATE_FILES 3
|
|
|
|
|
|
typedef struct tagREINT_INFO {
|
|
|
|
int nCurrentState; // 0->deleting files, 1->deleting directories, 2->creating directoires
|
|
// 3 ->creating files
|
|
node *lpnodeInsertList; // reint errors
|
|
|
|
LPCSCPROC lpfnMergeProgress;
|
|
|
|
DWORD_PTR dwContext;
|
|
DWORD dwFileSystemFlags; // From GetVolumeInformation, used to set ACCLs etc. if the
|
|
// share is hosted on NTFS
|
|
HSHARE hShare; // handle to the share being reintegrated
|
|
ULONG ulPrincipalID;
|
|
_TCHAR tzDrive[4]; // mapped drive to the remote path
|
|
|
|
} REINT_INFO, *LPREINT_INFO;
|
|
|
|
|
|
#define SPARSEFILL_SLEEP_TIME_FOR_WIN95 2000 // two seconds
|
|
#define MAX_SPARSEFILL_SLEEP_TIME_FOR_WIN95 2 * 60 * 100 // two minutes
|
|
|
|
|
|
#define DFS_ROOT_FILE_SYSTEM_FLAGS 0xffffffff
|
|
|
|
//
|
|
// Variables Global/Local
|
|
//
|
|
|
|
_TCHAR *vlpExclusionList = NULL;
|
|
REINT_INFO vsRei; // global reint structure. NTRAID-455269-shishirp-1/31/2000 we should make this into a list
|
|
// in order to allow multiple reintegrations on various shares
|
|
|
|
|
|
unsigned long ulMinSparseFillPri = MIN_SPARSEFILL_PRI;
|
|
int cntDelay=0;
|
|
HANDLE vhShadow=NULL;
|
|
BOOL vfTimerON = FALSE, vfAutoDeleteOrphans=TRUE;
|
|
char vrgchBuff[1024], vrwBuff[4096], vrgchSrcName[350], vrgchDstName[300];
|
|
unsigned vcntDirty=0, vcntStale=0, vcntSparse=0, vcntWaitDirty=0;
|
|
LPFAILINFO lpheadFI = NULL;
|
|
HCURSOR vhcursor=NULL;
|
|
|
|
BOOL vfLogCopying=TRUE;
|
|
|
|
LPCONNECTINFO vlpLogonConnectList = NULL;
|
|
|
|
BOOL vfNeedPQTraversal = TRUE;
|
|
DWORD vdwSparseStaleDetectionCounter = 0;
|
|
|
|
|
|
_TCHAR vrgchCRLF[] = _TEXT("\r\n");
|
|
_TCHAR tzStarDotStar[] = _TEXT("*");
|
|
#pragma data_seg(DATASEG_READONLY)
|
|
|
|
ERRMSG rgErrorTab[] =
|
|
{
|
|
ERROR_CREATE_CONFLICT, IDS_CREATE_CONFLICT
|
|
|
|
,ERROR_DELETE_CONFLICT, IDS_DELETE_CONFLICT
|
|
|
|
,ERROR_UPDATE_CONFLICT, IDS_UPDATE_CONFLICT
|
|
|
|
,ERROR_ATTRIBUTE_CONFLICT, IDS_ATTRIBUTE_CONFLICT
|
|
|
|
};
|
|
|
|
|
|
static const _TCHAR vszMachineName[]= _TEXT("System\\CurrentControlSet\\Control\\ComputerName\\ComputerName");
|
|
static const _TCHAR vszComputerName[]=_TEXT("ComputerName");
|
|
static const _TCHAR vszLogUNCPath[]=_TEXT("\\\\scratch\\scratch\\t-trentg\\logs\\");
|
|
static const _TCHAR vszLogShare[]=_TEXT("\\\\scratch\\scratch");
|
|
static const _TCHAR vszLocalLogPath[]=_TEXT("c:\\shadow.log");
|
|
static const _TCHAR vszConflictDir[]=_TEXT("C:\\ConflictsWhileMerging");
|
|
|
|
#pragma data_seg()
|
|
|
|
AssertData;
|
|
AssertError;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Local prototypes
|
|
//
|
|
|
|
|
|
|
|
BOOL
|
|
CheckForStalenessAndRefresh(
|
|
HANDLE hShadowDB,
|
|
_TCHAR *lptzDrive,
|
|
LPCOPYPARAMS lpCP,
|
|
_TCHAR * lpszFullPath,
|
|
LPSHADOWINFO lpSI
|
|
);
|
|
|
|
BOOL
|
|
StalenessCheck(
|
|
BOOL hasBeenInited
|
|
);
|
|
|
|
VOID
|
|
GetLogCopyStatus(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
CopyLogToShare(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
AppendToShareLog(
|
|
HANDLE hLog
|
|
);
|
|
|
|
|
|
int
|
|
PRIVATE
|
|
AttemptReint(
|
|
int forceLevel
|
|
);
|
|
|
|
VOID
|
|
PRIVATE
|
|
AddToReintList(
|
|
LPCOPYPARAMS lpCP,
|
|
LPSHADOWINFO lpSI,
|
|
_TCHAR *szFileName
|
|
);
|
|
|
|
DWORD
|
|
PRIVATE
|
|
DoObjectEdit(
|
|
HANDLE hShadowDB,
|
|
_TCHAR * lpDrive,
|
|
_TCHAR * lptzFullPath,
|
|
LPCOPYPARAMS lpCP,
|
|
LPSHADOWINFO lpSI,
|
|
LPWIN32_FIND_DATA lpFind32Local,
|
|
LPWIN32_FIND_DATA lpFind32Remote,
|
|
int iShadowStatus,
|
|
int iFileStatus,
|
|
int uAction,
|
|
DWORD dwFileSystemFlags,
|
|
LPCSCPROC lpfnMergeProc,
|
|
DWORD_PTR dwContext
|
|
);
|
|
|
|
DWORD
|
|
PRIVATE
|
|
DoCreateDir(
|
|
HANDLE hShadowDB,
|
|
_TCHAR * lpDrive,
|
|
_TCHAR * lptzFullPath,
|
|
LPCOPYPARAMS lpCP,
|
|
LPSHADOWINFO lpSI,
|
|
LPWIN32_FIND_DATA lpFind32Local,
|
|
LPWIN32_FIND_DATA lpFind32Remote,
|
|
int iShadowStatus,
|
|
int iFileStatus,
|
|
int uAction,
|
|
DWORD dwFileSystemFlags,
|
|
LPCSCPROC lpfnMergeProc,
|
|
DWORD_PTR dwContext
|
|
);
|
|
|
|
DWORD
|
|
PRIVATE
|
|
CheckFileConflict(
|
|
LPSHADOWINFO,
|
|
LPWIN32_FIND_DATA
|
|
);
|
|
|
|
BOOL
|
|
FCheckAncestor(
|
|
node *lpnodeList,
|
|
LPCOPYPARAMS lpCP
|
|
);
|
|
|
|
int
|
|
PRIVATE
|
|
StampReintLog(
|
|
VOID
|
|
);
|
|
|
|
int
|
|
PRIVATE
|
|
LogReintError(
|
|
DWORD,
|
|
_TCHAR *,
|
|
_TCHAR *);
|
|
|
|
int
|
|
PRIVATE
|
|
WriteLog(
|
|
_TCHAR *
|
|
);
|
|
|
|
DWORD
|
|
PRIVATE
|
|
MoveConflictingFile(
|
|
LPCOPYPARAMS
|
|
);
|
|
|
|
DWORD
|
|
PRIVATE
|
|
GetUniqueName(
|
|
_TCHAR *,
|
|
_TCHAR *
|
|
);
|
|
|
|
VOID
|
|
PRIVATE
|
|
FormLocalNameFromRemoteName(
|
|
_TCHAR *,
|
|
_TCHAR *
|
|
);
|
|
|
|
DWORD
|
|
PRIVATE
|
|
InbCreateDir(
|
|
_TCHAR * lpDir,
|
|
DWORD dwAttr
|
|
);
|
|
|
|
int
|
|
PRIVATE
|
|
GetShadowByName(
|
|
HSHADOW,
|
|
_TCHAR *,
|
|
LPWIN32_FIND_DATA,
|
|
unsigned long *
|
|
);
|
|
|
|
_TCHAR *
|
|
PRIVATE
|
|
LpGetExclusionList(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
PRIVATE
|
|
ReleaseExclusionList(
|
|
LPVOID
|
|
);
|
|
|
|
|
|
BOOL
|
|
PRIVATE
|
|
FSkipObject(
|
|
HSHARE,
|
|
HSHADOW,
|
|
HSHADOW
|
|
);
|
|
|
|
int
|
|
PRIVATE
|
|
PurgeSkipQueue(
|
|
BOOL,
|
|
HSHARE,
|
|
HSHADOW,
|
|
HSHADOW
|
|
);
|
|
|
|
LPFAILINFO FAR *
|
|
LplpFindFailInfo(
|
|
HSHARE,
|
|
HSHADOW,
|
|
HSHADOW
|
|
);
|
|
|
|
VOID
|
|
PRIVATE
|
|
ReportStats(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
PRIVATE
|
|
CopyPQInfoToShadowInfo(
|
|
LPPQPARAMS,
|
|
LPSHADOWINFO
|
|
);
|
|
|
|
BOOL
|
|
PRIVATE
|
|
IsSlowLink(
|
|
_TCHAR *
|
|
);
|
|
|
|
VOID
|
|
PRIVATE
|
|
InferReplicaReintStatus(
|
|
LPSHADOWINFO lpSI,
|
|
LPWIN32_FIND_DATA lpFind32Local, // shadow info
|
|
LPWIN32_FIND_DATA lpFind32Remote, // if NULL, the remote doesn't exist
|
|
int *lpiShadowStatus,
|
|
int *lpiFileStatus,
|
|
unsigned *lpuAction
|
|
);
|
|
|
|
BOOL
|
|
GetRemoteWin32Info(
|
|
_TCHAR *lptzDrive,
|
|
LPCOPYPARAMS lpCP,
|
|
LPWIN32_FIND_DATA lpFind32,
|
|
BOOL *lpfExists
|
|
);
|
|
|
|
BOOL
|
|
PRIVATE
|
|
PerformOneReint(
|
|
HANDLE hShadowDB,
|
|
LPSECURITYINFO pShareSecurityInfo,
|
|
_TCHAR * lpszDrive, // drive mapped to the UNC name of lpSI->hShare
|
|
_TCHAR * lptzFullPath, // full UNC path
|
|
LPCOPYPARAMS lpCP, // copy parameters
|
|
LPSHADOWINFO lpSI, // shadowinfo structure
|
|
LPWIN32_FIND_DATA lpFind32Local, // local win32 data
|
|
LPWIN32_FIND_DATA lpFind32Remote, // remote win32 data, could be NULL
|
|
DWORD dwErrorRemoteFind32,// error code while getting remote win32 data
|
|
int iShadowStatus, // local copy status
|
|
int iFileStatus, // remote file status
|
|
unsigned uAction, // action to be taken
|
|
DWORD dwFileSystemFlags, // CODE.IMPROVEMENT, why not just pass down REINT_INFO
|
|
ULONG ulPrincipalID,
|
|
LPCSCPROC lpfnMergeProgress, // instead of the three parameters?
|
|
DWORD_PTR dwContext
|
|
);
|
|
|
|
ImpersonateALoggedOnUser(
|
|
VOID
|
|
);
|
|
|
|
HANDLE
|
|
CreateTmpFileWithSourceAcls(
|
|
_TCHAR *lptzSrc,
|
|
_TCHAR *lptzDst
|
|
);
|
|
|
|
BOOL
|
|
HasMultipleStreams(
|
|
_TCHAR *lpExistingFileName,
|
|
BOOL *lpfTrueFalse
|
|
);
|
|
|
|
int
|
|
PRIVATE
|
|
CALLBACK
|
|
RefreshProc(
|
|
LPCONNECTINFO lpCI,
|
|
DWORD dwCookie // LOWORD 0==Silently, 1== Give messages
|
|
// HIWORD 0==Nuke UNC, 1==Nuke all if no ongoing open/finds
|
|
// 2==Maximum force for shadow 3==Nuke ALL
|
|
);
|
|
|
|
|
|
|
|
BOOL
|
|
CALLBACK
|
|
ShdLogonProc(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Legacy code, used to be used in win9x implementation
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
switch(msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return TRUE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**************************** Fill routines *********************************/
|
|
|
|
int
|
|
AttemptCacheFill (
|
|
HSHARE hShareToSync,
|
|
int type,
|
|
BOOL fFullSync,
|
|
ULONG ulPrincipalID,
|
|
LPCSCPROC lpfnFillProgress,
|
|
DWORD_PTR dwContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine called by the agent and the fill API to do filling on a share.
|
|
|
|
Arguments:
|
|
|
|
hShareToSync Represnts the share to fill. If 0, fill all shares
|
|
|
|
type DO_ONE or DO_ALL. No one sets it to DO_ONE anymore
|
|
|
|
fFullSync if TRUE, we do a staleness check as well
|
|
|
|
ulPrincipalID ID of the principal as maintained in the shadow database
|
|
used to avoid ssyncing files for which the currently logged
|
|
on user doesn't have access
|
|
|
|
lpfnFillProgress Callback function to report progress, can be NULL
|
|
|
|
dwContext Callback context
|
|
|
|
|
|
Returns:
|
|
|
|
count of items done
|
|
|
|
Notes:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
PQPARAMS sPQP;
|
|
BOOL fFound = FALSE, fFoundBusy = FALSE, fAmAgent=FALSE, fNeedImpersonation=FALSE, fInodeTransaction=FALSE;
|
|
BOOL fSparseStaleDetected = FALSE;
|
|
SHADOWINFO sSI;
|
|
SHAREINFO sSR;
|
|
LPCOPYPARAMS lpCP = NULL;
|
|
DWORD dwError = NO_ERROR, dwFillStartTick=0, dwCount=0, dwSleepCount=0, dwSparseStaleDetectionCounter=0xffff;
|
|
HANDLE hShadowDB;
|
|
int cntDone = 0;
|
|
WIN32_FIND_DATA sFind32Local;
|
|
_TCHAR szNameBuff[MAX_PATH+MAX_SERVER_SHARE_NAME_FOR_CSC+10], tzDrive[4];
|
|
|
|
tzDrive[0] = 0;
|
|
|
|
fAmAgent = (GetCurrentThreadId() == vdwCopyChunkThreadId);
|
|
Assert(GetCurrentThreadId() != vdwAgentThreadId);
|
|
|
|
// when the agent comes here to fill, if the vfNeedPQTraversal flag is set
|
|
// we go ahead and traverse the priority Q.
|
|
|
|
// Otherwise, we check with the record manager whether since the last time we looked
|
|
// there has been any sparse or stale files encountered.
|
|
|
|
// If what the agent gets from the record manager
|
|
// is not the same as what the agent stored last time around
|
|
// then we let him traverse the Q.
|
|
|
|
// the whole idea here is to converse CPU cycles when
|
|
// there is nothing to fill
|
|
|
|
if (fAmAgent && !vfNeedPQTraversal)
|
|
{
|
|
GetSparseStaleDetectionCounter(INVALID_HANDLE_VALUE, &dwSparseStaleDetectionCounter);
|
|
|
|
if (dwSparseStaleDetectionCounter == vdwSparseStaleDetectionCounter)
|
|
{
|
|
ReintKdPrint(FILL, ("Agent.fill: SparseStaleDetectionCounter =%d is unchanged, not filling\n", vdwSparseStaleDetectionCounter));
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
vfNeedPQTraversal = TRUE;
|
|
vdwSparseStaleDetectionCounter = dwSparseStaleDetectionCounter;
|
|
ReintKdPrint(FILL, ("**Agent.fill: SparseStaleDetectionCounter =%d is changed**\n", vdwSparseStaleDetectionCounter));
|
|
}
|
|
}
|
|
|
|
if ((hShadowDB = OpenShadowDatabaseIO())==INVALID_HANDLE_VALUE)
|
|
{
|
|
goto bailout;
|
|
}
|
|
|
|
Assert(!(fFullSync && fAmAgent));
|
|
|
|
|
|
lpCP = LpAllocCopyParams();
|
|
|
|
if (!lpCP){
|
|
ReintKdPrint(BADERRORS, ("Agent:Allocation of copyparam buffer failed\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
if (fFullSync && hShareToSync)
|
|
{
|
|
ULONG ulStatus;
|
|
BOOL fIsDfsConnect;
|
|
|
|
if(GetShareInfo(hShadowDB, hShareToSync, &sSR, &ulStatus)<= 0)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("AttemptCacheFill: couldn't get status for server 0x%x\r\n", hShareToSync));
|
|
goto bailout;
|
|
}
|
|
|
|
dwError = DWConnectNet(sSR.rgSharePath, tzDrive, NULL, NULL, NULL, CONNECT_INTERACTIVE, &fIsDfsConnect);
|
|
|
|
if ((dwError != WN_SUCCESS) && (dwError != WN_CONNECTED_OTHER_PASSWORD) && (dwError != WN_CONNECTED_OTHER_PASSWORD_DEFAULT))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("AttemptCacheFill: connect to %ls failed error=%d\r\n", sSR.rgSharePath, dwError));
|
|
goto bailout;
|
|
}
|
|
|
|
}
|
|
|
|
memset(&sPQP, 0, sizeof(PQPARAMS));
|
|
memset(&sSI, 0, sizeof(SHADOWINFO));
|
|
|
|
if (type == DO_ALL)
|
|
{
|
|
PurgeSkipQueue(TRUE, hShareToSync, 0, 0);
|
|
}
|
|
|
|
if(BeginPQEnum(hShadowDB, &sPQP) == 0) {
|
|
goto bailout;
|
|
}
|
|
|
|
ReintKdPrint(FILL, ("Agent.fill:Started enumeration\n"));
|
|
|
|
do {
|
|
|
|
if (FAbortOperation())
|
|
{
|
|
cntDone = 0;
|
|
goto bailout;
|
|
}
|
|
if (fInodeTransaction)
|
|
{
|
|
DoShadowMaintenance(hShadowDB, SHADOW_END_INODE_TRANSACTION);
|
|
fInodeTransaction = FALSE;
|
|
}
|
|
if (fAmAgent)
|
|
{
|
|
Sleep(1); // Yield on NT because we are winlogon
|
|
}
|
|
if(!DoShadowMaintenance(hShadowDB, SHADOW_BEGIN_INODE_TRANSACTION))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("AttemptCacheFill: failed to begin inode transaction, aborting\n"));
|
|
break;
|
|
}
|
|
|
|
fInodeTransaction = TRUE;
|
|
if(NextPriShadow(hShadowDB, &sPQP) == 0) {
|
|
break;
|
|
}
|
|
|
|
if (++dwCount > 100000)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("AttemptCacheFill: Aborting, more than 100000 entries!!!\n"));
|
|
break;
|
|
}
|
|
|
|
if (!sPQP.hShadow) {
|
|
break;
|
|
}
|
|
|
|
if (fAmAgent && !fSparseStaleDetected &&
|
|
((mShadowIsFile(sPQP.ulStatus) && (sPQP.ulStatus & SHADOW_SPARSE)) || // sparse file
|
|
(sPQP.ulStatus & SHADOW_STALE))) // or stale file or dir
|
|
{
|
|
fSparseStaleDetected = TRUE;
|
|
}
|
|
|
|
if (!fFullSync && !(mShadowIsFile(sPQP.ulStatus) || (sPQP.ulStatus & SHADOW_STALE)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (hShareToSync && (hShareToSync != sPQP.hShare))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (fAmAgent && FSkipObject(sPQP.hShare, 0, 0)){
|
|
continue;
|
|
}
|
|
|
|
else if (mShadowNeedReint(sPQP.ulStatus)||
|
|
mShadowOrphan(sPQP.ulStatus)||
|
|
mShadowSuspect(sPQP.ulStatus)){
|
|
continue;
|
|
}
|
|
|
|
if (fAmAgent && FSkipObject(sPQP.hShare, sPQP.hDir, sPQP.hShadow)) {
|
|
continue;
|
|
}
|
|
|
|
// If we are not doing full sync then do only sparse filling
|
|
// or filling stale directories
|
|
// otherwise we also want to update the attributes and timestamps on
|
|
// the directories
|
|
|
|
if (fFullSync || (sPQP.ulStatus & (SHADOW_STALE|SHADOW_SPARSE))){
|
|
|
|
if (fAmAgent)
|
|
{
|
|
if (!hdesktopUser)
|
|
{
|
|
if (!(sPQP.ulHintFlags & FLAG_CSC_HINT_PIN_SYSTEM))
|
|
{
|
|
ReintKdPrint(FILL, ("AttemptCacheFill: skipping fill till logon happens\n"));
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (FAbortOperation())
|
|
{
|
|
cntDone = 0;
|
|
goto bailout;
|
|
}
|
|
|
|
if (!GetShadowInfoEx(hShadowDB, sPQP.hDir, sPQP.hShadow,
|
|
&sFind32Local, &sSI)){
|
|
|
|
ReintKdPrint(BADERRORS, ("AttemptCacheFill: GetShadowInfoEx failed\n"));
|
|
continue;
|
|
}
|
|
|
|
if (FAbortOperation())
|
|
{
|
|
cntDone = 0;
|
|
goto bailout;
|
|
}
|
|
|
|
if(GetUNCPath(hShadowDB, sPQP.hShare, sPQP.hDir, sPQP.hShadow, lpCP)){
|
|
|
|
// impersonate only if we are the agent and the file we are trying to
|
|
// bring down is not pinned for system. This was for remoteboot feature
|
|
// which doesn't exist any more.
|
|
|
|
fNeedImpersonation = (fAmAgent && !(sPQP.ulHintFlags & FLAG_CSC_HINT_PIN_SYSTEM));
|
|
|
|
if (!fNeedImpersonation ||
|
|
(mShadowIsFile(sPQP.ulStatus) && SetAgentThreadImpersonation(sPQP.hDir, sPQP.hShadow, FALSE))||
|
|
(!mShadowIsFile(sPQP.ulStatus) && ImpersonateALoggedOnUser()))
|
|
{
|
|
BOOL fStalenessCheck;
|
|
|
|
// !!NB Stale must be dealt with first
|
|
// because a sparse shadow can become stale
|
|
|
|
// NB!!! we assume the limits because we know that
|
|
// the database handles only that much size
|
|
|
|
lstrcpy(szNameBuff, lpCP->lpSharePath);
|
|
lstrcat(szNameBuff, lpCP->lpRemotePath);
|
|
|
|
fStalenessCheck = (fFullSync || (sSI.uStatus & SHADOW_STALE));
|
|
|
|
if (fStalenessCheck || (sSI.uStatus & SHADOW_SPARSE)) {
|
|
|
|
dwError = DoSparseFill(hShadowDB, szNameBuff, tzDrive, &sSI, &sFind32Local, lpCP, fStalenessCheck, ulPrincipalID, lpfnFillProgress, dwContext);
|
|
|
|
}
|
|
|
|
if (fNeedImpersonation)
|
|
{
|
|
ResetAgentThreadImpersonation();
|
|
}
|
|
|
|
if (fAmAgent)
|
|
{
|
|
if (dwFillStartTick == 0)
|
|
{
|
|
dwFillStartTick = GetTickCount();
|
|
ReintKdPrint(FILL, ("AttemptCacheFill: start tick count is %d ms\r\n", dwFillStartTick));
|
|
}
|
|
else
|
|
{
|
|
Assert(type != DO_ALL);
|
|
|
|
// if we have been filling too long
|
|
// comeback later
|
|
if (((int)(GetTickCount() - (dwFillStartTick+dwSleepCount)) > WAIT_INTERVAL_ATTEMPT_MS/3))
|
|
{
|
|
ReintKdPrint(FILL, ("AttemptCacheFill: aborting, been filling for more than %d ms\r\n", WAIT_INTERVAL_ATTEMPT_MS/3));
|
|
break;
|
|
}
|
|
|
|
}
|
|
Sleep(200);
|
|
dwSleepCount+=200;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Assert(fAmAgent);
|
|
Sleep(200);
|
|
dwSleepCount+=200;
|
|
|
|
// no one is allowed to read the entry
|
|
// go on to fill other things
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Agent: Shadow %08lx doesn't have an entry in the hierarchy \r\n", sPQP.hShadow));
|
|
continue;
|
|
}
|
|
|
|
if (dwError == NO_ERROR) {
|
|
|
|
cntDone += 1;
|
|
}
|
|
#if 0
|
|
if (type == DO_ONE_OBJECT) {
|
|
break;
|
|
}
|
|
#endif
|
|
if (dwError == ERROR_OPERATION_ABORTED)
|
|
{
|
|
cntDone = 0;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
} while (sPQP.uPos);
|
|
|
|
// if the agent traversed the entire PQ and didn't come across any item
|
|
// that needed to be filled or refreshed, then we turnoff the global flag indicating
|
|
// that we need priority Q traversal.
|
|
// From here on, the agent will be driven by the SparseStaleDetectionCount
|
|
if (fAmAgent)
|
|
{
|
|
// if even one sparse or stale was detected, traverse the queue again
|
|
if (fSparseStaleDetected)
|
|
{
|
|
vfNeedPQTraversal = TRUE;
|
|
}
|
|
else if (!sPQP.uPos)
|
|
{
|
|
vfNeedPQTraversal = FALSE;
|
|
ReintKdPrint(FILL, ("Agent.fill: No sparse stale entries found, going in querycount mode\r\n"));
|
|
}
|
|
}
|
|
|
|
// Close the enumeration
|
|
EndPQEnum(hShadowDB, &sPQP);
|
|
|
|
bailout:
|
|
if (fInodeTransaction)
|
|
{
|
|
DoShadowMaintenance(hShadowDB, SHADOW_END_INODE_TRANSACTION);
|
|
fInodeTransaction = FALSE;
|
|
}
|
|
if (hShadowDB != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseShadowDatabaseIO(hShadowDB);
|
|
}
|
|
|
|
if (lpCP) {
|
|
FreeCopyParams(lpCP);
|
|
}
|
|
|
|
if (tzDrive[0])
|
|
{
|
|
if(DWDisconnectDriveMappedNet(tzDrive, TRUE))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Failed disconnection of merge drive \r\n"));
|
|
}
|
|
else
|
|
{
|
|
ReintKdPrint(MERGE, ("Disconnected merge drive \r\n"));
|
|
}
|
|
|
|
}
|
|
|
|
ReintKdPrint(FILL, ("Agent.fill:done cachefill\n"));
|
|
return (cntDone);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DoRefresh(
|
|
HANDLE hShadowDB,
|
|
LPCOPYPARAMS lpCP,
|
|
_TCHAR * lpszFullPath,
|
|
LPSHADOWINFO lpSI,
|
|
_TCHAR * lptzDrive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks whether an item in the database has gone stale and if so, it refreshes it. If it is
|
|
a file, it is truncated and marked SPARSE
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = 0xffffffff;
|
|
|
|
if (!CheckForStalenessAndRefresh(hShadowDB, lptzDrive, lpCP, lpszFullPath, lpSI)) {
|
|
dwError = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
dwError = NO_ERROR;
|
|
}
|
|
|
|
if ((dwError != NOERROR) && IsNetDisconnected(dwError))
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
EnterSkipQueue(lpSI->hShare, lpSI->hDir, lpSI->hShadow, lpszFullPath);
|
|
#else
|
|
EnterSkipQueue(lpSI->hShare, lpSI->hDir, lpSI->hShadow);
|
|
#endif //DEBUG
|
|
}
|
|
|
|
return (dwError);
|
|
}
|
|
|
|
/*********************** Merging related routines **************************/
|
|
|
|
int
|
|
TraverseOneDirectory(
|
|
HANDLE hShadowDB,
|
|
LPSECURITYINFO pShareSecurityInfo,
|
|
HSHADOW hParentDir,
|
|
HSHADOW hDir,
|
|
LPTSTR lptzInputPath,
|
|
TRAVERSEFUNC lpfnTraverseDir,
|
|
LPVOID lpContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generic routine that traverses a directory in the database recursively and issues a
|
|
callback function to let callers do interesting things, such as merge or rename.
|
|
|
|
Arguments:
|
|
|
|
hShadowDB Handle to the redir for issuing ioctls
|
|
|
|
hParentDir handle to the parent directory
|
|
|
|
hDir handle to the directory to be traversed
|
|
|
|
lptzInputPath full qualified path of the directory
|
|
|
|
lpfnTraverseDir callback function to call at each step in the traversal
|
|
|
|
lpContext callback context
|
|
|
|
Returns:
|
|
|
|
return code, whether continue, cancel etc.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
WIN32_FIND_DATA sFind32;
|
|
SHADOWINFO sSI;
|
|
int retCode = TOD_CONTINUE, lenInputPath = 0, retCodeSav;
|
|
CSC_ENUMCOOKIE ulEnumCookie = NULL;
|
|
|
|
Assert(lptzInputPath);
|
|
|
|
lenInputPath = lstrlen(lptzInputPath);
|
|
|
|
Assert(lenInputPath && (lenInputPath < MAX_PATH));
|
|
|
|
ReintKdPrint(MERGE, ("Begin_Traverse directory %ls\r\n", lptzInputPath));
|
|
|
|
sSI.hDir = hParentDir;
|
|
sSI.hShadow = hDir;
|
|
retCode = (lpfnTraverseDir)(hShadowDB, pShareSecurityInfo, lptzInputPath, TOD_CALLBACK_REASON_BEGIN, &sFind32, &sSI, lpContext);
|
|
|
|
memset(&sFind32, 0, sizeof(sFind32));
|
|
lstrcpy(sFind32.cFileName, tzStarDotStar);
|
|
|
|
if(FindOpenShadow( hShadowDB, hDir, FINDOPEN_SHADOWINFO_ALL,
|
|
&sFind32, &sSI))
|
|
{
|
|
if (FAbortOperation())
|
|
{
|
|
ReintKdPrint(MERGE, ("TraverseOneDirectory:Abort received\r\n"));
|
|
SetLastError(ERROR_CANCELLED);
|
|
goto bailout;
|
|
}
|
|
|
|
|
|
ulEnumCookie = sSI.uEnumCookie;
|
|
|
|
do
|
|
{
|
|
int lenChildName;
|
|
|
|
lenChildName = lstrlen(sFind32.cFileName);
|
|
|
|
if (!lenChildName || ((lenInputPath+lenChildName+1) >= MAX_PATH))
|
|
{
|
|
ReintKdPrint(MERGE, ("TraverseOneDirectory: path exceeds max path or is invalid\r\n"));
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
retCode = TOD_ABORT;
|
|
goto bailout;
|
|
}
|
|
|
|
lptzInputPath[lenInputPath] = _T('\\');
|
|
lstrcpy(&lptzInputPath[lenInputPath+1], sFind32.cFileName);
|
|
|
|
if (sFind32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
retCode = (lpfnTraverseDir)(hShadowDB, pShareSecurityInfo, lptzInputPath, TOD_CALLBACK_REASON_NEXT_ITEM, &sFind32, &sSI, lpContext);
|
|
|
|
if (retCode == TOD_ABORT)
|
|
{
|
|
ReintKdPrint(MERGE, ("TraverseOneDirectory:Abort\r\n"));
|
|
goto bailout;
|
|
}
|
|
retCode = TraverseOneDirectory(hShadowDB, pShareSecurityInfo, sSI.hDir, sSI.hShadow, lptzInputPath, lpfnTraverseDir, lpContext);
|
|
}
|
|
else
|
|
{
|
|
retCode = (lpfnTraverseDir)(hShadowDB, pShareSecurityInfo, lptzInputPath, TOD_CALLBACK_REASON_NEXT_ITEM, &sFind32, &sSI, lpContext);
|
|
|
|
}
|
|
|
|
lptzInputPath[lenInputPath] = 0;
|
|
|
|
if (retCode == TOD_ABORT)
|
|
{
|
|
ReintKdPrint(MERGE, ("TraverseOneDirectory:Abort\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
} while(FindNextShadow(hShadowDB, ulEnumCookie, &sFind32, &sSI));
|
|
}
|
|
|
|
bailout:
|
|
|
|
sSI.hDir = hParentDir;
|
|
sSI.hShadow = hDir;
|
|
retCodeSav = (lpfnTraverseDir)(hShadowDB, pShareSecurityInfo, lptzInputPath, TOD_CALLBACK_REASON_END, &sFind32, &sSI, lpContext);
|
|
if (retCode != TOD_ABORT)
|
|
{
|
|
retCode = retCodeSav;
|
|
}
|
|
ReintKdPrint(MERGE, ("End_Traverse directory %ls\r\n", lptzInputPath));
|
|
|
|
if (ulEnumCookie)
|
|
{
|
|
FindCloseShadow(hShadowDB, ulEnumCookie);
|
|
}
|
|
|
|
return retCode;
|
|
}
|
|
|
|
int
|
|
ReintDirCallback(
|
|
HANDLE hShadowDB,
|
|
LPSECURITYINFO pShareSecurityInfo,
|
|
LPTSTR lptzFullPath,
|
|
DWORD dwCallbackReason,
|
|
WIN32_FIND_DATA *lpFind32,
|
|
SHADOWINFO *lpSI,
|
|
LPREINT_INFO lpRei
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
callback function used by ReintOneShare while calling TraverseOneDirectory. It is called
|
|
on each step in the traversal. This routine issues call to do the merging
|
|
|
|
Arguments:
|
|
|
|
hShadowDB Handle to issue ioctls to the redir
|
|
|
|
lptzFullPath fully qualified path to the item
|
|
|
|
dwCallbackReason TOD_CALLBACK_REASON_XXX (BEGIN, NEXT_ITEM or END)
|
|
|
|
lpFind32 local win32info
|
|
|
|
lpSI other info such as priority, pincount etc.
|
|
|
|
lpRei reintegration information context
|
|
|
|
Returns:
|
|
|
|
return code, whether continue, cancel etc.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
int retCode = TOD_CONTINUE;
|
|
int iFileStatus, iShadowStatus;
|
|
LPCOPYPARAMS lpCP = NULL;
|
|
BOOL fInsertInList = FALSE, fIsFile;
|
|
WIN32_FIND_DATA sFind32Remote, *lpFind32Remote = NULL;
|
|
unsigned uAction;
|
|
DWORD dwErrorRemote = ERROR_SUCCESS;
|
|
|
|
if (dwCallbackReason != TOD_CALLBACK_REASON_NEXT_ITEM)
|
|
{
|
|
return TOD_CONTINUE;
|
|
}
|
|
|
|
if ( mShadowOrphan(lpSI->uStatus)||
|
|
mShadowSuspect(lpSI->uStatus))
|
|
{
|
|
return TOD_CONTINUE;
|
|
}
|
|
|
|
if (!mShadowNeedReint(lpSI->uStatus))
|
|
{
|
|
return TOD_CONTINUE;
|
|
}
|
|
|
|
fIsFile = ((lpFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
|
|
|
|
switch (lpRei->nCurrentState)
|
|
{
|
|
case REINT_DELETE_FILES:
|
|
if (!fIsFile || !mShadowDeleted(lpSI->uStatus))
|
|
{
|
|
return TOD_CONTINUE;
|
|
}
|
|
break;
|
|
case REINT_DELETE_DIRS:
|
|
if (fIsFile || !mShadowDeleted(lpSI->uStatus))
|
|
{
|
|
return TOD_CONTINUE;
|
|
}
|
|
break;
|
|
case REINT_CREATE_UPDATE_FILES:
|
|
if (!fIsFile)
|
|
{
|
|
return TOD_CONTINUE;
|
|
}
|
|
break;
|
|
case REINT_CREATE_UPDATE_DIRS:
|
|
if (fIsFile)
|
|
{
|
|
return TOD_CONTINUE;
|
|
}
|
|
break;
|
|
default:
|
|
Assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
if (!fStamped){
|
|
StampReintLog();
|
|
fStamped = TRUE;
|
|
}
|
|
#endif
|
|
lpCP = LpAllocCopyParams();
|
|
|
|
if (!lpCP){
|
|
ReintKdPrint(BADERRORS, ("ReintDirCallback: Allocation of copyparam buffer failed\n"));
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
retCode = TOD_ABORT;
|
|
goto bailout;
|
|
}
|
|
if(!GetUNCPath(hShadowDB, lpSI->hShare, lpSI->hDir, lpSI->hShadow, lpCP)){
|
|
|
|
ReintKdPrint(BADERRORS, ("ReintDirCallback: GetUNCPath failed\n"));
|
|
Assert(FALSE);
|
|
retCode = TOD_CONTINUE;
|
|
goto bailout;
|
|
}
|
|
|
|
ReintKdPrint(MERGE, ("Merging local changes to <%ls%ls>\n", lpCP->lpSharePath, lpCP->lpRemotePath));
|
|
|
|
fInsertInList = FALSE;
|
|
|
|
lpFind32Remote = NULL;
|
|
|
|
// if there is an insertion list, then check whether his ancestor
|
|
// didn't fail in reintegration
|
|
if (lpRei->lpnodeInsertList)
|
|
{
|
|
// if there is an acestor then we should put this guy in the list
|
|
fInsertInList = FCheckAncestor(lpRei->lpnodeInsertList, lpCP);
|
|
}
|
|
|
|
// if we are not supposed to put him in the list then try getting
|
|
// his win32 strucuture
|
|
if (!fInsertInList)
|
|
{
|
|
|
|
BOOL fExists;
|
|
|
|
ReintKdPrint(MERGE, ("getting Remote win32 Info \n"));
|
|
|
|
if (!GetRemoteWin32Info(lpRei->tzDrive, lpCP, &sFind32Remote, &fExists) && (fExists == -1))
|
|
{
|
|
// NB: dwErrorRemote is set only when fExists is -1, ie there some error
|
|
// besides the file not being there
|
|
|
|
// some error happened while getting remote find32
|
|
if (IsNetDisconnected(dwErrorRemote = GetLastError()))
|
|
{
|
|
#ifdef DEBUG
|
|
EnterSkipQueue(lpSI->hShare, 0, 0, lpCP->lpSharePath);
|
|
#else
|
|
EnterSkipQueue(lpSI->hShare, 0, 0);
|
|
#endif //DEBUG
|
|
retCode = TOD_ABORT;
|
|
ReintKdPrint(BADERRORS, ("ReintDirCallback: Error = %d NetDisconnected aborting\r\n", GetLastError()));
|
|
goto bailout;
|
|
}
|
|
|
|
}
|
|
|
|
// passing remote find32 only if it succeeded
|
|
if (fExists == TRUE)
|
|
{
|
|
lpFind32Remote = &sFind32Remote;
|
|
}
|
|
Assert(!((fExists != -1) && (dwErrorRemote != NO_ERROR)));
|
|
}
|
|
else
|
|
{
|
|
ReintKdPrint(BADERRORS, ("ReintDirCallback: Inserting in failed list\r\n"));
|
|
}
|
|
|
|
|
|
// find out what needs to be done
|
|
// this is one central place to infer all the stuff
|
|
InferReplicaReintStatus(
|
|
lpSI, // shadowinfo
|
|
lpFind32, // win32 info for the shadow
|
|
lpFind32Remote, // remote win32 info
|
|
&iShadowStatus,
|
|
&iFileStatus,
|
|
&uAction
|
|
);
|
|
|
|
|
|
if (!fInsertInList)
|
|
{
|
|
ReintKdPrint(MERGE, ("Silently doing <%ls%ls>\n", lpCP->lpSharePath, lpCP->lpRemotePath));
|
|
|
|
|
|
fInsertInList = (PerformOneReint(
|
|
hShadowDB,
|
|
pShareSecurityInfo,
|
|
lpRei->tzDrive,
|
|
lptzFullPath,
|
|
lpCP,
|
|
lpSI,
|
|
lpFind32,
|
|
lpFind32Remote,
|
|
dwErrorRemote,
|
|
iShadowStatus,
|
|
iFileStatus,
|
|
uAction,
|
|
lpRei->dwFileSystemFlags,
|
|
lpRei->ulPrincipalID,
|
|
lpRei->lpfnMergeProgress,
|
|
lpRei->dwContext
|
|
) == FALSE);
|
|
if (fInsertInList)
|
|
{
|
|
if (IsNetDisconnected(GetLastError()))
|
|
{
|
|
#ifdef DEBUG
|
|
EnterSkipQueue(lpSI->hShare, 0, 0, lpCP->lpSharePath);
|
|
#else
|
|
EnterSkipQueue(lpSI->hShare, 0, 0);
|
|
#endif //DEBUG
|
|
retCode = TOD_ABORT;
|
|
ReintKdPrint(BADERRORS, ("ReintDirCallback: Error = %d NetDisconnected aborting\r\n", GetLastError()));
|
|
goto bailout;
|
|
}
|
|
else if (GetLastError() == ERROR_OPERATION_ABORTED)
|
|
{
|
|
retCode = TOD_ABORT;
|
|
ReintKdPrint(BADERRORS, ("ReintDirCallback: operation aborted becuase of ERROR_OPERATION_ABORT\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
retCode = TOD_CONTINUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReintKdPrint(BADERRORS, ("ReintDirCallback: Was Inserted in failed list\r\n"));
|
|
}
|
|
|
|
|
|
bailout:
|
|
if (lpCP) {
|
|
FreeCopyParams(lpCP);
|
|
}
|
|
|
|
return retCode;
|
|
}
|
|
|
|
BOOL
|
|
PUBLIC
|
|
ReintOneShare(
|
|
HSHARE hShare,
|
|
HSHADOW hRoot, // root inode
|
|
_TCHAR *lpDomainName,
|
|
_TCHAR *lpUserName,
|
|
_TCHAR *lpPassword,
|
|
ULONG ulPrincipalID,
|
|
LPCSCPROC lpfnMergeProgress,
|
|
DWORD_PTR dwContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the workhorse routine that does the merging of a share which may have modifications
|
|
made while offline.
|
|
|
|
The routine, first checks whether any modifications have been done at all on this share.
|
|
Is so, then it gets a list of all the drive-mapped and explicit UNC connections made to
|
|
this share.
|
|
|
|
The routine then creates a special drive mapping to the share, done by passing in an
|
|
extended attribute flag CSC_BYPASS (defined in lmuse.h). This tells the redir
|
|
to bypass all CSC functionality.
|
|
|
|
It then deletes all the connections in the list gathered before making the EA based
|
|
connections.
|
|
|
|
The merge then proceeds by enumerating the share from the database. It uses, TraverseOneDirectory
|
|
routine and gives it ReintDirCallback routine as a callback with REINT_INFO as the context.
|
|
The directory travesal proceeds from the root of the share
|
|
|
|
At the end of the merge, the EA connection is deleted and the connections in the list are
|
|
reconnected.
|
|
|
|
|
|
Arguments:
|
|
|
|
hShare // Represents the share to merged
|
|
|
|
hRoot // the root inode for the share
|
|
|
|
lpDomainName // domain name for EA drivemapping (can be NULL)
|
|
|
|
lpUserName // username for EA drivemapping (can be NULL)
|
|
|
|
lpPassword // password for EA drivemapping (can be NULL)
|
|
|
|
ulPrinciaplID // the ID of the guy calling reint
|
|
|
|
lpfnMergeProgress // callback function for reporting progress
|
|
|
|
dwContext // callback context
|
|
|
|
Returns:
|
|
|
|
TRUE if successful. If FALSE, GetLastError returns the actual errorcode.
|
|
|
|
Notes:
|
|
|
|
The EA drivemap is passed back to the callback routine during the CSCPROC_REASON_BEGIN callback
|
|
so that the callback routine can use the same driveletter to bypass CSC for doing whatever
|
|
it needs to do on the server without having CSC get in it's way.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fConnected=FALSE, fDone = FALSE;
|
|
BOOL fStamped = FALSE, fInsertInList = FALSE, fBeginReint = FALSE, fDisabledShadowing = FALSE;
|
|
unsigned long ulStatus;
|
|
HANDLE hShadowDB;
|
|
SHAREINFO sSR;
|
|
SHADOWINFO sSI;
|
|
int iRet, i;
|
|
ULONG nRet = 0;
|
|
DWORD dwError, dwRet, dwMaxComponentLength=0;
|
|
TCHAR tzFullPath[MAX_PATH+1], tzDrive[4];
|
|
LPCONNECTINFO lpHead = NULL;
|
|
BOOL fIsDfsConnect = FALSE;
|
|
LPVOID lpContext = NULL;
|
|
DWORD dwDummy;
|
|
SECURITYINFO rgsSecurityInfo[CSC_MAXIMUM_NUMBER_OF_CACHED_PRINCIPAL_IDS];
|
|
LPSECURITYINFO pShareSecurityInfo = NULL;
|
|
|
|
// if reintegration is going on on a share, then ask him to stop
|
|
// NTRAID-455269-shishirp-1/31/2000 we should allow reintegration on multiple shares
|
|
if (vsRei.hShare)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: reintegration is in progress\r\n"));
|
|
SetLastError(ERROR_SHARING_VIOLATION);
|
|
return FALSE;
|
|
}
|
|
|
|
// we enter a critical section because eventually we should allocate a reint_info strucuture
|
|
// and thread it in a list
|
|
|
|
EnterAgentCrit();
|
|
memset(&vsRei, 0, sizeof(vsRei));
|
|
|
|
vsRei.lpfnMergeProgress = lpfnMergeProgress;
|
|
|
|
vsRei.dwContext = dwContext;
|
|
vsRei.hShare = hShare;
|
|
vsRei.ulPrincipalID = ulPrincipalID;
|
|
|
|
LeaveAgentCrit();
|
|
|
|
memset(tzDrive, 0, sizeof(tzDrive));
|
|
|
|
if ((hShadowDB = OpenShadowDatabaseIO()) ==INVALID_HANDLE_VALUE)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: failed to open database\r\n"));
|
|
return FALSE;
|
|
}
|
|
if(GetShareInfo(hShadowDB, hShare, &sSR, &ulStatus)<= 0)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: couldn't get status for server 0x%x\r\n", hShare));
|
|
goto bailout;
|
|
}
|
|
|
|
dwDummy = sizeof(rgsSecurityInfo);
|
|
nRet = GetSecurityInfoForCSC(
|
|
hShadowDB,
|
|
0,
|
|
hRoot,
|
|
rgsSecurityInfo,
|
|
&dwDummy);
|
|
|
|
if (nRet > 0)
|
|
pShareSecurityInfo = rgsSecurityInfo;
|
|
|
|
lstrcpy(tzFullPath, sSR.rgSharePath);
|
|
|
|
// this will modify the reint bit on the share if necessary
|
|
if(!CSCEnumForStatsInternal(sSR.rgSharePath, NULL, FALSE, TRUE, 0))
|
|
{
|
|
ReintKdPrint(MERGE, ("ReintOneShare: Couldn't get stats for %ls \r\n", sSR.rgSharePath));
|
|
goto bailout;
|
|
|
|
}
|
|
|
|
if (!(ulStatus & SHARE_REINT))
|
|
{
|
|
ReintKdPrint(MERGE, ("ReintOneShare: server %ls doesn't need reintegration\r\n", sSR.rgSharePath));
|
|
fDone = TRUE;
|
|
goto bailout;
|
|
}
|
|
|
|
if (!GetShadowInfoEx(INVALID_HANDLE_VALUE, 0, hRoot, NULL, &sSI)){
|
|
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: GetShadowInfoEx failed\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
|
|
|
|
// put the share in reintegration mode
|
|
// if this is not marked system pinned, then it would be a blocking reint
|
|
// Putting the share in reintegration mode makes all open calls fail, except those
|
|
// done on the special EA drive mapping
|
|
|
|
// NB, beginreint is an async ioctl. This is the basis for cleanup
|
|
// when the thread does the merge dies
|
|
|
|
|
|
// All reint's are blocking reints.
|
|
if (!BeginReint(hShare, TRUE /*!(sSI.ulHintFlags & FLAG_CSC_HINT_PIN_SYSTEM)*/, &lpContext))
|
|
{
|
|
if (GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: Couldn't put server 0x%x in reintegration state\r\n", hShare));
|
|
goto bailout;
|
|
}
|
|
}
|
|
|
|
fBeginReint = TRUE;
|
|
|
|
// After putting the share in reintegration mode, we get the list of all
|
|
// connection to the share and delete them with maximum force.
|
|
// this ensures that no files are open after the share is put in reintegration mode
|
|
// Moreover any files that are open are only thorugh the EA drive mapping
|
|
|
|
// obtain the list of connections to this share.
|
|
// do this before making a the special CSC_BYPASS connection
|
|
// so that the list won't have it
|
|
FGetConnectionListEx(&lpHead, sSR.rgSharePath, FALSE, FALSE, NULL);
|
|
|
|
// now make the connection
|
|
ReintKdPrint(MERGE, ("CSC.ReintOneShare: Attempting to map drive letter to %ls \r\n", sSR.rgSharePath));
|
|
dwError = DWConnectNet(sSR.rgSharePath, tzDrive, lpDomainName, lpUserName, lpPassword, CONNECT_INTERACTIVE, &fIsDfsConnect);
|
|
if ((dwError != WN_SUCCESS) &&
|
|
(dwError != WN_CONNECTED_OTHER_PASSWORD_DEFAULT) &&
|
|
(dwError != WN_CONNECTED_OTHER_PASSWORD))
|
|
{
|
|
#ifdef DEBUG
|
|
EnterSkipQueue(hShare, 0, 0, sSR.rgSharePath);
|
|
#else
|
|
EnterSkipQueue(hShare, 0, 0);
|
|
#endif
|
|
SetLastError(dwError);
|
|
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: Error %d, couldn't connect to %ls\r\n", dwError, sSR.rgSharePath));
|
|
|
|
// clear the connection list and bailout
|
|
if (lpHead)
|
|
{
|
|
ClearConnectionList(&lpHead);
|
|
lpHead = NULL;
|
|
}
|
|
|
|
goto bailout;
|
|
|
|
}
|
|
|
|
fConnected = TRUE;
|
|
|
|
// if we have a connectionlist, disconnect all connections before attempting merge
|
|
// NB, this is done after the drivemapped connection is made, so that
|
|
// if there are special credentials on the server, they are maintained
|
|
// as there is always atelast one outstanding connection
|
|
|
|
if (lpHead)
|
|
{
|
|
DisconnectList(&lpHead, NULL, 0);
|
|
}
|
|
|
|
lstrcpy(vsRei.tzDrive, tzDrive);
|
|
|
|
tzDrive[2]='\\';tzDrive[3]=0;
|
|
ReintKdPrint(MERGE, ("CSC.ReintOneShare: mapped drive letter %ls IsDfs=%d\r\n", tzDrive, fIsDfsConnect));
|
|
|
|
vsRei.dwFileSystemFlags = 0;
|
|
|
|
// NTRAID#455273-shishirp-1/31/2000 we do getvolumeinfo only for non-dfs shares. This is because of a problem in
|
|
// the DFS code that doesn't bypass volume operations for CSC agent. GetVolumeInfo is not implemented
|
|
// by DFS
|
|
|
|
if (!fIsDfsConnect)
|
|
{
|
|
if(!GetVolumeInformation(tzDrive, NULL, 0, NULL, &dwMaxComponentLength, &vsRei.dwFileSystemFlags, NULL, 0))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("CSC.ReintOneShare: failed to get volume info for %ls Error=%d\r\n", tzDrive, GetLastError()));
|
|
goto bailout;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vsRei.dwFileSystemFlags = DFS_ROOT_FILE_SYSTEM_FLAGS;
|
|
}
|
|
|
|
tzDrive[2]=0;
|
|
|
|
ReintKdPrint(MERGE, ("CSC.ReintOneShare: FileSystemFlags=%x \r\n", vsRei.dwFileSystemFlags));
|
|
|
|
if (lpfnMergeProgress)
|
|
{
|
|
WIN32_FIND_DATA *lpFT;
|
|
|
|
lpFT = (WIN32_FIND_DATA *)LocalAlloc(LPTR, sizeof(WIN32_FIND_DATA));
|
|
|
|
if (!lpFT)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: Couldn't allocate find32 strucutre for callback \r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
lstrcpy(lpFT->cFileName, vsRei.tzDrive);
|
|
|
|
try
|
|
{
|
|
dwRet = (*lpfnMergeProgress)(sSR.rgSharePath, ulStatus, 0, 0, lpFT, CSCPROC_REASON_BEGIN, 0, 0, dwContext);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwRet = CSCPROC_RETURN_ABORT;
|
|
}
|
|
|
|
|
|
LocalFree(lpFT);
|
|
|
|
if (dwRet != CSCPROC_RETURN_CONTINUE)
|
|
{
|
|
if (dwRet == CSCPROC_RETURN_ABORT)
|
|
{
|
|
SetLastError(ERROR_OPERATION_ABORTED);
|
|
}
|
|
else
|
|
{
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i=0; i<4; ++i)
|
|
{
|
|
// for now we don't do directory deletions
|
|
// we will fix this later
|
|
|
|
if (i==REINT_DELETE_DIRS)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
vsRei.nCurrentState = i;
|
|
|
|
try
|
|
{
|
|
iRet = TraverseOneDirectory(
|
|
hShadowDB,
|
|
pShareSecurityInfo,
|
|
0,
|
|
hRoot,
|
|
tzFullPath,
|
|
ReintDirCallback,
|
|
(LPVOID)&vsRei);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
iRet = TOD_ABORT;
|
|
}
|
|
|
|
if (iRet == TOD_ABORT)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (iRet != TOD_ABORT)
|
|
{
|
|
if(!vsRei.lpnodeInsertList)
|
|
{
|
|
fDone = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fDone) {
|
|
|
|
SetShareStatus(hShadowDB, hShare, (unsigned long)(~SHARE_REINT), SHADOW_FLAGS_AND);
|
|
}
|
|
|
|
bailout:
|
|
|
|
|
|
if (!fDone)
|
|
{
|
|
dwError = GetLastError();
|
|
}
|
|
|
|
#if 0
|
|
if (fIsDfsConnect)
|
|
{
|
|
DbgPrint("Nuking DFS connects On Close %ls \n", sSR.rgSharePath);
|
|
do
|
|
{
|
|
if(WNetCancelConnection2(sSR.rgSharePath, 0, TRUE) != NO_ERROR)
|
|
{
|
|
DbgPrint("Nuked On Close %ls Error=%d\n", sSR.rgSharePath, GetLastError());
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("Nuked On Close %ls \n", sSR.rgSharePath);
|
|
}
|
|
|
|
} while (TRUE);
|
|
}
|
|
#endif
|
|
|
|
EnterAgentCrit();
|
|
|
|
if (fBeginReint){
|
|
|
|
Assert(hShare == vsRei.hShare);
|
|
|
|
EndReint(hShare, lpContext);
|
|
|
|
vsRei.hShare = 0;
|
|
|
|
|
|
fBeginReint = FALSE;
|
|
if (lpfnMergeProgress)
|
|
{
|
|
try
|
|
{
|
|
dwRet = (*lpfnMergeProgress)(sSR.rgSharePath, ulStatus, 0, 0, NULL, CSCPROC_REASON_END, 0, 0, dwContext);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwRet = CSCPROC_RETURN_ABORT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fDisabledShadowing)
|
|
{
|
|
EnableShadowingForThisThread(hShadowDB);
|
|
}
|
|
|
|
CloseShadowDatabaseIO(hShadowDB);
|
|
|
|
if(vsRei.lpnodeInsertList) {
|
|
killList(vsRei.lpnodeInsertList);
|
|
}
|
|
|
|
// reestablish the connection list
|
|
// NB we do this before we disconnect the drive mapping
|
|
// so that if any special credentials stay because
|
|
// there is always one connection outstanding to the server
|
|
|
|
if (lpHead)
|
|
{
|
|
ReconnectList(&lpHead, NULL);
|
|
ClearConnectionList(&lpHead);
|
|
}
|
|
|
|
if (fConnected) {
|
|
|
|
if(DWDisconnectDriveMappedNet(vsRei.tzDrive, TRUE))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Failed disconnection of merge drive \r\n"));
|
|
}
|
|
else
|
|
{
|
|
ReintKdPrint(MERGE, ("Disconnected merge drive \r\n"));
|
|
}
|
|
}
|
|
|
|
memset(&vsRei, 0, sizeof(vsRei));
|
|
|
|
LeaveAgentCrit();
|
|
|
|
if (!fDone)
|
|
{
|
|
|
|
ReintKdPrint(BADERRORS, ("Failed merge dwError=%d\r\n", dwError));
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
|
|
return (fDone);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* enumerate all the shares, checking to see if the share needs to be
|
|
* merged before starting.
|
|
* Returns: # of shares that needed to be merged and were successfully done.
|
|
*/
|
|
// HWND for parent for UI.
|
|
int
|
|
PUBLIC
|
|
ReintAllShares(
|
|
HWND hwndParent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
unsigned long ulStatus;
|
|
WIN32_FIND_DATA sFind32;
|
|
int iDone=0, iDoneOne;
|
|
SHADOWINFO sSI;
|
|
HANDLE hShadowDB;
|
|
CSC_ENUMCOOKIE ulEnumCookie=NULL;
|
|
|
|
if ((hShadowDB = OpenShadowDatabaseIO()) ==INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#if 0
|
|
if (!EnterAgentCrit()) {
|
|
ReintKdPrint(BADERRORS, ("ReintAllShares:Failed to enter critsect \r\n"));
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
vhcursor = LoadCursor(NULL, IDC_WAIT);
|
|
|
|
memset(&sFind32, 0, sizeof(sFind32));
|
|
lstrcpy(sFind32.cFileName, tzStarDotStar);
|
|
|
|
|
|
if(FindOpenShadow( hShadowDB, 0, FINDOPEN_SHADOWINFO_ALL, &sFind32, &sSI)){
|
|
|
|
ulEnumCookie = sSI.uEnumCookie;
|
|
|
|
do {
|
|
if (FAbortOperation())
|
|
{
|
|
break;
|
|
}
|
|
|
|
if(GetShareStatus(hShadowDB, sSI.hShare, &ulStatus)) {
|
|
if(TRUE/*ulStatus & SHARE_REINT*/){ // OLDCODE
|
|
iDoneOne = ReintOneShare(sSI.hShare, sSI.hShadow, NULL, NULL, NULL, CSC_INVALID_PRINCIPAL_ID, NULL, 0);
|
|
if (iDoneOne > 0){
|
|
if (iDone >= 0)
|
|
++iDone;
|
|
}
|
|
else if (iDoneOne < 0){
|
|
iDone = -1;
|
|
}
|
|
}
|
|
else {
|
|
ReintKdPrint(MERGE, ("server %d doesn't need reint.\n", sSI.hShare));
|
|
}
|
|
}
|
|
} while(FindNextShadow( hShadowDB, ulEnumCookie, &sFind32, &sSI));
|
|
|
|
FindCloseShadow(hShadowDB, ulEnumCookie);
|
|
}
|
|
|
|
#if 0
|
|
LeaveAgentCrit();
|
|
#endif
|
|
vhcursor = NULL;
|
|
|
|
CloseShadowDatabaseIO(hShadowDB);
|
|
return (iDone);
|
|
}
|
|
|
|
int
|
|
CheckDirtyShares(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
unsigned long ulStatus;
|
|
WIN32_FIND_DATA sFind32;
|
|
int cntDirty=0;
|
|
SHADOWINFO sSI;
|
|
HANDLE hShadowDB;
|
|
CSC_ENUMCOOKIE ulEnumCookie=NULL;
|
|
|
|
if ((hShadowDB = OpenShadowDatabaseIO()) ==INVALID_HANDLE_VALUE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
memset(&sFind32, 0, sizeof(sFind32));
|
|
lstrcpy(sFind32.cFileName, tzStarDotStar);
|
|
|
|
if(FindOpenShadow( hShadowDB, 0, FINDOPEN_SHADOWINFO_ALL,
|
|
&sFind32, &sSI))
|
|
{
|
|
ulEnumCookie = sSI.uEnumCookie;
|
|
|
|
do {
|
|
if(GetShareStatus(hShadowDB, sSI.hShare, &ulStatus)) {
|
|
|
|
if(ulStatus & SHARE_REINT){
|
|
++cntDirty;
|
|
}
|
|
|
|
}
|
|
} while(FindNextShadow(hShadowDB, ulEnumCookie, &sFind32, &sSI));
|
|
|
|
FindCloseShadow(hShadowDB, ulEnumCookie);
|
|
}
|
|
|
|
CloseShadowDatabaseIO(hShadowDB);
|
|
|
|
return cntDirty;
|
|
}
|
|
|
|
BOOL
|
|
GetRemoteWin32Info(
|
|
_TCHAR *lptzDrive,
|
|
LPCOPYPARAMS lpCP,
|
|
LPWIN32_FIND_DATA lpFind32,
|
|
BOOL *lpfExists
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
_TCHAR * lpT = NULL;
|
|
BOOL fRet = FALSE;
|
|
_TCHAR tzDrive[4];
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
*lpfExists = -1;
|
|
tzDrive[0] = 0;
|
|
|
|
lpT = AllocMem((lstrlen(lpCP->lpSharePath) + lstrlen(lpCP->lpRemotePath) + 2) * sizeof(_TCHAR));
|
|
|
|
if (lpT)
|
|
{
|
|
|
|
if (lptzDrive && lptzDrive[0])
|
|
{
|
|
lstrcpy(lpT, lptzDrive);
|
|
}
|
|
else
|
|
{
|
|
dwError = DWConnectNet(lpCP->lpSharePath, tzDrive, NULL, NULL, NULL, 0, NULL);
|
|
if ((dwError != WN_SUCCESS) && (dwError != WN_CONNECTED_OTHER_PASSWORD_DEFAULT))
|
|
{
|
|
tzDrive[0] = 0;
|
|
goto bailout;
|
|
}
|
|
|
|
lstrcpy(lpT, tzDrive);
|
|
}
|
|
|
|
lstrcat(lpT, lpCP->lpRemotePath);
|
|
|
|
fRet = GetWin32Info(lpT, lpFind32); // if this fails, GetLastError is properly set
|
|
|
|
if (fRet)
|
|
{
|
|
*lpfExists = TRUE;
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
if ((dwError == ERROR_FILE_NOT_FOUND)||
|
|
(dwError == ERROR_PATH_NOT_FOUND)||
|
|
(dwError == ERROR_INVALID_PARAMETER)
|
|
)
|
|
{
|
|
*lpfExists = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
bailout:
|
|
if (tzDrive[0])
|
|
{
|
|
if(DWDisconnectDriveMappedNet(tzDrive, TRUE))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Failed disconnection of remote drive \r\n"));
|
|
}
|
|
}
|
|
if (lpT)
|
|
{
|
|
FreeMem(lpT);
|
|
}
|
|
if (!fRet)
|
|
{
|
|
SetLastError(dwError);
|
|
}
|
|
return (fRet);
|
|
}
|
|
|
|
|
|
VOID
|
|
PRIVATE
|
|
InferReplicaReintStatus(
|
|
LPSHADOWINFO lpSI, // shadow info
|
|
LPWIN32_FIND_DATA lpFind32Local, // win32 info in the database
|
|
LPWIN32_FIND_DATA lpFind32Remote, // if NULL, the remote doesn't exist
|
|
int *lpiShadowStatus,
|
|
int *lpiFileStatus,
|
|
unsigned *lpuAction
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
As the name sugggests, the routine find out what changes have occurred on the local replica
|
|
and whether there is a conflict with the original on the remote.
|
|
|
|
Arguments:
|
|
|
|
lpSI shadow info
|
|
|
|
lpFind32Local win32 info in the database
|
|
|
|
lpFind32Remote win32 info for the original if NULL, the original doesn't exist
|
|
|
|
lpiShadowStatus status of local replica returned
|
|
|
|
lpiFileStatus status of remote replica returned
|
|
|
|
lpuAction Action to be performed to do the merge returned
|
|
|
|
|
|
Returns:
|
|
|
|
-
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
int iShadowStatus=SI_UNCHANGED, iFileStatus=SI_UNCHANGED;
|
|
unsigned int uAction=RAIA_TOOUT;
|
|
|
|
|
|
if(mShadowDeleted(lpSI->uStatus)){
|
|
iShadowStatus=SI_DELETED;
|
|
}
|
|
|
|
if(lpSI->uStatus & (SHADOW_DIRTY|SHADOW_TIME_CHANGE|SHADOW_ATTRIB_CHANGE)){
|
|
iShadowStatus=SI_CHANGED;
|
|
}
|
|
|
|
if(mShadowLocallyCreated(lpSI->uStatus)){
|
|
iShadowStatus=SI_NEW;
|
|
}
|
|
|
|
// no one should be calling this if there have been no offline changes
|
|
Assert(iShadowStatus != SI_UNCHANGED);
|
|
|
|
if(!lpFind32Remote){ // does the remote exist?
|
|
// No
|
|
// if the shadow was not locally created then it must have vanished from the share
|
|
if(iShadowStatus != SI_NEW) {
|
|
iFileStatus=SI_DELETED;
|
|
uAction = RAIA_MERGE;
|
|
ReintKdPrint(MERGE, ("<%ls> deleted at some stage\n", lpFind32Local->cFileName));
|
|
}
|
|
else {
|
|
// we mark the outside as not existing. We also have to
|
|
// create a file locally to get the insert to work right...
|
|
// don't forget to kill it later.
|
|
iFileStatus=SI_NOEXIST;
|
|
ReintKdPrint(MERGE, ("<%ls> will be created\n", lpFind32Local->cFileName));
|
|
}
|
|
}
|
|
else {
|
|
// check to see if server version has been touched
|
|
// NB the last accesstime field of the lpFind32Local contains the replica time
|
|
|
|
if ((lpFind32Local->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
!= (lpFind32Remote->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
// dir became file or vice versa
|
|
iFileStatus=SI_CHANGED;
|
|
uAction = RAIA_MERGE;
|
|
}
|
|
else
|
|
{
|
|
if(CompareTimesAtDosTimePrecision(lpFind32Remote->ftLastWriteTime, //dst
|
|
lpFind32Local->ftLastAccessTime)) //src , does (dst-src)
|
|
{
|
|
// the timestamps don't match
|
|
|
|
// mark the remote as changed only if it is a file
|
|
// will do the directories quitely
|
|
if (!(lpFind32Remote->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
iFileStatus=SI_CHANGED;
|
|
uAction = RAIA_MERGE;
|
|
ReintKdPrint(MERGE, ("<%ls> will be merged\n", lpFind32Local->cFileName));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*lpiShadowStatus = iShadowStatus;
|
|
*lpiFileStatus = iFileStatus;
|
|
*lpuAction = uAction;
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
PRIVATE
|
|
PerformOneReint(
|
|
HANDLE hShadowDB,
|
|
LPSECURITYINFO pShareSecurityInfo,
|
|
_TCHAR * lpszDrive, // drive mapped to the UNC name of lpSI->hShare
|
|
_TCHAR * lptzFullPath, // full UNC path
|
|
LPCOPYPARAMS lpCP, // copy parameters
|
|
LPSHADOWINFO lpSI, // shadowinfo structure
|
|
LPWIN32_FIND_DATA lpFind32Local, // local win32 data
|
|
LPWIN32_FIND_DATA lpFind32Remote, // remote win32 data, could be NULL
|
|
DWORD dwErrorRemoteFind32,// error code while getting remote win32 data
|
|
int iShadowStatus, // local copy status
|
|
int iFileStatus, // remote file status
|
|
unsigned uAction, // action to be taken
|
|
DWORD dwFileSystemFlags, // CODE.IMPROVEMENT, why not just pass down REINT_INFO
|
|
ULONG ulPrincipalID,
|
|
LPCSCPROC lpfnMergeProgress, // instead of the three parameters?
|
|
DWORD_PTR dwContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Merges a filesystem object by calling the routine for the appropriate type of FS object.
|
|
We implement only files and directories for NT5. Also does callbacks for the UI.
|
|
|
|
Arguments:
|
|
|
|
hShadowDB Shadow Database handle
|
|
|
|
lpszDrive drive mapped to the UNC name of lpSI->hShare
|
|
|
|
lptzFullPath full UNC path
|
|
|
|
lpCP copy parameters containing various paths for the object being merged
|
|
|
|
lpSI shadowinfo structure for the object being merged
|
|
|
|
lpFind32Local local win32 data for the object being merged
|
|
|
|
lpFind32Remote remote win32 data for the object being merged, could be NULL
|
|
|
|
iShadowStatus local copy status
|
|
|
|
iFileStatus remote file status
|
|
|
|
uAction action to be taken
|
|
|
|
dwFileSystemFlags remote filesystem
|
|
|
|
ulPrincipalID principal ID in order to skip selectively
|
|
|
|
lpfnMergeProgress callback function
|
|
|
|
dwContext callback context
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError, dwRet;
|
|
|
|
dwError = NO_ERROR;
|
|
|
|
ReintKdPrint(
|
|
MERGE,
|
|
("++++++++PerformOneReint: %s (%08x) %d %d perform:\n",
|
|
lptzFullPath,
|
|
lpSI->hShadow,
|
|
iShadowStatus,
|
|
iFileStatus));
|
|
|
|
if (lpfnMergeProgress)
|
|
{
|
|
ULONG uStatus = lpSI->uStatus;
|
|
DWORD dwsav0, dwsav1;
|
|
|
|
// if there is an error in getting remote find32, then
|
|
// don't tell any conflicts to the callback, because we don't want to show any UI
|
|
if (dwErrorRemoteFind32 != NO_ERROR)
|
|
{
|
|
iFileStatus = SI_CHANGED;
|
|
uAction = RAIA_TOOUT;
|
|
}
|
|
|
|
// if this is a file, check whether access is allowed for this user
|
|
if (!(lpFind32Local->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
BOOL fRet;
|
|
|
|
Assert(dwError == NO_ERROR);
|
|
Assert(ulPrincipalID != CSC_INVALID_PRINCIPAL_ID);
|
|
|
|
dwsav0 = lpFind32Local->dwReserved0;
|
|
dwsav1 = lpFind32Local->dwReserved1;
|
|
|
|
fRet = GetCSCAccessMaskForPrincipalEx(
|
|
ulPrincipalID,
|
|
lpSI->hDir,
|
|
lpSI->hShadow,
|
|
&uStatus,
|
|
&lpFind32Local->dwReserved0,
|
|
&lpFind32Local->dwReserved1);
|
|
|
|
//
|
|
// Adjust user and guest permissions based on share security, if
|
|
// we have such info.
|
|
//
|
|
if (pShareSecurityInfo != NULL) {
|
|
ULONG i;
|
|
ULONG GuestIdx = CSC_MAXIMUM_NUMBER_OF_CACHED_PRINCIPAL_IDS;
|
|
ULONG UserIdx = CSC_MAXIMUM_NUMBER_OF_CACHED_PRINCIPAL_IDS;
|
|
|
|
//
|
|
// Find the user's and guest's entries
|
|
//
|
|
for (i = 0; i < CSC_MAXIMUM_NUMBER_OF_CACHED_PRINCIPAL_IDS; i++) {
|
|
if (pShareSecurityInfo[i].ulPrincipalID == ulPrincipalID)
|
|
UserIdx = i;
|
|
if (pShareSecurityInfo[i].ulPrincipalID == CSC_GUEST_PRINCIPAL_ID)
|
|
GuestIdx = i;
|
|
}
|
|
//
|
|
// Only work with share perms if we found a guest perm in the list
|
|
//
|
|
if (GuestIdx < CSC_MAXIMUM_NUMBER_OF_CACHED_PRINCIPAL_IDS) {
|
|
if (UserIdx >= CSC_MAXIMUM_NUMBER_OF_CACHED_PRINCIPAL_IDS)
|
|
UserIdx = GuestIdx;
|
|
//
|
|
// Logical AND the share perms with the file perms - to prevent
|
|
// ACCESS_DENIED errors on files which a user has access to via
|
|
// file perms, but share perms deny such access.
|
|
//
|
|
lpFind32Local->dwReserved0 &= pShareSecurityInfo[UserIdx].ulPermissions;
|
|
lpFind32Local->dwReserved1 &= pShareSecurityInfo[GuestIdx].ulPermissions;
|
|
}
|
|
}
|
|
|
|
if (!fRet)
|
|
{
|
|
dwError = GetLastError();
|
|
ReintKdPrint(MERGE, ("Failed to get accessmask Error=%d \n", dwError));
|
|
lpFind32Local->dwReserved0 = dwsav0;
|
|
lpFind32Local->dwReserved1 = dwsav1;
|
|
goto bailout;
|
|
}
|
|
else
|
|
{
|
|
Assert((uStatus & ~FLAG_CSC_ACCESS_MASK) == lpSI->uStatus);
|
|
ReintKdPrint(MERGE, ("PerformOneReint: Status with mask 0x%x\n",uStatus));
|
|
}
|
|
}
|
|
|
|
try{
|
|
dwRet = (*lpfnMergeProgress)(
|
|
lptzFullPath,
|
|
uStatus,
|
|
lpSI->ulHintFlags,
|
|
lpSI->ulHintPri,
|
|
lpFind32Local,
|
|
CSCPROC_REASON_BEGIN,
|
|
(uAction == RAIA_MERGE),
|
|
(iFileStatus == SI_DELETED),
|
|
dwContext
|
|
);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwRet = CSCPROC_RETURN_ABORT;
|
|
}
|
|
|
|
lpFind32Local->dwReserved0 = dwsav0;
|
|
lpFind32Local->dwReserved1 = dwsav1;
|
|
|
|
if (dwRet != CSCPROC_RETURN_CONTINUE)
|
|
{
|
|
// if the guy said abort, we want to quit with the correct error code
|
|
if (dwRet == CSCPROC_RETURN_ABORT)
|
|
{
|
|
dwError = ERROR_OPERATION_ABORTED;
|
|
goto bailout;
|
|
}
|
|
|
|
|
|
if (dwRet == CSCPROC_RETURN_FORCE_INWARD)
|
|
{
|
|
// the remote copy wins
|
|
uAction = RAIA_TOIN;
|
|
}
|
|
else if (dwRet == CSCPROC_RETURN_FORCE_OUTWARD)
|
|
{
|
|
// local copy wins
|
|
#if defined(BITCOPY)
|
|
ReintKdPrint(MERGE, ("CSCPROC_RETURN_FORCE_OUTWARD\n"));
|
|
uAction = RAIA_MERGE;
|
|
#else
|
|
uAction = RAIA_TOOUT;
|
|
#endif // defined(BITCOPY)
|
|
}
|
|
else
|
|
{
|
|
goto bailout;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we are asked to continue, we press on irrespective of whether there is
|
|
// a conflict or not
|
|
|
|
#if defined(BITCOPY)
|
|
ReintKdPrint(MERGE, ("CSCPROC_RETURN_CONTINUE\n"));
|
|
#endif // defined(BITCOPY)
|
|
uAction = RAIA_TOOUT;
|
|
}
|
|
|
|
// if there is an error in getting remote find32, then
|
|
// tell the real error code to the callback
|
|
if (dwErrorRemoteFind32 != NO_ERROR)
|
|
{
|
|
dwError = dwErrorRemoteFind32;
|
|
goto bailout;
|
|
}
|
|
}
|
|
|
|
switch(uAction){
|
|
|
|
case RAIA_MERGE:
|
|
case RAIA_TOOUT:
|
|
ReintKdPrint(MERGE, ((uAction==RAIA_TOOUT)?"RAIA_TOOUT\n":"RAIA_MERGE\n"));
|
|
|
|
if (lpFind32Local->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
|
|
dwError = DoCreateDir(
|
|
hShadowDB,
|
|
lpszDrive,
|
|
lptzFullPath,
|
|
lpCP,
|
|
lpSI,
|
|
lpFind32Local,
|
|
lpFind32Remote,
|
|
iShadowStatus,
|
|
iFileStatus,
|
|
uAction,
|
|
dwFileSystemFlags,
|
|
lpfnMergeProgress,
|
|
dwContext
|
|
);
|
|
}
|
|
else {
|
|
dwError = DoObjectEdit(
|
|
hShadowDB,
|
|
lpszDrive,
|
|
lptzFullPath,
|
|
lpCP,
|
|
lpSI,
|
|
lpFind32Local,
|
|
lpFind32Remote,
|
|
iShadowStatus,
|
|
iFileStatus,
|
|
uAction,
|
|
dwFileSystemFlags,
|
|
lpfnMergeProgress,
|
|
dwContext
|
|
);
|
|
ReintKdPrint(MERGE, ("DoObjectEdit returned 0x%x\n", dwError));
|
|
}
|
|
break;
|
|
|
|
case RAIA_TOIN:
|
|
ReintKdPrint(MERGE, ("RAIA_TOIN\n"));
|
|
|
|
if((!SetShadowInfo(hShadowDB, lpSI->hDir, lpSI->hShadow, NULL, (unsigned long)~(SHADOW_MODFLAGS), SHADOW_FLAGS_AND))
|
|
||!CheckForStalenessAndRefresh(hShadowDB, lpszDrive, lpCP, lptzFullPath, lpSI))
|
|
{
|
|
dwError = GetLastError();
|
|
}
|
|
break;
|
|
|
|
case RAIA_SKIP:
|
|
ReintKdPrint(MERGE, ("RAIA_SKIP\n"));
|
|
break;
|
|
|
|
case RAIA_CONFLICT:
|
|
ReintKdPrint(MERGE, ("RAIA_CONFLICT\n"));
|
|
break;
|
|
|
|
case RAIA_SOMETHING:
|
|
ReintKdPrint(MERGE, ("RAIA_SOMETHING\n"));
|
|
break;
|
|
|
|
case RAIA_NOTHING:
|
|
ReintKdPrint(MERGE, ("RAIA_NOTHING\n"));
|
|
break;
|
|
|
|
case RAIA_ORPHAN:
|
|
ReintKdPrint(MERGE, ("RAIA_ORPHAN\n"));
|
|
break;
|
|
|
|
default:
|
|
ReintKdPrint(MERGE, ("BOGUS!!!!!!!!!!!! %d\n",uAction));
|
|
}
|
|
bailout:
|
|
|
|
if (lpfnMergeProgress)
|
|
{
|
|
try
|
|
{
|
|
dwRet = (*lpfnMergeProgress)(
|
|
lptzFullPath,
|
|
lpSI->uStatus,
|
|
lpSI->ulHintFlags,
|
|
lpSI->ulHintPri,
|
|
lpFind32Local,
|
|
CSCPROC_REASON_END,
|
|
(uAction == RAIA_MERGE),
|
|
dwError,
|
|
dwContext
|
|
);
|
|
ReintKdPrint(MERGE, ("Got %d from callback at CSCPROC_REASON_END\n", dwRet));
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwRet = CSCPROC_RETURN_ABORT;
|
|
}
|
|
if (dwRet == CSCPROC_RETURN_ABORT)
|
|
{
|
|
dwError = ERROR_OPERATION_ABORTED;
|
|
}
|
|
}
|
|
|
|
if (dwError == NO_ERROR) {
|
|
ReintKdPrint(MERGE, ("--------PerformOneReint exit TRUE\n"));
|
|
return TRUE;
|
|
}
|
|
|
|
ReintKdPrint(MERGE, ("--------PerformOneReint exit FALSE (0x%x)\n", dwError));
|
|
SetLastError(dwError);
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
/******************************* Conflict related operations ****************/
|
|
|
|
DWORD
|
|
PRIVATE
|
|
CheckFileConflict(
|
|
LPSHADOWINFO lpSI,
|
|
LPWIN32_FIND_DATA lpFind32Remote
|
|
)
|
|
{
|
|
unsigned long ulStatus = lpSI->uStatus;
|
|
|
|
if (!lpFind32Remote){
|
|
if (!(mShadowLocallyCreated(ulStatus)||mShadowDeleted(ulStatus))){
|
|
return (ERROR_DELETE_CONFLICT);
|
|
}
|
|
else{
|
|
return (NO_ERROR);
|
|
}
|
|
}
|
|
else {
|
|
// Create/Create conflict
|
|
if (mShadowLocallyCreated(ulStatus)){
|
|
return (ERROR_CREATE_CONFLICT);
|
|
}
|
|
|
|
if (lpFind32Remote->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
|
|
if (mShadowDeleted(ulStatus)){
|
|
return (NO_ERROR);
|
|
}
|
|
else{
|
|
return(ERROR_ATTRIBUTE_CONFLICT);
|
|
}
|
|
}
|
|
|
|
if(ChkUpdtStatus(INVALID_HANDLE_VALUE, lpSI->hDir, lpSI->hShadow, lpFind32Remote, &ulStatus) == 0){
|
|
return (GetLastError());
|
|
}
|
|
|
|
if (mShadowConflict(ulStatus)){
|
|
return (ERROR_UPDATE_CONFLICT);
|
|
}
|
|
}
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
|
|
DWORD
|
|
PRIVATE
|
|
InbCreateDir(
|
|
_TCHAR * lpDir,
|
|
DWORD dwAttr
|
|
)
|
|
{
|
|
SECURITY_ATTRIBUTES sSA;
|
|
DWORD dwError = NO_ERROR, dwT;
|
|
|
|
sSA.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sSA.lpSecurityDescriptor = NULL;
|
|
sSA.bInheritHandle = TRUE;
|
|
|
|
if ((dwT = GetFileAttributes(lpDir))==0xffffffff){
|
|
if (!CreateDirectory(lpDir, &sSA)){
|
|
dwError = GetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(dwT & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
dwError = ERROR_FILE_EXISTS; // there is a file by the same name
|
|
}
|
|
}
|
|
if (dwError == NO_ERROR)
|
|
{
|
|
if (dwAttr != 0xffffffff)
|
|
{
|
|
if(!SetFileAttributes(lpDir, dwAttr))
|
|
{
|
|
ReintKdPrint(MERGE, ("Benign error %x \n", GetLastError()));
|
|
}
|
|
}
|
|
}
|
|
return (dwError);
|
|
}
|
|
|
|
|
|
/*********************************** Misc routines **************************/
|
|
|
|
#if defined(BITCOPY)
|
|
int
|
|
PRIVATE
|
|
GetShadowByName(
|
|
HSHADOW hDir,
|
|
_TCHAR * lpName,
|
|
LPWIN32_FIND_DATA lpFind32,
|
|
unsigned long *lpuStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
HSHADOW hShadow;
|
|
memset(lpFind32, 0, sizeof(WIN32_FIND_DATA));
|
|
lstrcpyn(lpFind32->cFileName, lpName, sizeof(lpFind32->cFileName)-1);
|
|
return(GetShadow(INVALID_HANDLE_VALUE, hDir, &hShadow, lpFind32, lpuStatus));
|
|
}
|
|
#endif // defined(BITCOPY)
|
|
|
|
DWORD
|
|
DoSparseFill(
|
|
HANDLE hShadowDB,
|
|
_TCHAR * lpszFullPath,
|
|
_TCHAR * lptzDrive,
|
|
LPSHADOWINFO lpSI,
|
|
WIN32_FIND_DATA *lpFind32,
|
|
LPCOPYPARAMS lpCP,
|
|
BOOL fStalenessCheck,
|
|
ULONG ulPrincipalID,
|
|
LPCSCPROC lpfnProgress,
|
|
DWORD_PTR dwContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = 0xffffffff, dwRet, dwTotal=0, dwTotalSleepTime = 0, cntRetries=0, cntMaxRetries=1;
|
|
BOOL fConnected = FALSE, fIsSlowLink, fDisabledShadowing = FALSE, fAmAgent;
|
|
int cbRead;
|
|
COPYCHUNKCONTEXT CopyChunkContext;
|
|
HANDLE hAnchor = INVALID_HANDLE_VALUE;
|
|
ULONG uStatus;
|
|
|
|
fAmAgent = (GetCurrentThreadId() == vdwCopyChunkThreadId);
|
|
Assert(GetCurrentThreadId() != vdwAgentThreadId);
|
|
|
|
memset(&CopyChunkContext, 0, sizeof(CopyChunkContext));
|
|
CopyChunkContext.handle = INVALID_HANDLE_VALUE;
|
|
|
|
if (!fAmAgent)
|
|
{
|
|
cntMaxRetries = MAX_SPARSE_FILL_RETRIES;
|
|
}
|
|
|
|
ReintKdPrint(FILL, ("cntMaxRetries = %d \r\n", cntMaxRetries));
|
|
|
|
if(!DoShadowMaintenance(hShadowDB, SHADOW_BEGIN_INODE_TRANSACTION))
|
|
{
|
|
return GetLastError();
|
|
}
|
|
|
|
// report the progress
|
|
if (lpfnProgress)
|
|
{
|
|
DWORD dwsav0, dwsav1;
|
|
BOOL fRet;
|
|
|
|
uStatus = lpSI->uStatus;
|
|
|
|
|
|
Assert(ulPrincipalID != CSC_INVALID_PRINCIPAL_ID);
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
dwsav0 = lpFind32->dwReserved0;
|
|
dwsav1 = lpFind32->dwReserved1;
|
|
|
|
fRet = GetCSCAccessMaskForPrincipalEx(ulPrincipalID, lpSI->hDir, lpSI->hShadow, &uStatus, &lpFind32->dwReserved0, &lpFind32->dwReserved1);
|
|
|
|
|
|
if (!fRet)
|
|
{
|
|
dwError = GetLastError();
|
|
ReintKdPrint(BADERRORS, ("DoSparseFill Failed to get accessmask Error=%d\r\n", dwError));
|
|
lpFind32->dwReserved0 = dwsav0;
|
|
lpFind32->dwReserved1 = dwsav1;
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
Assert((uStatus & ~FLAG_CSC_ACCESS_MASK) == lpSI->uStatus);
|
|
}
|
|
|
|
try{
|
|
dwRet = (*lpfnProgress)(
|
|
lpszFullPath,
|
|
uStatus,
|
|
lpSI->ulHintFlags,
|
|
lpSI->ulHintPri,
|
|
lpFind32,
|
|
CSCPROC_REASON_BEGIN,
|
|
0,
|
|
0,
|
|
dwContext
|
|
);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwRet = CSCPROC_RETURN_ABORT;
|
|
}
|
|
|
|
lpFind32->dwReserved0 = dwsav0;
|
|
lpFind32->dwReserved1 = dwsav1;
|
|
|
|
if (dwRet != CSCPROC_RETURN_CONTINUE)
|
|
{
|
|
if (dwRet == CSCPROC_RETURN_ABORT)
|
|
{
|
|
dwError = ERROR_OPERATION_ABORTED;
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (fStalenessCheck)
|
|
{
|
|
ReintKdPrint(FILL, ("Doing staleness check %ls \r\n", lpszFullPath));
|
|
dwError = DoRefresh(hShadowDB, lpCP, lpszFullPath, lpSI, lptzDrive);
|
|
|
|
if (dwError != NO_ERROR)
|
|
{
|
|
ReintKdPrint(ALWAYS, ("Error = %x on refresh for %ls \r\n", dwError, lpszFullPath));
|
|
goto bailout;
|
|
}
|
|
|
|
if (!(lpSI->uStatus & SHADOW_SPARSE) && !(lpFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
HANDLE hFile;
|
|
|
|
|
|
if ( !lpfnProgress || // if this is not UI
|
|
(uStatus & FLAG_CSC_USER_ACCESS_MASK) || // or the user already has a mask
|
|
((uStatus & FLAG_CSC_GUEST_ACCESS_MASK)== // or the guest has full permission
|
|
((FLAG_CSC_READ_ACCESS|FLAG_CSC_WRITE_ACCESS)
|
|
<<FLAG_CSC_GUEST_ACCESS_SHIFT_COUNT)))
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
// open the file to get the access rights for the user
|
|
|
|
hFile = CreateFile(lpszFullPath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
goto bailout;
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
if (!lpSI->hDir || !mShadowIsFile(lpSI->uStatus))
|
|
{
|
|
ReintKdPrint(FILL, ("Done staleness check for directory %ls, quitting \r\n", lpszFullPath));
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
Assert(mShadowIsFile(lpSI->uStatus));
|
|
Assert((lpSI->uStatus & SHADOW_SPARSE));
|
|
|
|
fIsSlowLink = FALSE; // on NT we are always going aggressively
|
|
cbRead = (fIsSlowLink)?FILL_BUF_SIZE_SLOWLINK:FILL_BUF_SIZE_LAN;
|
|
|
|
for (cntRetries=0; cntRetries<cntMaxRetries; ++cntRetries)
|
|
{
|
|
|
|
memset(&CopyChunkContext, 0, sizeof(CopyChunkContext));
|
|
CopyChunkContext.handle = INVALID_HANDLE_VALUE;
|
|
if (fAmAgent)
|
|
{
|
|
CopyChunkContext.dwFlags |= COPYCHUNKCONTEXT_FLAG_IS_AGENT_OPEN;
|
|
}
|
|
|
|
if (!OpenFileWithCopyChunkIntent(hShadowDB, lpszFullPath,
|
|
&CopyChunkContext,
|
|
(fIsSlowLink)?FILL_BUF_SIZE_SLOWLINK
|
|
:FILL_BUF_SIZE_LAN
|
|
)) {
|
|
dwError = GetLastError();
|
|
if(dwError == ERROR_LOCK_VIOLATION)
|
|
{
|
|
if (cntMaxRetries > 1)
|
|
{
|
|
ReintKdPrint(FILL, ("LockViolation, Retrying Sparse filling %ls \r\n", lpszFullPath));
|
|
Sleep(1000);
|
|
continue;
|
|
}
|
|
}
|
|
ReintKdPrint(FILL, ("error %x, OpenCopyChunk failed %ls \r\n", dwError, lpszFullPath));
|
|
goto bailout;
|
|
}
|
|
|
|
do {
|
|
CopyChunkContext.ChunkSize = cbRead;
|
|
|
|
if (FAbortOperation())
|
|
{
|
|
dwError = ERROR_OPERATION_ABORTED;
|
|
goto done;
|
|
}
|
|
|
|
|
|
if((CopyChunk(hShadowDB, lpSI, &CopyChunkContext)) == 0){
|
|
|
|
// NB we break here deliberately in order to get into the outer loop
|
|
// where we will retry the operation
|
|
dwError = GetLastError();
|
|
ReintKdPrint(FILL, ("error %x, CopyChunk failed %ls \r\n", dwError, lpszFullPath));
|
|
break;
|
|
}
|
|
|
|
if (lpfnProgress)
|
|
{
|
|
dwRet = (*lpfnProgress)( lpszFullPath,
|
|
lpSI->uStatus,
|
|
lpSI->ulHintFlags,
|
|
lpSI->ulHintPri,
|
|
lpFind32,
|
|
CSCPROC_REASON_MORE_DATA,
|
|
(DWORD)(CopyChunkContext.LastAmountRead+
|
|
CopyChunkContext.TotalSizeBeforeThisRead), // low dword of bytes transferred
|
|
0,
|
|
dwContext
|
|
);
|
|
|
|
if (dwRet != CSCPROC_RETURN_CONTINUE)
|
|
{
|
|
// once we start copying, any return code
|
|
// other than continue is abort
|
|
dwError = ERROR_OPERATION_ABORTED;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// NB there seems to be a timing window here. The file could get out of ssync
|
|
// by the time we came here and it could have been marked sparse by then
|
|
if (!CopyChunkContext.LastAmountRead) {
|
|
SetShadowInfo(hShadowDB, lpSI->hDir, lpSI->hShadow, NULL, (unsigned long)~SHADOW_SPARSE, SHADOW_FLAGS_AND);
|
|
ReintKdPrint(FILL, ("Done Sparse filling %ls \r\n", lpszFullPath));
|
|
goto success;
|
|
}
|
|
|
|
|
|
}while (TRUE);
|
|
|
|
if (dwError == ERROR_GEN_FAILURE)
|
|
{
|
|
// this might be due to the fact that
|
|
// the guy we were piggybacking on went away
|
|
// Just try a few times
|
|
|
|
ReintKdPrint(FILL, ("Retrying Sparse filling %ls \r\n", lpszFullPath));
|
|
CloseFileWithCopyChunkIntent(hShadowDB, &CopyChunkContext);
|
|
CopyChunkContext.handle = INVALID_HANDLE_VALUE;
|
|
dwError = 0xffffffff;
|
|
continue;
|
|
}
|
|
else if (dwError != NO_ERROR)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Error %x while Sparse filling %ls \r\n", dwError, lpszFullPath));
|
|
goto bailout;
|
|
}
|
|
|
|
}
|
|
|
|
success:
|
|
|
|
dwError = NO_ERROR;
|
|
goto done;
|
|
|
|
bailout:
|
|
|
|
|
|
// if the net is disconnected then put the whole share in the skip queue
|
|
// else put the file in the queue
|
|
if (IsNetDisconnected(dwError))
|
|
{
|
|
#ifdef DEBUG
|
|
EnterSkipQueue(lpSI->hShare, 0, 0, lpszFullPath);
|
|
#else
|
|
EnterSkipQueue(lpSI->hShare, 0, 0);
|
|
#endif //DEBUG
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
EnterSkipQueue(lpSI->hShare, lpSI->hDir, lpSI->hShadow, lpszFullPath);
|
|
#else
|
|
EnterSkipQueue(lpSI->hShare, lpSI->hDir, lpSI->hShadow);
|
|
#endif //DEBUG
|
|
|
|
}
|
|
|
|
ReportLastError();
|
|
|
|
done:
|
|
|
|
if (lpfnProgress)
|
|
{
|
|
dwRet = (*lpfnProgress)( lpszFullPath,
|
|
lpSI->uStatus,
|
|
lpSI->ulHintFlags,
|
|
lpSI->ulHintPri,
|
|
lpFind32,
|
|
CSCPROC_REASON_END,
|
|
(DWORD)(CopyChunkContext.LastAmountRead+
|
|
CopyChunkContext.TotalSizeBeforeThisRead), // low dword of bytes transferred
|
|
dwError, // errorcode
|
|
dwContext
|
|
);
|
|
|
|
if (dwRet == CSCPROC_RETURN_ABORT)
|
|
{
|
|
dwError = ERROR_OPERATION_ABORTED;
|
|
}
|
|
|
|
}
|
|
|
|
if (CopyChunkContext.handle != INVALID_HANDLE_VALUE){
|
|
CloseFileWithCopyChunkIntent(hShadowDB, &CopyChunkContext);
|
|
}
|
|
|
|
if (hAnchor != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hAnchor);
|
|
}
|
|
|
|
DoShadowMaintenance(hShadowDB, SHADOW_END_INODE_TRANSACTION);
|
|
|
|
return (dwError);
|
|
}
|
|
|
|
BOOL
|
|
CheckForStalenessAndRefresh(
|
|
HANDLE hShadowDB,
|
|
_TCHAR *lptzDrive,
|
|
LPCOPYPARAMS lpCP,
|
|
_TCHAR *lpRemoteName,
|
|
LPSHADOWINFO lpSI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
BOOL fDone = FALSE, fDisabledShadowing=FALSE;
|
|
WIN32_FIND_DATA sFind32;
|
|
BOOL fExists = FALSE;
|
|
|
|
// Let us get the latest info
|
|
if (GetRemoteWin32Info(lptzDrive, lpCP, &sFind32, &fExists))
|
|
{
|
|
// If this is a file, update the file status
|
|
if (lpSI->hDir && mShadowIsFile(lpSI->uStatus))
|
|
{
|
|
if (!(lpSI->uStatus & SHADOW_STALE))
|
|
{
|
|
ReintKdPrint(FILL, ("Checking update status for a file %ls\r\n", lpRemoteName));
|
|
// compare the timestamp as obtained from the
|
|
// server with that on the database. If the two are the same
|
|
// the file in our database is still consistent with the one on the server
|
|
// Otherwise the call below will mark it as stale
|
|
if(ChkUpdtStatus( hShadowDB,
|
|
lpSI->hDir,
|
|
lpSI->hShadow,
|
|
&sFind32, &(lpSI->uStatus)) == 0){
|
|
ReintKdPrint(BADERRORS, ("ChkUpdt failed %X \r\n", lpSI->hShadow));
|
|
goto bailout;
|
|
}
|
|
|
|
}
|
|
|
|
if (lpSI->uStatus & SHADOW_STALE)
|
|
{
|
|
// if it changed from being a file, then mark it a orphan
|
|
// else truncate it's data and mark it sparse
|
|
if (IsFile(sFind32.dwFileAttributes))
|
|
{
|
|
ReintKdPrint(FILL, ("File %ls is stale, truncating\r\n", lpRemoteName));
|
|
|
|
if(SetShadowInfo(hShadowDB, lpSI->hDir, lpSI->hShadow, &sFind32, 0, SHADOW_FLAGS_OR|SHADOW_FLAGS_TRUNCATE_DATA))
|
|
{
|
|
lpSI->uStatus &= ~SHADOW_STALE;
|
|
lpSI->uStatus |= SHADOW_SPARSE;
|
|
|
|
fDone = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReintKdPrint(FILL, ("File %ls become directory, marking orphan\r\n", lpRemoteName));
|
|
if(SetShadowInfo(hShadowDB, lpSI->hDir, lpSI->hShadow, NULL, SHADOW_ORPHAN, SHADOW_FLAGS_OR))
|
|
{
|
|
lpSI->uStatus |= SHADOW_ORPHAN;
|
|
|
|
fDone = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fDone = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// NB, we do nothing if a directory changed to file
|
|
// we are letting the scavenging code remove the entries in due course.
|
|
// If one of the descendents of this directory are pinned, then they stay on
|
|
// in the database till the user actually cleans then up.
|
|
// Need a good startegy to warn the user about it.
|
|
|
|
if (!IsFile(sFind32.dwFileAttributes))
|
|
{
|
|
// this is a directory
|
|
// update it's win32 data so that things like attributes get updated
|
|
// We get here only during fullsync operations
|
|
if(SetShadowInfo(hShadowDB, lpSI->hDir, lpSI->hShadow, &sFind32, ~(SHADOW_STALE), SHADOW_FLAGS_AND|SHADOW_FLAGS_CHANGE_83NAME))
|
|
{
|
|
fDone = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
bailout:
|
|
|
|
if (fDisabledShadowing)
|
|
{
|
|
int iEnable;
|
|
|
|
iEnable = EnableShadowingForThisThread(hShadowDB);
|
|
|
|
Assert(iEnable);
|
|
|
|
}
|
|
|
|
if (!fDone)
|
|
{
|
|
ReportLastError();
|
|
|
|
}
|
|
|
|
return (fDone);
|
|
}
|
|
|
|
DWORD
|
|
DWConnectNetEx(
|
|
_TCHAR * lpSharePath,
|
|
_TCHAR * lpOutDrive,
|
|
BOOL fInteractive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NETRESOURCE sNR;
|
|
DWORD dwError;
|
|
_TCHAR szErr[16], szNP[16];
|
|
|
|
if (lpOutDrive){
|
|
lpOutDrive[0]='E'; // Let use start searching from e:
|
|
lpOutDrive[1]=':';
|
|
lpOutDrive[2]=0;
|
|
}
|
|
do{
|
|
memset(&sNR, 0, sizeof(NETRESOURCE));
|
|
sNR.lpRemoteName = lpSharePath;
|
|
if (lpOutDrive){
|
|
if(lpOutDrive[0]=='Z') {
|
|
break;
|
|
}
|
|
sNR.lpLocalName = lpOutDrive;
|
|
}
|
|
sNR.dwType = RESOURCETYPE_DISK;
|
|
dwError = WNetAddConnection3(vhwndMain, &sNR, NULL, NULL, 0);
|
|
if (dwError==WN_SUCCESS){
|
|
break;
|
|
}
|
|
else if (lpOutDrive &&
|
|
((dwError == WN_BAD_LOCALNAME)||
|
|
(dwError == WN_ALREADY_CONNECTED))){
|
|
++lpOutDrive[0];
|
|
continue;
|
|
}
|
|
else{
|
|
if (dwError==WN_EXTENDED_ERROR){
|
|
WNetGetLastError(&dwError, szErr, sizeof(szErr), szNP, sizeof(szNP));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
while (TRUE);
|
|
|
|
if ((dwError == ERROR_SUCCESS) && !IsShareReallyConnected((LPCTSTR)lpSharePath))
|
|
{
|
|
WNetCancelConnection2((lpOutDrive)?lpOutDrive:lpSharePath, 0, FALSE);
|
|
SetLastError(dwError = ERROR_REM_NOT_LIST);
|
|
}
|
|
|
|
return (dwError);
|
|
}
|
|
|
|
/************************** Skip queue related operations *******************/
|
|
|
|
#ifdef DEBUG
|
|
VOID
|
|
EnterSkipQueue(
|
|
HSHARE hShare,
|
|
HSHADOW hDir,
|
|
HSHADOW hShadow,
|
|
_TCHAR * lpPath
|
|
)
|
|
#else
|
|
VOID
|
|
EnterSkipQueue(
|
|
HSHARE hShare,
|
|
HSHADOW hDir,
|
|
HSHADOW hShadow
|
|
)
|
|
#endif //DEBUG
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
LPFAILINFO lpFI = NULL;
|
|
LPFAILINFO FAR * lplpFI;
|
|
|
|
if (!EnterAgentCrit()){
|
|
return;
|
|
}
|
|
|
|
if(lplpFI = LplpFindFailInfo(hShare, hDir, hShadow)){
|
|
lpFI = *lplpFI;
|
|
}
|
|
|
|
if (!lpFI){
|
|
if (lpFI = (LPFAILINFO)AllocMem(sizeof(FAILINFO))){
|
|
lpFI->hShare = hShare;
|
|
lpFI->hDir = hDir;
|
|
lpFI->hShadow = hShadow;
|
|
#ifdef DEBUG
|
|
lstrcpyn(lpFI->rgchPath, lpPath, MAX_SERVER_SHARE_NAME_FOR_CSC);
|
|
#endif //DEBUG
|
|
lpFI->cntFail = 1;
|
|
lpFI->cntMaxFail = (hShadow)?MAX_ATTEMPTS_SHADOW:MAX_ATTEMPTS_SHARE;
|
|
lpFI->lpnextFI = lpheadFI;
|
|
lpheadFI = lpFI;
|
|
}
|
|
}
|
|
|
|
if (lpFI){
|
|
if (lpFI->cntFail >= lpFI->cntMaxFail){
|
|
lpFI->dwFailTime = GetTickCount();
|
|
ReintKdPrint(SKIPQUEUE, ("EnterSkipQueue: Marking %ls for Skipping \r\n", lpPath));
|
|
} else{
|
|
// Increment the fail count
|
|
lpFI->cntFail++;
|
|
ReintKdPrint(SKIPQUEUE, ("EnterSkipQueue: Incementing failcount for %ls \r\n", lpPath));
|
|
}
|
|
}
|
|
LeaveAgentCrit();
|
|
}
|
|
|
|
|
|
BOOL
|
|
PRIVATE
|
|
FSkipObject(
|
|
HSHARE hShare,
|
|
HSHADOW hDir,
|
|
HSHADOW hShadow
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
LPFAILINFO FAR *lplpFI;
|
|
|
|
if (!EnterAgentCrit()){
|
|
return 0;
|
|
}
|
|
|
|
if (lplpFI = LplpFindFailInfo(hShare, hDir, hShadow)){
|
|
if ((*lplpFI)->cntFail >= (*lplpFI)->cntMaxFail) {
|
|
LeaveAgentCrit();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
LeaveAgentCrit();
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
PRIVATE
|
|
PurgeSkipQueue(
|
|
BOOL fAll,
|
|
HSHARE hShare,
|
|
HSHADOW hDir,
|
|
HSHADOW hShadow
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
LPFAILINFO FAR *lplpFI = NULL, lpfiTemp;
|
|
DWORD dwCurTime = GetTickCount();
|
|
int cntUnmark=0;
|
|
|
|
if (!EnterAgentCrit()){
|
|
return 0;
|
|
}
|
|
|
|
for (lplpFI = &lpheadFI; *lplpFI; lplpFI = &((*lplpFI)->lpnextFI)){
|
|
|
|
if (fAll ||
|
|
((dwCurTime - (*lplpFI)->dwFailTime) > WAIT_INTERVAL_SKIP_MS)){
|
|
if ((!hShare || (hShare==(*lplpFI)->hShare))
|
|
&& (!hDir || (hDir==(*lplpFI)->hDir))
|
|
&& (!hShadow || (hShadow==(*lplpFI)->hShadow)))
|
|
{
|
|
ReintKdPrint(SKIPQUEUE, ("PurgeSkipQueue: Purging Skip Queue Entry for %s \r\n"
|
|
,(*lplpFI)->rgchPath));
|
|
lpfiTemp = *lplpFI;
|
|
*lplpFI = lpfiTemp->lpnextFI;
|
|
FreeMem(lpfiTemp);
|
|
++cntUnmark;
|
|
if (!*lplpFI){
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LeaveAgentCrit();
|
|
return (cntUnmark);
|
|
}
|
|
|
|
LPFAILINFO FAR *
|
|
LplpFindFailInfo(
|
|
HSHARE hShare,
|
|
HSHADOW hDir,
|
|
HSHADOW hShadow
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
LPFAILINFO FAR *lplpFI = NULL;
|
|
|
|
// look for the inode or the server entry
|
|
|
|
for (lplpFI = &lpheadFI; *lplpFI; lplpFI = &((*lplpFI)->lpnextFI)) {
|
|
if ((hShadow && (hShadow == (*lplpFI)->hShadow)) ||
|
|
(hShare && ((*lplpFI)->hShare == hShare))){
|
|
return (lplpFI);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ReportLastError(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError;
|
|
|
|
dwError = GetLastError();
|
|
|
|
ReintKdPrint(FILL, ("Error # %ld \r\n", dwError));
|
|
}
|
|
|
|
VOID
|
|
PRIVATE
|
|
ReportStats(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
ReintKdPrint(BADERRORS, ("dirty=%d stale=%d sparse=%d \r\n"
|
|
, vcntDirty
|
|
, vcntStale
|
|
, vcntSparse));
|
|
}
|
|
|
|
|
|
VOID
|
|
PRIVATE
|
|
CopyPQInfoToShadowInfo(
|
|
LPPQPARAMS lpPQ,
|
|
LPSHADOWINFO lpShadowInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
lpShadowInfo->hShare = lpPQ->hShare;
|
|
lpShadowInfo->hDir = lpPQ->hDir;
|
|
lpShadowInfo->hShadow = lpPQ->hShadow;
|
|
lpShadowInfo->uStatus = lpPQ->ulStatus; //Sic
|
|
}
|
|
|
|
int
|
|
PUBLIC
|
|
EnterAgentCrit(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
if (!vhMutex){
|
|
return 0;
|
|
}
|
|
WaitForSingleObject(vhMutex, INFINITE);
|
|
return 1;
|
|
}
|
|
|
|
VOID
|
|
PUBLIC
|
|
LeaveAgentCrit(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
ReleaseMutex(vhMutex);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FGetConnectionList(
|
|
LPCONNECTINFO *lplpHead,
|
|
int *lpcntDiscon
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
return (FGetConnectionListEx(lplpHead, NULL, FALSE, FALSE, lpcntDiscon));
|
|
}
|
|
|
|
BOOL
|
|
FGetConnectionListEx(
|
|
LPCONNECTINFO *lplpHead,
|
|
LPCTSTR lptzShareName,
|
|
BOOL fAllSharesOnServer,
|
|
BOOL fServerIsOffline,
|
|
int *lpcntDiscon
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine makes a list of shares that are connected and are in disconnected state.
|
|
If lptzShareName is not NULL it returns all the mapping of that share that are
|
|
in disconnected state.
|
|
|
|
This is the first of a trilogy of routines used while doing a merge. The other two are
|
|
DisconnectList and ReconnectList.
|
|
|
|
Arguments:
|
|
|
|
lplpHead head of the list is created here.
|
|
|
|
lptzShareName list of connections for this share, if NULL, list of all connected shares
|
|
|
|
lpcntDiscon # of shares in the list. Can be NULL.
|
|
|
|
Returns:
|
|
|
|
TRUE if there are some entries in the connection list
|
|
|
|
Notes:
|
|
|
|
List is allocated using LocalAlloc. It is upto the caller to free it.
|
|
|
|
--*/
|
|
{
|
|
HANDLE hEnum;
|
|
DWORD cbNum, cbSize, dwError, dwDummy, len=0;
|
|
LPCONNECTINFO lpCI;
|
|
WIN32_FIND_DATA sFind32;
|
|
|
|
|
|
ReintKdPrint(MERGE, ("Getting conection list\r\n"));
|
|
|
|
try
|
|
{
|
|
if (lpcntDiscon){
|
|
*lpcntDiscon = 0;
|
|
}
|
|
|
|
*lplpHead = NULL;
|
|
|
|
if (lptzShareName)
|
|
{
|
|
len = lstrlen(lptzShareName);
|
|
if (fAllSharesOnServer)
|
|
{
|
|
_TCHAR *lpT, chT;
|
|
|
|
len = 2;
|
|
for (lpT = (LPTSTR)lptzShareName+2;;)
|
|
{
|
|
chT = *lpT++;
|
|
|
|
Assert(chT);
|
|
if (chT == (_TCHAR)'\\')
|
|
{
|
|
break;
|
|
}
|
|
|
|
++len;
|
|
}
|
|
ReintKdPrint(MERGE, ("Nuking shares %ls len %d \n", (LPTSTR)lptzShareName, len));
|
|
}
|
|
}
|
|
|
|
// enumerate all connected shares
|
|
if (WNetOpenEnum( RESOURCE_CONNECTED,
|
|
RESOURCETYPE_DISK, RESOURCEUSAGE_CONNECTABLE,
|
|
NULL, &hEnum) == NO_ERROR ){
|
|
do{
|
|
cbNum = 1;
|
|
cbSize = sizeof(dwDummy);
|
|
dwError = WNetEnumResource(hEnum, &cbNum, &dwDummy, &cbSize);
|
|
|
|
if (dwError==ERROR_MORE_DATA){
|
|
|
|
if (lpCI =
|
|
(LPCONNECTINFO)AllocMem(sizeof(CONNECTINFO)+cbSize)){
|
|
cbNum = 1;
|
|
dwError = WNetEnumResource(hEnum, &cbNum
|
|
, &(lpCI->rgFill[0])
|
|
, &cbSize);
|
|
|
|
if (!cbNum || (dwError!=NO_ERROR)){
|
|
FreeMem(lpCI);
|
|
break;
|
|
}
|
|
if(lptzShareName)
|
|
{
|
|
|
|
// do case insensitive prefix matching and ensure that
|
|
// the next character after the match is a path delimiter
|
|
|
|
if (!((CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
|
|
lptzShareName, len,
|
|
((NETRESOURCE *)&(lpCI->rgFill[0]))->lpRemoteName,len)
|
|
== CSTR_EQUAL)&&
|
|
((((NETRESOURCE *)&(lpCI->rgFill[0]))->lpRemoteName[len] == (_TCHAR)'\\')||
|
|
(((NETRESOURCE *)&(lpCI->rgFill[0]))->lpRemoteName[len] == (_TCHAR)0))))
|
|
{
|
|
FreeMem(lpCI);
|
|
continue;
|
|
}
|
|
|
|
ReintKdPrint(MERGE, ("Got %ls on %ls\r\n"
|
|
, (((NETRESOURCE *)&(lpCI->rgFill[0]))->lpLocalName)?((NETRESOURCE *)&(lpCI->rgFill[0]))->lpLocalName:L"Empty"
|
|
, ((NETRESOURCE *)&(lpCI->rgFill[0]))->lpRemoteName));
|
|
|
|
}
|
|
lpCI->lpnextCI = *lplpHead;
|
|
*lplpHead = lpCI;
|
|
|
|
if (!fServerIsOffline)
|
|
{
|
|
BOOL fRet;
|
|
SHADOWINFO sSI;
|
|
fRet = FindCreateShadowFromPath(((NETRESOURCE *)&(lpCI->rgFill[0]))->lpRemoteName,
|
|
FALSE, // Don't create, just look
|
|
&sFind32,
|
|
&sSI,
|
|
NULL
|
|
);
|
|
lpCI->uStatus = 0;
|
|
if (fRet && sSI.hShadow)
|
|
{
|
|
lpCI->uStatus = sSI.uStatus;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lpCI->uStatus |= SHARE_DISCONNECTED_OP;
|
|
}
|
|
if (lpcntDiscon && (lpCI->uStatus & SHARE_DISCONNECTED_OP)){
|
|
++*lpcntDiscon;
|
|
}
|
|
}
|
|
else{
|
|
//PANIC
|
|
break;
|
|
}
|
|
}
|
|
else{
|
|
break;
|
|
}
|
|
}while (TRUE);
|
|
WNetCloseEnum(hEnum);
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Took exception in FGetConnectionListEx list \n"));
|
|
}
|
|
|
|
return (*lplpHead != NULL);
|
|
}
|
|
|
|
int
|
|
DisconnectList(
|
|
LPCONNECTINFO *lplpHead,
|
|
LPFNREFRESHPROC lpfn,
|
|
DWORD dwCookie
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
disconnects all drive mapped shares in a list accumulated using FGetConnectionList
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
BOOL fOk = TRUE;
|
|
DWORD dwError;
|
|
int icntDriveMapped=0;
|
|
LPCONNECTINFO lpTmp = *lplpHead;
|
|
|
|
ReintKdPrint(MERGE, ("In DisconnectList \n"));
|
|
|
|
try
|
|
{
|
|
for (;lpTmp;lpTmp = lpTmp->lpnextCI){
|
|
|
|
if (!(lpTmp->uStatus & SHARE_DISCONNECTED_OP))
|
|
{
|
|
continue;
|
|
}
|
|
if (((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpLocalName){
|
|
|
|
++icntDriveMapped;
|
|
|
|
ReintKdPrint(MERGE, ("Nuking %ls on %ls\r\n"
|
|
, ((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpLocalName
|
|
, ((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpRemoteName));
|
|
dwError = WNetCancelConnection2( ((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpLocalName, 0, TRUE);
|
|
|
|
}
|
|
else{
|
|
ReintKdPrint(MERGE, ("Nuking %ls \r\n" , ((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpRemoteName));
|
|
dwError = WNetCancelConnection2(
|
|
((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpRemoteName
|
|
, 0
|
|
, TRUE);
|
|
}
|
|
|
|
if (dwError != NO_ERROR){
|
|
ReintKdPrint(BADERRORS, ("Error=%ld \r\n", dwError));
|
|
fOk = FALSE;
|
|
}
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Took exception in Disconnecte list \n"));
|
|
fOk = FALSE;
|
|
}
|
|
|
|
ReintKdPrint(MERGE, ("Out DisconnectList %x\n", (fOk?icntDriveMapped:-1)));
|
|
return (fOk?icntDriveMapped:-1);
|
|
}
|
|
|
|
int
|
|
CALLBACK
|
|
RefreshProc(
|
|
LPCONNECTINFO lpCI,
|
|
DWORD dwCookie // LOWORD 0==Silently, 1== Give messages
|
|
// HIWORD 0==Nuke UNC, 1==Nuke all if no ongoing open/finds
|
|
// 2==Maximum force for shadow 3==Nuke ALL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
WORD wVerbose = LOWORD(dwCookie), wForce = HIWORD(dwCookie);
|
|
int iRet = 0;
|
|
BOOL fDisconnectedOp=FALSE, fOpensFinds = FALSE;
|
|
|
|
|
|
fDisconnectedOp = (lpCI->uStatus & SHARE_DISCONNECTED_OP);
|
|
fOpensFinds = (lpCI->uStatus & (SHARE_FILES_OPEN|SHARE_FINDS_IN_PROGRESS));
|
|
|
|
switch (wForce){
|
|
case 0://shadow UNC connections with no opens/finds in progress
|
|
iRet = (fDisconnectedOp && !fOpensFinds && !((NETRESOURCE *)&(lpCI->rgFill[0]))->lpLocalName)?1:0;
|
|
break;
|
|
case 1://shadow connections (UNC+drivemapped) with no opens/finds in progress
|
|
iRet = (fDisconnectedOp && !fOpensFinds)?1:0;
|
|
break;
|
|
case 2://shadow connections with or without opens/finds
|
|
iRet = (fDisconnectedOp)?1:0;
|
|
break;
|
|
case 3://all connections
|
|
iRet = 1;
|
|
break;
|
|
}
|
|
if ((iRet==1) && wVerbose && fOpensFinds){
|
|
LoadString(vhinstCur, IDS_OPS_IN_PROGRESS, (LPTSTR)(vrgchBuff), 128 * sizeof(TCHAR));
|
|
LoadString(vhinstCur, IDS_SHADOW_AGENT, (LPTSTR)(vrgchBuff+128* sizeof(TCHAR)), 128* sizeof(TCHAR));
|
|
wsprintf((LPTSTR)(vrgchBuff+256), (LPTSTR)(vrgchBuff), ((NETRESOURCE *)&(lpCI->rgFill[0]))->lpRemoteName);
|
|
MessageBox(vhwndMain, (LPTSTR)(vrgchBuff+256* sizeof(TCHAR)), (LPTSTR)(vrgchBuff+128* sizeof(TCHAR)), MB_OK);
|
|
}
|
|
|
|
return (iRet);
|
|
}
|
|
|
|
|
|
//
|
|
// Reconnects a list of shares
|
|
// if you pass in a parent HWND then you will get UI
|
|
//
|
|
int
|
|
ReconnectList(
|
|
LPCONNECTINFO *lplpHead,
|
|
HWND hwndParent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
reconnects all the connections in disconnected state.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
int iDone = 0;
|
|
DWORD dwError;
|
|
LPCONNECTINFO lpTmp = *lplpHead;
|
|
_TCHAR * lpSave;
|
|
HWND hwndUI=NULL;
|
|
|
|
try
|
|
{
|
|
for (;lpTmp;lpTmp = lpTmp->lpnextCI){
|
|
|
|
if (!(lpTmp->uStatus & SHARE_DISCONNECTED_OP))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpLocalName){
|
|
ReintKdPrint(MERGE, ("Adding back %ls on %ls\r\n"
|
|
, ((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpLocalName
|
|
, ((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpRemoteName));
|
|
|
|
}
|
|
else{
|
|
ReintKdPrint(MERGE, ((LPSTR)vrgchBuff, "Adding back %ls\r\n" , ((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpRemoteName));
|
|
}
|
|
|
|
lpSave = ((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpProvider;
|
|
((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpProvider = NULL;
|
|
dwError = WNetAddConnection3(vhwndMain, (NETRESOURCE *)&(lpTmp->rgFill[0]), NULL, NULL, CONNECT_INTERACTIVE);
|
|
((NETRESOURCE *)&(lpTmp->rgFill[0]))->lpProvider = lpSave;
|
|
if (dwError!=NO_ERROR){
|
|
|
|
ReintKdPrint(BADERRORS, ("Error=%ld \r\n", dwError));
|
|
iDone = -1;
|
|
}
|
|
else if (iDone >= 0){
|
|
++iDone;
|
|
}
|
|
}
|
|
|
|
if( hwndUI ){
|
|
|
|
DestroyWindow(hwndUI);
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Took exception in Reconnect list \n"));
|
|
iDone = 0;
|
|
}
|
|
return (iDone);
|
|
}
|
|
|
|
VOID
|
|
ClearConnectionList(
|
|
LPCONNECTINFO *lplpHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
LPCONNECTINFO lpTmp = *lplpHead, lpSave;
|
|
for (;lpTmp;){
|
|
lpSave = lpTmp->lpnextCI;
|
|
FreeMem(lpTmp);
|
|
lpTmp = lpSave;
|
|
}
|
|
*lplpHead = NULL;
|
|
}
|
|
|
|
BOOL
|
|
PRIVATE
|
|
IsSlowLink(
|
|
_TCHAR * lpPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NETRESOURCE sNR;
|
|
NETCONNECTINFOSTRUCT sNCINFO;
|
|
int done = 0;
|
|
BOOL fRet = FALSE;
|
|
|
|
memset(&sNCINFO, 0, sizeof(NETCONNECTINFOSTRUCT));
|
|
sNCINFO.cbStructure = sizeof(NETCONNECTINFOSTRUCT);
|
|
memset(&sNR, 0, sizeof(NETRESOURCE));
|
|
sNR.lpRemoteName=lpPath;
|
|
if ((MultinetGetConnectionPerformance(&sNR, &sNCINFO)==WN_SUCCESS)
|
|
&& (sNCINFO.dwSpeed < SLOWLINK_SPEED)){
|
|
fRet = TRUE;
|
|
}
|
|
|
|
return (fRet);
|
|
}
|
|
|
|
|
|
int RefreshConnectionsInternal(
|
|
int force,
|
|
BOOL verbose
|
|
)
|
|
{
|
|
return (RefreshConnectionsEx(force, verbose, NULL, 0));
|
|
}
|
|
|
|
|
|
int
|
|
BreakConnectionsInternal(
|
|
int force,
|
|
BOOL verbose
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
LPCONNECTINFO lpHead = NULL;
|
|
if (FGetConnectionList(&lpHead, NULL)){
|
|
DisconnectList(&lpHead, RefreshProc, MAKELONG(verbose,force));
|
|
ClearConnectionList(&lpHead);
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
//
|
|
// This refreshes all the connections.
|
|
// Force -
|
|
// Verbose - causes annoying UI to be displayed
|
|
// lpfn -
|
|
// dwCookie - parameter for lpfn
|
|
//
|
|
int RefreshConnectionsEx(
|
|
int force,
|
|
BOOL verbose,
|
|
LPFNREFRESHEXPROC lpfn,
|
|
DWORD dwCookie)
|
|
{
|
|
int cntDriveMapped, iRet = -1;
|
|
LPCONNECTINFO lpHead = NULL;
|
|
|
|
if (FGetConnectionList(&lpHead, NULL)){
|
|
cntDriveMapped = DisconnectList(&lpHead, RefreshProc, MAKELONG(verbose,force));
|
|
if (cntDriveMapped < 0){
|
|
goto bailout;
|
|
}
|
|
if (lpfn){
|
|
(*lpfn)(cntDriveMapped, dwCookie);
|
|
}
|
|
if (cntDriveMapped > 0){
|
|
ReconnectList(&lpHead,verbose?vhwndMain:NULL);
|
|
}
|
|
ClearConnectionList(&lpHead);
|
|
iRet = 1;
|
|
}
|
|
else
|
|
{
|
|
iRet = 0;
|
|
}
|
|
|
|
bailout:
|
|
|
|
return iRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FCheckAncestor(
|
|
node *lpnodeList,
|
|
LPCOPYPARAMS lpCP
|
|
)
|
|
{
|
|
node *lpItem;
|
|
BOOL fHaveAncestor = FALSE;
|
|
unsigned lenDest;
|
|
#ifdef DEBUG
|
|
unsigned lenSrc;
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
lenSrc = lstrlen(lpCP->lpRemotePath);
|
|
#endif
|
|
for(lpItem = lpnodeList; lpItem; lpItem = lpItem->next)
|
|
{
|
|
// is it on the same share?
|
|
if (!lstrcmpi(lpItem->lpCP->lpSharePath, lpCP->lpSharePath))
|
|
{
|
|
// check upto the length of the ancestor. By definition he is supposed to be smaller
|
|
// than the src
|
|
lenDest = lstrlen(lpItem->lpCP->lpRemotePath);
|
|
Assert(lenDest <= lenSrc);
|
|
|
|
// NB, we do memcmp because, the strings will have the same case
|
|
// as they are obtained from the same source, ie, the CSC database.
|
|
|
|
// is it a child of any item in the list?
|
|
if (!memcmp(lpItem->lpCP->lpRemotePath, lpCP->lpRemotePath, lenDest * sizeof(_TCHAR)))
|
|
{
|
|
fHaveAncestor = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (fHaveAncestor);
|
|
}
|
|
|
|
DWORD
|
|
PRIVATE
|
|
GetUniqueName(
|
|
_TCHAR * lpName,
|
|
_TCHAR * lpUniqueName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
int i=0, orglen;
|
|
DWORD dwError;
|
|
_TCHAR buff[10 * sizeof(_TCHAR)];
|
|
|
|
lstrcpy(lpUniqueName, lpName);
|
|
|
|
orglen = lstrlen(lpName);
|
|
|
|
if (orglen >= MAX_PATH-1){
|
|
lpUniqueName[MAX_PATH-5] = 0;
|
|
orglen = MAX_PATH-5;
|
|
}
|
|
for (i=0; i<100; ++i){
|
|
if (GetFileAttributes(lpUniqueName)==0xffffffff){
|
|
dwError = GetLastError();
|
|
if ((dwError==ERROR_FILE_NOT_FOUND)||
|
|
(dwError == ERROR_PATH_NOT_FOUND)){
|
|
break;
|
|
}
|
|
}
|
|
lpUniqueName[orglen] = 0;
|
|
wsprintf(buff, _TEXT("(%2d)"), i);
|
|
lstrcat(lpUniqueName, (LPTSTR)buff);
|
|
}
|
|
if (i < 100){
|
|
dwError = NO_ERROR;
|
|
}
|
|
else{
|
|
dwError = 0xffffffff;
|
|
}
|
|
return(dwError);
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef MAYBE_USEFUL
|
|
|
|
/***************************************************************************
|
|
* reintegrate one server.
|
|
*/
|
|
//
|
|
// Pass in the Share to merge on
|
|
// and the parent window.
|
|
//
|
|
BOOL
|
|
PUBLIC
|
|
ReintOneShare(
|
|
HSHARE hShare,
|
|
HWND hwndParent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
node *lpnodeInsertList=NULL; // merge file list.
|
|
PQPARAMS sPQP;
|
|
int state, iFileStatus, iShadowStatus;
|
|
BOOL fConnected=FALSE, fBeginReint=FALSE, fDone = FALSE;
|
|
BOOL fStamped = FALSE, fInsertInList = FALSE;
|
|
SHADOWINFO sSI;
|
|
LPCOPYPARAMS lpCP = NULL;
|
|
_TCHAR szDrive[3];
|
|
unsigned long ulStatus, uAction;
|
|
DWORD dwError;
|
|
WIN32_FIND_DATA sFind32Local, sFind32Remote;
|
|
WIN32_FIND_DATA *lpFind32Remote = NULL; // temporary variable
|
|
HANDLE hShadowDB;
|
|
BOOL fAmAgent=FALSE;
|
|
|
|
if ((hShadowDB = OpenShadowDatabaseIO()) ==INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
fAmAgent = (GetCurrentThreadId() == vdwCopyChunkThreadId);
|
|
|
|
lpCP = LpAllocCopyParams();
|
|
|
|
if (!lpCP){
|
|
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare():Allocation of copyparam buffer failed \r\n"));
|
|
|
|
goto bailout;
|
|
}
|
|
|
|
// Reint in multiple passes. Do directories first, files next
|
|
for (state=STATE_REINT_BEGIN;state<STATE_REINT_END;++state) {
|
|
if (FAbortOperation())
|
|
{
|
|
goto bailout;
|
|
}
|
|
dwError = NO_ERROR;
|
|
memset(&sPQP, 0, sizeof(PQPARAMS));
|
|
memset(&sSI, 0, sizeof(SHADOWINFO));
|
|
|
|
// Start looking through the queue
|
|
if(BeginPQEnum(hShadowDB, &sPQP) == 0) {
|
|
goto bailout;
|
|
}
|
|
// Start looking through the queue
|
|
do {
|
|
|
|
if (FAbortOperation())
|
|
{
|
|
goto bailout;
|
|
}
|
|
|
|
if(PrevPriShadow(hShadowDB, &sPQP) == 0){
|
|
break;
|
|
}
|
|
// end of this enumeration
|
|
if (!sPQP.hShadow){
|
|
break;
|
|
}
|
|
|
|
if ( mShadowOrphan(sPQP.ulStatus)||
|
|
mShadowSuspect(sPQP.ulStatus))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// keep going if this is a file and we are trying to reintegrate directories
|
|
// or if this entry isn't from the server we are dealing with.
|
|
if ((sPQP.hShare != hShare) ||
|
|
((state != STATE_REINT_FILES) && mShadowIsFile(sPQP.ulStatus)) ||
|
|
((state == STATE_REINT_FILES) && !mShadowIsFile(sPQP.ulStatus))){
|
|
continue;
|
|
}
|
|
|
|
|
|
if (mShadowNeedReint(sPQP.ulStatus)){
|
|
|
|
|
|
if (!fStamped){
|
|
|
|
StampReintLog();
|
|
fStamped = TRUE;
|
|
}
|
|
|
|
if (fAmAgent && FSkipObject(sPQP.hShare, 0, 0)){
|
|
continue;
|
|
}
|
|
|
|
if (!GetShadowInfo(hShadowDB, sPQP.hDir, sPQP.hShadow,
|
|
&sFind32Local, &ulStatus)){
|
|
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: GetShadowInfo failed\n"));
|
|
continue;
|
|
}
|
|
|
|
CopyPQInfoToShadowInfo(&sPQP, &sSI);
|
|
|
|
sSI.lpFind32 = &sFind32Local;
|
|
|
|
if(!GetUNCPath(hShadowDB, sPQP.hShare, sPQP.hDir, sPQP.hShadow, lpCP)){
|
|
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: GetUNCPath failed\n"));
|
|
continue;
|
|
}
|
|
if (!fConnected){
|
|
DWORD dwError2;
|
|
dwError2 = DWConnectNetEx(lpCP->lpSharePath, szDrive, TRUE);
|
|
if(dwError2 == WN_SUCCESS || dwError2 == WN_CONNECTED_OTHER_PASSWORD_DEFAULT)
|
|
{
|
|
fConnected = TRUE;
|
|
|
|
if (!BeginReint(hShadowDB, hShare)) {
|
|
goto bailout;
|
|
}
|
|
|
|
fBeginReint = TRUE;
|
|
}
|
|
else{
|
|
#ifdef DEBUG
|
|
EnterSkipQueue(sPQP.hShare, 0, 0, lpCP->lpSharePath);
|
|
#else
|
|
EnterSkipQueue(sPQP.hShare, 0, 0);
|
|
#endif //DEBUG
|
|
// try some other server for reintegration
|
|
goto bailout;
|
|
}
|
|
}
|
|
|
|
ReintKdPrint(BADERRORS, ("Merging local changes to <%s%s>\n", lpCP->lpSharePath, lpCP->lpRemotePath));
|
|
|
|
Assert((sPQP.hShare == hShare) && // this is the given server
|
|
(
|
|
((state != STATE_REINT_FILES) && !mShadowIsFile(sPQP.ulStatus)) ||
|
|
((state == STATE_REINT_FILES) && mShadowIsFile(sPQP.ulStatus))
|
|
)
|
|
);
|
|
|
|
fInsertInList = FALSE;
|
|
|
|
lpFind32Remote = NULL;
|
|
|
|
// if there is an insertion list, then check whether his ancestor
|
|
// didn't fail in reintegration
|
|
if (lpnodeInsertList)
|
|
{
|
|
// if there is an acestor then we should put this guy in the list
|
|
fInsertInList = FCheckAncestor(lpnodeInsertList, lpCP);
|
|
}
|
|
|
|
// if we are not supposed to put him in the list then try getting
|
|
// his win32 strucuture
|
|
if (!fInsertInList)
|
|
{
|
|
BOOL fExists;
|
|
|
|
GetRemoteWin32Info(NULL, lpCP, &sFind32Remote, &fExists);
|
|
|
|
// insert in list only if some error happened
|
|
if (fExists == -1)
|
|
{
|
|
fInsertInList = TRUE;
|
|
}
|
|
|
|
// passing remote find32 only if it succeeded
|
|
if (fExists == TRUE)
|
|
{
|
|
lpFind32Remote = &sFind32Remote;
|
|
}
|
|
}
|
|
|
|
// find out what needs to be done
|
|
// this one central place to infer all the stuff
|
|
InferReplicaReintStatus(
|
|
&sSI, // shadowinfo
|
|
&sFind32Local, // win32 info for the shadow
|
|
lpFind32Remote, // remote win32 info
|
|
&iShadowStatus,
|
|
&iFileStatus,
|
|
&uAction
|
|
);
|
|
|
|
// insert if it had an ancestor in the list or some merge needed to be done
|
|
fInsertInList = (fInsertInList || (uAction == RAIA_MERGE) || (uAction==RAIA_CONFLICT));
|
|
|
|
if (!fInsertInList)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Silently doing <%s%s>\n", lpCP->lpSharePath, lpCP->lpRemotePath));
|
|
fInsertInList = (PerformOneReint(
|
|
hShadowDB,
|
|
szDrive,
|
|
lpCP,
|
|
&sSI,
|
|
&sFind32Local,
|
|
lpFind32Remote,
|
|
iShadowStatus,
|
|
iFileStatus,
|
|
uAction
|
|
) == FALSE);
|
|
}
|
|
|
|
if (fInsertInList)
|
|
{
|
|
if(!insertList( &lpnodeInsertList,
|
|
lpCP,
|
|
&sSI,
|
|
&sFind32Local,
|
|
lpFind32Remote,
|
|
iShadowStatus,
|
|
iFileStatus,
|
|
uAction
|
|
))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("ReintOneShare: insertlist failed in memory allocation \r\n"));
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
fDone = FALSE;
|
|
goto bailout;
|
|
}
|
|
ReintKdPrint(BADERRORS, ("Inserted <%s%s> in list\n", lpCP->lpSharePath, lpCP->lpRemotePath));
|
|
}
|
|
}
|
|
} while (sPQP.uPos); // Priority queue enumeration
|
|
|
|
// Close the enumeration
|
|
EndPQEnum(hShadowDB, &sPQP);
|
|
|
|
} // reint pass 1 & 2
|
|
|
|
if (fBeginReint){
|
|
// we found something to merge
|
|
if (lpnodeInsertList)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Found reint list, doing UI \n"));
|
|
fDone = DoFilesListReint(hShadowDB, szDrive, hwndParent, lpnodeInsertList); // 1 if successful, -1 if error, 0 if cancelled
|
|
}
|
|
else
|
|
{
|
|
// all went well
|
|
fDone = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fConnected){
|
|
|
|
DWDisconnectDriveMappedNet(szDrive, TRUE); // force a disconnect
|
|
fConnected = FALSE;
|
|
}
|
|
|
|
if (fDone==TRUE) {
|
|
|
|
SetShareStatus(hShadowDB, hShare, (unsigned long)(~SHARE_REINT), SHADOW_FLAGS_AND);
|
|
}
|
|
|
|
bailout:
|
|
|
|
if (fBeginReint){
|
|
EndReint(hShadowDB, hShare);
|
|
fBeginReint = FALSE;
|
|
}
|
|
|
|
CloseShadowDatabaseIO(hShadowDB);
|
|
|
|
if(lpnodeInsertList) {
|
|
killList(lpnodeInsertList);
|
|
lpnodeInsertList = NULL; //general paranoia
|
|
}
|
|
|
|
FreeCopyParams(lpCP);
|
|
|
|
if (fConnected) {
|
|
WNetCancelConnection2(szDrive, 0, FALSE);
|
|
}
|
|
|
|
// Remove the tray notification about merging
|
|
if( CheckDirtyShares()==0 ) {
|
|
|
|
Tray_Modify(vhwndMain,0,NULL);
|
|
}
|
|
|
|
return fDone;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Query the registry to see if we should make log copies
|
|
*/
|
|
VOID GetLogCopyStatus(VOID)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwSize = MAX_NAME_LEN;
|
|
_TCHAR szDoCopy[MAX_NAME_LEN];
|
|
|
|
// get the user name.
|
|
if(RegOpenKey(HKEY_LOCAL_MACHINE, vszShadowReg, &hKey) != ERROR_SUCCESS) {
|
|
ReintKdPrint(BADERRORS, ("GetLogCopyStatus: RegOpenKey failed\n"));
|
|
return;
|
|
}
|
|
|
|
if(RegQueryValueEx(hKey, vszDoLogCopy, NULL, NULL, szDoCopy, &dwSize) != ERROR_SUCCESS) {
|
|
RegCloseKey(hKey);
|
|
ReintKdPrint(BADERRORS, ("GetLogCopyStatus: RegQueryValueEx failed\n"));
|
|
return;
|
|
}
|
|
|
|
if(mystrnicmp(szDoCopy, MY_SZ_TRUE, strlen(szDoCopy)))
|
|
vfLogCopying = FALSE;
|
|
else
|
|
vfLogCopying = TRUE;
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Make a connection to the logging server and copy the log over.
|
|
*/
|
|
VOID CopyLogToShare(VOID)
|
|
{
|
|
HKEY hKeyShadow;
|
|
DWORD dwSize = MAX_NAME_LEN, dwRes;
|
|
_TCHAR szComputerName[MAX_NAME_LEN];
|
|
_TCHAR szLogDirPath[MAX_PATH], szLogPath[MAX_PATH];
|
|
WIN32_FIND_DATAA sFind32;
|
|
int iCurrFile=0;
|
|
NETRESOURCE sNR;
|
|
HANDLE hLog;
|
|
|
|
// check to see if we should copy the log over.
|
|
if(!vfLogCopying) {
|
|
return;
|
|
}
|
|
|
|
// get the user name.
|
|
if(RegOpenKey(HKEY_LOCAL_MACHINE, vszMachineName, &hKeyShadow) != ERROR_SUCCESS) {
|
|
ReintKdPrint(BADERRORS, ("RegOpenKey failed\n"));
|
|
}
|
|
|
|
if(RegQueryValueEx(hKeyShadow, vszComputerName, NULL, NULL, szComputerName, &dwSize) != ERROR_SUCCESS) {
|
|
RegCloseKey(hKeyShadow);
|
|
ReintKdPrint(BADERRORS, ("RegQueryValueEx failed\n"));
|
|
return;
|
|
}
|
|
RegCloseKey(hKeyShadow);
|
|
|
|
lstrcpy(szLogDirPath, vszLogUNCPath);
|
|
lstrcat(szLogDirPath, szComputerName);
|
|
|
|
sNR.lpRemoteName = vszLogShare;
|
|
sNR.lpLocalName = NULL;
|
|
sNR.lpProvider = NULL;
|
|
sNR.dwType = RESOURCETYPE_DISK;
|
|
dwRes = WNetAddConnection3(vhwndMain, &sNR, NULL, NULL, CONNECT_TEMPORARY);
|
|
if(dwRes != WN_SUCCESS) {
|
|
ReintKdPrint(BADERRORS, ("CopyLogToShare() AddConn3 failed (%d)\n", dwRes));
|
|
return;
|
|
}
|
|
|
|
// check to see if that dir lives on the server.
|
|
if(!GetWin32Info(szLogDirPath, &sFind32)) {
|
|
// if not, create it.
|
|
ReintKdPrint(BADERRORS, ("dir not found\n"));
|
|
if(!CreateDirectory(szLogDirPath, NULL)) {
|
|
ReintKdPrint(BADERRORS, ("Create dir failed, reason = %d\n", GetLastError()));
|
|
}
|
|
}
|
|
wsprintf(szLogPath, "%s\\status.log",szLogDirPath);
|
|
// copy file over.
|
|
ReintKdPrint(BADERRORS, ("we'll use <%s> next\n", szLogPath));
|
|
if((hLog = CreateFile(szLogPath
|
|
, GENERIC_READ|GENERIC_WRITE
|
|
, FILE_SHARE_READ|FILE_SHARE_WRITE
|
|
, NULL
|
|
, OPEN_ALWAYS
|
|
, 0
|
|
, NULL)) != INVALID_HANDLE_VALUE) {
|
|
ReintKdPrint(BADERRORS, ("file created\n"));
|
|
AppendToShareLog(hLog);
|
|
CloseHandle(hLog);
|
|
} else {
|
|
ReintKdPrint(BADERRORS, ("create failed, reason = %d\n", GetLastError()));
|
|
}
|
|
WNetCancelConnection2(vszLogShare, CONNECT_REFCOUNT, FALSE);
|
|
}
|
|
|
|
#define MAX_BUF_SIZE 1024
|
|
|
|
/****************************************************************************
|
|
* Copy the final stats from local log to the server version (hLog)
|
|
*/
|
|
VOID AppendToShareLog(HANDLE hLog)
|
|
{
|
|
HANDLE hLocal=0;
|
|
DWORD dwBytesRead, dwBytesWritten, dwPos, x;
|
|
BOOL fDone=FALSE;
|
|
_TCHAR cBuffer[MAX_BUF_SIZE];
|
|
|
|
if((hLocal = CreateFile(vszLocalLogPath
|
|
, GENERIC_READ
|
|
, FILE_SHARE_READ
|
|
, NULL
|
|
, OPEN_EXISTING
|
|
, 0
|
|
, NULL)) != INVALID_HANDLE_VALUE) {
|
|
ReintKdPrint(BADERRORS, ("local log file opened (0x%x)\n", hLocal));
|
|
dwPos = SetFilePointer(hLog, 0, NULL, FILE_END);
|
|
if(dwPos == 0xFFFFFFFF) {
|
|
ReintKdPrint(BADERRORS, ("Failed seek on remote file, reason = %d\n", GetLastError()));
|
|
goto cleanup;
|
|
}
|
|
dwPos = SetFilePointer(hLocal, 0, NULL, FILE_END);
|
|
if(dwPos == 0xFFFFFFFF) {
|
|
ReintKdPrint(BADERRORS, ("Failed seek on local file, reason = %d\n", GetLastError()));
|
|
goto cleanup;
|
|
}
|
|
if((dwPos = SetFilePointer(hLocal, -MAX_BUF_SIZE, NULL, FILE_CURRENT)) == 0xFFFFFFFF)
|
|
goto cleanup;
|
|
|
|
// move backwards until we find the "!*" that I use as my start token.
|
|
while(!fDone) {
|
|
if(!ReadFile(hLocal, cBuffer, MAX_BUF_SIZE, &dwBytesRead, NULL) || !dwBytesRead) {
|
|
if(!dwBytesRead) {
|
|
ReintKdPrint(BADERRORS, ("local eof\n"));
|
|
} else {
|
|
ReintKdPrint(BADERRORS, ("R error: %d\n", GetLastError()));
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
for(x=0;x<dwBytesRead;x++) {
|
|
if(cBuffer[x] == '!' && cBuffer[x+1] == '*') {
|
|
fDone = TRUE;
|
|
dwPos += x;
|
|
break;
|
|
}
|
|
}
|
|
if(!fDone)
|
|
if((dwPos = SetFilePointer(hLocal, -2*MAX_BUF_SIZE, NULL, FILE_CURRENT)) == 0xFFFFFFFF) {
|
|
ReintKdPrint(BADERRORS, ("seeked all the way and failed, error=%d\n",GetLastError()));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
// we have found the !*. Seek there and copy until end of file.
|
|
// tHACK. We should have a final delimiter (ie: *!)
|
|
if((dwPos = SetFilePointer(hLocal, dwPos, NULL, FILE_BEGIN)) == 0xFFFFFFFF)
|
|
goto cleanup;
|
|
for(;;) {
|
|
if(!ReadFile(hLocal, cBuffer, MAX_BUF_SIZE, &dwBytesRead, NULL)) {
|
|
ReintKdPrint(BADERRORS, ("R error: %d\n", GetLastError()));
|
|
break;
|
|
}
|
|
if(dwBytesRead == 0)
|
|
break;
|
|
if(!WriteFile(hLog, cBuffer, dwBytesRead, &dwBytesWritten, NULL)) {
|
|
ReintKdPrint(BADERRORS, ("W error: %d\n", GetLastError()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
cleanup:
|
|
if(hLocal)
|
|
CloseHandle(hLocal);
|
|
if(!FlushFileBuffers(hLog)) {
|
|
ReintKdPrint(BADERRORS, ("FlushFileBuffers failed, reason = %d\n",GetLastError()));
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
PRIVATE
|
|
MoveConflictingFile(
|
|
LPCOPYPARAMS lpCP
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError;
|
|
_TCHAR * lpLeaf;
|
|
|
|
|
|
lstrcpy(vrgchBuff, vszConflictDir);
|
|
lstrcat(vrgchBuff, vszSlash);
|
|
FormLocalNameFromRemoteName(vrgchBuff+strlen(vrgchBuff), lpCP->lpSharePath);
|
|
|
|
dwError = InbCreateDir(vrgchBuff, 0xffffffff);
|
|
|
|
if (dwError != NO_ERROR) {
|
|
dwError = ERROR_NO_CONFLICT_DIR;
|
|
goto bailout;
|
|
}
|
|
|
|
lpLeaf = GetLeafPtr(lpCP->lpRemotePath);
|
|
|
|
lstrcat(vrgchBuff, vszSlash);
|
|
lstrcat(vrgchBuff, lpLeaf);
|
|
GetUniqueName(vrgchBuff, vrgchBuff+512);
|
|
ReintKdPrint(BADERRORS, ("Shadow of %s!%s is saved as %s \r\n"
|
|
, lpCP->lpSharePath
|
|
, lpCP->lpRemotePath
|
|
, vrgchBuff+512));
|
|
|
|
if(!MoveFile(lpCP->lpLocalPath, vrgchBuff+512)){
|
|
dwError = GetLastError();
|
|
}
|
|
else{
|
|
wsprintf(vrwBuff, "Shadow of %s!%s is saved as %s \r\n"
|
|
, lpCP->lpSharePath
|
|
, lpCP->lpRemotePath
|
|
, vrgchBuff+512);
|
|
WriteLog(vrwBuff);
|
|
dwError = 0;
|
|
}
|
|
bailout:
|
|
return (dwError);
|
|
}
|
|
|
|
|
|
VOID
|
|
PRIVATE
|
|
FormLocalNameFromRemoteName(
|
|
_TCHAR * lpBuff,
|
|
_TCHAR * lpRemoteName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
lstrcpy(lpBuff, lpRemoteName);
|
|
for (i= strlen(lpRemoteName)-1; i>=0 ; --i){
|
|
if (lpBuff[i]=='\\') {
|
|
lpBuff[i] = '_';
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
PRIVATE
|
|
StampReintLog(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
SYSTEMTIME sST;
|
|
|
|
GetLocalTime(&sST);
|
|
wsprintf(vrgchBuff, vszTimeDateFormat, sST.wHour, sST.wMinute, sST.wSecond, sST.wMonth, sST.wDay, sST.wYear);
|
|
return (WriteLog(vrgchBuff));
|
|
}
|
|
|
|
int PRIVATE LogReintError(
|
|
DWORD dwError,
|
|
_TCHAR * lpSharePath,
|
|
_TCHAR * lpRemotePath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i< sizeof(rgErrorTab)/sizeof(ERRMSG); ++i){
|
|
if (dwError == rgErrorTab[i].dwError){
|
|
LoadString(vhinstCur, rgErrorTab[i].uMessageID, vrgchBuff, 128);
|
|
wsprintf(vrgchBuff+128, "%s%s: %s \r\n"
|
|
, lpSharePath
|
|
, lpRemotePath
|
|
, vrgchBuff);
|
|
WriteLog(vrgchBuff+128);
|
|
return (1);
|
|
}
|
|
}
|
|
wsprintf(vrgchBuff, "%s%s: ", lpSharePath, lpRemotePath);
|
|
WriteLog(vrgchBuff);
|
|
if (FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, dwError,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
vrgchBuff, sizeof(vrgchBuff), NULL)){
|
|
WriteLog(vrgchBuff);
|
|
}
|
|
|
|
WriteLog(vrgchCRLF);
|
|
}
|
|
|
|
int
|
|
PRIVATE
|
|
WriteLog(
|
|
_TCHAR * lpStrLog
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
HANDLE hfLog;
|
|
DWORD dwRetLen;
|
|
|
|
if((hfLog = CreateFile(vszLogFile
|
|
, GENERIC_READ|GENERIC_WRITE
|
|
, FILE_SHARE_READ|FILE_SHARE_WRITE
|
|
, NULL
|
|
, OPEN_ALWAYS
|
|
, 0
|
|
, NULL)) != INVALID_HANDLE_VALUE){
|
|
SetFilePointer(hfLog, 0, NULL, FILE_END);
|
|
WriteFile(hfLog, lpStrLog, strlen(lpStrLog), &dwRetLen, NULL);
|
|
CloseHandle(hfLog);
|
|
return (1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
#endif //MAYBE_USEFUL
|
|
|
|
BOOL
|
|
GetWin32Info(
|
|
_TCHAR * lpFile,
|
|
LPWIN32_FIND_DATA lpFW32
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
return GetWin32InfoForNT(lpFile, lpFW32);
|
|
}
|
|
|
|
|
|
DWORD
|
|
PRIVATE
|
|
DoObjectEdit(
|
|
HANDLE hShadowDB,
|
|
_TCHAR * lpDrive,
|
|
_TCHAR * lptzFullPath,
|
|
LPCOPYPARAMS lpCP,
|
|
LPSHADOWINFO lpSI,
|
|
LPWIN32_FIND_DATA lpFind32Local,
|
|
LPWIN32_FIND_DATA lpFind32Remote,
|
|
int iShadowStatus,
|
|
int iFileStatus,
|
|
int uAction,
|
|
DWORD dwFileSystemFlags,
|
|
LPCSCPROC lpfnMergeProgress,
|
|
DWORD_PTR dwContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does the actual merge for files
|
|
|
|
Arguments:
|
|
|
|
hShadowDB Handle to the redir to call issue ioctl calls
|
|
|
|
lpDrive drivemapping to bypass CSC while making changes on the remote
|
|
|
|
lptzFullPath Fully qualified path
|
|
|
|
lpCP Copy parameters, contain share name, path relative to the share and the
|
|
the name in the local database
|
|
|
|
lpSI info such as pincount and pinflags
|
|
|
|
lpFind32Local win32 info for the local replica
|
|
|
|
lpFind32Remote win32 infor for the origianl, NULL if the original doesn't exist
|
|
|
|
iShadowStatus status of the local copy
|
|
|
|
iFileStatus status of the remote copy
|
|
|
|
uAction action to be performed
|
|
|
|
dwFileSystemFlags filesystem flags to do special things for NTFS
|
|
|
|
lpfnMergeProgress progress callback
|
|
|
|
dwContext callback context
|
|
|
|
|
|
Returns:
|
|
|
|
error code as defined in winerror.h
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE hfSrc = INVALID_HANDLE_VALUE, hfDst = INVALID_HANDLE_VALUE;
|
|
HANDLE hDst=0;
|
|
_TCHAR * lpT;
|
|
LONG lOffset=0;
|
|
DWORD dwError=ERROR_REINT_FAILED;
|
|
BOOL fRet, fFileExists, fOverWrite=FALSE, fForceAttribute = FALSE;
|
|
WIN32_FIND_DATA sFind32Remote;
|
|
DWORD dwTotal = 0, dwRet;
|
|
_TCHAR szSrcName[MAX_PATH+MAX_SERVER_SHARE_NAME_FOR_CSC+10];
|
|
_TCHAR szDstName[MAX_PATH+MAX_SERVER_SHARE_NAME_FOR_CSC+10];
|
|
_TCHAR *lprwBuff = NULL;
|
|
_TCHAR *lptzLocalPath = NULL;
|
|
_TCHAR *lptzLocalPathCscBmp = NULL;
|
|
LPCSC_BITMAP_U lpbitmap = NULL;
|
|
DWORD fileSize, fileSizeHigh;
|
|
int cscReintRet;
|
|
|
|
lprwBuff = LocalAlloc(LPTR, FILL_BUF_SIZE_LAN);
|
|
|
|
if (!lprwBuff)
|
|
{
|
|
return (ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
if (!(lptzLocalPath = GetTempFileForCSC(NULL)))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("DoObjectEdit: failed to get temp file\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
if (!CopyShadow(hShadowDB, lpSI->hDir, lpSI->hShadow, lptzLocalPath))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("DoObjectEdit: failed to make local copy\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
// for EFS files, we overwrite the original file, that way the encryption information
|
|
// will not be lost due to us doing a new create followed by a rename and delete
|
|
|
|
fOverWrite = ((lpFind32Local->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0);
|
|
|
|
if (!fOverWrite && lpFind32Remote)
|
|
{
|
|
fOverWrite = (((lpFind32Remote->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0)||
|
|
((lpFind32Remote->dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0));
|
|
}
|
|
|
|
// if this is the DFS root, always overwrite. This is to avoind sharing violation problems
|
|
// while merging
|
|
if (!fOverWrite && (dwFileSystemFlags == DFS_ROOT_FILE_SYSTEM_FLAGS))
|
|
{
|
|
fOverWrite = TRUE;
|
|
}
|
|
|
|
ReintKdPrint(MERGE, ("Overwrite=%d\r\n", fOverWrite));
|
|
|
|
lOffset=0;
|
|
|
|
// Create x:\foo\00010002 kind of temporary filename
|
|
|
|
lstrcpy(szDstName, lpDrive);
|
|
lstrcat(szDstName, lpCP->lpRemotePath);
|
|
|
|
lpT = GetLeafPtr(szDstName);
|
|
*lpT = 0; // remove the remote leaf
|
|
|
|
lpT = GetLeafPtr(lpCP->lpLocalPath);
|
|
|
|
// attach the local leaf
|
|
lstrcat(szDstName, lpT);
|
|
|
|
// Let us also create the real name x:\foo\bar
|
|
lstrcpy(szSrcName, lpDrive);
|
|
lstrcat(szSrcName, lpCP->lpRemotePath);
|
|
|
|
fFileExists = (lpFind32Remote != NULL);
|
|
|
|
if (!fFileExists)
|
|
{
|
|
fOverWrite = FALSE;
|
|
ReintKdPrint(MERGE, ("File doesn't exist, Overwrite=%d\r\n", fOverWrite));
|
|
}
|
|
|
|
if (mShadowDeleted(lpSI->uStatus)){
|
|
|
|
ReintKdPrint(MERGE, ("Deleting %ls \r\n", szSrcName));
|
|
|
|
if (lpFind32Remote)
|
|
{
|
|
if((lpFind32Remote->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
ReintKdPrint(MERGE, ("DoObjectEdit:attribute conflict on %ls \r\n", szSrcName));
|
|
goto bailout;
|
|
}
|
|
if(!DeleteFile(szSrcName))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("DoObjectEdit:delete failed %ls error=%d\r\n", szSrcName, GetLastError()));
|
|
goto bailout;
|
|
}
|
|
}
|
|
|
|
// if this operation fails we want to abort
|
|
// directory
|
|
if(!DeleteShadow(hShadowDB, lpSI->hDir, lpSI->hShadow))
|
|
{
|
|
dwError = GetLastError();
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
dwError = 0;
|
|
goto bailout;
|
|
}
|
|
|
|
}
|
|
|
|
if (mShadowDirty(lpSI->uStatus)
|
|
|| mShadowLocallyCreated(lpSI->uStatus)){
|
|
|
|
|
|
hfSrc = CreateFile( lptzLocalPath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
if (hfSrc == INVALID_HANDLE_VALUE)
|
|
{
|
|
goto bailout;
|
|
}
|
|
|
|
if (lpFind32Remote && uAction != RAIA_MERGE) {
|
|
// Load the bitmap only when the remote file exists and
|
|
// that no conflict occurs. (if there's a conflict,
|
|
// uAction == RAIA_MERGE. See PerformOneReint()
|
|
lptzLocalPathCscBmp = (_TCHAR *)LocalAlloc(
|
|
LPTR,
|
|
(lstrlen(lptzLocalPath) +
|
|
CSC_BitmapStreamNameLen() + 1) * sizeof(_TCHAR));
|
|
lstrcpy(lptzLocalPathCscBmp, lptzLocalPath);
|
|
CSC_BitmapAppendStreamName(
|
|
lptzLocalPathCscBmp,
|
|
(lstrlen(lptzLocalPath) + CSC_BitmapStreamNameLen() + 1) * sizeof(_TCHAR));
|
|
ReintKdPrint(MERGE, ("TempFileBmp (WCHAR) %ws\r\n", lptzLocalPathCscBmp));
|
|
switch(CSC_BitmapRead(&lpbitmap, lptzLocalPathCscBmp)) {
|
|
// for return values of CSC_BitmapRead see csc_bmpu.c
|
|
case 0:
|
|
ReintKdPrint(BADERRORS, ("&lpbitmap is null, cannot happen\n"));
|
|
lpbitmap = NULL;
|
|
break;
|
|
case 1:
|
|
ReintKdPrint(MERGE, ("Read bitmap successful\n"));
|
|
// Overwrite the updated parts of the original file in the
|
|
// share
|
|
fOverWrite = TRUE;
|
|
CSC_BitmapOutput(lpbitmap); // this is NOTHING in free build
|
|
break;
|
|
case -2:
|
|
ReintKdPrint(
|
|
MERGE,
|
|
("No Bitmap file %ws exists\n",
|
|
lptzLocalPathCscBmp));
|
|
lpbitmap = NULL;
|
|
break;
|
|
case -1:
|
|
ReintKdPrint(
|
|
MERGE,
|
|
("Error in reading Bitmap file %ws\n",
|
|
lptzLocalPathCscBmp));
|
|
lpbitmap = NULL;
|
|
break;
|
|
default:
|
|
ReintKdPrint(MERGE, ("CSC_BitmapRead return code unknown\n"));
|
|
lpbitmap = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if the destination file has multiple streams
|
|
// we should overwrite it
|
|
if (mShadowDirty(lpSI->uStatus) &&
|
|
(dwFileSystemFlags & FS_PERSISTENT_ACLS)&& // indication of NTFS
|
|
(fFileExists)&&
|
|
!fOverWrite)
|
|
{
|
|
BOOL fStreams = FALSE;
|
|
|
|
// check if this has multiple streams
|
|
if(!HasMultipleStreams(szSrcName, &fStreams) || fStreams )
|
|
{
|
|
// if the call failed, we go conservative and assume there are multiple streams
|
|
ReintKdPrint(MERGE, ("Have multiple streams, overwriting\n"));
|
|
fOverWrite = TRUE;
|
|
}
|
|
|
|
}
|
|
if (!fOverWrite)
|
|
{
|
|
|
|
ReintKdPrint(MERGE, ("Creating temp \r\n"));
|
|
|
|
if ((dwFileSystemFlags & FS_PERSISTENT_ACLS)&&(fFileExists))
|
|
{
|
|
hfDst = CreateTmpFileWithSourceAcls(
|
|
szSrcName,
|
|
szDstName);
|
|
}
|
|
else
|
|
{
|
|
hfDst = CreateFile(szDstName,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
0,
|
|
NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReintKdPrint(MERGE, ("Overwriting existing file\r\n"));
|
|
Assert(lpFind32Remote);
|
|
if (lpFind32Remote->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
|
|
{
|
|
ReintKdPrint(MERGE, ("Clearing Readonly attribute \r\n"));
|
|
if(!SetFileAttributes(szSrcName, (lpFind32Remote->dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))){
|
|
ReintKdPrint(MERGE, ("Failed to clear Readonly attribute, bailing\r\n"));
|
|
goto error;
|
|
}
|
|
|
|
fForceAttribute = TRUE;
|
|
}
|
|
|
|
// Want to open existing so can copy only those parts of
|
|
// file that needs to be updated
|
|
ReintKdPrint(MERGE, ("Opening %ws\n", szSrcName));
|
|
hfDst = CreateFile(
|
|
szSrcName,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
(lpbitmap == NULL) ? TRUNCATE_EXISTING : OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
if (hfDst == INVALID_HANDLE_VALUE) {
|
|
dwError = GetLastError();
|
|
ReintKdPrint(MERGE, ("open failed %d\n", dwError));
|
|
SetLastError(dwError);
|
|
goto error;
|
|
}
|
|
|
|
if (lpbitmap != NULL) {
|
|
|
|
// Resize the destination file
|
|
fileSizeHigh = 0;
|
|
fileSize = GetFileSize(hfSrc, &fileSizeHigh);
|
|
if (fileSize == 0xFFFFFFFF && GetLastError() != NO_ERROR) {
|
|
ReintKdPrint(BADERRORS, ("Error getting source file size\n"));
|
|
goto error;
|
|
}
|
|
ReintKdPrint(MERGE, ("Source FileSize %u\n", fileSize));
|
|
if (SetFilePointer(
|
|
hfDst,
|
|
fileSize,
|
|
&fileSizeHigh,
|
|
FILE_BEGIN) == INVALID_SET_FILE_POINTER
|
|
&&
|
|
GetLastError() != NO_ERROR
|
|
) {
|
|
ReintKdPrint(BADERRORS, ("Error setting destination file pointer\n"));
|
|
goto error;
|
|
}
|
|
if (!SetEndOfFile(hfDst)) {
|
|
ReintKdPrint(BADERRORS,
|
|
("Error setting EOF info of destination file\n"));
|
|
goto error;
|
|
}
|
|
|
|
ReintKdPrint(MERGE, ("Resized Destination FileSize %u\n",
|
|
GetFileSize(hfDst, NULL)));
|
|
|
|
if (fileSizeHigh != 0 && lpbitmap) {
|
|
// file size cannot be represented by 32 bit (> 4Gb)
|
|
// do not use CSCBmp
|
|
CSC_BitmapDelete(&lpbitmap);
|
|
lpbitmap = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hfDst == INVALID_HANDLE_VALUE)
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
// let us append
|
|
if((lOffset = SetFilePointer(hfDst, 0, NULL, FILE_END))==0xffffffff) {
|
|
goto error;
|
|
}
|
|
|
|
ReintKdPrint(MERGE, ("Copying back %ls to %ls%ls \r\n"
|
|
, lpCP->lpLocalPath
|
|
, lpCP->lpSharePath
|
|
, lpCP->lpRemotePath
|
|
));
|
|
|
|
do {
|
|
unsigned cbRead;
|
|
if (lpbitmap) {
|
|
// Use CSC_BitmapReint Function
|
|
cscReintRet = CSC_BitmapReint(
|
|
lpbitmap,
|
|
hfSrc,
|
|
hfDst,
|
|
lprwBuff,
|
|
FILL_BUF_SIZE_LAN,
|
|
&cbRead);
|
|
if (cscReintRet == CSC_BITMAPReintCont) {
|
|
NOTHING;
|
|
} else if (cscReintRet == CSC_BITMAPReintDone) {
|
|
ReintKdPrint(
|
|
MERGE,
|
|
("Done reint\n"));
|
|
break;
|
|
} else if (cscReintRet == CSC_BITMAPReintInvalid) {
|
|
ReintKdPrint(
|
|
BADERRORS,
|
|
("Invalid param in calling CSC_BitmapReint\n"));
|
|
goto error;
|
|
} else if (cscReintRet == CSC_BITMAPReintError) {
|
|
ReintKdPrint(
|
|
BADERRORS,
|
|
("Error in transferring data\n"));
|
|
goto error;
|
|
} else {
|
|
ReintKdPrint(
|
|
BADERRORS,
|
|
("Unrecognized CSC_BitmapReint return code\n"));
|
|
goto error;
|
|
}
|
|
} else {
|
|
if (!ReadFile(hfSrc, lprwBuff, FILL_BUF_SIZE_LAN, &cbRead, NULL)) {
|
|
goto error;
|
|
}
|
|
// ReintKdPrint(BADERRORS, ("Read %d bytes \r\n", cbRead));
|
|
if (!cbRead) {
|
|
break;
|
|
}
|
|
if(!WriteFile(hfDst, (LPBYTE)lprwBuff, cbRead, &cbRead, NULL)){
|
|
goto error;
|
|
}
|
|
dwTotal += cbRead;
|
|
}
|
|
|
|
if (lpfnMergeProgress)
|
|
{
|
|
dwRet = (*lpfnMergeProgress)(
|
|
szSrcName,
|
|
lpSI->uStatus,
|
|
lpSI->ulHintFlags,
|
|
lpSI->ulHintPri,
|
|
lpFind32Local,
|
|
CSCPROC_REASON_MORE_DATA,
|
|
cbRead,
|
|
0,
|
|
dwContext);
|
|
|
|
if (dwRet != CSCPROC_RETURN_CONTINUE)
|
|
{
|
|
SetLastError(ERROR_OPERATION_ABORTED);
|
|
goto bailout;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (FAbortOperation())
|
|
{
|
|
SetLastError(ERROR_OPERATION_ABORTED);
|
|
goto error;
|
|
}
|
|
} while(TRUE);
|
|
|
|
CloseHandle(hfSrc);
|
|
hfSrc = 0;
|
|
|
|
|
|
CloseHandle(hfDst);
|
|
hfDst = 0;
|
|
|
|
|
|
// if we are not overwriting the original file, then we must make sure to cleanup
|
|
if (!fOverWrite)
|
|
{
|
|
// nuke the remote one if it exists
|
|
if (fFileExists){
|
|
if(!SetFileAttributes(szSrcName, FILE_ATTRIBUTE_NORMAL)
|
|
|| !DeleteFile(szSrcName)){
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
// Now rename the temp file to the real filename
|
|
if(!MoveFile(szDstName, szSrcName)){
|
|
ReintKdPrint(BADERRORS, ("Error #%ld Renaming %ls to %ls%ls\r\n"
|
|
, GetLastError()
|
|
, szSrcName
|
|
, lpCP->lpSharePath
|
|
, lpCP->lpRemotePath
|
|
));
|
|
goto error;
|
|
}
|
|
|
|
ReintKdPrint(MERGE, ("Renamed %ls to %ls%ls\r\n"
|
|
, szDstName
|
|
, lpCP->lpSharePath
|
|
, lpCP->lpRemotePath));
|
|
}
|
|
}
|
|
|
|
if (fForceAttribute ||
|
|
mShadowAttribChange((lpSI->uStatus))||
|
|
mShadowTimeChange((lpSI->uStatus))){
|
|
|
|
if(!SetFileAttributes(szSrcName, FILE_ATTRIBUTE_NORMAL)) {
|
|
goto error;
|
|
}
|
|
|
|
if (mShadowTimeChange((lpSI->uStatus))){
|
|
|
|
if((hDst = CreateFile(szSrcName,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
))!=INVALID_HANDLE_VALUE){
|
|
fRet = SetFileTime( hDst, NULL,
|
|
NULL, &(lpFind32Local->ftLastWriteTime));
|
|
}
|
|
CloseHandle(hDst);
|
|
hDst = 0;
|
|
|
|
if (!fRet) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if(!SetFileAttributes(szSrcName, lpFind32Local->dwFileAttributes)){
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
// Get the latest timestamps/attributes/LFN/SFN on the file we just copied back
|
|
if (!GetWin32Info(szSrcName, &sFind32Remote)) {
|
|
goto error;
|
|
}
|
|
|
|
lpSI->uStatus &= (unsigned long)(~(SHADOW_MODFLAGS));
|
|
|
|
if (!SetShadowInfo(hShadowDB, lpSI->hDir, lpSI->hShadow, &sFind32Remote, ~(SHADOW_MODFLAGS), SHADOW_FLAGS_AND|SHADOW_FLAGS_CHANGE_83NAME))
|
|
{
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
dwError = NO_ERROR;
|
|
goto bailout;
|
|
}
|
|
|
|
error:
|
|
dwError = GetLastError();
|
|
ReportLastError();
|
|
|
|
#if 0
|
|
LogReintError( dwError,
|
|
lpCP->lpSharePath,
|
|
lpCP->lpRemotePath);
|
|
#endif
|
|
|
|
bailout:
|
|
|
|
if (hfSrc != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hfSrc);
|
|
|
|
if (hfDst != INVALID_HANDLE_VALUE) {
|
|
|
|
CloseHandle(hfDst);
|
|
|
|
// if we failed,
|
|
if (dwError != ERROR_SUCCESS)
|
|
DeleteFile(szDstName);
|
|
}
|
|
|
|
if (lptzLocalPath) {
|
|
DeleteFile(lptzLocalPath);
|
|
LocalFree(lptzLocalPath);
|
|
}
|
|
|
|
if (lprwBuff)
|
|
LocalFree(lprwBuff);
|
|
|
|
if (lptzLocalPathCscBmp)
|
|
LocalFree(lptzLocalPathCscBmp);
|
|
|
|
if (lpbitmap)
|
|
CSC_BitmapDelete(&lpbitmap);
|
|
|
|
ReintKdPrint(MERGE, ("DoObjectEdit returning %d\n", dwError));
|
|
return (dwError);
|
|
}
|
|
|
|
DWORD
|
|
PRIVATE
|
|
DoCreateDir(
|
|
HANDLE hShadowDB,
|
|
_TCHAR * lpDrive,
|
|
_TCHAR * lptzFullPath,
|
|
LPCOPYPARAMS lpCP,
|
|
LPSHADOWINFO lpSI,
|
|
LPWIN32_FIND_DATA lpFind32Local,
|
|
LPWIN32_FIND_DATA lpFind32Remote,
|
|
int iShadowStatus,
|
|
int iFileStatus,
|
|
int uAction,
|
|
DWORD dwFileSystemFlags,
|
|
LPCSCPROC lpfnMergeProgress,
|
|
DWORD_PTR dwContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does the actual merge for directories
|
|
|
|
Arguments:
|
|
|
|
hShadowDB Handle to the redir to call issue ioctl calls
|
|
|
|
lpDrive drivemapping to bypass CSC while making changes on the remote
|
|
|
|
lptzFullPath Fully qualified path
|
|
|
|
lpCP Copy parameters, contain share name, path relative to the share and the
|
|
the name in the local database
|
|
|
|
lpSI info such as pincount and pinflags
|
|
|
|
lpFind32Local win32 info for the local replica
|
|
|
|
lpFind32Remote win32 infor for the origianl, NULL if the original doesn't exist
|
|
|
|
iShadowStatus status of the local copy
|
|
|
|
iFileStatus status of the remote copy
|
|
|
|
uAction action to be performed
|
|
|
|
dwFileSystemFlags filesystem flags to do special things for NTFS
|
|
|
|
lpfnMergeProgress progress callback
|
|
|
|
dwContext callback context
|
|
|
|
|
|
Returns:
|
|
|
|
error code as defined in winerror.h
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError=ERROR_FILE_NOT_FOUND;
|
|
WIN32_FIND_DATA sFind32Remote;
|
|
BOOL fCreateDir = FALSE;
|
|
_TCHAR szSrcName[MAX_PATH+MAX_SERVER_SHARE_NAME_FOR_CSC+10];
|
|
_TCHAR *lprwBuff = NULL;
|
|
|
|
lprwBuff = LocalAlloc(LPTR, FILL_BUF_SIZE_LAN);
|
|
|
|
if (!lprwBuff)
|
|
{
|
|
return (ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
// Let us create the real name x:\foo\bar
|
|
lstrcpy(szSrcName, lpDrive);
|
|
lstrcat(szSrcName, lpCP->lpRemotePath);
|
|
|
|
if(lpFind32Remote &&
|
|
!(lpFind32Remote->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
|
|
|
|
if (lpSI->uStatus & SHADOW_REUSED){
|
|
|
|
// we now know that a file by this name has been deleted
|
|
// and a directory has been created in it's place
|
|
// we try to delete the file before createing the directory
|
|
// NB, the other way is not possible because we don't allow directory deletes
|
|
// in disconnected mode
|
|
dwError = (!DeleteFile(szSrcName)) ? GetLastError(): NO_ERROR;
|
|
|
|
if ((dwError==NO_ERROR)||
|
|
(dwError==ERROR_FILE_NOT_FOUND)||
|
|
(dwError==ERROR_PATH_NOT_FOUND)){
|
|
lstrcpy(lprwBuff, szSrcName);
|
|
dwError = NO_ERROR;
|
|
}
|
|
}
|
|
|
|
if (dwError != NO_ERROR){
|
|
#if 0
|
|
LogReintError(ERROR_ATTRIBUTE_CONFLICT, lpCP->lpSharePath, lpCP->lpRemotePath);
|
|
#endif
|
|
dwError = GetUniqueName(szSrcName, lprwBuff);
|
|
}
|
|
|
|
if (dwError == NO_ERROR){
|
|
if ((dwError = InbCreateDir( lprwBuff,
|
|
(mShadowAttribChange(lpSI->uStatus)
|
|
? lpFind32Local->dwFileAttributes
|
|
: 0xffffffff)
|
|
))==NO_ERROR)
|
|
{
|
|
if(!GetWin32Info(lprwBuff, &sFind32Remote)){
|
|
dwError = GetLastError();
|
|
}
|
|
else{
|
|
#if 0
|
|
lpLeaf1 = GetLeafPtr(szSrcName);
|
|
lpLeaf2 = GetLeafPtr(lprwBuff);
|
|
wsprintf(lprwBuff+512
|
|
, "Directory Name changed from %s to %s on %s\r\n"
|
|
, lpLeaf1, lpLeaf2, lpCP->lpSharePath);
|
|
WriteLog(lprwBuff+512);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
if ((dwError = InbCreateDir(szSrcName,
|
|
(mShadowAttribChange(lpSI->uStatus)
|
|
? lpFind32Local->dwFileAttributes
|
|
: 0xffffffff)
|
|
))==NO_ERROR){
|
|
if (!GetWin32Info(szSrcName, &sFind32Remote)){
|
|
dwError = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwError == NO_ERROR){
|
|
|
|
if(!SetShadowInfo(hShadowDB, lpSI->hDir, lpSI->hShadow, &sFind32Remote, (unsigned)(~SHADOW_MODFLAGS), SHADOW_FLAGS_AND))
|
|
{
|
|
dwError = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
ReintKdPrint(MERGE, ("Created directory %s%s", lpCP->lpSharePath, lpCP->lpRemotePath));
|
|
}
|
|
}
|
|
else{
|
|
#if 0
|
|
wsprintf(lprwBuff, "Error merging %s%s\r\n"
|
|
, lpCP->lpSharePath
|
|
, lpCP->lpRemotePath);
|
|
WriteLog(lprwBuff);
|
|
#endif
|
|
}
|
|
|
|
if (lprwBuff)
|
|
{
|
|
LocalFree(lprwBuff);
|
|
|
|
}
|
|
return (dwError);
|
|
}
|
|
|
|
VOID
|
|
CleanupReintState(
|
|
VOID
|
|
)
|
|
{
|
|
if (vsRei.hShare)
|
|
{
|
|
|
|
ReintKdPrint(MERGE, ("CSCDLL.CleanupReintState: ending reint on hShare=%x\r\n", vsRei.hShare));
|
|
// EndReint(INVALID_HANDLE_VALUE, vsRei.hShare);
|
|
|
|
if (vsRei.tzDrive[0])
|
|
{
|
|
ReintKdPrint(MERGE, ("CSCDLL.CleanupReintState: unmapping merge drive\r\n"));
|
|
DWDisconnectDriveMappedNet(vsRei.tzDrive, TRUE);
|
|
vsRei.tzDrive[0] = 0;
|
|
}
|
|
|
|
vsRei.hShare = 0;
|
|
|
|
}
|
|
}
|
|
|
|
HANDLE
|
|
CreateTmpFileWithSourceAcls(
|
|
_TCHAR *lptzSrc,
|
|
_TCHAR *lptzDst
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used by DoObjectEdit while pushing back a file during merge.
|
|
It's job is to get the descritionary ACLs from the source file and use
|
|
them to create a temp file to which we are going to copy the data before renaming it to
|
|
the source
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
file handle is successful, INVALID_HANDLE_VALUE if failed. In case of failure,
|
|
GetLastError() tells the specific error code.
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
char buff[1];
|
|
BOOL fRet = FALSE;
|
|
SECURITY_ATTRIBUTES sSA;
|
|
DWORD dwSize = 0;
|
|
HANDLE hDst = INVALID_HANDLE_VALUE;
|
|
|
|
memset(&sSA, 0, sizeof(sSA));
|
|
|
|
sSA.lpSecurityDescriptor = buff;
|
|
dwSize = 0;
|
|
|
|
if(!GetFileSecurity(
|
|
lptzSrc,
|
|
DACL_SECURITY_INFORMATION,
|
|
sSA.lpSecurityDescriptor,
|
|
0,
|
|
&dwSize))
|
|
{
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
sSA.lpSecurityDescriptor = LocalAlloc(LPTR, dwSize);
|
|
|
|
if (sSA.lpSecurityDescriptor)
|
|
{
|
|
if(GetFileSecurity(
|
|
lptzSrc,
|
|
DACL_SECURITY_INFORMATION,
|
|
sSA.lpSecurityDescriptor,
|
|
dwSize,
|
|
&sSA.nLength))
|
|
{
|
|
sSA.nLength = sizeof(sSA);
|
|
fRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
dwSize = GetLastError();
|
|
LocalFree(sSA.lpSecurityDescriptor);
|
|
SetLastError(dwSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fRet = TRUE;
|
|
}
|
|
|
|
if (fRet)
|
|
{
|
|
hDst = CreateFile(lptzDst,
|
|
GENERIC_WRITE,
|
|
0,
|
|
&sSA,
|
|
CREATE_ALWAYS,
|
|
0,
|
|
NULL);
|
|
|
|
if (hDst == INVALID_HANDLE_VALUE)
|
|
{
|
|
dwSize = GetLastError();
|
|
}
|
|
|
|
if (sSA.lpSecurityDescriptor)
|
|
{
|
|
LocalFree(sSA.lpSecurityDescriptor);
|
|
}
|
|
if (hDst == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(dwSize);
|
|
}
|
|
|
|
}
|
|
|
|
return hDst;
|
|
}
|
|
|
|
BOOL
|
|
HasMultipleStreams(
|
|
_TCHAR *lpExistingFileName,
|
|
BOOL *lpfTrueFalse
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used by DoObjectEdit while pushing back a file during merge.
|
|
It looks to see whether the destination file has multiple streams.
|
|
|
|
Arguments:
|
|
|
|
lpExistingFileName Name of an existing file
|
|
lpfTrueFalse output parameter, returns TRUE is the call succeedes and there are multiple streams
|
|
|
|
Returns:
|
|
|
|
returns TRUE if successful
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
HANDLE SourceFile = INVALID_HANDLE_VALUE;
|
|
PFILE_STREAM_INFORMATION StreamInfoBase = NULL;
|
|
ULONG StreamInfoSize;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
BOOL fRet = FALSE;
|
|
DWORD Status;
|
|
|
|
*lpfTrueFalse = FALSE;
|
|
|
|
SourceFile = CreateFile(
|
|
lpExistingFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
if (SourceFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Obtain the full set of streams we have to copy. Since the Io subsystem does
|
|
// not provide us a way to find out how much space this information will take,
|
|
// we must iterate the call, doubling the buffer size upon each failure.
|
|
//
|
|
// If the underlying file system does not support stream enumeration, we end up
|
|
// with a NULL buffer. This is acceptable since we have at least a default
|
|
// data stream,
|
|
//
|
|
|
|
StreamInfoSize = 4096;
|
|
do {
|
|
StreamInfoBase = LocalAlloc(LPTR, StreamInfoSize );
|
|
|
|
if ( !StreamInfoBase ) {
|
|
SetLastError( STATUS_NO_MEMORY );
|
|
goto bailout;
|
|
}
|
|
|
|
Status = NtQueryInformationFile(
|
|
SourceFile,
|
|
&IoStatus,
|
|
(PVOID) StreamInfoBase,
|
|
StreamInfoSize,
|
|
FileStreamInformation
|
|
);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
//
|
|
// We failed the call. Free up the previous buffer and set up
|
|
// for another pass with a buffer twice as large
|
|
//
|
|
|
|
LocalFree(StreamInfoBase);
|
|
StreamInfoBase = NULL;
|
|
StreamInfoSize *= 2;
|
|
}
|
|
|
|
} while ( Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL );
|
|
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
fRet = TRUE;
|
|
|
|
if (StreamInfoBase)
|
|
{
|
|
if (StreamInfoBase->NextEntryOffset)
|
|
{
|
|
*lpfTrueFalse = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
bailout:
|
|
|
|
if (SourceFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(SourceFile);
|
|
}
|
|
|
|
if (StreamInfoBase)
|
|
{
|
|
LocalFree(StreamInfoBase);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
BOOL
|
|
CompareFilePrefixes(
|
|
_TCHAR *lptzRemotePath,
|
|
_TCHAR *lptzLocalPath,
|
|
SHADOWINFO *lpSI,
|
|
WIN32_FIND_DATA *lpFind32,
|
|
LPCSCPROC lpfnMergeProgress,
|
|
DWORD_PTR dwContext
|
|
)
|
|
{
|
|
|
|
HANDLE hfSrc = INVALID_HANDLE_VALUE, hfDst = INVALID_HANDLE_VALUE;
|
|
LPVOID lpvSrc = NULL, lpvDst = NULL;
|
|
unsigned cbSrcTotal = 0, cbDstTotal = 0;
|
|
DWORD dwError = NO_ERROR, dwRemoteSize;
|
|
|
|
if (lpfnMergeProgress)
|
|
{
|
|
(*lpfnMergeProgress)(lptzRemotePath, lpSI->uStatus, lpSI->ulHintFlags, lpSI->ulHintPri, lpFind32, CSCPROC_REASON_BEGIN, 0, 0, dwContext);
|
|
}
|
|
|
|
ReintKdPrint(ALWAYS, ("Comparing %ls with %ls \r\n", lptzLocalPath, lptzRemotePath));
|
|
|
|
lpvSrc = LocalAlloc(LPTR, FILL_BUF_SIZE_LAN);
|
|
|
|
if (!lpvSrc)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("CompareFilesPrefix: Memory Allocation Error\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
lpvDst = LocalAlloc(LPTR, FILL_BUF_SIZE_LAN);
|
|
|
|
if (!lpvDst)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("CompareFilesPrefix: Memory Allocation Error\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
hfSrc = CreateFile(lptzLocalPath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
if (hfSrc == INVALID_HANDLE_VALUE)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Failed to open database file for Inode=%x\r\n", lpSI->hShadow));
|
|
goto bailout;
|
|
}
|
|
|
|
hfDst = CreateFile(lptzRemotePath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
if (hfDst == INVALID_HANDLE_VALUE)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Failed to open remote file for Inode=%x\r\n", lpSI->hShadow));
|
|
goto error;
|
|
}
|
|
|
|
dwRemoteSize = GetFileSize(hfDst, NULL);
|
|
|
|
if (dwRemoteSize == 0xffffffff)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Failed to get size for remote file for Inode=%x\r\n", lpSI->hShadow));
|
|
goto error;
|
|
}
|
|
|
|
if (dwRemoteSize != lpFind32->nFileSizeLow)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("mismatched local and remote sizes for Inode=%x\r\n", lpSI->hShadow));
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
goto error;
|
|
}
|
|
|
|
do{
|
|
unsigned cbReadSrc, cbReadDst;
|
|
|
|
if (!ReadFile(hfSrc, lpvSrc, FILL_BUF_SIZE_LAN, &cbReadSrc, NULL)){
|
|
goto error;
|
|
}
|
|
|
|
if (!cbReadSrc) {
|
|
break;
|
|
}
|
|
|
|
cbSrcTotal += cbReadSrc;
|
|
|
|
if(!ReadFile(hfDst, (LPBYTE)lpvDst, cbReadSrc, &cbReadDst, NULL)){
|
|
goto error;
|
|
}
|
|
|
|
cbDstTotal += cbReadDst;
|
|
|
|
if (cbReadSrc > cbReadDst)
|
|
{
|
|
ReintKdPrint(ALWAYS, ("CompareFilesPrefix: RemoteFile sized is smaller than Local Size\r\n"));
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
goto error;
|
|
}
|
|
|
|
if (memcmp(lpvSrc, lpvDst, cbReadSrc))
|
|
{
|
|
ReintKdPrint(ALWAYS, ("mismatched!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\r\n"));
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
goto error;
|
|
}
|
|
|
|
} while(TRUE);
|
|
|
|
goto bailout;
|
|
|
|
error:
|
|
dwError = GetLastError();
|
|
ReintKdPrint(BADERRORS, ("Error=%d\r\n", dwError));
|
|
|
|
bailout:
|
|
if (hfSrc != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(hfSrc);
|
|
}
|
|
|
|
if (hfDst != INVALID_HANDLE_VALUE) {
|
|
|
|
CloseHandle(hfDst);
|
|
}
|
|
|
|
if (lpvSrc)
|
|
{
|
|
FreeMem(lpvSrc);
|
|
}
|
|
|
|
if (lpvDst)
|
|
{
|
|
FreeMem(lpvDst);
|
|
}
|
|
|
|
if (lpfnMergeProgress)
|
|
{
|
|
(*lpfnMergeProgress)(lptzRemotePath, lpSI->uStatus, lpSI->ulHintFlags, lpSI->ulHintPri, lpFind32, CSCPROC_REASON_END, cbDstTotal, dwError, dwContext);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
CheckCSCDirCallback(
|
|
HANDLE hShadowDB,
|
|
LPSECURITYINFO pShareSecurityInfo,
|
|
LPTSTR lptzFullPath,
|
|
DWORD dwCallbackReason,
|
|
WIN32_FIND_DATA *lpFind32,
|
|
SHADOWINFO *lpSI,
|
|
LPREINT_INFO lpRei
|
|
)
|
|
{
|
|
int retCode = TOD_CONTINUE;
|
|
LPCOPYPARAMS lpCP = NULL;
|
|
BOOL fInsertInList = FALSE, fIsFile;
|
|
_TCHAR *lptzLocalPath = NULL;
|
|
_TCHAR szRemoteName[MAX_PATH+MAX_SERVER_SHARE_NAME_FOR_CSC+10];
|
|
|
|
if (dwCallbackReason != TOD_CALLBACK_REASON_NEXT_ITEM)
|
|
{
|
|
return retCode;
|
|
}
|
|
fIsFile = ((lpFind32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
|
|
|
|
if (!fIsFile)
|
|
{
|
|
return retCode;
|
|
}
|
|
|
|
lpCP = LpAllocCopyParams();
|
|
|
|
if (!lpCP){
|
|
ReintKdPrint(BADERRORS, ("CheckCSCDirCallback: Allocation of copyparam buffer failed\n"));
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
retCode = TOD_ABORT;
|
|
goto bailout;
|
|
}
|
|
|
|
if(!GetUNCPath(hShadowDB, lpSI->hShare, lpSI->hDir, lpSI->hShadow, lpCP)){
|
|
|
|
ReintKdPrint(BADERRORS, ("CheckCSCDirCallback: GetUNCPath failed\n"));
|
|
Assert(FALSE);
|
|
retCode = TOD_CONTINUE;
|
|
goto bailout;
|
|
}
|
|
|
|
Assert(lpRei);
|
|
|
|
if (!(lptzLocalPath = GetTempFileForCSC(NULL)))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("CheckCSCDirCallback: failed to get temp file\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
if (!CopyShadow(hShadowDB, lpSI->hDir, lpSI->hShadow, lptzLocalPath))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("CheckCSCDirCallback: failed to make local copy\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
// Let us create the real name x:\foo\bar
|
|
lstrcpy(szRemoteName, lpRei->tzDrive);
|
|
lstrcat(szRemoteName, lpCP->lpRemotePath);
|
|
CompareFilePrefixes(
|
|
szRemoteName,
|
|
lptzLocalPath,
|
|
lpSI,
|
|
lpFind32,
|
|
lpRei->lpfnMergeProgress,
|
|
lpRei->dwContext
|
|
);
|
|
|
|
|
|
bailout:
|
|
if (lptzLocalPath)
|
|
{
|
|
DeleteFile(lptzLocalPath);
|
|
LocalFree(lptzLocalPath);
|
|
}
|
|
if (lpCP) {
|
|
FreeCopyParams(lpCP);
|
|
}
|
|
|
|
return retCode;
|
|
}
|
|
|
|
BOOL
|
|
PUBLIC
|
|
CheckCSCShare(
|
|
_TCHAR *lptzShare,
|
|
LPCSCPROC lpfnMergeProgress,
|
|
DWORD dwContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fConnected=FALSE, fDone = FALSE;
|
|
BOOL fStamped = FALSE, fInsertInList = FALSE, fBeginReint = FALSE, fDisabledShadowing = FALSE;
|
|
HANDLE hShadowDB;
|
|
SHADOWINFO sSI;
|
|
int iRet;
|
|
DWORD dwError=NO_ERROR;
|
|
TCHAR tzFullPath[MAX_PATH+1];
|
|
WIN32_FIND_DATA sFind32;
|
|
REINT_INFO sRei;
|
|
|
|
if (!LpBreakPath(lptzShare, TRUE, &fDone) && !fDone)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
fDone = FALSE;
|
|
|
|
memset(&sRei, 0, sizeof(sRei));
|
|
sRei.lpfnMergeProgress = lpfnMergeProgress;
|
|
sRei.dwContext = dwContext;
|
|
memset(sRei.tzDrive, 0, sizeof(sRei.tzDrive));
|
|
|
|
if ((hShadowDB = OpenShadowDatabaseIO()) ==INVALID_HANDLE_VALUE)
|
|
{
|
|
ReintKdPrint(BADERRORS, ("CheckShare: failed to open database\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
memset(&sFind32, 0, sizeof(sFind32));
|
|
lstrcpy(sFind32.cFileName, lptzShare);
|
|
|
|
if(!GetShadowEx(hShadowDB, 0, &sFind32, &sSI)||(!sSI.hShadow))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("CheckShare: failed to get the share info\r\n"));
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto bailout;
|
|
}
|
|
|
|
lstrcpy(tzFullPath, lptzShare);
|
|
|
|
dwError = DWConnectNet(lptzShare, sRei.tzDrive, NULL, NULL, NULL, 0, NULL);
|
|
if ((dwError != WN_SUCCESS) && (dwError != WN_CONNECTED_OTHER_PASSWORD_DEFAULT))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("CheckCSCOneShare: Error %d, couldn't connect to %s\r\n", dwError, lptzShare));
|
|
SetLastError(dwError);
|
|
goto bailout;
|
|
|
|
}
|
|
|
|
ReintKdPrint(MERGE, ("CSC.CheckShare: mapped drive letter %ls \r\n", sRei.tzDrive));
|
|
|
|
if (lpfnMergeProgress)
|
|
{
|
|
(*lpfnMergeProgress)(lptzShare, 0, 0, 0, NULL, CSCPROC_REASON_BEGIN, 0, 0, dwContext);
|
|
}
|
|
|
|
fConnected = TRUE;
|
|
|
|
iRet = TraverseOneDirectory(hShadowDB, NULL, 0, sSI.hShadow, tzFullPath, CheckCSCDirCallback, &sRei);
|
|
|
|
if (lpfnMergeProgress)
|
|
{
|
|
(*lpfnMergeProgress)(lptzShare, 0, 0, 0, NULL, CSCPROC_REASON_END, 0, 0, dwContext);
|
|
}
|
|
|
|
fDone = TRUE;
|
|
|
|
bailout:
|
|
|
|
CloseShadowDatabaseIO(hShadowDB);
|
|
|
|
if (fConnected) {
|
|
|
|
if(DWDisconnectDriveMappedNet(sRei.tzDrive, TRUE))
|
|
{
|
|
ReintKdPrint(BADERRORS, ("Failed disconnection of merge drive \r\n"));
|
|
}
|
|
else
|
|
{
|
|
ReintKdPrint(MERGE, ("Disconnected merge drive \r\n"));
|
|
}
|
|
}
|
|
|
|
return (fDone);
|
|
}
|
|
#endif
|