|
|
//---------------------------------------------------------------------------
//
// 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; }
|