mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
7.1 KiB
302 lines
7.1 KiB
/*
|
|
* cache.c
|
|
*
|
|
* Implements cache mechanism for SLM
|
|
*
|
|
* Currently, this file depends on WIN32 APIs,
|
|
* but I think it's easy to make these code portable if you want.
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
EnableAssert
|
|
|
|
/* Similar to FScanLn but can work without modifying original string,
|
|
* and can specify the length of original string.
|
|
* Designed to be used for mapped files. */
|
|
F
|
|
FScanLnSz(
|
|
char** ppch,
|
|
int cch,
|
|
const char* szFmt,
|
|
char* sz,
|
|
int cchMax
|
|
)
|
|
{
|
|
char *pch = *ppch;
|
|
char* pchLim = pch + cch;
|
|
|
|
while (*szFmt != 0){
|
|
if (*szFmt == ' '){
|
|
/* skip all white space in input */
|
|
while (pch < pchLim && (*pch == ' ' || *pch == '\t'))
|
|
pch++;
|
|
|
|
szFmt++; /* advance format */
|
|
} else {
|
|
if (toupper(*szFmt) != toupper(*pch))
|
|
return fFalse;
|
|
|
|
szFmt++; /* advance format */
|
|
pch++; /* advance input */
|
|
}
|
|
if (pch >= pchLim)
|
|
return fFalse;
|
|
}
|
|
|
|
/* skip value */
|
|
cch = 0;
|
|
while (pch < pchLim){
|
|
char ch = *pch++;
|
|
if (ch == '\0' || ch == '\n' || ch == '\r')
|
|
break;
|
|
if (ch == ' ')
|
|
/* no blanks in value */
|
|
return fFalse;
|
|
if (cch++ < cchMax)
|
|
*sz++ = ch;
|
|
}
|
|
|
|
if (cch < cchMax)
|
|
*sz = '\0';
|
|
|
|
while (pch < pchLim && (*pch == '\n' || *pch == '\r'))
|
|
pch++;
|
|
|
|
*ppch = pch;
|
|
|
|
/* return true if not zero length and length below max */
|
|
return cch > 0 && cch < cchMax;
|
|
}
|
|
|
|
F
|
|
FScanLnF(
|
|
char** ppch,
|
|
int cch,
|
|
const char* szFmt,
|
|
F* pf
|
|
)
|
|
{
|
|
char sz[80+1], ch0;
|
|
if (!FScanLnSz(ppch, cch, szFmt, sz, 80))
|
|
return fFalse;
|
|
ch0 = toupper(sz[0]);
|
|
*pf = (ch0 == 'Y' || ch0 == 'T' || ch0 == 'E');
|
|
return fTrue;
|
|
}
|
|
|
|
F
|
|
FLoadCacheRc(
|
|
AD* pad
|
|
)
|
|
{
|
|
PTH pthRc[cchPthMax+1];
|
|
MF* pmf;
|
|
char* pch;
|
|
char* pchEnv;
|
|
char sz[cchPthMax+1];
|
|
F f;
|
|
|
|
sprintf(pthRc, "%s/SLMCACHE.INI", pad->pthURoot);
|
|
pmf = PmfOpen(pthRc, omReadOnly, fxNil);
|
|
if (!pmf)
|
|
return fFalse;
|
|
pch = MapMf(pmf, ReadOnly);
|
|
if (pch){
|
|
long cch = SeekMf(pmf, 0, 2);
|
|
char* pchLim = pch + cch;
|
|
int ln;
|
|
|
|
pad->fCacheSrcEnabled = fTrue;
|
|
pad->fCacheStatusEnabled = fTrue;
|
|
pad->fCacheUpdateEnabled = fTrue;
|
|
for (ln = 1; pch < pchLim; ln++){
|
|
cch = pchLim - pch;
|
|
if (FScanLnSz(&pch, cch, "cache root = ", sz, cchPthMax)){
|
|
PthCopySz(pad->pthCRoot, sz);
|
|
} else if (FScanLnF(&pch, cch, "cache src = ", &f)){
|
|
pad->fCacheSrcEnabled = f;
|
|
} else if (FScanLnF(&pch, cch, "cache status = ", &f)){
|
|
pad->fCacheStatusEnabled = f;
|
|
} else if (FScanLnF(&pch, cch, "cache update = ", &f)){
|
|
pad->fCacheUpdateEnabled = f;
|
|
} else {
|
|
Warn("Unknown option in %s, line %d, ignored.\n", pthRc, ln);
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseMf(pmf);
|
|
|
|
pchEnv = getenv("SLMCACHE");
|
|
|
|
if (pchEnv){
|
|
char ch = toupper(pchEnv[0]);
|
|
if (ch == 'N' || ch == 'F' || ch == 'D') // disable cache
|
|
pad->pthCRoot[0] = 0;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/*
|
|
* Cache pthSFile to pthCFile.
|
|
* pthCDir must be a directory part of pthCFile.
|
|
*/
|
|
private F
|
|
_FCacheFilePth(
|
|
AD* pad,
|
|
PTH* pthCFile,
|
|
PTH* pthSFile,
|
|
PTH* pthCDir
|
|
)
|
|
{
|
|
struct _stat stS, stC;
|
|
char szCFile[cchPthMax+1];
|
|
char szSFile[cchPthMax+1];
|
|
char szLock[cchPthMax+1];
|
|
HANDLE hLock;
|
|
F fSucc = fFalse;
|
|
DWORD dwErr;
|
|
int cRetry;
|
|
|
|
SzPhysPath(szSFile, pthSFile);
|
|
SzPhysPath(szCFile, pthCFile);
|
|
sprintf(szLock, "%s.LCK", szCFile);
|
|
|
|
DeferSignals("caching file");
|
|
|
|
for (cRetry = 0;; cRetry++){
|
|
// Check cache availability
|
|
if (FStatPth(pthCFile, &stC) && FStatPth(pthSFile, &stS)){
|
|
if (stS.st_size == stC.st_size &&
|
|
stS.st_mtime == stC.st_mtime){
|
|
fSucc = fTrue;
|
|
goto LCacheFileDone;
|
|
}
|
|
}
|
|
|
|
if (!pad->fCacheUpdateEnabled)
|
|
goto LCacheFileDone;
|
|
|
|
// Copy file to cache
|
|
if (!FEnsurePth(pthCDir))
|
|
goto LCacheFileDone;
|
|
|
|
// Prevents two programs copy the same file at the same time
|
|
hLock = CreateFile(szLock, GENERIC_READ, 0, NULL,
|
|
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hLock != INVALID_HANDLE_VALUE)
|
|
break;
|
|
dwErr = GetLastError();
|
|
if (dwErr != ERROR_ALREADY_EXISTS){
|
|
Warn("Cannot lock %s (%d), use master file\n", szLock, dwErr);
|
|
goto LCacheFileDone;
|
|
}
|
|
if (cRetry >= 10)
|
|
goto LCacheFileDone;
|
|
if (!FQueryUser("Cache lock file %s exists; retry ? ", szLock))
|
|
goto LCacheFileDone;
|
|
SleepCsecs(10);
|
|
}
|
|
|
|
if (fVerbose)
|
|
PrErr("Caching file %s", pthSFile);
|
|
|
|
SLM_Unlink(szCFile);
|
|
fSucc = CopyFileA(szSFile, szCFile, FALSE);
|
|
if (fVerbose)
|
|
PrErr("\n");
|
|
if (!fSucc){
|
|
char rgchBuf[512];
|
|
DWORD dw = GetLastError();
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw,
|
|
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
|
|
rgchBuf, sizeof(rgchBuf)/sizeof(rgchBuf[0]), NULL);
|
|
PrErr("Caching failed: %d: %s", dw, rgchBuf);
|
|
SLM_Unlink(szCFile); // for safe guard
|
|
}
|
|
|
|
CloseHandle(hLock);
|
|
DeleteFile(szLock);
|
|
|
|
LCacheFileDone:
|
|
RestoreSignals();
|
|
|
|
return fSucc;
|
|
/// return FCopyFileNow(pthTo, pthFrom, permRO, fFalse, fxLocal);
|
|
}
|
|
|
|
F
|
|
FCacheFilePfi(
|
|
AD* pad,
|
|
PTH* pthCFile,
|
|
FI* pfi
|
|
)
|
|
{
|
|
PTH pthCDir[cchPthMax+1];
|
|
PTH pthSFile[cchPthMax+1];
|
|
|
|
if (pad->pthCRoot[0] == 0 || !pad->fCacheSrcEnabled)
|
|
return fFalse;
|
|
|
|
PthForSFile(pad, pfi, pthSFile);
|
|
PthForCFile(pad, pfi, pthCFile);
|
|
PthForCDir(pad, pthCDir);
|
|
|
|
return _FCacheFilePth(pad, pthCFile, pthSFile, pthCDir);
|
|
}
|
|
|
|
/*
|
|
* Same as PthForSFile, except, this function tries to cache files,
|
|
* and returns path to the cached file if succeeds.
|
|
* When cache is not enabled, or caching failed, returns path to
|
|
* server file.
|
|
*/
|
|
PTH*
|
|
PthForCachedSFile(
|
|
AD* pad,
|
|
FI* pfi,
|
|
PTH* pth
|
|
)
|
|
{
|
|
if (!FCacheFilePfi(pad, pth, pfi))
|
|
return PthForSFile(pad, pfi, pth);
|
|
return pth;
|
|
}
|
|
|
|
F
|
|
FCacheStatusFile(
|
|
AD* pad,
|
|
PTH* pthStatus
|
|
)
|
|
{
|
|
PTH pthCDir[cchPthMax+1];
|
|
PTH pthStatusMaster[cchPthMax+1];
|
|
|
|
if (pad->pthCRoot[0] == 0 || !pad->fCacheStatusEnabled)
|
|
return fFalse;
|
|
|
|
PthForStatus(pad, pthStatusMaster);
|
|
PthForCStatus(pad, pthStatus);
|
|
PthForCStatusDir(pad, pthCDir);
|
|
|
|
return _FCacheFilePth(pad, pthStatus, pthStatusMaster, pthCDir);
|
|
}
|
|
|
|
/*
|
|
* Same as PthForStatus, except, this function tries to cache files,
|
|
* and returns path to the cached file if succeeds.
|
|
* When cache is not enabled, or caching failed, returns path to
|
|
* server file.
|
|
*/
|
|
PTH*
|
|
PthForCachedStatus(
|
|
AD* pad,
|
|
PTH* pthStatus
|
|
)
|
|
{
|
|
if (!FCacheStatusFile(pad, pthStatus))
|
|
return PthForStatus(pad, pthStatus);
|
|
return pthStatus;
|
|
}
|