// IFSCreate2.bcpl -- code for creating an Interim File System
//	This portion need not be in the resident initialization
// Copyright Xerox Corporation 1979, 1980, 1981

// Last modified November 19, 1981  9:19 AM by Taft

get "ifs.decl"
get "ifsfiles.decl"
get "ifsdirs.decl"
get "streams.d"

external
[
// Outgoing procedures
CreateIFS; CreateIFSPart2; CreateSystemEntries;

// Incoming procedures
CreateIFSPart1; OpenIFSPart1; OpenIFSPart2; CloseIFS
OpenIFSTree; CloseBTree
TFSDiskModel; OpenDisk; OpenSystemFile; CreateIFSDisk
CreateOFT; DestroyOFT; CreateUser; TransferLeaderPage
CreateFD; DestroyFD; LookupIFSFile; GetBufferForFD; LockDirFD; UnlockDirFD
CreateDirectoryEntry; UpdateDIFRec; UpdatePageUsage
Lock; Unlock; ExtendStackCall; IFSError
CopyString; CreateStringStream; PutTemplate
DefaultArgs; SysAllocate; SysFree; MoveBlock; Zero
OpenFile; KsHintLastPageFa; Closes; ReadBlock; WriteBlock

// Incoming statics
driveTab; openLock
]

//---------------------------------------------------------------------------
let CreateIFS(cPar, lvError) = ExtendStackCall(3000, DoCreate, cPar, lvError)
//---------------------------------------------------------------------------
// Creates and opens an IFS using the parameters supplied in cPar.
// id and name are strings to insert into the home block.
// numUnits is the number of disk units in the file system.
// lpMap is a vector describing the logical-to-physical unit
// number map.  ifsType is one of ifsTypePrimary or ifsTypeBackup.
// If successful, returns the ifs structure.  If unsuccessful,
// stores an error code in @lvError and returns zero.

//---------------------------------------------------------------------------
and DoCreate(cPar, lvError) = valof
//---------------------------------------------------------------------------
[
@lvError = CreateIFSPart1(cPar)
if @lvError ne 0 resultis 0
Lock(openLock, true)
let ifs = OpenIFSPart1(cPar>>CPar.id, lvError)
if ifs ne 0 then
   [
   CreateIFSPart2(ifs)
   ifs = OpenIFSPart2(ifs, lvError)
   ]
Unlock(openLock)
resultis ifs
]

//---------------------------------------------------------------------------
and CreateIFSPart2(ifs) be
//---------------------------------------------------------------------------
// Performs the second part of IFS creation, given the ifs structure
// returned by OpenIFSPart1.  It initializes the directory B-tree,
// enters all system files into it, and closes it.
[
// initialize the B-tree
let fp = vec lFP
TFSNameToFP("IFS.Dir", ifs>>IFS.lpdt↑0, fp)
ifs>>IFS.dirBTree = OpenIFSTree(fp, ifs, 0, 0, true, 1000)
CreateOFT(ifs, 8)

// make initial Directory Information Files
manifest capWheel = #100000 rshift offset Capabilities.wheel
CreateUser("System", "ifs", 10000, 0, capWheel, true, ifs)
ZeroWriteDate(ifs, "<System>!1")
if ifs>>IFS.type eq ifsTypePrimary then
   [
   CreateUser("Default-User", "*n", 1000, 0, 0, true, ifs)
   CreateUser("Mail", "*n", 10000, "System", 0, false, ifs)
   ZeroWriteDate(ifs, "<Default-User>!1")
   ZeroWriteDate(ifs, "<Mail>!1")
   ]

// make directory entries in <System> for all system files
CreateSystemEntry(ifs, 0, "IFS.Dir")
if ifs>>IFS.type eq ifsTypePrimary then
   [
   CreateSystemEntry(ifs, 0, "IFS.Swap")
   CreateSystemEntry(ifs, 0, "IFS.Errors", 0, ftText, true)
   CreateSystemEntry(ifs, 0, "IFS.Syms", 0, 0, true)
   ]
for i = 0 to ifs>>IFS.numUnits-1 do CreateSystemEntries(ifs, i)

CloseBTree(ifs>>IFS.dirBTree)
ifs>>IFS.dirBTree = 0
unless DestroyOFT(ifs) do IFSError(ecCantDestroyOFT)
]

