1868 lines
39 KiB
C
1868 lines
39 KiB
C
/*
|
|
* volume.c - Volume ADT module.
|
|
*/
|
|
|
|
|
|
/* Headers
|
|
**********/
|
|
|
|
#include "project.h"
|
|
#pragma hdrstop
|
|
|
|
#include "volume.h"
|
|
|
|
|
|
/* Constants
|
|
************/
|
|
|
|
/* VOLUMELIST PTRARRAY allocation parameters */
|
|
|
|
#define NUM_START_VOLUMES (16)
|
|
#define NUM_VOLUMES_TO_ADD (16)
|
|
|
|
/* VOLUMELIST string table allocation parameters */
|
|
|
|
#define NUM_VOLUME_HASH_BUCKETS (31)
|
|
|
|
|
|
/* Types
|
|
********/
|
|
|
|
/* volume list */
|
|
|
|
typedef struct _volumelist
|
|
{
|
|
/* array of pointers to VOLUMEs */
|
|
|
|
HPTRARRAY hpa;
|
|
|
|
/* table of volume root path strings */
|
|
|
|
HSTRINGTABLE hst;
|
|
|
|
/* flags from RESOLVELINKINFOINFLAGS */
|
|
|
|
DWORD dwFlags;
|
|
|
|
/*
|
|
* handle to parent window, only valid if RLI_IFL_ALLOW_UI is set in dwFlags
|
|
* field
|
|
*/
|
|
|
|
HWND hwndOwner;
|
|
}
|
|
VOLUMELIST;
|
|
DECLARE_STANDARD_TYPES(VOLUMELIST);
|
|
|
|
/* VOLUME flags */
|
|
|
|
typedef enum _volumeflags
|
|
{
|
|
/* The volume root path string indicated by hsRootPath is valid. */
|
|
|
|
VOLUME_FL_ROOT_PATH_VALID = 0x0001,
|
|
|
|
/*
|
|
* The net resource should be disconnected by calling DisconnectLinkInfo()
|
|
* when finished.
|
|
*/
|
|
|
|
VOLUME_FL_DISCONNECT = 0x0002,
|
|
|
|
/* Any cached volume information should be verified before use. */
|
|
|
|
VOLUME_FL_VERIFY_VOLUME = 0x0004,
|
|
|
|
/* flag combinations */
|
|
|
|
ALL_VOLUME_FLAGS = (VOLUME_FL_ROOT_PATH_VALID |
|
|
VOLUME_FL_DISCONNECT |
|
|
VOLUME_FL_VERIFY_VOLUME)
|
|
}
|
|
VOLUMEFLAGS;
|
|
|
|
/* VOLUME states */
|
|
|
|
typedef enum _volumestate
|
|
{
|
|
VS_UNKNOWN,
|
|
|
|
VS_AVAILABLE,
|
|
|
|
VS_UNAVAILABLE
|
|
}
|
|
VOLUMESTATE;
|
|
DECLARE_STANDARD_TYPES(VOLUMESTATE);
|
|
|
|
/* volume structure */
|
|
|
|
typedef struct _volume
|
|
{
|
|
/* reference count */
|
|
|
|
ULONG ulcLock;
|
|
|
|
/* bit mask of flags from VOLUMEFLAGS */
|
|
|
|
DWORD dwFlags;
|
|
|
|
/* volume state */
|
|
|
|
VOLUMESTATE vs;
|
|
|
|
/* pointer to LinkInfo structure indentifying volume */
|
|
|
|
PLINKINFO pli;
|
|
|
|
/*
|
|
* handle to volume root path string, only valid if
|
|
* VOLUME_FL_ROOT_PATH_VALID is set in dwFlags field
|
|
*/
|
|
|
|
HSTRING hsRootPath;
|
|
|
|
/* pointer to parent volume list */
|
|
|
|
PVOLUMELIST pvlParent;
|
|
}
|
|
VOLUME;
|
|
DECLARE_STANDARD_TYPES(VOLUME);
|
|
|
|
/* database volume list header */
|
|
|
|
typedef struct _dbvolumelistheader
|
|
{
|
|
/* number of volumes in list */
|
|
|
|
LONG lcVolumes;
|
|
|
|
/* length of longest LinkInfo structure in volume list in bytes */
|
|
|
|
UINT ucbMaxLinkInfoLen;
|
|
}
|
|
DBVOLUMELISTHEADER;
|
|
DECLARE_STANDARD_TYPES(DBVOLUMELISTHEADER);
|
|
|
|
/* database volume structure */
|
|
|
|
typedef struct _dbvolume
|
|
{
|
|
/* old handle to volume */
|
|
|
|
HVOLUME hvol;
|
|
|
|
/* old LinkInfo structure follows */
|
|
|
|
/* first DWORD of LinkInfo structure is total size in bytes */
|
|
}
|
|
DBVOLUME;
|
|
DECLARE_STANDARD_TYPES(DBVOLUME);
|
|
|
|
|
|
/***************************** Private Functions *****************************/
|
|
|
|
/* Module Prototypes
|
|
********************/
|
|
|
|
PRIVATE_CODE COMPARISONRESULT VolumeSortCmp(PCVOID, PCVOID);
|
|
PRIVATE_CODE COMPARISONRESULT VolumeSearchCmp(PCVOID, PCVOID);
|
|
PRIVATE_CODE BOOL SearchForVolumeByRootPathCmp(PCVOID, PCVOID);
|
|
PRIVATE_CODE BOOL UnifyVolume(PVOLUMELIST, PLINKINFO, PVOLUME *);
|
|
PRIVATE_CODE BOOL CreateVolume(PVOLUMELIST, PLINKINFO, PVOLUME *);
|
|
PRIVATE_CODE void UnlinkVolume(PCVOLUME);
|
|
PRIVATE_CODE BOOL DisconnectVolume(PVOLUME);
|
|
PRIVATE_CODE void DestroyVolume(PVOLUME);
|
|
PRIVATE_CODE void LockVolume(PVOLUME);
|
|
PRIVATE_CODE BOOL UnlockVolume(PVOLUME);
|
|
PRIVATE_CODE void InvalidateVolumeInfo(PVOLUME);
|
|
PRIVATE_CODE void ClearVolumeInfo(PVOLUME);
|
|
PRIVATE_CODE void GetUnavailableVolumeRootPath(PCLINKINFO, LPTSTR);
|
|
PRIVATE_CODE BOOL VerifyAvailableVolume(PVOLUME);
|
|
PRIVATE_CODE void ExpensiveResolveVolumeRootPath(PVOLUME, LPTSTR);
|
|
PRIVATE_CODE void ResolveVolumeRootPath(PVOLUME, LPTSTR);
|
|
PRIVATE_CODE VOLUMERESULT VOLUMERESULTFromLastError(VOLUMERESULT);
|
|
PRIVATE_CODE TWINRESULT WriteVolume(HCACHEDFILE, PVOLUME);
|
|
PRIVATE_CODE TWINRESULT ReadVolume(HCACHEDFILE, PVOLUMELIST, PLINKINFO, UINT, HHANDLETRANS);
|
|
|
|
#if defined(DEBUG) || defined(VSTF)
|
|
|
|
PRIVATE_CODE BOOL IsValidPCVOLUMELIST(PCVOLUMELIST);
|
|
PRIVATE_CODE BOOL IsValidVOLUMESTATE(VOLUMESTATE);
|
|
PRIVATE_CODE BOOL IsValidPCVOLUME(PCVOLUME);
|
|
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
|
|
PRIVATE_CODE BOOL IsValidPCVOLUMEDESC(PCVOLUMEDESC);
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
** VolumeSortCmp()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
**
|
|
** Volumes are sorted by:
|
|
** 1) LinkInfo volume
|
|
** 2) pointer
|
|
*/
|
|
PRIVATE_CODE COMPARISONRESULT VolumeSortCmp(PCVOID pcvol1, PCVOID pcvol2)
|
|
{
|
|
COMPARISONRESULT cr;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcvol1, CVOLUME));
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcvol2, CVOLUME));
|
|
|
|
cr = CompareLinkInfoVolumes(((PCVOLUME)pcvol1)->pli,
|
|
((PCVOLUME)pcvol2)->pli);
|
|
|
|
if (cr == CR_EQUAL)
|
|
cr = ComparePointers(pcvol1, pcvol1);
|
|
|
|
return(cr);
|
|
}
|
|
|
|
|
|
/*
|
|
** VolumeSearchCmp()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
**
|
|
** Volumes are searched by:
|
|
** 1) LinkInfo volume
|
|
*/
|
|
PRIVATE_CODE COMPARISONRESULT VolumeSearchCmp(PCVOID pcli, PCVOID pcvol)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcli, CLINKINFO));
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcvol, CVOLUME));
|
|
|
|
return(CompareLinkInfoVolumes(pcli, ((PCVOLUME)pcvol)->pli));
|
|
}
|
|
|
|
|
|
/*
|
|
** SearchForVolumeByRootPathCmp()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
**
|
|
** Volumes are searched by:
|
|
** 1) available volume root path
|
|
*/
|
|
PRIVATE_CODE BOOL SearchForVolumeByRootPathCmp(PCVOID pcszFullPath,
|
|
PCVOID pcvol)
|
|
{
|
|
BOOL bDifferent;
|
|
|
|
ASSERT(IsFullPath(pcszFullPath));
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcvol, CVOLUME));
|
|
|
|
if (((PCVOLUME)pcvol)->vs == VS_AVAILABLE &&
|
|
IS_FLAG_SET(((PCVOLUME)pcvol)->dwFlags, VOLUME_FL_ROOT_PATH_VALID))
|
|
{
|
|
LPCTSTR pcszVolumeRootPath;
|
|
|
|
pcszVolumeRootPath = GetString(((PCVOLUME)pcvol)->hsRootPath);
|
|
|
|
bDifferent = MyLStrCmpNI(pcszFullPath, pcszVolumeRootPath,
|
|
lstrlen(pcszVolumeRootPath));
|
|
}
|
|
else
|
|
bDifferent = TRUE;
|
|
|
|
return(bDifferent);
|
|
}
|
|
|
|
|
|
/*
|
|
** UnifyVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL UnifyVolume(PVOLUMELIST pvl, PLINKINFO pliRoot,
|
|
PVOLUME *ppvol)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvl, CVOLUMELIST));
|
|
ASSERT(IS_VALID_STRUCT_PTR(pliRoot, CLINKINFO));
|
|
ASSERT(IS_VALID_WRITE_PTR(ppvol, PVOLUME));
|
|
|
|
if (AllocateMemory(sizeof(**ppvol), ppvol))
|
|
{
|
|
if (CopyLinkInfo(pliRoot, &((*ppvol)->pli)))
|
|
{
|
|
ARRAYINDEX aiUnused;
|
|
|
|
(*ppvol)->ulcLock = 0;
|
|
(*ppvol)->dwFlags = 0;
|
|
(*ppvol)->vs = VS_UNKNOWN;
|
|
(*ppvol)->hsRootPath = NULL;
|
|
(*ppvol)->pvlParent = pvl;
|
|
|
|
if (AddPtr(pvl->hpa, VolumeSortCmp, *ppvol, &aiUnused))
|
|
bResult = TRUE;
|
|
else
|
|
{
|
|
FreeMemory((*ppvol)->pli);
|
|
UNIFYVOLUME_BAIL:
|
|
FreeMemory(*ppvol);
|
|
}
|
|
}
|
|
else
|
|
goto UNIFYVOLUME_BAIL;
|
|
}
|
|
|
|
ASSERT(! bResult ||
|
|
IS_VALID_STRUCT_PTR(*ppvol, CVOLUME));
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** CreateVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL CreateVolume(PVOLUMELIST pvl, PLINKINFO pliRoot,
|
|
PVOLUME *ppvol)
|
|
{
|
|
BOOL bResult;
|
|
PVOLUME pvol;
|
|
ARRAYINDEX aiFound;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvl, CVOLUMELIST));
|
|
ASSERT(IS_VALID_STRUCT_PTR(pliRoot, CLINKINFO));
|
|
ASSERT(IS_VALID_WRITE_PTR(ppvol, PVOLUME));
|
|
|
|
/* Does a volume for the given root path already exist? */
|
|
|
|
if (SearchSortedArray(pvl->hpa, &VolumeSearchCmp, pliRoot, &aiFound))
|
|
{
|
|
pvol = GetPtr(pvl->hpa, aiFound);
|
|
bResult = TRUE;
|
|
}
|
|
else
|
|
bResult = UnifyVolume(pvl, pliRoot, &pvol);
|
|
|
|
if (bResult)
|
|
{
|
|
LockVolume(pvol);
|
|
*ppvol = pvol;
|
|
}
|
|
|
|
ASSERT(! bResult ||
|
|
IS_VALID_STRUCT_PTR(*ppvol, CVOLUME));
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** UnlinkVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void UnlinkVolume(PCVOLUME pcvol)
|
|
{
|
|
HPTRARRAY hpa;
|
|
ARRAYINDEX aiFound;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcvol, CVOLUME));
|
|
|
|
hpa = pcvol->pvlParent->hpa;
|
|
|
|
if (EVAL(SearchSortedArray(hpa, &VolumeSortCmp, pcvol, &aiFound)))
|
|
{
|
|
ASSERT(GetPtr(hpa, aiFound) == pcvol);
|
|
|
|
DeletePtr(hpa, aiFound);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** DisconnectVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL DisconnectVolume(PVOLUME pvol)
|
|
{
|
|
BOOL bResult;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
if (IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_DISCONNECT))
|
|
{
|
|
bResult = DisconnectLinkInfo(pvol->pli);
|
|
|
|
CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_DISCONNECT);
|
|
}
|
|
else
|
|
bResult = TRUE;
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** DestroyVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void DestroyVolume(PVOLUME pvol)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
ClearVolumeInfo(pvol);
|
|
|
|
FreeMemory(pvol->pli);
|
|
FreeMemory(pvol);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** LockVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void LockVolume(PVOLUME pvol)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
ASSERT(pvol->ulcLock < ULONG_MAX);
|
|
pvol->ulcLock++;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** UnlockVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL UnlockVolume(PVOLUME pvol)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
if (EVAL(pvol->ulcLock > 0))
|
|
pvol->ulcLock--;
|
|
|
|
return(pvol->ulcLock > 0);
|
|
}
|
|
|
|
|
|
/*
|
|
** InvalidateVolumeInfo()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void InvalidateVolumeInfo(PVOLUME pvol)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
SET_FLAG(pvol->dwFlags, VOLUME_FL_VERIFY_VOLUME);
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** ClearVolumeInfo()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void ClearVolumeInfo(PVOLUME pvol)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
DisconnectVolume(pvol);
|
|
|
|
if (IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID))
|
|
{
|
|
DeleteString(pvol->hsRootPath);
|
|
|
|
CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID);
|
|
}
|
|
|
|
CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_VERIFY_VOLUME);
|
|
|
|
pvol->vs = VS_UNKNOWN;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** GetUnavailableVolumeRootPath()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void GetUnavailableVolumeRootPath(PCLINKINFO pcli,
|
|
LPTSTR pszRootPathBuf)
|
|
{
|
|
LPCSTR pcszLinkInfoData;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pcli, CLINKINFO));
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszRootPathBuf, STR, MAX_PATH_LEN));
|
|
|
|
/*
|
|
* Try unavailable volume root paths in the following order:
|
|
* 1) last redirected device
|
|
* 2) net resource name
|
|
* 3) local path ...and take the _last_ good one!
|
|
*/
|
|
|
|
if (GetLinkInfoData(pcli, LIDT_REDIRECTED_DEVICE, &pcszLinkInfoData) ||
|
|
GetLinkInfoData(pcli, LIDT_NET_RESOURCE, &pcszLinkInfoData) ||
|
|
GetLinkInfoData(pcli, LIDT_LOCAL_BASE_PATH, &pcszLinkInfoData))
|
|
{
|
|
//ASSERT(IS_VALID_STRING_PTR(pcszLinkInfoData, CSTR));
|
|
ASSERT(lstrlenA(pcszLinkInfoData) < MAX_PATH_LEN);
|
|
|
|
// BUGBUG somewhere, someone might need to handle unicode base paths
|
|
|
|
#ifdef UNICODE
|
|
{
|
|
TCHAR szTmp[MAX_PATH] = TEXT("");
|
|
MultiByteToWideChar(CP_ACP, 0, pcszLinkInfoData, -1, szTmp, MAX_PATH);
|
|
ComposePath(pszRootPathBuf, szTmp, TEXT("\\"));
|
|
}
|
|
#else
|
|
|
|
ComposePath(pszRootPathBuf, pcszLinkInfoData, TEXT("\\"));
|
|
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
pszRootPathBuf[0] = TEXT('\0');
|
|
|
|
ERROR_OUT((TEXT("GetUnavailableVolumeRootPath(): Net resource name and local base path unavailable. Using empty string as unavailable root path.")));
|
|
}
|
|
|
|
ASSERT(IsRootPath(pszRootPathBuf) &&
|
|
EVAL(lstrlen(pszRootPathBuf) < MAX_PATH_LEN));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** VerifyAvailableVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL VerifyAvailableVolume(PVOLUME pvol)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
PLINKINFO pli;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
ASSERT(pvol->vs == VS_AVAILABLE);
|
|
ASSERT(IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID));
|
|
|
|
WARNING_OUT((TEXT("VerifyAvailableVolume(): Calling CreateLinkInfo() to verify volume on %s."),
|
|
GetString(pvol->hsRootPath)));
|
|
|
|
if (CreateLinkInfo(GetString(pvol->hsRootPath), &pli))
|
|
{
|
|
bResult = (CompareLinkInfoReferents(pvol->pli, pli) == CR_EQUAL);
|
|
|
|
DestroyLinkInfo(pli);
|
|
|
|
if (bResult)
|
|
TRACE_OUT((TEXT("VerifyAvailableVolume(): Volume %s has not changed."),
|
|
GetString(pvol->hsRootPath)));
|
|
else
|
|
WARNING_OUT((TEXT("VerifyAvailableVolume(): Volume %s has changed."),
|
|
GetString(pvol->hsRootPath)));
|
|
}
|
|
else
|
|
WARNING_OUT((TEXT("VerifyAvailableVolume(): CreateLinkInfo() failed for %s."),
|
|
GetString(pvol->hsRootPath)));
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** ExpensiveResolveVolumeRootPath()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void ExpensiveResolveVolumeRootPath(PVOLUME pvol,
|
|
LPTSTR pszVolumeRootPathBuf)
|
|
{
|
|
BOOL bResult;
|
|
DWORD dwOutFlags;
|
|
PLINKINFO pliUpdated;
|
|
HSTRING hsRootPath;
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszVolumeRootPathBuf, STR, MAX_PATH_LEN));
|
|
|
|
if (pvol->vs == VS_UNKNOWN ||
|
|
pvol->vs == VS_AVAILABLE)
|
|
{
|
|
/*
|
|
* Only request a connection if connections are still permitted in this
|
|
* volume list.
|
|
*/
|
|
|
|
WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Calling ResolveLinkInfo() to determine volume availability and root path.")));
|
|
|
|
bResult = ResolveLinkInfo(pvol->pli, pszVolumeRootPathBuf,
|
|
pvol->pvlParent->dwFlags,
|
|
pvol->pvlParent->hwndOwner, &dwOutFlags,
|
|
&pliUpdated);
|
|
|
|
if (bResult)
|
|
{
|
|
pvol->vs = VS_AVAILABLE;
|
|
|
|
if (IS_FLAG_SET(dwOutFlags, RLI_OFL_UPDATED))
|
|
{
|
|
PLINKINFO pliUpdatedCopy;
|
|
|
|
ASSERT(IS_FLAG_SET(pvol->pvlParent->dwFlags, RLI_IFL_UPDATE));
|
|
|
|
if (CopyLinkInfo(pliUpdated, &pliUpdatedCopy))
|
|
{
|
|
FreeMemory(pvol->pli);
|
|
pvol->pli = pliUpdatedCopy;
|
|
}
|
|
|
|
DestroyLinkInfo(pliUpdated);
|
|
|
|
WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Updating LinkInfo for volume %s."),
|
|
pszVolumeRootPathBuf));
|
|
}
|
|
|
|
if (IS_FLAG_SET(dwOutFlags, RLI_OFL_DISCONNECT))
|
|
{
|
|
SET_FLAG(pvol->dwFlags, VOLUME_FL_DISCONNECT);
|
|
|
|
WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Volume %s must be disconnected when finished."),
|
|
pszVolumeRootPathBuf));
|
|
}
|
|
|
|
TRACE_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Volume %s is available."),
|
|
pszVolumeRootPathBuf));
|
|
}
|
|
else
|
|
ASSERT(GetLastError() != ERROR_INVALID_PARAMETER);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(pvol->vs == VS_UNAVAILABLE);
|
|
bResult = FALSE;
|
|
}
|
|
|
|
if (! bResult)
|
|
{
|
|
pvol->vs = VS_UNAVAILABLE;
|
|
|
|
if (GetLastError() == ERROR_CANCELLED)
|
|
{
|
|
ASSERT(IS_FLAG_SET(pvol->pvlParent->dwFlags, RLI_IFL_CONNECT));
|
|
|
|
CLEAR_FLAG(pvol->pvlParent->dwFlags, RLI_IFL_CONNECT);
|
|
|
|
WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Connection attempt cancelled. No subsequent connections will be attempted.")));
|
|
}
|
|
|
|
GetUnavailableVolumeRootPath(pvol->pli, pszVolumeRootPathBuf);
|
|
|
|
WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Using %s as unavailable volume root path."),
|
|
pszVolumeRootPathBuf));
|
|
}
|
|
|
|
/* Add volume root path string to volume list's string table. */
|
|
|
|
if (IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID))
|
|
{
|
|
CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID);
|
|
DeleteString(pvol->hsRootPath);
|
|
}
|
|
|
|
if (AddString(pszVolumeRootPathBuf, pvol->pvlParent->hst, GetHashBucketIndex, &hsRootPath))
|
|
{
|
|
SET_FLAG(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID);
|
|
pvol->hsRootPath = hsRootPath;
|
|
}
|
|
else
|
|
WARNING_OUT((TEXT("ExpensiveResolveVolumeRootPath(): Unable to save %s as volume root path."),
|
|
pszVolumeRootPathBuf));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** ResolveVolumeRootPath()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE void ResolveVolumeRootPath(PVOLUME pvol,
|
|
LPTSTR pszVolumeRootPathBuf)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszVolumeRootPathBuf, STR, MAX_PATH_LEN));
|
|
|
|
/* Do we have a cached volume root path to use? */
|
|
|
|
if (IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID) &&
|
|
(IS_FLAG_CLEAR(pvol->dwFlags, VOLUME_FL_VERIFY_VOLUME) ||
|
|
(pvol->vs == VS_AVAILABLE &&
|
|
VerifyAvailableVolume(pvol))))
|
|
{
|
|
/* Yes. */
|
|
|
|
MyLStrCpyN(pszVolumeRootPathBuf, GetString(pvol->hsRootPath), MAX_PATH_LEN);
|
|
ASSERT(lstrlen(pszVolumeRootPathBuf) < MAX_PATH_LEN);
|
|
|
|
ASSERT(pvol->vs != VS_UNKNOWN);
|
|
}
|
|
else
|
|
/* No. Welcome in I/O City. */
|
|
ExpensiveResolveVolumeRootPath(pvol, pszVolumeRootPathBuf);
|
|
|
|
CLEAR_FLAG(pvol->dwFlags, VOLUME_FL_VERIFY_VOLUME);
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** VOLUMERESULTFromLastError()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE VOLUMERESULT VOLUMERESULTFromLastError(VOLUMERESULT vr)
|
|
{
|
|
switch (GetLastError())
|
|
{
|
|
case ERROR_OUTOFMEMORY:
|
|
vr = VR_OUT_OF_MEMORY;
|
|
break;
|
|
|
|
case ERROR_BAD_PATHNAME:
|
|
vr = VR_INVALID_PATH;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(vr);
|
|
}
|
|
|
|
|
|
/*
|
|
** WriteVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE TWINRESULT WriteVolume(HCACHEDFILE hcf, PVOLUME pvol)
|
|
{
|
|
TWINRESULT tr;
|
|
DBVOLUME dbvol;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvol, CVOLUME));
|
|
|
|
/* Write database volume followed by LinkInfo structure. */
|
|
|
|
dbvol.hvol = (HVOLUME)pvol;
|
|
|
|
if (WriteToCachedFile(hcf, (PCVOID)&dbvol, sizeof(dbvol), NULL) &&
|
|
WriteToCachedFile(hcf, pvol->pli, *(PDWORD)(pvol->pli), NULL))
|
|
tr = TR_SUCCESS;
|
|
else
|
|
tr = TR_BRIEFCASE_WRITE_FAILED;
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** ReadVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE TWINRESULT ReadVolume(HCACHEDFILE hcf, PVOLUMELIST pvl,
|
|
PLINKINFO pliBuf, UINT ucbLinkInfoBufLen,
|
|
HHANDLETRANS hhtVolumes)
|
|
{
|
|
TWINRESULT tr = TR_CORRUPT_BRIEFCASE;
|
|
DBVOLUME dbvol;
|
|
DWORD dwcbRead;
|
|
UINT ucbLinkInfoLen;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvl, CVOLUMELIST));
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pliBuf, LINKINFO, ucbLinkInfoBufLen));
|
|
ASSERT(IS_VALID_HANDLE(hhtVolumes, HANDLETRANS));
|
|
|
|
if (ReadFromCachedFile(hcf, &dbvol, sizeof(dbvol), &dwcbRead) &&
|
|
dwcbRead == sizeof(dbvol) &&
|
|
ReadFromCachedFile(hcf, &ucbLinkInfoLen, sizeof(ucbLinkInfoLen), &dwcbRead) &&
|
|
dwcbRead == sizeof(ucbLinkInfoLen) &&
|
|
ucbLinkInfoLen <= ucbLinkInfoBufLen)
|
|
{
|
|
/* Read the remainder of the LinkInfo structure into memory. */
|
|
|
|
DWORD dwcbRemainder;
|
|
|
|
pliBuf->ucbSize = ucbLinkInfoLen;
|
|
dwcbRemainder = ucbLinkInfoLen - sizeof(ucbLinkInfoLen);
|
|
|
|
if (ReadFromCachedFile(hcf, (PBYTE)pliBuf + sizeof(ucbLinkInfoLen),
|
|
dwcbRemainder, &dwcbRead) &&
|
|
dwcbRead == dwcbRemainder &&
|
|
IsValidLinkInfo(pliBuf))
|
|
{
|
|
PVOLUME pvol;
|
|
|
|
if (CreateVolume(pvl, pliBuf, &pvol))
|
|
{
|
|
/*
|
|
* To leave read volumes with 0 initial lock count, we must undo
|
|
* the LockVolume() performed by CreateVolume().
|
|
*/
|
|
|
|
UnlockVolume(pvol);
|
|
|
|
if (AddHandleToHandleTranslator(hhtVolumes,
|
|
(HGENERIC)(dbvol.hvol),
|
|
(HGENERIC)pvol))
|
|
tr = TR_SUCCESS;
|
|
else
|
|
{
|
|
UnlinkVolume(pvol);
|
|
DestroyVolume(pvol);
|
|
|
|
tr = TR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
tr = TR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
#if defined(DEBUG) || defined(VSTF)
|
|
|
|
/*
|
|
** IsValidPCVOLUMELIST()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidPCVOLUMELIST(PCVOLUMELIST pcvl)
|
|
{
|
|
return(IS_VALID_READ_PTR(pcvl, CVOLUMELIST) &&
|
|
IS_VALID_HANDLE(pcvl->hpa, PTRARRAY) &&
|
|
IS_VALID_HANDLE(pcvl->hst, STRINGTABLE) &&
|
|
FLAGS_ARE_VALID(pcvl->dwFlags, ALL_RLI_IFLAGS) &&
|
|
(IS_FLAG_CLEAR(pcvl->dwFlags, RLI_IFL_ALLOW_UI) ||
|
|
IS_VALID_HANDLE(pcvl->hwndOwner, WND)));
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidVOLUMESTATE()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidVOLUMESTATE(VOLUMESTATE vs)
|
|
{
|
|
BOOL bResult;
|
|
|
|
switch (vs)
|
|
{
|
|
case VS_UNKNOWN:
|
|
case VS_AVAILABLE:
|
|
case VS_UNAVAILABLE:
|
|
bResult = TRUE;
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT((TEXT("IsValidVOLUMESTATE(): Invalid VOLUMESTATE %d."),
|
|
vs));
|
|
bResult = FALSE;
|
|
break;
|
|
}
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidPCVOLUME()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidPCVOLUME(PCVOLUME pcvol)
|
|
{
|
|
return(IS_VALID_READ_PTR(pcvol, CVOLUME) &&
|
|
FLAGS_ARE_VALID(pcvol->dwFlags, ALL_VOLUME_FLAGS) &&
|
|
EVAL(IsValidVOLUMESTATE(pcvol->vs)) &&
|
|
IS_VALID_STRUCT_PTR(pcvol->pli, CLINKINFO) &&
|
|
(IS_FLAG_CLEAR(pcvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID) ||
|
|
IS_VALID_HANDLE(pcvol->hsRootPath, STRING)) &&
|
|
IS_VALID_STRUCT_PTR(pcvol->pvlParent, CVOLUMELIST));
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** IsValidPCVOLUMEDESC()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PRIVATE_CODE BOOL IsValidPCVOLUMEDESC(PCVOLUMEDESC pcvoldesc)
|
|
{
|
|
/*
|
|
* A set dwSerialNumber may be any value. An unset dwSerialNumber must be
|
|
* 0. A set strings may be any valid string. An unset string must be the
|
|
* empty string.
|
|
*/
|
|
|
|
return(IS_VALID_READ_PTR(pcvoldesc, CVOLUMEDESC) &&
|
|
EVAL(pcvoldesc->ulSize == sizeof(*pcvoldesc)) &&
|
|
FLAGS_ARE_VALID(pcvoldesc->dwFlags, ALL_VD_FLAGS) &&
|
|
(IS_FLAG_SET(pcvoldesc->dwFlags, VD_FL_SERIAL_NUMBER_VALID) ||
|
|
! pcvoldesc->dwSerialNumber) &&
|
|
((IS_FLAG_CLEAR(pcvoldesc->dwFlags, VD_FL_VOLUME_LABEL_VALID) &&
|
|
! pcvoldesc->rgchVolumeLabel[0]) ||
|
|
(IS_FLAG_SET(pcvoldesc->dwFlags, VD_FL_VOLUME_LABEL_VALID) &&
|
|
IS_VALID_STRING_PTR(pcvoldesc->rgchVolumeLabel, CSTR) &&
|
|
EVAL(lstrlen(pcvoldesc->rgchVolumeLabel) < ARRAYSIZE(pcvoldesc->rgchVolumeLabel)))) &&
|
|
((IS_FLAG_CLEAR(pcvoldesc->dwFlags, VD_FL_NET_RESOURCE_VALID) &&
|
|
! pcvoldesc->rgchNetResource[0]) ||
|
|
(IS_FLAG_SET(pcvoldesc->dwFlags, VD_FL_NET_RESOURCE_VALID) &&
|
|
IS_VALID_STRING_PTR(pcvoldesc->rgchNetResource, CSTR) &&
|
|
EVAL(lstrlen(pcvoldesc->rgchNetResource) < ARRAYSIZE(pcvoldesc->rgchNetResource)))));
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/****************************** Public Functions *****************************/
|
|
|
|
|
|
/*
|
|
** CreateVolumeList()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL CreateVolumeList(DWORD dwFlags, HWND hwndOwner,
|
|
PHVOLUMELIST phvl)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
PVOLUMELIST pvl;
|
|
|
|
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(phvl, HVOLUMELIST));
|
|
|
|
if (AllocateMemory(sizeof(*pvl), &pvl))
|
|
{
|
|
NEWSTRINGTABLE nszt;
|
|
|
|
/* Create string table for volume root path strngs. */
|
|
|
|
nszt.hbc = NUM_VOLUME_HASH_BUCKETS;
|
|
|
|
if (CreateStringTable(&nszt, &(pvl->hst)))
|
|
{
|
|
NEWPTRARRAY npa;
|
|
|
|
/* Create pointer array of volumes. */
|
|
|
|
npa.aicInitialPtrs = NUM_START_VOLUMES;
|
|
npa.aicAllocGranularity = NUM_VOLUMES_TO_ADD;
|
|
npa.dwFlags = NPA_FL_SORTED_ADD;
|
|
|
|
if (CreatePtrArray(&npa, &(pvl->hpa)))
|
|
{
|
|
pvl->dwFlags = dwFlags;
|
|
pvl->hwndOwner = hwndOwner;
|
|
|
|
*phvl = (HVOLUMELIST)pvl;
|
|
bResult = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DestroyStringTable(pvl->hst);
|
|
CREATEVOLUMELIST_BAIL:
|
|
FreeMemory(pvl);
|
|
}
|
|
}
|
|
else
|
|
goto CREATEVOLUMELIST_BAIL;
|
|
}
|
|
|
|
ASSERT(! bResult ||
|
|
IS_VALID_HANDLE(*phvl, VOLUMELIST));
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** DestroyVolumeList()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void DestroyVolumeList(HVOLUMELIST hvl)
|
|
{
|
|
ARRAYINDEX aicPtrs;
|
|
ARRAYINDEX ai;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
|
|
|
|
/* First free all volumes in array. */
|
|
|
|
aicPtrs = GetPtrCount(((PCVOLUMELIST)hvl)->hpa);
|
|
|
|
for (ai = 0; ai < aicPtrs; ai++)
|
|
DestroyVolume(GetPtr(((PCVOLUMELIST)hvl)->hpa, ai));
|
|
|
|
/* Now wipe out the array. */
|
|
|
|
DestroyPtrArray(((PCVOLUMELIST)hvl)->hpa);
|
|
|
|
ASSERT(! GetStringCount(((PCVOLUMELIST)hvl)->hst));
|
|
DestroyStringTable(((PCVOLUMELIST)hvl)->hst);
|
|
|
|
FreeMemory((PVOLUMELIST)hvl);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** InvalidateVolumeListInfo()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void InvalidateVolumeListInfo(HVOLUMELIST hvl)
|
|
{
|
|
ARRAYINDEX aicPtrs;
|
|
ARRAYINDEX ai;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
|
|
|
|
aicPtrs = GetPtrCount(((PCVOLUMELIST)hvl)->hpa);
|
|
|
|
for (ai = 0; ai < aicPtrs; ai++)
|
|
InvalidateVolumeInfo(GetPtr(((PCVOLUMELIST)hvl)->hpa, ai));
|
|
|
|
WARNING_OUT((TEXT("InvalidateVolumeListInfo(): Volume cache invalidated.")));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** ClearVolumeListInfo()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void ClearVolumeListInfo(HVOLUMELIST hvl)
|
|
{
|
|
ARRAYINDEX aicPtrs;
|
|
ARRAYINDEX ai;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
|
|
|
|
aicPtrs = GetPtrCount(((PCVOLUMELIST)hvl)->hpa);
|
|
|
|
for (ai = 0; ai < aicPtrs; ai++)
|
|
ClearVolumeInfo(GetPtr(((PCVOLUMELIST)hvl)->hpa, ai));
|
|
|
|
WARNING_OUT((TEXT("ClearVolumeListInfo(): Volume cache cleared.")));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** AddVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE VOLUMERESULT AddVolume(HVOLUMELIST hvl, LPCTSTR pcszPath,
|
|
PHVOLUME phvol, LPTSTR pszPathSuffixBuf)
|
|
{
|
|
VOLUMERESULT vr;
|
|
TCHAR rgchPath[MAX_PATH_LEN];
|
|
LPTSTR pszFileName;
|
|
DWORD dwPathLen;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
|
|
ASSERT(IS_VALID_STRING_PTR(pcszPath, CSTR));
|
|
ASSERT(IS_VALID_WRITE_PTR(phvol, HVOLUME));
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszPathSuffixBuf, STR, MAX_PATH_LEN));
|
|
|
|
dwPathLen = GetFullPathName(pcszPath, ARRAYSIZE(rgchPath), rgchPath,
|
|
&pszFileName);
|
|
|
|
if (dwPathLen > 0 && dwPathLen < ARRAYSIZE(rgchPath))
|
|
{
|
|
ARRAYINDEX aiFound;
|
|
|
|
/* Does a volume for this root path already exist? */
|
|
|
|
if (LinearSearchArray(((PVOLUMELIST)hvl)->hpa,
|
|
&SearchForVolumeByRootPathCmp, rgchPath,
|
|
&aiFound))
|
|
{
|
|
PVOLUME pvol;
|
|
LPCTSTR pcszVolumeRootPath;
|
|
|
|
/* Yes. */
|
|
|
|
pvol = GetPtr(((PVOLUMELIST)hvl)->hpa, aiFound);
|
|
|
|
LockVolume(pvol);
|
|
|
|
ASSERT(pvol->vs == VS_AVAILABLE &&
|
|
IS_FLAG_SET(pvol->dwFlags, VOLUME_FL_ROOT_PATH_VALID));
|
|
|
|
pcszVolumeRootPath = GetString(pvol->hsRootPath);
|
|
|
|
ASSERT(lstrlen(pcszVolumeRootPath) <= lstrlen(rgchPath));
|
|
|
|
lstrcpy(pszPathSuffixBuf, rgchPath + lstrlen(pcszVolumeRootPath));
|
|
|
|
*phvol = (HVOLUME)pvol;
|
|
vr = VR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwOutFlags;
|
|
TCHAR rgchNetResource[MAX_PATH_LEN];
|
|
LPTSTR pszRootPathSuffix;
|
|
|
|
/* No. Create a new volume. */
|
|
|
|
if (GetCanonicalPathInfo(pcszPath, rgchPath, &dwOutFlags,
|
|
rgchNetResource, &pszRootPathSuffix))
|
|
{
|
|
PLINKINFO pli;
|
|
|
|
lstrcpy(pszPathSuffixBuf, pszRootPathSuffix);
|
|
*pszRootPathSuffix = TEXT('\0');
|
|
|
|
WARNING_OUT((TEXT("AddVolume(): Creating LinkInfo for root path %s."),
|
|
rgchPath));
|
|
|
|
if (CreateLinkInfo(rgchPath, &pli))
|
|
{
|
|
PVOLUME pvol;
|
|
|
|
if (CreateVolume((PVOLUMELIST)hvl, pli, &pvol))
|
|
{
|
|
TCHAR rgchUnusedVolumeRootPath[MAX_PATH_LEN];
|
|
|
|
ResolveVolumeRootPath(pvol, rgchUnusedVolumeRootPath);
|
|
|
|
*phvol = (HVOLUME)pvol;
|
|
vr = VR_SUCCESS;
|
|
}
|
|
else
|
|
vr = VR_OUT_OF_MEMORY;
|
|
|
|
DestroyLinkInfo(pli);
|
|
}
|
|
else
|
|
/*
|
|
* Differentiate between VR_UNAVAILABLE_VOLUME and
|
|
* VR_OUT_OF_MEMORY.
|
|
*/
|
|
vr = VOLUMERESULTFromLastError(VR_UNAVAILABLE_VOLUME);
|
|
}
|
|
else
|
|
vr = VOLUMERESULTFromLastError(VR_INVALID_PATH);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(! dwPathLen);
|
|
|
|
vr = VOLUMERESULTFromLastError(VR_INVALID_PATH);
|
|
}
|
|
|
|
ASSERT(vr != VR_SUCCESS ||
|
|
(IS_VALID_HANDLE(*phvol, VOLUME) &&
|
|
EVAL(IsValidPathSuffix(pszPathSuffixBuf))));
|
|
|
|
return(vr);
|
|
}
|
|
|
|
|
|
/*
|
|
** DeleteVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void DeleteVolume(HVOLUME hvol)
|
|
{
|
|
ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
|
|
|
|
if (! UnlockVolume((PVOLUME)hvol))
|
|
{
|
|
UnlinkVolume((PVOLUME)hvol);
|
|
DestroyVolume((PVOLUME)hvol);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** CompareVolumes()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE COMPARISONRESULT CompareVolumes(HVOLUME hvolFirst,
|
|
HVOLUME hvolSecond)
|
|
{
|
|
ASSERT(IS_VALID_HANDLE(hvolFirst, VOLUME));
|
|
ASSERT(IS_VALID_HANDLE(hvolSecond, VOLUME));
|
|
|
|
/* This comparison works across volume lists. */
|
|
|
|
return(CompareLinkInfoVolumes(((PCVOLUME)hvolFirst)->pli,
|
|
((PCVOLUME)hvolSecond)->pli));
|
|
}
|
|
|
|
|
|
/*
|
|
** CopyVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL CopyVolume(HVOLUME hvolSrc, HVOLUMELIST hvlDest,
|
|
PHVOLUME phvolCopy)
|
|
{
|
|
BOOL bResult;
|
|
PVOLUME pvol;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hvolSrc, VOLUME));
|
|
ASSERT(IS_VALID_HANDLE(hvlDest, VOLUMELIST));
|
|
ASSERT(IS_VALID_WRITE_PTR(phvolCopy, HVOLUME));
|
|
|
|
/* Is the destination volume list the source volume's volume list? */
|
|
|
|
if (((PCVOLUME)hvolSrc)->pvlParent == (PCVOLUMELIST)hvlDest)
|
|
{
|
|
/* Yes. Use the source volume. */
|
|
|
|
LockVolume((PVOLUME)hvolSrc);
|
|
pvol = (PVOLUME)hvolSrc;
|
|
bResult = TRUE;
|
|
}
|
|
else
|
|
bResult = CreateVolume((PVOLUMELIST)hvlDest, ((PCVOLUME)hvolSrc)->pli,
|
|
&pvol);
|
|
|
|
if (bResult)
|
|
*phvolCopy = (HVOLUME)pvol;
|
|
|
|
ASSERT(! bResult ||
|
|
IS_VALID_HANDLE(*phvolCopy, VOLUME));
|
|
|
|
return(bResult);
|
|
}
|
|
|
|
|
|
/*
|
|
** IsVolumeAvailable()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL IsVolumeAvailable(HVOLUME hvol)
|
|
{
|
|
TCHAR rgchUnusedVolumeRootPath[MAX_PATH_LEN];
|
|
|
|
ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
|
|
|
|
ResolveVolumeRootPath((PVOLUME)hvol, rgchUnusedVolumeRootPath);
|
|
|
|
ASSERT(IsValidVOLUMESTATE(((PCVOLUME)hvol)->vs) &&
|
|
((PCVOLUME)hvol)->vs != VS_UNKNOWN);
|
|
|
|
return(((PCVOLUME)hvol)->vs == VS_AVAILABLE);
|
|
}
|
|
|
|
|
|
/*
|
|
** GetVolumeRootPath()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void GetVolumeRootPath(HVOLUME hvol, LPTSTR pszRootPathBuf)
|
|
{
|
|
ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszRootPathBuf, STR, MAX_PATH_LEN));
|
|
|
|
ResolveVolumeRootPath((PVOLUME)hvol, pszRootPathBuf);
|
|
|
|
ASSERT(IsRootPath(pszRootPathBuf));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
** DebugGetVolumeRootPath()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
**
|
|
** N.b., DebugGetVolumeRootPath() must be non-intrusive.
|
|
*/
|
|
PUBLIC_CODE LPTSTR DebugGetVolumeRootPath(HVOLUME hvol, LPTSTR pszRootPathBuf)
|
|
{
|
|
ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
|
|
ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszRootPathBuf, STR, MAX_PATH_LEN));
|
|
|
|
if (IS_FLAG_SET(((PVOLUME)hvol)->dwFlags, VOLUME_FL_ROOT_PATH_VALID))
|
|
MyLStrCpyN(pszRootPathBuf, GetString(((PVOLUME)hvol)->hsRootPath), MAX_PATH_LEN);
|
|
else
|
|
GetUnavailableVolumeRootPath(((PVOLUME)hvol)->pli, pszRootPathBuf);
|
|
|
|
ASSERT(IsRootPath(pszRootPathBuf));
|
|
|
|
return(pszRootPathBuf);
|
|
}
|
|
|
|
|
|
/*
|
|
** GetVolumeCount()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE ULONG GetVolumeCount(HVOLUMELIST hvl)
|
|
{
|
|
ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
|
|
|
|
return(GetPtrCount(((PCVOLUMELIST)hvl)->hpa));
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
** DescribeVolume()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void DescribeVolume(HVOLUME hvol, PVOLUMEDESC pvoldesc)
|
|
{
|
|
PCVOID pcv;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hvol, VOLUME));
|
|
ASSERT(IS_VALID_WRITE_PTR(pvoldesc, VOLUMEDESC));
|
|
|
|
ASSERT(pvoldesc->ulSize == sizeof(*pvoldesc));
|
|
|
|
pvoldesc->dwFlags = 0;
|
|
|
|
if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_VOLUME_SERIAL_NUMBER, &pcv))
|
|
{
|
|
pvoldesc->dwSerialNumber = *(PCDWORD)pcv;
|
|
SET_FLAG(pvoldesc->dwFlags, VD_FL_SERIAL_NUMBER_VALID);
|
|
}
|
|
else
|
|
pvoldesc->dwSerialNumber = 0;
|
|
|
|
if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_VOLUME_LABELW, &pcv) && pcv)
|
|
{
|
|
lstrcpy(pvoldesc->rgchVolumeLabel, pcv);
|
|
SET_FLAG(pvoldesc->dwFlags, VD_FL_VOLUME_LABEL_VALID);
|
|
}
|
|
else if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_VOLUME_LABEL, &pcv) && pcv)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0, pcv, -1, pvoldesc->rgchVolumeLabel, MAX_PATH);
|
|
SET_FLAG(pvoldesc->dwFlags, VD_FL_VOLUME_LABEL_VALID);
|
|
}
|
|
else
|
|
{
|
|
pvoldesc->rgchVolumeLabel[0] = TEXT('\0');
|
|
}
|
|
|
|
if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_NET_RESOURCEW, &pcv) && pcv)
|
|
{
|
|
lstrcpy(pvoldesc->rgchNetResource, pcv);
|
|
SET_FLAG(pvoldesc->dwFlags, VD_FL_NET_RESOURCE_VALID);
|
|
}
|
|
else if (GetLinkInfoData(((PCVOLUME)hvol)->pli, LIDT_NET_RESOURCE, &pcv) && pcv)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0, pcv, -1, pvoldesc->rgchNetResource, MAX_PATH);
|
|
SET_FLAG(pvoldesc->dwFlags, VD_FL_NET_RESOURCE_VALID);
|
|
}
|
|
else
|
|
pvoldesc->rgchNetResource[0] = TEXT('\0');
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pvoldesc, CVOLUMEDESC));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** WriteVolumeList()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE TWINRESULT WriteVolumeList(HCACHEDFILE hcf, HVOLUMELIST hvl)
|
|
{
|
|
TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED;
|
|
DWORD dwcbDBVolumeListHeaderOffset;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
|
|
ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
|
|
|
|
/* Save initial file position. */
|
|
|
|
dwcbDBVolumeListHeaderOffset = GetCachedFilePointerPosition(hcf);
|
|
|
|
if (dwcbDBVolumeListHeaderOffset != INVALID_SEEK_POSITION)
|
|
{
|
|
DBVOLUMELISTHEADER dbvlh;
|
|
|
|
/* Leave space for volume list header. */
|
|
|
|
ZeroMemory(&dbvlh, sizeof(dbvlh));
|
|
|
|
if (WriteToCachedFile(hcf, (PCVOID)&dbvlh, sizeof(dbvlh), NULL))
|
|
{
|
|
ARRAYINDEX aicPtrs;
|
|
ARRAYINDEX ai;
|
|
UINT ucbMaxLinkInfoLen = 0;
|
|
LONG lcVolumes = 0;
|
|
|
|
tr = TR_SUCCESS;
|
|
|
|
aicPtrs = GetPtrCount(((PCVOLUMELIST)hvl)->hpa);
|
|
|
|
/* Write all volumes. */
|
|
|
|
for (ai = 0; ai < aicPtrs; ai++)
|
|
{
|
|
PVOLUME pvol;
|
|
|
|
pvol = GetPtr(((PCVOLUMELIST)hvl)->hpa, ai);
|
|
|
|
/*
|
|
* As a sanity check, don't save any volume with a lock count of 0.
|
|
* A 0 lock count implies that the volume has not been referenced
|
|
* since it was restored from the database, or something is broken.
|
|
*/
|
|
|
|
if (pvol->ulcLock > 0)
|
|
{
|
|
tr = WriteVolume(hcf, pvol);
|
|
|
|
if (tr == TR_SUCCESS)
|
|
{
|
|
ASSERT(lcVolumes < LONG_MAX);
|
|
lcVolumes++;
|
|
|
|
if (pvol->pli->ucbSize > ucbMaxLinkInfoLen)
|
|
ucbMaxLinkInfoLen = pvol->pli->ucbSize;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
ERROR_OUT((TEXT("WriteVolumeList(): VOLUME has 0 lock count and will not be written.")));
|
|
}
|
|
|
|
/* Save volume list header. */
|
|
|
|
if (tr == TR_SUCCESS)
|
|
{
|
|
dbvlh.lcVolumes = lcVolumes;
|
|
dbvlh.ucbMaxLinkInfoLen = ucbMaxLinkInfoLen;
|
|
|
|
tr = WriteDBSegmentHeader(hcf, dwcbDBVolumeListHeaderOffset,
|
|
&dbvlh, sizeof(dbvlh));
|
|
|
|
TRACE_OUT((TEXT("WriteVolumeList(): Wrote %ld volumes; maximum LinkInfo length %u bytes."),
|
|
dbvlh.lcVolumes,
|
|
dbvlh.ucbMaxLinkInfoLen));
|
|
}
|
|
}
|
|
}
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** ReadVolumeList()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE TWINRESULT ReadVolumeList(HCACHEDFILE hcf, HVOLUMELIST hvl,
|
|
PHHANDLETRANS phht)
|
|
{
|
|
TWINRESULT tr;
|
|
DBVOLUMELISTHEADER dbvlh;
|
|
DWORD dwcbRead;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE));
|
|
ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
|
|
ASSERT(IS_VALID_WRITE_PTR(phht, HHANDLETRANS));
|
|
|
|
if (ReadFromCachedFile(hcf, &dbvlh, sizeof(dbvlh), &dwcbRead) &&
|
|
dwcbRead == sizeof(dbvlh))
|
|
{
|
|
HHANDLETRANS hht;
|
|
|
|
tr = TR_OUT_OF_MEMORY;
|
|
|
|
if (CreateHandleTranslator(dbvlh.lcVolumes, &hht))
|
|
{
|
|
PLINKINFO pliBuf;
|
|
|
|
if (AllocateMemory(dbvlh.ucbMaxLinkInfoLen, &pliBuf))
|
|
{
|
|
LONG l;
|
|
|
|
tr = TR_SUCCESS;
|
|
|
|
TRACE_OUT((TEXT("ReadPathList(): Reading %ld volumes; maximum LinkInfo length %u bytes."),
|
|
dbvlh.lcVolumes,
|
|
dbvlh.ucbMaxLinkInfoLen));
|
|
|
|
for (l = 0; l < dbvlh.lcVolumes; l++)
|
|
{
|
|
tr = ReadVolume(hcf, (PVOLUMELIST)hvl, pliBuf,
|
|
dbvlh.ucbMaxLinkInfoLen, hht);
|
|
|
|
if (tr != TR_SUCCESS)
|
|
break;
|
|
}
|
|
|
|
if (tr == TR_SUCCESS)
|
|
{
|
|
PrepareForHandleTranslation(hht);
|
|
*phht = hht;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hvl, VOLUMELIST));
|
|
ASSERT(IS_VALID_HANDLE(*phht, HANDLETRANS));
|
|
}
|
|
else
|
|
DestroyHandleTranslator(hht);
|
|
|
|
FreeMemory(pliBuf);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
tr = TR_CORRUPT_BRIEFCASE;
|
|
|
|
ASSERT(tr != TR_SUCCESS ||
|
|
(IS_VALID_HANDLE(hvl, VOLUMELIST) &&
|
|
IS_VALID_HANDLE(*phht, HANDLETRANS)));
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/*
|
|
** IsValidHVOLUME()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL IsValidHVOLUME(HVOLUME hvol)
|
|
{
|
|
return(IS_VALID_STRUCT_PTR((PCVOLUME)hvol, CVOLUME));
|
|
}
|
|
|
|
|
|
#if defined(DEBUG) || defined(VSTF)
|
|
|
|
/*
|
|
** IsValidHVOLUMELIST()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE BOOL IsValidHVOLUMELIST(HVOLUMELIST hvl)
|
|
{
|
|
return(IS_VALID_STRUCT_PTR((PCVOLUMELIST)hvl, CVOLUMELIST));
|
|
}
|
|
|
|
#endif
|
|
|