442 lines
13 KiB
C
442 lines
13 KiB
C
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1993-1994
|
|
//
|
|
// File: cbs.c
|
|
//
|
|
// This files contains code for the cached briefcase structs
|
|
//
|
|
// History:
|
|
// 09-02-93 ScottH Created
|
|
// 01-31-94 ScottH Moved from cache.c
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
///////////////////////////////////////////////////// INCLUDES
|
|
|
|
#include "brfprv.h" // common headers
|
|
#include "res.h"
|
|
|
|
|
|
CACHE g_cacheCBS = {0, 0, 0}; // Briefcase structure cache
|
|
|
|
#define CBS_EnterCS() EnterCriticalSection(&g_cacheCBS.cs)
|
|
#define CBS_LeaveCS() LeaveCriticalSection(&g_cacheCBS.cs)
|
|
|
|
SETbl const c_rgseOpenBriefcase[] = {
|
|
{ E_TR_OUT_OF_MEMORY, IDS_OOM_OPENBRIEFCASE, MB_ERROR },
|
|
{ E_OUTOFMEMORY, IDS_OOM_OPENBRIEFCASE, MB_ERROR },
|
|
{ E_TR_BRIEFCASE_LOCKED, IDS_ERR_BRIEFCASE_LOCKED, MB_WARNING },
|
|
{ E_TR_BRIEFCASE_OPEN_FAILED, IDS_ERR_OPEN_ACCESS_DENIED, MB_WARNING },
|
|
{ E_TR_NEWER_BRIEFCASE, IDS_ERR_NEWER_BRIEFCASE, MB_INFO },
|
|
{ E_TR_SUBTREE_CYCLE_FOUND, IDS_ERR_OPEN_SUBTREECYCLE, MB_WARNING },
|
|
};
|
|
|
|
|
|
#ifdef DEBUG
|
|
void PRIVATE CBS_DumpEntry(
|
|
CBS * pcbs)
|
|
{
|
|
ASSERT(pcbs);
|
|
|
|
TRACE_MSG(TF_ALWAYS, TEXT("CBS: Atom %d: %s"), pcbs->atomBrf, Atom_GetName(pcbs->atomBrf));
|
|
TRACE_MSG(TF_ALWAYS, TEXT(" Ref [%u] Hbrf = %lx "),
|
|
Cache_GetRefCount(&g_cacheCBS, pcbs->atomBrf),
|
|
pcbs->hbrf);
|
|
}
|
|
|
|
|
|
void PUBLIC CBS_DumpAll()
|
|
{
|
|
CBS * pcbs;
|
|
int atom;
|
|
BOOL bDump;
|
|
|
|
ENTEREXCLUSIVE();
|
|
{
|
|
bDump = IsFlagSet(g_uDumpFlags, DF_CBS);
|
|
}
|
|
LEAVEEXCLUSIVE();
|
|
|
|
if (!bDump)
|
|
return ;
|
|
|
|
atom = Cache_FindFirstKey(&g_cacheCBS);
|
|
while (atom != ATOM_ERR)
|
|
{
|
|
pcbs = Cache_GetPtr(&g_cacheCBS, atom);
|
|
ASSERT(pcbs);
|
|
if (pcbs)
|
|
{
|
|
CBS_DumpEntry(pcbs);
|
|
CBS_Delete(atom, NULL); // Decrement count
|
|
}
|
|
|
|
atom = Cache_FindNextKey(&g_cacheCBS, atom);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Save and close the briefcase.
|
|
Returns: --
|
|
Cond:
|
|
This function is serialized by the caller (Cache_Term or
|
|
Cache_DeleteItem).
|
|
*/
|
|
void CALLBACK CBS_Free(
|
|
LPVOID lpv,
|
|
HWND hwndOwner)
|
|
{
|
|
HBRFCASE hbrf;
|
|
CBS * pcbs = (CBS *)lpv;
|
|
CRL * pcrl;
|
|
int atomPath = pcbs->atomBrf;
|
|
int atom;
|
|
TWINRESULT tr1;
|
|
TWINRESULT tr2;
|
|
DECLAREHOURGLASS;
|
|
|
|
hbrf = pcbs->hbrf;
|
|
|
|
// Save the briefcase with the same name it was opened
|
|
//
|
|
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Saving and closing Briefcase %s (0x%lx)"),
|
|
Atom_GetName(atomPath), hbrf); )
|
|
|
|
// Search thru the CRL cache for entries
|
|
// sharing the same partial path as this briefcase
|
|
// and nuke them.
|
|
//
|
|
atom = Cache_FindFirstKey(&g_cacheCRL);
|
|
while (atom != ATOM_ERR)
|
|
{
|
|
pcrl = Cache_GetPtr(&g_cacheCRL, atom);
|
|
ASSERT(pcrl);
|
|
|
|
if (pcrl)
|
|
{
|
|
if (hbrf == pcrl->hbrf)
|
|
{
|
|
// This atomKey belongs to this briefcase. Nuke it.
|
|
//
|
|
DEBUG_CODE( TRACE_MSG(TF_CACHE, TEXT("CACHE Nuking CRL %d"), atom); )
|
|
CRL_Nuke(atom);
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
DEBUG_CODE( TRACE_MSG(TF_CACHE, TEXT("CACHE NOT Nuking CRL %d"), atom); )
|
|
#endif
|
|
|
|
Cache_DeleteItem(&g_cacheCRL, atom, FALSE, hwndOwner, CRL_Free); // Decrement count
|
|
}
|
|
|
|
atom = Cache_FindNextKey(&g_cacheCRL, atom);
|
|
}
|
|
|
|
// Save the briefcase. We normally (re)specify the database
|
|
// pathname to handle the rename case. However, if the
|
|
// move bit has been set, then we use the NULL parameter
|
|
// (save under current name) because we will depend on the
|
|
// shell to move the database.
|
|
//
|
|
ASSERT(Sync_IsEngineLoaded());
|
|
|
|
// First check if the disk is available. If it isn't, Windows will
|
|
// blue-screen because we cannot close the database file. So before
|
|
// that happens, bring up a friendlier retry messagebox.
|
|
RETRY_BEGIN(FALSE)
|
|
{
|
|
// Is disk unavailable?
|
|
if ( !PathExists(Atom_GetName(atomPath)) )
|
|
{
|
|
// Yes; ask user to retry/cancel
|
|
int id = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_CLOSE_UNAVAIL_VOL),
|
|
MAKEINTRESOURCE(IDS_CAP_SAVE), NULL, MB_RETRYCANCEL | MB_ICONWARNING);
|
|
if (IDRETRY == id)
|
|
RETRY_SET();
|
|
}
|
|
}
|
|
RETRY_END()
|
|
|
|
SetHourglass();
|
|
tr1 = Sync_SaveBriefcase(pcbs->hbrf);
|
|
tr2 = Sync_CloseBriefcase(pcbs->hbrf);
|
|
if (TR_SUCCESS != tr1 || TR_SUCCESS != tr2)
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
|
|
switch (dwError)
|
|
{
|
|
case ERROR_ACCESS_DENIED:
|
|
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_SAVE_UNAVAIL_VOL),
|
|
MAKEINTRESOURCE(IDS_CAP_SAVE), NULL, MB_ERROR);
|
|
break;
|
|
|
|
default:
|
|
if (TR_BRIEFCASE_WRITE_FAILED == tr1 || TR_BRIEFCASE_WRITE_FAILED == tr2)
|
|
{
|
|
LPTSTR psz;
|
|
|
|
static UINT rgids[2] = { IDS_ERR_1_FullDiskSave, IDS_ERR_2_FullDiskSave };
|
|
|
|
if (FmtString(&psz, IDS_ERR_F_FullDiskSave, rgids, ARRAYSIZE(rgids)))
|
|
{
|
|
MsgBox(hwndOwner, psz, MAKEINTRESOURCE(IDS_CAP_SAVE), NULL, MB_ERROR);
|
|
GFree(psz);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
ResetHourglass();
|
|
|
|
AbortEvt_Free(pcbs->pabortevt);
|
|
|
|
SharedFree(&pcbs);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Actually opens the briefcase and adds the briefcase
|
|
handle to the given CBS struct.
|
|
|
|
Returns: standard hresult
|
|
Cond: --
|
|
*/
|
|
HRESULT PRIVATE OpenTheBriefcase(
|
|
LPCTSTR pszDatPath,
|
|
int atomPath,
|
|
CBS * pcbs,
|
|
HWND hwndOwner)
|
|
{
|
|
HRESULT hres;
|
|
TWINRESULT tr;
|
|
BOOL bRet = FALSE;
|
|
DWORD dwFlags = OB_FL_OPEN_DATABASE | OB_FL_TRANSLATE_DB_FOLDER | OB_FL_ALLOW_UI;
|
|
int nDrive;
|
|
int nDriveType;
|
|
|
|
// Determine if we want to record the existence of this briefcase.
|
|
// We don't care about briefcases on remote or floppy drives.
|
|
nDrive = PathGetDriveNumber(pszDatPath);
|
|
|
|
// Record this briefcase?
|
|
nDriveType = DriveType(nDrive);
|
|
if (DRIVE_CDROM != nDriveType && DRIVE_REMOVABLE != nDriveType &&
|
|
DRIVE_RAMDRIVE != nDriveType &&
|
|
!PathIsUNC(pszDatPath) && !IsNetDrive(nDrive))
|
|
{
|
|
// Yes
|
|
SetFlag(dwFlags, OB_FL_LIST_DATABASE);
|
|
|
|
TRACE_MSG(TF_GENERAL, TEXT("Remembering briefcase %s"), pszDatPath);
|
|
}
|
|
|
|
RETRY_BEGIN(FALSE)
|
|
{
|
|
tr = Sync_OpenBriefcase(pszDatPath, dwFlags, GetDesktopWindow(), &pcbs->hbrf);
|
|
hres = HRESULT_FROM_TR(tr);
|
|
|
|
// Unavailable disk?
|
|
if (FAILED(hres))
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
|
|
if (ERROR_INVALID_DATA == dwError || ERROR_ACCESS_DENIED == dwError)
|
|
{
|
|
// Yes; ask user to retry/cancel
|
|
int id = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_OPEN_UNAVAIL_VOL),
|
|
MAKEINTRESOURCE(IDS_CAP_OPEN), NULL, MB_RETRYCANCEL | MB_ICONWARNING);
|
|
|
|
// Set specific error value
|
|
hres = E_TR_UNAVAILABLE_VOLUME;
|
|
|
|
if (IDRETRY == id)
|
|
{
|
|
RETRY_SET(); // Try again
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RETRY_END()
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (!Cache_AddItem(&g_cacheCBS, atomPath, (LPVOID)pcbs))
|
|
{
|
|
Sync_CloseBriefcase(pcbs->hbrf);
|
|
hres = ResultFromScode(E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: This function handles the case when the engine fails
|
|
to open the database because the database file is
|
|
corrupt.
|
|
|
|
Returns: standard hresult
|
|
Cond: --
|
|
*/
|
|
HRESULT PRIVATE HandleCorruptDatabase(
|
|
CBS * pcbs,
|
|
int atomPath,
|
|
LPCTSTR pszDatPath, // Path of database file
|
|
HWND hwndOwner)
|
|
{
|
|
TCHAR szTemplate[MAXPATHLEN];
|
|
TCHAR szNewFile[MAXPATHLEN];
|
|
LPTSTR pszNewPath = szTemplate;
|
|
LPCTSTR pszPath = Atom_GetName(atomPath);
|
|
LPTSTR psz;
|
|
DWORD dwAttr;
|
|
|
|
static UINT rgids[2] = { IDS_ERR_1_CorruptDB, IDS_ERR_2_CorruptDB };
|
|
|
|
ASSERT(pszPath);
|
|
|
|
// Create the new database name
|
|
//
|
|
SzFromIDS(IDS_BOGUSDBTEMPLATE, szTemplate, ARRAYSIZE(szTemplate));
|
|
PathMakeUniqueName(szNewFile, ARRAYSIZE(szNewFile), TEXT("badbc.dat"), szTemplate,
|
|
pszPath);
|
|
lstrcpy(pszNewPath, pszPath);
|
|
PathAppend(pszNewPath, szNewFile);
|
|
|
|
// Move the database
|
|
//
|
|
MoveFile(pszDatPath, pszNewPath);
|
|
|
|
// Unhide the corrupt database
|
|
//
|
|
dwAttr = GetFileAttributes(pszNewPath);
|
|
if (dwAttr != 0xFFFFFFFF)
|
|
{
|
|
ClearFlag(dwAttr, FILE_ATTRIBUTE_HIDDEN);
|
|
SetFileAttributes(pszNewPath, dwAttr);
|
|
}
|
|
|
|
if (FmtString(&psz, IDS_ERR_F_CorruptDB, rgids, ARRAYSIZE(rgids)))
|
|
{
|
|
MsgBox(hwndOwner, psz, MAKEINTRESOURCE(IDS_CAP_OPEN), NULL, MB_ERROR);
|
|
GFree(psz);
|
|
}
|
|
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Renaming corrupt database to %s"), pszNewPath); )
|
|
|
|
// Retry opening...
|
|
//
|
|
return OpenTheBriefcase(pszDatPath, atomPath, pcbs, hwndOwner);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Purpose: Add the atomPath to the cache. We open the briefcase
|
|
database if it needs opening. If atomPath is already
|
|
in the cache, simply return the pointer to the entry.
|
|
|
|
Returns: standard hresult
|
|
|
|
Cond: Must call CBS_Delete for every call to this function
|
|
*/
|
|
HRESULT PUBLIC CBS_Add(
|
|
PCBS * ppcbs,
|
|
int atomPath,
|
|
HWND hwndOwner)
|
|
{
|
|
HRESULT hres = NOERROR;
|
|
TCHAR szDatPath[MAXPATHLEN];
|
|
CBS * pcbs;
|
|
|
|
CBS_EnterCS();
|
|
{
|
|
pcbs = Cache_GetPtr(&g_cacheCBS, atomPath);
|
|
if (NULL == pcbs)
|
|
{
|
|
// Allocate using commctrl's Alloc, so the structure will be in
|
|
// shared heap space across processes.
|
|
pcbs = SharedAllocType(CBS);
|
|
if (NULL == pcbs)
|
|
{
|
|
hres = ResultFromScode(E_OUTOFMEMORY);
|
|
}
|
|
else
|
|
{
|
|
LPCTSTR pszPath = Atom_GetName(atomPath);
|
|
LPCTSTR pszDBName;
|
|
|
|
ASSERT(pszPath);
|
|
|
|
pcbs->atomBrf = atomPath;
|
|
pcbs->uFlags = 0;
|
|
|
|
// Create an abort event object simply so we can programmatically
|
|
// cancel a createreclist call in the worker thread. This
|
|
// would happen if the user closed the briefcase during
|
|
// CreateRecList.
|
|
|
|
// (it is ok if this fails)
|
|
AbortEvt_Create(&pcbs->pabortevt, AEF_SHARED);
|
|
|
|
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Opening Briefcase %s..."), pszPath); )
|
|
|
|
if (IsLFNDrive(pszPath))
|
|
{
|
|
pszDBName = g_szDBName;
|
|
SetFlag(pcbs->uFlags, CBSF_LFNDRIVE);
|
|
}
|
|
else
|
|
pszDBName = g_szDBNameShort;
|
|
|
|
if (PathsTooLong(pszPath, pszDBName))
|
|
{
|
|
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_OPEN_TOOLONG),
|
|
MAKEINTRESOURCE(IDS_CAP_OPEN), NULL, MB_ERROR);
|
|
hres = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
PathCombine(szDatPath, pszPath, pszDBName);
|
|
hres = OpenTheBriefcase(szDatPath, atomPath, pcbs, hwndOwner);
|
|
if (FAILED(hres))
|
|
{
|
|
DEBUG_CODE( TRACE_MSG(TF_ERROR, TEXT("Open failed. Error is %s"), SzFromTR(GET_TR(hres))); )
|
|
|
|
SEMsgBox(hwndOwner, IDS_CAP_OPEN, hres, c_rgseOpenBriefcase, ARRAYSIZE(c_rgseOpenBriefcase));
|
|
|
|
// Is this a corrupt briefcase?
|
|
if (E_TR_CORRUPT_BRIEFCASE == hres)
|
|
{
|
|
// Yes; try to create a new database
|
|
hres = HandleCorruptDatabase(pcbs, atomPath, szDatPath, hwndOwner);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Did something fail above?
|
|
if (FAILED(hres))
|
|
{
|
|
// Yes; cleanup
|
|
if (pcbs)
|
|
{
|
|
if (pcbs->hbrf)
|
|
Sync_CloseBriefcase(pcbs->hbrf);
|
|
|
|
SharedFree(&pcbs);
|
|
}
|
|
}
|
|
|
|
*ppcbs = pcbs;
|
|
}
|
|
CBS_LeaveCS();
|
|
|
|
return hres;
|
|
}
|
|
|
|
|