//---------------------------------------------------------------------------
and ZeroWriteDate(ifs, name) be
//---------------------------------------------------------------------------
// The purpose of this exercise is to cause the file to be very old
// and hence eligible for restoration by the backup system.  This is necessary
// for <System>!1 and <Default-User>!1, since these are created by
// CreateIFS with dummy information and the real stuff must be restored
// from the backup system.
[
let fd = LookupIFSFile(name, lcVExplicit, 0, ifs)
if fd eq 0 then IFSError(ecTridentFile, name)
let buf = GetBufferForFD(fd)
TransferLeaderPage(fd, buf)
Zero(lv buf>>ILD.written, 2)
TransferLeaderPage(fd, buf, true)
SysFree(buf)
DestroyFD(fd)
]

//---------------------------------------------------------------------------
and TFSNameToFP(name, disk, fp) = valof
//---------------------------------------------------------------------------
// Looks up the TFS file on the specified disk and stores the
// file's FP in the structure pointed to by fp.
// Returns the size of the file in pages.
[
Zero(fp, lFP)
let str = OpenFile(name, ksTypeReadOnly, 0, 0, fp, 0, 0, 0, disk)
if str eq 0 then IFSError(ecTridentFile, name)
let pages = KsHintLastPageFa(str)>>FA.pageNumber+1
Closes(str)
resultis pages
]

//---------------------------------------------------------------------------
and CreateSystemEntries(ifs, unit) be
//---------------------------------------------------------------------------
[
CreateSystemEntry(ifs, unit, "IFS.Home", true)
CreateSystemEntry(ifs, unit, "SysDir", true)
CreateSystemEntry(ifs, unit, "DiskDescriptor", true)
]

//---------------------------------------------------------------------------
and CreateSystemEntry(ifs, unit, name, append, fileType, world; numargs na) be
//---------------------------------------------------------------------------
// Creates an IFS directory entry for the TFS file "name"
// on logical unit "unit".  If "append" is false or not supplied,
// the IFS name for TFS file "foo" is "<System>foo!1".
// If unit is supplied, the name is "<System>foo.u!1".
// "world" should be true if the file is to be readable by the world.
// Also fixes up the leader page to have all correct IFS stuff.
[
DefaultArgs(lv na, -3, false, ftBinary, false)

// lookup the supplied TFS name
let fp = vec lFP
let pages = TFSNameToFP(name, ifs>>IFS.lpdt↑unit, fp)
fp>>IFP.unit = unit

// construct IFS name and build FD
let ifsName = SysAllocate(lenPathName)
MakeIFSName(ifsName, name, (append? unit, -1))
let fd = CreateFD(ifsName, lcCreate+lcVNext, 0, ifs)
if fd eq 0 then IFSError(ecIllegalExistingName, ifsName)

// make IFS directory entry
LockDirFD(fd, lockWrite)
MoveBlock(lv fd>>FD.dr>>DR.fp, fp, lFP)
CreateDirectoryEntry(fd)

// fix up the leader page
let buf = GetBufferForFD(fd)
TransferLeaderPage(fd, buf)
manifest lLD = offset ILD.pathName/16
Zero(buf+lLD, ifs>>IFS.pageLength-lLD)
CopyString(lv buf>>ILD.pathName, ifsName)
buf>>ILD.readProt.owner = true  // only owner can read
buf>>ILD.readProt.world = world
CopyString(lv buf>>ILD.author, "System")
buf>>ILD.type = fileType
buf>>ILD.byteSize = 8
buf>>ILD.undeletable = true
buf>>ILD.noBackup = true
TransferLeaderPage(fd, buf, true)
SysFree(buf)
UnlockDirFD(fd)

UpdateDIFRec(fd, UpdatePageUsage, pages)
DestroyFD(fd)
SysFree(ifsName)
]

//---------------------------------------------------------------------------
and MakeIFSName(ifsName, tfsName, unit) be
//---------------------------------------------------------------------------
[
let s = CreateStringStream(ifsName, maxPathNameChars)
PutTemplate(s, (unit ge 0? "<System>$S.$O!1", "<System>$S!1"), tfsName, unit)
Closes(s)
]