// IfsDiskError.bcpl -- Disk error reporting
// Copyright Xerox Corporation 1982
// Last modified October 3, 1982  2:18 PM by Taft

get "Ifs.decl"
get "IfsSystemInfo.decl"
get "IfsFiles.decl"
get "IfsDirs.decl"
get "Disks.d"
get "Tfs.d"

external
[
// outgoing procedures
InitDiskError; RecordDiskError; InterpretDiskError

// incoming procedures
VFileReadPage; VFileWritePage; LockCell; UnlockCell
Ws; PutTemplate; Plural; Zero

// outgoing statics
newDiskErrorReported

// incoming statics
infoVMD
]

static
[
newDiskErrorReported = false
]

structure DE:  // hard disk errors, recorded in dePage of <System>Info
[
numErrors word		// total number of errors
numSameError word	// number of consecutive occurrences of this error
cb @CB			// copy of disk command block at time of error
ec word			// TFS error code
haveDR word		// following information is valid
fsID @DirName		// name of file system in which error occurred
dr @DR			// copy of directory record for file suffering error
]
manifest lenDE = size DE/16
compileif lenDE gr 1 lshift logStdPageLength then
   [ Barf("DE structure too large for one IFS page") ]

manifest maxLenDR = maxLenDRHeader+lenPathName+lenDIFRec

//----------------------------------------------------------------------------
let InitDiskError() be
//----------------------------------------------------------------------------
[
let de = VFileWritePage(infoVMD, dePage)
Zero(de, lenDE)
newDiskErrorReported = false
]

//----------------------------------------------------------------------------
and RecordDiskError(cb, fd, ec) be
//----------------------------------------------------------------------------
// Records a disk error that has occurred.
// cb is the disk command block for which the error occurred.
// fd, if nonzero, is alleged to designate the file in which the error occurred.
// ec is the TFS error code.
[
let de = VFileWritePage(infoVMD, dePage)

de>>DE.numErrors = de>>DE.numErrors+1
test MultEq(lv cb>>CB.diskAddress, lv de>>DE.cb.diskAddress, 3)  // disk address & drive
   ifso de>>DE.numSameError = de>>DE.numSameError+1
   ifnot de>>DE.numSameError = 1

MoveBlock(lv de>>DE.cb, cb, lCB)
de>>DE.ec = ec

de>>DE.haveFD = false
if fd ne 0 then
   [
   let fs = fsQ!0
   while fs ne 0 do
      [
      if fd>>FD.fs eq fs &
       fs>>IFS.lpdt↑(fd>>FD.dr>>DR.unit) eq cb>>CB.cbz>>CBZ.disk then
         [
         MoveBlock(lv de>>DE.fsID, fs>>IFS.id, fs>>IFS.id>>String.length rshift 1 +1)
         MoveBlock(lv de>>DE.dr, fd>>FD.dr, Min(fd>>FD.dr>>DR.length, maxLenDR))
         de>>DE.haveFD = true
         break
         ]
      fs = fs!0
      ]
   ]

newDiskErrorReported = true
]

//----------------------------------------------------------------------------
and InterpretDiskError(s) be
//----------------------------------------------------------------------------
[
let de = VFileReadPage(infoVMD, dePage)
LockCell(lv de)

if de>>DE.numErrors ne 0 then
   [
   PutTemplate(s,
    "Unrecoverable disk error (code = $UD) on drive $UD, cyl $UD, hd $UD, sec $UD*n",
    de>>DE.ec, de>>DE.cb.drive, de>>DE.cb.track, de>>DE.cb.head, de>>DE.cb.sector)
   PutTemplate(s,
    "Command: header = $P, label = $P, data = $P*n",
    InterpCmd, de>>DE.cb.CommH, InterpCmd, de>>DE.cb.CommL, InterpCmd, de>>DE.cb.CommD)
   PutTemplate(s,
    "Status: header = $UO, label = $UO, data = $UO*n",
    de>>DE.cb.StatusH, de>>DE.cb.StatusL, de>>DE.cb.StatusD)
   let block = lv de>>DE.cb.CommH
   for rec = 0 to 2 do
      [
      let status = block>>KCBblock.Status
      if (status & dstErrors) ne 0 then
         [
         let interp = status<<DST.ECCerror? "checksum (ECC)",
          status<<DST.CompErr? "compare", 0
         if interp ne 0 then
            PutTemplate(s, "Interpretation: $S error in $S field*n", interp,
             selecton rec into [ case 0: "header"; case 1: "label"; case 2: "data"])
         ]
      block = block+lKCBbl
      ]
   PutTemplate(s, "Total of $UD error$S", de>>DE.numErrors, Plural(de>>DE.numErrors))
   if de>>DE.numSameError ugr 1 then
      PutTemplate(s, ", including $UD consecutive occurrence$S of this error",
       de>>DE.numSameError, Plural(de>>DE.numSameError))
   ]

UnlockCell(lv de)
]

//----------------------------------------------------------------------------
and InterpCmd(s, cmd) be
//----------------------------------------------------------------------------
   PutTemplate(s, (selecton cmd into
      [
      case 0: "no-op"
      case diskRead: "read"
      case diskWrite: "write"
      case diskCheck: "check"
      default: "?[$UO]"
      ], cmd)