|
|
/*
* 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
lstrcpyn(szUnicode, pcszPath, ARRAYSIZE(szUnicode)); 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, ARRAYSIZE(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, ARRAYSIZE(rgchChildPathSuffix));
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, int cchMax) { ASSERT(IS_VALID_HANDLE(hpath, PATH)); ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathBuf, STR, cchMax));
GetPathRootString(hpath, pszPathBuf, cchMax); CatPath(pszPathBuf, GetString(((PPATH)hpath)->hsPathSuffix), cchMax);
ASSERT(IsCanonicalPath(pszPathBuf));
return; }
/*
** GetPathRootString() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void GetPathRootString(HPATH hpath, LPTSTR pszPathRootBuf, int cchMax) { ASSERT(IS_VALID_HANDLE(hpath, PATH)); ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathRootBuf, STR, cchMax));
GetVolumeRootPath(((PPATH)hpath)->hvol, pszPathRootBuf, cchMax);
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, ARRAYSIZE(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) { /* Allow 4 debug paths. */ static TCHAR SrgrgchPaths[][MAX_PATH_LEN] = { TEXT(""), TEXT(""), TEXT(""), TEXT("") }; static UINT SuiPath = 0; LPTSTR pszPath;
ASSERT(IS_VALID_HANDLE(hpath, PATH));
pszPath = SrgrgchPaths[SuiPath];
DebugGetVolumeRootPath(((PPATH)hpath)->hvol, pszPath, ARRAYSIZE(SrgrchPaths[SuiPath])); CatPath(pszPath, GetString(((PPATH)hpath)->hsPathSuffix), ARRAYSIZE(SrgrchPaths[SuiPath]));
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));
rgchVolumeRootPath[0] = TEXT('\0'); if (IsVolumeAvailable(((PPATH)hpath)->hvol)) { GetVolumeRootPath(((PPATH)hpath)->hvol, rgchVolumeRootPath, ARRAYSIZE(rgchVolumeRootPath));
bResult = (MyLStrCmpNI(pcszPath, rgchVolumeRootPath, lstrlen(rgchVolumeRootPath)) == CR_EQUAL); } else { TRACE_OUT((TEXT("MyIsPathOnVolume(): Failing on unavailable volume %s."), DebugGetVolumeRootPath(((PPATH)hpath)->hvol, rgchVolumeRootPath, ARRAYSIZE(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); }
|