windows-nt/Source/XPSP1/NT/shell/ext/brfcase/filesync/syncui/cbs.c
2020-09-26 16:20:57 +08:00

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