NT4/private/windows/rover/filesync/core/path.c
2020-09-30 17:12:29 +02:00

1891 lines
38 KiB
C

/*
* path.c - Path ADT module.
*/
/* Headers
**********/
#include "project.h"
#pragma hdrstop
#include "volume.h"
/* Constants
************/
/* PATHLIST PTRARRAY allocation parameters */
#define NUM_START_PATHS (32)
#define NUM_PATHS_TO_ADD (32)
/* PATHLIST string table allocation parameters */
#define NUM_PATH_HASH_BUCKETS (67)
/* Types
********/
/* path list */
typedef struct _pathlist
{
/* array of pointers to PATHs */
HPTRARRAY hpa;
/* list of volumes */
HVOLUMELIST hvl;
/* table of path suffix strings */
HSTRINGTABLE hst;
}
PATHLIST;
DECLARE_STANDARD_TYPES(PATHLIST);
/* path structure */
typedef struct _path
{
/* reference count */
ULONG ulcLock;
/* handle to parent volume */
HVOLUME hvol;
/* handle to path suffix string */
HSTRING hsPathSuffix;
/* pointer to PATH's parent PATHLIST */
PPATHLIST pplParent;
}
PATH;
DECLARE_STANDARD_TYPES(PATH);
/* PATH search structure used by PathSearchCmp() */
typedef struct _pathsearchinfo
{
HVOLUME hvol;
LPCTSTR pcszPathSuffix;
}
PATHSEARCHINFO;
DECLARE_STANDARD_TYPES(PATHSEARCHINFO);
/* database path list header */
typedef struct _dbpathlistheader
{
/* number of paths in list */
LONG lcPaths;
}
DBPATHLISTHEADER;
DECLARE_STANDARD_TYPES(DBPATHLISTHEADER);
/* database path structure */
typedef struct _dbpath
{
/* old handle to path */
HPATH hpath;
/* old handle to parent volume */
HVOLUME hvol;
/* old handle to path suffix string */
HSTRING hsPathSuffix;
}
DBPATH;
DECLARE_STANDARD_TYPES(DBPATH);
/***************************** Private Functions *****************************/
/* Module Prototypes
********************/
PRIVATE_CODE COMPARISONRESULT PathSortCmp(PCVOID, PCVOID);
PRIVATE_CODE COMPARISONRESULT PathSearchCmp(PCVOID, PCVOID);
PRIVATE_CODE BOOL UnifyPath(PPATHLIST, HVOLUME, LPCTSTR, PPATH *);
PRIVATE_CODE BOOL CreatePath(PPATHLIST, HVOLUME, LPCTSTR, PPATH *);
PRIVATE_CODE void DestroyPath(PPATH);
PRIVATE_CODE void UnlinkPath(PCPATH);
PRIVATE_CODE void LockPath(PPATH);
PRIVATE_CODE BOOL UnlockPath(PPATH);
PRIVATE_CODE PATHRESULT TranslateVOLUMERESULTToPATHRESULT(VOLUMERESULT);
PRIVATE_CODE TWINRESULT WritePath(HCACHEDFILE, PPATH);
PRIVATE_CODE TWINRESULT ReadPath(HCACHEDFILE, PPATHLIST, HHANDLETRANS, HHANDLETRANS, HHANDLETRANS);
#if defined(DEBUG) || defined(VSTF)
PRIVATE_CODE BOOL IsValidPCPATHLIST(PCPATHLIST);
PRIVATE_CODE BOOL IsValidPCPATH(PCPATH);
#endif
#if defined(DEBUG)
PRIVATE_CODE BOOL IsValidPCPATHSEARCHINFO(PCPATHSEARCHINFO);
#endif
/*
** PathSortCmp()
**
** Pointer comparison function used to sort the module array of paths.
**
** Arguments: pcpath1 - pointer to first path
** pcpath2 - pointer to second path
**
** Returns:
**
** Side Effects: none
**
** The internal paths are sorted by:
** 1) volume
** 2) path suffix
** 3) pointer value
*/
PRIVATE_CODE COMPARISONRESULT PathSortCmp(PCVOID pcpath1, PCVOID pcpath2)
{
COMPARISONRESULT cr;
ASSERT(IS_VALID_STRUCT_PTR(pcpath1, CPATH));
ASSERT(IS_VALID_STRUCT_PTR(pcpath2, CPATH));
cr = CompareVolumes(((PCPATH)pcpath1)->hvol,
((PCPATH)pcpath2)->hvol);
if (cr == CR_EQUAL)
{
cr = ComparePathStringsByHandle(((PCPATH)pcpath1)->hsPathSuffix,
((PCPATH)pcpath2)->hsPathSuffix);
if (cr == CR_EQUAL)
cr = ComparePointers(pcpath1, pcpath2);
}
return(cr);
}
/*
** PathSearchCmp()
**
** Pointer comparison function used to search for a path.
**
** Arguments: pcpathsi - pointer to PATHSEARCHINFO describing path to
** search for
** pcpath - pointer to path to examine
**
** Returns:
**
** Side Effects: none
**
** The internal paths are searched by:
** 1) volume
** 2) path suffix string
*/
PRIVATE_CODE COMPARISONRESULT PathSearchCmp(PCVOID pcpathsi, PCVOID pcpath)
{
COMPARISONRESULT cr;
ASSERT(IS_VALID_STRUCT_PTR(pcpathsi, CPATHSEARCHINFO));
ASSERT(IS_VALID_STRUCT_PTR(pcpath, CPATH));
cr = CompareVolumes(((PCPATHSEARCHINFO)pcpathsi)->hvol,
((PCPATH)pcpath)->hvol);
if (cr == CR_EQUAL)
cr = ComparePathStrings(((PCPATHSEARCHINFO)pcpathsi)->pcszPathSuffix,
GetString(((PCPATH)pcpath)->hsPathSuffix));
return(cr);
}
/*
** UnifyPath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL UnifyPath(PPATHLIST ppl, HVOLUME hvol, LPCTSTR pcszPathSuffix,
PPATH *pppath)
{
BOOL bResult = FALSE;
ASSERT(IS_VALID_STRUCT_PTR(ppl, CPATHLIST));
ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
ASSERT(IsValidPathSuffix(pcszPathSuffix));
ASSERT(IS_VALID_WRITE_PTR(pppath, PPATH));
/* Allocate space for PATH structure. */
if (AllocateMemory(sizeof(**pppath), pppath))
{
if (CopyVolume(hvol, ppl->hvl, &((*pppath)->hvol)))
{
if (AddString(pcszPathSuffix, ppl->hst, GetHashBucketIndex, &((*pppath)->hsPathSuffix)))
{
ARRAYINDEX aiUnused;
/* Initialize remaining PATH fields. */
(*pppath)->ulcLock = 0;
(*pppath)->pplParent = ppl;
/* Add new PATH to array. */
if (AddPtr(ppl->hpa, PathSortCmp, *pppath, &aiUnused))
bResult = TRUE;
else
{
DeleteString((*pppath)->hsPathSuffix);
UNIFYPATH_BAIL1:
DeleteVolume((*pppath)->hvol);
UNIFYPATH_BAIL2:
FreeMemory(*pppath);
}
}
else
goto UNIFYPATH_BAIL1;
}
else
goto UNIFYPATH_BAIL2;
}
ASSERT(! bResult ||
IS_VALID_STRUCT_PTR(*pppath, CPATH));
return(bResult);
}
/*
** CreatePath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL CreatePath(PPATHLIST ppl, HVOLUME hvol, LPCTSTR pcszPathSuffix,
PPATH *pppath)
{
BOOL bResult;
ARRAYINDEX aiFound;
PATHSEARCHINFO pathsi;
ASSERT(IS_VALID_STRUCT_PTR(ppl, CPATHLIST));
ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
ASSERT(IsValidPathSuffix(pcszPathSuffix));
ASSERT(IS_VALID_WRITE_PTR(pppath, CPATH));
/* Does a path for the given volume and path suffix already exist? */
pathsi.hvol = hvol;
pathsi.pcszPathSuffix = pcszPathSuffix;
bResult = SearchSortedArray(ppl->hpa, &PathSearchCmp, &pathsi, &aiFound);
if (bResult)
/* Yes. Return it. */
*pppath = GetPtr(ppl->hpa, aiFound);
else
bResult = UnifyPath(ppl, hvol, pcszPathSuffix, pppath);
if (bResult)
LockPath(*pppath);
ASSERT(! bResult ||
IS_VALID_STRUCT_PTR(*pppath, CPATH));
return(bResult);
}
/*
** DestroyPath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE void DestroyPath(PPATH ppath)
{
ASSERT(IS_VALID_STRUCT_PTR(ppath, CPATH));
DeleteVolume(ppath->hvol);
DeleteString(ppath->hsPathSuffix);
FreeMemory(ppath);
return;
}
/*
** UnlinkPath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE void UnlinkPath(PCPATH pcpath)
{
HPTRARRAY hpa;
ARRAYINDEX aiFound;
ASSERT(IS_VALID_STRUCT_PTR(pcpath, CPATH));
hpa = pcpath->pplParent->hpa;
if (EVAL(SearchSortedArray(hpa, &PathSortCmp, pcpath, &aiFound)))
{
ASSERT(GetPtr(hpa, aiFound) == pcpath);
DeletePtr(hpa, aiFound);
}
return;
}
/*
** LockPath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE void LockPath(PPATH ppath)
{
ASSERT(IS_VALID_STRUCT_PTR(ppath, CPATH));
ASSERT(ppath->ulcLock < ULONG_MAX);
ppath->ulcLock++;
return;
}
/*
** UnlockPath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL UnlockPath(PPATH ppath)
{
ASSERT(IS_VALID_STRUCT_PTR(ppath, CPATH));
if (EVAL(ppath->ulcLock > 0))
ppath->ulcLock--;
return(ppath->ulcLock > 0);
}
/*
** TranslateVOLUMERESULTToPATHRESULT()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE PATHRESULT TranslateVOLUMERESULTToPATHRESULT(VOLUMERESULT vr)
{
PATHRESULT pr;
switch (vr)
{
case VR_SUCCESS:
pr = PR_SUCCESS;
break;
case VR_UNAVAILABLE_VOLUME:
pr = PR_UNAVAILABLE_VOLUME;
break;
case VR_OUT_OF_MEMORY:
pr = PR_OUT_OF_MEMORY;
break;
default:
ASSERT(vr == VR_INVALID_PATH);
pr = PR_INVALID_PATH;
break;
}
return(pr);
}
/*
** WritePath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE TWINRESULT WritePath(HCACHEDFILE hcf, PPATH ppath)
{
TWINRESULT tr;
DBPATH dbpath;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
ASSERT(IS_VALID_STRUCT_PTR(ppath, CPATH));
/* Write database path. */
dbpath.hpath = (HPATH)ppath;
dbpath.hvol = ppath->hvol;
dbpath.hsPathSuffix = ppath->hsPathSuffix;
if (WriteToCachedFile(hcf, (PCVOID)&dbpath, sizeof(dbpath), NULL))
tr = TR_SUCCESS;
else
tr = TR_BRIEFCASE_WRITE_FAILED;
return(tr);
}
/*
** ReadPath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE TWINRESULT ReadPath(HCACHEDFILE hcf, PPATHLIST ppl,
HHANDLETRANS hhtVolumes,
HHANDLETRANS hhtStrings,
HHANDLETRANS hhtPaths)
{
TWINRESULT tr;
DBPATH dbpath;
DWORD dwcbRead;
HVOLUME hvol;
HSTRING hsPathSuffix;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
ASSERT(IS_VALID_STRUCT_PTR(ppl, CPATHLIST));
ASSERT(IS_VALID_HANDLE(hhtVolumes, HANDLETRANS));
ASSERT(IS_VALID_HANDLE(hhtStrings, HANDLETRANS));
ASSERT(IS_VALID_HANDLE(hhtPaths, HANDLETRANS));
if (ReadFromCachedFile(hcf, &dbpath, sizeof(dbpath), &dwcbRead) &&
dwcbRead == sizeof(dbpath) &&
TranslateHandle(hhtVolumes, (HGENERIC)(dbpath.hvol), (PHGENERIC)&hvol) &&
TranslateHandle(hhtStrings, (HGENERIC)(dbpath.hsPathSuffix), (PHGENERIC)&hsPathSuffix))
{
PPATH ppath;
if (CreatePath(ppl, hvol, GetString(hsPathSuffix), &ppath))
{
/*
* To leave read paths with 0 initial lock count, we must undo
* the LockPath() performed by CreatePath().
*/
UnlockPath(ppath);
if (AddHandleToHandleTranslator(hhtPaths,
(HGENERIC)(dbpath.hpath),
(HGENERIC)ppath))
tr = TR_SUCCESS;
else
{
UnlinkPath(ppath);
DestroyPath(ppath);
tr = TR_OUT_OF_MEMORY;
}
}
else
tr = TR_OUT_OF_MEMORY;
}
else
tr = TR_CORRUPT_BRIEFCASE;
return(tr);
}
#if defined(DEBUG) || defined(VSTF)
/*
** IsValidPCPATHLIST()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL IsValidPCPATHLIST(PCPATHLIST pcpl)
{
return(IS_VALID_READ_PTR(pcpl, CPATHLIST) &&
IS_VALID_HANDLE(pcpl->hpa, PTRARRAY) &&
IS_VALID_HANDLE(pcpl->hvl, VOLUMELIST) &&
IS_VALID_HANDLE(pcpl->hst, STRINGTABLE));
}
/*
** IsValidPCPATH()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL IsValidPCPATH(PCPATH pcpath)
{
return(IS_VALID_READ_PTR(pcpath, CPATH) &&
IS_VALID_HANDLE(pcpath->hvol, VOLUME) &&
IS_VALID_HANDLE(pcpath->hsPathSuffix, STRING) &&
IsValidPathSuffix(GetString(pcpath->hsPathSuffix)) &&
IS_VALID_READ_PTR(pcpath->pplParent, CPATHLIST));
}
#endif
#if defined(DEBUG)
/*
** IsValidPCPATHSEARCHINFO()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PRIVATE_CODE BOOL IsValidPCPATHSEARCHINFO(PCPATHSEARCHINFO pcpathsi)
{
return(IS_VALID_READ_PTR(pcpathsi, CPATHSEARCHINFO) &&
IS_VALID_HANDLE(pcpathsi->hvol, VOLUME) &&
IsValidPathSuffix(pcpathsi->pcszPathSuffix));
}
#endif
/****************************** Public Functions *****************************/
/*
** CreatePathList()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL CreatePathList(DWORD dwFlags, HWND hwndOwner, PHPATHLIST phpl)
{
BOOL bResult = FALSE;
PPATHLIST ppl;
ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_RLI_IFLAGS));
ASSERT(IS_FLAG_CLEAR(dwFlags, RLI_IFL_ALLOW_UI) ||
IS_VALID_HANDLE(hwndOwner, WND));
ASSERT(IS_VALID_WRITE_PTR(phpl, HPATHLIST));
if (AllocateMemory(sizeof(*ppl), &ppl))
{
NEWPTRARRAY npa;
/* Create pointer array of paths. */
npa.aicInitialPtrs = NUM_START_PATHS;
npa.aicAllocGranularity = NUM_PATHS_TO_ADD;
npa.dwFlags = NPA_FL_SORTED_ADD;
if (CreatePtrArray(&npa, &(ppl->hpa)))
{
if (CreateVolumeList(dwFlags, hwndOwner, &(ppl->hvl)))
{
NEWSTRINGTABLE nszt;
/* Create string table for path suffix strings. */
nszt.hbc = NUM_PATH_HASH_BUCKETS;
if (CreateStringTable(&nszt, &(ppl->hst)))
{
*phpl = (HPATHLIST)ppl;
bResult = TRUE;
}
else
{
DestroyVolumeList(ppl->hvl);
CREATEPATHLIST_BAIL1:
DestroyPtrArray(ppl->hpa);
CREATEPATHLIST_BAIL2:
FreeMemory(ppl);
}
}
else
goto CREATEPATHLIST_BAIL1;
}
else
goto CREATEPATHLIST_BAIL2;
}
ASSERT(! bResult ||
IS_VALID_HANDLE(*phpl, PATHLIST));
return(bResult);
}
/*
** DestroyPathList()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void DestroyPathList(HPATHLIST hpl)
{
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
/* First free all paths in array. */
aicPtrs = GetPtrCount(((PCPATHLIST)hpl)->hpa);
for (ai = 0; ai < aicPtrs; ai++)
DestroyPath(GetPtr(((PCPATHLIST)hpl)->hpa, ai));
/* Now wipe out the array. */
DestroyPtrArray(((PCPATHLIST)hpl)->hpa);
ASSERT(! GetVolumeCount(((PCPATHLIST)hpl)->hvl));
DestroyVolumeList(((PCPATHLIST)hpl)->hvl);
ASSERT(! GetStringCount(((PCPATHLIST)hpl)->hst));
DestroyStringTable(((PCPATHLIST)hpl)->hst);
FreeMemory((PPATHLIST)hpl);
return;
}
/*
** InvalidatePathListInfo()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void InvalidatePathListInfo(HPATHLIST hpl)
{
InvalidateVolumeListInfo(((PCPATHLIST)hpl)->hvl);
return;
}
/*
** ClearPathListInfo()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void ClearPathListInfo(HPATHLIST hpl)
{
ClearVolumeListInfo(((PCPATHLIST)hpl)->hvl);
return;
}
/*
** AddPath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE PATHRESULT AddPath(HPATHLIST hpl, LPCTSTR pcszPath, PHPATH phpath)
{
PATHRESULT pr;
HVOLUME hvol;
TCHAR rgchPathSuffix[MAX_PATH_LEN];
LPCTSTR pszPath;
#ifdef UNICODE
WCHAR szUnicode[MAX_PATH];
#endif
ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
ASSERT(IS_VALID_WRITE_PTR(phpath, HPATH));
// On NT, we want to convert a unicode string to an ANSI shortened path for
// the sake of interop
#if defined(UNICODE)
{
CHAR szAnsi[MAX_PATH];
szUnicode[0] = L'\0';
WideCharToMultiByte(CP_ACP, 0, pcszPath, -1, szAnsi, ARRAYSIZE(szAnsi), NULL, NULL);
MultiByteToWideChar(CP_ACP, 0, szAnsi, -1, szUnicode, ARRAYSIZE(szUnicode));
if (lstrcmp(szUnicode, pcszPath))
{
// Cannot convert losslessly from Unicode -> Ansi, so get the short path
lstrcpy(szUnicode, pcszPath);
SheShortenPath(szUnicode, TRUE);
pszPath = szUnicode;
}
else
{
// It will convert OK, so just use the original
pszPath = pcszPath;
}
}
#else
pszPath = pcszPath;
#endif
pr = TranslateVOLUMERESULTToPATHRESULT(
AddVolume(((PCPATHLIST)hpl)->hvl, pszPath, &hvol, rgchPathSuffix));
if (pr == PR_SUCCESS)
{
PPATH ppath;
if (CreatePath((PPATHLIST)hpl, hvol, rgchPathSuffix, &ppath))
*phpath = (HPATH)ppath;
else
pr = PR_OUT_OF_MEMORY;
DeleteVolume(hvol);
}
ASSERT(pr != PR_SUCCESS ||
IS_VALID_HANDLE(*phpath, PATH));
return(pr);
}
/*
** AddChildPath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL AddChildPath(HPATHLIST hpl, HPATH hpathParent,
LPCTSTR pcszSubPath, PHPATH phpathChild)
{
BOOL bResult;
TCHAR rgchChildPathSuffix[MAX_PATH_LEN];
LPCTSTR pcszPathSuffix;
LPTSTR pszPathSuffixEnd;
PPATH ppathChild;
ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
ASSERT(IS_VALID_HANDLE(hpathParent, PATH));
ASSERT(IS_VALID_STRING_PTR(pcszSubPath, CSTR));
ASSERT(IS_VALID_WRITE_PTR(phpathChild, HPATH));
ComposePath(rgchChildPathSuffix,
GetString(((PCPATH)hpathParent)->hsPathSuffix),
pcszSubPath);
pcszPathSuffix = rgchChildPathSuffix;
if (IS_SLASH(*pcszPathSuffix))
pcszPathSuffix++;
pszPathSuffixEnd = CharPrev(pcszPathSuffix,
pcszPathSuffix + lstrlen(pcszPathSuffix));
if (IS_SLASH(*pszPathSuffixEnd))
*pszPathSuffixEnd = TEXT('\0');
ASSERT(IsValidPathSuffix(pcszPathSuffix));
bResult = CreatePath((PPATHLIST)hpl, ((PCPATH)hpathParent)->hvol,
pcszPathSuffix, &ppathChild);
if (bResult)
*phpathChild = (HPATH)ppathChild;
ASSERT(! bResult ||
IS_VALID_HANDLE(*phpathChild, PATH));
return(bResult);
}
/*
** DeletePath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void DeletePath(HPATH hpath)
{
ASSERT(IS_VALID_HANDLE(hpath, PATH));
if (! UnlockPath((PPATH)hpath))
{
UnlinkPath((PPATH)hpath);
DestroyPath((PPATH)hpath);
}
return;
}
/*
** CopyPath()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL CopyPath(HPATH hpathSrc, HPATHLIST hplDest, PHPATH phpathCopy)
{
BOOL bResult;
PPATH ppath;
ASSERT(IS_VALID_HANDLE(hpathSrc, PATH));
ASSERT(IS_VALID_HANDLE(hplDest, PATHLIST));
ASSERT(IS_VALID_WRITE_PTR(phpathCopy, HPATH));
/* Is the destination path list the source path's path list? */
if (((PCPATH)hpathSrc)->pplParent == (PCPATHLIST)hplDest)
{
/* Yes. Use the source path. */
LockPath((PPATH)hpathSrc);
ppath = (PPATH)hpathSrc;
bResult = TRUE;
}
else
bResult = CreatePath((PPATHLIST)hplDest, ((PCPATH)hpathSrc)->hvol,
GetString(((PCPATH)hpathSrc)->hsPathSuffix),
&ppath);
if (bResult)
*phpathCopy = (HPATH)ppath;
ASSERT(! bResult ||
IS_VALID_HANDLE(*phpathCopy, PATH));
return(bResult);
}
/*
** GetPathString()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void GetPathString(HPATH hpath, LPTSTR pszPathBuf)
{
ASSERT(IS_VALID_HANDLE(hpath, PATH));
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathBuf, STR, MAX_PATH_LEN));
GetPathRootString(hpath, pszPathBuf);
CatPath(pszPathBuf, GetString(((PPATH)hpath)->hsPathSuffix));
ASSERT(IsCanonicalPath(pszPathBuf));
return;
}
/*
** GetPathRootString()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void GetPathRootString(HPATH hpath, LPTSTR pszPathRootBuf)
{
ASSERT(IS_VALID_HANDLE(hpath, PATH));
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathRootBuf, STR, MAX_PATH_LEN));
GetVolumeRootPath(((PPATH)hpath)->hvol, pszPathRootBuf);
ASSERT(IsCanonicalPath(pszPathRootBuf));
return;
}
/*
** GetPathSuffixString()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE void GetPathSuffixString(HPATH hpath, LPTSTR pszPathSuffixBuf)
{
ASSERT(IS_VALID_HANDLE(hpath, PATH));
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathSuffixBuf, STR, MAX_PATH_LEN));
ASSERT(lstrlen(GetString(((PPATH)hpath)->hsPathSuffix)) < MAX_PATH_LEN);
MyLStrCpyN(pszPathSuffixBuf, GetString(((PPATH)hpath)->hsPathSuffix), MAX_PATH_LEN);
ASSERT(IsValidPathSuffix(pszPathSuffixBuf));
return;
}
/*
** AllocatePathString()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL AllocatePathString(HPATH hpath, LPTSTR *ppszPath)
{
TCHAR rgchPath[MAX_PATH_LEN];
ASSERT(IS_VALID_HANDLE(hpath, PATH));
ASSERT(IS_VALID_WRITE_PTR(ppszPath, LPTSTR));
GetPathString(hpath, rgchPath);
return(StringCopy(rgchPath, ppszPath));
}
#ifdef DEBUG
/*
** DebugGetPathString()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
**
** N.b., DebugGetPathString() must be non-intrusive.
*/
PUBLIC_CODE LPCTSTR DebugGetPathString(HPATH hpath)
{
#pragma data_seg(DATA_SEG_SHARED)
/* Allow 4 debug paths. */
static TCHAR SrgrgchPaths[][MAX_PATH_LEN] = { TEXT(""), TEXT(""), TEXT(""), TEXT("") };
static UINT SuiPath = 0;
#pragma data_seg()
LPTSTR pszPath;
ASSERT(IS_VALID_HANDLE(hpath, PATH));
pszPath = SrgrgchPaths[SuiPath];
DebugGetVolumeRootPath(((PPATH)hpath)->hvol, pszPath);
CatPath(pszPath, GetString(((PPATH)hpath)->hsPathSuffix));
SuiPath++;
SuiPath %= ARRAY_ELEMENTS(SrgrgchPaths);
return(pszPath);
}
/*
** GetPathCount()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE ULONG GetPathCount(HPATHLIST hpl)
{
ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
return(GetPtrCount(((PCPATHLIST)hpl)->hpa));
}
#endif
/*
** IsPathVolumeAvailable()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL IsPathVolumeAvailable(HPATH hpath)
{
ASSERT(IS_VALID_HANDLE(hpath, PATH));
return(IsVolumeAvailable(((PCPATH)hpath)->hvol));
}
/*
** GetPathVolumeID()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE HVOLUMEID GetPathVolumeID(HPATH hpath)
{
ASSERT(IS_VALID_HANDLE(hpath, PATH));
return((HVOLUMEID)hpath);
}
/*
** MyIsPathOnVolume()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
**
** MyIsPathOnVolume() will fail for a new root path alias for a volume. E.g.,
** if the same net resource is connected to both X: and Y:, MyIsPathOnVolume()
** will only return TRUE for the drive root path that the net resource was
** connected to through the given HVOLUME.
*/
PUBLIC_CODE BOOL MyIsPathOnVolume(LPCTSTR pcszPath, HPATH hpath)
{
BOOL bResult;
TCHAR rgchVolumeRootPath[MAX_PATH_LEN];
ASSERT(IsFullPath(pcszPath));
ASSERT(IS_VALID_HANDLE(hpath, PATH));
if (IsVolumeAvailable(((PPATH)hpath)->hvol))
{
GetVolumeRootPath(((PPATH)hpath)->hvol, rgchVolumeRootPath);
bResult = (MyLStrCmpNI(pcszPath, rgchVolumeRootPath,
lstrlen(rgchVolumeRootPath))
== CR_EQUAL);
}
else
{
TRACE_OUT((TEXT("MyIsPathOnVolume(): Failing on unavailable volume %s."),
DebugGetVolumeRootPath(((PPATH)hpath)->hvol, rgchVolumeRootPath)));
bResult = FALSE;
}
return(bResult);
}
/*
** ComparePaths()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
**
** PATHs are compared by:
** 1) volume
** 2) path suffix
*/
PUBLIC_CODE COMPARISONRESULT ComparePaths(HPATH hpath1, HPATH hpath2)
{
COMPARISONRESULT cr;
ASSERT(IS_VALID_HANDLE(hpath1, PATH));
ASSERT(IS_VALID_HANDLE(hpath2, PATH));
/* This comparison works across path lists. */
cr = ComparePathVolumes(hpath1, hpath2);
if (cr == CR_EQUAL)
cr = ComparePathStringsByHandle(((PCPATH)hpath1)->hsPathSuffix,
((PCPATH)hpath2)->hsPathSuffix);
return(cr);
}
/*
** ComparePathVolumes()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE COMPARISONRESULT ComparePathVolumes(HPATH hpath1, HPATH hpath2)
{
ASSERT(IS_VALID_HANDLE(hpath1, PATH));
ASSERT(IS_VALID_HANDLE(hpath2, PATH));
return(CompareVolumes(((PCPATH)hpath1)->hvol, ((PCPATH)hpath2)->hvol));
}
/*
** IsPathPrefix()
**
** Determines whether or not one path is a prefix of another.
**
** Arguments: hpathChild - whole path (longer or same length)
** hpathParent - prefix path to test (shorter or same length)
**
** Returns: TRUE if the second path is a prefix of the first path. FALSE
** if not.
**
** Side Effects: none
**
** Read 'IsPathPrefix(A, B)' as 'Is A in B's subtree?'.
*/
PUBLIC_CODE BOOL IsPathPrefix(HPATH hpathChild, HPATH hpathParent)
{
BOOL bResult;
ASSERT(IS_VALID_HANDLE(hpathParent, PATH));
ASSERT(IS_VALID_HANDLE(hpathChild, PATH));
if (ComparePathVolumes(hpathParent, hpathChild) == CR_EQUAL)
{
TCHAR rgchParentSuffix[MAX_PATH_LEN];
TCHAR rgchChildSuffix[MAX_PATH_LEN];
int nParentSuffixLen;
int nChildSuffixLen;
/* Ignore path roots when comparing path strings. */
GetPathSuffixString(hpathParent, rgchParentSuffix);
GetPathSuffixString(hpathChild, rgchChildSuffix);
/* Only root paths should have no path suffix off the root. */
nParentSuffixLen = lstrlen(rgchParentSuffix);
nChildSuffixLen = lstrlen(rgchChildSuffix);
/*
* The parent path is a path prefix of the child path iff:
* 1) The parent's path suffix string is shorter than or the same
* length as the child's path suffix string.
* 2) The two path suffix strings match through the length of the
* parent's path suffix string.
* 3) The prefix of the child's path suffix string is followed
* immediately by a null terminator or a path separator.
*/
bResult = (nChildSuffixLen >= nParentSuffixLen &&
MyLStrCmpNI(rgchParentSuffix, rgchChildSuffix,
nParentSuffixLen) == CR_EQUAL &&
(nChildSuffixLen == nParentSuffixLen || /* same paths */
! nParentSuffixLen || /* root parent */
IS_SLASH(rgchChildSuffix[nParentSuffixLen]))); /* non-root parent */
}
else
bResult = FALSE;
return(bResult);
}
/*
** SubtreesIntersect()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
**
** N.b., two subtrees cannot both intersect a third subtree unless they
** intersect each other.
*/
PUBLIC_CODE BOOL SubtreesIntersect(HPATH hpath1, HPATH hpath2)
{
ASSERT(IS_VALID_HANDLE(hpath1, PATH));
ASSERT(IS_VALID_HANDLE(hpath2, PATH));
return(IsPathPrefix(hpath1, hpath2) ||
IsPathPrefix(hpath2, hpath1));
}
/*
** FindEndOfRootSpec()
**
** Finds the end of the root specification in a path string.
**
** Arguments: pcszPath - path to examine for root specification
** hpath - handle to PATH that path string was generated from
**
** Returns: pointer to first character after end of root specification
**
** Side Effects: none
**
** Examples:
**
** input path output string
** ---------- -------------
** c:\ <empty string>
** c:\foo foo
** c:\foo\bar foo\bar
** \\pyrex\user\ <empty string>
** \\pyrex\user\foo foo
** \\pyrex\user\foo\bar foo\bar
*/
PUBLIC_CODE LPTSTR FindEndOfRootSpec(LPCTSTR pcszFullPath, HPATH hpath)
{
LPCTSTR pcsz;
UINT ucchPathLen;
UINT ucchSuffixLen;
ASSERT(IsCanonicalPath(pcszFullPath));
ASSERT(IS_VALID_HANDLE(hpath, PATH));
ucchPathLen = lstrlen(pcszFullPath);
ucchSuffixLen = lstrlen(GetString(((PCPATH)hpath)->hsPathSuffix));
pcsz = pcszFullPath + ucchPathLen;
if (ucchPathLen > ucchSuffixLen)
pcsz -= ucchSuffixLen;
else
/* Assume path is root path. */
ERROR_OUT((TEXT("FindEndOfRootSpec(): Path suffix %s is longer than full path %s."),
GetString(((PCPATH)hpath)->hsPathSuffix),
pcszFullPath));
ASSERT(IsValidPathSuffix(pcsz));
return((LPTSTR)pcsz);
}
/*
** FindPathSuffix()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE LPTSTR FindChildPathSuffix(HPATH hpathParent, HPATH hpathChild,
LPTSTR pszChildSuffixBuf)
{
LPCTSTR pcszChildSuffix;
TCHAR rgchParentSuffix[MAX_PATH_LEN];
ASSERT(IS_VALID_HANDLE(hpathParent, PATH));
ASSERT(IS_VALID_HANDLE(hpathChild, PATH));
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszChildSuffixBuf, STR, MAX_PATH_LEN));
ASSERT(IsPathPrefix(hpathChild, hpathParent));
GetPathSuffixString(hpathParent, rgchParentSuffix);
GetPathSuffixString(hpathChild, pszChildSuffixBuf);
ASSERT(lstrlen(rgchParentSuffix) <= lstrlen(pszChildSuffixBuf));
pcszChildSuffix = pszChildSuffixBuf + lstrlen(rgchParentSuffix);
if (IS_SLASH(*pcszChildSuffix))
pcszChildSuffix++;
ASSERT(IsValidPathSuffix(pcszChildSuffix));
return((LPTSTR)pcszChildSuffix);
}
/*
** ComparePointers()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE COMPARISONRESULT ComparePointers(PCVOID pcv1, PCVOID pcv2)
{
COMPARISONRESULT cr;
/* pcv1 and pcv2 may be any value. */
if (pcv1 < pcv2)
cr = CR_FIRST_SMALLER;
else if (pcv1 > pcv2)
cr = CR_FIRST_LARGER;
else
cr = CR_EQUAL;
return(cr);
}
/*
** TWINRESULTFromLastError()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE TWINRESULT TWINRESULTFromLastError(TWINRESULT tr)
{
switch (GetLastError())
{
case ERROR_OUTOFMEMORY:
tr = TR_OUT_OF_MEMORY;
break;
default:
break;
}
return(tr);
}
/*
** WritePathList()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE TWINRESULT WritePathList(HCACHEDFILE hcf, HPATHLIST hpl)
{
TWINRESULT tr;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
tr = WriteVolumeList(hcf, ((PCPATHLIST)hpl)->hvl);
if (tr == TR_SUCCESS)
{
tr = WriteStringTable(hcf, ((PCPATHLIST)hpl)->hst);
if (tr == TR_SUCCESS)
{
DWORD dwcbDBPathListHeaderOffset;
tr = TR_BRIEFCASE_WRITE_FAILED;
/* Save initial file position. */
dwcbDBPathListHeaderOffset = GetCachedFilePointerPosition(hcf);
if (dwcbDBPathListHeaderOffset != INVALID_SEEK_POSITION)
{
DBPATHLISTHEADER dbplh;
/* Leave space for path list header. */
ZeroMemory(&dbplh, sizeof(dbplh));
if (WriteToCachedFile(hcf, (PCVOID)&dbplh, sizeof(dbplh), NULL))
{
ARRAYINDEX aicPtrs;
ARRAYINDEX ai;
LONG lcPaths = 0;
tr = TR_SUCCESS;
aicPtrs = GetPtrCount(((PCPATHLIST)hpl)->hpa);
/* Write all paths. */
for (ai = 0; ai < aicPtrs; ai++)
{
PPATH ppath;
ppath = GetPtr(((PCPATHLIST)hpl)->hpa, ai);
/*
* As a sanity check, don't save any path with a lock count
* of 0. A 0 lock count implies that the path has not been
* referenced since it was restored from the database, or
* something is broken.
*/
if (ppath->ulcLock > 0)
{
tr = WritePath(hcf, ppath);
if (tr == TR_SUCCESS)
{
ASSERT(lcPaths < LONG_MAX);
lcPaths++;
}
else
break;
}
else
ERROR_OUT((TEXT("WritePathList(): PATH for path %s has 0 lock count and will not be written."),
DebugGetPathString((HPATH)ppath)));
}
/* Save path list header. */
if (tr == TR_SUCCESS)
{
dbplh.lcPaths = lcPaths;
tr = WriteDBSegmentHeader(hcf, dwcbDBPathListHeaderOffset, &dbplh,
sizeof(dbplh));
TRACE_OUT((TEXT("WritePathList(): Wrote %ld paths."),
dbplh.lcPaths));
}
}
}
}
}
return(tr);
}
/*
** ReadPathList()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE TWINRESULT ReadPathList(HCACHEDFILE hcf, HPATHLIST hpl,
PHHANDLETRANS phht)
{
TWINRESULT tr;
HHANDLETRANS hhtVolumes;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
ASSERT(IS_VALID_WRITE_PTR(phht, HHANDLETRANS));
tr = ReadVolumeList(hcf, ((PCPATHLIST)hpl)->hvl, &hhtVolumes);
if (tr == TR_SUCCESS)
{
HHANDLETRANS hhtStrings;
tr = ReadStringTable(hcf, ((PCPATHLIST)hpl)->hst, &hhtStrings);
if (tr == TR_SUCCESS)
{
DBPATHLISTHEADER dbplh;
DWORD dwcbRead;
tr = TR_CORRUPT_BRIEFCASE;
if (ReadFromCachedFile(hcf, &dbplh, sizeof(dbplh), &dwcbRead) &&
dwcbRead == sizeof(dbplh))
{
HHANDLETRANS hht;
if (CreateHandleTranslator(dbplh.lcPaths, &hht))
{
LONG l;
tr = TR_SUCCESS;
TRACE_OUT((TEXT("ReadPathList(): Reading %ld paths."),
dbplh.lcPaths));
for (l = 0; l < dbplh.lcPaths; l++)
{
tr = ReadPath(hcf, (PPATHLIST)hpl, hhtVolumes, hhtStrings,
hht);
if (tr != TR_SUCCESS)
break;
}
if (tr == TR_SUCCESS)
{
PrepareForHandleTranslation(hht);
*phht = hht;
ASSERT(IS_VALID_HANDLE(hpl, PATHLIST));
ASSERT(IS_VALID_HANDLE(*phht, HANDLETRANS));
}
else
DestroyHandleTranslator(hht);
}
else
tr = TR_OUT_OF_MEMORY;
}
DestroyHandleTranslator(hhtStrings);
}
DestroyHandleTranslator(hhtVolumes);
}
return(tr);
}
/*
** IsValidHPATH()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL IsValidHPATH(HPATH hp)
{
return(IS_VALID_STRUCT_PTR((PCPATH)hp, CPATH));
}
/*
** IsValidHVOLUMEID()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL IsValidHVOLUMEID(HVOLUMEID hvid)
{
return(IS_VALID_HANDLE((HPATH)hvid, PATH));
}
#if defined(DEBUG) || defined(VSTF)
/*
** IsValidHPATHLIST()
**
**
**
** Arguments:
**
** Returns:
**
** Side Effects: none
*/
PUBLIC_CODE BOOL IsValidHPATHLIST(HPATHLIST hpl)
{
return(IS_VALID_STRUCT_PTR((PCPATHLIST)hpl, CPATHLIST));
}
#endif
/***************************** Exported Functions ****************************/
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | IsPathOnVolume | Determines whether or not a given path is on
a given volume.
@parm PCSTR | pcszPath | A pointer to a string indicating the path to be
checked.
@parm HVOLUMEID | hvid | A handle to a volume ID.
@parm PBOOL | pbOnVolume | A pointer to a BOOL to be filled in with TRUE if the
given path is on the given volume, or FALSE if not. *pbOnVolume is only valid
if TR_SUCCESS is returned.
@rdesc If the volume check was successful, TR_SUCCESS is returned. Otherwise,
the volume check was not successful, and the return value indicates the error
that occurred.
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI IsPathOnVolume(LPCTSTR pcszPath, HVOLUMEID hvid,
PBOOL pbOnVolume)
{
TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess())
{
DebugEntry(IsPathOnVolume);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_STRING_PTR(pcszPath, CSTR) &&
IS_VALID_HANDLE(hvid, VOLUMEID) &&
IS_VALID_WRITE_PTR(pbOnVolume, BOOL))
#endif
{
TCHAR rgchFullPath[MAX_PATH_LEN];
LPTSTR pszFileName;
DWORD dwPathLen;
dwPathLen = GetFullPathName(pcszPath, ARRAYSIZE(rgchFullPath),
rgchFullPath, &pszFileName);
if (dwPathLen > 0 && dwPathLen < ARRAYSIZE(rgchFullPath))
{
*pbOnVolume = MyIsPathOnVolume(rgchFullPath, (HPATH)hvid);
tr = TR_SUCCESS;
}
else
{
ASSERT(! dwPathLen);
tr = TR_INVALID_PARAMETER;
}
}
#ifdef EXPV
else
tr = TR_INVALID_PARAMETER;
#endif
DebugExitTWINRESULT(IsPathOnVolume, tr);
EndExclusiveBriefcaseAccess();
}
else
tr = TR_REENTERED;
return(tr);
}
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | GetVolumeDescription | Retrieves some descriptive information
for a volume, if that information is available.
@parm HVOLUMEID | hvid | A handle to a volume ID.
@parm PVOLUMEDESC | pvoldesc | A pointer to a VOLUMEDESC to be filled in with
information describing the volume. The ulSize field of the VOLUMEDESC
structure should be filled in with sizeof(VOLUMEDESC) before calling
GetVolumeDescription().
@rdesc If the volume was described successfully, TR_SUCCESS is returned.
Otherwise, the volume was not described successfully, and the return value
indicates the error that occurred.
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI GetVolumeDescription(HVOLUMEID hvid,
PVOLUMEDESC pvoldesc)
{
TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess())
{
DebugEntry(GetVolumeDescription);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hvid, VOLUMEID) &&
IS_VALID_WRITE_PTR(pvoldesc, VOLUMEDESC) &&
EVAL(pvoldesc->ulSize == sizeof(*pvoldesc)))
#endif
{
DescribeVolume(((PCPATH)hvid)->hvol, pvoldesc);
tr = TR_SUCCESS;
}
#ifdef EXPV
else
tr = TR_INVALID_PARAMETER;
#endif
DebugExitTWINRESULT(GetVolumeDescription, tr);
EndExclusiveBriefcaseAccess();
}
else
tr = TR_REENTERED;
return(tr);
}