1891 lines
38 KiB
C
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);
|
||
|
}
|
||
|
|