FSMainImpl1.mesa
Copyright © 1984, 1985, 1986 by Xerox Corporation. All rights reserved.
Exports FS.Open, FS.Create, FS.Copy, FS.Delete, and FS.Rename
See FSMainImpl2 for FS.EnumerateForInfo, FS.EnumerateForNames, FS.FileInfo and FS.SetKeep
Russ Atkinson (RRA) April 28, 1986 12:42:47 pm PDT
Bob Hagmann, November 5, 1985 9:14:20 am PST
DIRECTORY
BasicTime USING [GMT, Now, nullGMT, Period],
BTree USING [SetUpdateInProgress],
File USING [FP, Handle, nullFP],
FS USING [BytesForPages, Create, Error, Lock, Open, OpenFile, PagesForBytes],
FSBackdoor USING [EntryType, GuestProcsRec, highestVersion, lowestVersion, MakeFName, ProduceError, Version],
FSDir USING [AcquireNextLName, AcquireOldFName, AcquireOldOrNewGName, DeleteEntry, UpdateAttachedEntry, UpdateCachedEntry, UpdateLocalEntry],
FSFileOps USING [Copy, CreateFile, CreateFileStream, DeleteFile, GetFileInfo, GetProps, OpenFile, RecordUsage, SetBytesAndCreated, SetFilePages, SetProps, VolumeDesc],
FSLock USING [ActiveFile, LockFile, LockAttachedToRecord, NewOpenFile, ReleaseRecord],
FSName USING [IsLocal, ParseClientName, ParsedFName, ParseName, ServerAndFileRopes, VersionInfo, VersionPartFromVersion],
FSRemoteFile USING [Delete, Info, Rename, Retrieve, Store],
FSReport USING [LockConflict, NoCache, ReportCreation, ReportRemote, UnknownVolumeLName, UnknownFile, VersionSpecified],
IO USING [Close, GetIndex, STREAM],
Rope USING [Concat, Equal, Flatten, Match, ROPE, Text];
FSMainImpl1: CEDAR PROGRAM
IMPORTS BasicTime, BTree, FS, FSBackdoor, FSDir, FSFileOps, FSLock, FSName, FSReport, Rope, FSRemoteFile, IO
EXPORTS FS, FSBackdoor
= BEGIN
Useful types declared externally
ActiveFile: TYPE = FSLock.ActiveFile;
GMT: TYPE = BasicTime.GMT;
Lock: TYPE = FS.Lock;
OpenFile: TYPE = FS.OpenFile;
ParsedFName: TYPE = FSName.ParsedFName;
ROPE: TYPE = Rope.ROPE;
Text: TYPE = Rope.Text;
STREAM: TYPE = IO.STREAM;
Version: TYPE = FSBackdoor.Version;
Local copy of IsGuestProcess procedure, and dummy proc
IsGuestProcess: PROC [] RETURNS [isGuest: BOOL] ← FalseProc;
FalseProc: PROC RETURNS [isGuest: BOOL] = {
isGuest ← FALSE;
};
Exported to FSBackdoor
GuestProcs: PUBLIC REF FSBackdoor.GuestProcsRec ← NEW[FSBackdoor.GuestProcsRec ← [FalseProc]];
RegisterGuestProcs: PUBLIC PROC [newProcs: REF FSBackdoor.GuestProcsRec] = {
GuestProcs ← newProcs;
IsGuestProcess ← newProcs.IsGuestProcess;
};
Exported to FS
Open: PUBLIC PROC [name: ROPE, lock: Lock, wantedCreatedTime: GMT, remoteCheck: BOOL, wDir: ROPE] RETURNS [file: OpenFile] = {
openedName: ROPENIL;
a: ActiveFile;
pn: ParsedFName;
IF IsGuestProcess[] THEN
RETURN [GuestProcs.Open[name, lock, wantedCreatedTime, remoteCheck, wDir]];
[pn, ] ← FSName.ParseClientName[name, wDir, TRUE];
IF FSName.IsLocal[pn.nameBody]
THEN {
open LName
keep: CARDINAL;
IF pn.volDesc = NIL THEN FSReport.UnknownVolumeLName[pn.fullName];
[a, keep] ← GetLName[pn, wantedCreatedTime, remoteCheck];
pn.version ← a.version;
IF lock = read
THEN {
open LName for read
gotLock: BOOL = FSLock.LockFile[a, read];
IF a.attachedTo # NIL
THEN { -- set lock in attachment too
IF gotLock
THEN { IF NOT FSLock.LockFile[a.attachedTo, read] THEN ERROR };
FSLock.ReleaseRecord[a.attachedTo];
};
FSLock.ReleaseRecord[a];
IF NOT gotLock THEN Conflict[pn];
}
ELSE {
open LName for write
IF a.fileLock # none
THEN {
won't be able to get a write lock
FSLock.ReleaseRecord[a.attachedTo]; -- nop if a.attachedTo = NIL
FSLock.ReleaseRecord[a];
Conflict[pn];
};
IF a.attachedTo # NIL THEN {
collapse attachment
ENABLE FS.Error => {
FSLock.ReleaseRecord[a.attachedTo];
FSLock.ReleaseRecord[a]
};
globalNB: Text = Rope.Flatten[a.attachedTo.nameBody];
IF a.attachedTo.fileLock = none AND pn.volDesc.prefix = NIL
THEN
can do it by renaming
RenameToLocalName[pn.volDesc, globalNB, a.attachedTo.version, pn.nameBody, pn.version, a.h, keep]
ELSE {
is open for read or on different volume, so must copy
bytes: INT;
createdTime: GMT;
toFP: File.FP;
[toFP, a.h] ← FSFileOps.CreateFile[pn.volDesc.vol, FSFileOps.GetFileInfo[a.attachedTo.h].pages];
{
ENABLE FS.Error => FSFileOps.DeleteFile[a.h ! FS.Error => CONTINUE];
[bytes, createdTime] ← FSFileOps.Copy[a.attachedTo.h, a.h];
FSFileOps.SetProps[a.h, bytes, keep, createdTime, pn.nameBody, pn.version];
};
FSDir.UpdateLocalEntry[pn.volDesc, globalNB, a.version, keep, toFP, replace];
};
FSLock.ReleaseRecord[a.attachedTo];
a.attachedTo ← NIL;
};
IF NOT FSLock.LockFile[a, write] THEN ERROR;
FSLock.ReleaseRecord[a];
FSFileOps.SetBytesAndCreated[a.h, -1, BasicTime.Now[]]; -- update create-time
};
}
ELSE {
open GName
gotLock: BOOL;
IF lock = write THEN GlobalWriteLock[];
IF pn.volDesc = NIL THEN FSReport.NoCache[pn.fullName];
a ← GetGName[pn, wantedCreatedTime, remoteCheck];
gotLock ← FSLock.LockFile[a, read];
FSLock.ReleaseRecord[a];
IF NOT gotLock THEN Conflict[pn];
};
file ← FSLock.NewOpenFile[a];
};
Create: PUBLIC PROC [name: ROPE, setPages: BOOL, pages: INT, setKeep: BOOL, keep: CARDINAL, wDir: ROPE] RETURNS [file: OpenFile] = {
IF IsGuestProcess[] THEN RETURN [GuestProcs.Create[name, setPages, pages, setKeep, keep, wDir]];
RETURN[CreateBody[name, setPages, pages, setKeep, keep, wDir, FALSE]];
};
CreateVMBacking: PUBLIC PROC [name: ROPE, setPages: BOOL, pages: INT, setKeep: BOOL, keep: CARDINAL, wDir: ROPE] RETURNS [file: OpenFile] = {
RETURN[CreateBody[name, setPages, pages, setKeep, keep, wDir, TRUE]];
};
CreateBody: PUBLIC PROC [name: ROPE, setPages: BOOL, pages: INT, setKeep: BOOL, keep: CARDINAL, wDir: ROPE, VMBackingFile: BOOL] RETURNS [file: OpenFile] = {
a: ActiveFile;
newKeep: CARDINAL;
fp: File.FP;
pn: ParsedFName;
vI: FSName.VersionInfo;
[pn, vI] ← FSName.ParseClientName[name, wDir, TRUE];
IF NOT FSName.IsLocal[pn.nameBody]
THEN FSBackdoor.ProduceError[globalCreation, "FS.Create illegal for a GName"];
IF pn.volDesc = NIL THEN FSReport.UnknownVolumeLName[pn.fullName];
IF vI # missing THEN FSReport.VersionSpecified[pn.fullName];
[a, newKeep, fp] ← InnerCreate[pn.volDesc, pn.nameBody, setPages, pages, setKeep, keep, VMBackingFile];
pn.version ← a.version; -- the real version number
FSFileOps.SetProps[a.h, 0, newKeep, BasicTime.Now[], pn.nameBody, pn.version
! FS.Error => FSLock.ReleaseRecord[a] ];
FSDir.UpdateLocalEntry[pn.volDesc, pn.nameBody, pn.version, newKeep, fp, insert];
IF NOT FSLock.LockFile[a, write] THEN ERROR;
FSLock.ReleaseRecord[a];
file ← FSLock.NewOpenFile[a];
};
OpenOrCreate: PUBLIC PROC [name: ROPE, keep: CARDINAL, pages: INT, wDir: ROPE] RETURNS [file: OpenFile] = {
needToCreate: BOOLFALSE;
file ← FS.Open[name: name, lock: write, wDir: wDir
! FS.Error => IF error.code = $unknownFile THEN {needToCreate ← TRUE; CONTINUE} ];
IF needToCreate
THEN file ← FS.Create [name: name, pages: pages, keep: keep, wDir: wDir];
};
Copy: PUBLIC PROC [from, to: ROPE, setKeep: BOOL, keep: CARDINAL, wantedCreatedTime: GMT, remoteCheck: BOOL, attach: BOOL, wDir: ROPE] RETURNS [toFName: ROPE] = {
fpn, tpn: ParsedFName;
tvI: FSName.VersionInfo;
IF IsGuestProcess[] THEN toFName ← GuestProcs.Copy[from, to, setKeep, keep,wantedCreatedTime, remoteCheck, attach, wDir];
[tpn, tvI] ← FSName.ParseClientName[to, wDir, TRUE];
IF tvI # missing THEN FSReport.VersionSpecified[tpn.fullName];
[fpn, ] ← FSName.ParseClientName[from, wDir, TRUE];
toFName ← InnerCopy[fpn, tpn, setKeep, keep, wantedCreatedTime, remoteCheck, attach].toFName;
IF FSName.IsLocal[toFName] THEN FSReport.ReportCreation[copyTo, toFName];
};
Delete: PUBLIC PROC [name: ROPE, wantedCreatedTime: GMT, wDir: ROPE] = {
pn: ParsedFName;
IF IsGuestProcess[] THEN {
GuestProcs.Delete[name, wantedCreatedTime, wDir];
}
ELSE {
[pn, ] ← FSName.ParseClientName[name, wDir, FALSE];
InnerDelete[pn, wantedCreatedTime];
};
};
Rename: PUBLIC PROC [from, to: ROPE, setKeep: BOOL, keep: CARDINAL, wantedCreatedTime: GMT, wDir: ROPE] = {
fpn, tpn: ParsedFName;
tvI: FSName.VersionInfo;
toFName: ROPE;
localFrom, localTo: BOOL;
fromType: FSBackdoor.EntryType;
fromFP: File.FP;
fromA: ActiveFile ← NIL;
IF IsGuestProcess[]
THEN {
GuestProcs.Rename[from, to, setKeep, keep, wantedCreatedTime, wDir];
}
ELSE {
[tpn, tvI] ← FSName.ParseClientName[to, wDir, TRUE];
IF tvI # missing THEN FSReport.VersionSpecified[tpn.fullName];
[fpn, ] ← FSName.ParseClientName[from, wDir, TRUE];
localFrom ← FSName.IsLocal[fpn.nameBody];
localTo ← FSName.IsLocal[tpn.nameBody];
SELECT TRUE FROM
localFrom AND localTo => {
IF fpn.volDesc = NIL
THEN FSReport.UnknownVolumeLName[fpn.fullName];
IF tpn.volDesc = NIL
THEN FSReport.UnknownVolumeLName[tpn.fullName];
IF fpn.volDesc = tpn.volDesc THEN {
attachedTo: Text;
created: GMT;
toA: ActiveFile;
[fromType, fpn.version, , fromFP, created, attachedTo, fromA] ← FSDir.AcquireOldFName[fpn.volDesc, fpn.nameBody, fpn.version, wantedCreatedTime];
SELECT fromType FROM
notFound =>
FSReport.UnknownFile[fpn.fullName, wantedCreatedTime];
local, attached => {
from local file to local file
ENABLE FS.Error => FSLock.ReleaseRecord[fromA];
IF fromA.fileLock # none THEN Conflict[fpn];
[toA, keep, ] ← NewLName[tpn.volDesc, tpn.nameBody, FALSE, setKeep, keep];
tpn.version ← toA.version;
IF fromType = local
THEN {
unattached local to local on same volume
ENABLE FS.Error => FSLock.ReleaseRecord[toA];
RenameToLocalName[fpn.volDesc, fpn.nameBody, fpn.version, tpn.nameBody, tpn.version, FSFileOps.OpenFile[fpn.volDesc.vol, fromFP], keep];
}
ELSE {
attached local to local
FSDir.DeleteEntry[fpn.volDesc, fpn.nameBody, fpn.version];
FSDir.UpdateAttachedEntry[tpn.volDesc, tpn.nameBody, tpn.version, keep, created, attachedTo, insert];
};
toFName ← FSBackdoor.MakeFName[toA.nameBody, toA.version];
FSLock.ReleaseRecord[fromA];
FSLock.ReleaseRecord[toA];
FSReport.ReportCreation[renameTo, toFName];
RETURN;
};
ENDCASE => ERROR;
};
};
NOT localFrom AND NOT localTo => {
startedRenaming: BOOLFALSE;
fromServer, toServer, fromFile, toFile: ROPE;
[fromServer, fromFile] ← FSName.ServerAndFileRopes[fpn.fullName];
[toServer, toFile] ← FSName.ServerAndFileRopes[tpn.fullName];
IF Rope.Equal[fromServer, toServer, FALSE]
THEN {
both GNames on the same server
ENABLE FS.Error, ABORTED => {
FSLock.ReleaseRecord[fromA];
IF startedRenaming
THEN {
FSReport.ReportRemote[endRenaming, fpn.fullName];
startedRenaming ← FALSE;
};
};
ConfirmRename: PROC [v: Version] RETURNS [BOOL] = {
fpn.version ← v;
IF fpn.volDesc # NIL
THEN {
have a cache
[fromFP, fromA] ← FSDir.AcquireOldOrNewGName[fpn.volDesc, fpn.nameBody, fpn.version];
IF fromA.fileLock # none THEN Conflict[fpn];
};
fpn.fullName ← FSBackdoor.MakeFName[fpn.nameBody, fpn.version];
FSReport.ReportRemote[startRenaming, fpn.fullName];
startedRenaming ← TRUE;
RETURN [TRUE];
};
fromFP ← File.nullFP;
FSRemoteFile.Rename[fromServer, fromFile, wantedCreatedTime, toFile, ConfirmRename];
IF fromFP # File.nullFP THEN {
from file is cached, so must be deleted
FSDir.DeleteEntry[fpn.volDesc, fpn.nameBody, fpn.version];
FSFileOps.DeleteFile[FSFileOps.OpenFile[fpn.volDesc.vol, fromFP]];
};
FSReport.ReportRemote[endRenaming, fpn.fullName];
FSLock.ReleaseRecord[fromA];
RETURN;
};
};
ENDCASE;
[wantedCreatedTime, toFName] ← InnerCopy[fpn, tpn, setKeep, keep, wantedCreatedTime, FALSE, FALSE];
InnerDelete[fpn, wantedCreatedTime];
IF FSName.IsLocal[toFName] THEN FSReport.ReportCreation[renameTo, toFName];
};
};
Internal procedures
Conflict: PROC [pn: ParsedFName] = {
FSReport.LockConflict[pn.volDesc.prefix, pn.nameBody, pn.version] };
GlobalWriteLock: PROC = {
FSBackdoor.ProduceError[globalWriteLock, "Can not open a GName with a write lock"];
};
GetLName: PROC [pn: ParsedFName, wantedCreatedTime: GMT, remoteCheck: BOOL] RETURNS [a: ActiveFile, keep: CARDINAL] = {
attachedTo: Text;
attachmentCreatedTime: GMT;
type: FSBackdoor.EntryType;
fp: File.FP;
[type, pn.version, keep, fp, attachmentCreatedTime, attachedTo, a] ← FSDir.AcquireOldFName[pn.volDesc, pn.nameBody, pn.version, wantedCreatedTime];
SELECT type FROM
notFound =>
FSReport.UnknownFile[pn.fullName, wantedCreatedTime];
local =>
IF a.h = NIL THEN
a.h ← FSFileOps.OpenFile[pn.volDesc.vol, fp
! FS.Error => FSLock.ReleaseRecord[a] ];
attached =>
IF a.attachedTo = NIL
THEN {
ENABLE FS.Error, ABORTED => FSLock.ReleaseRecord[a];
gpn: ParsedFName = FSName.ParseName[NIL, attachedTo];
IF gpn.volDesc = NIL THEN FSReport.UnknownVolumeLName[gpn.fullName];
a.attachedTo ← GetGName[gpn, attachmentCreatedTime, remoteCheck];
a.h ← a.attachedTo.h;
}
ELSE FSLock.LockAttachedToRecord[a];
ENDCASE => ERROR;
};
GetGName: PROC [pn: ParsedFName, wantedCreatedTime: GMT, remoteCheck: BOOL] RETURNS [a: ActiveFile] = {
usedTime: GMT;
server, file: ROPE;
type: FSBackdoor.EntryType;
fp: File.FP;
[server, file] ← FSName.ServerAndFileRopes[pn.fullName];
IF remoteCheck AND pn.version NOT IN (FSBackdoor.lowestVersion .. FSBackdoor.highestVersion) AND wantedCreatedTime = BasicTime.nullGMT THEN
[version: pn.version, created: wantedCreatedTime] ← FSRemoteFile.Info[server, file, BasicTime.nullGMT];
[type, pn.version, , fp, usedTime, , a] ← FSDir.AcquireOldFName[pn.volDesc, pn.nameBody, pn.version, wantedCreatedTime];
SELECT type FROM
notFound => {
need to go to remote server
RetrieveProc: PROC
[fullGName: ROPE, bytes: INT, created: GMT] RETURNS [STREAM] = {
pn ← FSName.ParseName[NIL, fullGName];
[fp, a] ← FSDir.AcquireOldOrNewGName[pn.volDesc, pn.nameBody, pn.version];
What if STP times out while we're waiting?
wantedCreatedTime ← created;
IF fp = File.nullFP
THEN {
not in the cache, so must create and retrieve it
[fp, a.h] ← FSFileOps.CreateFile[pn.volDesc.vol, FS.PagesForBytes[bytes]];
fileCreated ← TRUE;
fileStream ← FSFileOps.CreateFileStream[a.h, newAppendOnly];
FSReport.ReportRemote[startRetrieving, pn.fullName];
}
ELSE {
something is in the cache
IF a.h = NIL
THEN {
not already open, so have name lock
a.h ← FSFileOps.OpenFile[pn.volDesc.vol, fp];
IF wantedCreatedTime # FSFileOps.GetProps[a.h].created THEN {
wrong created time in the cache
fileToDelete ← a.h; -- remember this file for later deletion
[fp, a.h] ← FSFileOps.CreateFile[pn.volDesc.vol, FS.PagesForBytes[bytes]];
fileCreated ← TRUE;
fileStream ← FSFileOps.CreateFileStream[a.h, newAppendOnly];
FSReport.ReportRemote[startRetrieving, pn.fullName];
};
}
ELSE {
already open, so have read lock and can't delete
IF wantedCreatedTime # FSFileOps.GetProps[a.h].created THEN
Conflict[pn];
};
};
RETURN[fileStream];
};
fileStream: STREAMNIL;
fileToDelete: File.Handle ← NIL;
fileCreated: BOOLFALSE;
a ← NIL;
FSRemoteFile.Retrieve[server, file, wantedCreatedTime, RetrieveProc
! FS.Error, ABORTED => {
IF fileStream # NIL THEN {
fileStream.Close[ ! FS.Error => CONTINUE ];
fileStream ← NIL; -- ABORTED can follow an FS.Error
FSReport.ReportRemote[endRetrieving, pn.fullName];
};
IF fileCreated THEN {
FSFileOps.DeleteFile[a.h ! FS.Error => CONTINUE ];
fileCreated ← FALSE; -- ABORTED can follow an FS.Error
};
FSLock.ReleaseRecord[a];
} ];
IF fileStream # NIL THEN {
new file was created and filled in
ENABLE FS.Error => FSLock.ReleaseRecord[a];
byteCount: INT = fileStream.GetIndex[];
neededPages: INT = FS.PagesForBytes[byteCount];
FSReport.ReportRemote[endRetrieving, pn.fullName];
fileStream.Close[];
IF neededPages # FSFileOps.GetFileInfo[a.h].pages
THEN FSFileOps.SetFilePages[a.h, neededPages];
FSFileOps.SetProps[a.h, byteCount, 0, wantedCreatedTime, pn.nameBody, pn.version];
usedTime ← BasicTime.Now[];
FSDir.UpdateCachedEntry[pn.volDesc, pn.nameBody, pn.version, usedTime, fp, IF fileToDelete = NIL THEN insert ELSE replace ];
FSFileOps.RecordUsage[fp, usedTime];
IF fileToDelete # NIL THEN
FSFileOps.DeleteFile[fileToDelete ! FS.Error => CONTINUE ];
};
};
cached => {
already in cache
IF a.h = NIL THEN
a.h ← FSFileOps.OpenFile[pn.volDesc.vol, fp
! FS.Error => FSLock.ReleaseRecord[a] ];
UpdateUsedTime[pn, fp, usedTime];
};
ENDCASE => ERROR;
};
RenameToLocalName: PROC [vDesc: FSFileOps.VolumeDesc, fNB: Text, fV: Version, tNB: Text, tV: Version, h: File.Handle, k: CARDINAL] = {
BTree.SetUpdateInProgress[vDesc.tree, TRUE];
FSDir.DeleteEntry[vDesc, fNB, fV];
{ ENABLE FS.Error => BTree.SetUpdateInProgress[vDesc.tree, FALSE];
FSFileOps.SetProps[ h, -1, k, BasicTime.nullGMT, tNB, tV];
FSDir.UpdateLocalEntry[vDesc, tNB, tV, k, FSFileOps.GetFileInfo[h].fp, insertOrReplace];
};
BTree.SetUpdateInProgress[vDesc.tree, FALSE];
};
usedTimeGrain: INT = 600; --in seconds
UpdateUsedTime: PROC [gpn: ParsedFName, fp: File.FP, usedTime: GMT] = {
IF BasicTime.Period[from: usedTime, to: BasicTime.Now[]] > usedTimeGrain THEN {
usedTime ← BasicTime.Now[];
FSDir.UpdateCachedEntry[gpn.volDesc, gpn.nameBody, gpn.version, usedTime, fp, replace];
FSFileOps.RecordUsage[fp, usedTime];
};
};
InnerCreate: PROC [vDesc: FSFileOps.VolumeDesc, body: Text, setPages: BOOL, pages: INT, setKeep: BOOL, keep: CARDINAL, VMBackingFile: BOOLFALSE] RETURNS [a: ActiveFile, newKeep: CARDINAL, fp: File.FP] = {
[a, newKeep, fp] ← NewLName[vDesc, body, TRUE, setKeep, keep];
{
ENABLE FS.Error => FSLock.ReleaseRecord[a];
IF fp = File.nullFP
THEN {
[fp, a.h] ← FSFileOps.CreateFile[vDesc.vol, pages, VMBackingFile];
setPages ← FALSE;
}
ELSE a.h ← FSFileOps.OpenFile[vDesc.vol, fp];
IF setPages THEN FSFileOps.SetFilePages[a.h, pages];
};
};
InnerCopy: PROC [fpn, tpn: ParsedFName, setKeep: BOOL, keep: CARDINAL, wantedCreatedTime: GMT, remoteCheck: BOOL, attach: BOOL] RETURNS [createdTime: GMT, toFName: ROPE] = {
localFrom: BOOL = FSName.IsLocal[fpn.nameBody];
localTo: BOOL = FSName.IsLocal[tpn.nameBody];
IF (fpn.volDesc = NIL AND localFrom) THEN
FSReport.UnknownVolumeLName[fpn.fullName];
IF (tpn.volDesc = NIL AND localTo) THEN
FSReport.UnknownVolumeLName[tpn.fullName];
createdTime ← wantedCreatedTime;
SELECT TRUE FROM
NOT localFrom AND localTo => {
IF attach
THEN {
attach GName to LName
toA: ActiveFile;
IF remoteCheck OR createdTime = BasicTime.nullGMT THEN {
server, file: ROPE;
[server, file] ← FSName.ServerAndFileRopes[fpn.fullName];
[version: fpn.version, created: createdTime] ← FSRemoteFile.Info[server, file, createdTime];
fpn.fullName ← Rope.Concat[fpn.nameBody, FSName.VersionPartFromVersion[fpn.version]];
};
[toA, keep, ] ← NewLName[tpn.volDesc, tpn.nameBody, FALSE, setKeep, keep];
tpn.version ← toA.version;
FSDir.UpdateAttachedEntry[tpn.volDesc, tpn.nameBody, tpn.version, keep, createdTime, Rope.Flatten[fpn.fullName], insert];
toFName ← FSBackdoor.MakeFName[toA.nameBody, toA.version];
FSLock.ReleaseRecord[toA];
}
ELSE {
copy GName to LName
toA: ActiveFile;
[createdTime, toA] ← CopyGlobalToLocal[fpn, createdTime, remoteCheck, tpn, setKeep, keep];
toFName ← FSBackdoor.MakeFName[toA.nameBody, toA.version];
FSLock.ReleaseRecord[toA];
};
};
NOT localTo => {
IF localFrom
THEN {
LName to GName
fromA, globalA: ActiveFile;
fromKeep: CARDINAL;
[fromA, fromKeep] ← GetLName[fpn, createdTime, FALSE];
fpn.version ← fromA.version;
{
ENABLE FS.Error, ABORTED => {
FSLock.ReleaseRecord[fromA.attachedTo];
FSLock.ReleaseRecord[fromA];
};
IF fromA.fileLock = write
currently open for write
OR (attach AND fromA.fileLock=read AND tpn.volDesc#fpn.volDesc)
open for read and system volume is different or missing
THEN Conflict[fpn];
[tpn.version, createdTime, globalA] ← CopyFileToGlobal[fromA.h, tpn, fromA];
toFName ← FSBackdoor.MakeFName[tpn.nameBody, tpn.version];
IF attach
THEN {
attach LName to GName
FSDir.UpdateAttachedEntry[fpn.volDesc, fpn.nameBody, fpn.version, fromKeep, createdTime, Rope.Flatten[toFName], replace];
IF fromA.attachedTo = NIL THEN {
LName was not attached
ENABLE FS.Error => FSLock.ReleaseRecord[globalA];
SELECT TRUE FROM
(tpn.volDesc # fpn.volDesc) => {
local and global volumes are different
IF fromA.fileLock#none THEN ERROR; -- checked above
FSFileOps.DeleteFile[fromA.h];
};
(fromA.fileLock = none) =>
local file not open right now
Encache[fromA, tpn.nameBody, tpn.version, tpn.volDesc];
(fromA.fileLock = read) => {
local file open for read right now
Encache[fromA, tpn.nameBody, tpn.version, tpn.volDesc];
THROUGH [1.. fromA.fileLockCount] DO
IF NOT FSLock.LockFile[globalA, read] THEN ERROR;
ENDLOOP;
globalA.h ← fromA.h;
fromA.attachedTo ← globalA;
globalA ← NIL;
};
ENDCASE => ERROR;
};
}
ELSE {
opened LName already attached so just leave it
};
};
FSLock.ReleaseRecord[globalA];
FSLock.ReleaseRecord[fromA.attachedTo];
FSLock.ReleaseRecord[fromA];
}
ELSE {
GName to GName
fromA, globalA: ActiveFile;
IF fpn.volDesc = NIL THEN FSReport.NoCache[fpn.fullName];
fromA ← GetGName[fpn, createdTime, remoteCheck];
fpn.version ← fromA.version;
[tpn.version, createdTime, globalA] ← CopyFileToGlobal[fromA.h, tpn
! FS.Error => FSLock.ReleaseRecord[fromA] ];
toFName ← FSBackdoor.MakeFName[tpn.nameBody, tpn.version];
FSLock.ReleaseRecord[globalA];
FSLock.ReleaseRecord[fromA];
};
};
ENDCASE => {
LName to LName
newKeep: CARDINAL;
toFP: File.FP;
fromA, toA: ActiveFile ← NIL;
fromPages: INT;
[fromA, ] ← GetLName[fpn, createdTime, remoteCheck];
fpn.version ← fromA.version;
{
ENABLE FS.Error, ABORTED => {
FSLock.ReleaseRecord[fromA.attachedTo];
FSLock.ReleaseRecord[fromA];
FSLock.ReleaseRecord[toA];
};
fromPages ← FSFileOps.GetFileInfo[fromA.h].pages;
[toA, newKeep, toFP] ← InnerCreate[tpn.volDesc, tpn.nameBody, TRUE, fromPages, setKeep, keep];
tpn.version ← toA.version; -- the real version number
{
ENABLE FS.Error => { FSFileOps.DeleteFile[toA.h ! FS.Error => CONTINUE] };
bytes: INT;
[bytes, createdTime] ← FSFileOps.Copy[fromA.h, toA.h];
FSFileOps.SetProps[toA.h, bytes, newKeep, createdTime, tpn.nameBody, tpn.version];
};
};
FSDir.UpdateLocalEntry[tpn.volDesc, tpn.nameBody, tpn.version, newKeep, toFP, insert];
FSLock.ReleaseRecord[fromA.attachedTo];
FSLock.ReleaseRecord[fromA];
toFName ← FSBackdoor.MakeFName[toA.nameBody, toA.version];
FSLock.ReleaseRecord[toA];
};
};
InnerDelete: PROC [pn: ParsedFName, wantedCreatedTime: GMT] = {
type: FSBackdoor.EntryType;
fp: File.FP;
a: ActiveFile;
IF FSName.IsLocal[pn.nameBody]
THEN {
local deletion
IF pn.volDesc = NIL THEN FSReport.UnknownVolumeLName[pn.fullName];
[type, pn.version, , fp, , , a] ← FSDir.AcquireOldFName[pn.volDesc, pn.nameBody, pn.version, wantedCreatedTime];
IF type = notFound THEN FSReport.UnknownFile[pn.fullName, wantedCreatedTime];
IF a.fileLock # none
THEN { FSLock.ReleaseRecord[a]; Conflict[pn] };
}
ELSE {
remote deletion
ConfirmDeletion: PROC [v: Version] RETURNS [BOOL] = {
pn.version ← v;
IF pn.volDesc # NIL THEN {
have a cache
[type, , , fp, , , a] ← FSDir.AcquireOldFName[pn.volDesc, pn.nameBody, pn.version, BasicTime.nullGMT];
IF type # notFound AND a.fileLock # none THEN Conflict[pn];
};
pn.fullName ← FSBackdoor.MakeFName[pn.nameBody, pn.version];
FSReport.ReportRemote[startDeleting, pn.fullName];
startedDeletion ← TRUE;
RETURN [TRUE];
};
startedDeletion: BOOLFALSE;
server, file: ROPE;
[server, file] ← FSName.ServerAndFileRopes[pn.fullName];
type ← notFound;
a ← NIL;
FSRemoteFile.Delete[server, file, wantedCreatedTime, ConfirmDeletion
! FS.Error, ABORTED => {
FSLock.ReleaseRecord[a];
IF startedDeletion
THEN {
FSReport.ReportRemote[endDeleting, pn.fullName];
startedDeletion ← FALSE;
};
}
];
};
FSReport.ReportRemote[endDeleting, pn.fullName];
IF type # notFound THEN {
have a file to delete
FSDir.DeleteEntry[pn.volDesc, pn.nameBody, pn.version];
FSLock.ReleaseRecord[a]; -- not in directory any more, so ok to release lock
IF type # attached
THEN FSFileOps.DeleteFile[ FSFileOps.OpenFile[pn.volDesc.vol, fp] ];
};
};
NewLName: PROC [vDesc: FSFileOps.VolumeDesc, body: Text, wantFP: BOOL, setKeep: BOOL, keep: CARDINAL] RETURNS [a: ActiveFile, newKeep: CARDINAL, fp: File.FP] = {
IF keep = 0 THEN FSBackdoor.ProduceError[zeroKeep, "Zero is an illegal keep"];
[a, newKeep, fp] ← FSDir.AcquireNextLName[vDesc, body, IF setKeep THEN keep ELSE 0];
IF a.version = 1
THEN
no existing versions, so use keep supplied
newKeep ← keep
ELSE {
newKeep has been set
IF NOT wantFP AND fp # File.nullFP THEN {
have an fp we need to delete
ENABLE FS.Error => FSLock.ReleaseRecord[a];
FSFileOps.DeleteFile[FSFileOps.OpenFile[vDesc.vol, fp]];
fp ← File.nullFP;
};
};
};
CopyGlobalToLocal: PROC [gpn: ParsedFName, wantedCreatedTime: GMT, remoteCheck: BOOL, lpn: ParsedFName, setKeep: BOOL, keep: CARDINAL] RETURNS [createdTime: GMT, toA: ActiveFile] = {
localFP: File.FP;
localStream: STREAMNIL;
createdTime ← wantedCreatedTime;
toA ← NIL;
{
ENABLE FS.Error, ABORTED => {
IF localStream # NIL THEN {
localStream.Close[ ! FS.Error => CONTINUE ];
localStream ← NIL; -- ABORTED can follow an FS.Error
FSReport.ReportRemote[endRetrieving, gpn.fullName];
};
IF toA # NIL THEN {
IF toA.h # NIL
THEN FSFileOps.DeleteFile[ toA.h ! FS.Error => CONTINUE ];
FSLock.ReleaseRecord[toA];
};
};
MakeLocalFile: PROC = {
localPages ← FS.PagesForBytes[localBytes];
[toA, keep, localFP] ← NewLName[lpn.volDesc, lpn.nameBody, TRUE, setKeep, keep];
lpn.version ← toA.version;
IF localFP = File.nullFP
THEN [localFP, toA.h] ← FSFileOps.CreateFile[lpn.volDesc.vol, localPages]
ELSE {
toA.h ← FSFileOps.OpenFile[lpn.volDesc.vol, localFP];
FSFileOps.SetFilePages[toA.h, localPages];
};
};
type: FSBackdoor.EntryType;
globalFP: File.FP;
globalA: ActiveFile;
usedTime: GMT;
localBytes, localPages: INT;
server, file: ROPE;
[server, file] ← FSName.ServerAndFileRopes[gpn.fullName];
IF remoteCheck AND gpn.version NOT IN (FSBackdoor.lowestVersion .. FSBackdoor.highestVersion) AND createdTime = BasicTime.nullGMT
THEN [gpn.version, localBytes, createdTime] ← FSRemoteFile.Info[server, file, BasicTime.nullGMT];
IF gpn.volDesc = NIL
THEN type ← notFound
ELSE [type, gpn.version, , globalFP, usedTime, , globalA] ← FSDir.AcquireOldFName[gpn.volDesc, gpn.nameBody, gpn.version, createdTime];
SELECT type FROM
notFound => {
need to go to remote server
RetrieveProc: PROC [fullGName: ROPE, bytes: INT, created: GMT] RETURNS [STREAM] = {
gpn.fullName ← fullGName;
createdTime ← created;
localBytes ← bytes;
MakeLocalFile[];
localStream ← FSFileOps.CreateFileStream[toA.h, newAppendOnly];
FSReport.ReportRemote[startRetrieving, gpn.fullName];
RETURN[localStream];
};
FSRemoteFile.Retrieve[server, file, createdTime, RetrieveProc];
localBytes ← localStream.GetIndex[];
localPages ← FS.PagesForBytes[localBytes];
localStream.Close[];
localStream ← NIL;
FSReport.ReportRemote[endRetrieving, gpn.fullName];
IF localPages # FSFileOps.GetFileInfo[toA.h].pages THEN
FSFileOps.SetFilePages[toA.h, localPages];
};
cached => {
already in the cache
ENABLE FS.Error => FSLock.ReleaseRecord[globalA];
IF globalA.h = NIL THEN
globalA.h ← FSFileOps.OpenFile[gpn.volDesc.vol, globalFP];
localBytes ← FS.BytesForPages[FSFileOps.GetFileInfo[globalA.h].pages];
MakeLocalFile[];
[localBytes, createdTime] ← FSFileOps.Copy[globalA.h, toA.h];
UpdateUsedTime[gpn, globalFP, usedTime];
FSLock.ReleaseRecord[globalA];
}
ENDCASE => ERROR;
FSFileOps.SetProps[toA.h, localBytes, keep, createdTime, lpn.nameBody, lpn.version];
};
FSDir.UpdateLocalEntry[lpn.volDesc, lpn.nameBody, lpn.version, keep, localFP, insertOrReplace];
};
CopyFileToGlobal: PROC [h: File.Handle, global: ParsedFName, fromActiveFile: ActiveFile ← NIL] RETURNS [globalVersion: Version, created: GMT, globalA: ActiveFile] = {
GetVersion: PROC [v: Version] RETURNS [BOOL] = {
globalVersion ← global.version ← v;
global.fullName ← FSBackdoor.MakeFName[global.nameBody, global.version];
IF global.volDesc # NIL THEN {
have a cache
nameBody: ROPE ← global.nameBody;
IF NOT Rope.Match["[*", nameBody] THEN
nameBody ← Rope.Concat[global.volDesc.prefix, nameBody];
IF fromActiveFile # NIL AND fromActiveFile.attachedTo # NIL AND Rope.Equal[fromActiveFile.attachedTo.nameBody, nameBody] AND fromActiveFile.attachedTo.version = global.version
THEN {
version assigned by server is same as the one we are copying. This can happen due to crash or inadvertent deletion of server files.
dummy ← dummy + 1;
a place to hang performance hooks
}
ELSE {
[fpToDelete, globalA] ← FSDir.AcquireOldOrNewGName[global.volDesc, global.nameBody, global.version];
IF globalA.fileLock # none THEN Conflict[global];
server's !N version already cached and open
};
};
FSReport.ReportRemote[startStoring, global.fullName];
RETURN [TRUE];
};
dummy: INT ← 0 ; -- no real use
fpToDelete: File.FP ← File.nullFP;
server, file: ROPE;
fromStream: STREAM ← FSFileOps.CreateFileStream[h, oldReadOnly];
created ← FSFileOps.GetProps[h].created;
[server, file] ← FSName.ServerAndFileRopes[global.fullName];
globalA ← NIL;
FSRemoteFile.Store[server, file, fromStream, created, GetVersion
! FS.Error, ABORTED => {
IF fromStream # NIL THEN {
fromStream.Close[];
fromStream ← NIL; -- ABORTED can follow an FS.Error
FSReport.ReportRemote[endStoring, global.fullName];
};
FSLock.ReleaseRecord[globalA];
} ];
fromStream.Close[];
FSReport.ReportRemote[endStoring, global.fullName];
IF fpToDelete # File.nullFP THEN {
version assigned by server was already in the cache, so we must flush it
FSDir.DeleteEntry[global.volDesc, global.nameBody, global.version];
FSFileOps.DeleteFile[ FSFileOps.OpenFile[global.volDesc.vol, fpToDelete]
! FS.Error, ABORTED => FSLock.ReleaseRecord[globalA] ];
};
};
Encache: PROC [localA: ActiveFile, nB: Text, ver: Version, vDesc: FSFileOps.VolumeDesc] = {
used: GMT = BasicTime.Now[];
fp: File.FP = FSFileOps.GetFileInfo[localA.h].fp;
FSFileOps.SetProps[localA.h, -1, 0, BasicTime.nullGMT, nB, ver];
FSDir.UpdateCachedEntry[vDesc, nB, ver, used, fp, insert];
FSFileOps.RecordUsage[fp, used];
};
END.
Bob Hagmann January 29, 1985 3:37:37 pm PST
Cedar 6.0 interface changes
Bob Hagmann March 15, 1985 4:10:26 pm PST
changes to: DIRECTORY, IsGuestProcess, FalseProc, GuestProcs, RegisterGuestProcs, Open, Create, Copy, Delete, Rename
Russ Atkinson (RRA) August 27, 1985 1:21:08 pm PDT
changes to: FSMainImpl1 (fixed bad interaction between -r switch on pseudo-servers and remoteCheck option)
Bob Hagmann November 5, 1985 8:28:29 am PST
fix lockup when a global copy of an attached file that is cached results in the same global name and version number as the cached copy (due to server crash or user error)
changes to: InnerCopy, CopyFileToGlobal, GetVersion (local of CopyFileToGlobal)
Russ Atkinson (RRA) April 28, 1986 12:41:10 pm PDT
fix lockup problem (testing fromActiveFile.attachedTo.version = global.version, used to test fromActiveFile.version = global.version)
changes to: GetVersion