|
|
/*
* twin.c - Twin ADT module. */
/*
*/
/* Headers
**********/
#include "project.h"
#pragma hdrstop
#include "stub.h"
#include "oleutil.h"
/* Constants
************/
/* twin family pointer array allocation constants */
#define NUM_START_TWIN_FAMILY_PTRS (16)
#define NUM_TWIN_FAMILY_PTRS_TO_ADD (16)
/* Types
********/
/* twin families database structure header */
typedef struct _twinfamiliesdbheader { /* number of twin families */
LONG lcTwinFamilies; } TWINFAMILIESDBHEADER; DECLARE_STANDARD_TYPES(TWINFAMILIESDBHEADER);
/* individual twin family database structure header */
typedef struct _twinfamilydbheader { /* stub flags */
DWORD dwStubFlags;
/* old string handle of name */
HSTRING hsName;
/* number of object twins in family */
LONG lcObjectTwins; } TWINFAMILYDBHEADER; DECLARE_STANDARD_TYPES(TWINFAMILYDBHEADER);
/* object twin database structure */
typedef struct _dbobjecttwin { /* stub flags */
DWORD dwStubFlags;
/* old handle to folder string */
HPATH hpath;
/* time stamp at last reconciliation */
FILESTAMP fsLastRec; } DBOBJECTTWIN; DECLARE_STANDARD_TYPES(DBOBJECTTWIN);
/* GenerateSpinOffObjectTwin() callback structure */
typedef struct _spinoffobjecttwininfo { PCFOLDERPAIR pcfp;
HLIST hlistNewObjectTwins; } SPINOFFOBJECTTWININFO; DECLARE_STANDARD_TYPES(SPINOFFOBJECTTWININFO);
typedef void (CALLBACK *COPYOBJECTTWINPROC)(POBJECTTWIN, PCDBOBJECTTWIN);
/***************************** Private Functions *****************************/
/* Module Prototypes
********************/
PRIVATE_CODE TWINRESULT TwinJustTheseTwoObjects(HBRFCASE, HPATH, HPATH, LPCTSTR, POBJECTTWIN *, POBJECTTWIN *, HLIST); PRIVATE_CODE BOOL CreateTwinFamily(HBRFCASE, LPCTSTR, PTWINFAMILY *); PRIVATE_CODE void CollapseTwinFamilies(PTWINFAMILY, PTWINFAMILY); PRIVATE_CODE BOOL GenerateSpinOffObjectTwin(PVOID, PVOID); PRIVATE_CODE BOOL BuildBradyBunch(PVOID, PVOID); PRIVATE_CODE BOOL CreateObjectTwinAndAddToList(PTWINFAMILY, HPATH, HLIST, POBJECTTWIN *, PHNODE); PRIVATE_CODE BOOL CreateListOfGeneratedObjectTwins(PCFOLDERPAIR, PHLIST); PRIVATE_CODE void NotifyNewObjectTwins(HLIST, HCLSIFACECACHE); PRIVATE_CODE HRESULT NotifyOneNewObjectTwin(PINotifyReplica, PCOBJECTTWIN, LPCTSTR); PRIVATE_CODE HRESULT CreateOtherReplicaMonikers(PCOBJECTTWIN, PULONG, PIMoniker **); PRIVATE_CODE COMPARISONRESULT TwinFamilySortCmp(PCVOID, PCVOID); PRIVATE_CODE COMPARISONRESULT TwinFamilySearchCmp(PCVOID, PCVOID); PRIVATE_CODE BOOL ObjectTwinSearchCmp(PCVOID, PCVOID); PRIVATE_CODE TWINRESULT WriteTwinFamily(HCACHEDFILE, PCTWINFAMILY); PRIVATE_CODE TWINRESULT WriteObjectTwin(HCACHEDFILE, PCOBJECTTWIN); PRIVATE_CODE TWINRESULT ReadTwinFamily(HCACHEDFILE, HBRFCASE, PCDBVERSION, HHANDLETRANS, HHANDLETRANS); PRIVATE_CODE TWINRESULT ReadObjectTwin(HCACHEDFILE, PCDBVERSION, PTWINFAMILY, HHANDLETRANS); PRIVATE_CODE void CopyTwinFamilyInfo(PTWINFAMILY, PCTWINFAMILYDBHEADER); PRIVATE_CODE void CopyObjectTwinInfo(POBJECTTWIN, PCDBOBJECTTWIN); PRIVATE_CODE void CopyM8ObjectTwinInfo(POBJECTTWIN, PCDBOBJECTTWIN); PRIVATE_CODE BOOL DestroyObjectTwinStubWalker(PVOID, PVOID); PRIVATE_CODE BOOL MarkObjectTwinNeverReconciledWalker(PVOID, PVOID); PRIVATE_CODE BOOL LookForSrcFolderTwinsWalker(PVOID, PVOID); PRIVATE_CODE BOOL IncrementSrcFolderTwinsWalker(PVOID, PVOID); PRIVATE_CODE BOOL ClearSrcFolderTwinsWalker(PVOID, PVOID); PRIVATE_CODE BOOL SetTwinFamilyWalker(PVOID, PVOID); PRIVATE_CODE BOOL InsertNodeAtFrontWalker(POBJECTTWIN, PVOID);
#ifdef VSTF
PRIVATE_CODE BOOL IsValidObjectTwinWalker(PVOID, PVOID); PRIVATE_CODE BOOL IsValidPCNEWOBJECTTWIN(PCNEWOBJECTTWIN); PRIVATE_CODE BOOL IsValidPCSPINOFFOBJECTTWININFO(PCSPINOFFOBJECTTWININFO);
#endif
#ifdef DEBUG
PRIVATE_CODE BOOL AreTwinFamiliesValid(HPTRARRAY);
#endif
/*
** TwinJustTheseTwoObjects() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT TwinJustTheseTwoObjects(HBRFCASE hbr, HPATH hpathFolder1, HPATH hpathFolder2, LPCTSTR pcszName, POBJECTTWIN *ppot1, POBJECTTWIN *ppot2, HLIST hlistNewObjectTwins) { TWINRESULT tr = TR_OUT_OF_MEMORY; HNODE hnodeSearch; BOOL bFound1; BOOL bFound2;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_HANDLE(hpathFolder1, PATH)); ASSERT(IS_VALID_HANDLE(hpathFolder2, PATH)); ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR)); ASSERT(IS_VALID_WRITE_PTR(ppot1, POBJECTTWIN)); ASSERT(IS_VALID_WRITE_PTR(ppot2, POBJECTTWIN)); ASSERT(IS_VALID_HANDLE(hlistNewObjectTwins, LIST));
/* Determine twin families of existing object twins. */
bFound1 = FindObjectTwin(hbr, hpathFolder1, pcszName, &hnodeSearch);
if (bFound1) *ppot1 = (POBJECTTWIN)GetNodeData(hnodeSearch);
bFound2 = FindObjectTwin(hbr, hpathFolder2, pcszName, &hnodeSearch);
if (bFound2) *ppot2 = (POBJECTTWIN)GetNodeData(hnodeSearch);
/* Take action based upon existence of two object twins. */
if (! bFound1 && ! bFound2) { PTWINFAMILY ptfNew;
/* Neither object is already present. Create a new twin family. */
if (CreateTwinFamily(hbr, pcszName, &ptfNew)) { HNODE hnodeNew1;
if (CreateObjectTwinAndAddToList(ptfNew, hpathFolder1, hlistNewObjectTwins, ppot1, &hnodeNew1)) { HNODE hnodeNew2;
if (CreateObjectTwinAndAddToList(ptfNew, hpathFolder2, hlistNewObjectTwins, ppot2, &hnodeNew2)) { TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Created a twin family for object %s in folders %s and %s."), pcszName, DebugGetPathString(hpathFolder1), DebugGetPathString(hpathFolder2)));
ASSERT(IsStubFlagClear(&(ptfNew->stub), STUB_FL_DELETION_PENDING));
tr = TR_SUCCESS; } else { DeleteNode(hnodeNew1); DestroyStub(&((*ppot1)->stub)); TWINJUSTTHESETWOOBJECTS_BAIL: DestroyStub(&(ptfNew->stub)); } } else goto TWINJUSTTHESETWOOBJECTS_BAIL; } } else if (bFound1 && bFound2) { /*
* Both objects are already present. Are they members of the same twin * family? */
if ((*ppot1)->ptfParent == (*ppot2)->ptfParent) { /* Yes, same twin family. Complain that these twins already exist. */
TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Object %s is already twinned in folders %s and %s."), pcszName, DebugGetPathString(hpathFolder1), DebugGetPathString(hpathFolder2)));
tr = TR_DUPLICATE_TWIN; } else { /*
* No, different twin families. Collapse the two families. * * "That's the way they became the Brady bunch..." * * *ppot1 and *ppot2 remain valid across this call. */
TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Collapsing separate twin families for object %s in folders %s and %s."), pcszName, DebugGetPathString(hpathFolder1), DebugGetPathString(hpathFolder2)));
CollapseTwinFamilies((*ppot1)->ptfParent, (*ppot2)->ptfParent);
tr = TR_SUCCESS; } } else { PTWINFAMILY ptfParent; HNODE hnodeUnused;
/*
* Only one of the two objects is present. Add the new object twin * to the existing object twin's family. */
if (bFound1) { /* First object is already a twin. */
ptfParent = (*ppot1)->ptfParent;
if (CreateObjectTwinAndAddToList(ptfParent, hpathFolder2, hlistNewObjectTwins, ppot2, &hnodeUnused)) { TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Adding twin of object %s\\%s to existing twin family including %s\\%s."), DebugGetPathString(hpathFolder2), pcszName, DebugGetPathString(hpathFolder1), pcszName));
tr = TR_SUCCESS; } } else { /* Second object is already a twin. */
ptfParent = (*ppot2)->ptfParent;
if (CreateObjectTwinAndAddToList(ptfParent, hpathFolder1, hlistNewObjectTwins, ppot1, &hnodeUnused)) { TRACE_OUT((TEXT("TwinJustTheseTwoObjects(): Adding twin of object %s\\%s to existing twin family including %s\\%s."), DebugGetPathString(hpathFolder1), pcszName, DebugGetPathString(hpathFolder2), pcszName));
tr = TR_SUCCESS; } } }
ASSERT((tr != TR_SUCCESS && tr != TR_DUPLICATE_TWIN) || IS_VALID_STRUCT_PTR(*ppot1, COBJECTTWIN) && IS_VALID_STRUCT_PTR(*ppot2, COBJECTTWIN));
return(tr); }
/*
** CreateTwinFamily() ** ** Creates a new empty twin family, and adds it to a briefcase. ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE BOOL CreateTwinFamily(HBRFCASE hbr, LPCTSTR pcszName, PTWINFAMILY *pptf) { BOOL bResult = FALSE; PTWINFAMILY ptfNew;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR)); ASSERT(IS_VALID_WRITE_PTR(pptf, PTWINFAMILY));
/* Try to create a new TWINFAMILY structure. */
if (AllocateMemory(sizeof(*ptfNew), &ptfNew)) { NEWLIST nl; HLIST hlistObjectTwins;
/* Create a list of object twins for the new twin family. */
nl.dwFlags = 0;
if (CreateList(&nl, &hlistObjectTwins)) { HSTRING hsName;
/* Add the object name to the name string table. */
if (AddString(pcszName, GetBriefcaseNameStringTable(hbr), GetHashBucketIndex, &hsName)) { ARRAYINDEX aiUnused;
/* Fill in TWINFAMILY fields. */
InitStub(&(ptfNew->stub), ST_TWINFAMILY);
ptfNew->hsName = hsName; ptfNew->hlistObjectTwins = hlistObjectTwins; ptfNew->hbr = hbr;
MarkTwinFamilyNeverReconciled(ptfNew);
/* Add the twin family to the briefcase's list of twin families. */
if (AddPtr(GetBriefcaseTwinFamilyPtrArray(hbr), TwinFamilySortCmp, ptfNew, &aiUnused)) { *pptf = ptfNew; bResult = TRUE;
ASSERT(IS_VALID_STRUCT_PTR(*pptf, CTWINFAMILY)); } else { DeleteString(hsName); CREATETWINFAMILY_BAIL1: DestroyList(hlistObjectTwins); CREATETWINFAMILY_BAIL2: FreeMemory(ptfNew); } } else goto CREATETWINFAMILY_BAIL1; } else goto CREATETWINFAMILY_BAIL2; }
return(bResult); }
/*
** CollapseTwinFamilies() ** ** Collapses two twin families into one. N.b., this function should only be ** called on two twin families with the same object name! ** ** Arguments: ptf1 - pointer to destination twin family ** ptf2 - pointer to source twin family ** ** Returns: void ** ** Side Effects: Twin family *ptf2 is destroyed. */ PRIVATE_CODE void CollapseTwinFamilies(PTWINFAMILY ptf1, PTWINFAMILY ptf2) { ASSERT(IS_VALID_STRUCT_PTR(ptf1, CTWINFAMILY)); ASSERT(IS_VALID_STRUCT_PTR(ptf2, CTWINFAMILY));
ASSERT(CompareNameStringsByHandle(ptf1->hsName, ptf2->hsName) == CR_EQUAL);
/* Use the first twin family as the collapsed twin family. */
/*
* Change the parent twin family of the object twins in the second twin * family to the first twin family. */
EVAL(WalkList(ptf2->hlistObjectTwins, &SetTwinFamilyWalker, ptf1));
/* Append object list from second twin family on to first. */
AppendList(ptf1->hlistObjectTwins, ptf2->hlistObjectTwins);
MarkTwinFamilyNeverReconciled(ptf1);
/* Wipe out the old twin family. */
DestroyStub(&(ptf2->stub));
ASSERT(IS_VALID_STRUCT_PTR(ptf1, CTWINFAMILY));
return; }
/*
** GenerateSpinOffObjectTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL GenerateSpinOffObjectTwin(PVOID pot, PVOID pcsooti) { BOOL bResult; HPATH hpathMatchingFolder; HNODE hnodeUnused;
ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(IS_VALID_STRUCT_PTR(pcsooti, CSPINOFFOBJECTTWININFO));
/*
* Append the generated object twin's subpath to the matching folder twin's * base path for subtree twins. */
if (BuildPathForMatchingObjectTwin( ((PCSPINOFFOBJECTTWININFO)pcsooti)->pcfp, pot, GetBriefcasePathList(((POBJECTTWIN)pot)->ptfParent->hbr), &hpathMatchingFolder)) { /*
* Does this generated object twin's twin family already contain an * object twin generated by the other half of the pair of folder twins? */
if (! SearchUnsortedList(((POBJECTTWIN)pot)->ptfParent->hlistObjectTwins, &ObjectTwinSearchCmp, hpathMatchingFolder, &hnodeUnused)) { /*
* No. Does the other object twin already exist in a different twin * family? */
if (FindObjectTwin(((POBJECTTWIN)pot)->ptfParent->hbr, hpathMatchingFolder, GetString(((POBJECTTWIN)pot)->ptfParent->hsName), &hnodeUnused)) { /* Yes. */
ASSERT(((PCOBJECTTWIN)GetNodeData(hnodeUnused))->ptfParent != ((POBJECTTWIN)pot)->ptfParent);
bResult = TRUE; } else { POBJECTTWIN potNew;
/*
* No. Create a new object twin, and add it to the bookkeeping * list of new object twins. */
bResult = CreateObjectTwinAndAddToList( ((POBJECTTWIN)pot)->ptfParent, hpathMatchingFolder, ((PCSPINOFFOBJECTTWININFO)pcsooti)->hlistNewObjectTwins, &potNew, &hnodeUnused);
#ifdef DEBUG
if (bResult) { TRACE_OUT((TEXT("GenerateSpinOffObjectTwin(): Generated spin-off object twin for object %s\\%s."), DebugGetPathString(potNew->hpath), GetString(potNew->ptfParent->hsName))); }
#endif
} } else bResult = TRUE;
DeletePath(hpathMatchingFolder); } else bResult = FALSE;
return(bResult); }
/*
** BuildBradyBunch() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL BuildBradyBunch(PVOID pot, PVOID pcfp) { BOOL bResult; HPATH hpathMatchingFolder; HNODE hnodeOther;
ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
/*
* Append the generated object twin's subpath to the matching folder twin's * base path for subtree twins. */
bResult = BuildPathForMatchingObjectTwin( pcfp, pot, GetBriefcasePathList(((POBJECTTWIN)pot)->ptfParent->hbr), &hpathMatchingFolder);
if (bResult) { /*
* Does this generated object twin's twin family already contain an object * twin generated by the other half of the pair of folder twins? */
if (! SearchUnsortedList(((POBJECTTWIN)pot)->ptfParent->hlistObjectTwins, &ObjectTwinSearchCmp, hpathMatchingFolder, &hnodeOther)) { /*
* The other object twin should already exist in a different twin family. */
if (EVAL(FindObjectTwin(((POBJECTTWIN)pot)->ptfParent->hbr, hpathMatchingFolder, GetString(((POBJECTTWIN)pot)->ptfParent->hsName), &hnodeOther))) { PCOBJECTTWIN pcotOther;
pcotOther = (PCOBJECTTWIN)GetNodeData(hnodeOther);
if (EVAL(pcotOther->ptfParent != ((POBJECTTWIN)pot)->ptfParent)) { /* It does. Crush them. */
CollapseTwinFamilies(((POBJECTTWIN)pot)->ptfParent, pcotOther->ptfParent);
TRACE_OUT((TEXT("BuildBradyBunch(): Collapsed separate twin families for object %s\\%s and %s\\%s."), DebugGetPathString(((POBJECTTWIN)pot)->hpath), GetString(((POBJECTTWIN)pot)->ptfParent->hsName), DebugGetPathString(pcotOther->hpath), GetString(pcotOther->ptfParent->hsName))); } } }
DeletePath(hpathMatchingFolder); }
return(bResult); }
/*
** CreateObjectTwinAndAddToList() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL CreateObjectTwinAndAddToList(PTWINFAMILY ptf, HPATH hpathFolder, HLIST hlistObjectTwins, POBJECTTWIN *ppot, PHNODE phnode) { BOOL bResult = FALSE;
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY)); ASSERT(IS_VALID_HANDLE(hpathFolder, PATH)); ASSERT(IS_VALID_HANDLE(hlistObjectTwins, LIST)); ASSERT(IS_VALID_WRITE_PTR(ppot, POBJECTTWIN)); ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
if (CreateObjectTwin(ptf, hpathFolder, ppot)) { if (InsertNodeAtFront(hlistObjectTwins, NULL, *ppot, phnode)) bResult = TRUE; else DestroyStub(&((*ppot)->stub)); }
return(bResult); }
/*
** CreateListOfGeneratedObjectTwins() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL CreateListOfGeneratedObjectTwins(PCFOLDERPAIR pcfp, PHLIST phlistGeneratedObjectTwins) { NEWLIST nl; BOOL bResult = FALSE;
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR)); ASSERT(IS_VALID_WRITE_PTR(phlistGeneratedObjectTwins, HLIST));
nl.dwFlags = 0;
if (CreateList(&nl, phlistGeneratedObjectTwins)) { if (EnumGeneratedObjectTwins(pcfp, &InsertNodeAtFrontWalker, *phlistGeneratedObjectTwins)) bResult = TRUE; else DestroyList(*phlistGeneratedObjectTwins); }
return(bResult); }
/*
** NotifyNewObjectTwins() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE void NotifyNewObjectTwins(HLIST hlistNewObjectTwins, HCLSIFACECACHE hcic) { BOOL bContinue; HNODE hnode;
ASSERT(IS_VALID_HANDLE(hlistNewObjectTwins, LIST)); ASSERT(IS_VALID_HANDLE(hcic, CLSIFACECACHE));
for (bContinue = GetFirstNode(hlistNewObjectTwins, &hnode); bContinue; bContinue = GetNextNode(hnode, &hnode)) { PCOBJECTTWIN pcot; TCHAR rgchPath[MAX_PATH_LEN]; CLSID clsidReplicaNotification;
pcot = (PCOBJECTTWIN)GetNodeData(hnode);
rgchPath[0] = TEXT('\0'); GetPathString(pcot->hpath, rgchPath, ARRAYSIZE(rgchPath)); CatPath(rgchPath, GetString(pcot->ptfParent->hsName), ARRAYSIZE(rgchPath)); ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath));
if (SUCCEEDED(GetReplicaNotificationClassID(rgchPath, &clsidReplicaNotification))) { PINotifyReplica pinr;
if (SUCCEEDED(GetClassInterface(hcic, &clsidReplicaNotification, &IID_INotifyReplica, &pinr))) /* Ignore return value. */ NotifyOneNewObjectTwin(pinr, pcot, rgchPath); else TRACE_OUT((TEXT("NotifyNewObjectTwins(): Failed to get INotifyReplica for replica %s."), rgchPath)); } else TRACE_OUT((TEXT("NotifyNewObjectTwins(): Failed to get replica notification class ID for replica %s."), rgchPath)); }
return; }
/*
** NotifyOneNewObjectTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE HRESULT NotifyOneNewObjectTwin(PINotifyReplica pinr, PCOBJECTTWIN pcot, LPCTSTR pcszPath) { HRESULT hr; HSTGIFACE hstgi;
ASSERT(IS_VALID_STRUCT_PTR(pinr, CINotifyReplica)); ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN)); ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
hr = GetStorageInterface((PIUnknown)pinr, &hstgi);
if (SUCCEEDED(hr)) { hr = LoadFromStorage(hstgi, pcszPath);
if (SUCCEEDED(hr)) { ULONG ulcOtherReplicas; PIMoniker *ppimkOtherReplicas;
/*
* RAIDRAID: (16270) (Performance) We may create a file moniker for * the same object twin multiple times here. */
hr = CreateOtherReplicaMonikers(pcot, &ulcOtherReplicas, &ppimkOtherReplicas);
if (SUCCEEDED(hr)) { hr = pinr->lpVtbl->YouAreAReplica(pinr, ulcOtherReplicas, ppimkOtherReplicas);
if (SUCCEEDED(hr)) { hr = SaveToStorage(hstgi);
if (SUCCEEDED(hr)) TRACE_OUT((TEXT("NotifyOneNewObjectTwin(): Replica %s successfully notified."), pcszPath)); else WARNING_OUT((TEXT("NotifyOneNewObjectTwin(): Failed to save replica %s to storage."), pcszPath)); } else WARNING_OUT((TEXT("NotifyOneNewObjectTwin(): Failed to notify replica %s."), pcszPath));
ReleaseIUnknowns(ulcOtherReplicas, (PIUnknown *)ppimkOtherReplicas); } else WARNING_OUT((TEXT("NotifyOneNewObjectTwin(): Failed to create monikers for other replicas of replica %s."), pcszPath)); } else WARNING_OUT((TEXT("NotifyOneNewObjectTwin(): Failed to load replica %s from storage."), pcszPath));
ReleaseStorageInterface(hstgi); } else WARNING_OUT((TEXT("NotifyOneNewObjectTwin(): Failed to get storage interface for replica %s."), pcszPath));
return(hr); }
/*
** CreateOtherReplicaMonikers() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE HRESULT CreateOtherReplicaMonikers(PCOBJECTTWIN pcotMaster, PULONG pulcOtherReplicas, PIMoniker **pppimk) { HRESULT hr; HLIST hlist; ULONG ulcOtherReplicas;
ASSERT(IS_VALID_STRUCT_PTR(pcotMaster, COBJECTTWIN)); ASSERT(IS_VALID_WRITE_PTR(pulcOtherReplicas, ULONG)); ASSERT(IS_VALID_WRITE_PTR(pppimk, PIMoniker *));
hlist = pcotMaster->ptfParent->hlistObjectTwins;
ulcOtherReplicas = GetNodeCount(hlist); ASSERT(ulcOtherReplicas > 0); ulcOtherReplicas--;
if (AllocateMemory(ulcOtherReplicas * sizeof(**pppimk), (PVOID *)pppimk)) { BOOL bContinue; HNODE hnode;
hr = S_OK; *pulcOtherReplicas = 0;
for (bContinue = GetFirstNode(hlist, &hnode); bContinue; bContinue = GetNextNode(hnode, &hnode)) { PCOBJECTTWIN pcot;
pcot = (PCOBJECTTWIN)GetNodeData(hnode);
if (pcot != pcotMaster) { TCHAR rgchPath[MAX_PATH_LEN];
rgchPath[0] = TEXT('\0'); GetPathString(pcot->hpath, rgchPath, ARRAYSIZE(rgchPath));
hr = MyCreateFileMoniker(rgchPath, GetString(pcot->ptfParent->hsName), &((*pppimk)[*pulcOtherReplicas]));
if (SUCCEEDED(hr)) { ASSERT(*pulcOtherReplicas < ulcOtherReplicas); (*pulcOtherReplicas)++; } else break; } }
if (FAILED(hr)) ReleaseIUnknowns(*pulcOtherReplicas, *(PIUnknown **)pppimk); } else hr = E_OUTOFMEMORY;
return(hr); }
/*
** TwinFamilySortCmp() ** ** Pointer comparison function used to sort the global array of twin families. ** ** Arguments: pctf1 - pointer to TWINFAMILY describing first twin family ** pctf2 - pointer to TWINFAMILY describing second twin family ** ** Returns: ** ** Side Effects: none ** ** Twin families are sorted by: ** 1) name string ** 2) pointer value */ PRIVATE_CODE COMPARISONRESULT TwinFamilySortCmp(PCVOID pctf1, PCVOID pctf2) { COMPARISONRESULT cr;
ASSERT(IS_VALID_STRUCT_PTR(pctf1, CTWINFAMILY)); ASSERT(IS_VALID_STRUCT_PTR(pctf2, CTWINFAMILY));
cr = CompareNameStringsByHandle(((PCTWINFAMILY)pctf1)->hsName, ((PCTWINFAMILY)pctf2)->hsName);
if (cr == CR_EQUAL) /* Same name strings. Now sort by pointer value. */ cr = ComparePointers(pctf1, pctf2);
return(cr); }
/*
** TwinFamilySearchCmp() ** ** Pointer comparison function used to search the global array of twin families ** for the first twin family for a given name. ** ** Arguments: pcszName - name string to search for ** pctf - pointer to TWINFAMILY to examine ** ** Returns: ** ** Side Effects: none ** ** Twin families are searched by: ** 1) name string */ PRIVATE_CODE COMPARISONRESULT TwinFamilySearchCmp(PCVOID pcszName, PCVOID pctf) { ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR)); ASSERT(IS_VALID_STRUCT_PTR(pctf, CTWINFAMILY));
return(CompareNameStrings(pcszName, GetString(((PCTWINFAMILY)pctf)->hsName))); }
/*
** ObjectTwinSearchCmp() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL ObjectTwinSearchCmp(PCVOID hpath, PCVOID pcot) { ASSERT(IS_VALID_HANDLE((HPATH)hpath, PATH)); ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN));
return(ComparePaths((HPATH)hpath, ((PCOBJECTTWIN)pcot)->hpath)); }
/*
** WriteTwinFamily() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT WriteTwinFamily(HCACHEDFILE hcf, PCTWINFAMILY pctf) { TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED; DWORD dwcbTwinFamilyDBHeaderOffset;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_STRUCT_PTR(pctf, CTWINFAMILY));
/* Save initial file poisition. */
dwcbTwinFamilyDBHeaderOffset = GetCachedFilePointerPosition(hcf);
if (dwcbTwinFamilyDBHeaderOffset != INVALID_SEEK_POSITION) { TWINFAMILYDBHEADER tfdbh;
/* Leave space for the twin family's header. */
ZeroMemory(&tfdbh, sizeof(tfdbh));
if (WriteToCachedFile(hcf, (PCVOID)&tfdbh, sizeof(tfdbh), NULL)) { BOOL bContinue; HNODE hnode; LONG lcObjectTwins = 0;
/* Save twin family's object twins. */
ASSERT(GetNodeCount(pctf->hlistObjectTwins) >= 2);
tr = TR_SUCCESS;
for (bContinue = GetFirstNode(pctf->hlistObjectTwins, &hnode); bContinue; bContinue = GetNextNode(hnode, &hnode)) { POBJECTTWIN pot;
pot = (POBJECTTWIN)GetNodeData(hnode);
tr = WriteObjectTwin(hcf, pot);
if (tr == TR_SUCCESS) { ASSERT(lcObjectTwins < LONG_MAX); lcObjectTwins++; } else break; }
/* Save twin family's database header. */
if (tr == TR_SUCCESS) { ASSERT(lcObjectTwins >= 2);
tfdbh.dwStubFlags = (pctf->stub.dwFlags & DB_STUB_FLAGS_MASK); tfdbh.hsName = pctf->hsName; tfdbh.lcObjectTwins = lcObjectTwins;
tr = WriteDBSegmentHeader(hcf, dwcbTwinFamilyDBHeaderOffset, &tfdbh, sizeof(tfdbh)); } } }
return(tr); }
/*
** WriteObjectTwin() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT WriteObjectTwin(HCACHEDFILE hcf, PCOBJECTTWIN pcot) { TWINRESULT tr; DBOBJECTTWIN dbot;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN));
/* Set up object twin database structure. */
dbot.dwStubFlags = (pcot->stub.dwFlags & DB_STUB_FLAGS_MASK); dbot.hpath = pcot->hpath; dbot.hpath = pcot->hpath; dbot.fsLastRec = pcot->fsLastRec;
/* Save object twin database structure. */
if (WriteToCachedFile(hcf, (PCVOID)&dbot, sizeof(dbot), NULL)) tr = TR_SUCCESS; else tr = TR_BRIEFCASE_WRITE_FAILED;
return(tr); }
/*
** ReadTwinFamily() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT ReadTwinFamily(HCACHEDFILE hcf, HBRFCASE hbr, PCDBVERSION pcdbver, HHANDLETRANS hhtFolderTrans, HHANDLETRANS hhtNameTrans) { TWINRESULT tr = TR_CORRUPT_BRIEFCASE; TWINFAMILYDBHEADER tfdbh; DWORD dwcbRead;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_READ_PTR(pcdbver, DBVERSION)); ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS)); ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
if (ReadFromCachedFile(hcf, &tfdbh, sizeof(tfdbh), &dwcbRead) && dwcbRead == sizeof(tfdbh)) { if (tfdbh.lcObjectTwins >= 2) { HSTRING hsName;
if (TranslateHandle(hhtNameTrans, (HGENERIC)(tfdbh.hsName), (PHGENERIC)&hsName)) { PTWINFAMILY ptfParent;
if (CreateTwinFamily(hbr, GetString(hsName), &ptfParent)) { LONG l;
CopyTwinFamilyInfo(ptfParent, &tfdbh);
tr = TR_SUCCESS;
for (l = tfdbh.lcObjectTwins; l > 0 && tr == TR_SUCCESS; l--) tr = ReadObjectTwin(hcf, pcdbver, ptfParent, hhtFolderTrans);
if (tr != TR_SUCCESS) DestroyStub(&(ptfParent->stub)); } else tr = TR_OUT_OF_MEMORY; } } }
return(tr); }
/*
** ReadObjectTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT ReadObjectTwin(HCACHEDFILE hcf, PCDBVERSION pcdbver, PTWINFAMILY ptfParent, HHANDLETRANS hhtFolderTrans) { TWINRESULT tr; DBOBJECTTWIN dbot; DWORD dwcbRead; HPATH hpath; DWORD dwcbSize; COPYOBJECTTWINPROC pfnCopy;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_READ_PTR(pcdbver, DBVERSION)); ASSERT(IS_VALID_STRUCT_PTR(ptfParent, CTWINFAMILY)); ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS));
if (HEADER_M8_MINOR_VER == pcdbver->dwMinorVer) { /* The M8 database does not have the ftModLocal in the FILESTAMP
** structure. */
dwcbSize = sizeof(dbot) - sizeof(FILETIME); pfnCopy = CopyM8ObjectTwinInfo; } else { ASSERT(HEADER_MINOR_VER == pcdbver->dwMinorVer); dwcbSize = sizeof(dbot); pfnCopy = CopyObjectTwinInfo; }
if ((ReadFromCachedFile(hcf, &dbot, dwcbSize, &dwcbRead) && dwcbRead == dwcbSize) && TranslateHandle(hhtFolderTrans, (HGENERIC)(dbot.hpath), (PHGENERIC)&hpath)) { POBJECTTWIN pot;
/* Create the new object twin and add it to the twin family. */
if (CreateObjectTwin(ptfParent, hpath, &pot)) { pfnCopy(pot, &dbot);
tr = TR_SUCCESS; } else tr = TR_OUT_OF_MEMORY; } else tr = TR_CORRUPT_BRIEFCASE;
return(tr); }
/*
** CopyTwinFamilyInfo() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE void CopyTwinFamilyInfo(PTWINFAMILY ptf, PCTWINFAMILYDBHEADER pctfdbh) { ASSERT(IS_VALID_WRITE_PTR(ptf, TWINFAMILY)); ASSERT(IS_VALID_READ_PTR(pctfdbh, CTWINFAMILYDBHEADER));
ptf->stub.dwFlags = pctfdbh->dwStubFlags;
return; }
/*
** CopyObjectTwinInfo() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE void CopyObjectTwinInfo(POBJECTTWIN pot, PCDBOBJECTTWIN pcdbot) { ASSERT(IS_VALID_WRITE_PTR(pot, OBJECTTWIN)); ASSERT(IS_VALID_READ_PTR(pcdbot, CDBOBJECTTWIN));
pot->stub.dwFlags = pcdbot->dwStubFlags; pot->fsLastRec = pcdbot->fsLastRec;
return; }
/*
** CopyM8ObjectTwinInfo() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE void CopyM8ObjectTwinInfo(POBJECTTWIN pot, PCDBOBJECTTWIN pcdbot) { ASSERT(IS_VALID_WRITE_PTR(pot, OBJECTTWIN)); ASSERT(IS_VALID_READ_PTR(pcdbot, CDBOBJECTTWIN));
pot->stub.dwFlags = pcdbot->dwStubFlags; pot->fsLastRec = pcdbot->fsLastRec;
/* The pot->fsLastRec.ftModLocal field is invalid, so fill it in */
if ( !FileTimeToLocalFileTime(&pot->fsLastRec.ftMod, &pot->fsLastRec.ftModLocal) ) { /* Just copy the time if FileTimeToLocalFileTime failed */
pot->fsLastRec.ftModLocal = pot->fsLastRec.ftMod; }
return; }
#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */
/*
** DestroyObjectTwinStubWalker() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL DestroyObjectTwinStubWalker(PVOID pot, PVOID pvUnused) { ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(! pvUnused);
/*
* Set ulcSrcFolderTwins to 0 so UnlinkObjectTwin() succeeds. * DestroyStub() will unlink and destroy any new twin family created. */
((POBJECTTWIN)pot)->ulcSrcFolderTwins = 0; DestroyStub(&(((POBJECTTWIN)pot)->stub));
return(TRUE); }
/*
** MarkObjectTwinNeverReconciledWalker() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL MarkObjectTwinNeverReconciledWalker(PVOID pot, PVOID pvUnused) { ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(! pvUnused);
MarkObjectTwinNeverReconciled(pot);
return(TRUE); }
/*
** LookForSrcFolderTwinsWalker() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL LookForSrcFolderTwinsWalker(PVOID pot, PVOID pvUnused) { ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(! pvUnused);
return(! ((POBJECTTWIN)pot)->ulcSrcFolderTwins); }
/*
** IncrementSrcFolderTwinsWalker() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IncrementSrcFolderTwinsWalker(PVOID pot, PVOID pvUnused) { ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(! pvUnused);
ASSERT(((POBJECTTWIN)pot)->ulcSrcFolderTwins < ULONG_MAX); ((POBJECTTWIN)pot)->ulcSrcFolderTwins++;
return(TRUE); }
/*
** ClearSrcFolderTwinsWalker() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL ClearSrcFolderTwinsWalker(PVOID pot, PVOID pvUnused) { ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(! pvUnused);
((POBJECTTWIN)pot)->ulcSrcFolderTwins = 0;
return(TRUE); }
#pragma warning(default:4100) /* "unreferenced formal parameter" warning */
/*
** SetTwinFamilyWalker() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL SetTwinFamilyWalker(PVOID pot, PVOID ptfParent) { ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(IS_VALID_STRUCT_PTR(ptfParent, CTWINFAMILY));
((POBJECTTWIN)pot)->ptfParent = ptfParent;
return(TRUE); }
/*
** InsertNodeAtFrontWalker() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL InsertNodeAtFrontWalker(POBJECTTWIN pot, PVOID hlist) { HNODE hnodeUnused;
ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(IS_VALID_HANDLE(hlist, LIST));
return(InsertNodeAtFront(hlist, NULL, pot, &hnodeUnused)); }
#ifdef VSTF
/*
** IsValidObjectTwinWalker() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidObjectTwinWalker(PVOID pcot, PVOID pctfParent) { return(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN) && EVAL(((PCOBJECTTWIN)pcot)->ptfParent == pctfParent) && EVAL(IsStubFlagClear(&(((PCOBJECTTWIN)pcot)->stub), STUB_FL_KEEP) || IsStubFlagSet(&(((PCTWINFAMILY)pctfParent)->stub), STUB_FL_DELETION_PENDING))); }
/*
** IsValidPCNEWOBJECTTWIN() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidPCNEWOBJECTTWIN(PCNEWOBJECTTWIN pcnot) { return(IS_VALID_READ_PTR(pcnot, CNEWOBJECTTWIN) && EVAL(pcnot->ulSize == sizeof(*pcnot)) && IS_VALID_STRING_PTR(pcnot->pcszFolder1, CSTR) && IS_VALID_STRING_PTR(pcnot->pcszFolder2, CSTR) && IS_VALID_STRING_PTR(pcnot->pcszName, CSTR)); }
/*
** IsValidPCSPINOFFOBJECTTWININFO() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidPCSPINOFFOBJECTTWININFO(PCSPINOFFOBJECTTWININFO pcsooti) { return(IS_VALID_READ_PTR(pcsooti, CSPINOFFOBJECTTWININFO) && IS_VALID_STRUCT_PTR(pcsooti->pcfp, CFOLDERPAIR) && IS_VALID_HANDLE(pcsooti->hlistNewObjectTwins, LIST)); }
#endif
#ifdef DEBUG
/*
** AreTwinFamiliesValid() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL AreTwinFamiliesValid(HPTRARRAY hpaTwinFamilies) { ARRAYINDEX aicPtrs; ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hpaTwinFamilies, PTRARRAY));
aicPtrs = GetPtrCount(hpaTwinFamilies);
for (ai = 0; ai < aicPtrs; ai++) { PCTWINFAMILY pctf;
pctf = GetPtr(hpaTwinFamilies, ai);
if (! IS_VALID_STRUCT_PTR(pctf, CTWINFAMILY) || ! EVAL(GetNodeCount(pctf->hlistObjectTwins) >= 2)) break; }
return(ai == aicPtrs); }
#endif
/****************************** Public Functions *****************************/
/*
** CompareNameStrings() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE COMPARISONRESULT CompareNameStrings(LPCTSTR pcszFirst, LPCTSTR pcszSecond) { ASSERT(IS_VALID_STRING_PTR(pcszFirst, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszSecond, CSTR));
return(MapIntToComparisonResult(lstrcmpi(pcszFirst, pcszSecond))); }
/*
** CompareNameStringsByHandle() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE COMPARISONRESULT CompareNameStringsByHandle(HSTRING hsFirst, HSTRING hsSecond) { ASSERT(IS_VALID_HANDLE(hsFirst, STRING)); ASSERT(IS_VALID_HANDLE(hsSecond, STRING));
return(CompareStringsI(hsFirst, hsSecond)); }
/*
** TranslatePATHRESULTToTWINRESULT() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT TranslatePATHRESULTToTWINRESULT(PATHRESULT pr) { TWINRESULT tr;
switch (pr) { case PR_SUCCESS: tr = TR_SUCCESS; break;
case PR_UNAVAILABLE_VOLUME: tr = TR_UNAVAILABLE_VOLUME; break;
case PR_OUT_OF_MEMORY: tr = TR_OUT_OF_MEMORY; break;
default: ASSERT(pr == PR_INVALID_PATH); tr = TR_INVALID_PARAMETER; break; }
return(tr); }
/*
** CreateTwinFamilyPtrArray() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL CreateTwinFamilyPtrArray(PHPTRARRAY phpa) { NEWPTRARRAY npa;
ASSERT(IS_VALID_WRITE_PTR(phpa, HPTRARRAY));
/* Try to create a twin family pointer array. */
npa.aicInitialPtrs = NUM_START_TWIN_FAMILY_PTRS; npa.aicAllocGranularity = NUM_TWIN_FAMILY_PTRS_TO_ADD; npa.dwFlags = NPA_FL_SORTED_ADD;
return(CreatePtrArray(&npa, phpa)); }
/*
** DestroyTwinFamilyPtrArray() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void DestroyTwinFamilyPtrArray(HPTRARRAY hpa) { ARRAYINDEX aicPtrs; ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
/* First free all twin families in pointer array. */
aicPtrs = GetPtrCount(hpa);
for (ai = 0; ai < aicPtrs; ai++) DestroyTwinFamily(GetPtr(hpa, ai));
/* Now wipe out the pointer array. */
DestroyPtrArray(hpa);
return; }
/*
** GetTwinBriefcase() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE HBRFCASE GetTwinBriefcase(HTWIN htwin) { HBRFCASE hbr;
ASSERT(IS_VALID_HANDLE(htwin, TWIN));
switch (((PSTUB)htwin)->st) { case ST_OBJECTTWIN: hbr = ((PCOBJECTTWIN)htwin)->ptfParent->hbr; break;
case ST_TWINFAMILY: hbr = ((PCTWINFAMILY)htwin)->hbr; break;
case ST_FOLDERPAIR: hbr = ((PCFOLDERPAIR)htwin)->pfpd->hbr; break;
default: ERROR_OUT((TEXT("GetTwinBriefcase() called on unrecognized stub type %d."), ((PSTUB)htwin)->st)); hbr = NULL; break; }
return(hbr); }
/*
** FindObjectTwinInList() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL FindObjectTwinInList(HLIST hlist, HPATH hpath, PHNODE phnode) { ASSERT(IS_VALID_HANDLE(hlist, LIST)); ASSERT(IS_VALID_HANDLE(hpath, PATH)); ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
return(SearchUnsortedList(hlist, &ObjectTwinSearchCmp, hpath, phnode)); }
/*
** EnumTwins() ** ** Enumerates folder twins and twin families in a briefcase. ** ** Arguments: ** ** Returns: TRUE if halted. FALSE if not. ** ** Side Effects: none */ PUBLIC_CODE BOOL EnumTwins(HBRFCASE hbr, ENUMTWINSPROC etp, LPARAM lpData, PHTWIN phtwinStop) { HPTRARRAY hpa; ARRAYINDEX aicPtrs; ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_CODE_PTR(etp, ENUMTWINSPROC)); ASSERT(IS_VALID_WRITE_PTR(phtwinStop, HTWIN));
/* Enumerate folder pairs. */
*phtwinStop = NULL;
hpa = GetBriefcaseFolderPairPtrArray(hbr);
aicPtrs = GetPtrCount(hpa);
for (ai = 0; ai < aicPtrs; ai++) { PCFOLDERPAIR pcfp;
pcfp = GetPtr(hpa, ai);
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
if (! (*etp)((HTWIN)pcfp, lpData)) { *phtwinStop = (HTWIN)pcfp; break; } }
if (! *phtwinStop) { /* Enumerate twin families. */
hpa = GetBriefcaseTwinFamilyPtrArray(hbr);
aicPtrs = GetPtrCount(hpa);
for (ai = 0; ai < aicPtrs; ai++) { PCTWINFAMILY pctf;
pctf = GetPtr(hpa, ai);
ASSERT(IS_VALID_STRUCT_PTR(pctf, CTWINFAMILY));
if (! (*etp)((HTWIN)pctf, lpData)) { *phtwinStop = (HTWIN)pctf; break; } } }
return(*phtwinStop != NULL); }
/*
** FindObjectTwin() ** ** Looks for a twin family containing a specified object twin. ** ** Arguments: hpathFolder - folder containing object ** pcszName - name of object ** ** Returns: Handle to list node containing pointer to object twin if ** found, or NULL if not found. ** ** Side Effects: none */ PUBLIC_CODE BOOL FindObjectTwin(HBRFCASE hbr, HPATH hpathFolder, LPCTSTR pcszName, PHNODE phnode) { BOOL bFound = FALSE; HPTRARRAY hpaTwinFamilies; ARRAYINDEX aiFirst;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_HANDLE(hpathFolder, PATH)); ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR)); ASSERT(IS_VALID_WRITE_PTR(phnode, HNODE));
/* Search for a matching twin family. */
*phnode = NULL;
hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(hbr);
if (SearchSortedArray(hpaTwinFamilies, &TwinFamilySearchCmp, pcszName, &aiFirst)) { ARRAYINDEX aicPtrs; ARRAYINDEX ai; PTWINFAMILY ptf;
/*
* aiFirst holds the index of the first twin family with a common object * name matching pcszName. */
/*
* Now search each of these twin families for a folder matching * pcszFolder. */
aicPtrs = GetPtrCount(hpaTwinFamilies);
ASSERT(aicPtrs > 0); ASSERT(aiFirst >= 0); ASSERT(aiFirst < aicPtrs);
for (ai = aiFirst; ai < aicPtrs; ai++) { ptf = GetPtr(hpaTwinFamilies, ai);
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
/* Is this a twin family of objects of the given name? */
if (CompareNameStrings(GetString(ptf->hsName), pcszName) == CR_EQUAL) { bFound = SearchUnsortedList(ptf->hlistObjectTwins, &ObjectTwinSearchCmp, hpathFolder, phnode);
if (bFound) break; } else /* No. Stop searching. */ break; } }
return(bFound); }
/*
** TwinObjects() ** ** Twins two objects. ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none ** ** N.b., *ppot1 and *ppot2 are valid if TR_SUCCESS or TR_DUPLICATE_TWIN is ** returned. */ PUBLIC_CODE TWINRESULT TwinObjects(HBRFCASE hbr, HCLSIFACECACHE hcic, HPATH hpathFolder1, HPATH hpathFolder2, LPCTSTR pcszName, POBJECTTWIN *ppot1, POBJECTTWIN *ppot2) { TWINRESULT tr;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_HANDLE(hcic, CLSIFACECACHE)); ASSERT(IS_VALID_HANDLE(hpathFolder1, PATH)); ASSERT(IS_VALID_HANDLE(hpathFolder2, PATH)); ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR)); ASSERT(IS_VALID_WRITE_PTR(ppot1, POBJECTTWIN)); ASSERT(IS_VALID_WRITE_PTR(ppot2, POBJECTTWIN));
/* Fail twinning a file to itself. */
if (ComparePaths(hpathFolder1, hpathFolder2) != CR_EQUAL) { NEWLIST nl; HLIST hlistNewObjectTwins;
nl.dwFlags = 0;
if (CreateList(&nl, &hlistNewObjectTwins)) { /* Twin 'em. */
tr = TwinJustTheseTwoObjects(hbr, hpathFolder1, hpathFolder2, pcszName, ppot1, ppot2, hlistNewObjectTwins);
/*
* Add any new object twins to the lists of generated object twins for * all intersecting folder twins. Create new spin-off object twins * from the other folder twin connected to each intersecting folder * twin. Spin-off object twins are added to the twin family as they * are created. */
if (tr == TR_SUCCESS) { if (ApplyNewObjectTwinsToFolderTwins(hlistNewObjectTwins)) { /*
* Notify new object twins that they are object twins. Don't * notify folder object twins. */
if (*pcszName) NotifyNewObjectTwins(hlistNewObjectTwins, hcic); } else tr = TR_OUT_OF_MEMORY; }
if (tr != TR_SUCCESS) /*
* We must maintain a consistent internal state by deleting any new * twin family and object twins on failure, independent of source * folder twin count. */ EVAL(WalkList(hlistNewObjectTwins, &DestroyObjectTwinStubWalker, NULL));
DestroyList(hlistNewObjectTwins); } else tr = TR_OUT_OF_MEMORY; } else tr = TR_SAME_FOLDER;
ASSERT((tr != TR_SUCCESS && tr != TR_DUPLICATE_TWIN) || IS_VALID_STRUCT_PTR(*ppot1, COBJECTTWIN) && IS_VALID_STRUCT_PTR(*ppot2, COBJECTTWIN));
return(tr); }
/*
** CreateObjectTwin() ** ** Creates a new object twin, and adds it to a twin family. ** ** Arguments: ptf - pointer to parent twin family ** hpathFolder - folder of new object twin ** ** Returns: Pointer to new object twin if successful, or NULL if ** unsuccessful. ** ** Side Effects: none ** ** N.b., this function does not first check to see if the object twin already ** exists in the family. */ PUBLIC_CODE BOOL CreateObjectTwin(PTWINFAMILY ptf, HPATH hpathFolder, POBJECTTWIN *ppot) { BOOL bResult = FALSE; POBJECTTWIN potNew;
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY)); ASSERT(IS_VALID_HANDLE(hpathFolder, PATH)); ASSERT(IS_VALID_WRITE_PTR(ppot, POBJECTTWIN));
#ifdef DEBUG
{ HNODE hnodeUnused;
/* Is this object twin already in a twin family? */
if (FindObjectTwin(ptf->hbr, hpathFolder, GetString(ptf->hsName), &hnodeUnused)) ERROR_OUT((TEXT("CreateObjectTwin(): An object twin for %s\\%s already exists."), DebugGetPathString(hpathFolder), GetString(ptf->hsName))); }
#endif
/* Create a new OBJECTTWIN structure. */
if (AllocateMemory(sizeof(*potNew), &potNew)) { if (CopyPath(hpathFolder, GetBriefcasePathList(ptf->hbr), &(potNew->hpath))) { HNODE hnodeUnused;
/* Fill in new OBJECTTWIN fields. */
InitStub(&(potNew->stub), ST_OBJECTTWIN);
potNew->ptfParent = ptf; potNew->ulcSrcFolderTwins = 0;
MarkObjectTwinNeverReconciled(potNew);
/* Add the object twin to the twin family's list of object twins. */
if (InsertNodeAtFront(ptf->hlistObjectTwins, NULL, potNew, &hnodeUnused)) { *ppot = potNew; bResult = TRUE;
ASSERT(IS_VALID_STRUCT_PTR(*ppot, COBJECTTWIN)); } else { DeletePath(potNew->hpath); CREATEOBJECTTWIN_BAIL: FreeMemory(potNew); } } else goto CREATEOBJECTTWIN_BAIL; }
return(bResult); }
/*
** UnlinkObjectTwin() ** ** Unlinks an object twin. ** ** Arguments: pot - pointer to object twin to unlink ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT UnlinkObjectTwin(POBJECTTWIN pot) { TWINRESULT tr;
ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
ASSERT(IsStubFlagClear(&(pot->stub), STUB_FL_UNLINKED));
TRACE_OUT((TEXT("UnlinkObjectTwin(): Unlinking object twin for folder %s."), DebugGetPathString(pot->hpath)));
/* Is the object twin's twin family being deleted? */
if (IsStubFlagSet(&(pot->ptfParent->stub), STUB_FL_BEING_DELETED)) /* Yes. No need to unlink the object twin. */ tr = TR_SUCCESS; else { /* Are there any folder twin sources left for this object twin? */
if (! pot->ulcSrcFolderTwins) { HNODE hnode;
/*
* Search the object twin's parent's list of object twins for the * object twin to be unlinked. */
if (EVAL(FindObjectTwinInList(pot->ptfParent->hlistObjectTwins, pot->hpath, &hnode)) && EVAL(GetNodeData(hnode) == pot)) { ULONG ulcRemainingObjectTwins;
/* Unlink the object twin. */
DeleteNode(hnode);
SetStubFlag(&(pot->stub), STUB_FL_UNLINKED);
/*
* If we have just unlinked the second last object twin in a twin * family, destroy the twin family. */
ulcRemainingObjectTwins = GetNodeCount(pot->ptfParent->hlistObjectTwins);
if (ulcRemainingObjectTwins < 2) {
#ifdef DEBUG
TCHAR rgchName[MAX_NAME_LEN];
lstrcpyn(rgchName, GetString(pot->ptfParent->hsName), ARRAYSIZE(rgchName));
#endif
/* It's the end of the family line. */
tr = DestroyStub(&(pot->ptfParent->stub));
#ifdef DEBUG
if (tr == TR_SUCCESS) TRACE_OUT((TEXT("UnlinkObjectTwin(): Implicitly destroyed twin family for object %s."), rgchName));
#endif
if (ulcRemainingObjectTwins == 1 && tr == TR_HAS_FOLDER_TWIN_SRC) tr = TR_SUCCESS; } else tr = TR_SUCCESS; } else tr = TR_INVALID_PARAMETER;
ASSERT(tr == TR_SUCCESS); } else tr = TR_HAS_FOLDER_TWIN_SRC; }
return(tr); }
/*
** DestroyObjectTwin() ** ** Destroys an object twin. ** ** Arguments: pot - pointer to object twin to destroy ** ** Returns: void ** ** Side Effects: none */ PUBLIC_CODE void DestroyObjectTwin(POBJECTTWIN pot) { ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
TRACE_OUT((TEXT("DestroyObjectTwin(): Destroying object twin for folder %s."), DebugGetPathString(pot->hpath)));
DeletePath(pot->hpath); FreeMemory(pot);
return; }
/*
** UnlinkTwinFamily() ** ** Unlinks a twin family. ** ** Arguments: ptf - pointer to twin family to unlink ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT UnlinkTwinFamily(PTWINFAMILY ptf) { TWINRESULT tr;
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED)); ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_BEING_DELETED));
/*
* A twin family containing object twins generated by folder twins may not * be deleted, since those object twins may not be directly deleted. */
if (WalkList(ptf->hlistObjectTwins, &LookForSrcFolderTwinsWalker, NULL)) { HPTRARRAY hpaTwinFamilies; ARRAYINDEX aiUnlink;
TRACE_OUT((TEXT("UnlinkTwinFamily(): Unlinking twin family for object %s."), GetString(ptf->hsName)));
/* Search for the twin family to be unlinked. */
hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(ptf->hbr);
if (EVAL(SearchSortedArray(hpaTwinFamilies, &TwinFamilySortCmp, ptf, &aiUnlink))) { /* Unlink the twin family. */
ASSERT(GetPtr(hpaTwinFamilies, aiUnlink) == ptf);
DeletePtr(hpaTwinFamilies, aiUnlink);
SetStubFlag(&(ptf->stub), STUB_FL_UNLINKED); }
tr = TR_SUCCESS; } else tr = TR_HAS_FOLDER_TWIN_SRC;
return(tr); }
/*
** DestroyTwinFamily() ** ** Destroys a twin family. ** ** Arguments: ptf - pointer to twin family to destroy ** ** Returns: void ** ** Side Effects: none */ PUBLIC_CODE void DestroyTwinFamily(PTWINFAMILY ptf) { ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_BEING_DELETED));
TRACE_OUT((TEXT("DestroyTwinFamily(): Destroying twin family for object %s."), GetString(ptf->hsName)));
SetStubFlag(&(ptf->stub), STUB_FL_BEING_DELETED);
/*
* Destroy the object twins in the family one by one. Be careful not to use * an object twin after it has been destroyed. */
EVAL(WalkList(ptf->hlistObjectTwins, &DestroyObjectTwinStubWalker, NULL));
/* Destroy TWINFAMILY fields. */
DestroyList(ptf->hlistObjectTwins); DeleteString(ptf->hsName); FreeMemory(ptf);
return; }
/*
** MarkTwinFamilyNeverReconciled() ** ** Marks a twin family as never reconciled. ** ** Arguments: ptf - pointer to twin family to be marked never reconciled ** ** Returns: void ** ** Side Effects: Clears the twin family's last reconciliation time stamp. ** Marks all the object twins in the family never reconciled. */ PUBLIC_CODE void MarkTwinFamilyNeverReconciled(PTWINFAMILY ptf) { /*
* If we're being called from CreateTwinFamily(), the fields we're about to * set may currently be invalid. Don't fully verify the TWINFAMILY * structure. */
ASSERT(IS_VALID_WRITE_PTR(ptf, TWINFAMILY));
/* Mark all object twins in twin family as never reconciled. */
EVAL(WalkList(ptf->hlistObjectTwins, MarkObjectTwinNeverReconciledWalker, NULL));
return; }
/*
** MarkObjectTwinNeverReconciled() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE void MarkObjectTwinNeverReconciled(PVOID pot) { /*
* If we're being called from CreateObjectTwin(), the fields we're about to * set may currently be invalid. Don't fully verify the OBJECTTWIN * structure. */
ASSERT(IS_VALID_WRITE_PTR((PCOBJECTTWIN)pot, COBJECTTWIN));
ASSERT(IsStubFlagClear(&(((PCOBJECTTWIN)pot)->stub), STUB_FL_NOT_RECONCILED));
ZeroMemory(&(((POBJECTTWIN)pot)->fsLastRec), sizeof(((POBJECTTWIN)pot)->fsLastRec));
((POBJECTTWIN)pot)->fsLastRec.fscond = FS_COND_UNAVAILABLE;
return; }
/*
** MarkTwinFamilyDeletionPending() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void MarkTwinFamilyDeletionPending(PTWINFAMILY ptf) { ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
if (IsStubFlagClear(&(ptf->stub), STUB_FL_DELETION_PENDING)) TRACE_OUT((TEXT("MarkTwinFamilyDeletionPending(): Deletion now pending for twin family for %s."), GetString(ptf->hsName)));
SetStubFlag(&(ptf->stub), STUB_FL_DELETION_PENDING);
return; }
/*
** UnmarkTwinFamilyDeletionPending() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void UnmarkTwinFamilyDeletionPending(PTWINFAMILY ptf) { BOOL bContinue; HNODE hnode;
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
if (IsStubFlagSet(&(ptf->stub), STUB_FL_DELETION_PENDING)) { for (bContinue = GetFirstNode(ptf->hlistObjectTwins, &hnode); bContinue; bContinue = GetNextNode(hnode, &hnode)) { POBJECTTWIN pot;
pot = GetNodeData(hnode);
ClearStubFlag(&(pot->stub), STUB_FL_KEEP); }
ClearStubFlag(&(ptf->stub), STUB_FL_DELETION_PENDING);
TRACE_OUT((TEXT("UnmarkTwinFamilyDeletionPending(): Deletion no longer pending for twin family for %s."), GetString(ptf->hsName))); }
return; }
/*
** IsTwinFamilyDeletionPending() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsTwinFamilyDeletionPending(PCTWINFAMILY pctf) { ASSERT(IS_VALID_STRUCT_PTR(pctf, CTWINFAMILY));
return(IsStubFlagSet(&(pctf->stub), STUB_FL_DELETION_PENDING)); }
/*
** ClearTwinFamilySrcFolderTwinCount() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void ClearTwinFamilySrcFolderTwinCount(PTWINFAMILY ptf) { ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
EVAL(WalkList(ptf->hlistObjectTwins, &ClearSrcFolderTwinsWalker, NULL));
return; }
/*
** EnumObjectTwins() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL EnumObjectTwins(HBRFCASE hbr, ENUMGENERATEDOBJECTTWINSPROC egotp, PVOID pvRefData) { BOOL bResult = TRUE; HPTRARRAY hpaTwinFamilies; ARRAYINDEX aicPtrs; ARRAYINDEX ai;
/* pvRefData may be any value. */
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_CODE_PTR(egotp, ENUMGENERATEDOBJECTTWINPROC));
/* Walk the array of twin families. */
hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(hbr);
aicPtrs = GetPtrCount(hpaTwinFamilies); ai = 0;
while (ai < aicPtrs) { PTWINFAMILY ptf; BOOL bContinue; HNODE hnodePrev;
ptf = GetPtr(hpaTwinFamilies, ai);
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY)); ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED));
/* Lock the twin family so it isn't deleted out from under us. */
LockStub(&(ptf->stub));
/*
* Walk each twin family's list of object twins, calling the callback * function with each object twin. */
bContinue = GetFirstNode(ptf->hlistObjectTwins, &hnodePrev);
while (bContinue) { HNODE hnodeNext; POBJECTTWIN pot;
bContinue = GetNextNode(hnodePrev, &hnodeNext);
pot = (POBJECTTWIN)GetNodeData(hnodePrev);
ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
bResult = (*egotp)(pot, pvRefData);
if (! bResult) break;
hnodePrev = hnodeNext; }
/* Was the twin family unlinked? */
if (IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED)) /* No. */ ai++; else { /* Yes. */ aicPtrs--; ASSERT(aicPtrs == GetPtrCount(hpaTwinFamilies)); TRACE_OUT((TEXT("EnumObjectTwins(): Twin family for object %s unlinked by callback."), GetString(ptf->hsName))); }
UnlockStub(&(ptf->stub));
if (! bResult) break; }
return(bResult); }
/*
** ApplyNewFolderTwinsToTwinFamilies() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none ** ** If FALSE is returned, the array of twin families is in the same state it was ** in before ApplyNewFolderTwinsToTwinFamilies() was called. No clean-up is ** required by the caller in case of failure. ** ** This function collapses a pair of separate twin families when an object twin ** in one twin family intersects one of the folder twins in the pair of new ** folder twins and an object twin in the other twin family intersects the ** other folder twin in the pair of new folder twins. ** ** This function generates a spinoff object twin when an existing object twin ** intersects one of the folder twins in the pair of new folder twins, and no ** corresponding object twin for the other folder twin in the pair of new ** folder twins exists in the briefcase. The spinoff object twin is added to ** the generating object twin's twin family. A spinoff object twins cannot ** cause any existing pairs of twin families to be collapsed because the ** spinoff object twin did not previously exist in a twin family. ** ** A new folder twin may collapse pairs of existing twin families. E.g., ** consider the following scenario: ** ** 1) Twin families (c:\, d:\, foo), (e:\, f:\, foo), (c:\, d:\, bar), and ** (e:\, f:\, bar) exist. ** 2) New folder twin (d:\, e:\, *.*) is added. ** 3) Twin families (c:\, d:\, foo) and (e:\, f:\, foo) must be collpased into ** a single twin family because of the (d:\, e:\, *.*) folder twin. ** 4) Twin families (c:\, d:\, bar) and (e:\, f:\, bar) must be collpased into ** a single twin family because of the (d:\, e:\, *.*) folder twin. ** ** So we see that new folder twin (d:\, e:\, *.*) must collapse two pairs of ** existing twin families a single twin family each. Twin family ** (c:\, d:\, foo) plus twin family (e:\, f:\, foo) becomes twin family ** (c:\, d:\, e:\, f:\, foo). Twin family (c:\, d:\, bar) plus twin family ** (e:\, f:\, bar) becomes twin family (c:\, d:\, e:\, f:\, bar). */ PUBLIC_CODE BOOL ApplyNewFolderTwinsToTwinFamilies(PCFOLDERPAIR pcfp) { BOOL bResult = FALSE; HLIST hlistGeneratedObjectTwins;
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
/*
* Create lists to contain existing object twins generated by both folder * twins. */
if (CreateListOfGeneratedObjectTwins(pcfp, &hlistGeneratedObjectTwins)) { HLIST hlistOtherGeneratedObjectTwins;
if (CreateListOfGeneratedObjectTwins(pcfp->pfpOther, &hlistOtherGeneratedObjectTwins)) { NEWLIST nl; HLIST hlistNewObjectTwins;
/* Create list to contain spin-off object twins. */
nl.dwFlags = 0;
if (CreateList(&nl, &hlistNewObjectTwins)) { SPINOFFOBJECTTWININFO sooti;
/*
* Generate list of new object twins generated by new folder twins * to seed ApplyNewObjectTwinToFolderTwins(). */
sooti.pcfp = pcfp; sooti.hlistNewObjectTwins = hlistNewObjectTwins;
if (WalkList(hlistGeneratedObjectTwins, &GenerateSpinOffObjectTwin, &sooti)) { sooti.pcfp = pcfp->pfpOther; ASSERT(sooti.hlistNewObjectTwins == hlistNewObjectTwins);
if (WalkList(hlistOtherGeneratedObjectTwins, &GenerateSpinOffObjectTwin, &sooti)) { /*
* ApplyNewObjectTwinsToFolderTwins() sets ulcSrcFolderTwins * for all object twins in hlistNewObjectTwins. */
if (ApplyNewObjectTwinsToFolderTwins(hlistNewObjectTwins)) { /*
* Collapse separate twin families joined by new folder * twin. */
EVAL(WalkList(hlistGeneratedObjectTwins, &BuildBradyBunch, (PVOID)pcfp));
/*
* We don't need to call BuildBradyBunch() for * pcfp->pfpOther and hlistOtherGeneratedObjectTwins since * one twin family from each collapsed pair of twin * families must come from each list of generated object * twins. */
/*
* Increment source folder twin count for all pre-existing * object twins generated by the new folder twins. */
EVAL(WalkList(hlistGeneratedObjectTwins, &IncrementSrcFolderTwinsWalker, NULL)); EVAL(WalkList(hlistOtherGeneratedObjectTwins, &IncrementSrcFolderTwinsWalker, NULL));
bResult = TRUE; } } }
/* Wipe out any new object twins on failure. */
if (! bResult) EVAL(WalkList(hlistNewObjectTwins, &DestroyObjectTwinStubWalker, NULL));
DestroyList(hlistNewObjectTwins); }
DestroyList(hlistOtherGeneratedObjectTwins); }
DestroyList(hlistGeneratedObjectTwins); }
return(bResult); }
/*
** TransplantObjectTwin() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT TransplantObjectTwin(POBJECTTWIN pot, HPATH hpathOldFolder, HPATH hpathNewFolder) { TWINRESULT tr;
ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(IS_VALID_HANDLE(hpathOldFolder, PATH)); ASSERT(IS_VALID_HANDLE(hpathNewFolder, PATH));
/* Is this object twin rooted in the renamed folder's subtree? */
if (IsPathPrefix(pot->hpath, hpathOldFolder)) { TCHAR rgchPathSuffix[MAX_PATH_LEN]; LPCTSTR pcszSubPath; HPATH hpathNew;
/* Yes. Change the object twin's root. */
pcszSubPath = FindChildPathSuffix(hpathOldFolder, pot->hpath, rgchPathSuffix);
if (AddChildPath(GetBriefcasePathList(pot->ptfParent->hbr), hpathNewFolder, pcszSubPath, &hpathNew)) { TRACE_OUT((TEXT("TransplantObjectTwin(): Transplanted object twin %s\\%s to %s\\%s."), DebugGetPathString(pot->hpath), GetString(pot->ptfParent->hsName), DebugGetPathString(hpathNew), GetString(pot->ptfParent->hsName)));
DeletePath(pot->hpath); pot->hpath = hpathNew;
tr = TR_SUCCESS; } else tr = TR_OUT_OF_MEMORY; } else tr = TR_SUCCESS;
return(tr); }
/*
** IsFolderObjectTwinName() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsFolderObjectTwinName(LPCTSTR pcszName) { ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
return(! *pcszName); }
/*
** IsValidHTWIN() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsValidHTWIN(HTWIN htwin) { BOOL bValid = FALSE;
if (IS_VALID_STRUCT_PTR((PCSTUB)htwin, CSTUB)) { switch (((PSTUB)htwin)->st) { case ST_OBJECTTWIN: bValid = IS_VALID_HANDLE((HOBJECTTWIN)htwin, OBJECTTWIN); break;
case ST_TWINFAMILY: bValid = IS_VALID_HANDLE((HTWINFAMILY)htwin, TWINFAMILY); break;
case ST_FOLDERPAIR: bValid = IS_VALID_HANDLE((HFOLDERTWIN)htwin, FOLDERTWIN); break;
default: ERROR_OUT((TEXT("IsValidHTWIN() called on unrecognized stub type %d."), ((PSTUB)htwin)->st)); break; } } else ERROR_OUT((TEXT("IsValidHTWIN() called on bad twin handle %#lx."), htwin));
return(bValid); }
/*
** IsValidHTWINFAMILY() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsValidHTWINFAMILY(HTWINFAMILY htf) { return(IS_VALID_STRUCT_PTR((PTWINFAMILY)htf, CTWINFAMILY)); }
/*
** IsValidHOBJECTTWIN() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsValidHOBJECTTWIN(HOBJECTTWIN hot) { return(IS_VALID_STRUCT_PTR((POBJECTTWIN)hot, COBJECTTWIN)); }
#ifdef VSTF
/*
** IsValidPCTWINFAMILY() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsValidPCTWINFAMILY(PCTWINFAMILY pctf) { BOOL bResult;
/* All the fields of an unlinked twin family should be valid. */
/* Don't validate hbr. */
/*
* In some cases there may be fewer than two object twins in a twin family, * e.g., when two twin families are being collapsed, when a twin family is * being deleted, and when a twin family is being read in from a database. */
if (IS_VALID_READ_PTR(pctf, CTWINFAMILY) && IS_VALID_STRUCT_PTR(&(pctf->stub), CSTUB) && FLAGS_ARE_VALID(GetStubFlags(&(pctf->stub)), ALL_TWIN_FAMILY_FLAGS) && IS_VALID_HANDLE(pctf->hsName, STRING) && IS_VALID_HANDLE(pctf->hlistObjectTwins, LIST)) bResult = WalkList(pctf->hlistObjectTwins, &IsValidObjectTwinWalker, (PVOID)pctf); else bResult = FALSE;
return(bResult); }
/*
** IsValidPCOBJECTTWIN() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsValidPCOBJECTTWIN(PCOBJECTTWIN pcot) { /*
* All the fields of an unlinked object twin should be valid, except * possibly ptfParent and fsCurrent. */
/*
* Winner of the 1995 "I think the compiler generates better code * if its takes up less space on the screen" award. * * Running up in the "Make the debugger execute 2K of code as an * atomic operation while debugger" category. * */
return(IS_VALID_READ_PTR(pcot, COBJECTTWIN) && IS_VALID_STRUCT_PTR(&(pcot->stub), CSTUB) && FLAGS_ARE_VALID(GetStubFlags(&(pcot->stub)), ALL_OBJECT_TWIN_FLAGS) && IS_VALID_HANDLE(pcot->hpath, PATH) && (IsStubFlagSet(&(pcot->stub), STUB_FL_UNLINKED) || IS_VALID_READ_PTR(pcot->ptfParent, CTWINFAMILY)) && IS_VALID_STRUCT_PTR(&(pcot->fsLastRec), CFILESTAMP) && (IsStubFlagClear(&(pcot->stub), STUB_FL_FILE_STAMP_VALID) || (IS_VALID_STRUCT_PTR(&(pcot->fsCurrent), CFILESTAMP))) && EVAL(! (! IsReconciledFileStamp(&(pcot->fsLastRec)) && IsStubFlagSet(&(pcot->stub), STUB_FL_NOT_RECONCILED)))); }
#endif
/*
** WriteTwinFamilies() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT WriteTwinFamilies(HCACHEDFILE hcf, HPTRARRAY hpaTwinFamilies) { TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED; DWORD dwcbTwinFamiliesDBHeaderOffset;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_HANDLE(hpaTwinFamilies, PTRARRAY));
/* Save initial file poisition. */
dwcbTwinFamiliesDBHeaderOffset = GetCachedFilePointerPosition(hcf);
if (dwcbTwinFamiliesDBHeaderOffset != INVALID_SEEK_POSITION) { TWINFAMILIESDBHEADER tfdbh;
/* Leave space for the twin families' header. */
ZeroMemory(&tfdbh, sizeof(tfdbh));
if (WriteToCachedFile(hcf, (PCVOID)&tfdbh, sizeof(tfdbh), NULL)) { ARRAYINDEX aicPtrs; ARRAYINDEX ai;
tr = TR_SUCCESS;
aicPtrs = GetPtrCount(hpaTwinFamilies);
for (ai = 0; ai < aicPtrs && tr == TR_SUCCESS; ai++) tr = WriteTwinFamily(hcf, GetPtr(hpaTwinFamilies, ai));
if (tr == TR_SUCCESS) { /* Save twin families' header. */
tfdbh.lcTwinFamilies = aicPtrs;
tr = WriteDBSegmentHeader(hcf, dwcbTwinFamiliesDBHeaderOffset, &tfdbh, sizeof(tfdbh));
if (tr == TR_SUCCESS) TRACE_OUT((TEXT("WriteTwinFamilies(): Wrote %ld twin families."), tfdbh.lcTwinFamilies)); } } }
return(tr); }
/*
** ReadTwinFamilies() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT ReadTwinFamilies(HCACHEDFILE hcf, HBRFCASE hbr, PCDBVERSION pcdbver, HHANDLETRANS hhtFolderTrans, HHANDLETRANS hhtNameTrans) { TWINRESULT tr; TWINFAMILIESDBHEADER tfdbh; DWORD dwcbRead;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_READ_PTR(pcdbver, DBVERSION)); ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS)); ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
if (ReadFromCachedFile(hcf, &tfdbh, sizeof(tfdbh), &dwcbRead) && dwcbRead == sizeof(tfdbh)) { LONG l;
tr = TR_SUCCESS;
TRACE_OUT((TEXT("ReadTwinFamilies(): Reading %ld twin families."), tfdbh.lcTwinFamilies));
for (l = 0; l < tfdbh.lcTwinFamilies && tr == TR_SUCCESS; l++) tr = ReadTwinFamily(hcf, hbr, pcdbver, hhtFolderTrans, hhtNameTrans);
ASSERT(AreTwinFamiliesValid(GetBriefcaseTwinFamilyPtrArray(hbr))); } else tr = TR_CORRUPT_BRIEFCASE;
return(tr); }
/***************************** Exported Functions ****************************/
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | AddObjectTwin | Twins two objects.
@parm HBRFCASE | hbr | A handle to the open briefcase that the new object twins are to be added to.
@parm PCNEWOBJECTTWIN | pcnot | A pointer to a CNEWOBJECTTWIN describing the objects to be twinned.
@parm PHTWINFAMILY | phtf | A pointer to an HTWINFAMILY to be filled in with a handle to the twin family to which the object twins were added. This handle may refer to a new or existing twin family. *phtf is only valid if TR_SUCCESS is returned.
@rdesc If the objects were twinned successfully, TR_SUCCESS is returned, and *phTwinFamily contains a handle to the associated twin family. Otherwise, the objects were not twinned successfully, the return value indicates the error that occurred, and *phtf is undefined. If one or both of the volumes specified by the NEWOBJECTTWIN structure is not present, TR_UNAVAILABLE_VOLUME will be returned, and the object twin will not be added.
@comm Once the caller is finshed with the twin handle returned by AddObjectTwin(), ReleaseTwinHandle() should be called to release the twin handle. DeleteTwin() does not release a twin handle returned by AddObjectTwin().
@xref ReleaseTwinHandle DeleteTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI AddObjectTwin(HBRFCASE hbr, PCNEWOBJECTTWIN pcnot, PHTWINFAMILY phtf) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(AddObjectTwin);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hbr, BRFCASE) && IS_VALID_STRUCT_PTR(pcnot, CNEWOBJECTTWIN) && EVAL(pcnot->ulSize == sizeof(*pcnot)) && IS_VALID_WRITE_PTR(phtf, HTWINFAMILY)) #endif
{ HCLSIFACECACHE hcic;
if (CreateClassInterfaceCache(&hcic)) { HPATHLIST hplBriefcase; HPATH hpathFolder1;
InvalidatePathListInfo(GetBriefcasePathList(hbr));
hplBriefcase = GetBriefcasePathList(hbr);
tr = TranslatePATHRESULTToTWINRESULT(AddPath(hplBriefcase, pcnot->pcszFolder1, &hpathFolder1));
if (tr == TR_SUCCESS) { HPATH hpathFolder2;
tr = TranslatePATHRESULTToTWINRESULT(AddPath(hplBriefcase, pcnot->pcszFolder2, &hpathFolder2));
if (tr == TR_SUCCESS) { POBJECTTWIN pot1; POBJECTTWIN pot2;
tr = TwinObjects(hbr, hcic, hpathFolder1, hpathFolder2, pcnot->pcszName, &pot1, &pot2);
/*
* These twins are not really duplicates unless they were already * connected as object twins. */
if (tr == TR_DUPLICATE_TWIN && (IsStubFlagClear(&(pot1->stub), STUB_FL_FROM_OBJECT_TWIN) || IsStubFlagClear(&(pot2->stub), STUB_FL_FROM_OBJECT_TWIN))) tr = TR_SUCCESS;
if (tr == TR_SUCCESS) { /* Success! */
ASSERT(pot1->ptfParent == pot2->ptfParent); ASSERT(IS_VALID_HANDLE((HTWINFAMILY)(pot1->ptfParent), TWINFAMILY));
LockStub(&(pot1->ptfParent->stub));
SetStubFlag(&(pot1->stub), STUB_FL_FROM_OBJECT_TWIN); SetStubFlag(&(pot2->stub), STUB_FL_FROM_OBJECT_TWIN);
*phtf = (HTWINFAMILY)(pot1->ptfParent); }
DeletePath(hpathFolder2); }
DeletePath(hpathFolder1); }
DestroyClassInterfaceCache(hcic); } else tr = TR_OUT_OF_MEMORY; } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(AddObjectTwin, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | ReleaseTwinHandle | Releases a twin handle returned by AddObjectTwin(), AddFolderTwin(), or GetObjectTwinHandle().
@parm HTWIN | hTwin | The twin handle that is to be released.
@rdesc If the twin handle was released successfully, TR_SUCCESS is returned. Otherwise, the twin handle was not released successfully, and the return value indicates the error that occurred. hTwin is no longer a valid twin handle after ReleaseTwinHandle() is called.
@comm If the lock count of the twin drops to 0 and deletion is pending against the twin, the twin is deleted. If ReleaseTwinHandle() is called with a valid handle to a twin that has been deleted, TR_SUCCESS will be returned. DeleteTwin() does not release a twin handle returned by AddObjectTwin(), AddFolderTwin(), or GetObjectTwinHandle(). ReleaseTwinHandle() should be called to release a twin handle returned by AddObjectTwin(), AddFolderTwin(), or GetObjectTwinHandle(). DeleteTwin() should be called before ReleaseTwinHandle() if the twin is to be deleted.
@xref AddObjectTwin AddFolderTwin DeleteTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI ReleaseTwinHandle(HTWIN hTwin) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(ReleaseTwinHandle);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hTwin, TWIN)) #endif
{ UnlockStub((PSTUB)hTwin);
tr = TR_SUCCESS; } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(ReleaseTwinHandle, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | DeleteTwin | Deletes a twin from the synchronization database. A twin is added to the synchronization database by AddObjectTwin() or AddFolderTwin().
@parm HTWIN | htwin | A handle to the twin being deleted.
@rdesc If the twin was deleted successfully, TR_SUCCESS is returned. Otherwise, the twin was not deleted successfully, and the return value indicates the error that occurred.
@comm If DeleteTwin() is called with a valid handle to a twin that has been deleted, TR_SUCCESS will be returned. DeleteTwin() does not release a twin handle returned by AddObjectTwin(), AddFolderTwin(), or GetObjectTwinHandle(). ReleaseTwinHandle() should be called to release a twin handle returned by AddObjectTwin(), AddFolderTwin(), or GetObjectTwinHandle(). DeleteTwin() should be called before ReleaseTwinHandle() if the twin is to be deleted. DeleteTwin() will always succeed on a valid HFOLDERTWIN. DeleteTwin() will fail on a valid HOBJECTTWIN for any object twin that has source folder twins, returning TR_HAS_FOLDER_TWIN_SRC. DeleteTwin() will also fail on a valid HTWINFAMILY for any twin family that contains two or more object twins with source folder twins, returning TR_HAS_FOLDER_TWIN_SRC. A twin family cannot contain only one object twin with source folder twins. Twin families can only contain 0, 2, or more object twins with source folder twins.
@xref AddObjectTwin AddFolderTwin ReleaseTwinHandle IsOrphanObjectTwin CountSourceFolderTwins
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI DeleteTwin(HTWIN hTwin) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(DeleteTwin);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hTwin, TWIN)) #endif
{ tr = DestroyStub((PSTUB)hTwin); } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(DeleteTwin, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | GetObjectTwinHandle | Determines whether or not an object is a twin. If the object is a twin, a twin handle for the twinned object is returned.
@parm HBRFCASE | hbr | A handle to the open briefcase to be checked for the object twin.
@parm PCSTR | pcszFolder | A pointer to a string indicating the object's folder.
@parm PCSTR | pcszName | A pointer to a string indicating the object's name.
@parm PHOBJECTTWIN | phot | A pointer to an HOBJECTTWIN to be filled in with a handle to the object twin or NULL. If the object is a twin, *phObjectTwin is filled in with a handle to the object twin. If the object is not a twin, *phObjectTwin is filled in with NULL. *phObjectTwin is only valid if TR_SUCCESS is returned.
@rdesc If the lookup was successful, TR_SUCCESS is returned. Otherwise, the lookup was not successful, and the return value indicates the error that occurred.
@comm Once the caller is finshed with the twin handle returned by GetObjectTwinHandle(), ReleaseTwinHandle() should be called to release the twin handle. N.b., DeleteTwin() does not release a twin handle returned by GetObjectTwinHandle().
@xref AddObjectTwin ReleaseTwinHandle DeleteTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI GetObjectTwinHandle(HBRFCASE hbr, LPCTSTR pcszFolder, LPCTSTR pcszName, PHOBJECTTWIN phot) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(GetObjectTwinHandle);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hbr, BRFCASE) && IS_VALID_STRING_PTR(pcszFolder, CSTR) && IS_VALID_STRING_PTR(pcszName, CSTR) && IS_VALID_WRITE_PTR(phot, HOBJECTTWIN)) #endif
{ HPATH hpath;
InvalidatePathListInfo(GetBriefcasePathList(hbr));
tr = TranslatePATHRESULTToTWINRESULT( AddPath(GetBriefcasePathList(hbr), pcszFolder, &hpath));
if (tr == TR_SUCCESS) { BOOL bFound; HNODE hnode; POBJECTTWIN pot;
/* Is this object already an object twin? */
bFound = FindObjectTwin(hbr, hpath, pcszName, &hnode);
if (bFound) /* Yes. */ pot = (POBJECTTWIN)GetNodeData(hnode); else /*
* No. Expand folder twins, and check for a generating folder * twin. */ tr = TryToGenerateObjectTwin(hbr, hpath, pcszName, &bFound, &pot);
if (tr == TR_SUCCESS) { if (bFound) { LockStub(&(pot->stub));
TRACE_OUT((TEXT("GetObjectTwinHandle(): %s\\%s is an object twin."), DebugGetPathString(hpath), pcszName));
*phot = (HOBJECTTWIN)pot;
ASSERT(IS_VALID_HANDLE(*phot, OBJECTTWIN)); } else { TRACE_OUT((TEXT("GetObjectTwinHandle(): %s\\%s is not an object twin."), DebugGetPathString(hpath), pcszName));
*phot = NULL; } }
DeletePath(hpath); } } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(GetObjectTwinHandle, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | IsOrphanObjectTwin | Determines whether or not an object twin was added to the synchronization database through a call to AddObjectTwin().
@parm HOBJECTTWIN | hot | A handle to the object twin whose orphan status is to be determined.
@parm PBOOL | pbIsOrphanObjectTwin | A pointer to a BOOL to be filled in with TRUE if the object twin was added through AddObjectTwin(). *pbIsOrphanObjectTwin is only valid if TR_SUCCESS is returned.
@rdesc If the lookup was successful, TR_SUCCESS is returned. Otherwise, the lookup was not successful, and the return value indicates the error that occurred.
@comm If IsOrphanObjectTwin() is called with a valid handle to an object twin that has been deleted, TR_DELETED_TWIN will be returned.
@xref AddObjectTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI IsOrphanObjectTwin(HOBJECTTWIN hot, PBOOL pbIsOrphanObjectTwin) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(IsOrphanObjectTwin);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hot, OBJECTTWIN) && IS_VALID_WRITE_PTR(pbIsOrphanObjectTwin, BOOL)) #endif
{ /* Has this object twin been deleted? */
if (IsStubFlagClear(&(((POBJECTTWIN)(hot))->stub), STUB_FL_UNLINKED)) { /* No. */
if (IsStubFlagSet(&(((POBJECTTWIN)hot)->stub), STUB_FL_FROM_OBJECT_TWIN)) { *pbIsOrphanObjectTwin = TRUE;
TRACE_OUT((TEXT("IsOrphanObjectTwin(): Object twin %s\\%s is an orphan object twin."), DebugGetPathString(((POBJECTTWIN)hot)->hpath), GetString(((POBJECTTWIN)hot)->ptfParent->hsName))); } else { *pbIsOrphanObjectTwin = FALSE;
TRACE_OUT((TEXT("IsOrphanObjectTwin(): Object twin %s\\%s is not an orphan object twin."), DebugGetPathString(((POBJECTTWIN)hot)->hpath), GetString(((POBJECTTWIN)hot)->ptfParent->hsName))); }
ASSERT(*pbIsOrphanObjectTwin || ((POBJECTTWIN)hot)->ulcSrcFolderTwins);
tr = TR_SUCCESS; } else /* Yes. */ tr = TR_DELETED_TWIN; } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(IsOrphanObjectTwin, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | CountSourceFolderTwins | Determines the number of folder twins that generate an object twin.
@parm HOBJECTTWIN | hot | A handle to the object twin whose folder twin sources are to be counted.
@parm PULONG | pulcSrcFolderTwins | A pointer to a ULONG to be filled in with the number of folder twins that generate the object twin. *pulcSrcFolderTwins is only valid if TR_SUCCESS is returned.
@rdesc If the lookup was successful, TR_SUCCESS is returned. Otherwise, the lookup was not successful, and the return value indicates the error that occurred.
@comm If CountSourceFolderTwins() is called with a valid handle to a folder twin that has been deleted, TR_DELETED_TWIN will be returned.
@xref AddFolderTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI CountSourceFolderTwins(HOBJECTTWIN hot, PULONG pulcSrcFolderTwins) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(CountSourceFolderTwins);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hot, OBJECTTWIN) && IS_VALID_WRITE_PTR(pulcSrcFolderTwins, ULONG)) #endif
{ /* Has this object twin been deleted? */
if (IsStubFlagClear(&(((POBJECTTWIN)(hot))->stub), STUB_FL_UNLINKED)) { /* No. */
*pulcSrcFolderTwins = ((POBJECTTWIN)hot)->ulcSrcFolderTwins;
ASSERT(*pulcSrcFolderTwins > 0 || IsStubFlagSet(&(((POBJECTTWIN)hot)->stub), STUB_FL_FROM_OBJECT_TWIN));
TRACE_OUT((TEXT("CountSourceFolderTwins(): Object twin %s\\%s has %lu source folder twins."), DebugGetPathString(((POBJECTTWIN)hot)->hpath), GetString(((POBJECTTWIN)hot)->ptfParent->hsName), *pulcSrcFolderTwins));
tr = TR_SUCCESS; } else /* Yes. */ tr = TR_DELETED_TWIN; } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(CountSourceFolderTwins, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
/******************************************************************************
@doc SYNCENGAPI
@api BOOL | AnyTwins | Determines whether or not any twins currently exist in a briefcase.
@parm HBRFCASE | hbr | A handle to the open briefcase to be checked for twins.
@parm PBOOL | pbAnyTwins | A pointer to a BOOL to be filled in with TRUE if the given briefcase contains any twins or FALSE if not. *pbAnyTwins is only valid if TR_SUCCESS is returned.
@rdesc If the lookup was successful, TR_SUCCESS is returned. Otherwise, the lookup was not successful, and the return value indicates the error that occurred.
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI AnyTwins(HBRFCASE hbr, PBOOL pbAnyTwins) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(AnyTwins);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hbr, BRFCASE) && IS_VALID_WRITE_PTR(pbAnyTwins, BOOL)) #endif
{ if (GetPtrCount(GetBriefcaseTwinFamilyPtrArray(hbr)) || GetPtrCount(GetBriefcaseFolderPairPtrArray(hbr))) { *pbAnyTwins = TRUE;
TRACE_OUT((TEXT("AnyTwins(): There are twins in briefcase %#lx."), hbr)); } else { *pbAnyTwins = FALSE;
TRACE_OUT((TEXT("AnyTwins(): There are not any twins in briefcase %#lx."), hbr)); }
tr = TR_SUCCESS; } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(AnyTwins, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
|