// IfsOpen.bcpl -- Open/Close an IFS
// Copyright Xerox Corporation 1979, 1980

// Last modified April 19, 1980  12:19 PM by Taft

get "IfsFiles.decl"
get "Streams.d"
get "IFS.decl"
get "Disks.d"

external
[
// Outgoing procedures
OpenIFS; CloseIFS
OpenIFSPart1; OpenIFSPart2; OpenIFSTree
OpenTFSDisk; CloseTFSDisk; InitDisks

// Incoming procedures
TFSInit; TFSClose; IFSCreateDDMgr; TFSDiskModel
IFSError; MultEq; FreePointer; Enqueue; Unqueue; Block
Allocate; Free; MoveBlock; Zero; DefaultArgs; Min
OpenFile; Closes; ReadBlock
ExtractSubstring; StringCompare
CallersFrame; ReturnFrom; ExtendStackCall
CreateOFT; DestroyOFT; Lock; Unlock
OpenFPTree; CloseBTree
DirCompareKey; DirEntryLength

// Outgoing statics
fsQ; driveTab; ifsDDMgr; openLock

// Incoming statics
sysZone; bigZone; primaryIFS
]

static [ driveTab; ifsDDMgr; fsQ; openLock ]

manifest ecReturnFrom = 17

// Local structures used to keep track of disks in OpenIFSPart1
structure PDSK:
[
disk word
thisUnit word
created word 2
]

structure PLDT↑0,nDisks-1 @PDSK
manifest lenPLDT = size PLDT/16

//OpenIFS is split into two parts because IFS initialization
//needs the results of OpenIFSPart1 to set up the virtual
//memory, which must be done before calling OpenIFSPart2.

//OpenIFS calls TFSInit which calls OpenFile - which takes
//lots of stack space.  There is plenty of stack available
//during initialization when the individual parts are called,
//but not during normal operation, so OpenIFS extends the stack
//before calling the parts.
//----------------------------------------------------------------------------
let OpenIFS(id,lvEc) = ExtendStackCall(2048, DoOpen, id, lvEc)
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
and DoOpen(id, lvEc) = valof
//----------------------------------------------------------------------------
[
Lock(openLock, true)
let res = OpenIFSPart2(OpenIFSPart1(id, lvEc), lvEc)
Unlock(openLock)
resultis res
]

//----------------------------------------------------------------------------
and CloseIFS(ifs, force; numargs na) = valof
//----------------------------------------------------------------------------
//if force = true, the ifs will be closed regardless of whether
// there are open files.
//if force = false or omitted, CloseIFS will return false if
// the directory is locked or there are open files.
[
if na ls 2 then force = false
Lock(openLock, true)
until Lock(lv ifs>>IFS.dirLock, true, true) do
   [ ifs>>IFS.dirLockConflict = true; Block() ]
unless force do
   if ifs>>IFS.oft ne 0 then unless DestroyOFT(ifs) resultis false
if ifs>>IFS.dirBTree ne 0 then CloseBTree(ifs>>IFS.dirBTree)
for lUnit = 0 to ifs>>IFS.numUnits-1 do CloseTFSDisk(ifs>>IFS.lpdt↑lUnit)
Unqueue(fsQ,ifs)
DestroyIFS(ifs)
Unlock(openLock)
resultis true
]

//----------------------------------------------------------------------------
and DestroyIFS(ifs) be
//----------------------------------------------------------------------------
[
FreePointer(lv ifs>>IFS.oft, lv ifs>>IFS.id, lv ifs>>IFS.name)
Free(sysZone, ifs)
]

//----------------------------------------------------------------------------
and OpenIFSPart1(id, lvErrorCode) = valof
//----------------------------------------------------------------------------
//returns an ifs structure or false with @lvErrorCode saying why
[
//try TFSInit on all physical units that aren't now open
let home = Allocate(sysZone, lenHome)
let ifs = 0
let pldt = vec lenPLDT
Zero(pldt, lenPLDT)
for drive = 0 to nDrives-1 do for fs = 0 to 1 do
   if driveTab>>DriveTab↑drive.disk↑fs eq 0 then
      [
      if fs ne 0 then
         [
         let disk0 = driveTab>>DriveTab↑drive.disk↑0
         if disk0 eq 0 % TFSDiskModel(disk0) ne 300 loop
         ]
      let disk = OpenTFSDisk(drive, fs)
      if disk eq 0 loop  //no pack or no drive
      let s = OpenFile("IFS.home", ksTypeReadOnly, 0, 0, 0, 0, 0, 0, disk)
      if s eq 0 then [ CloseTFSDisk(disk); loop ]  //not ifs
      ReadBlock(s, home, lenHome)
      Closes(s)
      if StringCompare(id, lv home>>Home.id) ne 0 then
         [ CloseTFSDisk(disk); loop ]  //wrong id
      let pdsk = lv pldt>>PLDT↑(2*drive+fs)
      pdsk>>PDSK.disk = disk
      pdsk>>PDSK.thisUnit = home>>Home.thisUnit
      MoveBlock(lv pdsk>>PDSK.created, lv home>>Home.created, lTIME)
      if home>>Home.thisUnit eq 0 then
         [  // Found logical unit 0, create IFS structure
         if ifs ne 0 then
            [ Free(sysZone, home); OpenIFSFail(ec2LUnit0s, pldt, ifs) ]
         ifs = Allocate(sysZone, lenIFS)
         Zero(ifs, lenIFS)
         ifs>>IFS.logPageLength = disk>>DSK.lnPageSize
         ifs>>IFS.pageLength = 1 lshift ifs>>IFS.logPageLength
         ifs>>IFS.numUnits = home>>Home.numUnits
         ifs>>IFS.type = home>>Home.type
         ifs>>IFS.id = ExtractSubstring(lv home>>Home.id)
         ifs>>IFS.name = ExtractSubstring(lv home>>Home.name)
         MoveBlock(lv ifs>>IFS.created, lv home>>Home.created, lTIME)
         ]
      ]
Free(sysZone, home)
if ifs eq 0 then OpenIFSFail(ecNoLUnit0, pldt, 0)  //no logical unit 0 found

// OpenIFSPart1 (cont'd)

// Insert packs with the correct PDSK.created into the ifs,
// counting the number of packs inserted as we go
let numUnits = 0
for drive = 0 to nDrives-1 do for fs = 0 to 1 do
   [
   let pdsk = lv pldt>>PLDT↑(2*drive+fs)
   if pdsk>>PDSK.disk ne 0 then
      test MultEq(lv pdsk>>PDSK.created, lv ifs>>IFS.created)
         ifso
            [
            if ifs>>IFS.lpdt↑(pdsk>>PDSK.thisUnit) ne 0 then
               OpenIFSFail(ecDupLUnit, pldt, ifs)
            ifs>>IFS.lpdt↑(pdsk>>PDSK.thisUnit) = pdsk>>PDSK.disk
            driveTab>>DriveTab↑drive.ifs = ifs
            numUnits = numUnits+1
            ]
         ifnot
            [ CloseTFSDisk(pdsk>>PDSK.disk); pdsk>>PDSK.disk = 0 ]
   ]

// Did we find all the logical units?
if numUnits ne ifs>>IFS.numUnits then OpenIFSFail(ecNumUnits, pldt, ifs)

Enqueue(fsQ,ifs)
resultis ifs
]

