// IfsCachedDIF.bcpl // Copyright Xerox Corporation 1981 // Last modified January 4, 1982 3:31 PM by Taft get "Ifs.decl" get "IfsSystemInfo.decl" get "IfsFiles.decl" get "IfsRS.decl" external [ // outgoing procedures InitCachedDIF; GetDIF; UpdateCachedDIF; InvalidateCachedDIF // incoming procedures ReadDIF; WriteDIF; WheelCall; MakeQualifiedRName; StripDefaultReg VFileWritePage; LockCell CopyString; StringCompare Lock; Unlock; SysAllocateZero; SysFree; FreePointer DefaultArgs; MoveBlock; IFSError; Block // incoming statics infoVMD; CtxRunning ] static [ @dc = 0 ] // -> in-core version of DC, if nonzero // This module maintains a write-through cache of partial DIFs (Directory // Information Files). The DIF fields maintained are: connectProt, // timeLastValid, miscDirAttributes, password, userGroups, and capabilities. // GetDIF ordinarily returns a DIF record that is valid only for these // fields; however, it can be forced to read the DIF file itself so as to // ensure that all fields are valid. // Updates affecting only these fields may be made by calling // UpdateCachedDIF; these updates will write through to the underlying DIF. // Updates of other fields must be made by calling WriteDIF. // If Grapevine authentication is enabled (enableGrapevineAuth), then // it is possible for the cache to contain DIF entries that do not // correspond to a real DIF file. In this case, of course, updates do not // write through. // Important note on registry qualifications: // If Grapevine authentication is enabled, the cache is always keyed on // fully-qualified names -- regardless of whether such qualification is // meaningful (i.e., corresponds to Grapevine names); however, the cache // also remembers whether or not the underlying DIF name is qualified. // This implies that the cache must be flushed when Grapevine authentication // is enabled or disabled. // Procedures in this module bypass all access control. Any required // access control must be performed by the caller. // Note: the DIF Cache is a temporary structure that does not survive // restarts of IFS. Therefore the data structures may be changed freely. structure DC: // DIF Cache: layout of difPage of Info [ end word // offset of first free word age word // age of cache lock @Lock // interlock for cache activity firstDCE word 0 // first DCE starts here ] structure DCE: // DIF Cache entry [ length word age word // age of most recent access to this entry name word // relative -> directory name owner word // relative -> owner string flags word = [ unqualified bit // true DIF name is not qualified by registry ] // stuff from DIF -- see IfsFiles.decl connectProt @Protection timeLastValid word 2 hintNotUserGroups @Protection // *** following stuff must be parallel to DIF -- from here *** miscDirAttributes word = [ filesOnly bit blank bit 13 validGrapevineRName bit nonexistentDIF bit ] password word size DIF.password/16 userGroups @Protection capabilities @Capabilities // *** to here. *** // end of stuff from DIF strings word 0 ] manifest lenDCE = size DCE/16 // fixed portion only manifest nWordsParallelDIF = (offset DCE.strings - offset DCE.miscDirAttributes)/16 manifest [ stdPageLength = 1 lshift logStdPageLength ecDIFCache = 123 ] //---------------------------------------------------------------------------- let InitCachedDIF() be //---------------------------------------------------------------------------- // Called to initialize or flush the cache. [ if LockDIFCache() then [ dc>>DC.end = offset DC.firstDCE/16 LockCell(lv dc) // no-op if already locked dc = 0 ] ] //---------------------------------------------------------------------------- and GetDIF(name, bypassCache, lvNameDIF; numargs na) = valof //---------------------------------------------------------------------------- // Returns a copy of the DIF corresponding to supplied directory name. // Caller must free it to SysZone when done with it. // Returns zero if no such DIF exists either in the cache or in the // file system. // If name is unqualified, the default registry is assumed. // If name is omitted, the current context's userName is used. // If bypassCache, the DIF is always read from the file system so that all // fields of the result will be valid; and zero is returned if the DIF // does not exist in the file system, even if one does exist in the cache. // If lvNameDIF is supplied, GetDIF stores in @lvDIF a pointer to a new // string containing the true DIF name, which may or may not be qualified // and which may be capitalized differently. [ if na eq 0 then name = 0 // DefaultArgs doesn't work for this DefaultArgs(lv na, -1, false, 0) let localName = MakeQualifiedRName((name ne 0? name, CtxRunning>>RSCtx.userInfo>>UserInfo.userName)) // Try to find entry for name in cache. localName is fully qualified. let dif = 0 let dce = LockDIFCache()? FindCachedDIF(localName), 0 if dce ne 0 then [ // Found name in cache. Copy the true DIF name. CopyString(localName, dce + dce>>DCE.name) if dce>>DCE.unqualified then StripDefaultReg(localName) unless bypassCache do [ // construct DIF containing cached information dif = SysAllocateZero(lenDIF) CopyString(lv dif>>DIF.owner, dce + dce>>DCE.owner) MoveBlock(lv dif>>DIF.connectProt, lv dce>>DCE.connectProt, lenProtection) MoveBlock(lv dif>>DIF.hintNotUserGroups, lv dce>>DCE.hintNotUserGroups, lenProtection) MoveBlock(lv dif>>DIF.timeLastValid, lv dce>>DCE.timeLastValid, 2) MoveBlock(lv dif>>DIF.miscDirAttributes, lv dce>>DCE.miscDirAttributes, nWordsParallelDIF) ] ] // Now localName matches DIF name if DIF was found in cache, // else is fully qualified. Stop here if the DIF was found in the cache // and the cache knows the DIF is nonexistent in the file system. if dif eq 0 & not (dce ne 0 & dce>>DCE.nonexistentDIF) then [ // Not in cache, or forced read; must read DIF from file system. // Note that ReadDIF overwrites its name argument with the true DIF name. let unqualified = false dif = WheelCall(ReadDIF, localName) if dif eq 0 & StripDefaultReg(localName) then [ unqualified = true; dif = WheelCall(ReadDIF, localName) ] if dif ne 0 & dce eq 0 & dc ne 0 then [ // found, put DIF in cache let fullName = MakeQualifiedRName(localName) InsertCachedDIF(name, dif)>>DCE.unqualified = unqualified SysFree(fullName) ] ] dc = 0 test dif ne 0 & lvNameDIF ne 0 ifso @lvNameDIF = localName ifnot SysFree(localName) resultis dif ] //---------------------------------------------------------------------------- and InvalidateCachedDIF(name) be //---------------------------------------------------------------------------- // Invalidates cache entry for name, if there is one. // If name is unqualified, the default registry is assumed. [ if LockDIFCache() then [ let localName = MakeQualifiedRName(name) let dce = FindCachedDIF(localName) if dce ne 0 then DeleteCachedDIF(dce) SysFree(localName) dc = 0 ] ] //---------------------------------------------------------------------------- and UpdateCachedDIF(name, dif) be //---------------------------------------------------------------------------- // Puts dif into the cache, replacing any existing cache entry for name. // Additionally, if an underlying DIF exists, rewrites the underlying DIF // with the new information. Note that only the fields kept in the cache are // rewritten to the underline DIF; remaining fields are unchanged. // If name is unqualified, the default registry is assumed. [ let fullName = MakeQualifiedRName(name) let unqualified = false let difName = 0 // This will fail if there is no underlying DIF, even if there is a // cache entry for name. let rdif = GetDIF(name, true, lv difName) dif>>DIF.nonexistentDIF = rdif eq 0 if rdif ne 0 then [ // Found underlying DIF. Update valid fields from the new DIF // and rewrite it. CopyString(lv rdif>>DIF.owner, lv dif>>DIF.owner) MoveBlock(lv rdif>>DIF.connectProt, lv dif>>DIF.connectProt, lenProtection) MoveBlock(lv rdif>>DIF.hintNotUserGroups, lv dif>>DIF.hintNotUserGroups, lenProtection) MoveBlock(lv rdif>>DIF.timeLastValid, lv dif>>DIF.timeLastValid, 2) MoveBlock(lv rdif>>DIF.miscDirAttributes, lv dif>>DIF.miscDirAttributes, nWordsParallelDIF) WheelCall(WriteDIF, difName, rdif, 0, true) // don't flush from cache unqualified = StringCompare(fullName, difName) ne 0 ] if LockDIFCache() then [ // GetDIF should have brought DIF into the cache already, but // conceivably someone else has sneaked in and flushed it. let dce = FindCachedDIF(fullName) if dce ne 0 then [ unqualified = dce>>DCE.unqualified; DeleteCachedDIF(dce) ] InsertCachedDIF(fullName, dif)>>DCE.unqualified = unqualified dc = 0 ] FreePointer(lv fullName, lv difName, lv rdif) ] // internal procedures //---------------------------------------------------------------------------- and LockDIFCache() = valof //---------------------------------------------------------------------------- // Waits until the DC is free, locks it into memory, dirties it, and // stores a pointer to it in the static dc. // Caller must unlock it simply by storing zero in dc. // Returns true normally, false if the Info file hasn't been // opened yet (early system initialization). [ while dc ne 0 do Block() if infoVMD ne 0 then [ dc = -1 // reserve lock while reading difPage from disk dc = VFileWritePage(infoVMD, difPage) ] resultis dc ne 0 ] //---------------------------------------------------------------------------- and FindCachedDIF(name) = valof //---------------------------------------------------------------------------- // Returns dce for DIF matching name, or zero if name not in cache. // Updates age of matching entry if found. // If Grapevine authentication is enabled, name must be fully qualified. // dc must be locked and dirtied by caller. [ let nameWord0 = name!0 & 177737B // length and char^1, ignoring case let dce = lv dc>>DC.firstDCE while dce ls dc + dc>>DC.end do [ let dceName = dce + dce>>DCE.name if (dceName!0 & 177737B) eq nameWord0 & // hack to speed search StringCompare(name, dceName) eq 0 then [ dc>>DC.age = dc>>DC.age+1; dce>>DCE.age = dc>>DC.age; resultis dce ] dce = dce + dce>>DCE.length ] resultis 0 ] //---------------------------------------------------------------------------- and InsertCachedDIF(name, dif) = valof //---------------------------------------------------------------------------- // Inserts cache entry for name using data from dif; returns pointer to dce. // If Grapevine authentication is enabled, name must be fully qualified. // name must not already exist in the cache. // dc must be locked and dirtied by caller. [ let dce = nil let len = lenDCE + name>>String.length rshift 1 + dif>>DIF.owner.length rshift 1 + 2 until dc>>DC.end+len le stdPageLength do [ // Repeatedly delete oldest entry until new entry will fit let oldest = lv dc>>DC.firstDCE dce = oldest while dce ls dc+dc>>DC.end do [ if dc>>DC.age-dce>>DCE.age ugr dc>>DC.age-oldest>>DCE.age then oldest = dce dce = dce + dce>>DCE.length ] if dce eq oldest then IFSError(ecDIFCache) DeleteCachedDIF(oldest) ] // Make new entry and copy stuff from DIF into it dce = dc + dc>>DC.end dc>>DC.end = dc>>DC.end + len dce>>DCE.length = len dc>>DC.age = dc>>DC.age + 1 dce>>DCE.age = dc>>DC.age dce>>DCE.name = offset DCE.strings/16 CopyString(dce + dce>>DCE.name, name) dce>>DCE.owner = dce>>DCE.name + name>>String.length rshift 1 +1 CopyString(dce + dce>>DCE.owner, lv dif>>DIF.owner) MoveBlock(lv dce>>DCE.connectProt, lv dif>>DIF.connectProt, lenProtection) MoveBlock(lv dce>>DCE.hintNotUserGroups, lv dif>>DIF.hintNotUserGroups, lenProtection) MoveBlock(lv dce>>DCE.timeLastValid, lv dif>>DIF.timeLastValid, 2) MoveBlock(lv dce>>DCE.miscDirAttributes, lv dif>>DIF.miscDirAttributes, nWordsParallelDIF) resultis dce ] //---------------------------------------------------------------------------- and DeleteCachedDIF(dce) be //---------------------------------------------------------------------------- // Deletes dce from cache. // dc must be locked and dirtied by caller. [ let len = dce>>DCE.length MoveBlock(dce, dce+len, dc>>DC.end - (dce+len-dc)) dc>>DC.end = dc>>DC.end - len ]