NT4/private/windows/rover/filesync/core/twin.c

3726 lines
92 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*
* 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);
GetPathString(pcot->hpath, rgchPath);
CatPath(rgchPath, GetString(pcot->ptfParent->hsName));
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];
GetPathString(pcot->hpath, 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];
lstrcpy(rgchName, GetString(pot->ptfParent->hsName));
#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);
}