-- File: NetDirFileAlto.mesa, Last Edit: REH April 20, 1982 1:00 PM DIRECTORY AltoFileDefs USING [FA], Inline USING [COPY, LowHalf], Process USING [Yield], Put USING [Line], Storage USING [Node, String], String USING [ AppendChar, AppendDecimal, AppendString, UpperCase, WordsForString], StreamDefs USING [GetFA, JumpToFA], StringDefs USING [BcplSTRING], System USING [Pulses, GetClockPulses, PulsesToMicroseconds], Time USING [AppendCurrent], File USING [Capability, nullCapability], George USING [ CreateInputStream, Destroy, GetLength, GetWord, GetWords, Handle, LookupExistingFile, SetIndex], NetDirDefs USING [ Addr, AddrOffset, Attribute, Entry, EntryOffset, Header, last, Name, NameOffset, Offset, maxAddrsPerEntry, maxCharsPerName, maxNamesPerEntry, sizeOfBasicName], StatsDefs USING [StatCounterIndex, StatBump], Lock USING [LockDisk, UnlockDisk], NameServerDefs USING [CacheEntry, msg], PupTypes USING [PupAddress]; NetDirFileAlto: MONITOR IMPORTS Inline, Process, Put, Storage, StreamDefs, String, System, Time, George, StatsDefs, Lock, NameServerDefs EXPORTS NameServerDefs = BEGIN OPEN NetDirDefs; PupAddress: TYPE = PupTypes.PupAddress; CacheEntry: TYPE = NameServerDefs.CacheEntry; verbose: BOOLEAN = TRUE; fast: BOOLEAN = TRUE; diskBufferSize: CARDINAL = 350; maxStringWords: CARDINAL = (maxCharsPerName + 1 + 1)/2; maxNameSize: CARDINAL = sizeOfBasicName + maxStringWords; -- assuming that we don't want to look at the attributes maxNodeSize: CARDINAL = MAX[maxNameSize, SIZE[Entry], SIZE[Addr]]; dir: File.Capability ← File.nullCapability; dirStream: George.Handle ← NIL; -- eats up a page for a buffer too version: CARDINAL ← 0; -- 0 if none/unknown numberOfNames, numberOfAddrs: CARDINAL; startOfAddrs, startOfNames, startOfEntrys: AltoFileDefs.FA; dirLength: LONG CARDINAL; statMsScanningFile: PUBLIC StatsDefs.StatCounterIndex; fileName: STRING = "Pup-Network.directory"; CruftyNetworkDirectoryFile: ERROR = CODE; GetDirectoryFile: PUBLIC PROCEDURE RETURNS [File.Capability] = BEGIN RETURN[dir]; END; SearchNetDirForName: PUBLIC ENTRY PROCEDURE [key: STRING, ce: CacheEntry] RETURNS [BOOLEAN] = BEGIN diskBuffer: ARRAY [0..diskBufferSize) OF WORD; name: POINTER TO Name; inc, disp, remainder: CARDINAL; pulses: System.Pulses ← System.GetClockPulses[]; IF dirStream = NIL THEN RETURN[FALSE]; IF ~Lock.LockDisk[fileName, read, fast] THEN RETURN[FALSE]; StreamDefs.JumpToFA[dirStream, @startOfNames]; disp ← diskBufferSize; THROUGH [0..numberOfNames) DO IF disp > (diskBufferSize - maxNameSize) THEN BEGIN -- slide the buffer down and refill the tail Process.Yield[]; remainder ← diskBufferSize - disp; Inline.COPY[to: @diskBuffer, from: @diskBuffer + disp, nwords: remainder]; [] ← George.GetWords[dirStream, @diskBuffer + remainder, disp]; Process.Yield[]; disp ← 0; END; name ← LOOPHOLE[@diskBuffer[disp]]; IF CheckStrings[key, @name.string] THEN BEGIN CopyThingsFromFile[ce, name.entry]; EXIT; END; inc ← 2 + (name.string.length + 2)/2; -- Kludge, but .... IF (inc MOD 2) # 0 THEN inc ← inc + 1; -- MAXC requires this IF inc > 30 THEN ERROR CruftyNetworkDirectoryFile; disp ← disp + inc; REPEAT FINISHED => CopyMissedName[key, ce]; ENDLOOP; pulses ← System.Pulses[System.GetClockPulses[] - pulses]; StatsDefs.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 diskBuffer: ARRAY [0..diskBufferSize) OF WORD; addr: POINTER TO Addr; pulses: System.Pulses ← System.GetClockPulses[]; inc, disp, remainder: CARDINAL; IF dirStream = NIL THEN RETURN[FALSE]; IF ~Lock.LockDisk[fileName, read, fast] THEN RETURN[FALSE]; StreamDefs.JumpToFA[dirStream, @startOfAddrs]; disp ← diskBufferSize; THROUGH [0..numberOfAddrs) DO IF disp > (diskBufferSize - SIZE[Addr]) THEN BEGIN -- slide the buffer down and refill the tail Process.Yield[]; remainder ← diskBufferSize - disp; Inline.COPY[to: @diskBuffer, from: @diskBuffer + disp, nwords: remainder]; [] ← George.GetWords[dirStream, @diskBuffer + remainder, disp]; Process.Yield[]; disp ← 0; END; addr ← LOOPHOLE[@diskBuffer[disp]]; IF key = addr.port THEN BEGIN CopyThingsFromFile[ce, addr.entry]; EXIT; END; inc ← SIZE[Addr] + addr.numberOfAttributes*SIZE[Attribute]; IF inc > 30 THEN ERROR CruftyNetworkDirectoryFile; disp ← disp + inc; REPEAT FINISHED => CopyMissedAddress[key, ce]; ENDLOOP; pulses ← System.Pulses[System.GetClockPulses[] - pulses]; StatsDefs.StatBump[ -- This might overflow 60 serconds statMsScanningFile,Inline.LowHalf[System.PulsesToMicroseconds[pulses]/1000]]; RETURN[TRUE]; END; CopyThingsFromFile: PROCEDURE [ce: CacheEntry, entOff: EntryOffset] = BEGIN addOff: AddrOffset; nameOff: NameOffset; diskBuffer: ARRAY [0..maxNodeSize) OF WORD; entry: POINTER TO Entry = LOOPHOLE[@diskBuffer]; name: POINTER TO Name = LOOPHOLE[@diskBuffer]; addr: POINTER TO Addr = LOOPHOLE[@diskBuffer]; names: ARRAY [0..maxNamesPerEntry) OF STRING; addrs: ARRAY [0..maxAddrsPerEntry) OF PupAddress; n, words: CARDINAL; StreamDefs.JumpToFA[dirStream, @startOfEntrys]; SetIndex[entOff]; [] ← George.GetWords[dirStream, @diskBuffer, SIZE[Entry]]; addOff ← entry.addr; nameOff ← entry.name; StreamDefs.JumpToFA[dirStream, @startOfAddrs]; FOR n ← 0, n + 1 UNTIL addOff = last OR n = maxAddrsPerEntry DO SetIndex[addOff]; [] ← George.GetWords[dirStream, addr, SIZE[Addr]]; addrs[n] ← addr.port; addOff ← addr.next; ENDLOOP; words ← n*SIZE[PupAddress]; ce.size ← ce.size + words; ce.addrs ← DESCRIPTOR[Storage.Node[words], n]; StreamDefs.JumpToFA[dirStream, @startOfNames]; Inline.COPY[to: BASE[ce.addrs], from: @addrs, nwords: words]; FOR n ← 0, n + 1 UNTIL nameOff = last OR n = maxNamesPerEntry DO SetIndex[nameOff]; [] ← George.GetWords[dirStream, addr, maxNodeSize]; words ← String.WordsForString[name.string.length]; ce.size ← ce.size + words; names[n] ← Storage.String[name.string.length]; CopyString[names[n], @name.string]; nameOff ← name.next; ENDLOOP; ce.size ← ce.size + n; ce.names ← DESCRIPTOR[Storage.Node[n], n]; Inline.COPY[to: BASE[ce.names], from: @names, nwords: n]; Lock.UnlockDisk[fileName, fast]; END; CopyMissedName: PROCEDURE [key: STRING, ce: CacheEntry] = BEGIN ce.names ← DESCRIPTOR[Storage.Node[1], 1]; ce.names[0] ← Storage.String[key.length]; String.AppendString[ce.names[0], key]; ce.size ← ce.size + 1 + String.WordsForString[key.length]; Lock.UnlockDisk[fileName, fast]; END; CopyMissedAddress: PROCEDURE [key: PupAddress, ce: CacheEntry] = BEGIN ce.addrs ← DESCRIPTOR[Storage.Node[SIZE[PupAddress]], 1]; ce.addrs[0] ← key; ce.size ← ce.size + SIZE[PupAddress]; Lock.UnlockDisk[fileName, fast]; END; CheckStrings: PROCEDURE [key: STRING, name: POINTER TO StringDefs.BcplSTRING] RETURNS [BOOLEAN] = BEGIN OPEN String; i: CARDINAL; IF key.length # name.length THEN RETURN[FALSE]; FOR i IN [0..key.length) DO IF UpperCase[key[i]] # UpperCase[name.char[i]] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE] END; CopyString: PROCEDURE [key: STRING, name: POINTER TO StringDefs.BcplSTRING] = BEGIN key.length ← 0; FOR i: CARDINAL IN [0..name.length) DO String.AppendChar[key, name.char[i]]; ENDLOOP; END; SetIndex: PROCEDURE [offset: Offset] = BEGIN off: LONG CARDINAL = LONG[LOOPHOLE[offset, CARDINAL]]; IF off > dirLength THEN ERROR CruftyNetworkDirectoryFile; George.SetIndex[dirStream, 2*off]; END; GetDirectoryVersion: PUBLIC PROCEDURE RETURNS [CARDINAL] = BEGIN RETURN[version]; END; OpenDirectoryFile: PUBLIC PROCEDURE = BEGIN IF dirStream = NIL THEN [] ← ResetDirectoryFile[]; END; CloseDirectoryFile: PUBLIC ENTRY PROCEDURE = BEGIN IF dirStream # NIL THEN George.Destroy[dirStream]; dirStream ← NIL; END; ResetDirectoryFile: PUBLIC ENTRY PROCEDURE RETURNS [CARDINAL] = BEGIN header: Header; IF dirStream # NIL THEN George.Destroy[dirStream]; dirStream ← NIL; IF dir = File.nullCapability THEN BEGIN dir ← George.LookupExistingFile[fileName]; IF dir = File.nullCapability THEN RETURN[0]; END; IF ~BlessDirectoryFile[dir, FALSE] THEN RETURN[0]; dirStream ← George.CreateInputStream[dir]; [] ← George.GetWords[dirStream, @header, SIZE[Header]]; dirLength ← George.GetLength[dirStream]; SetIndex[header.nameLookupTable]; SetIndex[George.GetWord[dirStream]]; StreamDefs.GetFA[dirStream, @startOfNames]; SetIndex[header.addrLookupTable]; SetIndex[George.GetWord[dirStream]]; StreamDefs.GetFA[dirStream, @startOfAddrs]; SetIndex[header.firstEntry]; StreamDefs.GetFA[dirStream, @startOfEntrys]; IF verbose THEN BEGIN text: STRING = [75]; Time.AppendCurrent[text]; String.AppendString[text, " Current PupNameLookup Directory version is "L]; String.AppendDecimal[text, header.version]; String.AppendChar[text, '.]; LogString[text]; END; version ← header.version; numberOfNames ← header.numberOfNames; numberOfAddrs ← header.numberOfAddrs; RETURN[version]; END; LogString: PROCEDURE [text: STRING] = BEGIN IF NameServerDefs.msg # NIL THEN Put.Line[NameServerDefs.msg, text]; Put.Line[NIL, text]; END; CheckDirectoryFile: PUBLIC PROCEDURE [file: File.Capability] RETURNS [ok: BOOLEAN] = BEGIN RETURN[BlessDirectoryFile[file, TRUE]]; END; BlessDirectoryFile: PROCEDURE [file: File.Capability, new: BOOLEAN] RETURNS [ok: BOOLEAN] = BEGIN header: Header; temp: George.Handle; length: LONG CARDINAL; message: STRING ← NIL; SetIndex: PROCEDURE [offset: Offset] = BEGIN off: LONG CARDINAL = LONG[LOOPHOLE[offset, CARDINAL]]; IF off > length THEN ERROR CruftyNetworkDirectoryFile; George.SetIndex[temp, 2*off]; END; CheckPosition: PROCEDURE [offset: Offset] = BEGIN off: LONG CARDINAL = LONG[LOOPHOLE[offset, CARDINAL]]; IF off > length THEN ERROR CruftyNetworkDirectoryFile; END; ok ← TRUE; temp ← George.CreateInputStream[file]; BEGIN ENABLE CruftyNetworkDirectoryFile => GOTO Bad; length ← George.GetLength[temp]; message ← "Bad Checksum"L; TestFileChecksum[temp]; George.SetIndex[temp, 0]; IF George.GetWords[temp, @header, SIZE[Header]] # SIZE[Header] THEN BEGIN message ← "File is way too short"L; GOTO Bad; END; -- crude checks to see if file looks ok message ← "Bad internal pointer"L; IF header.numberOfNames > LAST[CARDINAL]/sizeOfBasicName THEN GOTO Bad; SetIndex[header.nameLookupTable]; -- check name offsets THROUGH [0..header.numberOfNames) DO CheckPosition[George.GetWord[temp]]; ENDLOOP; IF header.numberOfAddrs > LAST[CARDINAL]/SIZE[Addr] THEN GOTO Bad; SetIndex[header.addrLookupTable]; -- check address offsets THROUGH [0..header.numberOfAddrs) DO CheckPosition[George.GetWord[temp]]; 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, " PupNameLookup Directory is crufty: "L]; String.AppendString[text, message]; String.AppendChar[text, '.]; LogString[text]; END; END; END; George.Destroy[temp]; END; TestFileChecksum: PROCEDURE [stream: George.Handle] = BEGIN bufferSize: CARDINAL = 100; buffer: ARRAY [0..bufferSize) OF WORD; cs: WORD ← 0; hunk: CARDINAL ← bufferSize; left, length: LONG CARDINAL; length ← George.GetLength[stream]; left ← length/2 - 1; George.SetIndex[stream, 0]; UNTIL left = 0 DO IF left < bufferSize THEN hunk ← Inline.LowHalf[left]; IF George.GetWords[stream, @buffer, hunk] # hunk THEN ERROR CruftyNetworkDirectoryFile; cs ← MoreChecksum[cs, @buffer, hunk]; left ← left - hunk; Process.Yield[]; ENDLOOP; IF cs # George.GetWord[stream] THEN ERROR CruftyNetworkDirectoryFile; END; MoreChecksum: PROCEDURE [cs: WORD, q: POINTER, size: CARDINAL] RETURNS [WORD] = BEGIN p: POINTER TO ARRAY [0..0) OF WORD = q; i, t: CARDINAL; FOR i IN [0..size) DO t ← cs + p[i]; cs ← (IF cs > t THEN t + 1 ELSE t); cs ← (IF cs >= 100000B THEN cs*2 + 1 ELSE cs*2); ENDLOOP; IF cs = 177777B THEN cs ← 0; RETURN[cs]; END; END.