// IfsBootInstall.bcpl -- boot file installation
// Copyright Xerox Corporation 1979, 1980, 1981, 1982

// Last modified December 12, 1982  11:22 AM by Taft

get "Pup0.decl"
get "Pup1.decl"
get "Ifs.decl"
get "IfsRs.decl"
get "IfsBootServ.decl"
get "IfsFiles.decl"
get "IfsDirs.decl"

external
[
// outgoing procedures
CreateBootServ; InstallBootFiles

// incoming procedures
BootServEvent; FindBFE; InsertBFE; BFTCompareKey; BFTEntryLength
CreateEvent
LookupIFSFile; NextFD; DestroyFD; GetBufferForFD
OpenIFSStream; CloseIFSStream; DeleteFileFromFD; IFSOpenFile; StreamsFD
ReadBlock; WriteBlock; Closes; FileLength; SetFilePos; PositionPage
ReadLeaderPage; WriteLeaderPage
OpenIFSTree; CloseBTree; @VWRP; VirtualPage
ExtractSubstring; CopyString
SysAllocate; SysAllocateZero; SysFree; MoveBlock; IFSError

// incoming statics
@bs; primaryIFS
]

//----------------------------------------------------------------------------
let CreateBootServ() be
//----------------------------------------------------------------------------
[
bs = SysAllocateZero(lenBS)
bs>>BS.stats.version = bootStatsVersion
bs>>BS.internalLock = true  // until BFT B-tree is built
CreateEvent(BootServEvent)
]

//----------------------------------------------------------------------------
and InstallBootFiles() be
//----------------------------------------------------------------------------
// Install all boot files currently residing in <System>Boot>.
// Caller is responsible for write-locking the BFT.
[
if bs>>BS.tree ne 0 then CloseBTree(bs>>BS.tree)

// (Re)create the B-tree file with all appropriate attributes
let stream = IFSOpenFile("Boot>BootDirectory.tree", 0, modeReadWrite)
if stream eq 0 then IFSError(ecCreateEssentialFile)
let ld = GetBufferForFD(StreamsFD(stream))
ReadLeaderPage(stream, ld)
ld>>ILD.type = ftBinary
ld>>ILD.byteSize = 8
ld>>ILD.noBackup = true
ld>>ILD.undeletable = true
WriteLeaderPage(stream, ld)
PositionPage(stream, bootTreePages)
bs>>BS.tree = OpenIFSTree(lv StreamsFD(stream)>>FD.dr>>DR.fp, primaryIFS,
 BFTCompareKey, BFTEntryLength, true, bootTreePages)
SysFree(ld)
Closes(stream)

// InstallBootFiles (cont'd)

// Enumerate all files with names of the form "<System>Boot>bfn-name.boot",
// reformat them if necessary, and insert them into the BFT.
let fd = LookupIFSFile("Boot>**-**", lcVHighest+lcMultiple)
if fd ne 0 then
   [ // repeat
   let bootFile = OpenIFSStream(fd)  // modeRead
   if bootFile eq 0 loop

   // pick apart the file name and build BFD for it
   let bfd = vec (offset BFD.name/16 + 127)
   bfd>>BFD.bfn = 0
   let i = fd>>FD.lenSubDirString+1
   let char = nil
   while i ls fd>>FD.lenBodyString do
      [
      char = fd>>FD.dr>>DR.pathName.char↑i
      if (char-$0) ugr 7 break
      bfd>>BFD.bfn = bfd>>BFD.bfn lshift 3 + char - $0
      i = i+1
      ]
   if char ne $- %  // improper filename, bypass
    FindBFE(bfd>>BFD.bfn) ne 0 then  // duplicate bfn, bypass
      [ CloseIFSStream(bootFile); loop ]
   let bootName = ExtractSubstring(lv fd>>FD.dr>>DR.pathName,
    i+1, fd>>FD.lenBodyString-1)
   CopyString(lv bfd>>BFD.name, bootName)
   SysFree(bootName)

   // There are two kinds of boot file.
   // For more on boot files see the BuildBoot documentation

   // B-Files produced by BuildBoot.run:
   //	File page 1:	DiskBoot loader
   //	File page 2:	locations 0-377b
   //	File page 3:	locations 1000b - 1377b
   //	File page 4:	locations 1400b - 1777b
   //	...
   //	File page n:	locations (n-1)*400b - (n-1)*400b+377b
   // B-Files have 0 in word 1 of the first page.
   // B-Files are started by jmp @0 when loading is complete.

   // S-Files produced by Swat OutLd:
   //	File page 1:	InOutLd loader
   //	File page 2:	locations 1000b - 1377b
   //	File page 3:	locations 1400 - 1777b
   //	...
   //	File page 253:	locations 176400b-176777b
   //	File page 254:	locations 400b - 777b
   //	File page 255:	locations 0 - 377b
   // S-Files have a nonzero value in [1..377B] in word 1 of the first page.
   // Some S-Files can be started by jmp @0.
   // This is the kind we use, but we re-format them first.

   // There are other kinds of boot files as well (e.g., microcode files,
   // Sun boot files, etc.)  These are identified by word 1 not in [1..377B].
   // We use these as-is and do not attempt to reformat them.

// InstallBootFiles (cont'd)

   let buffer = SysAllocate(256)
   ReadBlock(bootFile, buffer, 256)
   MoveBlock(lv bfd>>BFD.date, buffer+3, 2)
   let newFile = valof
      [  // this block returns a disk stream iff the file should be reformatted
      if (buffer!1)-1 uge 377B then  // not in [1..377B]?
         resultis 0  // B-file or file of unknown type
      // S-files are always exactly 377000B bytes long.  If this file
      // is not the correct length then do not try to reformat it.
      let fileLength = vec 1
      FileLength(bootFile, fileLength)
      unless fileLength!0 eq 1 & fileLength!1 eq 177000B do resultis 0
      let newName = ExtractSubstring(lv fd>>FD.dr>>DR.pathName,
       1, fd>>FD.lenBodyString-1)
      let s = IFSOpenFile(newName, 0, modeWrite)
      SysFree(newName)
      resultis s
      ]
   test newFile eq 0
      ifso CloseIFSStream(bootFile)
      ifnot  // S-file - make it B-format
         [
         buffer!1 = 0 // mark it as a B-format file
         WriteBlock(newFile, buffer, 256)  //useless swat InLdr

         // read page containing loc 0-#377
         SetFilePos(bootFile, 1, 254 lshift 9)  // last page of file (255)
         WriteBlock(newFile, buffer, ReadBlock(bootFile, buffer, 256))

         // now copy the rest of the file.
         // stop on page count not end of file.
         // note that file page 254 (core page 1) is discarded
         SetFilePos(bootFile, 0, 512)  // set up to read page 2
         // S-Format files are guaranteed to be at least 255 pages long
         for i = 2 to 253 do
            WriteBlock(newFile, buffer, ReadBlock(bootFile, buffer, 256))

         Closes(newFile)
         CloseIFSStream(bootFile)
         DeleteFileFromFD(fd)  // delete the old one
         ]

   SysFree(buffer)

   // Now insert the entry into the bft
   let bfe = InsertBFE(bfd)
   bfe>>BFE.exists = true
   VWRP(VirtualPage(bfe rshift 8))  // mark BFE dirty
   ] repeatwhile NextFD(fd)

if fd ne 0 then DestroyFD(fd)
bs>>BS.internalLock = false
]