//----------------------------------------------------------------------------
and OpenIFSFail(code, pldt, ifs) be
//----------------------------------------------------------------------------
// Frees the ifs if nonzero, closes all open disks named in pldt,
// stores the error code in the caller's lvErrorCode, and returns
// false from the caller.  This procedure knows that the second argument
// of the caller is lvErrorCode.
[
if ifs ne 0 then DestroyIFS(ifs)
for i = 0 to nDisks-1 do
   if pldt>>PLDT↑i.disk ne 0 then CloseTFSDisk(pldt>>PLDT↑i.disk)
let cf = CallersFrame()
@(cf!5) = code
ReturnFrom(cf, false)
IFSError(ecReturnFrom)
]

//----------------------------------------------------------------------------
and OpenTFSDisk(drive, fs) = valof
//----------------------------------------------------------------------------
[
let disk = TFSInit(sysZone, true, #400*fs + drive, ifsDDMgr)
driveTab>>DriveTab↑drive.disk↑fs = disk
resultis disk
]

//----------------------------------------------------------------------------
and CloseTFSDisk(disk) be
//----------------------------------------------------------------------------
[
let dte = lv driveTab>>DriveTab↑(disk>>DSK.driveNumber)
TFSClose(disk, true)
for fs = 0 to 1 do
   if disk eq dte>>DTE.disk↑fs then dte>>DTE.disk↑fs = 0
if dte>>DTE.disk↑0 eq 0 & dte>>DTE.disk↑1 eq 0 then dte>>DTE.ifs = 0
]

//----------------------------------------------------------------------------
and OpenIFSPart2(ifs, lvErrorCode) = valof
//----------------------------------------------------------------------------
[
if ifs ne 0 then  //OpenIFSPart1 failed.  Do nothing.
   [
   //open the directory B-Tree.  Need ifp for IFS.Dir.
   let ifp = vec lFP; Zero(ifp, lFP)
   let ifsDir = OpenFile("IFS.Dir", ksTypeReadOnly, 0, 0, ifp,
    0, 0, 0, ifs>>IFS.lpdt↑0)
   if ifsDir eq 0 then OpenIFSFail(ecNoIFSDir, ifs)
   Closes(ifsDir)
   ifp>>IFP.unit = 0
   // Assign enough vMem for 1000 disk pages per unit, up to a maximum of 3000
   ifs>>IFS.dirBTree = OpenIFSTree(ifp, ifs, 0, 0, 0,
    Min(1000*ifs>>IFS.numUnits, 3000))
   //Initialize the Open File Table
   CreateOFT(ifs, 16)  // must be power of 2
   ]
resultis ifs
]

//----------------------------------------------------------------------------
and OpenIFSTree(ifp, ifs, CompareKeyRtn, LengthRtn, initializeTree,
 diskPages, logBTreePageLength; numargs na) = valof
//----------------------------------------------------------------------------
[
DefaultArgs(lv na, -2, DirCompareKey, DirEntryLength, 0, 0, 0)
resultis OpenFPTree(ifp, ifs>>IFS.lpdt↑(ifp>>IFP.unit), CompareKeyRtn,
 LengthRtn, initializeTree, diskPages, logBTreePageLength)
]

//----------------------------------------------------------------------------
and InitDisks() be
//----------------------------------------------------------------------------
[
// Initialize various disk-related data structures
ifsDDMgr = IFSCreateDDMgr()
driveTab = Allocate(sysZone, lenDriveTab+4); Zero(driveTab, lenDriveTab+4)
fsQ = driveTab+lenDriveTab
openLock = fsQ+2
]