-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. -- NetDirFileTajo.mesa, HGM, 5-Nov-85 19:32:34 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 { MFile.Release[fh]; 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 { MFile.Release[fh]; 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.