DIRECTORY Ascii USING [Upper], Basics USING [Comparison], BasicTime USING [GMT, nullGMT], BTree USING [DeleteKey, Entry, EntSize, EnumerateEntries, Key, PageSize, PathStk, ReadEntry, Relation, Tree, UpdateEntry, UpdateType], File USING [Error, FP, nullFP, RC, Reason, Volume], FS USING [Error, ErrorDesc, maxFNameLength], FSBackdoor USING [Entry, EntryPtr, EntryType, highestVersion, lowestVersion, MakeFName, TextRep, TextRP, ProduceError, Version], FSDir USING [VersionMatching], FSFileOps USING [DeleteFile, LPCreatedTime, OpenFile, VolumeDesc], FSLock USING [ActiveFile, ReleaseRecord, LockRecord, WaitForRecord], PrincOpsUtils USING [ByteBlt], Rope USING [Cat, Length, NewText, ROPE, Text]; FSDirImpl: CEDAR MONITOR IMPORTS Ascii, BTree, File, FS, FSBackdoor, FSFileOps, FSLock, PrincOpsUtils, Rope EXPORTS FSBackdoor, FSDir = { AppendText: UNSAFE PROC [eP: FSBackdoor.EntryPtr, text: Rope.Text] RETURNS [textRP: FSBackdoor.TextRP] = UNCHECKED { textRep: LONG POINTER TO FSBackdoor.TextRep; textRP _ LOOPHOLE[eP.size]; textRep _ @eP[textRP]; LOOPHOLE[textRep, LONG POINTER TO CARDINAL]^ _ Rope.Length[text]; -- sets textRep.length [] _ PrincOpsUtils.ByteBlt [ to: [ BASE[DESCRIPTOR[textRep]], 0, textRep.length ], from: [ BASE[DESCRIPTOR[text]], 0, textRep.length ] ]; eP.size _ eP.size+SIZE[FSBackdoor.TextRep[textRep.length]]; }; GetText: UNSAFE PROC [textRep: LONG POINTER TO FSBackdoor.TextRep, text: REF TEXT] = UNCHECKED { text.length _ textRep.length; [] _ PrincOpsUtils.ByteBlt [ to: [ BASE[DESCRIPTOR[text]], 0, textRep.length ], from: [ BASE[DESCRIPTOR[textRep]], 0, textRep.length ] ]; }; EntryNameMatches: UNSAFE PROC [name: Rope.Text, entryPtr: FSBackdoor.EntryPtr] RETURNS[BOOL] = UNCHECKED { entryName: LONG POINTER TO FSBackdoor.TextRep = @entryPtr[entryPtr.nameBody]; nameBody: REF READONLY TEXT = LOOPHOLE[name]; IF entryName.length # nameBody.length THEN RETURN[FALSE]; FOR i: CARDINAL IN [ 0 .. nameBody.length ) DO IF Ascii.Upper[entryName[i]] # Ascii.Upper[nameBody[i]] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; KeyObject: TYPE = RECORD [version: FSBackdoor.Version, nameBody: Rope.Text]; stockKey: REF KeyObject _ NIL; CheckoutKey: ENTRY PROC [v: FSBackdoor.Version, nB: Rope.Text] RETURNS [k: REF KeyObject] = { IF stockKey = NIL THEN k _ NEW [KeyObject] ELSE { k _ stockKey; stockKey _ NIL }; k^ _ KeyObject[v, nB]; }; RecycleKey: ENTRY PROC [k: REF KeyObject] = { stockKey _ k; }; NoMoreVersions: PROC [prefix, nameBody: Rope.ROPE, version: FSBackdoor.Version] = { FSBackdoor.ProduceError[noMoreVersions, Rope.Cat["Next version of \"", FSBackdoor.MakeFName[nameBody, version, prefix], "\" would be too big."]]; }; Compare: PUBLIC UNSAFE PROC [key: BTree.Key, entry: BTree.Entry] RETURNS [Basics.Comparison] = UNCHECKED { keyRef: REF KeyObject = NARROW[key]; keyName: REF READONLY TEXT = LOOPHOLE[keyRef.nameBody]; entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; entryName: LONG POINTER TO FSBackdoor.TextRep = @entryPtr[entryPtr.nameBody]; lenKey, lenEntry: CARDINAL; IF keyName = NIL THEN RETURN [less]; lenKey _ keyName.length; lenEntry _ entryName.length; FOR i: CARDINAL IN [ 0 .. MIN[lenKey, lenEntry] ) DO cKey: CHAR = Ascii.Upper[keyName[i]]; cEntry: CHAR = Ascii.Upper[entryName[i]]; SELECT cKey FROM < cEntry => RETURN [less]; > cEntry => RETURN [greater]; ENDCASE; ENDLOOP; SELECT lenKey FROM < lenEntry => RETURN [less]; > lenEntry => RETURN [greater]; ENDCASE => SELECT keyRef.version FROM < entryPtr.version => RETURN [less]; > entryPtr.version => RETURN [greater]; ENDCASE => RETURN [equal]; }; EntrySize: PUBLIC UNSAFE PROC [entry: BTree.Entry] RETURNS [words: BTree.EntSize] = UNCHECKED { RETURN [ MIN[LOOPHOLE[entry, FSBackdoor.EntryPtr].size, BTree.PageSize.LAST] ] }; TextFromTextRep: PUBLIC PROC [textRep: LONG POINTER TO FSBackdoor.TextRep] RETURNS [r: Rope.Text] = TRUSTED { r _ Rope.NewText[textRep.length]; GetText[textRep, LOOPHOLE[r]]; }; EnumerateEntries: PUBLIC PROC [ vDesc: FSFileOps.VolumeDesc, start: Rope.Text, versions: FSDir.VersionMatching, matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOL], acceptProc: PROC RETURNS [stop: BOOL] ] = TRUSTED { whyError: File.Reason; errorDiskPage: INT; { ENABLE File.Error => { whyError _ why; errorDiskPage _ diskPage; GOTO FileError; }; GetAll: UNSAFE PROC [entry: BTree.Entry] RETURNS [continue: BOOL] = { entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; [accept, stop] _ matchProc[ entryPtr ]; IF stop THEN continue _ FALSE ELSE { IF accept THEN { GetText[@entryPtr[entryPtr.nameBody], keyText]; keyRef.version _ entryPtr.version; continue _ FALSE; } ELSE continue _ TRUE; }; }; MatchEntry: UNSAFE PROC [entry: BTree.Entry] = { entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; IF entryPtr = NIL THEN stop _ TRUE ELSE { GetText[@entryPtr[entryPtr.nameBody], keyText]; [accept, stop] _ matchProc[ entryPtr ]; }; }; FindL: UNSAFE PROC [entry: BTree.Entry] = { entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; IF entryPtr = NIL THEN stop _ TRUE ELSE GetText[@entryPtr[entryPtr.nameBody], keyText]; }; keyText: REF TEXT = NEW [ TEXT[FS.maxFNameLength] ]; keyRef: REF KeyObject = CheckoutKey[FSBackdoor.lowestVersion, start]; stop, accept: BOOL; SELECT versions FROM all => { UNTIL BTree.EnumerateEntries[tree: vDesc.tree, relation: greater, key: keyRef, Proc: GetAll] DO IF stop OR (accept AND acceptProc[]) THEN EXIT; keyRef.nameBody _ LOOPHOLE[keyText]; -- key from last iteration ENDLOOP; }; bangLOnly => { BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: MatchEntry]; keyRef^ _ [FSBackdoor.highestVersion, LOOPHOLE[keyText]]; -- for later interations UNTIL stop DO IF accept AND acceptProc[] THEN EXIT; BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: MatchEntry]; ENDLOOP; }; bangHOnly => { stop _ FALSE; BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: FindL]; keyRef^ _ [FSBackdoor.highestVersion, LOOPHOLE[keyText]]; -- for later interations UNTIL stop DO BTree.ReadEntry[tree: vDesc.tree, relation: lessEqual, key: keyRef, Proc: MatchEntry]; IF accept AND acceptProc[] THEN EXIT; BTree.ReadEntry[tree: vDesc.tree, relation: greater, key: keyRef, Proc: FindL]; ENDLOOP; }; ENDCASE => ERROR; RecycleKey[keyRef]; EXITS FileError => ERROR File.Error[whyError, errorDiskPage]; }; }; AcquireOldFName: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, wantedVersion: FSBackdoor.Version, wantedTime: BasicTime.GMT] RETURNS [type: FSBackdoor.EntryType, version: FSBackdoor.Version, keep: CARDINAL, fp: File.FP, time: BasicTime.GMT, attachedTo: Rope.Text, a: FSLock.ActiveFile] = TRUSTED { GetEntry: UNSAFE PROC [entry: BTree.Entry] = { entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; type _ notFound; IF entry = NIL OR NOT EntryNameMatches[nameBody, entryPtr] THEN {outcome _ notFound; RETURN}; version _ entryPtr.version; IF wantedTime # BasicTime.nullGMT THEN { time _ WITH e: entryPtr^ SELECT FROM local => FSFileOps.LPCreatedTime[vDesc.vol, e.fp], attached => e.created, cached => FSFileOps.LPCreatedTime[vDesc.vol, e.fp], ENDCASE => ERROR; IF wantedTime # time THEN {outcome _ wrongTime; RETURN}; }; type _ entryPtr.type; nameBody _ TextFromTextRep[@entryPtr[entryPtr.nameBody]]; -- get caps right [a, lockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, version]; IF lockSet THEN WITH e: entryPtr^ SELECT FROM local => { keep _ e.keep; fp _ e.fp; }; attached => { keep _ e.keep; time _ e.created; attachedTo _TextFromTextRep[@entryPtr[e.attachedTo]]; }; cached => { time _ e.used; fp _ e.fp; }; ENDCASE => ERROR; outcome _ seeLockSet; }; FindAndLock: PROC RETURNS [BOOL] = TRUSTED { DO errorDesc: FS.ErrorDesc; fileRC: File.RC _ ok; errorDiskPage: INT; BTree.ReadEntry[tree: vDesc.tree, relation: search, key: keyRef, Proc: GetEntry ! FS.Error => {errorDesc _ error; CONTINUE}; File.Error => {fileRC _ why; errorDiskPage _ diskPage; CONTINUE} ]; IF errorDesc.group # ok THEN ERROR FS.Error[errorDesc]; IF fileRC # ok THEN ERROR File.Error[fileRC, errorDiskPage]; SELECT outcome FROM notFound => IF search = equal AND wantedTime # BasicTime.nullGMT THEN RETURN[TRUE] -- useful to keep looking ELSE RETURN[FALSE]; wrongTime => RETURN[TRUE]; seeLockSet => IF lockSet THEN RETURN[FALSE] ELSE FSLock.WaitForRecord[a]; ENDCASE => ERROR; ENDLOOP; }; outcome: {notFound, wrongTime, seeLockSet}; lockSet, keepLooking: BOOL; keyRef: REF KeyObject = CheckoutKey [wantedVersion, nameBody]; search: BTree.Relation _ SELECT wantedVersion FROM FSBackdoor.highestVersion => lessEqual, FSBackdoor.lowestVersion => greater, ENDCASE => equal; IF keepLooking _ FindAndLock[] THEN { -- search for version with desired createdTime IF search = lessEqual THEN keyRef.version _ [version - 1] -- already found highest version ELSE { keyRef.version _ FSBackdoor.highestVersion; search _ lessEqual }; WHILE keepLooking DO keepLooking _ FindAndLock[]; keyRef.version _ [version - 1]; ENDLOOP; }; RecycleKey[keyRef]; }; AcquireNextLName: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, newKeep: CARDINAL] RETURNS [a: FSLock.ActiveFile, keep: CARDINAL, fp: File.FP] = TRUSTED { whyError: File.Reason; errorDiskPage: INT; { ENABLE File.Error => { whyError _ why; errorDiskPage _ diskPage; GOTO FileError; }; GetEntry: UNSAFE PROC [entry: BTree.Entry] = { entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; type _ notFound; IF entry # NIL AND EntryNameMatches[nameBody, entryPtr] THEN { keyRef.version _ entryPtr.version; type _ entryPtr.type; WITH e: entryPtr^ SELECT FROM local => { IF keep = 0 THEN keep _ e.keep; entryFP _ e.fp; }; attached => { IF keep = 0 THEN keep _ e.keep; }; ENDCASE => ERROR; } ELSE keyRef.version _ FSBackdoor.lowestVersion; IF count = 0 THEN { IF keyRef.version + 1 = FSBackdoor.highestVersion THEN { noMoreVersions _ TRUE; RETURN }; -- version too big [a, nextLockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, [keyRef.version + 1]]; IF NOT nextLockSet OR keyRef.version < keep THEN RETURN; }; IF type # notFound THEN { count _ count + 1; IF count < keep THEN entryLockSet _ FALSE ELSE [entryA, entryLockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, keyRef.version]; }; }; keyRef: REF KeyObject = CheckoutKey[ , nameBody]; entryFP: File.FP; entryA: FSLock.ActiveFile; entryLockSet, nextLockSet: BOOL; noMoreVersions: BOOL _ FALSE; type: FSBackdoor.EntryType; count: CARDINAL _ 0; fp _ File.nullFP; keep _ newKeep; -- will get reset if =0 and any entries are found DO -- get a name lock on !N keyRef.version _ FSBackdoor.highestVersion; BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry]; IF noMoreVersions THEN NoMoreVersions[vDesc.prefix, nameBody, FSBackdoor.highestVersion]; IF nextLockSet THEN EXIT ELSE FSLock.WaitForRecord[a]; ENDLOOP; IF keyRef.version >= keep THEN UNTIL type = notFound DO -- do keep processing IF entryLockSet THEN { IF entryA.fileLock = none THEN { -- can delete or reuse this file IF NOT BTree.DeleteKey[tree: vDesc.tree, key: keyRef] THEN ERROR; IF type = local THEN { IF fp = File.nullFP THEN fp _ entryFP ELSE FSFileOps.DeleteFile[ FSFileOps.OpenFile[vDesc.vol, entryFP] ]; }; }; FSLock.ReleaseRecord[entryA]; }; BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry]; ENDLOOP; RecycleKey[keyRef]; EXITS FileError => ERROR File.Error[whyError, errorDiskPage]; }; }; DoKeeps: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text] = TRUSTED { whyError: File.Reason; errorDiskPage: INT; { ENABLE File.Error => { whyError _ why; errorDiskPage _ diskPage; GOTO FileError; }; GetEntry: UNSAFE PROC [entry: BTree.Entry] = { entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; IF entry = NIL OR NOT EntryNameMatches[nameBody, entryPtr] THEN {type _ notFound; RETURN}; keyRef.version _ entryPtr.version; type _ entryPtr.type; WITH e: entryPtr^ SELECT FROM local => { IF count = 0 THEN keep _ e.keep; fp _ e.fp }; attached => { IF count = 0 THEN keep _ e.keep }; ENDCASE => ERROR; count _ count + 1; IF count <= keep THEN lockSet _ FALSE ELSE [a, lockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, keyRef.version]; }; keyRef: REF KeyObject = CheckoutKey[FSBackdoor.highestVersion, nameBody]; count: CARDINAL _ 0; fp: File.FP; a: FSLock.ActiveFile; lockSet: BOOL; type: FSBackdoor.EntryType; keep: CARDINAL; BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry]; IF type # notFound AND keyRef.version > keep THEN DO BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry]; IF type = notFound THEN EXIT; IF lockSet THEN { IF a.fileLock = none THEN { IF NOT BTree.DeleteKey[tree: vDesc.tree, key: keyRef] THEN ERROR; IF type = local THEN FSFileOps.DeleteFile[ FSFileOps.OpenFile[vDesc.vol, fp] ]; }; FSLock.ReleaseRecord[a]; }; ENDLOOP; RecycleKey[keyRef]; EXITS FileError => ERROR File.Error[whyError, errorDiskPage]; }; }; AcquireOldGName: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, wantedVersion: FSBackdoor.Version] RETURNS [type: FSBackdoor.EntryType, time: BasicTime.GMT, fp: File.FP, a: FSLock.ActiveFile] = TRUSTED { GetEntry: UNSAFE PROC [entry: BTree.Entry] = { entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; lockSet: BOOL; IF entry = NIL THEN type _ notFound ELSE { type _ entryPtr.type; IF type = cached THEN { nameBody _ TextFromTextRep[@entryPtr[entryPtr.nameBody]]; -- get caps right [a, lockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, wantedVersion]; IF lockSet THEN { WITH e: entryPtr^ SELECT FROM cached => { time _ e.used; fp _ e.fp }; ENDCASE => ERROR; } ELSE a _ NIL; }; }; }; keyRef: REF KeyObject = CheckoutKey[wantedVersion, nameBody]; fileRC: File.RC _ ok; errorDiskPage: INT; BTree.ReadEntry[tree: vDesc.tree, key: keyRef, Proc: GetEntry ! File.Error => {fileRC _ why; errorDiskPage _ diskPage; CONTINUE} ]; IF fileRC # ok THEN ERROR File.Error[fileRC, errorDiskPage]; RecycleKey[keyRef]; }; AcquireOldOrNewGName: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, wantedVersion: FSBackdoor.Version] RETURNS [fp: File.FP, a: FSLock.ActiveFile] = TRUSTED { GetEntry: UNSAFE PROC [entry: BTree.Entry] = { entryPtr: FSBackdoor.EntryPtr = LOOPHOLE[entry]; type _ notFound; fp _ File.nullFP; IF entry # NIL THEN { type _ entryPtr.type; WITH e: entryPtr^ SELECT FROM cached => fp _ e.fp; ENDCASE => ERROR; nameBody _ TextFromTextRep[@entryPtr[entryPtr.nameBody]]; -- get caps right }; [a, lockSet] _ FSLock.LockRecord[vDesc.prefix, nameBody, wantedVersion] }; keyRef: REF KeyObject = CheckoutKey[wantedVersion, nameBody]; type: FSBackdoor.EntryType; lockSet: BOOL; DO fileRC: File.RC _ ok; errorDiskPage: INT; BTree.ReadEntry[tree: vDesc.tree, key: keyRef, Proc: GetEntry ! File.Error => {fileRC _ why; errorDiskPage _ diskPage; CONTINUE} ]; IF fileRC # ok THEN ERROR File.Error[fileRC, errorDiskPage]; IF NOT lockSet THEN FSLock.WaitForRecord[a] ELSE EXIT; ENDLOOP; RecycleKey[keyRef]; }; UpdateLocalEntry: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version, keep: CARDINAL, fp: File.FP, update: BTree.UpdateType] = TRUSTED { WriteEntry: UNSAFE PROCEDURE [entry: BTree.Entry] = { localPtr: LONG POINTER TO FSBackdoor.Entry.local = LOOPHOLE[entry]; localPtr^ _ [ SIZE[FSBackdoor.Entry.local], version, , local [keep, fp] ]; localPtr.nameBody _ AppendText[localPtr, nameBody]; }; keyRef: REF KeyObject = CheckoutKey[version, nameBody]; BTree.UpdateEntry[tree: vDesc.tree, key: keyRef, words: SIZE[FSBackdoor.Entry.local] + SIZE[FSBackdoor.TextRep[Rope.Length[nameBody]]], Proc: WriteEntry, updateType: update]; RecycleKey[keyRef]; }; UpdateCachedEntry: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version, used: BasicTime.GMT, fp: File.FP, update: BTree.UpdateType] = TRUSTED { WriteEntry: UNSAFE PROCEDURE [entry: BTree.Entry] = { cachedPtr: LONG POINTER TO FSBackdoor.Entry.cached = LOOPHOLE[entry]; cachedPtr^ _ [ SIZE[FSBackdoor.Entry.cached], version, , cached [used, fp] ]; cachedPtr.nameBody _ AppendText[cachedPtr, nameBody]; }; keyRef: REF KeyObject = CheckoutKey[version, nameBody]; BTree.UpdateEntry[tree: vDesc.tree, key: keyRef, words: SIZE[FSBackdoor.Entry.cached] + SIZE[FSBackdoor.TextRep[Rope.Length[nameBody]]], Proc: WriteEntry, updateType: update]; RecycleKey[keyRef]; }; UpdateAttachedEntry: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version, keep: CARDINAL, created: BasicTime.GMT, attachedTo: Rope.Text, update: BTree.UpdateType] = TRUSTED { WriteEntry: UNSAFE PROCEDURE [entry: BTree.Entry] = { attachedPtr: LONG POINTER TO FSBackdoor.Entry.attached = LOOPHOLE[entry]; attachedPtr^ _ [ SIZE[FSBackdoor.Entry.attached], version, , attached [keep, created, ] ]; attachedPtr.nameBody _ AppendText[attachedPtr, nameBody]; attachedPtr.attachedTo _ AppendText[attachedPtr, attachedTo]; }; keyRef: REF KeyObject = CheckoutKey[version, nameBody]; BTree.UpdateEntry[tree: vDesc.tree, key: keyRef, words: SIZE[FSBackdoor.Entry.attached] + SIZE[FSBackdoor.TextRep[Rope.Length[nameBody]]] + SIZE[FSBackdoor.TextRep[Rope.Length[attachedTo]]], Proc: WriteEntry, updateType: update]; RecycleKey[keyRef]; }; DeleteEntry: PUBLIC PROC [vDesc: FSFileOps.VolumeDesc, nameBody: Rope.Text, version: FSBackdoor.Version] = { keyRef: REF KeyObject = CheckoutKey[version, nameBody]; [] _ BTree.DeleteKey[tree: vDesc.tree, key: keyRef]; RecycleKey[keyRef]; }; }. FSDirImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Bob Hagmann January 16, 1986 8:23:18 am PST Schroeder, January 19, 1984 1:53 pm Levin, September 22, 1983 12:54 pm Russ Atkinson (RRA) May 13, 1985 8:27:59 pm PDT Directory Entry Representation BTree key management Error generation BTree access primitives, exported to FSDir Exported to FSBackdoor Directory/cache manipulation procedures, exported to FSDir save nameBody for restarting enumeration this GetEntry checks the created-time from a leader page sometimes do keep processing can delete this file Bob Hagmann February 4, 1985 9:46:33 am PST changes to: Copyright Bob Hagmann April 29, 1985 1:47:40 pm PDT Make sure that a File.Error inside of the BTree package does not leave the BTree locked. changes to: EnumerateEntries, DIRECTORY, FSDirImpl, AcquireOldFName, AcquireNextLName, DoKeeps, AcquireOldGName, AcquireOldOrNewGName Bob Hagmann January 16, 1986 8:23:18 am PST In EntrySize, return the maximum possible entry size when it is out of range. This allows BTreeRead.SalvageEntries to read a bogus BTree page, not get a TrapsImpl.BoundsFault, and ignore the page. The maximum possible entry size is also illegal since it did not account for the overhead at the start of the page. changes to: EntrySize Ê– "cedar" style˜codešœ™Kšœ Ïmœ1™˜IMšœžœ˜Mšœ žœ˜ M˜Mšœ žœ˜M˜Mšœžœ˜MšœO˜Ošžœžœžœž˜4Mšœ™MšœO˜OMšžœžœžœ˜šžœžœ˜šžœžœ˜Mšœ™Mšžœžœ0žœžœ˜AMšžœžœ;˜OMšœ˜—Mšœ˜Mšœ˜—Mšžœ˜—M˜šž˜Mšœ žœ%˜7—M˜—Mšœ˜—š Ÿœž œWžœ.žœ žœžœ˜ÛšŸœžœžœ˜.Mšœ žœ˜0Mšœ žœ˜Mšžœ ž˜Mšžœ˜šžœ˜M˜Mšžœ˜šžœ˜Mšœ: ˜KMšœH˜HMšžœ˜ šžœ˜Mšžœžœž˜Mšœ'˜'Mšžœž˜Mšœ˜—Mšžœžœ˜ Mšœ˜—Mšœ˜—Mšœ˜—Mšœžœ2˜=Mšœ žœ˜Mšœžœ˜Mšœwžœ˜ƒMšžœ žœžœ#˜