1360 lines
37 KiB
C
1360 lines
37 KiB
C
/*
|
|
* recon.c - Reconciliation routines.
|
|
*/
|
|
|
|
|
|
/* Headers
|
|
**********/
|
|
|
|
#include "project.h"
|
|
#pragma hdrstop
|
|
|
|
#include "stub.h"
|
|
#include "oleutil.h"
|
|
|
|
|
|
/***************************** Private Functions *****************************/
|
|
|
|
/* Module Prototypes
|
|
********************/
|
|
|
|
PRIVATE_CODE void GenerateShellEvents(PCRECITEM);
|
|
PRIVATE_CODE TWINRESULT MyReconcileItem(PCRECITEM, RECSTATUSPROC, LPARAM, DWORD, HWND, HWND);
|
|
PRIVATE_CODE void UpdateObjectTwinStates(PCRECITEM);
|
|
PRIVATE_CODE TWINRESULT CopyFolder(PCRECITEM, RECSTATUSPROC, LPARAM);
|
|
PRIVATE_CODE TWINRESULT DeleteFolder(PCRECITEM, RECSTATUSPROC, LPARAM);
|
|
PRIVATE_CODE TWINRESULT DealWithCopy(PCRECITEM, RECSTATUSPROC, LPARAM, DWORD, HWND, HWND);
|
|
PRIVATE_CODE TWINRESULT DealWithMerge(PCRECITEM, RECSTATUSPROC, LPARAM, DWORD, HWND, HWND);
|
|
PRIVATE_CODE TWINRESULT DealWithDelete(PCRECITEM, RECSTATUSPROC, LPARAM);
|
|
PRIVATE_CODE ULONG CountRECNODEs(PCRECITEM, RECNODEACTION);
|
|
PRIVATE_CODE TWINRESULT UpdateRecNodeFileStamps(PCRECITEM);
|
|
PRIVATE_CODE BOOL DeletedTwinsInRecItem(PCRECITEM);
|
|
|
|
|
|
/*
|
|
** GenerateShellEvents()
|
|
**
|
|
** Notifies the Shell about reconciliation events for a RECITEM.
|
|
**
|
|
** Arguments: pcri - reconciled RECITEM to notify Shell about
|
|
**
|
|
** Returns: void
|
|
**
|
|
** Side Effects: None
|
|
*/
|
|
PRIVATE_CODE void GenerateShellEvents(PCRECITEM pcri)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
|
|
/* Any reconciliation events to report? */
|
|
|
|
if (pcri->riaction == RIA_NOTHING ||
|
|
pcri->riaction == RIA_COPY ||
|
|
pcri->riaction == RIA_MERGE ||
|
|
pcri->riaction == RIA_DELETE)
|
|
{
|
|
PRECNODE prn;
|
|
|
|
/*
|
|
* Yes. Send an appropriate notification to the Shell about the file
|
|
* operations assumed carried out during reconciliation. The file system
|
|
* is lame, and does not support notifications for some of the
|
|
* interesting reconciliation operations. We also generate a specious
|
|
* update notification for the source file in a copy operation to cause
|
|
* the Briefcase ui to recalculate the status string for that file, even
|
|
* though the file itself has not changed.
|
|
*/
|
|
|
|
for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
|
|
{
|
|
BOOL bNotify;
|
|
NOTIFYSHELLEVENT nse;
|
|
LPCTSTR pcszPath;
|
|
TCHAR rgchPath[MAX_PATH_LEN];
|
|
|
|
/* How shall I notify you? Let me enumerate the ways. */
|
|
|
|
bNotify = TRUE;
|
|
|
|
if (IsFolderObjectTwinName(pcri->pcszName))
|
|
{
|
|
nse = NSE_UPDATE_FOLDER;
|
|
|
|
pcszPath = prn->pcszFolder;
|
|
|
|
switch (prn->rnaction)
|
|
{
|
|
/*
|
|
* Notifications about folders that were copied or deleted
|
|
* during reconciliation were sent during reconciliation. Don't
|
|
* send redundant notifications.
|
|
*/
|
|
case RNA_COPY_TO_ME:
|
|
case RNA_DELETE_ME:
|
|
bNotify = FALSE;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(prn->rnaction == RNA_NOTHING ||
|
|
prn->rnaction == RNA_COPY_FROM_ME);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nse = NSE_UPDATE_ITEM;
|
|
|
|
ComposePath(rgchPath, prn->pcszFolder, pcri->pcszName);
|
|
pcszPath = rgchPath;
|
|
|
|
switch (prn->rnaction)
|
|
{
|
|
case RNA_COPY_TO_ME:
|
|
if (prn->rnstate == RNS_DOES_NOT_EXIST ||
|
|
prn->rnstate == RNS_DELETED)
|
|
nse = NSE_CREATE_ITEM;
|
|
break;
|
|
|
|
case RNA_DELETE_ME:
|
|
nse = NSE_DELETE_ITEM;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(prn->rnaction == RNA_NOTHING ||
|
|
prn->rnaction == RNA_COPY_FROM_ME ||
|
|
prn->rnaction == RNA_MERGE_ME);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bNotify)
|
|
NotifyShell(pcszPath, nse);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** MyReconcileItem()
|
|
**
|
|
** Reconciles a reconciliation item.
|
|
**
|
|
** Arguments: pcri - pointer to reconciliation item to be reconciled
|
|
**
|
|
** Side Effects:
|
|
*/
|
|
PRIVATE_CODE TWINRESULT MyReconcileItem(PCRECITEM pcri, RECSTATUSPROC rsp,
|
|
LPARAM lpCallbackData, DWORD dwFlags,
|
|
HWND hwndOwner,
|
|
HWND hwndProgressFeedback)
|
|
{
|
|
TWINRESULT tr;
|
|
|
|
/* lpCallbackData may be any value. */
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
ASSERT(! rsp ||
|
|
IS_VALID_CODE_PTR(rsp, RECSTATUSPROC));
|
|
ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_RI_FLAGS));
|
|
ASSERT(IS_FLAG_CLEAR(dwFlags, RI_FL_ALLOW_UI) ||
|
|
IS_VALID_HANDLE(hwndOwner, WND));
|
|
ASSERT(IS_FLAG_CLEAR(dwFlags, RI_FL_FEEDBACK_WINDOW_VALID) ||
|
|
IS_VALID_HANDLE(hwndProgressFeedback, WND));
|
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
LPCTSTR pcszGerund;
|
|
|
|
switch (pcri->riaction)
|
|
{
|
|
case RIA_NOTHING:
|
|
pcszGerund = TEXT("Taking no action on");
|
|
break;
|
|
|
|
case RIA_COPY:
|
|
pcszGerund = TEXT("Copying");
|
|
break;
|
|
|
|
case RIA_MERGE:
|
|
pcszGerund = TEXT("Merging");
|
|
break;
|
|
|
|
case RIA_BROKEN_MERGE:
|
|
pcszGerund = TEXT("Broken merge for");
|
|
break;
|
|
|
|
case RIA_DELETE:
|
|
pcszGerund = TEXT("Deleting");
|
|
break;
|
|
|
|
default:
|
|
pcszGerund = TEXT("Unknown action specifed for");
|
|
break;
|
|
}
|
|
|
|
TRACE_OUT((TEXT("MyReconcileItem(): %s %s."),
|
|
pcszGerund,
|
|
*(pcri->pcszName) ? pcri->pcszName : TEXT("folder")));
|
|
}
|
|
|
|
#endif
|
|
|
|
switch (pcri->riaction)
|
|
{
|
|
case RIA_NOTHING:
|
|
tr = TR_SUCCESS;
|
|
break;
|
|
|
|
case RIA_COPY:
|
|
if (*(pcri->pcszName))
|
|
tr = DealWithCopy(pcri, rsp, lpCallbackData, dwFlags, hwndOwner,
|
|
hwndProgressFeedback);
|
|
else
|
|
tr = CopyFolder(pcri, rsp, lpCallbackData);
|
|
|
|
if (tr == TR_SUCCESS)
|
|
tr = UpdateRecNodeFileStamps(pcri);
|
|
break;
|
|
|
|
case RIA_MERGE:
|
|
tr = DealWithMerge(pcri, rsp, lpCallbackData, dwFlags, hwndOwner,
|
|
hwndProgressFeedback);
|
|
|
|
if (tr == TR_SUCCESS || tr == TR_MERGE_INCOMPLETE)
|
|
tr = UpdateRecNodeFileStamps(pcri);
|
|
break;
|
|
|
|
case RIA_DELETE:
|
|
if (*(pcri->pcszName))
|
|
tr = DealWithDelete(pcri, rsp, lpCallbackData);
|
|
else
|
|
{
|
|
tr = DeleteFolder(pcri, rsp, lpCallbackData);
|
|
|
|
if (tr == TR_DEST_WRITE_FAILED)
|
|
tr = TR_SUCCESS;
|
|
}
|
|
|
|
if (tr == TR_SUCCESS)
|
|
tr = UpdateRecNodeFileStamps(pcri);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(pcri->riaction == RIA_BROKEN_MERGE);
|
|
tr = TR_NO_MERGE_HANDLER;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Only update briefcase time stamps if the entire reconciliation operation
|
|
* on this RECITEM is successful. However, the RECNODE time stamps in the
|
|
* given RECITEM have been updated as they were changed.
|
|
*/
|
|
|
|
if (tr == TR_SUCCESS)
|
|
{
|
|
UpdateObjectTwinStates(pcri);
|
|
|
|
DetermineDeletionPendingState(pcri);
|
|
|
|
DeleteTwinsFromRecItem(pcri);
|
|
}
|
|
else if (tr == TR_MERGE_INCOMPLETE)
|
|
tr = TR_SUCCESS;
|
|
|
|
if (tr == TR_SUCCESS)
|
|
GenerateShellEvents(pcri);
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** UpdateObjectTwinStates()
|
|
**
|
|
** Updates the last reconciliation time stamp of the twin family and the object
|
|
** twins associated with a RECITEM that has just been successfully reconciled.
|
|
**
|
|
** Arguments: pri - pointer to reconciliation item that has just been
|
|
** reconciled
|
|
**
|
|
** Returns: TWINRESULT
|
|
**
|
|
** Side Effects: Implicitly deletes twin family if the last known state of
|
|
** every component object twins is non-existence.
|
|
**
|
|
** N.b., this function assumes that the actions specified in the RECNODEs of
|
|
** the RECITEM were carried out successfully.
|
|
**
|
|
** This function assumes that all available RECNODES were reconciled.
|
|
**
|
|
** This function assumes that the time stamp fields of the RECNODEs associated
|
|
** with objects that were modified during reconciliation were filled in
|
|
** immediately after each of those RECNODEs was reconciled. I.e., that all
|
|
** time stamp fields in reconciled RECNODEs are up-to-date with respect to any
|
|
** modifications that may have been made to them during reconciliation.
|
|
*/
|
|
PRIVATE_CODE void UpdateObjectTwinStates(PCRECITEM pcri)
|
|
{
|
|
PRECNODE prn;
|
|
BOOL bNewVersion = FALSE;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
|
|
/*
|
|
* There is a new version if any changed or never reconciled RECNODEs were
|
|
* reconciled as RNA_NOTHING, RNA_COPY_FROM_ME, or RNA_MERGE_ME.
|
|
*/
|
|
|
|
for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
|
|
{
|
|
if ((prn->rnstate == RNS_NEVER_RECONCILED ||
|
|
prn->rnstate == RNS_CHANGED) &&
|
|
(prn->rnaction == RNA_NOTHING ||
|
|
prn->rnaction == RNA_COPY_FROM_ME ||
|
|
prn->rnaction == RNA_MERGE_ME))
|
|
bNewVersion = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Save file stamps of available files. Mark unavailable object twins not
|
|
* reconciled if any new versions exist in the reconciled set of files.
|
|
*/
|
|
|
|
for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
|
|
{
|
|
POBJECTTWIN pot;
|
|
|
|
pot = (POBJECTTWIN)(prn->hObjectTwin);
|
|
|
|
if (prn->fsCurrent.fscond != FS_COND_UNAVAILABLE)
|
|
{
|
|
ClearStubFlag(&(pot->stub), STUB_FL_NOT_RECONCILED);
|
|
pot->fsLastRec = prn->fsCurrent;
|
|
|
|
/*
|
|
* Remember not to delete object twins as requested. Treat any folder
|
|
* that could not be deleted as implicitly kept as well.
|
|
*/
|
|
|
|
if (IS_FLAG_SET(prn->dwFlags, RN_FL_DELETION_SUGGESTED) &&
|
|
IsTwinFamilyDeletionPending((PCTWINFAMILY)(pcri->hTwinFamily)) &&
|
|
(pcri->riaction == RIA_NOTHING ||
|
|
pcri->riaction == RIA_DELETE) &&
|
|
(prn->rnaction != RNA_DELETE_ME ||
|
|
IsFolderObjectTwinName(pcri->pcszName)))
|
|
{
|
|
SetStubFlag(&(pot->stub), STUB_FL_KEEP);
|
|
|
|
TRACE_OUT((TEXT("UpdateObjectTwinStates(): Object twin %s\\%s will be kept and not deleted."),
|
|
prn->pcszFolder,
|
|
prn->priParent->pcszName));
|
|
}
|
|
}
|
|
else if (bNewVersion &&
|
|
IsReconciledFileStamp(&(prn->fsLast)))
|
|
{
|
|
SetStubFlag(&(pot->stub), STUB_FL_NOT_RECONCILED);
|
|
|
|
WARNING_OUT((TEXT("UpdateObjectTwinStates(): Marked %s\\%s not reconciled."),
|
|
prn->pcszFolder,
|
|
pcri->pcszName));
|
|
}
|
|
}
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** CopyFolder()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE TWINRESULT CopyFolder(PCRECITEM pcri, RECSTATUSPROC rsp,
|
|
LPARAM lpCallbackData)
|
|
{
|
|
TWINRESULT tr;
|
|
RECSTATUSUPDATE rsu;
|
|
|
|
/* lpCallbackData may be any value. */
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
ASSERT(! rsp ||
|
|
IS_VALID_CODE_PTR(rsp, RECSTATUSPROC));
|
|
|
|
ASSERT(IsFolderObjectTwinName(pcri->pcszName));
|
|
|
|
rsu.ulScale = CountRECNODEs(pcri, RNA_COPY_TO_ME);
|
|
ASSERT(rsu.ulScale > 0);
|
|
rsu.ulProgress = 0;
|
|
|
|
if (NotifyReconciliationStatus(rsp, RS_BEGIN_COPY, (LPARAM)&rsu,
|
|
lpCallbackData))
|
|
{
|
|
PRECNODE prn;
|
|
|
|
tr = TR_SUCCESS;
|
|
|
|
for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
|
|
{
|
|
if (prn->rnaction == RNA_COPY_TO_ME)
|
|
tr = CreateFolders(prn->pcszFolder, (HPATH)(prn->hvid));
|
|
}
|
|
|
|
if (tr == TR_SUCCESS)
|
|
{
|
|
/* 100% complete. */
|
|
|
|
rsu.ulProgress = rsu.ulScale;
|
|
|
|
/* Don't allow abort. */
|
|
|
|
NotifyReconciliationStatus(rsp, RS_END_COPY, (LPARAM)&rsu,
|
|
lpCallbackData);
|
|
}
|
|
}
|
|
else
|
|
tr = TR_ABORT;
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** DeleteFolder()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE TWINRESULT DeleteFolder(PCRECITEM pcri, RECSTATUSPROC rsp,
|
|
LPARAM lpCallbackData)
|
|
{
|
|
TWINRESULT tr;
|
|
RECSTATUSUPDATE rsu;
|
|
|
|
/* lpCallbackData may be any value. */
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
ASSERT(! rsp ||
|
|
IS_VALID_CODE_PTR(rsp, RECSTATUSPROC));
|
|
|
|
ASSERT(IsFolderObjectTwinName(pcri->pcszName));
|
|
|
|
rsu.ulScale = CountRECNODEs(pcri, RNA_DELETE_ME);
|
|
ASSERT(rsu.ulScale > 0);
|
|
rsu.ulProgress = 0;
|
|
|
|
if (NotifyReconciliationStatus(rsp, RS_BEGIN_DELETE, (LPARAM)&rsu,
|
|
lpCallbackData))
|
|
{
|
|
PRECNODE prn;
|
|
|
|
tr = TR_SUCCESS;
|
|
|
|
for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
|
|
{
|
|
if (prn->rnaction == RNA_DELETE_ME)
|
|
tr = DestroySubtree(prn->pcszFolder, (HPATH)(prn->hvid));
|
|
}
|
|
|
|
if (tr == TR_SUCCESS)
|
|
{
|
|
/* 100% complete. */
|
|
|
|
rsu.ulProgress = rsu.ulScale;
|
|
|
|
/* Don't allow abort. */
|
|
|
|
NotifyReconciliationStatus(rsp, RS_END_DELETE, (LPARAM)&rsu,
|
|
lpCallbackData);
|
|
}
|
|
}
|
|
else
|
|
tr = TR_ABORT;
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** DealWithCopy()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE TWINRESULT DealWithCopy(PCRECITEM pcri, RECSTATUSPROC rsp,
|
|
LPARAM lpCallbackData, DWORD dwInFlags,
|
|
HWND hwndOwner, HWND hwndProgressFeedback)
|
|
{
|
|
TWINRESULT tr;
|
|
PRECNODE prnCopySrc;
|
|
|
|
/* lpCallbackData may be any value. */
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
ASSERT(! rsp ||
|
|
IS_VALID_CODE_PTR(rsp, RECSTATUSPROC));
|
|
ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_RI_FLAGS));
|
|
ASSERT(IS_FLAG_CLEAR(dwInFlags, RI_FL_ALLOW_UI) ||
|
|
IS_VALID_HANDLE(hwndOwner, WND));
|
|
ASSERT(IS_FLAG_CLEAR(dwInFlags, RI_FL_FEEDBACK_WINDOW_VALID) ||
|
|
IS_VALID_HANDLE(hwndProgressFeedback, WND));
|
|
|
|
tr = FindCopySource(pcri, &prnCopySrc);
|
|
|
|
if (EVAL(tr == TR_SUCCESS))
|
|
tr = CopyHandler(prnCopySrc, rsp, lpCallbackData, dwInFlags, hwndOwner,
|
|
hwndProgressFeedback);
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** DealWithMerge()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE TWINRESULT DealWithMerge(PCRECITEM pcri, RECSTATUSPROC rsp,
|
|
LPARAM lpCallbackData, DWORD dwInFlags,
|
|
HWND hwndOwner,
|
|
HWND hwndProgressFeedback)
|
|
{
|
|
TWINRESULT tr;
|
|
HRESULT hr;
|
|
PRECNODE prnMergeDest;
|
|
PRECNODE prnMergedResult;
|
|
|
|
/* lpCallbackData may be any value. */
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
ASSERT(! rsp ||
|
|
IS_VALID_CODE_PTR(rsp, RECSTATUSPROC));
|
|
ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_RI_FLAGS));
|
|
ASSERT(IS_FLAG_CLEAR(dwInFlags, RI_FL_ALLOW_UI) ||
|
|
IS_VALID_HANDLE(hwndOwner, WND));
|
|
ASSERT(IS_FLAG_CLEAR(dwInFlags, RI_FL_FEEDBACK_WINDOW_VALID) ||
|
|
IS_VALID_HANDLE(hwndProgressFeedback, WND));
|
|
|
|
ChooseMergeDestination(pcri, &prnMergeDest);
|
|
|
|
hr = MergeHandler(prnMergeDest, rsp, lpCallbackData, dwInFlags, hwndOwner,
|
|
hwndProgressFeedback, &prnMergedResult);
|
|
|
|
if (hr == S_OK ||
|
|
hr == REC_S_NOTCOMPLETEBUTPROPAGATE)
|
|
{
|
|
tr = CopyHandler(prnMergedResult, rsp, lpCallbackData, dwInFlags,
|
|
hwndOwner, hwndProgressFeedback);
|
|
|
|
if (tr == TR_SUCCESS)
|
|
TRACE_OUT((TEXT("DealWithMerge(): Propagated merged result %s\\%s successfully."),
|
|
prnMergedResult->pcszFolder,
|
|
pcri->pcszName));
|
|
else
|
|
WARNING_OUT((TEXT("DealWithMerge(): Propagating merged result %s\\%s failed."),
|
|
prnMergedResult->pcszFolder,
|
|
pcri->pcszName));
|
|
}
|
|
else
|
|
tr = TR_SUCCESS;
|
|
|
|
return((tr == TR_SUCCESS) ? TranslateHRESULTToTWINRESULT(hr)
|
|
: tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** DealWithDelete()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE TWINRESULT DealWithDelete(PCRECITEM pcri, RECSTATUSPROC rsp,
|
|
LPARAM lpCallbackData)
|
|
{
|
|
TWINRESULT tr;
|
|
RECSTATUSUPDATE rsu;
|
|
|
|
/* lpCallbackData may be any value. */
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
ASSERT(! rsp ||
|
|
IS_VALID_CODE_PTR(rsp, RECSTATUSPROC));
|
|
|
|
rsu.ulScale = CountRECNODEs(pcri, RNA_DELETE_ME);
|
|
ASSERT(rsu.ulScale > 0);
|
|
rsu.ulProgress = 0;
|
|
|
|
if (NotifyReconciliationStatus(rsp, RS_BEGIN_DELETE, (LPARAM)&rsu,
|
|
lpCallbackData))
|
|
{
|
|
PRECNODE prn;
|
|
|
|
tr = TR_SUCCESS;
|
|
|
|
for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
|
|
{
|
|
if (prn->rnaction == RNA_DELETE_ME)
|
|
{
|
|
TCHAR rgchPath[MAX_PATH_LEN];
|
|
|
|
ComposePath(rgchPath, prn->pcszFolder, prn->priParent->pcszName);
|
|
ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath));
|
|
|
|
if (MyIsPathOnVolume(rgchPath, (HPATH)(prn->hvid)))
|
|
{
|
|
if (DeleteFile(rgchPath))
|
|
WARNING_OUT((TEXT("DealWithDelete(): Deleted file %s."),
|
|
rgchPath));
|
|
else
|
|
{
|
|
tr = TR_DEST_OPEN_FAILED;
|
|
|
|
WARNING_OUT((TEXT("DealWithDelete(): Failed to delete file %s."),
|
|
rgchPath));
|
|
}
|
|
}
|
|
else
|
|
tr = TR_UNAVAILABLE_VOLUME;
|
|
}
|
|
}
|
|
|
|
if (tr == TR_SUCCESS)
|
|
{
|
|
/* 100% complete. */
|
|
|
|
rsu.ulProgress = rsu.ulScale;
|
|
|
|
/* Don't allow abort. */
|
|
|
|
NotifyReconciliationStatus(rsp, RS_END_DELETE, (LPARAM)&rsu,
|
|
lpCallbackData);
|
|
}
|
|
}
|
|
else
|
|
tr = TR_ABORT;
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** CountRECNODEs()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE ULONG CountRECNODEs(PCRECITEM pcri, RECNODEACTION rnaction)
|
|
{
|
|
ULONG ulc = 0;
|
|
PRECNODE prn;
|
|
|
|
for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
|
|
{
|
|
if (prn->rnaction == rnaction)
|
|
{
|
|
ASSERT(ulc < ULONG_MAX);
|
|
ulc++;
|
|
}
|
|
}
|
|
|
|
return(ulc);
|
|
}
|
|
|
|
|
|
/*
|
|
** UpdateRecNodeFileStamps()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE TWINRESULT UpdateRecNodeFileStamps(PCRECITEM pcri)
|
|
{
|
|
TWINRESULT tr;
|
|
PRECNODE prn;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
|
|
tr = TR_SUCCESS;
|
|
|
|
for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
|
|
{
|
|
ASSERT(IS_VALID_HANDLE(prn->hObjectTwin, OBJECTTWIN));
|
|
|
|
/* Was the RECNODE supposed to be reconciled? */
|
|
|
|
/*
|
|
* BUGBUG: We should avoid updating file stamps of copy sources here in
|
|
* the SimpleCopy() case.
|
|
*/
|
|
|
|
if (prn->rnaction != RNA_NOTHING)
|
|
{
|
|
ASSERT(prn->fsCurrent.fscond != FS_COND_UNAVAILABLE);
|
|
|
|
/* Leave prn->fsLast as pot->fsLastRec here. */
|
|
|
|
MyGetFileStampByHPATH(((PCOBJECTTWIN)(prn->hObjectTwin))->hpath,
|
|
GetString(((PCOBJECTTWIN)(prn->hObjectTwin))->ptfParent->hsName),
|
|
&(prn->fsCurrent));
|
|
}
|
|
}
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** DeletedTwinsInRecItem()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL DeletedTwinsInRecItem(PCRECITEM pcri)
|
|
{
|
|
BOOL bResult = TRUE;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
|
|
/* Has the associated twin family been deleted? */
|
|
|
|
if (IsStubFlagClear(&(((PTWINFAMILY)(pcri->hTwinFamily))->stub), STUB_FL_UNLINKED))
|
|
{
|
|
PRECNODE prn;
|
|
|
|
/* No. Have any of the associated object twins been deleted? */
|
|
|
|
for (prn = pcri->prnFirst;
|
|
prn && IsStubFlagClear(&(((PCOBJECTTWIN)(prn->hObjectTwin))->stub), STUB_FL_UNLINKED);
|
|
prn = prn->prnNext)
|
|
;
|
|
|
|
if (! prn)
|
|
bResult = FALSE;
|
|
}
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/****************************** Public Functions *****************************/
|
|
|
|
|
|
/*
|
|
** CopyFileStampFromFindData()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void CopyFileStampFromFindData(PCWIN32_FIND_DATA pcwfdSrc,
|
|
PFILESTAMP pfsDest)
|
|
{
|
|
ASSERT(IS_VALID_READ_PTR(pcwfdSrc, CWIN32_FIND_DATA));
|
|
ASSERT(IS_VALID_WRITE_PTR(pfsDest, FILESTAMP));
|
|
|
|
pfsDest->dwcbHighLength = pcwfdSrc->nFileSizeHigh;
|
|
pfsDest->dwcbLowLength = pcwfdSrc->nFileSizeLow;
|
|
|
|
/* Convert to local time and save that too */
|
|
|
|
if ( !FileTimeToLocalFileTime(&pcwfdSrc->ftLastWriteTime, &pfsDest->ftModLocal) )
|
|
{
|
|
/* Just copy the time if FileTimeToLocalFileTime failed */
|
|
|
|
pfsDest->ftModLocal = pcwfdSrc->ftLastWriteTime;
|
|
}
|
|
pfsDest->ftMod = pcwfdSrc->ftLastWriteTime;
|
|
pfsDest->fscond = FS_COND_EXISTS;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pfsDest, CFILESTAMP));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** MyGetFileStamp()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void MyGetFileStamp(LPCTSTR pcszFile, PFILESTAMP pfs)
|
|
{
|
|
WIN32_FIND_DATA wfd;
|
|
HANDLE hff;
|
|
|
|
ASSERT(IS_VALID_STRING_PTR(pcszFile, CSTR));
|
|
ASSERT(IS_VALID_WRITE_PTR(pfs, FILESTAMP));
|
|
|
|
ZeroMemory(pfs, sizeof(*pfs));
|
|
|
|
hff = FindFirstFile(pcszFile, &wfd);
|
|
|
|
if (hff != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (! IS_ATTR_DIR(wfd.dwFileAttributes))
|
|
CopyFileStampFromFindData(&wfd, pfs);
|
|
else
|
|
pfs->fscond = FS_COND_EXISTS;
|
|
|
|
EVAL(FindClose(hff));
|
|
}
|
|
else
|
|
pfs->fscond = FS_COND_DOES_NOT_EXIST;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pfs, CFILESTAMP));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** MyGetFileStampByHPATH()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects:
|
|
*/
|
|
PUBLIC_CODE void MyGetFileStampByHPATH(HPATH hpath, LPCTSTR pcszSubPath,
|
|
PFILESTAMP pfs)
|
|
{
|
|
ASSERT(IS_VALID_HANDLE(hpath, PATH));
|
|
ASSERT(! pcszSubPath ||
|
|
IS_VALID_STRING_PTR(pcszSubPath, CSTR));
|
|
ASSERT(IS_VALID_WRITE_PTR(pfs, FILESTAMP));
|
|
|
|
if (IsPathVolumeAvailable(hpath))
|
|
{
|
|
TCHAR rgchPath[MAX_PATH_LEN];
|
|
|
|
/* The root of the file's path is accessible. */
|
|
|
|
GetPathString(hpath, rgchPath);
|
|
if (pcszSubPath)
|
|
CatPath(rgchPath, pcszSubPath);
|
|
ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath));
|
|
|
|
MyGetFileStamp(rgchPath, pfs);
|
|
}
|
|
else
|
|
{
|
|
ZeroMemory(pfs, sizeof(*pfs));
|
|
pfs->fscond = FS_COND_UNAVAILABLE;
|
|
}
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pfs, CFILESTAMP));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** MyCompareFileStamps()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
**
|
|
** Any FS_COND_UNAVAILABLE == any FS_COND_UNAVAILABLE.
|
|
** Any FS_COND_UNAVAILABLE < any FS_COND_DOES_NOT_EXIST.
|
|
** Any FS_COND_DOES_NOT_EXIST == any FS_COND_DOES_NOT_EXIST.
|
|
** Any FS_COND_DOES_NOT_EXIST < any FS_COND_EXISTS.
|
|
** Two FS_COND_EXISTS are compared by date and time.
|
|
**
|
|
** Hack Warning: This function depends upon the constant values of
|
|
** FS_COND_UNAVAILABLE, FS_COND_DOES_NOT_EXIST, and FS_COND_EXISTS being in
|
|
** increasing order, i.e.,
|
|
**
|
|
** FS_COND_UNAVAILABLE < FS_COND_DOES_NOT_EXIST < FS_COND_EXISTS
|
|
*/
|
|
PUBLIC_CODE int MyCompareFileStamps(PCFILESTAMP pcfs1, PCFILESTAMP pcfs2)
|
|
{
|
|
int nResult;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcfs1, CFILESTAMP));
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcfs2, CFILESTAMP));
|
|
|
|
nResult = (int)(pcfs1->fscond - pcfs2->fscond);
|
|
|
|
if (! nResult && pcfs1->fscond == FS_COND_EXISTS)
|
|
{
|
|
/* File times are stored as UTC times. However, files on FAT
|
|
** file systems only store the local time. This means the UTC
|
|
** is derived from the local time, and fudged depending on the
|
|
** current timezone info. This means that the UTC time will
|
|
** differ between timezone changes.
|
|
**
|
|
** For remote files, the time's derivation depends on the server.
|
|
** NTFS servers provide the absolute UTC time, regardless of timezone.
|
|
** These are the best. Likewise, NWServer keeps track of the
|
|
** timezone and puts the UTC time on the wire like NTFS. FAT
|
|
** systems convert the local time to UTC time based on the server's
|
|
** timezone, and places the UTC time on the wire. Netware 3.31
|
|
** and some SMB servers put the local time on the wire and have
|
|
** the client convert to UTC time, so it uses the client's timezone.
|
|
**
|
|
** One way to cover most of the holes that occur due to timezone
|
|
** changes is store both the UTC time and the local time. If either
|
|
** are the same, then the file has not changed.
|
|
*/
|
|
|
|
BOOL bModEqual = (pcfs1->ftMod.dwHighDateTime == pcfs2->ftMod.dwHighDateTime);
|
|
BOOL bModLocalEqual = (pcfs1->ftModLocal.dwHighDateTime == pcfs2->ftModLocal.dwHighDateTime);
|
|
|
|
if (bModEqual || bModLocalEqual)
|
|
{
|
|
if (bModEqual && pcfs1->ftMod.dwLowDateTime == pcfs2->ftMod.dwLowDateTime ||
|
|
bModLocalEqual && pcfs1->ftModLocal.dwLowDateTime == pcfs2->ftModLocal.dwLowDateTime)
|
|
{
|
|
if (pcfs1->dwcbHighLength == pcfs2->dwcbHighLength)
|
|
{
|
|
if (pcfs1->dwcbLowLength == pcfs2->dwcbLowLength)
|
|
nResult = CR_EQUAL;
|
|
else if (pcfs1->dwcbLowLength < pcfs2->dwcbLowLength)
|
|
nResult = CR_FIRST_SMALLER;
|
|
else
|
|
nResult = CR_FIRST_LARGER;
|
|
}
|
|
else if (pcfs1->dwcbHighLength < pcfs2->dwcbHighLength)
|
|
nResult = CR_FIRST_SMALLER;
|
|
else
|
|
nResult = CR_FIRST_LARGER;
|
|
}
|
|
else if (pcfs1->ftMod.dwLowDateTime < pcfs2->ftMod.dwLowDateTime)
|
|
nResult = CR_FIRST_SMALLER;
|
|
else
|
|
nResult = CR_FIRST_LARGER;
|
|
}
|
|
else if (pcfs1->ftMod.dwHighDateTime < pcfs2->ftMod.dwHighDateTime)
|
|
nResult = CR_FIRST_SMALLER;
|
|
else
|
|
nResult = CR_FIRST_LARGER;
|
|
}
|
|
|
|
return(MapIntToComparisonResult(nResult));
|
|
}
|
|
|
|
|
|
/***************************** Exported Functions ****************************/
|
|
|
|
/* RAIDRAID: (16205) AutoDoc RECSTATUSPROC messages below. */
|
|
|
|
/******************************************************************************
|
|
|
|
@doc SYNCENGAPI
|
|
|
|
@api TWINRESULT | ReconcileItem | Reconciles a reconciliation item created by
|
|
CreateRecList().
|
|
|
|
@parm PCRECITEM | pcri | A pointer to a reconciliation item to be reconciled.
|
|
|
|
@parm RECSTATUSPROC | rsp | A procedure instance address of a callback function
|
|
to be called with status information during the reconciliation of the given
|
|
RECITEM. rsp may be NULL to indicate that no reconciliation status callback
|
|
function is to be called. (See the reconciliation handler SPI documentation
|
|
for details.)
|
|
|
|
@parm LPARAM | lpCallbackData | Callback data to be supplied to the
|
|
reconciliation status callback function. If rsp is NULL, lpCallbackData is
|
|
ignored.
|
|
|
|
@parm DWORD | dwFlags | A bit mask of flags. This parameter may be any
|
|
combination of the following values:
|
|
RI_FL_ALLOW_UI - Allow interaction with the user during reconciliation.
|
|
RI_FL_FEEDBACK_WINDOW_VALID - hwndProgressFeedback is valid, and may be used
|
|
to communicate reconciliation progress information to the user during
|
|
reconciliation.
|
|
|
|
@parm HWND | hwndOwner | A handle to the parent window to be used when
|
|
requesting user interaction. This parameter is ignored if the RI_FL_ALLOW_UI
|
|
flag is clear.
|
|
|
|
@parm HWND | hwndProgressFeedback | A handle to the window to be used to
|
|
provide progress information to the user during reconciliation. This parameter
|
|
is ignored if the RI_FL_FEEDBACK_WINDOW_VALID flag is clear.
|
|
|
|
@rdesc If the reconciliation item was reconciled successfully, TR_SUCCESS is
|
|
returned. Otherwise, the reconciliation item was not reconciled successfully,
|
|
and the return value indicates the error that occurred.
|
|
|
|
@comm All the fields in the given RECITEM and its child structures are left
|
|
unchanged by ReconcileItem(), except for the fsCurrent fields of RECNODEs
|
|
associated with objects that are overwritten during reconciliation. The
|
|
fsCurrent field of each RECNODE associated with an object that is overwritten
|
|
during reconciliation (i.e., RECNODEs with rnaction set to RNA_COPY_TO_ME or
|
|
RNA_MERGE_ME) is updated to reflect the current time stamp of the object after
|
|
it is overwritten. If ReconcileItem() returns TR_SUCCESS, all the available
|
|
RECNODEs (i.e., all RECNODEs whose uState field is not RNS_UNAVAILABLE) in the
|
|
RECITEM may be assumed to be up-to-date. If ReconcileItem() does not return
|
|
TR_SUCCESS, no assumption may be made about the states of the RECNODEs in the
|
|
RECITEM. If ReconcileItem() is called on a RECITEM that references a twin
|
|
family that has been deleted or one or more object twins that have been
|
|
deleted, TR_DELETED_TWIN is returned. In this case, no assumption may be made
|
|
about what reconciliation actions have been carried out on the RECITEM. If
|
|
TR_DELETED_TWIN is returned, the client may attempt to create a RECLIST for the
|
|
twin family associated with the RECITEM in order to retry the reconciliation
|
|
operation. (The client would call MarkTwin(), followed by CreateRecList().) If
|
|
TR_DELETED_TWIN is returned by MarkTwin(), the entire twin family has been
|
|
deleted. If TR_SUCCESS is returned by MarkTwin(), the client should be able to
|
|
call CreateRecList() to create a RECLIST containing a more up-to-date RECITEM
|
|
for the twin family.
|
|
|
|
@xref CreateRecList
|
|
|
|
******************************************************************************/
|
|
|
|
SYNCENGAPI TWINRESULT WINAPI ReconcileItem(PCRECITEM pcri, RECSTATUSPROC rsp,
|
|
LPARAM lpCallbackData,
|
|
DWORD dwFlags, HWND hwndOwner,
|
|
HWND hwndProgressFeedback)
|
|
{
|
|
TWINRESULT tr;
|
|
|
|
if (BeginExclusiveBriefcaseAccess())
|
|
{
|
|
DebugEntry(ReconcileItem);
|
|
|
|
#ifdef EXPV
|
|
/* Verify parameters. */
|
|
|
|
/* lpCallbackData may be any value. */
|
|
|
|
if (IS_VALID_STRUCT_PTR(pcri, CRECITEM) &&
|
|
(! rsp ||
|
|
IS_VALID_CODE_PTR(rsp, RECSTATUSPROC)) &&
|
|
FLAGS_ARE_VALID(dwFlags, ALL_RI_FLAGS) &&
|
|
(IS_FLAG_CLEAR(dwFlags, RI_FL_ALLOW_UI) ||
|
|
IS_VALID_HANDLE(hwndOwner, WND)) &&
|
|
(IS_FLAG_CLEAR(dwFlags, RI_FL_FEEDBACK_WINDOW_VALID) ||
|
|
IS_VALID_HANDLE(hwndProgressFeedback, WND)))
|
|
#endif
|
|
{
|
|
/* Check for any deleted twins referenced by this RECITEM. */
|
|
|
|
if (! DeletedTwinsInRecItem(pcri))
|
|
{
|
|
InvalidatePathListInfo(GetBriefcasePathList(((PCTWINFAMILY)(pcri->hTwinFamily))->hbr));
|
|
|
|
tr = MyReconcileItem(pcri, rsp, lpCallbackData, dwFlags,
|
|
hwndOwner, hwndProgressFeedback);
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
|
|
}
|
|
else
|
|
tr = TR_DELETED_TWIN;
|
|
}
|
|
#ifdef EXPV
|
|
else
|
|
tr = TR_INVALID_PARAMETER;
|
|
#endif
|
|
|
|
DebugExitTWINRESULT(ReconcileItem, tr);
|
|
|
|
EndExclusiveBriefcaseAccess();
|
|
}
|
|
else
|
|
tr = TR_REENTERED;
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
@doc SYNCENGAPI
|
|
|
|
@api TWINRESULT | BeginReconciliation | Indicates to the synchronization engine
|
|
that the caller is about to make multiple calls to ReconcileItem().
|
|
|
|
@parm HBRFCASE | hbr | A handle to the open briefcase about to be reconciled.
|
|
|
|
@rdesc If reconciliation for the given briefcase was initialized successfully,
|
|
TR_SUCCESS is returned. Otherwise, reconciliation for the given briefcase was
|
|
not initialized successfully, and the return value indicates the error that
|
|
occurred.
|
|
|
|
@comm Synchronization engine clients need not call BeginReconciliation() before
|
|
calling ReconcileItem(). BeginReconciliation() is simply provided to allow
|
|
synchronization engine clients to give the synchronization engine a hint that
|
|
multiple calls to ReconcileItem() are about to occur. Each call to
|
|
EndReconciliation() should be followed by a call to EndReconciliation().
|
|
|
|
@xref EndReconciliation ReconcileItem
|
|
|
|
******************************************************************************/
|
|
|
|
SYNCENGAPI TWINRESULT WINAPI BeginReconciliation(HBRFCASE hbr)
|
|
{
|
|
TWINRESULT tr;
|
|
|
|
if (BeginExclusiveBriefcaseAccess())
|
|
{
|
|
DebugEntry(BeginReconciliation);
|
|
|
|
#ifdef EXPV
|
|
/* Verify parameters. */
|
|
|
|
if (IS_VALID_HANDLE(hbr, BRFCASE))
|
|
#endif
|
|
{
|
|
BeginCopy();
|
|
BeginMerge();
|
|
|
|
tr = TR_SUCCESS;
|
|
}
|
|
#ifdef EXPV
|
|
else
|
|
tr = TR_INVALID_PARAMETER;
|
|
#endif
|
|
|
|
DebugExitTWINRESULT(BeginReconciliation, tr);
|
|
|
|
EndExclusiveBriefcaseAccess();
|
|
}
|
|
else
|
|
tr = TR_REENTERED;
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
@doc SYNCENGAPI
|
|
|
|
@api TWINRESULT | EndReconciliation | Indicates to the synchronization engine
|
|
that the client has finished making multiple calls to ReconcileItem(),
|
|
preceded by a call to BeginReconciliation().
|
|
|
|
@parm HBRFCASE | hbr | A handle to the open briefcase whose reconciliation has
|
|
been completed.
|
|
|
|
@rdesc If reconciliation for the given briefcase was terminaterd successfully,
|
|
TR_SUCCESS is returned. Otherwise, reconciliation for the given briefcase was
|
|
not terminated successfully, and the return value indicates the error that
|
|
occurred.
|
|
|
|
@comm EndReconciliation() should only be called after a call to
|
|
BeginReconciliation(). Each call to BeginReconciliation() should be followed
|
|
by a matching call to EndReconciliation().
|
|
|
|
@xref BeginReconciliation ReconcileItem
|
|
|
|
******************************************************************************/
|
|
|
|
SYNCENGAPI TWINRESULT WINAPI EndReconciliation(HBRFCASE hbr)
|
|
{
|
|
TWINRESULT tr;
|
|
|
|
if (BeginExclusiveBriefcaseAccess())
|
|
{
|
|
DebugEntry(EndReconciliation);
|
|
|
|
#ifdef EXPV
|
|
/* Verify parameters. */
|
|
|
|
if (IS_VALID_HANDLE(hbr, BRFCASE))
|
|
#endif
|
|
{
|
|
EndMerge();
|
|
EndCopy();
|
|
|
|
tr = TR_SUCCESS;
|
|
}
|
|
#ifdef EXPV
|
|
else
|
|
tr = TR_INVALID_PARAMETER;
|
|
#endif
|
|
|
|
DebugExitTWINRESULT(EndReconciliation, tr);
|
|
|
|
EndExclusiveBriefcaseAccess();
|
|
}
|
|
else
|
|
tr = TR_REENTERED;
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
@doc SYNCENGAPI
|
|
|
|
@api TWINRESULT | GetFileStamp | Retrieves the file stamp for an open file.
|
|
|
|
@parm PCSTR | pcszFile | A pointer to a string indicating the file whose file
|
|
stamp is to be retrieved.
|
|
|
|
@parm PFILESTAMP | pcr | A pointer to a FILESTAMP to be filled in with the
|
|
file stamp of the given open file.
|
|
|
|
@rdesc If the comparison was successful, TR_SUCCESS is returned. Otherwise,
|
|
the comparison was not successful, and the return value indicates the error
|
|
that occurred.
|
|
|
|
@xref CompareFileStamps
|
|
|
|
******************************************************************************/
|
|
|
|
SYNCENGAPI TWINRESULT WINAPI GetFileStamp(LPCTSTR pcszFile, PFILESTAMP pfs)
|
|
{
|
|
TWINRESULT tr;
|
|
|
|
/* No need for exclusive access here. */
|
|
|
|
DebugEntry(GetFileStamp);
|
|
|
|
#ifdef EXPV
|
|
/* Verify parameters. */
|
|
|
|
if (IS_VALID_STRING_PTR(pcszFile, CSTR) &&
|
|
IS_VALID_WRITE_PTR(pfs, FILESTAMP))
|
|
#endif
|
|
{
|
|
MyGetFileStamp(pcszFile, pfs);
|
|
|
|
tr = TR_SUCCESS;
|
|
}
|
|
#ifdef EXPV
|
|
else
|
|
tr = TR_INVALID_PARAMETER;
|
|
#endif
|
|
|
|
DebugExitTWINRESULT(GetFileStamp, tr);
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
@doc SYNCENGAPI
|
|
|
|
@api COMPARISONRESULT | CompareFileStamps | Compares two file stamps.
|
|
|
|
@parm PCFILESTAMP | pcfs1 | A pointer to the first FILESTAMP to be compared.
|
|
|
|
@parm PCFILESTAMP | pcfs2 | A pointer to the second FILESTAMP to be compared.
|
|
|
|
@parm PCOMPARISONRESULT | pcr | A pointer to a COMPARISONRESULT to be filled in
|
|
with the result of the file stamp comparison. *pcr is only valid if TR_SUCCESS
|
|
is returned.
|
|
|
|
@rdesc If the file stamp was retrieved successfully, TR_SUCCESS is returned.
|
|
Otherwise, the file stamp was not retrieved successfully, and the return value
|
|
indicates the error that occurred.
|
|
|
|
@comm File stamps are compared by fields as follows:
|
|
1) condition
|
|
Any FS_COND_UNAVAILABLE equals any FS_COND_UNAVAILABLE.
|
|
Any FS_COND_UNAVAILABLE is less than any FS_COND_DOES_NOT_EXIST.
|
|
Any FS_COND_DOES_NOT_EXIST equals any FS_COND_DOES_NOT_EXIST.
|
|
Any FS_COND_DOES_NOT_EXIST is less than any FS_COND_EXISTS.
|
|
Two FS_COND_EXISTS are compared by date and time.
|
|
2) date and time of last modification
|
|
3) length
|
|
|
|
@xref GetFileStamp
|
|
|
|
******************************************************************************/
|
|
|
|
SYNCENGAPI TWINRESULT WINAPI CompareFileStamps(PCFILESTAMP pcfs1,
|
|
PCFILESTAMP pcfs2,
|
|
PCOMPARISONRESULT pcr)
|
|
{
|
|
TWINRESULT tr;
|
|
|
|
/* No need for exclusive access here. */
|
|
|
|
DebugEntry(CompareFileStamps);
|
|
|
|
#ifdef EXPV
|
|
/* Verify parameters. */
|
|
|
|
if (IS_VALID_STRUCT_PTR(pcfs1, CFILESTAMP) &&
|
|
IS_VALID_STRUCT_PTR(pcfs2, CFILESTAMP) &&
|
|
IS_VALID_WRITE_PTR(pcr, COMPARISONRESULT))
|
|
#endif
|
|
{
|
|
*pcr = MyCompareFileStamps(pcfs1, pcfs2);
|
|
tr = TR_SUCCESS;
|
|
}
|
|
#ifdef EXPV
|
|
else
|
|
tr = TR_INVALID_PARAMETER;
|
|
#endif
|
|
|
|
DebugExitTWINRESULT(CompareFileStamps, tr);
|
|
|
|
return(tr);
|
|
}
|
|
|