Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2745 lines
72 KiB

/*
* foldtwin.c - Folder twin ADT module.
*/
/* Headers
**********/
#include "project.h"
#pragma hdrstop
#include "stub.h"
#include "subcycle.h"
/* Constants
************/
/* pointer array allocation constants */
#define NUM_START_FOLDER_TWIN_PTRS (16)
#define NUM_FOLDER_TWIN_PTRS_TO_ADD (16)
/* Types
********/
/* internal new folder twin description */
typedef struct _inewfoldertwin
{
HPATH hpathFirst;
HPATH hpathSecond;
HSTRING hsName;
DWORD dwAttributes;
HBRFCASE hbr;
DWORD dwFlags;
}
INEWFOLDERTWIN;
DECLARE_STANDARD_TYPES(INEWFOLDERTWIN);
/* database folder twin list header */
typedef struct _dbfoldertwinlistheader
{
LONG lcFolderPairs;
}
DBFOLDERTWINLISTHEADER;
DECLARE_STANDARD_TYPES(DBFOLDERTWINLISTHEADER);
/* database folder twin structure */
typedef struct _dbfoldertwin
{
/* shared stub flags */
DWORD dwStubFlags;
/* old handle to first folder path */
HPATH hpath1;
/* old handle to second folder path */
HPATH hpath2;
/* old handle to name string */
HSTRING hsName;
/* attributes to match */
DWORD dwAttributes;
}
DBFOLDERTWIN;
DECLARE_STANDARD_TYPES(DBFOLDERTWIN);
/***************************** Private Functions *****************************/
/* Module Prototypes
********************/
PRIVATE_CODE TWINRESULT MakeINewFolderTwin(HBRFCASE, PCNEWFOLDERTWIN, PINEWFOLDERTWIN);
PRIVATE_CODE void ReleaseINewFolderTwin(PINEWFOLDERTWIN);
PRIVATE_CODE TWINRESULT TwinFolders(PCINEWFOLDERTWIN, PFOLDERPAIR *);
PRIVATE_CODE BOOL FindFolderPair(PCINEWFOLDERTWIN, PFOLDERPAIR *);
PRIVATE_CODE BOOL CreateFolderPair(PCINEWFOLDERTWIN, PFOLDERPAIR *);
PRIVATE_CODE BOOL CreateHalfOfFolderPair(HPATH, HBRFCASE, PFOLDERPAIR *);
PRIVATE_CODE void DestroyHalfOfFolderPair(PFOLDERPAIR);
PRIVATE_CODE BOOL CreateSharedFolderPairData(PCINEWFOLDERTWIN, PFOLDERPAIRDATA *);
PRIVATE_CODE void DestroySharedFolderPairData(PFOLDERPAIRDATA);
PRIVATE_CODE COMPARISONRESULT FolderPairSortCmp(PCVOID, PCVOID);
PRIVATE_CODE COMPARISONRESULT FolderPairSearchCmp(PCVOID, PCVOID);
PRIVATE_CODE BOOL RemoveSourceFolderTwin(POBJECTTWIN, PVOID);
PRIVATE_CODE void UnlinkHalfOfFolderPair(PFOLDERPAIR);
PRIVATE_CODE BOOL FolderTwinIntersectsFolder(PCFOLDERPAIR, HPATH);
PRIVATE_CODE TWINRESULT CreateListOfFolderTwins(HBRFCASE, ARRAYINDEX, HPATH, PFOLDERTWIN *, PARRAYINDEX);
PRIVATE_CODE void DestroyListOfFolderTwins(PFOLDERTWIN);
PRIVATE_CODE TWINRESULT AddFolderTwinToList(PFOLDERPAIR, PFOLDERTWIN, PFOLDERTWIN *);
PRIVATE_CODE TWINRESULT TransplantFolderPair(PFOLDERPAIR, HPATH, HPATH);
PRIVATE_CODE TWINRESULT WriteFolderPair(HCACHEDFILE, PFOLDERPAIR);
PRIVATE_CODE TWINRESULT ReadFolderPair(HCACHEDFILE, HBRFCASE, HHANDLETRANS, HHANDLETRANS);
#ifdef VSTF
PRIVATE_CODE BOOL IsValidPCNEWFOLDERTWIN(PCNEWFOLDERTWIN);
PRIVATE_CODE BOOL IsValidPCFOLDERTWINLIST(PCFOLDERTWINLIST);
PRIVATE_CODE BOOL IsValidPCFOLDERTWIN(PCFOLDERTWIN);
PRIVATE_CODE BOOL IsValidFolderPairHalf(PCFOLDERPAIR);
PRIVATE_CODE BOOL IsValidPCFOLDERPAIRDATA(PCFOLDERPAIRDATA);
#endif
#ifdef DEBUG
PRIVATE_CODE BOOL IsValidPCINEWFOLDERTWIN(PCINEWFOLDERTWIN);
PRIVATE_CODE BOOL AreFolderPairsValid(HPTRARRAY);
#endif
/*
** MakeINewFolderTwin()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE TWINRESULT MakeINewFolderTwin(HBRFCASE hbr,
PCNEWFOLDERTWIN pcnftSrc,
PINEWFOLDERTWIN pinftDest)
{
TWINRESULT tr;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
ASSERT(IS_VALID_STRUCT_PTR(pcnftSrc, CNEWFOLDERTWIN));
ASSERT(IS_VALID_WRITE_PTR(pinftDest, CINEWFOLDERTWIN));
if (AddString(pcnftSrc->pcszName, GetBriefcaseNameStringTable(hbr),
GetHashBucketIndex, &(pinftDest->hsName)))
{
HPATHLIST hpl;
hpl = GetBriefcasePathList(hbr);
tr = TranslatePATHRESULTToTWINRESULT(
AddPath(hpl, pcnftSrc->pcszFolder1, &(pinftDest->hpathFirst)));
if (tr == TR_SUCCESS)
{
tr = TranslatePATHRESULTToTWINRESULT(
AddPath(hpl, pcnftSrc->pcszFolder2, &(pinftDest->hpathSecond)));
if (tr == TR_SUCCESS)
{
pinftDest->dwAttributes = pcnftSrc->dwAttributes;
pinftDest->dwFlags = pcnftSrc->dwFlags;
pinftDest->hbr = hbr;
}
else
{
DeletePath(pinftDest->hpathFirst);
MAKEINEWFOLDERTWIN_BAIL:
DeleteString(pinftDest->hsName);
}
}
else
goto MAKEINEWFOLDERTWIN_BAIL;
}
else
tr = TR_OUT_OF_MEMORY;
ASSERT(tr != TR_SUCCESS ||
IS_VALID_STRUCT_PTR(pinftDest, CINEWFOLDERTWIN));
return(tr);
}
/*
** ReleaseINewFolderTwin()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE void ReleaseINewFolderTwin(PINEWFOLDERTWIN pinft)
{
ASSERT(IS_VALID_STRUCT_PTR(pinft, CINEWFOLDERTWIN));
DeletePath(pinft->hpathFirst);
DeletePath(pinft->hpathSecond);
DeleteString(pinft->hsName);
return;
}
/*
** TwinFolders()
**
** Twins two folders.
**
** Arguments:
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PRIVATE_CODE TWINRESULT TwinFolders(PCINEWFOLDERTWIN pcinft, PFOLDERPAIR *ppfp)
{
PFOLDERPAIR pfp;
TWINRESULT tr;
ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN));
ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
/* Are the two folders the same? */
if (ComparePaths(pcinft->hpathFirst, pcinft->hpathSecond) != CR_EQUAL)
{
/* Look for the two folders in existing folder pairs. */
if (FindFolderPair(pcinft, &pfp))
{
/* Found a existing matching folder pair. Complain. */
*ppfp = pfp;
tr = TR_DUPLICATE_TWIN;
}
else
{
/*
* No existing matching folder pairs found. Only allowing twinning to
* paths whose roots are available.
*/
if (IsPathVolumeAvailable(pcinft->hpathFirst) &&
IsPathVolumeAvailable(pcinft->hpathSecond))
{
/*
* If this is a new folder subtree pair, check to see if it would
* create a cycle.
*/
if (IS_FLAG_SET(pcinft->dwFlags, NFT_FL_SUBTREE))
tr = CheckForSubtreeCycles(
GetBriefcaseFolderPairPtrArray(pcinft->hbr),
pcinft->hpathFirst, pcinft->hpathSecond,
pcinft->hsName);
else
tr = TR_SUCCESS;
if (tr == TR_SUCCESS)
{
if (CreateFolderPair(pcinft, &pfp))
{
*ppfp = pfp;
TRACE_OUT((TEXT("TwinFolders(): Creating %s twin pair %s and %s, files %s."),
IS_FLAG_SET(pcinft->dwFlags, NFT_FL_SUBTREE) ? TEXT("subtree") : TEXT("folder"),
DebugGetPathString(pcinft->hpathFirst),
DebugGetPathString(pcinft->hpathSecond),
GetString(pcinft->hsName)));
}
else
tr = TR_OUT_OF_MEMORY;
}
}
else
tr = TR_UNAVAILABLE_VOLUME;
}
}
else
tr = TR_SAME_FOLDER;
return(tr);
}
/*
** FindFolderPair()
**
** Looks for a folder pair matching the given description.
**
** Arguments: pcinft - pointer to INEWFOLDERTWIN describing folder pair to
** search for
**
** Returns: Pointer to PFOLDERPAIR if found. NULL if not found.
**
** Side Effects: none
*/
PRIVATE_CODE BOOL FindFolderPair(PCINEWFOLDERTWIN pcinft, PFOLDERPAIR *ppfp)
{
ARRAYINDEX aiFirst;
ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN));
ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
/*
* Search all folder pairs containing the first folder. Then scan all these
* folder pairs for the second folder.
*/
*ppfp = NULL;
if (SearchSortedArray(GetBriefcaseFolderPairPtrArray(pcinft->hbr),
&FolderPairSearchCmp, pcinft->hpathFirst, &aiFirst))
{
ARRAYINDEX aicPtrs;
HPTRARRAY hpaFolderPairs;
LONG ai;
PFOLDERPAIR pfp;
/*
* aiFirst holds the index of the first folder pair that
* contains the first folder name.
*/
/*
* Now search each of these folder pairs for all paired folders
* using the second folder name.
*/
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pcinft->hbr);
aicPtrs = GetPtrCount(hpaFolderPairs);
ASSERT(aicPtrs > 0);
ASSERT(! (aicPtrs % 2));
ASSERT(aiFirst >= 0);
ASSERT(aiFirst < aicPtrs);
for (ai = aiFirst; ai < aicPtrs; ai++)
{
pfp = GetPtr(hpaFolderPairs, ai);
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
/* Does this folder pair match the proposed folder pair? */
if (ComparePaths(pfp->hpath, pcinft->hpathFirst) == CR_EQUAL)
{
/*
* An existing pair of folder twins is considered the same as a
* proposed new pair of folder twins when the two pairs of folder
* twins share the same:
* 1) pair of PATHs
* 2) name specification
* 3) file attributes
* 4) subtree flag setting
*/
if (ComparePaths(pfp->pfpOther->hpath, pcinft->hpathSecond) == CR_EQUAL &&
CompareNameStringsByHandle(pfp->pfpd->hsName, pcinft->hsName) == CR_EQUAL &&
pfp->pfpd->dwAttributes == pcinft->dwAttributes &&
((IS_FLAG_SET(pfp->stub.dwFlags, STUB_FL_SUBTREE) &&
IS_FLAG_SET(pcinft->dwFlags, NFT_FL_SUBTREE)) ||
(IS_FLAG_CLEAR(pfp->stub.dwFlags, STUB_FL_SUBTREE) &&
IS_FLAG_CLEAR(pcinft->dwFlags, NFT_FL_SUBTREE))))
{
/* Yes. */
*ppfp = pfp;
break;
}
}
else
break;
}
}
return(*ppfp != NULL);
}
/*
** CreateFolderPair()
**
** Creates a new folder pair, and adds them to a briefcase's list of folder
** pairs.
**
** Arguments: pcinft - pointer to INEWFOLDERTWIN describing folder pair to
** create
** ppfp - pointer to PFOLDERPAIR to be filled in with pointer to
** half of new folder pair representing
** pcnft->pcszFolder1
**
** Returns:
**
** Side Effects: Adds the new folder pair to the global array of folder pairs.
**
** N.b., this function does not first check to see if the folder pair already
** exists in the global list of folder pairs.
*/
PRIVATE_CODE BOOL CreateFolderPair(PCINEWFOLDERTWIN pcinft, PFOLDERPAIR *ppfp)
{
BOOL bResult = FALSE;
PFOLDERPAIRDATA pfpd;
ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN));
ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
/* Try to create the shared folder data structure. */
if (CreateSharedFolderPairData(pcinft, &pfpd))
{
PFOLDERPAIR pfpNew1;
BOOL bPtr1Loose = TRUE;
if (CreateHalfOfFolderPair(pcinft->hpathFirst, pcinft->hbr, &pfpNew1))
{
PFOLDERPAIR pfpNew2;
if (CreateHalfOfFolderPair(pcinft->hpathSecond, pcinft->hbr,
&pfpNew2))
{
HPTRARRAY hpaFolderPairs;
ARRAYINDEX ai1;
/* Combine the two folder pair halves. */
pfpNew1->pfpd = pfpd;
pfpNew1->pfpOther = pfpNew2;
pfpNew2->pfpd = pfpd;
pfpNew2->pfpOther = pfpNew1;
/* Set flags. */
if (IS_FLAG_SET(pcinft->dwFlags, NFT_FL_SUBTREE))
{
SetStubFlag(&(pfpNew1->stub), STUB_FL_SUBTREE);
SetStubFlag(&(pfpNew2->stub), STUB_FL_SUBTREE);
}
/*
* Try to add the two folder pairs to the global list of folder
* pairs.
*/
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pcinft->hbr);
if (AddPtr(hpaFolderPairs, FolderPairSortCmp, pfpNew1, &ai1))
{
ARRAYINDEX ai2;
bPtr1Loose = FALSE;
if (AddPtr(hpaFolderPairs, FolderPairSortCmp, pfpNew2, &ai2))
{
ASSERT(IS_VALID_STRUCT_PTR(pfpNew1, CFOLDERPAIR));
ASSERT(IS_VALID_STRUCT_PTR(pfpNew2, CFOLDERPAIR));
if (ApplyNewFolderTwinsToTwinFamilies(pfpNew1))
{
*ppfp = pfpNew1;
bResult = TRUE;
}
else
{
DeletePtr(hpaFolderPairs, ai2);
CREATEFOLDERPAIR_BAIL1:
DeletePtr(hpaFolderPairs, ai1);
CREATEFOLDERPAIR_BAIL2:
/*
* Don't try to remove pfpNew2 from the global list of
* folder pairs here since it was never added
* successfully.
*/
DestroyHalfOfFolderPair(pfpNew2);
CREATEFOLDERPAIR_BAIL3:
/*
* Don't try to remove pfpNew1 from the global list of
* folder pairs here since it was never added
* successfully.
*/
DestroyHalfOfFolderPair(pfpNew1);
CREATEFOLDERPAIR_BAIL4:
DestroySharedFolderPairData(pfpd);
}
}
else
goto CREATEFOLDERPAIR_BAIL1;
}
else
goto CREATEFOLDERPAIR_BAIL2;
}
else
goto CREATEFOLDERPAIR_BAIL3;
}
else
goto CREATEFOLDERPAIR_BAIL4;
}
return(bResult);
}
/*
** CreateHalfOfFolderPair()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL CreateHalfOfFolderPair(HPATH hpathFolder, HBRFCASE hbr,
PFOLDERPAIR *ppfp)
{
BOOL bResult = FALSE;
PFOLDERPAIR pfpNew;
ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
/* Try to create a new FOLDERPAIR structure. */
if (AllocateMemory(sizeof(*pfpNew), &pfpNew))
{
/* Try to add the folder string to the folder string table. */
if (CopyPath(hpathFolder, GetBriefcasePathList(hbr), &(pfpNew->hpath)))
{
/* Fill in the fields of the new FOLDERPAIR structure. */
InitStub(&(pfpNew->stub), ST_FOLDERPAIR);
*ppfp = pfpNew;
bResult = TRUE;
}
else
FreeMemory(pfpNew);
}
return(bResult);
}
/*
** DestroyHalfOfFolderPair()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE void DestroyHalfOfFolderPair(PFOLDERPAIR pfp)
{
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
TRACE_OUT((TEXT("DestroyHalfOfFolderPair(): Destroying folder twin %s."),
DebugGetPathString(pfp->hpath)));
/* Has the other half of this folder pair already been destroyed? */
if (IsStubFlagClear(&(pfp->stub), STUB_FL_BEING_DELETED))
/* No. Indicate that this half has already been deleted. */
SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_BEING_DELETED);
/* Destroy FOLDERPAIR fields. */
DeletePath(pfp->hpath);
FreeMemory(pfp);
return;
}
/*
** CreateSharedFolderPairData()
**
** Creates a shared folder pair data structure.
**
** Arguments: pcinft - pointer to INEWFOLDERTWIN describing folder pair
** being created
**
** Returns: Pointer to new folder pair data structure if successful.
** NULL if unsuccessful.
**
** Side Effects: none
*/
PRIVATE_CODE BOOL CreateSharedFolderPairData(PCINEWFOLDERTWIN pcinft,
PFOLDERPAIRDATA *ppfpd)
{
PFOLDERPAIRDATA pfpd;
ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN));
ASSERT(IS_VALID_WRITE_PTR(ppfpd, PFOLDERPAIRDATA));
/* Try to allocate a new shared folder pair data data structure. */
*ppfpd = NULL;
if (AllocateMemory(sizeof(*pfpd), &pfpd))
{
/* Fill in the FOLDERPAIRDATA structure fields. */
LockString(pcinft->hsName);
pfpd->hsName = pcinft->hsName;
pfpd->dwAttributes = pcinft->dwAttributes;
pfpd->hbr = pcinft->hbr;
ASSERT(! IS_ATTR_DIR(pfpd->dwAttributes));
CLEAR_FLAG(pfpd->dwAttributes, FILE_ATTRIBUTE_DIRECTORY);
*ppfpd = pfpd;
ASSERT(IS_VALID_STRUCT_PTR(*ppfpd, CFOLDERPAIRDATA));
}
return(*ppfpd != NULL);
}
/*
** DestroySharedFolderPairData()
**
** Destroys shared folder pair data.
**
** Arguments: pfpd - pointer to shared folder pair data to destroy
**
** Returns: void
**
** Side Effects: none
*/
PRIVATE_CODE void DestroySharedFolderPairData(PFOLDERPAIRDATA pfpd)
{
ASSERT(IS_VALID_STRUCT_PTR(pfpd, CFOLDERPAIRDATA));
/* Destroy FOLDERPAIRDATA fields. */
DeleteString(pfpd->hsName);
FreeMemory(pfpd);
return;
}
/*
** FolderPairSortCmp()
**
** Pointer comparison function used to sort the global array of folder pairs.
**
** Arguments: pcfp1 - pointer to FOLDERPAIR describing first folder pair
** pcfp2 - pointer to FOLDERPAIR describing second folder pair
**
** Returns:
**
** Side Effects: none
**
** Folder pairs are sorted by:
** 1) path
** 2) pointer value
*/
PRIVATE_CODE COMPARISONRESULT FolderPairSortCmp(PCVOID pcfp1, PCVOID pcfp2)
{
COMPARISONRESULT cr;
ASSERT(IS_VALID_STRUCT_PTR(pcfp1, CFOLDERPAIR));
ASSERT(IS_VALID_STRUCT_PTR(pcfp2, CFOLDERPAIR));
cr = ComparePaths(((PCFOLDERPAIR)pcfp1)->hpath,
((PCFOLDERPAIR)pcfp2)->hpath);
if (cr == CR_EQUAL)
cr = ComparePointers(pcfp1, pcfp2);
return(cr);
}
/*
** FolderPairSearchCmp()
**
** Pointer comparison function used to search the global array of folder pairs
** for the first folder pair for a given folder.
**
** Arguments: hpath - folder pair to search for
** pcfp - pointer to FOLDERPAIR to examine
**
** Returns:
**
** Side Effects: none
**
** Folder pairs are searched by:
** 1) path
*/
PRIVATE_CODE COMPARISONRESULT FolderPairSearchCmp(PCVOID hpath, PCVOID pcfp)
{
ASSERT(IS_VALID_HANDLE((HPATH)hpath, PATH));
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
return(ComparePaths((HPATH)hpath, ((PCFOLDERPAIR)pcfp)->hpath));
}
/*
** RemoveSourceFolderTwin()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */
PRIVATE_CODE BOOL RemoveSourceFolderTwin(POBJECTTWIN pot, PVOID pv)
{
ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
ASSERT(! pv);
if (EVAL(pot->ulcSrcFolderTwins > 0))
pot->ulcSrcFolderTwins--;
/*
* If there are no more source folder twins for this object twin, and this
* object twin is not a separate "orphan" object twin, wipe it out.
*/
if (! pot->ulcSrcFolderTwins &&
IsStubFlagClear(&(pot->stub), STUB_FL_FROM_OBJECT_TWIN))
EVAL(DestroyStub(&(pot->stub)) == TR_SUCCESS);
return(TRUE);
}
#pragma warning(default:4100) /* "unreferenced formal parameter" warning */
/*
** UnlinkHalfOfFolderPair()
**
** Unlinks one half of a pair of folder twins.
**
** Arguments: pfp - pointer to folder pair half to unlink
**
** Returns: void
**
** Side Effects: Removes a source folder twin from each of the object twin's
** in the folder pair's list of generated object twins. May
** cause object twins and twin families to be destroyed.
*/
PRIVATE_CODE void UnlinkHalfOfFolderPair(PFOLDERPAIR pfp)
{
HPTRARRAY hpaFolderPairs;
ARRAYINDEX aiUnlink;
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
TRACE_OUT((TEXT("UnlinkHalfOfFolderPair(): Unlinking folder twin %s."),
DebugGetPathString(pfp->hpath)));
/* Search for the folder pair to be unlinked. */
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pfp->pfpd->hbr);
if (EVAL(SearchSortedArray(hpaFolderPairs, &FolderPairSortCmp, pfp,
&aiUnlink)))
{
/* Unlink folder pair. */
ASSERT(GetPtr(hpaFolderPairs, aiUnlink) == pfp);
DeletePtr(hpaFolderPairs, aiUnlink);
/*
* Don't mark folder pair stub unlinked here. Let caller do that after
* both folder pair halves have been unlinked.
*/
/* Remove a source folder twin from all generated object twins. */
EVAL(EnumGeneratedObjectTwins(pfp, &RemoveSourceFolderTwin, NULL));
}
return;
}
/*
** FolderTwinIntersectsFolder()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL FolderTwinIntersectsFolder(PCFOLDERPAIR pcfp, HPATH hpathFolder)
{
BOOL bResult;
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
if (IsStubFlagSet(&(pcfp->stub), STUB_FL_SUBTREE))
bResult = IsPathPrefix(hpathFolder, pcfp->hpath);
else
bResult = (ComparePaths(hpathFolder, pcfp->hpath) == CR_EQUAL);
return(bResult);
}
/*
** CreateListOfFolderTwins()
**
** Creates a list of folder twins from a block of folder pairs.
**
** Arguments: aiFirst - index of first folder pair in the array of folder
** pairs
** hpathFolder - folder that list of folder twins is to be
** created for
** ppftHead - pointer to PFOLDERTWIN to be filled in with
** pointer to first folder twin in new list
** paic - pointer to ARRAYINDEX to be filled in with number of
** folder twins in new list
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PRIVATE_CODE TWINRESULT CreateListOfFolderTwins(HBRFCASE hbr, ARRAYINDEX aiFirst,
HPATH hpathFolder,
PFOLDERTWIN *ppftHead,
PARRAYINDEX paic)
{
TWINRESULT tr;
PFOLDERPAIR pfp;
HPATH hpath;
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
PFOLDERTWIN pftHead;
HPTRARRAY hpaFolderTwins;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
ASSERT(IS_VALID_WRITE_PTR(ppftHead, PFOLDERTWIN));
ASSERT(IS_VALID_WRITE_PTR(paic, ARRAYINDEX));
/*
* Get the handle to the common folder that the list of folder twins is
* being prepared for.
*/
hpaFolderTwins = GetBriefcaseFolderPairPtrArray(hbr);
pfp = GetPtr(hpaFolderTwins, aiFirst);
hpath = pfp->hpath;
/*
* Add the other half of each matching folder pair to the folder twin list
* as a folder twin.
*/
aicPtrs = GetPtrCount(hpaFolderTwins);
ASSERT(aicPtrs > 0);
ASSERT(! (aicPtrs % 2));
ASSERT(aiFirst >= 0);
ASSERT(aiFirst < aicPtrs);
/* Start with an empty list of folder twins. */
pftHead = NULL;
/*
* A pointer to the first folder pair is already in pfp, but we'll look it
* up again.
*/
TRACE_OUT((TEXT("CreateListOfFolderTwins(): Creating list of folder twins of folder %s."),
DebugGetPathString(hpath)));
tr = TR_SUCCESS;
for (ai = aiFirst; ai < aicPtrs && tr == TR_SUCCESS; ai++)
{
pfp = GetPtr(hpaFolderTwins, ai);
if (ComparePaths(pfp->hpath, hpathFolder) == CR_EQUAL)
tr = AddFolderTwinToList(pfp, pftHead, &pftHead);
else
break;
}
TRACE_OUT((TEXT("CreateListOfFolderTwins(): Finished creating list of folder twins of folder %s."),
DebugGetPathString(hpath)));
if (tr == TR_SUCCESS)
{
/* Success! Fill in the result parameters. */
*ppftHead = pftHead;
*paic = ai - aiFirst;
}
else
/* Free any folder twins that have been added to the list. */
DestroyListOfFolderTwins(pftHead);
return(tr);
}
/*
** DestroyListOfFolderTwins()
**
** Wipes out the folder twins in a folder twin list.
**
** Arguments: pftHead - pointer to first folder twin in list
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PRIVATE_CODE void DestroyListOfFolderTwins(PFOLDERTWIN pftHead)
{
while (pftHead)
{
PFOLDERTWIN pftOldHead;
ASSERT(IS_VALID_STRUCT_PTR(pftHead, CFOLDERTWIN));
UnlockStub(&(((PFOLDERPAIR)(pftHead->hftSrc))->stub));
UnlockStub(&(((PFOLDERPAIR)(pftHead->hftOther))->stub));
pftOldHead = pftHead;
pftHead = (PFOLDERTWIN)(pftHead->pcftNext);
FreeMemory((LPTSTR)(pftOldHead->pcszSrcFolder));
FreeMemory((LPTSTR)(pftOldHead->pcszOtherFolder));
FreeMemory(pftOldHead);
}
return;
}
/*
** AddFolderTwinToList()
**
** Adds a folder twin to a list of folder twins.
**
** Arguments: pfpSrc - pointer to source folder pair to be added
** pftHead - pointer to head of folder twin list, may be NULL
** ppft - pointer to PFOLDERTWIN to be filled in with pointer
** to new folder twin, ppft may be &pftHead
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PRIVATE_CODE TWINRESULT AddFolderTwinToList(PFOLDERPAIR pfpSrc,
PFOLDERTWIN pftHead,
PFOLDERTWIN *ppft)
{
TWINRESULT tr = TR_OUT_OF_MEMORY;
PFOLDERTWIN pftNew;
ASSERT(IS_VALID_STRUCT_PTR(pfpSrc, CFOLDERPAIR));
ASSERT(! pftHead || IS_VALID_STRUCT_PTR(pftHead, CFOLDERTWIN));
ASSERT(IS_VALID_WRITE_PTR(ppft, PFOLDERTWIN));
/* Try to create a new FOLDERTWIN structure. */
if (AllocateMemory(sizeof(*pftNew), &pftNew))
{
LPTSTR pszFirstFolder;
if (AllocatePathString(pfpSrc->hpath, &pszFirstFolder))
{
LPTSTR pszSecondFolder;
if (AllocatePathString(pfpSrc->pfpOther->hpath, &pszSecondFolder))
{
/* Fill in FOLDERTWIN structure fields. */
pftNew->pcftNext = pftHead;
pftNew->hftSrc = (HFOLDERTWIN)pfpSrc;
pftNew->hvidSrc = (HVOLUMEID)(pfpSrc->hpath);
pftNew->pcszSrcFolder = pszFirstFolder;
pftNew->hftOther = (HFOLDERTWIN)(pfpSrc->pfpOther);
pftNew->hvidOther = (HVOLUMEID)(pfpSrc->pfpOther->hpath);
pftNew->pcszOtherFolder = pszSecondFolder;
pftNew->pcszName = GetString(pfpSrc->pfpd->hsName);
pftNew->dwFlags = 0;
if (IsStubFlagSet(&(pfpSrc->stub), STUB_FL_SUBTREE))
pftNew->dwFlags = FT_FL_SUBTREE;
LockStub(&(pfpSrc->stub));
LockStub(&(pfpSrc->pfpOther->stub));
*ppft = pftNew;
tr = TR_SUCCESS;
TRACE_OUT((TEXT("AddFolderTwinToList(): Added folder twin %s of folder %s matching objects %s."),
pftNew->pcszSrcFolder,
pftNew->pcszOtherFolder,
pftNew->pcszName));
}
else
{
FreeMemory(pszFirstFolder);
ADDFOLDERTWINTOLIST_BAIL:
FreeMemory(pftNew);
}
}
else
goto ADDFOLDERTWINTOLIST_BAIL;
}
ASSERT(tr != TR_SUCCESS ||
IS_VALID_STRUCT_PTR(*ppft, CFOLDERTWIN));
return(tr);
}
/*
** TransplantFolderPair()
**
**
**
** Arguments:
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PRIVATE_CODE TWINRESULT TransplantFolderPair(PFOLDERPAIR pfp,
HPATH hpathOldFolder,
HPATH hpathNewFolder)
{
TWINRESULT tr;
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
ASSERT(IS_VALID_HANDLE(hpathOldFolder, PATH));
ASSERT(IS_VALID_HANDLE(hpathNewFolder, PATH));
/* Is this folder pair rooted in the renamed folder's subtree? */
if (IsPathPrefix(pfp->hpath, hpathOldFolder))
{
TCHAR rgchPathSuffix[MAX_PATH_LEN];
LPCTSTR pcszSubPath;
HPATH hpathNew;
/* Yes. Change the folder pair's root. */
pcszSubPath = FindChildPathSuffix(hpathOldFolder, pfp->hpath,
rgchPathSuffix);
if (AddChildPath(GetBriefcasePathList(pfp->pfpd->hbr), hpathNewFolder,
pcszSubPath, &hpathNew))
{
if (IsStubFlagSet(&(pfp->stub), STUB_FL_SUBTREE))
{
ASSERT(IsStubFlagSet(&(pfp->pfpOther->stub), STUB_FL_SUBTREE));
BeginTranslateFolder(pfp);
tr = CheckForSubtreeCycles(
GetBriefcaseFolderPairPtrArray(pfp->pfpd->hbr), hpathNew,
pfp->pfpOther->hpath, pfp->pfpd->hsName);
EndTranslateFolder(pfp);
}
else
tr = TR_SUCCESS;
if (tr == TR_SUCCESS)
{
TRACE_OUT((TEXT("TransplantFolderPair(): Transplanted folder twin %s to %s."),
DebugGetPathString(pfp->hpath),
DebugGetPathString(hpathNew)));
DeletePath(pfp->hpath);
pfp->hpath = hpathNew;
}
else
DeletePath(hpathNew);
}
else
tr = TR_OUT_OF_MEMORY;
}
else
tr = TR_SUCCESS;
return(tr);
}
/*
** WriteFolderPair()
**
**
**
** Arguments:
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PRIVATE_CODE TWINRESULT WriteFolderPair(HCACHEDFILE hcf, PFOLDERPAIR pfp)
{
TWINRESULT tr;
DBFOLDERTWIN dbft;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
/* Set up folder pair database structure. */
dbft.dwStubFlags = (pfp->stub.dwFlags & DB_STUB_FLAGS_MASK);
dbft.hpath1 = pfp->hpath;
dbft.hpath2 = pfp->pfpOther->hpath;
dbft.hsName = pfp->pfpd->hsName;
dbft.dwAttributes = pfp->pfpd->dwAttributes;
/* Save folder pair database structure. */
if (WriteToCachedFile(hcf, (PCVOID)&dbft, sizeof(dbft), NULL))
tr = TR_SUCCESS;
else
tr = TR_BRIEFCASE_WRITE_FAILED;
return(tr);
}
/*
** ReadFolderPair()
**
**
**
** Arguments:
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PRIVATE_CODE TWINRESULT ReadFolderPair(HCACHEDFILE hcf, HBRFCASE hbr,
HHANDLETRANS hhtFolderTrans,
HHANDLETRANS hhtNameTrans)
{
TWINRESULT tr = TR_CORRUPT_BRIEFCASE;
DBFOLDERTWIN dbft;
DWORD dwcbRead;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS));
ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
if (ReadFromCachedFile(hcf, &dbft, sizeof(dbft), &dwcbRead) &&
dwcbRead == sizeof(dbft))
{
INEWFOLDERTWIN inft;
if (TranslateHandle(hhtFolderTrans, (HGENERIC)(dbft.hpath1), (PHGENERIC)&(inft.hpathFirst)))
{
if (TranslateHandle(hhtFolderTrans, (HGENERIC)(dbft.hpath2), (PHGENERIC)&(inft.hpathSecond)))
{
if (TranslateHandle(hhtNameTrans, (HGENERIC)(dbft.hsName), (PHGENERIC)&(inft.hsName)))
{
PFOLDERPAIR pfp;
inft.dwAttributes = dbft.dwAttributes;
inft.hbr = hbr;
if (IS_FLAG_SET(dbft.dwStubFlags, STUB_FL_SUBTREE))
inft.dwFlags = NFT_FL_SUBTREE;
else
inft.dwFlags = 0;
if (CreateFolderPair(&inft, &pfp))
tr = TR_SUCCESS;
else
tr = TR_OUT_OF_MEMORY;
}
}
}
}
return(tr);
}
#ifdef VSTF
/*
** IsValidPCNEWFOLDERTWIN()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL IsValidPCNEWFOLDERTWIN(PCNEWFOLDERTWIN pcnft)
{
return(IS_VALID_READ_PTR(pcnft, CNEWFOLDERTWIN) &&
EVAL(pcnft->ulSize == sizeof(*pcnft)) &&
IS_VALID_STRING_PTR(pcnft->pcszFolder1, CSTR) &&
IS_VALID_STRING_PTR(pcnft->pcszFolder2, CSTR) &&
IS_VALID_STRING_PTR(pcnft->pcszName, CSTR) &&
FLAGS_ARE_VALID(pcnft->dwAttributes, ALL_FILE_ATTRIBUTES) &&
FLAGS_ARE_VALID(pcnft->dwFlags, ALL_NFT_FLAGS));
}
/*
** IsValidPCFOLDERTWINLIST()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL IsValidPCFOLDERTWINLIST(PCFOLDERTWINLIST pcftl)
{
BOOL bResult = FALSE;
if (IS_VALID_READ_PTR(pcftl, CFOLDERTWINLIST) &&
IS_VALID_HANDLE(pcftl->hbr, BRFCASE))
{
PCFOLDERTWIN pcft;
ULONG ulcFolderTwins = 0;
for (pcft = pcftl->pcftFirst;
pcft && IS_VALID_STRUCT_PTR(pcft, CFOLDERTWIN);
pcft = pcft->pcftNext)
{
ASSERT(ulcFolderTwins < ULONG_MAX);
ulcFolderTwins++;
}
if (! pcft && EVAL(ulcFolderTwins == pcftl->ulcItems))
bResult = TRUE;
}
return(bResult);
}
/*
** IsValidPCFOLDERTWIN()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL IsValidPCFOLDERTWIN(PCFOLDERTWIN pcft)
{
/* dwUser may be any value. */
return(IS_VALID_READ_PTR(pcft, CFOLDERTWIN) &&
IS_VALID_HANDLE(pcft->hftSrc, FOLDERTWIN) &&
IS_VALID_HANDLE(pcft->hvidSrc, VOLUMEID) &&
IS_VALID_STRING_PTR(pcft->pcszSrcFolder, CSTR) &&
IS_VALID_HANDLE(pcft->hftOther, FOLDERTWIN) &&
IS_VALID_HANDLE(pcft->hvidOther, VOLUMEID) &&
IS_VALID_STRING_PTR(pcft->pcszOtherFolder, CSTR) &&
IS_VALID_STRING_PTR(pcft->pcszName, CSTR) &&
FLAGS_ARE_VALID(pcft->dwFlags, ALL_FT_FLAGS));
}
/*
** IsValidFolderPairHalf()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL IsValidFolderPairHalf(PCFOLDERPAIR pcfp)
{
return(IS_VALID_READ_PTR(pcfp, CFOLDERPAIR) &&
IS_VALID_STRUCT_PTR(&(pcfp->stub), CSTUB) &&
FLAGS_ARE_VALID(GetStubFlags(&(pcfp->stub)), ALL_FOLDER_TWIN_FLAGS) &&
IS_VALID_HANDLE(pcfp->hpath, PATH) &&
IS_VALID_STRUCT_PTR(pcfp->pfpd, CFOLDERPAIRDATA) &&
(IsStubFlagSet(&(pcfp->stub), STUB_FL_BEING_DELETED) ||
IS_VALID_READ_PTR(pcfp->pfpOther, CFOLDERPAIR)));
}
/*
** IsValidPCFOLDERPAIRDATA()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL IsValidPCFOLDERPAIRDATA(PCFOLDERPAIRDATA pcfpd)
{
/* Don't validate hbr. */
return(IS_VALID_READ_PTR(pcfpd, CFOLDERPAIRDATA) &&
IS_VALID_HANDLE(pcfpd->hsName, STRING));
}
#endif
#ifdef DEBUG
/*
** IsValidPCINEWFOLDERTWIN()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL IsValidPCINEWFOLDERTWIN(PCINEWFOLDERTWIN pcinft)
{
return(IS_VALID_READ_PTR(pcinft, CINEWFOLDERTWIN) &&
IS_VALID_HANDLE(pcinft->hpathFirst, PATH) &&
IS_VALID_HANDLE(pcinft->hpathSecond, PATH) &&
IS_VALID_HANDLE(pcinft->hsName, STRING) &&
FLAGS_ARE_VALID(pcinft->dwAttributes, ALL_FILE_ATTRIBUTES) &&
FLAGS_ARE_VALID(pcinft->dwFlags, ALL_NFT_FLAGS) &&
IS_VALID_HANDLE(pcinft->hbr, BRFCASE));
}
/*
** AreFolderPairsValid()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL AreFolderPairsValid(HPTRARRAY hpaFolderPairs)
{
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hpaFolderPairs, PTRARRAY));
aicPtrs = GetPtrCount(hpaFolderPairs);
ASSERT(! (aicPtrs % 2));
for (ai = 0;
ai < aicPtrs && IS_VALID_STRUCT_PTR(GetPtr(hpaFolderPairs, ai), CFOLDERPAIR);
ai++)
;
return(ai == aicPtrs);
}
#endif
/****************************** Public Functions *****************************/
/*
** CreateFolderPairPtrArray()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL CreateFolderPairPtrArray(PHPTRARRAY phpa)
{
NEWPTRARRAY npa;
ASSERT(IS_VALID_WRITE_PTR(phpa, HPTRARRAY));
/* Try to create a folder pair pointer array. */
npa.aicInitialPtrs = NUM_START_FOLDER_TWIN_PTRS;
npa.aicAllocGranularity = NUM_FOLDER_TWIN_PTRS_TO_ADD;
npa.dwFlags = NPA_FL_SORTED_ADD;
return(CreatePtrArray(&npa, phpa));
}
/*
** DestroyFolderPairPtrArray()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void DestroyFolderPairPtrArray(HPTRARRAY hpa)
{
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
/* Free all folder pairs in pointer array. */
aicPtrs = GetPtrCount(hpa);
ASSERT(! (aicPtrs % 2));
for (ai = 0; ai < aicPtrs; ai++)
{
PFOLDERPAIR pfp;
PFOLDERPAIR pfpOther;
PFOLDERPAIRDATA pfpd;
BOOL bDeleteFolderPairData;
pfp = GetPtr(hpa, ai);
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
/* Copy fields needed after folder pair half's demise. */
pfpOther = pfp->pfpOther;
pfpd = pfp->pfpd;
bDeleteFolderPairData = IsStubFlagSet(&(pfp->stub), STUB_FL_BEING_DELETED);
DestroyHalfOfFolderPair(pfp);
/* Has the other half of this folder pair already been destroyed? */
if (bDeleteFolderPairData)
/* Yes. Destroy the pair's shared data. */
DestroySharedFolderPairData(pfpd);
}
/* Now wipe out the pointer array. */
DestroyPtrArray(hpa);
return;
}
/*
** LockFolderPair()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void LockFolderPair(PFOLDERPAIR pfp)
{
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_UNLINKED));
ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_UNLINKED));
ASSERT(pfp->stub.ulcLock < ULONG_MAX);
pfp->stub.ulcLock++;
ASSERT(pfp->pfpOther->stub.ulcLock < ULONG_MAX);
pfp->pfpOther->stub.ulcLock++;
return;
}
/*
** UnlockFolderPair()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void UnlockFolderPair(PFOLDERPAIR pfp)
{
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
if (EVAL(pfp->stub.ulcLock > 0))
pfp->stub.ulcLock--;
if (EVAL(pfp->pfpOther->stub.ulcLock > 0))
pfp->pfpOther->stub.ulcLock--;
if (! pfp->stub.ulcLock &&
IsStubFlagSet(&(pfp->stub), STUB_FL_UNLINKED))
{
ASSERT(! pfp->pfpOther->stub.ulcLock);
ASSERT(IsStubFlagSet(&(pfp->pfpOther->stub), STUB_FL_UNLINKED));
DestroyFolderPair(pfp);
}
return;
}
/*
** UnlinkFolderPair()
**
** Unlinks a folder pair.
**
** Arguments: pfp - pointer to folder pair to be unlinked
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PUBLIC_CODE TWINRESULT UnlinkFolderPair(PFOLDERPAIR pfp)
{
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_UNLINKED));
ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_UNLINKED));
/* Unlink both halves of the folder pair. */
UnlinkHalfOfFolderPair(pfp);
UnlinkHalfOfFolderPair(pfp->pfpOther);
SetStubFlag(&(pfp->stub), STUB_FL_UNLINKED);
SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_UNLINKED);
return(TR_SUCCESS);
}
/*
** DestroyFolderPair()
**
** Destroys a folder pair.
**
** Arguments: pfp - pointer to folder pair to destroy
**
** Returns: void
**
** Side Effects: none
*/
PUBLIC_CODE void DestroyFolderPair(PFOLDERPAIR pfp)
{
PFOLDERPAIRDATA pfpd;
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
/* Destroy both FOLDERPAIR halves, and shared data. */
pfpd = pfp->pfpd;
DestroyHalfOfFolderPair(pfp->pfpOther);
DestroyHalfOfFolderPair(pfp);
DestroySharedFolderPairData(pfpd);
return;
}
/*
** MyTranslateFolder()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE TWINRESULT MyTranslateFolder(HBRFCASE hbr, HPATH hpathOld,
HPATH hpathNew)
{
TWINRESULT tr = TR_SUCCESS;
HPTRARRAY hpaFolderPairs;
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
ASSERT(IS_VALID_HANDLE(hpathOld, PATH));
ASSERT(IS_VALID_HANDLE(hpathNew, PATH));
/*
* Change folders of all folder pairs rooted in pcszOldFolder's subtree to
* being rooted in pcszNewFolder's subtree.
*/
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(hbr);
aicPtrs = GetPtrCount(hpaFolderPairs);
ASSERT(! (aicPtrs % 2));
for (ai = 0; ai < aicPtrs; ai++)
{
tr = TransplantFolderPair(GetPtr(hpaFolderPairs, ai), hpathOld,
hpathNew);
if (tr != TR_SUCCESS)
break;
}
if (tr == TR_SUCCESS)
{
HPTRARRAY hpaTwinFamilies;
/* Restore folder pair array to sorted order. */
SortPtrArray(hpaFolderPairs, &FolderPairSortCmp);
TRACE_OUT((TEXT("MyTranslateFolder(): Sorted folder pair array after folder translation.")));
/*
* Change folders of all object twins in pcszOldFolder's old subtree to
* being in pcszNewFolder's subtree.
*/
hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(hbr);
aicPtrs = GetPtrCount(hpaTwinFamilies);
for (ai = 0; ai < aicPtrs; ai++)
{
PTWINFAMILY ptf;
BOOL bContinue;
HNODE hnode;
ptf = GetPtr(hpaTwinFamilies, ai);
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
/*
* Walk each twin family's list of object twins looking for object
* twins in the translated folder's subtree.
*/
for (bContinue = GetFirstNode(ptf->hlistObjectTwins, &hnode);
bContinue;
bContinue = GetNextNode(hnode, &hnode))
{
POBJECTTWIN pot;
pot = (POBJECTTWIN)GetNodeData(hnode);
tr = TransplantObjectTwin(pot, hpathOld, hpathNew);
if (tr != TR_SUCCESS)
break;
}
if (tr != TR_SUCCESS)
break;
}
/* Twin family array is still in sorted order. */
}
return(tr);
}
/*
** ApplyNewObjectTwinsToFolderTwins()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: Adds new spin-off object twins to hlistNewObjectTwins as they
** are created.
**
** N.b., new object twins may have been added to hlistNewObjectTwins even if
** FALSE is returned. Clean-up of these new object twins in case of failure is
** the caller's responsibility.
**
** A new object twin may generate more new object twins, implied by existing
** folder twins. E.g., consider the following scenario:
**
** 1) Folder twins (c:\, d:\, *.*) and (d:\, e:\, *.*) exist.
** 2) Files c:\foo, d:\foo, and e:\foo do not exist.
** 3) File e:\foo is created.
** 4) New object twin e:\foo is added.
** 5) d:\foo must be added as a new object twin from the e:\foo object twin
** because of the (d:\, e:\, *.*) folder twin.
** 6) c:\foo must be added as a new object twin from the d:\foo object twin
** because of the (c:\, d:\, *.*) folder twin.
**
** So we see that new object twin e:\foo must generate two more new object
** twins, d:\foo and c:\foo, implied by the two existing folder twins,
** (c:\, d:\, *.*) and (d:\, e:\, *.*).
*/
PUBLIC_CODE BOOL ApplyNewObjectTwinsToFolderTwins(HLIST hlistNewObjectTwins)
{
BOOL bResult = TRUE;
BOOL bContinue;
HNODE hnode;
ASSERT(IS_VALID_HANDLE(hlistNewObjectTwins, LIST));
/*
* Don't use WalkList() here because we want to insert new nodes in
* hlistNewObjectTwins after the current node.
*/
for (bContinue = GetFirstNode(hlistNewObjectTwins, &hnode);
bContinue && bResult;
bContinue = GetNextNode(hnode, &hnode))
{
POBJECTTWIN pot;
HPATHLIST hpl;
HPTRARRAY hpaFolderPairs;
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
pot = GetNodeData(hnode);
ASSERT(! pot->ulcSrcFolderTwins);
TRACE_OUT((TEXT("ApplyNewObjectTwinsToFolderTwins(): Applying new object twin %s\\%s."),
DebugGetPathString(pot->hpath),
GetString(pot->ptfParent->hsName)));
/*
* Assume that hpl, hpaFolderPairs, and aicPtrs don't change during this
* loop. Calculate them outside the loop.
*/
hpl = GetBriefcasePathList(pot->ptfParent->hbr);
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pot->ptfParent->hbr);
aicPtrs = GetPtrCount(hpaFolderPairs);
ASSERT(! (aicPtrs % 2));
for (ai = 0; ai < aicPtrs; ai++)
{
PFOLDERPAIR pfp;
pfp = GetPtr(hpaFolderPairs, ai);
if (FolderTwinGeneratesObjectTwin(pfp, pot->hpath,
GetString(pot->ptfParent->hsName)))
{
HPATH hpathMatchingFolder;
HNODE hnodeUnused;
ASSERT(pot->ulcSrcFolderTwins < ULONG_MAX);
pot->ulcSrcFolderTwins++;
/*
* Append the generated object twin's subpath to the matching
* folder twin's base path for subtree twins.
*/
if (BuildPathForMatchingObjectTwin(pfp, pot, hpl,
&hpathMatchingFolder))
{
/*
* We don't want to collapse any twin families if the matching
* object twin is found in a different twin family. This will
* be done by ApplyNewFolderTwinsToTwinFamilies() for spin-off
* object twins generated by new folder twins.
*
* Spin-off object twins created by new object twins never
* require collapsing twin families. For a spin-off object twin
* generated by a new object twin to collapse twin families,
* there would have to have been separate twin families
* connected by a folder twin. But if those twin families were
* already connected by a folder twin, they would not be
* separate because they would already have been collapsed by
* ApplyNewFolderTwinsToTwinFamilies() when the connecting
* folder twin was added.
*/
if (! FindObjectTwin(pot->ptfParent->hbr, hpathMatchingFolder,
GetString(pot->ptfParent->hsName),
&hnodeUnused))
{
POBJECTTWIN potNew;
/*
* CreateObjectTwin() ASSERT()s that an object twin for
* hpathMatchingFolder is not found, so we don't need to do
* that here.
*/
if (CreateObjectTwin(pot->ptfParent, hpathMatchingFolder,
&potNew))
{
/*
* Add the new object twin to hlistNewObjectTwins after
* the new object twin currently being processed to make
* certain that it gets processed in the outside loop
* through hlistNewObjectTwins.
*/
if (! InsertNodeAfter(hnode, NULL, potNew, &hnodeUnused))
{
DestroyStub(&(potNew->stub));
bResult = FALSE;
break;
}
}
}
DeletePath(hpathMatchingFolder);
}
else
{
bResult = FALSE;
break;
}
}
}
}
return(bResult);
}
/*
** BuildPathForMatchingObjectTwin()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: Path is added to object twin's briefcase's path list.
*/
PUBLIC_CODE BOOL BuildPathForMatchingObjectTwin(PCFOLDERPAIR pcfp,
PCOBJECTTWIN pcot,
HPATHLIST hpl, PHPATH phpath)
{
BOOL bResult;
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN));
ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
ASSERT(IS_VALID_WRITE_PTR(phpath, HPATH));
ASSERT(FolderTwinGeneratesObjectTwin(pcfp, pcot->hpath, GetString(pcot->ptfParent->hsName)));
/* Is the generating folder twin a subtree twin? */
if (IsStubFlagSet(&(pcfp->stub), STUB_FL_SUBTREE))
{
TCHAR rgchPathSuffix[MAX_PATH_LEN];
LPCTSTR pcszSubPath;
/*
* Yes. Append the object twin's subpath to the subtree twin's base
* path.
*/
pcszSubPath = FindChildPathSuffix(pcfp->hpath, pcot->hpath,
rgchPathSuffix);
bResult = AddChildPath(hpl, pcfp->pfpOther->hpath, pcszSubPath, phpath);
}
else
/* No. Just use the matching folder twin's folder. */
bResult = CopyPath(pcfp->pfpOther->hpath, hpl, phpath);
return(bResult);
}
/*
** EnumGeneratedObjectTwins()
**
**
**
** Arguments:
**
** Returns: FALSE if callback aborted. TRUE if not.
**
** Side Effects: none
*/
PUBLIC_CODE BOOL EnumGeneratedObjectTwins(PCFOLDERPAIR pcfp,
ENUMGENERATEDOBJECTTWINSPROC egotp,
PVOID pvRefData)
{
BOOL bResult = TRUE;
HPTRARRAY hpaTwinFamilies;
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
/* pvRefData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
ASSERT(IS_VALID_CODE_PTR(egotp, ENUMGENERATEDOBJECTTWINPROC));
/*
* Walk the array of twin families, looking for twin families whose names
* intersect the given folder twin's name specification.
*/
hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(pcfp->pfpd->hbr);
aicPtrs = GetPtrCount(hpaTwinFamilies);
ai = 0;
while (ai < aicPtrs)
{
PTWINFAMILY ptf;
LPCTSTR pcszName;
ptf = GetPtr(hpaTwinFamilies, ai);
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED));
/*
* Does the twin family's name match the folder twin's name
* specification?
*/
pcszName = GetString(ptf->hsName);
if (IsFolderObjectTwinName(pcszName) ||
NamesIntersect(pcszName, GetString(pcfp->pfpd->hsName)))
{
BOOL bContinue;
HNODE hnodePrev;
/* Yes. Look for a matching folder. */
/* 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 looking for object
* twins in the given folder twin's subtree.
*/
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));
if (FolderTwinIntersectsFolder(pcfp, pot->hpath))
{
/*
* A given object twin should only be generated by one of the
* folder twins in a pair of folder twins.
*/
ASSERT(! FolderTwinGeneratesObjectTwin(pcfp->pfpOther, pot->hpath, GetString(pot->ptfParent->hsName)));
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("EnumGeneratedObjectTwins(): Twin family for object %s unlinked by callback."),
GetString(ptf->hsName)));
}
UnlockStub(&(ptf->stub));
if (! bResult)
break;
}
else
/* No. Skip it. */
ai++;
}
return(bResult);
}
/*
** EnumGeneratingFolderTwins()
**
**
**
** Arguments:
**
** Returns: FALSE if callback aborted. TRUE if not.
**
** Side Effects: none
**
** N.b., if the egftp callback removes a pair of folder twins, it must remove
** the pair from the first folder twin encountered. If it removes the pair of
** folder twins from the second folder twin encountered, a folder twin will be
** skipped.
*/
PUBLIC_CODE BOOL EnumGeneratingFolderTwins(PCOBJECTTWIN pcot,
ENUMGENERATINGFOLDERTWINSPROC egftp,
PVOID pvRefData,
PULONG pulcGeneratingFolderTwins)
{
BOOL bResult = TRUE;
HPTRARRAY hpaFolderPairs;
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
/* pvRefData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN));
ASSERT(IS_VALID_CODE_PTR(egftp, ENUMGENERATINGFOLDERTWINSPROC));
ASSERT(IS_VALID_WRITE_PTR(pulcGeneratingFolderTwins, ULONG));
*pulcGeneratingFolderTwins = 0;
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pcot->ptfParent->hbr);
aicPtrs = GetPtrCount(hpaFolderPairs);
ASSERT(! (aicPtrs % 2));
ai = 0;
while (ai < aicPtrs)
{
PFOLDERPAIR pfp;
pfp = GetPtr(hpaFolderPairs, ai);
if (FolderTwinGeneratesObjectTwin(pfp, pcot->hpath,
GetString(pcot->ptfParent->hsName)))
{
ASSERT(! FolderTwinGeneratesObjectTwin(pfp->pfpOther, pcot->hpath, GetString(pcot->ptfParent->hsName)));
ASSERT(*pulcGeneratingFolderTwins < ULONG_MAX);
(*pulcGeneratingFolderTwins)++;
/*
* Lock the pair of folder twins so they don't get deleted out from
* under us.
*/
LockStub(&(pfp->stub));
bResult = (*egftp)(pfp, pvRefData);
if (IsStubFlagSet(&(pfp->stub), STUB_FL_UNLINKED))
{
WARNING_OUT((TEXT("EnumGeneratingFolderTwins(): Folder twin pair unlinked during callback.")));
aicPtrs -= 2;
ASSERT(! (aicPtrs % 2));
ASSERT(aicPtrs == GetPtrCount(hpaFolderPairs));
}
else
ai++;
UnlockStub(&(pfp->stub));
if (! bResult)
break;
}
else
ai++;
}
return(bResult);
}
/*
** FolderTwinGeneratesObjectTwin()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
**
** A folder twin or subtree twin is said to generate an object twin when the
** following conditions are met:
**
** 1) The folder twin or subtree twin is on the same volume as the object twin.
**
** 2) The name of the object twin (literal) intersects the objects matched by
** the folder twin or subtree twin (literal or wildcard).
**
** 3) The folder twin's folder exactly matches the object twin's folder, or the
** subtree twin's root folder is a path prefix of the object twin's folder.
*/
PUBLIC_CODE BOOL FolderTwinGeneratesObjectTwin(PCFOLDERPAIR pcfp,
HPATH hpathFolder,
LPCTSTR pcszName)
{
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
return(FolderTwinIntersectsFolder(pcfp, hpathFolder) &&
(IsFolderObjectTwinName(pcszName) ||
NamesIntersect(pcszName, GetString(pcfp->pfpd->hsName))));
}
/*
** IsValidHFOLDERTWIN()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL IsValidHFOLDERTWIN(HFOLDERTWIN hft)
{
return(IS_VALID_STRUCT_PTR((PFOLDERPAIR)hft, CFOLDERPAIR));
}
#ifdef VSTF
/*
** IsValidPCFOLDERPAIR()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL IsValidPCFOLDERPAIR(PCFOLDERPAIR pcfp)
{
BOOL bResult = FALSE;
/* All the fields of an unlinked folder pair should be valid. */
if (EVAL(IsValidFolderPairHalf(pcfp)))
{
if (IsStubFlagSet(&(pcfp->stub), STUB_FL_BEING_DELETED))
bResult = TRUE;
else if (EVAL(IsValidFolderPairHalf(pcfp->pfpOther)) &&
EVAL(pcfp->pfpOther->pfpOther == pcfp) &&
EVAL(pcfp->pfpd == pcfp->pfpOther->pfpd) &&
EVAL(pcfp->stub.ulcLock == pcfp->pfpOther->stub.ulcLock))
{
BOOL bUnlinked;
BOOL bOtherUnlinked;
/*
* Neither or both folder pair halves may be unlinked, but not only
* one.
*/
bUnlinked = IsStubFlagSet(&(pcfp->stub), STUB_FL_UNLINKED);
bOtherUnlinked = IsStubFlagSet(&(pcfp->pfpOther->stub), STUB_FL_UNLINKED);
if (EVAL((bUnlinked && bOtherUnlinked) ||
(! bUnlinked && ! bOtherUnlinked)))
bResult = TRUE;
}
}
return(bResult);
}
#endif
/*
** WriteFolderPairList()
**
**
**
** Arguments:
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PUBLIC_CODE TWINRESULT WriteFolderPairList(HCACHEDFILE hcf,
HPTRARRAY hpaFolderPairs)
{
TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
DWORD dwcbDBFolderTwinListHeaderOffset;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
ASSERT(IS_VALID_HANDLE(hpaFolderPairs, PTRARRAY));
/* Save initial file position. */
dwcbDBFolderTwinListHeaderOffset = GetCachedFilePointerPosition(hcf);
if (dwcbDBFolderTwinListHeaderOffset != INVALID_SEEK_POSITION)
{
DBFOLDERTWINLISTHEADER dbftlh;
/* Leave space for folder twin data header. */
ZeroMemory(&dbftlh, sizeof(dbftlh));
if (WriteToCachedFile(hcf, (PCVOID)&dbftlh, sizeof(dbftlh), NULL))
{
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
tr = TR_SUCCESS;
/* Mark all folder pairs unused. */
ClearFlagInArrayOfStubs(hpaFolderPairs, STUB_FL_USED);
aicPtrs = GetPtrCount(hpaFolderPairs);
ASSERT(! (aicPtrs % 2));
/* Write all folder pairs. */
for (ai = 0; ai < aicPtrs; ai++)
{
PFOLDERPAIR pfp;
pfp = GetPtr(hpaFolderPairs, ai);
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
if (IsStubFlagClear(&(pfp->stub), STUB_FL_USED))
{
ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_USED));
tr = WriteFolderPair(hcf, pfp);
if (tr == TR_SUCCESS)
{
SetStubFlag(&(pfp->stub), STUB_FL_USED);
SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_USED);
}
else
break;
}
}
/* Save folder twin data header. */
if (tr == TR_SUCCESS)
{
ASSERT(! (aicPtrs % 2));
dbftlh.lcFolderPairs = aicPtrs / 2;
tr = WriteDBSegmentHeader(hcf, dwcbDBFolderTwinListHeaderOffset,
&dbftlh, sizeof(dbftlh));
if (tr == TR_SUCCESS)
TRACE_OUT((TEXT("WriteFolderPairList(): Wrote %ld folder pairs."),
dbftlh.lcFolderPairs));
}
}
}
return(tr);
}
/*
** ReadFolderPairList()
**
**
**
** Arguments:
**
** Returns: TWINRESULT
**
** Side Effects: none
*/
PUBLIC_CODE TWINRESULT ReadFolderPairList(HCACHEDFILE hcf, HBRFCASE hbr,
HHANDLETRANS hhtFolderTrans,
HHANDLETRANS hhtNameTrans)
{
TWINRESULT tr;
DBFOLDERTWINLISTHEADER dbftlh;
DWORD dwcbRead;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS));
ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
if (ReadFromCachedFile(hcf, &dbftlh, sizeof(dbftlh), &dwcbRead) &&
dwcbRead == sizeof(dbftlh))
{
LONG l;
tr = TR_SUCCESS;
TRACE_OUT((TEXT("ReadFolderPairList(): Reading %ld folder pairs."),
dbftlh.lcFolderPairs));
for (l = 0; l < dbftlh.lcFolderPairs && tr == TR_SUCCESS; l++)
tr = ReadFolderPair(hcf, hbr, hhtFolderTrans, hhtNameTrans);
ASSERT(tr != TR_SUCCESS || AreFolderPairsValid(GetBriefcaseFolderPairPtrArray(hbr)));
}
else
tr = TR_CORRUPT_BRIEFCASE;
return(tr);
}
/***************************** Exported Functions ****************************/
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | AddFolderTwin | Twins two folders.
@parm HBRFCASE | hbr | A handle to the open briefcase that the new folder twins
are to be added to.
@parm PCNEWFOLDERTWIN | pcnft | A pointer to a CNEWFOLDERTWIN describing the
two folders to be twinned.
@parm PHFOLDERTWIN | phft | A pointer to an HFOLDERTWIN to be filled in with
a handle to the new folder twins. *phft is only valid if TR_SUCCESS or
TR_DUPLICATE_TWIN is returned.
@rdesc If the folder twins were added successfully, TR_SUCCESS is returned, and
*phFolderTwin contains a handle to the new folder twins. Otherwise, the
folder twins were not added successfully, the return value indicates the error
that occurred, and *phFolderTwin is undefined. If one or both of the volumes
specified by the NEWFOLDERTWIN structure is not present, TR_UNAVAILABLE_VOLUME
will be returned, and the folder twin will not be added.
@comm Once the caller is finshed with the twin handle returned by
AddFolderTwin(), ReleaseTwinHandle() should be called to release the twin
handle. N.b., DeleteTwin() does not release a twin handle returned by
AddFolderTwin().
@xref ReleaseTwinHandle DeleteTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI AddFolderTwin(HBRFCASE hbr, PCNEWFOLDERTWIN pcnft,
PHFOLDERTWIN phft)
{
TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess())
{
DebugEntry(AddFolderTwin);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hbr, BRFCASE) &&
IS_VALID_STRUCT_PTR(pcnft, CNEWFOLDERTWIN) &&
EVAL(pcnft->ulSize == sizeof(*pcnft)) &&
IS_VALID_WRITE_PTR(phft, HFOLDERTWIN))
#endif
{
INEWFOLDERTWIN inft;
InvalidatePathListInfo(GetBriefcasePathList(hbr));
tr = MakeINewFolderTwin(hbr, pcnft, &inft);
if (tr == TR_SUCCESS)
{
PFOLDERPAIR pfp;
ASSERT(! IS_ATTR_DIR(pcnft->dwAttributes));
tr = TwinFolders(&inft, &pfp);
if (tr == TR_SUCCESS ||
tr == TR_DUPLICATE_TWIN)
{
LockStub(&(pfp->stub));
*phft = (HFOLDERTWIN)pfp;
}
ReleaseINewFolderTwin(&inft);
}
}
#ifdef EXPV
else
tr = TR_INVALID_PARAMETER;
#endif
DebugExitTWINRESULT(AddFolderTwin, tr);
EndExclusiveBriefcaseAccess();
}
else
tr = TR_REENTERED;
return(tr);
}
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | IsFolderTwin | Determines whether or not a folder is a
folder twin.
@parm HBRFCASE | hbr | A handle to the open briefcase to check for the folder
twin.
@parm PCSTR | pcszFolder | A pointer to a string indicating the folder in
question.
@parm PBOOL | pbIsFolderTwin | A pointer to a BOOL to be filled in with TRUE
if the folder is a folder twin, or FALSE if not. *pbIsFolderTwin 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.
@xref CreateFolderTwinList DestroyFolderTwinList
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI IsFolderTwin(HBRFCASE hbr, LPCTSTR pcszFolder,
PBOOL pbIsFolderTwin)
{
TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess())
{
DebugEntry(IsFolderTwin);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hbr, BRFCASE) &&
IS_VALID_STRING_PTR(pcszFolder, CSTR) &&
IS_VALID_WRITE_PTR(pbIsFolderTwin, BOOL))
#endif
{
HPATH hpath;
InvalidatePathListInfo(GetBriefcasePathList(hbr));
tr = TranslatePATHRESULTToTWINRESULT(
AddPath(GetBriefcasePathList(hbr), pcszFolder, &hpath));
if (tr == TR_SUCCESS)
{
ARRAYINDEX aiFirst;
/* Search for folder pair referencing given folder. */
*pbIsFolderTwin = SearchSortedArray(
GetBriefcaseFolderPairPtrArray(hbr),
&FolderPairSearchCmp, hpath, &aiFirst);
if (*pbIsFolderTwin)
TRACE_OUT((TEXT("IsFolderTwin(): %s is a folder twin."),
DebugGetPathString(hpath)));
else
TRACE_OUT((TEXT("IsFolderTwin(): %s is not a folder twin."),
DebugGetPathString(hpath)));
DeletePath(hpath);
}
}
#ifdef EXPV
else
tr = TR_INVALID_PARAMETER;
#endif
DebugExitTWINRESULT(IsFolderTwin, tr);
EndExclusiveBriefcaseAccess();
}
else
tr = TR_REENTERED;
return(tr);
}
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | CreateFolderTwinList | Creates a list of the folder twins of
a given folder.
@parm HBRFCASE | hbr | A handle to the open briefcase that the folder twin list
is to be created from.
@parm PCSTR | pcszFolder | A pointer to a string indicating the folder whose
folder twins are to be listed.
@parm PFOLDERTWINLIST | ppftl | A pointer to an PFOLDERTWINLIST to be
filled in with a pointer to the new list of folder twins. *ppFolderTwinList
is only valid if TR_SUCCESS is returned.
@rdesc If the folder twin list was created successfully, TR_SUCCESS is
returned. Otherwise, the folder twin list was not created successfully, and
the return value indicates the error that occurred.
@xref DestroyFolderTwinList IsFolderTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI CreateFolderTwinList(HBRFCASE hbr,
LPCTSTR pcszFolder,
PFOLDERTWINLIST *ppftl)
{
TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess())
{
DebugEntry(CreateFolderTwinList);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hbr, BRFCASE) &&
IS_VALID_STRING_PTR(pcszFolder, CSTR) &&
IS_VALID_WRITE_PTR(ppftl, PFOLDERTWINLIST))
#endif
{
HPATH hpath;
InvalidatePathListInfo(GetBriefcasePathList(hbr));
tr = TranslatePATHRESULTToTWINRESULT(
AddPath(GetBriefcasePathList(hbr), pcszFolder, &hpath));
if (tr == TR_SUCCESS)
{
PFOLDERTWINLIST pftlNew;
/* Try to create a new folder twin list. */
if (AllocateMemory(sizeof(*pftlNew), &pftlNew))
{
ARRAYINDEX ai;
/* Initialize FOLDERTWINLIST structure fields. */
pftlNew->ulcItems = 0;
pftlNew->pcftFirst = NULL;
pftlNew->hbr = hbr;
/* Search for first folder pair referencing given folder. */
if (SearchSortedArray(GetBriefcaseFolderPairPtrArray(hbr),
&FolderPairSearchCmp, hpath, &ai))
{
PFOLDERTWIN pftHead;
ARRAYINDEX aicFolderTwins;
tr = CreateListOfFolderTwins(hbr, ai, hpath, &pftHead, &aicFolderTwins);
if (tr == TR_SUCCESS)
{
/* Success! Update parent folder twin list fields. */
pftlNew->pcftFirst = pftHead;
pftlNew->ulcItems = aicFolderTwins;
}
else
/* Free data structure, ignoring return value. */
FreeMemory(pftlNew);
}
else
tr = TR_SUCCESS;
/* Return pointer to new FOLDERTWINLIST. */
if (tr == TR_SUCCESS)
{
*ppftl = pftlNew;
ASSERT(IS_VALID_STRUCT_PTR(*ppftl, CFOLDERTWINLIST));
}
}
else
tr = TR_OUT_OF_MEMORY;
DeletePath(hpath);
}
}
#ifdef EXPV
else
tr = TR_INVALID_PARAMETER;
#endif
DebugExitTWINRESULT(CreateFolderTwinList, tr);
EndExclusiveBriefcaseAccess();
}
else
tr = TR_REENTERED;
return(tr);
}
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | DestroyFolderTwinList | Destroys a folder twin list created
by CreateFolderTwinList().
@parm PFOLDERTWINLIST | pftl | A pointer to the folder twin list to be
destroyed. The FOLDERTWINLIST pointed to by pftl is not valid after
DestroyFolderTwinList() is called.
@rdesc If the folder twin list was deleted successfully, TR_SUCCESS is
returned. Otherwise, the folder twin list was not deleted successfully, and
the return value indicates the error that occurred.
@xref CreateFolderTwinList IsFolderTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI DestroyFolderTwinList(PFOLDERTWINLIST pftl)
{
TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess())
{
DebugEntry(DestroyFolderTwinList);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_STRUCT_PTR(pftl, CFOLDERTWINLIST))
#endif
{
DestroyListOfFolderTwins((PFOLDERTWIN)(pftl->pcftFirst));
FreeMemory(pftl);
tr = TR_SUCCESS;
}
#ifdef EXPV
else
tr = TR_INVALID_PARAMETER;
#endif
DebugExitTWINRESULT(DestroyFolderTwinList, tr);
EndExclusiveBriefcaseAccess();
}
else
tr = TR_REENTERED;
return(tr);
}