3726 lines
92 KiB
C
3726 lines
92 KiB
C
/*
|
|
* 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);
|
|
}
|
|
|