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.
2259 lines
57 KiB
2259 lines
57 KiB
// API
|
|
//
|
|
// FLoadStatus(pad, lck, ls)
|
|
// Lock the status file and load it into memory. If lck != lckNil, then
|
|
// also initialize the script.
|
|
// FlushStatus(pad)
|
|
// Write script operations to unlock the status file (if locked), then
|
|
// run any accumulated script.
|
|
// AbortStatus(pad)
|
|
// Unlock status file (if locked).
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "messages.h"
|
|
EnableAssert
|
|
|
|
// Max. bytes alloc-able from C6 = 65512, rounded down
|
|
#define SLMMAXSEG (unsigned)65500
|
|
#define MAXFI (SLMMAXSEG / sizeof(FI)) // limit of FI's in 64k
|
|
#define MAXED (SLMMAXSEG / sizeof(ED)) // limit of ED's in 64k
|
|
#define MAXFS (SLMMAXSEG / sizeof(FS))
|
|
|
|
const char *szAllowedFile = "\\:/^.&$%'-_@{}~`!#()";
|
|
|
|
char *LpbResStat(unsigned);
|
|
F FCheckSh(AD *, SH *);
|
|
F FLockEdRetry(AD *pad, MF *pmf, char *sz, int ichMax);
|
|
F FLockAllRetry(AD *pad, MF *pmf, char *sz, int ichMax);
|
|
F FRetryLock(AD *);
|
|
void UnlockEd(SH *, ED *, IED);
|
|
void UnlockAll(SH *);
|
|
void AssertShLocking(SH *);
|
|
F FLoadSh(AD *, SH *, MF *);
|
|
F FLoadFi(AD *, MF *);
|
|
F FLoadEd(AD *, MF *, F);
|
|
F FindIedCur(AD *);
|
|
F FLoadFs(AD *, MF *);
|
|
F FAllocLoadEdFiFs(AD *, MF *);
|
|
void Write1Ed(AD *, MF *);
|
|
void WriteAll(AD *, SH *, MF *);
|
|
void FlushSh(SH *, MF *);
|
|
void FlushFi(AD *, MF *);
|
|
void FlushEd(AD *, MF *);
|
|
void Flush1Ed(AD *, MF *);
|
|
void FlushFs(AD *, MF *);
|
|
|
|
extern Append_Date(char *, int);
|
|
|
|
extern LPVOID InPageErrorAddress;
|
|
|
|
extern DWORD dwPageSize;
|
|
|
|
char szLogFail[4096];
|
|
|
|
PTH pthStFile[] = "/status.slm";
|
|
PTH pthStBak[] = "/status.bak";
|
|
|
|
PV pvInit = { 1, 0, 0, "" }; // initial project version
|
|
|
|
#define INIT_SH(sh) { \
|
|
memset(&(sh), 0, sizeof(SH)); \
|
|
(sh).magic = MAGIC; \
|
|
(sh).version = VERSION; \
|
|
(sh).pv = pvInit; \
|
|
(sh).fRobust = fTrue; \
|
|
(sh).biNext = biMin; \
|
|
(sh).pthSSubDir[0] = '/'; \
|
|
(sh).pthSSubDir[1] = '\0'; \
|
|
}
|
|
|
|
// REVIEW: V2 warning should be removed/changed in later versions
|
|
F fV2Warned = fFalse;
|
|
F fV2Crap = fFalse;
|
|
|
|
short wStart; // start time
|
|
|
|
// PadStatus is used by AbortStatus to get the state of the status file.
|
|
// It is essential because the caller (the interrupt handler) can't supply
|
|
// the pad. We also use it to test if the status file is dirty.
|
|
//
|
|
// padStatus is set by FLoadStatus and cleared by AbortStatus and FlushStatus.
|
|
|
|
AD *padStatus = 0;
|
|
|
|
F
|
|
FClnStatus()
|
|
{
|
|
return padStatus == 0;
|
|
}
|
|
|
|
|
|
char *
|
|
LpbResStat(
|
|
unsigned cb
|
|
)
|
|
{
|
|
register char * lpb;
|
|
|
|
if ((lpb = LpbAllocCb(cb,fTrue)) == 0) {
|
|
Error("out of memory\n");
|
|
return 0;
|
|
}
|
|
|
|
return lpb;
|
|
}
|
|
|
|
extern DWORD dwPageSize;
|
|
|
|
SH *
|
|
PshAlloc(
|
|
F fPageAlign
|
|
)
|
|
{
|
|
SH *psh;
|
|
|
|
//
|
|
// If using QuickIO (which means FILE_FLAG_NO_BUFFERING for network files)
|
|
// then for all the fringe architectures (i.e. RISC) we need to make sure
|
|
// our buffers for I/O are aligned on the correct boundary. Since there is
|
|
// no API to call to find the correct one, use page size alignment as a sure
|
|
// bet. If we are going to go to the trouble to call VirtualAlloc, then
|
|
// reserve 4MB of address space, so code in FAllocStatus can commit more
|
|
// pages as needed to hold the FI, ED and FS portions of the status file.
|
|
//
|
|
|
|
if (fPageAlign) {
|
|
psh = VirtualAlloc(NULL, 4096 * 1024, MEM_RESERVE, PAGE_READWRITE);
|
|
if (psh != NULL) {
|
|
if (VirtualAlloc(psh, sizeof(*psh), MEM_COMMIT, PAGE_READWRITE) != psh) {
|
|
VirtualFree(psh, 0, MEM_RELEASE);
|
|
psh = NULL;
|
|
}
|
|
}
|
|
} else
|
|
psh = (SH *)LpbResStat(sizeof(SH));
|
|
|
|
return psh;
|
|
}
|
|
|
|
F
|
|
PshCommit(
|
|
SH *psh,
|
|
FI **rgfi,
|
|
ED **rged1,
|
|
FS **rgfs
|
|
)
|
|
{
|
|
PVOID p;
|
|
DWORD cb;
|
|
|
|
//
|
|
// Do one commit for the remaining pieces of the status file
|
|
// we will read, each on a page boundary to keep DarrylH happy
|
|
//
|
|
p = (PVOID)(((DWORD)psh + sizeof(*psh) + dwPageSize - 1) & ~(dwPageSize-1));
|
|
*rgfi = (FI *)p;
|
|
p = (PVOID)(((DWORD)p + (sizeof(FI) * psh->ifiMac) + dwPageSize - 1) & ~(dwPageSize-1));
|
|
*rged1 = (ED *)p;
|
|
p = (PVOID)(((DWORD)p + sizeof(ED) + dwPageSize - 1) & ~(dwPageSize-1));
|
|
*rgfs = (FS *)p;
|
|
p = (PVOID)(((DWORD)p + (sizeof(FS) * psh->ifiMac) + dwPageSize - 1) & ~(dwPageSize-1));
|
|
cb = (DWORD)p - (DWORD)*rgfi;
|
|
|
|
p = VirtualAlloc(*rgfi,
|
|
cb,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if ((PVOID)*rgfi != p)
|
|
return fFalse;
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
void
|
|
PshFree(
|
|
SH *psh,
|
|
F fPageAlign
|
|
)
|
|
{
|
|
if (fPageAlign)
|
|
VirtualFree(psh, 0, MEM_RELEASE);
|
|
else
|
|
FreeResStat((char *)psh);
|
|
|
|
return;
|
|
}
|
|
|
|
// Allocate space in pad->{rged,rgfi,mpiedrgfs}.
|
|
//
|
|
// This is also where all necessary size overflow checking is done.
|
|
//
|
|
// *** Assumes pad->{psh, cfiAdd, fExtraEd} has already been initialized ***.
|
|
|
|
F
|
|
FAllocStatus(
|
|
AD * pad
|
|
)
|
|
{
|
|
IED ied;
|
|
IFI cfi;
|
|
IED ced;
|
|
char szNoMoreThan[] = "Can't have more than %d %s in project.\n";
|
|
|
|
AssertF(pad->psh != 0);
|
|
|
|
cfi = pad->psh->ifiMac + pad->cfiAdd;
|
|
ced = pad->psh->iedMac + pad->fExtraEd;
|
|
pad->rgfi = 0;
|
|
pad->rged = 0;
|
|
pad->mpiedrgfs = 0;
|
|
pad->rged1 = 0;
|
|
pad->rgfs = 0;
|
|
if (pad->fMappedIO) {
|
|
if ((pad->mpiedrgfs = (FS * *)LpbResStat(sizeof(FS *) * ced)) == 0)
|
|
return fFalse;
|
|
|
|
pad->rgfi = (FI *)((char *)pad->psh + PosRgfi(pad->psh));
|
|
pad->rged = (ED *)((char *)pad->psh + PosRged(pad->psh));
|
|
for (ied = 0; ied < ced; ied++) {
|
|
pad->mpiedrgfs[ied] = (FS *)((char *)pad->psh + PosRgfsIed(pad->psh,ied));
|
|
}
|
|
return fTrue;
|
|
}
|
|
|
|
//special checks for excessive size; Apps can't understand 'out of memory' as error
|
|
|
|
if (pad->psh->version <= VERSION_64k_EDFI) {
|
|
if (cfi > MAXFI)
|
|
FatalError(szNoMoreThan, MAXFI, "files");
|
|
if (ced > MAXED)
|
|
FatalError(szNoMoreThan, MAXED, "enlistments");
|
|
|
|
// won't check FS because the FI and ED swamp it
|
|
}
|
|
|
|
if ((pad->rgfi = (FI *)LpbResStat(sizeof(FI) * cfi)) == 0 ||
|
|
(pad->rged = (ED *)LpbResStat(sizeof(ED) * ced)) == 0 ||
|
|
(pad->mpiedrgfs = (FS * *)LpbResStat(sizeof(FS *) * ced)) == 0)
|
|
return fFalse;
|
|
|
|
for (ied = 0; ied < ced; ied++) {
|
|
if ((pad->mpiedrgfs[ied] = (FS *)LpbResStat(sizeof(FS) * cfi)) == 0)
|
|
// The rest of mpiedrgfs[] are zero because LpbResStat
|
|
// zeroes the allocated memory.
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
// free all memory associated with a status file and clear pointers
|
|
void
|
|
FreeStatus(
|
|
AD *pad
|
|
)
|
|
{
|
|
register IED ied;
|
|
register IED iedLim=0;
|
|
|
|
AssertF(pad != 0);
|
|
|
|
if (pad->fMappedIO) {
|
|
if (pad->mpiedrgfs != 0)
|
|
FreeResStat((char *)pad->mpiedrgfs);
|
|
|
|
UnmapViewOfFile(pad->psh);
|
|
pad->fMappedIO = fFalse;
|
|
}
|
|
else if (pad->fQuickIO) {
|
|
if (pad->psh != 0) {
|
|
iedLim = pad->psh->iedMac + pad->fExtraEd;
|
|
PshFree(pad->psh, fTrue);
|
|
}
|
|
|
|
pad->fQuickIO = fFalse;
|
|
}
|
|
else {
|
|
if (pad->psh != 0) {
|
|
iedLim = pad->psh->iedMac + pad->fExtraEd;
|
|
PshFree(pad->psh, fFalse);
|
|
}
|
|
|
|
if (pad->mpiedrgfs != 0) {
|
|
for (ied = 0; ied < iedLim && pad->mpiedrgfs[ied] != 0; ied++)
|
|
FreeResStat((char *)pad->mpiedrgfs[ied]);
|
|
|
|
FreeResStat((char *)pad->mpiedrgfs);
|
|
}
|
|
|
|
if (pad->rged != 0)
|
|
FreeResStat((char *)pad->rged);
|
|
|
|
if (pad->rgfi != 0)
|
|
FreeResStat((char *)pad->rgfi);
|
|
}
|
|
|
|
pad->fStatusAlreadyLoaded = fFalse;
|
|
pad->psh = 0;
|
|
pad->rgfi = 0;
|
|
pad->cfiAdd = 0;
|
|
pad->rged = 0;
|
|
pad->mpiedrgfs = 0;
|
|
pad->rged1 = 0;
|
|
pad->rgfs = 0;
|
|
pad->fExtraEd = fFalse;
|
|
pad->iedCur = iedNil;
|
|
}
|
|
|
|
|
|
// Load the status file named in the ad; aborts on file errors; returns
|
|
// fFalse if the requested lock can't be granted.
|
|
|
|
F fDisplayStatusFilePath; // Set by SlmPeekThread in NTSYS.C
|
|
|
|
F
|
|
FLoadStatus(
|
|
AD *pad,
|
|
LCK lck,
|
|
LS ls
|
|
)
|
|
{
|
|
SH *psh;
|
|
PTH pth[cchPthMax];
|
|
MF *pmf = NULL;
|
|
int wDelay = 1;
|
|
F fJustFi = (ls&flsJustFi) != 0;
|
|
F fJustEd = (ls&flsJustEd) != 0;
|
|
char szProblem[160];
|
|
F fRetry;
|
|
|
|
__try {
|
|
|
|
AssertNoMf();
|
|
AssertF(padStatus == 0);
|
|
AssertF(!pad->fWLock);
|
|
AssertF(pad->psh == 0);
|
|
AssertF(!FEmptyNm(pad->nmProj));
|
|
AssertF(!FEmptyPth(pad->pthSRoot));
|
|
AssertF(!FEmptyPth(pad->pthURoot));
|
|
AssertF(lck >= lckNil && lck < lckMax);
|
|
AssertF(!((fJustFi || fJustEd) && lck != lckNil));
|
|
|
|
if (lck == lckNil)
|
|
PthForCachedStatus(pad, pth);
|
|
else {
|
|
PthForStatus(pad, pth);
|
|
}
|
|
|
|
if (fDisplayStatusFilePath) {
|
|
PrErr("Processing status file for: %&U/Q\n", pad, pad);
|
|
fDisplayStatusFilePath = fFalse;
|
|
}
|
|
|
|
wStart = (short) (time(NULL) >> 16);
|
|
|
|
// Loop until successfully locked.
|
|
for (;;) {
|
|
// Set these up on each attempt, AbortStatus clears them.
|
|
pad->cfiAdd = CfiAddOfLs(ls);
|
|
pad->fExtraEd = (ls&flsExtraEd) != 0;
|
|
AssertF(pad->cfiAdd == 0 || !pad->fExtraEd);
|
|
|
|
// Save pad for AbortStatus
|
|
padStatus = pad;
|
|
|
|
AssertNoMf();
|
|
AssertF(!pad->fWLock);
|
|
AssertF(pad->psh == 0);
|
|
|
|
pad->fMappedIO = fFalse;
|
|
pad->fQuickIO = fFalse;
|
|
|
|
if (pad->flags&flagMappedIO &&
|
|
lck == lckNil &&
|
|
FEmptyNm(pad->nmUser) &&
|
|
(pad->pecmd->cmd != cmdStatus || !(pad->flags&(flagStAllEd|flagStGlobal))) &&
|
|
FindIedCur(pad)
|
|
) {
|
|
pad->fQuickIO = fTrue;
|
|
}
|
|
|
|
while (TRUE) {
|
|
if (pad->fQuickIO)
|
|
pmf = PmfOpenNoBuffering(pth, omReadOnly, fxNil); // must be lckNil
|
|
else
|
|
pmf = PmfOpen(pth, (lck != lckNil) ? omReadWrite : omReadOnly, fxNil);
|
|
|
|
if (pmf != 0) {
|
|
if (pad->fQuickIO) {
|
|
if (!FAllocLoadEdFiFs(pad, pmf)) {
|
|
CloseMf(pmf);
|
|
continue;
|
|
}
|
|
psh = pad->psh;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (!FQueryApp("cannot open status file for %&P/C", "retry", pad)) {
|
|
AbortStatus();
|
|
return fFalse;
|
|
}
|
|
|
|
if (!FCanPrompt()) {
|
|
if (wDelay == 1 && FForce())
|
|
Error("cannot open status file for %&P/C\n", pad);
|
|
|
|
// printf("fLR: %s\tDelay: %d\n", pad->flags&flagLimitRetry ? "Set" : "Clear", wDelay);
|
|
|
|
if (60 == wDelay && pad->flags&flagLimitRetry) {
|
|
AbortStatus();
|
|
return (fFalse);
|
|
}
|
|
|
|
if (wDelay > 4 && pad->pecmd->cmd == cmdLog && pad->flags&flagLogDelDirToo) {
|
|
AbortStatus();
|
|
return (fFalse);
|
|
}
|
|
|
|
SleepCsecs(wDelay);
|
|
// Double the delay, up to 60 seconds.
|
|
wDelay = (wDelay > 30) ? 60 : wDelay * 2;
|
|
}
|
|
}
|
|
|
|
// Begin critical section, protected by an OS lock on the SH.
|
|
if (lck != lckNil && !FLockMf(pmf)) {
|
|
Error("status file for %&P/C in use\n", pad);
|
|
CloseMf(pmf);
|
|
AbortStatus();
|
|
return fFalse;
|
|
}
|
|
|
|
if (!pad->fQuickIO) {
|
|
if (pad->flags&flagMappedIO &&
|
|
pad->cfiAdd == 0 && !pad->fExtraEd && lck == lckNil &&
|
|
(psh = MapMf(pmf, pad->pecmd->cmd == cmdSsync ? ReadOnly : ReadWrite)) != NULL) {
|
|
pad->fMappedIO = fTrue;
|
|
InPageErrorAddress = NULL;
|
|
} else {
|
|
if ((psh = PshAlloc(fFalse)) == NULL) {
|
|
CloseMf(pmf);
|
|
AbortStatus();
|
|
return fFalse;
|
|
}
|
|
|
|
}
|
|
pad->psh = psh;
|
|
|
|
// Load sh and check that it is valid.
|
|
if (!FLoadSh(pad, psh, pmf) || !FCheckSh(pad, psh)) {
|
|
CloseMf(pmf);
|
|
AbortStatus();
|
|
return fFalse;
|
|
}
|
|
}
|
|
|
|
// break from loop (no lock needed)
|
|
if (lck == lckNil)
|
|
break;
|
|
|
|
// Load rged so that ssyncing status can be obtained
|
|
if (!FAllocStatus(pad) || !FLoadEd(pad, pmf, fTrue)) {
|
|
CloseMf(pmf);
|
|
AbortStatus();
|
|
return fFalse;
|
|
}
|
|
|
|
// So far, no retryable errors.
|
|
fRetry = fFalse;
|
|
|
|
// Try to apply the desired lock.
|
|
if (psh->fAdminLock &&
|
|
NmCmp(psh->nmLocker, pad->nmInvoker, cchUserMax) != 0) {
|
|
char szAdmin[cchUserMax + 1];
|
|
|
|
SzCopyNm(szAdmin, psh->nmLocker, cchUserMax);
|
|
SzPrint(szProblem, "status file for %&P/C locked by administrator %s", pad, szAdmin);
|
|
fRetry = fTrue;
|
|
}
|
|
|
|
else if (lck == lckEd)
|
|
fRetry = FLockEdRetry(pad, pmf, szProblem, sizeof szProblem);
|
|
else {
|
|
AssertF(lck == lckAll);
|
|
fRetry = FLockAllRetry(pad, pmf, szProblem, sizeof szProblem);
|
|
}
|
|
|
|
UnlockMf(pmf);
|
|
// End critical section.
|
|
|
|
// Exit loop if successfully locked.
|
|
if (pad->fWLock)
|
|
break;
|
|
|
|
// Status file is still open.
|
|
CloseMf(pmf);
|
|
AbortStatus();
|
|
|
|
if (!fRetry || !FQueryApp(szProblem, "retry"))
|
|
return fFalse;
|
|
|
|
// If not interactive, back off for a while.
|
|
if (!FCanPrompt()) {
|
|
// printf("fLR: %s\tDelay: %d\n", pad->flags&flagLimitRetry ? "Set" : "Clear", wDelay);
|
|
|
|
if (60 == wDelay && pad->flags&flagLimitRetry)
|
|
return (fFalse);
|
|
|
|
if (wDelay == 1 && FForce() && !fVerbose)
|
|
Error("%s\n", szProblem);
|
|
|
|
SleepCsecs(wDelay);
|
|
|
|
// Double the delay, up to 60 seconds.
|
|
wDelay = (wDelay > 30) ? 60 : wDelay * 2;
|
|
}
|
|
}
|
|
|
|
// Load rest of status file. If lck was not lckNil, the status memory
|
|
// is already allocated and the rged is already loaded. If fJustFi,
|
|
// load only the SH and the FI. If fQuickIO is set, then everything
|
|
// already done.
|
|
//
|
|
|
|
if (!pad->fQuickIO) {
|
|
if ((lck == lckNil && !FAllocStatus(pad)) ||
|
|
(!fJustEd && !FLoadFi(pad, pmf)) ||
|
|
(!fJustFi && lck == lckNil && !FLoadEd(pad, pmf, fTrue)) ||
|
|
(!(fJustFi || fJustEd) && !FLoadFs(pad, pmf))) {
|
|
CloseMf(pmf);
|
|
AbortStatus();
|
|
return fFalse;
|
|
}
|
|
}
|
|
|
|
CloseMf(pmf);
|
|
|
|
if (lck != lckNil && !FInitScript(pad, lck)) {
|
|
AbortStatus();
|
|
return fFalse;
|
|
}
|
|
|
|
// REVIEW: V2 warning should be removed/changed in later versions
|
|
if (psh->version == 2 && fVerbose && !fV2Warned) {
|
|
Warn(szV2Upgrade, pad);
|
|
fV2Warned = fTrue;
|
|
}
|
|
} except( GetExceptionCode() == 0x00001234 ? EXCEPTION_EXECUTE_HANDLER
|
|
: EXCEPTION_CONTINUE_SEARCH ) {
|
|
if (pmf && pmf->pthReal) {
|
|
CloseMf(pmf);
|
|
}
|
|
|
|
AbortStatus();
|
|
return fFalse;
|
|
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
// returns fTrue if the sh is somewhat sane.
|
|
F
|
|
FCheckSh(
|
|
AD *pad,
|
|
SH *psh
|
|
)
|
|
{
|
|
if (psh->magic != MAGIC) {
|
|
Error("status file for %&P/C has been damaged;\n"
|
|
"have your administrator run slmck -gr for %&P/C\n",
|
|
pad,
|
|
pad);
|
|
return fFalse;
|
|
}
|
|
|
|
else if (psh->version < VERSION_COMPAT_MAC) {
|
|
// e.g. we want to work with ver 2 status files even
|
|
// when VERSION == 3
|
|
|
|
Error("status file for %&P/C is an old version;\n"
|
|
"have your administrator run slmck -ngr for %&P/C to upgrade\n",
|
|
pad,
|
|
pad);
|
|
return fFalse;
|
|
} else
|
|
if (psh->version > VERSION) {
|
|
Error("status file for %&P/C is a new version; you are running an old binary.\n"
|
|
"Install the new slm binaries (available from your administrator).\n",
|
|
pad,
|
|
pad);
|
|
return fFalse;
|
|
}else
|
|
if (PthCmp(pad->pthSSubDir, psh->pthSSubDir) != 0) {
|
|
Error("status file for %&P/C directory (%ls) disagrees with current directory (%s)\n",
|
|
pad, psh->pthSSubDir,
|
|
pad->pthSSubDir);
|
|
return fTrue; // proceed anyway
|
|
} else
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
// Try to lock a single ed, reporting any problems in szProblem. Write the
|
|
// lock to disk. Return fTrue if there was a problem that might go away if
|
|
// the user retries. Rged must already be loaded.
|
|
|
|
F
|
|
FLockEdRetry(
|
|
AD *pad,
|
|
MF *pmf,
|
|
char *szProblem,
|
|
int ichProblemMax
|
|
)
|
|
{
|
|
ED *ped;
|
|
SH *psh = pad->psh;
|
|
static char szEdLocked[] =
|
|
"%&P/C is already locked for ssync or out by you!\n"
|
|
"(You can only run one ssync or out at a time.)\n"
|
|
"If the status file is wrongly locked, run \"sadmin unlock\"\n";
|
|
|
|
AssertF(psh != 0);
|
|
AssertF(pad->rged != 0);
|
|
|
|
if (psh->lck != lckNil && psh->lck != lckEd) {
|
|
SzLockers(pad, szProblem, ichProblemMax);
|
|
|
|
Append_Date(szProblem, pmf->fdRead);
|
|
|
|
return fTrue;
|
|
}else
|
|
if (pad->iedCur == iedNil) {
|
|
Error(szNotEnlisted, pad, pad, pad, pad);
|
|
return fFalse;
|
|
}
|
|
|
|
ped = pad->rged + pad->iedCur;
|
|
|
|
if (ped->fLocked) {
|
|
Error(szEdLocked, pad);
|
|
return fFalse;
|
|
}
|
|
|
|
if (NmCmp(pad->nmInvoker, ped->nmOwner, cchUserMax) != 0)
|
|
Warn("invoker is not owner of directory\n");
|
|
|
|
AssertShLocking(psh);
|
|
|
|
psh->lck = lckEd;
|
|
ped->fLocked = fTrue;
|
|
|
|
AssertShLocking(psh);
|
|
|
|
// If aborted after this statement, AbortStatus will remove the
|
|
// lock from the status file.
|
|
|
|
pad->fWLock = fTrue;
|
|
|
|
FlushSh(psh, pmf);
|
|
Flush1Ed(pad, pmf);
|
|
|
|
return fFalse; // No problems.
|
|
}
|
|
|
|
|
|
// Try to lock the entire status file; report any problems in szProblem.
|
|
// Write the lock to disk. Return fTrue if there was a problem that might go
|
|
// away if the user retries.
|
|
|
|
F
|
|
FLockAllRetry(
|
|
AD *pad,
|
|
MF *pmf,
|
|
char *szProblem,
|
|
int ichProblemMax
|
|
)
|
|
{
|
|
SH *psh = pad->psh;
|
|
|
|
AssertF(psh != 0);
|
|
|
|
if (psh->lck != lckNil) {
|
|
SzLockers(pad, szProblem, ichProblemMax);
|
|
|
|
Append_Date(szProblem, pmf->fdRead);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
AssertShLocking(psh);
|
|
|
|
psh->lck = lckAll;
|
|
NmCopy(psh->nmLocker, pad->nmInvoker, cchUserMax);
|
|
|
|
AssertShLocking(psh);
|
|
|
|
// If aborted after this statement, AbortStatus will remove the
|
|
// lock from the status file.
|
|
|
|
pad->fWLock = fTrue;
|
|
|
|
FlushSh(psh, pmf);
|
|
|
|
return fFalse; // No problems.
|
|
}
|
|
|
|
|
|
// Store the names of the lockers into szBuf; the file must be locked in some way.
|
|
|
|
char *
|
|
SzLockers(
|
|
AD *pad,
|
|
char *szBuf,
|
|
unsigned cchBuf
|
|
)
|
|
{
|
|
char szOwner[cchUserMax + 1 + 1];
|
|
IED ied;
|
|
|
|
AssertF(pad->psh != 0);
|
|
|
|
SzPrint(szBuf, "status file for %&P/C is locked", pad);
|
|
|
|
if (pad->psh->lck == lckAll) {
|
|
SzPrint(szBuf + strlen(szBuf), " by %&K", pad);
|
|
return szBuf;
|
|
}
|
|
|
|
AssertF(pad->psh->lck == lckEd);
|
|
AssertF(pad->rged != 0);
|
|
|
|
// Even if pad->psh->lck != lckEd, individual eds might be
|
|
// locked due to an error.
|
|
//
|
|
// Collect those rged[].nmOwner's s.t. rged[].fLocked.
|
|
|
|
strcat(szBuf, " for ssync or out by");
|
|
|
|
for (ied = 0; ied < pad->psh->iedMac; ied++) {
|
|
if ((!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) &&
|
|
pad->rged[ied].fLocked) {
|
|
SzPrint(szOwner, " %&O", pad, ied);
|
|
if (strlen(szOwner) + strlen(szBuf) < cchBuf-4)
|
|
strcat(szBuf, szOwner);
|
|
else {
|
|
strcat(szBuf, "...");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return szBuf;
|
|
}
|
|
|
|
|
|
// Unlock rged[ied], if it was locked.
|
|
void
|
|
UnlockEd(
|
|
SH *psh,
|
|
ED *rged,
|
|
IED ied
|
|
)
|
|
{
|
|
AssertF(psh != 0 && rged != 0 && ied != iedNil);
|
|
|
|
AssertShLocking(psh);
|
|
|
|
rged[ied].fLocked = fFalse;
|
|
|
|
// See if any other users have it locked.
|
|
for (ied = 0; ied < psh->iedMac; ied++) {
|
|
if ((!FIsFreeEdValid(psh) || !rged[ied].fFreeEd) &&
|
|
rged[ied].fLocked) {
|
|
// No, this isn't inconsistent with the possibilty that
|
|
// pad->psh->lck != lckEd in SzLockers above; that code must handle
|
|
// error conditions from locked files; here we have locked the in-
|
|
// core status file and the psh->lck had better be lckEd!
|
|
|
|
AssertF(psh->lck >= lckEd);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// clear psh->lck to lckNil unless we are lckAll. This fixes bug in the
|
|
// case that we call UnlockEd from FInstall1Ed when rerunning an existing
|
|
// script file, but there is an lckAll on the status file already from the
|
|
// command we are running (ie. enlist, etc...). This way we will not assert
|
|
// in the following AssertShLocking call.
|
|
|
|
if (psh->lck <= lckEd)
|
|
psh->lck = lckNil;
|
|
|
|
AssertShLocking(psh);
|
|
}
|
|
|
|
|
|
// Unlock the sh.
|
|
void
|
|
UnlockAll(
|
|
SH *psh
|
|
)
|
|
{
|
|
AssertF(psh != 0);
|
|
|
|
psh->lck = lckNil;
|
|
if (!psh->fAdminLock)
|
|
ClearLpbCb((char *)psh->nmLocker, cchUserMax);
|
|
|
|
AssertShLocking(psh);
|
|
}
|
|
|
|
|
|
void
|
|
AssertShLocking(
|
|
SH *psh
|
|
)
|
|
{
|
|
AssertF(psh != 0);
|
|
AssertF(FShLockInvariants(psh));
|
|
}
|
|
|
|
|
|
// ********** Validation utils **********
|
|
|
|
// CT - Condition Types for the AssertLogFail macro.
|
|
typedef unsigned CT;
|
|
#define ctRead (CT)1
|
|
#define ctWrite (CT)2
|
|
#define ctRdAfterWr (CT)3
|
|
|
|
// CTF - Condition Type Flags for the AssertLogFail macro.
|
|
#define ctfWarn (CT)(1<<9)
|
|
#define ctfFlagMask (CT)(0xFF)
|
|
|
|
#define AssertLogFail(ct,f) if (!(f)) LogFail(#f,ct,__FILE__,__LINE__)
|
|
|
|
void
|
|
LogFail(
|
|
char *szComment,
|
|
CT ct,
|
|
char *szSrcFile,
|
|
unsigned uSrcLineNo
|
|
)
|
|
{
|
|
extern AD adGlobal;
|
|
|
|
// REVIEW: V2 warning should be removed/changed in later versions
|
|
F fV2 = adGlobal.psh != NULL && adGlobal.psh->version == 2;
|
|
|
|
if (fV2 && !fV2Crap && !(ct & ctfWarn)) {
|
|
Warn(szV2Crap, &adGlobal);
|
|
fV2Crap = fTrue;
|
|
}
|
|
|
|
if (!fV2 || !(ct & ctfWarn)) {
|
|
// Show the condition that failed
|
|
Error(szAssertFailed, szComment, szSrcFile, uSrcLineNo);
|
|
}
|
|
|
|
// Return now to avoid a FatalError for V2 status files.
|
|
// Likewise for the sadmin dump command (otherwise,
|
|
// bad status files can't be dumped to be fixed).
|
|
|
|
if (fV2 || adGlobal.pecmd->cmd == cmdDump)
|
|
return;
|
|
|
|
// Mask off any modifying flags and switch on the CT value
|
|
ct &= ctfFlagMask;
|
|
switch (ct) {
|
|
case ctRead:
|
|
FatalError("The status file for %&P/C appears to be corrupted.\n%s",
|
|
&adGlobal,
|
|
szCallHELP);
|
|
|
|
case ctWrite:
|
|
FatalError("Continuing with this command would have caused the status file\n"
|
|
"\tfor %&P/C to become corrupted.\n%s",
|
|
&adGlobal,
|
|
szCallHELP);
|
|
|
|
case ctRdAfterWr:
|
|
FatalError("Inconsistent data may have been written to the status file\n"
|
|
"\tfor %&P/C.\n"
|
|
"\tIf subsequent commands indicate the status file has become\n"
|
|
"\tcorrupted, contact TRIO or NUTS for assistance in resolving the problem.\n",
|
|
&adGlobal);
|
|
|
|
default:
|
|
FatalError("AssertLogFail(%s) failed in %s, line %u\n\n",
|
|
szComment, szSrcFile, uSrcLineNo);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ValidatePth(
|
|
CT ct,
|
|
PTH *pth,
|
|
unsigned cchMax
|
|
)
|
|
{
|
|
unsigned ich;
|
|
|
|
AssertLogFail(ct, pth != NULL);
|
|
AssertLogFail(ct, *pth != '\0');
|
|
|
|
for (ich = 0; pth[ich] != '\0' && ich < cchMax; ich++) {
|
|
if (!(isalnum(pth[ich]) || strchr(szAllowedFile, pth[ich]))) {
|
|
sprintf(szLogFail, "Invalid char '%c (%X)' in Path: \"%*s\"",
|
|
pth[ich],
|
|
pth[ich],
|
|
__min(strlen(pth), cchMax),
|
|
pth);
|
|
LogFail(szLogFail, ct, __FILE__, __LINE__);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ValidateNm(
|
|
CT ct,
|
|
NM *nm,
|
|
unsigned cchMax,
|
|
unsigned fEmptyOk
|
|
)
|
|
{
|
|
unsigned ich;
|
|
|
|
AssertLogFail(ct, nm != NULL);
|
|
|
|
if (!fEmptyOk)
|
|
AssertLogFail(ct, *nm != '\0');
|
|
|
|
for(ich = 0; nm[ich] != '\0' && ich < cchMax; ich++) {
|
|
if (!(isalnum(nm[ich]) || strchr(szAllowedFile, nm[ich]))) {
|
|
sprintf(szLogFail, "Invalid char '%c (%X)' in Name: \"%*s\"",
|
|
nm[ich],
|
|
nm[ich],
|
|
__min(strlen(nm), cchMax),
|
|
nm);
|
|
LogFail(szLogFail, ct, __FILE__, __LINE__);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ValidateSz(
|
|
CT ct,
|
|
char *sz,
|
|
unsigned cchMax
|
|
)
|
|
{
|
|
unsigned ich;
|
|
|
|
AssertLogFail(ct, sz != NULL);
|
|
|
|
for(ich = 0; sz[ich] != '\0' && ich < cchMax; ich++)
|
|
;
|
|
|
|
AssertLogFail(ct, sz[ich] == '\0');
|
|
}
|
|
|
|
|
|
void
|
|
ValidateSh(
|
|
CT ct,
|
|
SH *psh
|
|
)
|
|
{
|
|
AssertLogFail(ct, psh->magic == MAGIC);
|
|
|
|
AssertLogFail(ct, psh->version >= 1);
|
|
AssertLogFail(ct, psh->version <= VERSION);
|
|
|
|
if (psh->version <= VERSION_64k_EDFI) {
|
|
AssertLogFail(ct, psh->ifiMac < MAXFI);
|
|
|
|
if (psh->iedMac != iedNil) {
|
|
AssertLogFail(ct, psh->iedMac < MAXED);
|
|
}
|
|
}
|
|
|
|
AssertLogFail(ct, psh->pv.rmj >= 0);
|
|
AssertLogFail(ct, psh->pv.rmm >= 0);
|
|
AssertLogFail(ct, psh->pv.rup >= 0);
|
|
|
|
ValidateSz(ct, psh->pv.szName, cchPvNameMax);
|
|
|
|
AssertLogFail(ct | ctfWarn, psh->rgfSpare == 0);
|
|
|
|
AssertLogFail(ct, FShLockInvariants(psh));
|
|
ValidateNm(ct, psh->nmLocker, cchUserMax, psh->lck < lckAll && !psh->fAdminLock);
|
|
|
|
AssertLogFail(ct | ctfWarn, psh->wSpare == 0);
|
|
AssertLogFail(ct, psh->biNext <= biNil);
|
|
|
|
ValidatePth(ct, psh->pthSSubDir, cchPthMax);
|
|
|
|
AssertLogFail(ct | ctfWarn, psh->rgwSpare[0] == 0);
|
|
AssertLogFail(ct | ctfWarn, psh->rgwSpare[1] == 0);
|
|
AssertLogFail(ct | ctfWarn, psh->rgwSpare[2] == 0);
|
|
AssertLogFail(ct | ctfWarn, psh->rgwSpare[3] == 0);
|
|
}
|
|
|
|
|
|
void
|
|
ValidateRgfi(
|
|
CT ct,
|
|
FI *rgfi,
|
|
unsigned cfi
|
|
)
|
|
{
|
|
unsigned ifi;
|
|
FI * pfi;
|
|
char * szErr = NULL;
|
|
|
|
for (ifi = 0; ifi < cfi; ifi++) {
|
|
pfi = &rgfi[ifi];
|
|
|
|
ValidateNm(ct, pfi->nmFile, cchFileMax, fFalse);
|
|
|
|
if (! ((pfi->fv >= fvInit) && (pfi->fv < fvLim))) {
|
|
szErr = "File Version Corrupt";
|
|
}
|
|
|
|
if (!szErr &&
|
|
! (pfi->fk == fkDir ||
|
|
pfi->fk == fkText ||
|
|
pfi->fk == fkBinary ||
|
|
pfi->fk == fkUnrec ||
|
|
pfi->fk == fkVersion ||
|
|
pfi->fk == fkUnicode) )
|
|
{
|
|
szErr = "File Type Invalid";
|
|
}
|
|
|
|
if (szErr) {
|
|
_snprintf(szLogFail, sizeof(szLogFail),
|
|
"%s: File Index #: %d\tname: %s\tfk: %s\n",
|
|
szErr, ifi, pfi->nmFile, pfi->fk);
|
|
LogFail(szLogFail, ct | ctfWarn, __FILE__, __LINE__);
|
|
}
|
|
|
|
if (!szErr) {
|
|
AssertLogFail(ct | ctfWarn, pfi->rgfSpare == 0);
|
|
AssertLogFail(ct | ctfWarn, pfi->wSpare == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ValidateRged(
|
|
SH *psh,
|
|
CT ct,
|
|
ED *rged,
|
|
unsigned ced
|
|
)
|
|
{
|
|
unsigned ied;
|
|
ED * ped;
|
|
|
|
for (ied = 0; ied < ced; ied++) {
|
|
ped = &rged[ied];
|
|
|
|
if (!FIsFreeEdValid(psh) || !ped->fFreeEd) {
|
|
ValidatePth(ct, ped->pthEd, cchPthMax);
|
|
|
|
ValidateNm(ct, ped->nmOwner, cchUserMax, fFalse);
|
|
|
|
if (ped->rgfSpare || (!FIsFreeEdValid(psh) && ped->fFreeEd)) {
|
|
_snprintf(szLogFail, sizeof(szLogFail),
|
|
"rgfSpare !0 - Enlistment #: %d\tname: %s\tpath: %s\tSpare: %d\n",
|
|
ied, ped->nmOwner, ped->pthEd, ped->rgfSpare);
|
|
LogFail(szLogFail, ct | ctfWarn, __FILE__, __LINE__);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ValidateRgfs(
|
|
CT ct,
|
|
FS *rgfs,
|
|
unsigned cfs
|
|
)
|
|
{
|
|
unsigned ifs;
|
|
FS * pfs;
|
|
char * szErr = NULL;
|
|
|
|
for (ifs = 0; ifs < cfs; ifs++) {
|
|
pfs = &rgfs[ifs];
|
|
|
|
if (!FValidFm(pfs->fm)) {
|
|
szErr = "Corrupt File Mode";
|
|
}
|
|
|
|
if (!szErr && (!(pfs->bi <= biNil))) {
|
|
szErr = "Corrupt Base Index";
|
|
}
|
|
|
|
if (!szErr && (!( pfs->fv >= fvInit && pfs->fv < fvLim))) {
|
|
szErr = "Corrupt File Version";
|
|
}
|
|
|
|
if (!szErr && (!( !(fmMerge == pfs->fm && biNil == pfs->bi)))) {
|
|
szErr = "File Mode/Base Index are mismatched";
|
|
}
|
|
|
|
if (szErr) {
|
|
_snprintf(szLogFail, sizeof(szLogFail),
|
|
"%s: File #: %d\tfm: %d\tfv: %d\tbi: %d\n",
|
|
szErr, ifs, pfs->fm, pfs->fv, pfs->bi);
|
|
LogFail(szLogFail, ct, __FILE__, __LINE__);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ********** CheckSum utils **********
|
|
|
|
typedef unsigned CKS;
|
|
|
|
// Generate checksum value to compare with later
|
|
#define SetCks(psh,cks,pb,cb) \
|
|
if ((psh)->fRobust) cks=CksCompute((unsigned char *)(pb),cb)
|
|
|
|
// Implentation of Fletcher's Checksum.
|
|
// See article on page 32, Dr. Dobb's Journal, May 1992 for details.
|
|
|
|
CKS
|
|
CksCompute(
|
|
unsigned char *pb,
|
|
unsigned cb
|
|
)
|
|
{
|
|
unsigned sum1 = 0;
|
|
unsigned long sum2 = 0;
|
|
|
|
while (cb--) {
|
|
sum1 += *pb++;
|
|
|
|
if (sum1 >= 255)
|
|
sum1-= 255;
|
|
|
|
sum2 += sum1;
|
|
}
|
|
|
|
return (unsigned)(sum2 % 255);
|
|
}
|
|
|
|
|
|
#define CheckCks(psh,cksCompare,pmf,pb,cb) \
|
|
if ((psh)->fRobust) CompareCks(cksCompare,pmf,(unsigned char *)(pb),cb)
|
|
|
|
// Read data back in after writing and compare checksum values
|
|
void
|
|
CompareCks(
|
|
CKS cksCompare,
|
|
MF *pmf,
|
|
unsigned char *pb,
|
|
unsigned cb
|
|
)
|
|
{
|
|
if (pmf->fdRead >= 0) {
|
|
SeekMf(pmf, -((POS)cb), 1);
|
|
ReadMf(pmf, pb, cb);
|
|
AssertLogFail(ctRdAfterWr, cksCompare == CksCompute(pb, cb));
|
|
}
|
|
else
|
|
Warn("can't re-read data, fRobust ignored\n");
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Name: CbStatusFromPsh
|
|
// Purpose: determine size that the status file should be
|
|
// Assumes: psh points to a valid SLM status file header
|
|
// Returns: calculated size of status file
|
|
|
|
unsigned long
|
|
CbStatusFromPsh(
|
|
SH *psh
|
|
)
|
|
{
|
|
return ((POS)sizeof(SH) +
|
|
(POS)sizeof(FI) * psh->ifiMac +
|
|
(POS)sizeof(ED) * psh->iedMac +
|
|
(POS)sizeof(FS) * psh->ifiMac * psh->iedMac);
|
|
}
|
|
|
|
|
|
// Load the sh from the file into *psh. Return fTrue if successful.
|
|
F
|
|
FLoadSh(
|
|
AD *pad,
|
|
SH *psh,
|
|
MF *pmf
|
|
)
|
|
{
|
|
POS cbStatus, cbCalc;
|
|
char szPath[cchPthMax];
|
|
|
|
AssertF(psh != 0);
|
|
|
|
cbStatus = SeekMf(pmf, 0, SEEK_END );
|
|
if (cbStatus < sizeof(SH)) {
|
|
SzPhysPath(szPath, pmf->pthReal);
|
|
FatalError("The status file %s\n"
|
|
"\tis incomplete; its size should be at least %u, "
|
|
"but appears to be %ld.\n%s",
|
|
szPath, sizeof(SH), cbStatus, szCallHELP);
|
|
}
|
|
|
|
if (!pad->fMappedIO) {
|
|
SeekMf(pmf, PosSh(), SEEK_SET );
|
|
ReadMf(pmf, (char *)psh, sizeof(SH));
|
|
ValidateSh(ctRead, psh);
|
|
}
|
|
|
|
// Calculate the size this status file should be
|
|
cbCalc = CbStatusFromPsh(psh);
|
|
|
|
if (cbStatus != cbCalc) {
|
|
SzPhysPath(szPath, pmf->pthReal);
|
|
FatalError("The size of status file %s\n"
|
|
"\tis different than indicated in its header; its calculated\n"
|
|
"\tsize is %ld, but its real size appears to be %ld.\n%s",
|
|
szPath, cbCalc, cbStatus, szCallHELP);
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
// loads the rgfi from the file given the information already in pad;
|
|
// Returns fTrue if successful.
|
|
|
|
F
|
|
FLoadFi(
|
|
AD *pad,
|
|
MF *pmf
|
|
)
|
|
{
|
|
AssertF(pad->psh != 0);
|
|
AssertF(pad->rgfi != 0);
|
|
|
|
if (!pad->fMappedIO) {
|
|
SeekMf(pmf, PosRgfi(pad->psh), 0);
|
|
|
|
//if ( pad->psh->ifiMac > MAXFI )
|
|
// FatalError("FI portion of status file can't be > 64K\n");
|
|
|
|
ReadMf(pmf, (char *)pad->rgfi, sizeof(FI) * pad->psh->ifiMac);
|
|
ValidateRgfi(ctRead, pad->rgfi, pad->psh->ifiMac);
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
// loads all or all+1 ed into memory. Returns fTrue if succ
|
|
F
|
|
FLoadEd(
|
|
AD *pad,
|
|
MF *pmf,
|
|
F fFindIedCur
|
|
)
|
|
{
|
|
register SH *psh = pad->psh;
|
|
|
|
AssertF(psh != 0);
|
|
AssertF(pad->rged != 0);
|
|
|
|
if (!pad->fMappedIO) {
|
|
SeekMf(pmf, PosRged(psh), 0);
|
|
|
|
ReadMf(pmf, (char *)pad->rged, sizeof(ED) * psh->iedMac);
|
|
|
|
if (pad->pecmd->cmd != cmdStatus &&
|
|
pad->pecmd->cmd != cmdSsync &&
|
|
pad->pecmd->cmd != cmdLog)
|
|
ValidateRged(psh, ctRead, pad->rged, psh->iedMac);
|
|
}
|
|
|
|
if (fFindIedCur)
|
|
FindIedCur(pad);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
// Find iedCur such that pthURoot == rged[iedCur].pthEd.
|
|
F
|
|
FindIedCur(
|
|
AD *pad
|
|
)
|
|
{
|
|
IED ied;
|
|
|
|
pad->iedCur = FLookupIedCache(pad);
|
|
if (pad->iedCur != iedNil) {
|
|
if (pad->rged == 0)
|
|
return fTrue;
|
|
|
|
if (pad->iedCur < pad->psh->iedMac &&
|
|
PthCmp(pad->pthURoot, pad->rged[pad->iedCur].pthEd) == 0)
|
|
return fTrue;
|
|
|
|
//
|
|
// Otherwise IED cache is bogus, so recompute
|
|
//
|
|
pad->iedCur = iedNil;
|
|
}
|
|
|
|
|
|
|
|
if (pad->rged == 0)
|
|
return fFalse;
|
|
|
|
for (ied = 0; ied < pad->psh->iedMac; ied++) {
|
|
if ((!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) &&
|
|
PthCmp(pad->pthURoot, pad->rged[ied].pthEd) == 0) {
|
|
pad->iedCur = ied;
|
|
FUpdateIedCache(ied, pad);
|
|
return fTrue;
|
|
}
|
|
}
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
// Load the single ed for iedCur, the rgfi and rgfsfi for iedCur
|
|
// Return fTrue if successful.
|
|
|
|
F
|
|
FAllocLoadEdFiFs(
|
|
AD *pad,
|
|
MF *pmf
|
|
)
|
|
{
|
|
register SH *psh;
|
|
FI *rgfi;
|
|
ED *rged1;
|
|
FS *rgfs;
|
|
F fValidate = fFalse;
|
|
|
|
AssertF(pad->psh == 0);
|
|
AssertF(pad->rged == 0);
|
|
AssertF(pad->rged1 == 0);
|
|
AssertF(pad->rgfi == 0);
|
|
AssertF(pad->mpiedrgfs == 0);
|
|
AssertF(pad->rgfs == 0);
|
|
AssertF(pad->fMappedIO == fFalse);
|
|
AssertF(pad->fQuickIO == fTrue);
|
|
AssertF(pad->iedCur != iedNil);
|
|
|
|
if (pad->pecmd->cmd != cmdStatus &&
|
|
pad->pecmd->cmd != cmdSsync &&
|
|
pad->pecmd->cmd != cmdLog)
|
|
fValidate = fTrue;
|
|
|
|
psh = 0;
|
|
rgfi = 0;
|
|
rged1 = 0;
|
|
rgfs = 0;
|
|
if ((psh = PshAlloc(fTrue)) != 0 &&
|
|
FLoadSh(pad, psh, pmf) &&
|
|
FCheckSh(pad, psh) &&
|
|
pad->iedCur < psh->iedMac &&
|
|
PshCommit(psh, &rgfi, &rged1, &rgfs)) {
|
|
|
|
//
|
|
// Read the entire FI array into rgfi
|
|
//
|
|
SeekMf(pmf, PosRgfi(psh), 0);
|
|
ReadMf(pmf, (char *)rgfi, sizeof(FI) * psh->ifiMac);
|
|
ValidateRgfi(ctRead, rgfi, psh->ifiMac);
|
|
|
|
//
|
|
// Read the single ED record for pad->iedCur into rged1
|
|
//
|
|
|
|
SeekMf(pmf, PosEd(psh, pad->iedCur), 0);
|
|
ReadMf(pmf, (char *)rged1, sizeof(ED));
|
|
|
|
//
|
|
// Verify that the iedCache is still correct. If not
|
|
// invalidate it and fail this function so we go the slow
|
|
// way.
|
|
//
|
|
|
|
if (PthCmp(pad->pthURoot, rged1->pthEd) == 0 &&
|
|
NmCmp(pad->nmInvoker, rged1->nmOwner, cchUserMax) == 0) {
|
|
|
|
if (fValidate)
|
|
ValidateRged(psh, ctRead, rged1, 1);
|
|
|
|
//
|
|
// Read the single FS array for pad->iedCur into rgfs
|
|
//
|
|
|
|
SeekMf(pmf, PosRgfsIed(psh, pad->iedCur), 0);
|
|
ReadMf(pmf, (char *)rgfs, sizeof(FS) * psh->ifiMac);
|
|
if (fValidate)
|
|
ValidateRgfs(ctRead, rgfs, psh->ifiMac);
|
|
|
|
pad->psh = psh;
|
|
pad->rgfi = rgfi;
|
|
pad->rged1 = rged1;
|
|
pad->rgfs = rgfs;
|
|
|
|
return fTrue;
|
|
}
|
|
else {
|
|
// if (psh->version == VERSION)
|
|
// Warn("Recomputing cached enlistment index (%u) for %s%s\n",pad->iedCur, pad->pthURoot, pad->pthUSubDir);
|
|
FInvalidateLastLookupIedCache();
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we get here then we could not do it with quick way, so
|
|
// discard any allocations and return fFalse to caller.
|
|
//
|
|
if (psh != 0)
|
|
PshFree(psh, fTrue);
|
|
|
|
pad->fQuickIO = fFalse;
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
// Load the rgrgfs from the file into pad->mpiedrgfs[].
|
|
// Return fTrue if successful.
|
|
|
|
F
|
|
FLoadFs(
|
|
AD *pad,
|
|
MF *pmf
|
|
)
|
|
{
|
|
register SH *psh = pad->psh;
|
|
IED ied;
|
|
|
|
AssertF(psh != 0);
|
|
AssertF(pad->mpiedrgfs != 0);
|
|
|
|
if (!pad->fMappedIO) {
|
|
SeekMf(pmf, PosRgrgfs(psh), 0);
|
|
|
|
// read rgfs vectors for each dir
|
|
for (ied = 0; ied < psh->iedMac; ied++) {
|
|
AssertF(pad->mpiedrgfs[ied] != 0);
|
|
|
|
ReadMf(pmf, (char *)pad->mpiedrgfs[ied],
|
|
sizeof(FS) * psh->ifiMac);
|
|
if (pad->pecmd->cmd != cmdStatus &&
|
|
pad->pecmd->cmd != cmdSsync &&
|
|
pad->pecmd->cmd != cmdLog)
|
|
ValidateRgfs(ctRead, pad->mpiedrgfs[ied], psh->ifiMac);
|
|
}
|
|
}
|
|
|
|
AssertF(!pad->fExtraEd || pad->mpiedrgfs[psh->iedMac] != 0);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
// Write the new status file (or changed ed) if necessary. Run the script.
|
|
void
|
|
FlushStatus(
|
|
AD *pad
|
|
)
|
|
{
|
|
AssertNoMf();
|
|
|
|
if (pad->fWLock) {
|
|
register MF *pmf;
|
|
PTH pth[cchPthMax];
|
|
SH sh;
|
|
|
|
AssertF(!FEmptyNm(pad->nmProj));
|
|
AssertF(!FEmptyPth(pad->pthSRoot));
|
|
AssertLoaded(pad);
|
|
AssertF(pad->psh->lck > lckNil);
|
|
AssertF(FShLockInvariants(pad->psh));
|
|
|
|
// Unlock and write the appropriate parts of the status file.
|
|
// Note that pad->fWLock is not cleared until the script has
|
|
// been safely run.
|
|
|
|
pmf = PmfCreate(PthForStatus(pad, pth), permSysFiles, fFalse, fxGlobal);
|
|
|
|
if (pad->psh->lck == lckEd) {
|
|
AssertF(pad->iedCur != iedNil);
|
|
pmf->mm = mmInstall1Ed;
|
|
// Unlock in Install1Ed.
|
|
Write1Ed(pad, pmf);
|
|
} else {
|
|
pmf->mm = mmInstall;
|
|
|
|
// Operate on a copy of pad->psh, we might still
|
|
// get interrupted.
|
|
|
|
sh = *pad->psh;
|
|
UnlockAll((SH *)&sh);
|
|
WriteAll(pad, (SH *)&sh, pmf);
|
|
}
|
|
|
|
CloseMf(pmf);
|
|
}
|
|
|
|
DeferSignals("installing files");
|
|
|
|
RunScript();
|
|
pad->fWLock = fFalse; // mark status file clean
|
|
|
|
padStatus = 0; // forget AbortStatus' saved pad
|
|
FreeStatus(pad);
|
|
|
|
RestoreSignals();
|
|
}
|
|
|
|
|
|
// Save 1 ed's information during a lckEd access.
|
|
void
|
|
Write1Ed(
|
|
AD *pad,
|
|
MF *pmf
|
|
)
|
|
{
|
|
IED ied = pad->iedCur;
|
|
ED *ped;
|
|
FS *rgfs;
|
|
unsigned cbfs;
|
|
CKS cksCompare;
|
|
|
|
AssertF(pad->psh != 0);
|
|
AssertF(pad->mpiedrgfs != 0);
|
|
AssertF(ied != iedNil);
|
|
AssertF(pad->mpiedrgfs[ied] != 0);
|
|
|
|
// Write ied
|
|
SetCks(pad->psh, cksCompare, &ied, sizeof(ied));
|
|
if (pad->psh->version <= VERSION_64k_EDFI) {
|
|
AssertLogFail(ctWrite, ied < MAXED);
|
|
}
|
|
WriteMf(pmf, (char *)&ied, sizeof(ied));
|
|
CheckCks(pad->psh, cksCompare, pmf, &ied, sizeof(ied));
|
|
|
|
// Write & optionally check ed
|
|
ped = &pad->rged[ied];
|
|
if (pad->psh->version > VERSION_64k_EDFI || fSetTime) {
|
|
if (fVerbose)
|
|
printf("Setting enlistment timestamp...\n");
|
|
ped->wSpare = wStart;
|
|
} else
|
|
if (fVerbose)
|
|
printf("Not Setting enlistment timestamp...\n");
|
|
|
|
SetCks(pad->psh, cksCompare, ped, sizeof(ED));
|
|
ValidateRged(pad->psh, ctWrite, ped, 1);
|
|
WriteMf(pmf, (char *)ped, sizeof(ED));
|
|
CheckCks(pad->psh, cksCompare, pmf, ped, sizeof(ED));
|
|
|
|
// Write & optionally check rgfs
|
|
rgfs = pad->mpiedrgfs[ied];
|
|
cbfs = sizeof(FS) * pad->psh->ifiMac;
|
|
SetCks(pad->psh, cksCompare, rgfs, cbfs);
|
|
ValidateRgfs(ctWrite, rgfs, pad->psh->ifiMac);
|
|
WriteMf(pmf, (char *)rgfs, cbfs);
|
|
CheckCks(pad->psh, cksCompare, pmf, rgfs, cbfs);
|
|
}
|
|
|
|
|
|
void
|
|
WriteAll(
|
|
AD *pad,
|
|
SH *psh,
|
|
MF *pmf
|
|
)
|
|
{
|
|
FlushSh(psh, pmf);
|
|
FlushFi(pad, pmf);
|
|
FlushEd(pad, pmf);
|
|
FlushFs(pad, pmf);
|
|
}
|
|
|
|
|
|
// Write the *psh to the file.
|
|
void
|
|
FlushSh(
|
|
SH *psh,
|
|
MF *pmf
|
|
)
|
|
{
|
|
CKS cksCompare;
|
|
|
|
AssertF(psh != 0);
|
|
|
|
SetCks(psh, cksCompare, psh, sizeof(SH));
|
|
SeekMf(pmf, PosSh(), 0);
|
|
ValidateSh(ctWrite, psh);
|
|
WriteMf(pmf, (char *)psh, sizeof(SH));
|
|
CheckCks(psh, cksCompare, pmf, psh, sizeof(SH));
|
|
}
|
|
|
|
|
|
// Write the rgfi to the file.
|
|
void
|
|
FlushFi(
|
|
AD *pad,
|
|
MF *pmf
|
|
)
|
|
{
|
|
CKS cksCompare;
|
|
unsigned cbfi;
|
|
|
|
AssertLoaded(pad);
|
|
|
|
cbfi = sizeof(FI) * pad->psh->ifiMac;
|
|
|
|
SetCks(pad->psh, cksCompare, pad->rgfi, cbfi);
|
|
SeekMf(pmf, PosRgfi(pad->psh), 0);
|
|
ValidateRgfi(ctWrite, pad->rgfi, pad->psh->ifiMac);
|
|
WriteMf(pmf, (char *)pad->rgfi, cbfi);
|
|
CheckCks(pad->psh, cksCompare, pmf, pad->rgfi, cbfi);
|
|
}
|
|
|
|
|
|
// Write the rged to the file.
|
|
void
|
|
FlushEd(
|
|
AD *pad,
|
|
MF *pmf
|
|
)
|
|
{
|
|
CKS cksCompare;
|
|
unsigned cbed;
|
|
|
|
AssertLoaded(pad);
|
|
|
|
cbed = sizeof(ED) * pad->psh->iedMac;
|
|
|
|
SetCks(pad->psh, cksCompare, pad->rged, cbed);
|
|
SeekMf(pmf, PosRged(pad->psh), 0);
|
|
if (pad->pecmd->cmd != cmdStatus &&
|
|
pad->pecmd->cmd != cmdSsync &&
|
|
pad->pecmd->cmd != cmdLog)
|
|
ValidateRged(pad->psh, ctWrite, pad->rged, pad->psh->iedMac);
|
|
WriteMf(pmf, (char *)pad->rged, cbed);
|
|
CheckCks(pad->psh, cksCompare, pmf, pad->rged, cbed);
|
|
}
|
|
|
|
|
|
// Write the current ed to the file.
|
|
void
|
|
Flush1Ed(
|
|
AD *pad,
|
|
MF *pmf
|
|
)
|
|
{
|
|
AssertF( pad->psh != NULL );
|
|
AssertF( pad->mpiedrgfs != NULL );
|
|
AssertF( pad->iedCur != iedNil );
|
|
AssertF( pad->mpiedrgfs[pad->iedCur] != NULL );
|
|
|
|
SeekMf(pmf, PosEd(pad->psh,pad->iedCur), 0);
|
|
WriteMf(pmf, (char *)&pad->rged[pad->iedCur], sizeof(ED));
|
|
}
|
|
|
|
|
|
// Write the rgfs to the file.
|
|
void
|
|
FlushFs(
|
|
AD *pad,
|
|
MF *pmf
|
|
)
|
|
{
|
|
register SH *psh = pad->psh;
|
|
FS *rgfs;
|
|
IED ied;
|
|
CKS cksCompare;
|
|
unsigned cbfs;
|
|
|
|
AssertLoaded(pad);
|
|
|
|
cbfs = sizeof(FS) * psh->ifiMac;
|
|
|
|
SeekMf(pmf, PosRgrgfs(psh), 0);
|
|
for (ied = 0; ied < psh->iedMac; ied++) {
|
|
if ((rgfs = pad->mpiedrgfs[ied]) != 0) {
|
|
SetCks(psh, cksCompare, rgfs, cbfs);
|
|
if (pad->pecmd->cmd != cmdStatus &&
|
|
pad->pecmd->cmd != cmdSsync &&
|
|
pad->pecmd->cmd != cmdLog)
|
|
ValidateRgfs(ctWrite, rgfs, psh->ifiMac);
|
|
WriteMf(pmf, (char *)rgfs, cbfs);
|
|
CheckCks(psh, cksCompare, pmf, rgfs, cbfs);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Called from RunScript to install the updated information for an ed
|
|
// which has been ssync'd. This merges the current status file (at szStatus)
|
|
// with the new information for one ed (at szTemp).
|
|
//
|
|
// This code is run with interrupts ignored.
|
|
|
|
F
|
|
FInstall1Ed(
|
|
char *szStatus,
|
|
char *szTemp
|
|
)
|
|
{
|
|
AD ad;
|
|
MF *pmfStatus;
|
|
MF *pmfTemp;
|
|
SH sh;
|
|
IED ied;
|
|
FS *rgfs;
|
|
int cbRgfs;
|
|
PTH pthStatus[cchPthMax];
|
|
PTH pthTemp[cchPthMax];
|
|
CKS cksCompare;
|
|
|
|
if (!FPthLogicalSz(pthStatus, szStatus) ||
|
|
!FPthLogicalSz(pthTemp, szTemp))
|
|
AssertF(fFalse);
|
|
|
|
// Might as well load the ied here, outside of the lock region.
|
|
pmfTemp = PmfOpen(pthTemp, omAReadOnly, fxNil);
|
|
ReadMf(pmfTemp, (char *)&ied, sizeof ied);
|
|
AssertF(ied != iedNil);
|
|
|
|
while ((pmfStatus = PmfOpen(pthStatus, omReadWrite, fxNil)) == 0) {
|
|
if (!FQueryApp("cannot open %s", "retry", pthStatus)) {
|
|
CloseMf(pmfTemp);
|
|
return fFalse;
|
|
}
|
|
}
|
|
|
|
ad.fMappedIO = fFalse;
|
|
ad.psh = &sh;
|
|
|
|
if (!FLockMf(pmfStatus)) {
|
|
Error("can't lock %s\n", pthStatus);
|
|
CloseMf(pmfTemp);
|
|
CloseMf(pmfStatus);
|
|
return fFalse;
|
|
}
|
|
|
|
// Status file is now locked. Load the current sh and rged, unlock
|
|
// the rged[ied]. Copy the new rgfs, update the ed and the sh.
|
|
|
|
ad.rged = NULL;
|
|
rgfs = NULL;
|
|
if (!FLoadSh(&ad, ad.psh, pmfStatus) ||
|
|
(ad.rged = (ED *)LpbResStat(ad.psh->iedMac * sizeof(ED))) == 0 ||
|
|
(rgfs = (FS *)LpbResStat(ad.psh->ifiMac * sizeof(FS))) == 0
|
|
) {
|
|
CloseMf(pmfTemp);
|
|
CloseMf(pmfStatus);
|
|
if (ad.rged != NULL) {
|
|
FreeResStat((char *)ad.rged);
|
|
}
|
|
if (rgfs != NULL) {
|
|
FreeResStat((char *)rgfs);
|
|
}
|
|
return fFalse;
|
|
}
|
|
|
|
// read rged from status file
|
|
SeekMf(pmfStatus, PosRged(ad.psh), 0);
|
|
ReadMf(pmfStatus, (char *)ad.rged, ad.psh->iedMac * sizeof(ED));
|
|
ValidateRged(ad.psh, ctRead, ad.rged, ad.psh->iedMac);
|
|
|
|
// read individual ed from temp file
|
|
ReadMf(pmfTemp, (char *)&ad.rged[ied], sizeof(ED));
|
|
ValidateRged(ad.psh, ctRead, ad.rged, ad.psh->iedMac); // check all ED's again!
|
|
UnlockEd(ad.psh, ad.rged, ied);
|
|
|
|
// write single ed to status file
|
|
SetCks(ad.psh, cksCompare, &ad.rged[ied], sizeof(ED));
|
|
SeekMf(pmfStatus, PosEd(ad.psh, ied), 0);
|
|
ValidateRged(ad.psh, ctWrite, &ad.rged[ied], 1);
|
|
WriteMf(pmfStatus, (char *)&ad.rged[ied], sizeof(ED));
|
|
CheckCks(ad.psh, cksCompare, pmfStatus, &ad.rged[ied], sizeof(ED));
|
|
|
|
// read rgfs from temp file
|
|
cbRgfs = ad.psh->ifiMac * sizeof(FS);
|
|
ReadMf(pmfTemp, (char *)rgfs, cbRgfs);
|
|
ValidateRgfs(ctRead, rgfs, ad.psh->ifiMac);
|
|
|
|
// write rgfs to status file
|
|
SetCks(ad.psh, cksCompare, rgfs, cbRgfs);
|
|
SeekMf(pmfStatus, PosRgfsIed(ad.psh, ied), 0);
|
|
ValidateRgfs(ctWrite, rgfs, ad.psh->ifiMac);
|
|
WriteMf(pmfStatus, (char *)rgfs, cbRgfs);
|
|
CheckCks(ad.psh, cksCompare, pmfStatus, rgfs, cbRgfs);
|
|
|
|
FlushSh(ad.psh, pmfStatus);
|
|
|
|
CloseMf(pmfStatus);
|
|
CloseMf(pmfTemp);
|
|
|
|
FreeResStat((char *)rgfs);
|
|
FreeResStat((char *)ad.rged);
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
// Unlock the status file and free the memory associated with the status; may
|
|
// be called after partially loading the status file. All files must be closed.
|
|
//
|
|
// This code can be called from Abort (in which case interrupts are already
|
|
// ignored) and from user's code; to play it safe we also ignore them.
|
|
|
|
void
|
|
AbortStatus(
|
|
void
|
|
)
|
|
{
|
|
register AD *pad = padStatus;
|
|
|
|
AssertNoMf();
|
|
AssertF(padStatus != 0);
|
|
|
|
DeferSignals("aborting");
|
|
|
|
if (padStatus->fWLock) {
|
|
register MF *pmf;
|
|
PTH pth[cchPthMax];
|
|
SH sh;
|
|
SH *psh = &sh;
|
|
|
|
AssertF(pad->psh != 0);
|
|
AssertF(pad->psh->lck != lckNil);
|
|
|
|
if ((pmf = PmfOpen(PthForStatus(pad, pth), omReadWrite, fxNil))== 0) {
|
|
Error("cannot open status for %&P/C to clear lock\nrun sadmin unlock for %&P/C\n", pad, pad);
|
|
goto unlock;
|
|
}
|
|
|
|
if (!FLockMf(pmf)) {
|
|
Error("lock for %&P/C not cleared\nrun sadmin unlock for %&P/C\n", pad, pad);
|
|
goto closeUnlock;
|
|
}
|
|
|
|
if (!FLoadSh(pad, psh, pmf) || !FCheckSh(pad, psh))
|
|
goto closeUnlock;
|
|
|
|
if (pad->psh->lck == lckEd) {
|
|
LCK lckWas;
|
|
CKS cksCompare;
|
|
|
|
AssertF(pad->iedCur != iedNil);
|
|
|
|
// Must load entire rged, it might have changed since it was
|
|
// initially loaded.
|
|
|
|
if (!FLoadEd(pad, pmf, fFalse))
|
|
AssertF(fFalse);
|
|
|
|
lckWas = psh->lck;
|
|
UnlockEd(psh, pad->rged, pad->iedCur);
|
|
|
|
CheckForBreak();
|
|
|
|
// Don't need to write entire rged, just this one
|
|
SetCks(pad->psh, cksCompare, pad->rged + pad->iedCur, sizeof(ED));
|
|
SeekMf(pmf, PosRged(psh) + pad->iedCur * sizeof(ED), 0);
|
|
ValidateRged(pad->psh, ctWrite, pad->rged + pad->iedCur, 1);
|
|
WriteMf(pmf, (char *)(pad->rged + pad->iedCur), sizeof(ED));
|
|
CheckCks(pad->psh, cksCompare, pmf, pad->rged + pad->iedCur, sizeof(ED));
|
|
|
|
// Only need to write sh if it changes the on-disk sh,
|
|
// but write the whole thing out just to be safe.
|
|
|
|
FlushSh(psh, pmf);
|
|
} else {
|
|
UnlockAll(psh);
|
|
FlushSh(psh, pmf);
|
|
}
|
|
|
|
closeUnlock:
|
|
CloseMf(pmf);
|
|
unlock:
|
|
pad->fWLock = fFalse;
|
|
}
|
|
|
|
FreeStatus(pad);
|
|
|
|
padStatus = 0; // forget saved pad
|
|
|
|
RestoreSignals();
|
|
}
|
|
|
|
|
|
// simulate FLoadStatus() for a new status file
|
|
F
|
|
FFakeStatus(
|
|
AD *pad
|
|
)
|
|
{
|
|
register SH *psh;
|
|
MF *pmf;
|
|
PTH pth[cchPthMax];
|
|
|
|
AssertF(!pad->fWLock);
|
|
AssertF(pad->psh == 0);
|
|
AssertF(!FEmptyNm(pad->nmProj));
|
|
AssertF(!FEmptyPth(pad->pthSRoot));
|
|
AssertF(!FEmptyPth(pad->pthURoot));
|
|
|
|
// Create a fake status so the script can rename something.
|
|
// This operation doesn't write a script entry.
|
|
|
|
pmf = PmfAlloc(PthForStatus(pad, pth), (char *)0, fxGlobal);
|
|
if (fVerbose) {
|
|
// print as if we created the original file
|
|
PrErr("Create %!s%s\n", pth, SzForMode(permSysFiles));
|
|
}
|
|
CreateMf(pmf, permSysFiles);
|
|
CloseMf(pmf);
|
|
|
|
psh = PshAlloc(fFalse);
|
|
AssertF(psh != 0);
|
|
pad->psh = psh;
|
|
|
|
// build an sh, pretend it is locked by the user
|
|
INIT_SH(*psh);
|
|
psh->lck = lckAll;
|
|
NmCopy(psh->nmLocker, pad->nmInvoker, cchUserMax);
|
|
|
|
pad->cfiAdd = 0;
|
|
pad->fExtraEd = fFalse;
|
|
pad->iedCur = iedNil;
|
|
|
|
if (!FAllocStatus(pad) || !FInitScript(pad, lckAll)) {
|
|
Abort();
|
|
return fFalse;
|
|
}
|
|
|
|
// Now it is safe to set fWLock.
|
|
pad->fWLock = fTrue;
|
|
|
|
// now use FlushStatus or AbortStatus as appropriate
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
// create a copy of the current status file in $slm/etc/$project/$subdir
|
|
void
|
|
CreateStatus(
|
|
AD *padCur, // current, registered ad
|
|
AD *padNew // should be a copy of pad with a different subdir; not registered for abort
|
|
)
|
|
{
|
|
MF *pmf;
|
|
PTH pth[cchPthMax];
|
|
SH sh;
|
|
SH *pshCur = padCur->psh;
|
|
|
|
AssertF(pshCur != 0);
|
|
AssertF(!FEmptyNm(padCur->nmProj));
|
|
AssertF(!FEmptyPth(padCur->pthSRoot));
|
|
|
|
// make padNew refer to the same status as padCur except that there
|
|
// will be no files in the directory.
|
|
|
|
INIT_SH(sh); // start with pristine SH
|
|
sh.iedMac = pshCur->iedMac;
|
|
sh.pv = pshCur->pv;
|
|
sh.fRelease = pshCur->fRelease;
|
|
sh.fRobust = pshCur->fRobust;
|
|
sh.version = pshCur->version;
|
|
PthCopy(sh.pthSSubDir, padNew->pthSSubDir);
|
|
|
|
padNew->psh = (SH *)&sh; // do NOT free
|
|
padNew->rgfi = padCur->rgfi; // do NOT free
|
|
padNew->cfiAdd = 0;
|
|
padNew->rged = padCur->rged; // copy pointer; do NOT free
|
|
padNew->mpiedrgfs = padCur->mpiedrgfs; // do NOT free
|
|
padNew->fExtraEd = fFalse;
|
|
padNew->iedCur = iedNil;
|
|
padNew->fMappedIO = fFalse;
|
|
|
|
// ensure owner and mode are correct
|
|
pmf = PmfCreate(PthForStatus(padNew, pth), permSysFiles, fTrue, fxGlobal);
|
|
|
|
FlushSh(padNew->psh, pmf);
|
|
// no need to FlushFi since there aren't any
|
|
FlushEd(padNew, pmf);
|
|
FlushFs(padNew, pmf);
|
|
|
|
CloseMf(pmf);
|
|
|
|
// reset the pointers so they will not be freed
|
|
padNew->psh = 0;
|
|
padNew->rgfi = 0;
|
|
padNew->rged = 0;
|
|
padNew->mpiedrgfs = 0;
|
|
}
|
|
|
|
// Called from -p/subdir arg processing, search subdir's status file for a
|
|
// rged[].pthEd which matches the current directory.
|
|
//
|
|
// If found, make pthURoot <- pthEd and pthUSubDir <- pthCWD - pthEd.
|
|
// If not, make pthURoot <- pthCWD and pthUSubDir <- "/".
|
|
|
|
void
|
|
InferUSubDir(
|
|
AD *pad
|
|
)
|
|
{
|
|
IED ied;
|
|
PTH pthEd[2 * cchPthMax]; // enough for pad->rged[ied].pthEd + pad->psh->pthSSubDir
|
|
int cchURoot;
|
|
|
|
if (PthCmp(pad->pthUSubDir, "/") != 0) {
|
|
PthCat(pad->pthURoot, pad->pthUSubDir);
|
|
PthCopy(pad->pthUSubDir, "/");
|
|
}
|
|
|
|
// PthURoot should now be the current working directory.
|
|
|
|
// REVIEW. Assert pthURoot is now the CWD.
|
|
BLOCK {
|
|
PTH pthCWD[cchPthMax];
|
|
|
|
GetCurPth(pthCWD);
|
|
AssertF(PthCmp(pthCWD, pad->pthURoot) == 0);
|
|
}
|
|
|
|
if (!FLoadStatus(pad, lckNil, flsJustEd))
|
|
return;
|
|
|
|
if (pad->iedCur != iedNil) {
|
|
FlushStatus(pad);
|
|
return;
|
|
}
|
|
|
|
// Search through rged[].pthEd for a pthEd which, when concatenated with
|
|
// psh->pthSSubDir, yields pthCWD. (Otherwise similar to FindIedCur).
|
|
|
|
pad->iedCur = iedNil;
|
|
for (ied = 0; ied < pad->psh->iedMac; ied++) {
|
|
if (!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) {
|
|
PthCopy(pthEd, pad->rged[ied].pthEd);
|
|
if (PthCmp(pad->psh->pthSSubDir, "/") != 0)
|
|
PthCat(pthEd, pad->psh->pthSSubDir);
|
|
|
|
if (PthCmp(pad->pthURoot, pthEd) == 0) {
|
|
pad->iedCur = ied;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ied >= pad->psh->iedMac) { // no match?
|
|
FlushStatus(pad);
|
|
return;
|
|
}
|
|
|
|
// Copy remaining part of pthURoot to pthUSubDir. There must be a
|
|
// remaining part because FindIedCur would otherwise have matched
|
|
// pthURoot to some rged[].pthEd.
|
|
|
|
cchURoot = CchOfPth(pad->rged[ied].pthEd);
|
|
AssertF(pad->pthURoot[cchURoot] != 0);
|
|
PthCopy(pad->pthUSubDir, pad->pthURoot + cchURoot);
|
|
pad->pthURoot[cchURoot] = 0;
|
|
|
|
FlushStatus(pad);
|
|
}
|
|
|
|
|
|
|
|
typedef struct _IED_CACHE_FILE_RECORD {
|
|
IED iedCached; /* Cached ED index */
|
|
PTH pthUSubDirCached[cchPthMax]; /* Cached ED subdirectory path */
|
|
} IED_CACHE_FILE_RECORD, *PIED_CACHE_FILE_RECORD;
|
|
|
|
typedef struct _IED_CACHE_FILE {
|
|
IED iedMac; /* Number of cached EDs */
|
|
PTH pthEd[cchPthMax]; /* ED path of root */
|
|
IED_CACHE_FILE_RECORD rgIedCache[1];
|
|
} IED_CACHE_FILE, *PIED_CACHE_FILE;
|
|
|
|
HANDLE hIedCache;
|
|
PIED_CACHE_FILE pIedCache;
|
|
PIED_CACHE_FILE_RECORD pIedCacheLastLookup;
|
|
|
|
/*
|
|
load ied cache information from iedcache.slm in the pthURoot directory
|
|
*/
|
|
PTH pthIedCache[cchPthMax];
|
|
|
|
F
|
|
FLoadIedCache(
|
|
AD *pad
|
|
)
|
|
{
|
|
F fOk;
|
|
|
|
if (pad->flags&(flagSlmRootOverride|flagProjectOverride))
|
|
if (pad->pecmd->cmd != cmdEnlist)
|
|
return fFalse;
|
|
|
|
SzPrint(pthIedCache, "%&/U/iedcache.slm", pad);
|
|
fOk = OpenMappedFile(pthIedCache,
|
|
TRUE,
|
|
FIELD_OFFSET(IED_CACHE_FILE, rgIedCache),
|
|
&hIedCache,
|
|
&pIedCache
|
|
);
|
|
if (fOk == fTrue && pIedCache->iedMac == 0)
|
|
PthCopy(pIedCache->pthEd, pad->pthURoot);
|
|
|
|
pIedCacheLastLookup = NULL;
|
|
return fOk;
|
|
}
|
|
|
|
void
|
|
RemoveIedCache(
|
|
AD *pad
|
|
)
|
|
{
|
|
PTH pth[cchPthMax];
|
|
|
|
FUnloadIedCache();
|
|
SzPrint(pthIedCache, "%&/U/iedcache.slm", pad);
|
|
UnlinkPth(pthIedCache, fxLocal);
|
|
}
|
|
|
|
int
|
|
_CRTAPI1
|
|
IedCacheCmpRoutine(
|
|
const void *key,
|
|
const void *elem
|
|
)
|
|
{
|
|
PTH *pKey = (PTH *)key;
|
|
PIED_CACHE_FILE_RECORD pElem = (PIED_CACHE_FILE_RECORD)elem;
|
|
|
|
// printf("IedCacheCmp( %s, %s ) == %d\n", pKey, pElem->pthUSubDirCached, PthCmp(pKey, pElem->pthUSubDirCached) );
|
|
|
|
return PthCmp(pKey, pElem->pthUSubDirCached);
|
|
}
|
|
|
|
IED
|
|
FLookupIedCache(
|
|
AD *pad
|
|
)
|
|
{
|
|
PIED_CACHE_FILE_RECORD pMatch;
|
|
|
|
if (pad->pecmd->cmd != cmdEnlist && pIedCache != NULL)
|
|
{
|
|
pMatch = bsearch(pad->pthUSubDir,
|
|
pIedCache->rgIedCache,
|
|
pIedCache->iedMac,
|
|
sizeof(IED_CACHE_FILE_RECORD),
|
|
IedCacheCmpRoutine);
|
|
if (pMatch != NULL)
|
|
{
|
|
pIedCacheLastLookup = pMatch;
|
|
return pMatch->iedCached;
|
|
}
|
|
}
|
|
|
|
pIedCacheLastLookup = NULL;
|
|
return iedNil;
|
|
}
|
|
|
|
void
|
|
FInvalidateLastLookupIedCache(void)
|
|
{
|
|
if (pIedCacheLastLookup != NULL)
|
|
pIedCacheLastLookup->iedCached = iedNil;
|
|
|
|
return;
|
|
}
|
|
|
|
int
|
|
_CRTAPI1
|
|
IedCacheSortRoutine(
|
|
const void *arg1,
|
|
const void *arg2
|
|
)
|
|
{
|
|
PIED_CACHE_FILE_RECORD p1 = (PIED_CACHE_FILE_RECORD)arg1;
|
|
PIED_CACHE_FILE_RECORD p2 = (PIED_CACHE_FILE_RECORD)arg2;
|
|
|
|
// printf("IedSortCmp( %s, %s ) == %d\n", p1->pthUSubDirCached, p2->pthUSubDirCached, PthCmp(p1->pthUSubDirCached, p2->pthUSubDirCached) );
|
|
|
|
return PthCmp(p1->pthUSubDirCached, p2->pthUSubDirCached);
|
|
}
|
|
|
|
F
|
|
FUpdateIedCache(
|
|
IED ied,
|
|
AD *pad
|
|
)
|
|
{
|
|
PIED_CACHE_FILE_RECORD pNew;
|
|
unsigned cb;
|
|
|
|
if (pad->pecmd->cmd != cmdDefect && pIedCache != NULL)
|
|
{
|
|
if (pIedCacheLastLookup != NULL) {
|
|
pIedCacheLastLookup->iedCached = ied;
|
|
PthCopy(pIedCacheLastLookup->pthUSubDirCached, pad->pthUSubDir);
|
|
}
|
|
else {
|
|
cb = FIELD_OFFSET(IED_CACHE_FILE, rgIedCache) +
|
|
(pIedCache->iedMac+1) * sizeof(IED_CACHE_FILE_RECORD);
|
|
if (GrowMappedFile(hIedCache, &pIedCache, cb))
|
|
{
|
|
pNew = &pIedCache->rgIedCache[ pIedCache->iedMac ];
|
|
pIedCache->iedMac += 1;
|
|
pNew->iedCached = ied;
|
|
PthCopy(pNew->pthUSubDirCached, pad->pthUSubDir);
|
|
qsort(pIedCache->rgIedCache,
|
|
pIedCache->iedMac,
|
|
sizeof(IED_CACHE_FILE_RECORD),
|
|
IedCacheSortRoutine);
|
|
|
|
// if (pad->psh->version == VERSION)
|
|
// Warn("Remembering enlistment index (%u) for %s\n",ied, pad->pthUSubDir);
|
|
return fTrue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
void
|
|
FUnloadIedCache(void)
|
|
{
|
|
CloseMappedFile(pthIedCache, &hIedCache, &pIedCache);
|
|
return;
|
|
}
|