-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. -- NetDirFileTajo.mesa, HGM, 16-Mar-85 20:16:03 DIRECTORY Ascii USING [CR], Checksum USING [ComputeChecksum], Environment USING [bytesPerPage], Inline USING [LongCOPY, LowHalf], MFile USING [ AcquireTemp, AddNotifyProc, CopyFileHandle, Error, Filter, GetLength, Handle, ReadOnly, Release, ReleaseChoice, RemoveNotifyProc, Rename, SetAccess, SwapNames], MSegment USING [Address, Create, Delete, Handle], MStream USING [Create], Put USING [Text], Stream USING [Handle], String USING [ AppendChar, AppendDecimal, AppendString, Equivalent, WordsForString], System USING [Pulses, GetClockPulses, PulsesToMicroseconds], Time USING [AppendCurrent], NameServerDefs USING [CacheEntry, FlushWholeCache, KickProber, msg], NetDirDefs, PupTypes USING [PupAddress], ServerHeap USING [CopyString, Node], Stats USING [StatCounterIndex, StatBump]; NetDirFileTajo: MONITOR IMPORTS Checksum, Inline, MFile, MSegment, MStream, Put, String, System, Time, Stats, NameServerDefs, ServerHeap EXPORTS NameServerDefs = BEGIN OPEN NetDirDefs; PupAddress: TYPE = PupTypes.PupAddress; CacheEntry: TYPE = NameServerDefs.CacheEntry; verbose: BOOLEAN = TRUE; useCount: CARDINAL ¬ 0; newVersion, oldVersion: CARDINAL ¬ 0; -- 0 if none/unknown newInTransit, oldInTransit: BOOLEAN ¬ FALSE; oldFile, newFile: MFile.Handle ¬ NIL; seg: MSegment.Handle ¬ NIL; header: LONG POINTER TO NetDirDefs.NewHeader; first: LONG POINTER TO NetDirDefs.NewEntry; Entry: TYPE = LONG POINTER TO NetDirDefs.NewEntry; statMsScanningFile: PUBLIC Stats.StatCounterIndex; oldFileName: STRING = "Pup-Network.directory"; newFileName: STRING = "Pup-Network.big"; CruftyNetworkDirectoryFile: ERROR = CODE; GetStreamForOldDirectoryFile: PUBLIC ENTRY PROCEDURE RETURNS [Stream.Handle] = BEGIN IF oldFile = NIL THEN RETURN[NIL]; RETURN[MStream.Create[MFile.CopyFileHandle[oldFile, []], []]]; END; GetStreamForNewDirectoryFile: PUBLIC ENTRY PROCEDURE RETURNS [Stream.Handle] = BEGIN IF newFile = NIL THEN RETURN[NIL]; RETURN[MStream.Create[MFile.CopyFileHandle[newFile, []], []]]; END; CreateTempFile: PUBLIC PROCEDURE RETURNS [fh: MFile.Handle, sh: Stream.Handle] = BEGIN fh ¬ MFile.AcquireTemp[binary, 256*LONG[Environment.bytesPerPage]]; sh ¬ MStream.Create[MFile.CopyFileHandle[fh, [], writeOnly], []]; END; MakeTempFileIntoOldDirectoryFile: PUBLIC PROCEDURE [fh: MFile.Handle] RETURNS [ok: BOOLEAN] = BEGIN IF ~BlessOldDirectoryFile[fh, TRUE] THEN RETURN[FALSE]; ok ¬ TRUE; MFile.SetAccess[fh, rename]; IF oldFile = NIL THEN BEGIN MFile.Rename[fh, oldFileName]; END ELSE BEGIN old: MFile.Handle ¬ MFile.CopyFileHandle[oldFile, []]; MFile.SetAccess[ old, rename ! MFile.Error => BEGIN ok ¬ FALSE; CONTINUE; END]; IF ok THEN MFile.SwapNames[fh, old]; MFile.Release[old]; END; MFile.Release[fh]; END; MakeTempFileIntoNewDirectoryFile: PUBLIC PROCEDURE [fh: MFile.Handle] RETURNS [ok: BOOLEAN] = BEGIN IF ~BlessNewDirectoryFile[fh, TRUE] THEN RETURN[FALSE]; ok ¬ TRUE; MFile.SetAccess[fh, rename]; IF newFile = NIL THEN BEGIN MFile.Rename[fh, newFileName]; END ELSE BEGIN old: MFile.Handle ¬ MFile.CopyFileHandle[newFile, []]; MFile.SetAccess[ old, rename ! MFile.Error => BEGIN ok ¬ FALSE; CONTINUE; END]; IF ok THEN MFile.SwapNames[fh, old]; MFile.Release[old]; END; MFile.Release[fh]; END; DeleteTempFile: PUBLIC PROCEDURE [fh: MFile.Handle] = BEGIN MFile.Release[fh]; END; SearchNetDirForName: PUBLIC ENTRY PROCEDURE [key: LONG STRING, ce: CacheEntry] RETURNS [BOOLEAN] = BEGIN entry: LONG POINTER TO NetDirDefs.NewEntry ¬ first; pulses: System.Pulses ¬ System.GetClockPulses[]; IF seg = NIL THEN RETURN[FALSE]; FOR i: CARDINAL IN [0..header.numberOfEntries) DO name: LONG STRING ¬ FirstName[entry]; FOR j: CARDINAL IN [0..entry.numberOfNames) DO IF String.Equivalent[key, name] THEN BEGIN CopyThingsFromFile[ce, entry]; GOTO Foundit; END; name ¬ NextName[name]; ENDLOOP; entry ¬ entry + entry.words; REPEAT Foundit => NULL; FINISHED => CopyMissedName[ce, key]; ENDLOOP; pulses ¬ System.Pulses[System.GetClockPulses[] - pulses]; Stats.StatBump[ -- This might overflow 60 serconds statMsScanningFile, Inline.LowHalf[ System.PulsesToMicroseconds[pulses]/1000]]; RETURN[TRUE]; END; SearchNetDirForAddress: PUBLIC ENTRY PROCEDURE [key: PupAddress, ce: CacheEntry] RETURNS [BOOLEAN] = BEGIN entry: LONG POINTER TO NetDirDefs.NewEntry ¬ first; pulses: System.Pulses ¬ System.GetClockPulses[]; IF seg = NIL THEN RETURN[FALSE]; FOR i: CARDINAL IN [0..header.numberOfEntries) DO aaddress: LONG POINTER TO PupAddress ¬ Address[entry]; FOR j: CARDINAL IN [0..entry.numberOfAddresses) DO IF key = aaddress­ THEN BEGIN CopyThingsFromFile[ce, entry]; GOTO Foundit; END; aaddress ¬ aaddress + SIZE[PupAddress]; ENDLOOP; entry ¬ entry + entry.words; REPEAT Foundit => NULL; FINISHED => CopyMissedAddress[ce, key]; ENDLOOP; pulses ¬ System.Pulses[System.GetClockPulses[] - pulses]; Stats.StatBump[ -- This might overflow 60 serconds statMsScanningFile, Inline.LowHalf[ System.PulsesToMicroseconds[pulses]/1000]]; RETURN[TRUE]; END; CopyThingsFromFile: PROCEDURE [ce: CacheEntry, entry: Entry] = BEGIN name: LONG STRING; words: CARDINAL; words ¬ SIZE[PupAddress, entry.numberOfAddresses]; ce.size ¬ ce.size + words; ce.addrs ¬ DESCRIPTOR[ServerHeap.Node[words], entry.numberOfAddresses]; Inline.LongCOPY[to: BASE[ce.addrs], from: Address[entry], nwords: words]; words ¬ SIZE[LONG STRING, entry.numberOfNames]; ce.size ¬ ce.size + words; ce.names ¬ DESCRIPTOR[ServerHeap.Node[words], entry.numberOfNames]; name ¬ FirstName[entry]; FOR i: CARDINAL IN [0..entry.numberOfNames) DO ce.size ¬ ce.size + String.WordsForString[name.length]; ce.names[i] ¬ ServerHeap.CopyString[name]; name ¬ NextName[name]; ENDLOOP; END; CopyMissedName: PROCEDURE [ce: CacheEntry, key: LONG STRING] = BEGIN words: CARDINAL = SIZE[LONG STRING]; ce.names ¬ DESCRIPTOR[ServerHeap.Node[words], 1]; ce.names[0] ¬ ServerHeap.CopyString[key]; ce.size ¬ ce.size + words + String.WordsForString[key.length]; END; CopyMissedAddress: PROCEDURE [ce: CacheEntry, key: PupAddress] = BEGIN words: CARDINAL = SIZE[PupAddress]; ce.addrs ¬ DESCRIPTOR[ServerHeap.Node[words], 1]; ce.addrs[0] ¬ key; ce.size ¬ ce.size + words; END; FirstName: PROCEDURE [entry: Entry] RETURNS [name: LONG STRING] = BEGIN name ¬ LOOPHOLE[entry]; name ¬ name + SIZE[NetDirDefs.NewEntry]; END; NextName: PROCEDURE [name: LONG STRING] RETURNS [LONG STRING] = BEGIN RETURN[name + String.WordsForString[name.length]]; END; Address: PROCEDURE [entry: Entry] RETURNS [address: LONG POINTER TO PupAddress] = BEGIN name: LONG STRING ¬ FirstName[entry]; FOR i: CARDINAL IN [0..entry.numberOfNames) DO name ¬ NextName[name]; ENDLOOP; address ¬ LOOPHOLE[name]; END; -- These routines are actually called from PupDirServer GetOldDirectoryVersion: PUBLIC PROCEDURE RETURNS [CARDINAL, BOOLEAN] = BEGIN RETURN[oldVersion, oldInTransit]; END; GetNewDirectoryVersion: PUBLIC PROCEDURE RETURNS [CARDINAL, BOOLEAN] = BEGIN RETURN[newVersion, newInTransit]; END; OpenDirectoryFiles: PUBLIC ENTRY PROCEDURE = BEGIN IF (useCount ¬ useCount + 1) = 1 THEN BEGIN MFile.AddNotifyProc[InspectOld, [oldFileName, null, readOnly], NIL]; MFile.AddNotifyProc[InspectNew, [newFileName, null, readOnly], NIL]; MFile.AddNotifyProc[InspectOld, [oldFileName, null, writeOnly], NIL]; MFile.AddNotifyProc[InspectNew, [newFileName, null, writeOnly], NIL]; ResetDirectoryFiles[]; END; END; CloseDirectoryFiles: PUBLIC ENTRY PROCEDURE = BEGIN IF useCount # 0 AND (useCount ¬ useCount - 1) = 0 THEN BEGIN MFile.RemoveNotifyProc[InspectOld, [oldFileName, null, readOnly], NIL]; MFile.RemoveNotifyProc[InspectNew, [newFileName, null, readOnly], NIL]; MFile.RemoveNotifyProc[InspectOld, [oldFileName, null, writeOnly], NIL]; MFile.RemoveNotifyProc[InspectNew, [newFileName, null, writeOnly], NIL]; IF seg # NIL THEN BEGIN MSegment.Delete[seg]; seg ¬ NIL; header ¬ NIL; END; IF oldFile # NIL THEN BEGIN MFile.Release[oldFile]; oldFile ¬ NIL; END; IF newFile # NIL THEN BEGIN MFile.Release[newFile]; newFile ¬ NIL; END; END; END; ResetDirectoryFiles: INTERNAL PROCEDURE = BEGIN ResetNewDirectoryFile[]; ResetOldDirectoryFile[]; END; ResetNewDirectoryFile: INTERNAL PROCEDURE = BEGIN newVersion ¬ 0; IF newFile = NIL THEN BEGIN newFile ¬ MFile.ReadOnly[newFileName, [ReleaseNewFile] ! MFile.Error => CONTINUE]; IF newFile = NIL THEN RETURN; END; IF ~BlessNewDirectoryFile[newFile, FALSE] THEN RETURN; IF seg # NIL THEN BEGIN MSegment.Delete[seg]; seg ¬ NIL; END; seg ¬ MSegment.Create[MFile.CopyFileHandle[newFile, []], [ReleaseSeg], 0]; header ¬ MSegment.Address[seg]; first ¬ LOOPHOLE[header]; first ¬ first + SIZE[NetDirDefs.NewHeader]; newVersion ¬ header.version; IF verbose THEN BEGIN text: STRING = [75]; Time.AppendCurrent[text]; String.AppendString[text, " Using "L]; String.AppendString[text, newFileName]; String.AppendString[text, "!"L]; String.AppendDecimal[text, newVersion]; LogString[text]; END; END; ResetOldDirectoryFile: INTERNAL PROCEDURE = BEGIN seg: MSegment.Handle; header: LONG POINTER TO NetDirDefs.Header; oldVersion ¬ 0; IF oldFile = NIL THEN BEGIN oldFile ¬ MFile.ReadOnly[oldFileName, [ReleaseOldFile] ! MFile.Error => CONTINUE]; IF oldFile = NIL THEN RETURN; END; IF ~BlessOldDirectoryFile[oldFile, FALSE] THEN RETURN; seg ¬ MSegment.Create[MFile.CopyFileHandle[oldFile, []], [ReleaseSeg], 0]; header ¬ MSegment.Address[seg]; oldVersion ¬ header.version; IF verbose THEN BEGIN text: STRING = [75]; Time.AppendCurrent[text]; String.AppendString[text, " Using "L]; String.AppendString[text, oldFileName]; String.AppendString[text, "!"L]; String.AppendDecimal[text, oldVersion]; LogString[text]; END; MSegment.Delete[seg]; END; LogString: PROCEDURE [text: LONG STRING] = BEGIN String.AppendChar[text, '.]; String.AppendChar[text, Ascii.CR]; Put.Text[NIL, text]; IF NameServerDefs.msg # NIL THEN Put.Text[NameServerDefs.msg, text]; END; BlessOldDirectoryFile: PROCEDURE [file: MFile.Handle, new: BOOLEAN] RETURNS [ok: BOOLEAN] = BEGIN seg: MSegment.Handle; h: LONG POINTER TO Header; e: EntryBase; n: NameBase; a: AddrBase; at: AddrTableBase; nt: NameTableBase; bytes: LONG CARDINAL ¬ MFile.GetLength[file]; message: STRING ¬ NIL; CheckPosition: PROCEDURE [offset: Offset] = BEGIN off: LONG CARDINAL = LONG[LOOPHOLE[offset, CARDINAL]]; IF off > bytes THEN ERROR CruftyNetworkDirectoryFile; END; ok ¬ TRUE; seg ¬ MSegment.Create[MFile.CopyFileHandle[file, []], [NIL, NIL]]; h ¬ MSegment.Address[seg]; e ¬ LOOPHOLE[h]; n ¬ LOOPHOLE[h]; a ¬ LOOPHOLE[h]; at ¬ LOOPHOLE[h]; nt ¬ LOOPHOLE[h]; BEGIN ENABLE CruftyNetworkDirectoryFile => GOTO Bad; message ¬ "File Too Short"L; IF bytes < 2*SIZE[Header] THEN ERROR CruftyNetworkDirectoryFile; message ¬ "File Too Long"L; IF bytes > 2*LONG[LAST[CARDINAL]] THEN ERROR CruftyNetworkDirectoryFile; message ¬ "Software Checksum Mismatch"L; IF ~TestFileChecksum[file] THEN ERROR CruftyNetworkDirectoryFile; message ¬ "Bad internal pointer"L; IF h.numberOfNames > maxNamesInFile THEN GOTO Bad; FOR i: CARDINAL IN [0..h.numberOfNames) DO CheckPosition[nt[h.nameLookupTable][i]]; ENDLOOP; IF h.numberOfAddrs > maxAddrsInFile THEN GOTO Bad; FOR i: CARDINAL IN [0..h.numberOfAddrs) DO CheckPosition[at[h.addrLookupTable][i]]; ENDLOOP; EXITS Bad => BEGIN ok ¬ FALSE; IF verbose THEN BEGIN text: STRING = [100]; Time.AppendCurrent[text]; String.AppendString[text, " "L]; String.AppendString[text, IF new THEN "New "L ELSE "Old "L]; String.AppendString[text, oldFileName]; String.AppendString[text, " is crufty: "L]; String.AppendString[text, message]; LogString[text]; END; END; END; MSegment.Delete[seg]; END; BlessNewDirectoryFile: PROCEDURE [file: MFile.Handle, new: BOOLEAN] RETURNS [ok: BOOLEAN] = BEGIN seg: MSegment.Handle; header: LONG POINTER TO NewHeader; entry: LONG POINTER TO NewEntry; bytes: LONG CARDINAL ¬ MFile.GetLength[file]; words: LONG CARDINAL = bytes/2; message: STRING ¬ NIL; CheckPosition: PROCEDURE [p: LONG POINTER] = BEGIN off: LONG CARDINAL ¬ LOOPHOLE[p]; off ¬ off - LOOPHOLE[header, LONG CARDINAL]; IF off > words THEN ERROR CruftyNetworkDirectoryFile; END; ok ¬ TRUE; seg ¬ MSegment.Create[MFile.CopyFileHandle[file, []], [NIL, NIL]]; header ¬ MSegment.Address[seg]; entry ¬ LOOPHOLE[header]; entry ¬ entry + SIZE[NewHeader]; BEGIN ENABLE CruftyNetworkDirectoryFile => GOTO Bad; message ¬ "File Too Short"L; IF bytes < 2*SIZE[Header] THEN ERROR CruftyNetworkDirectoryFile; message ¬ "File Too Long"L; message ¬ "Software Checksum Mismatch"L; IF ~TestFileChecksum[file] THEN ERROR CruftyNetworkDirectoryFile; message ¬ "Entry Too Long"L; FOR i: CARDINAL IN [0..header.numberOfEntries) DO name: LONG STRING; CheckPosition[entry]; name ¬ FirstName[entry]; FOR i: CARDINAL IN [0..entry.numberOfNames) DO CheckPosition[name]; name ¬ NextName[name]; ENDLOOP; entry ¬ entry + entry.words; ENDLOOP; EXITS Bad => BEGIN ok ¬ FALSE; IF verbose THEN BEGIN text: STRING = [100]; Time.AppendCurrent[text]; String.AppendString[text, " "L]; String.AppendString[text, IF new THEN "New "L ELSE "Old "L]; String.AppendString[text, newFileName]; String.AppendString[text, " is crufty: "L]; String.AppendString[text, message]; LogString[text]; END; END; END; MSegment.Delete[seg]; END; TestFileChecksum: PROCEDURE [file: MFile.Handle] RETURNS [ok: BOOLEAN] = BEGIN bytes: LONG CARDINAL = MFile.GetLength[file]; words: LONG CARDINAL; seg: MSegment.Handle; p: LONG POINTER; checksum: WORD ¬ 0; IF (bytes MOD 2) # 0 THEN RETURN[FALSE]; words ¬ bytes/2; words ¬ words - 1; -- Checksum seg ¬ MSegment.Create[MFile.CopyFileHandle[file, []], [], 0]; p ¬ MSegment.Address[seg]; UNTIL words = 0 DO clump: CARDINAL = Inline.LowHalf[MIN[words, 10000]]; checksum ¬ Checksum.ComputeChecksum[checksum, clump, p]; p ¬ p + clump; words ¬ words - clump; ENDLOOP; ok ¬ p­ = checksum; MSegment.Delete[seg]; END; ReleaseOldFile: ENTRY PROCEDURE [file: MFile.Handle, instanceData: LONG POINTER] RETURNS [MFile.ReleaseChoice] = BEGIN oldFile ¬ NIL; oldInTransit ¬ TRUE; RETURN[goAhead]; END; ReleaseNewFile: ENTRY PROCEDURE [file: MFile.Handle, instanceData: LONG POINTER] RETURNS [MFile.ReleaseChoice] = BEGIN newFile ¬ NIL; newInTransit ¬ TRUE; RETURN[goAhead]; END; ReleaseSeg: ENTRY PROCEDURE [ segment: MSegment.Handle, instanceData: LONG POINTER] RETURNS [MFile.ReleaseChoice] = BEGIN seg ¬ NIL; RETURN[goAhead]; END; InspectOld: PROCEDURE [ name: LONG STRING, file: MFile.Handle, clientInstanceData: LONG POINTER] RETURNS [BOOLEAN] = BEGIN IF InspectOldLocked[] THEN NameServerDefs.KickProber[]; RETURN[FALSE]; END; InspectOldLocked: ENTRY PROCEDURE RETURNS [BOOLEAN] = BEGIN IF oldFile # NIL THEN BEGIN oldInTransit ¬ FALSE; RETURN[FALSE]; END; -- duplicate supression ResetOldDirectoryFile[]; oldInTransit ¬ FALSE; RETURN[TRUE]; END; InspectNew: PROCEDURE [ name: LONG STRING, file: MFile.Handle, clientInstanceData: LONG POINTER] RETURNS [BOOLEAN] = BEGIN IF InspectNewLocked[] THEN BEGIN IF newVersion # 0 THEN NameServerDefs.FlushWholeCache[]; NameServerDefs.KickProber[]; END; RETURN[FALSE]; END; InspectNewLocked: ENTRY PROCEDURE RETURNS [BOOLEAN] = BEGIN IF newFile # NIL THEN BEGIN newInTransit ¬ FALSE; RETURN[FALSE]; END; -- duplicate supression ResetNewDirectoryFile[]; newInTransit ¬ FALSE; RETURN[TRUE]; END; END.