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