FSDirImpl.mesa
Copyright © 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
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
= {
Directory Entry Representation
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];
};
BTree key management
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;
};
Error generation
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."]];
};
BTree access primitives, exported to FSDir
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] ] };
Exported to FSBackdoor
TextFromTextRep: PUBLIC PROC [textRep: LONG POINTER TO FSBackdoor.TextRep] RETURNS [r: Rope.Text] = TRUSTED {
r ← Rope.NewText[textRep.length]; GetText[textRep, LOOPHOLE[r]];
};
Directory/cache manipulation procedures, exported to FSDir
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 {
save nameBody for restarting enumeration
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;
this GetEntry checks the created-time from a leader page sometimes
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: BOOLFALSE;
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
do keep processing
BTree.ReadEntry[tree: vDesc.tree, relation: less, key: keyRef, Proc: GetEntry];
IF type = notFound THEN EXIT;
IF lockSet THEN {
IF a.fileLock = none THEN {
can delete this file
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];
};
}.
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