/*static char *SCCSID = "@(#)hotfix.c	6.3 91/12/02";*/
#define SCCSID "@(#)hotfix.c	6.3 91/12/02"
/********************************************************/
/*                                                      */
/*       IBM/Microsoft Confidential                     */
/*                                                      */
/*       Copyright (c) IBM Corporation 1987, 1989       */
/*       Copyright (c) Microsoft Corporation 1987, 1989 */
/*                                                      */
/*       All Rights Reserved                            */
/********************************************************/

#define INCL_DOSP
#define OS2_NOPMAPI
#include <os2p.h>
#define INCL_TYPES                      // for basedef.h typedefs
// #include <osconst.h>
//#include <ostypes.h>
// #include <basemac.h>
#include <basedef.h>
#define FNO_NUM FNO_HOTFIX_C
#include <fnofs.h>
#include <osmac.h>
#include <mi.h>
#include <vmexport.h>
#include <selxport.h>
#include <pgexport.h>
#include <tkexport.h>
#include <smexport.h>
#include <sft.h>
#include <cache.h>
#include <strat2.h>
#include <vmowner.h>
#include <fslocal.h>
#include <filesys.h>
#include <filemode.h>
#include <opncreat.h>
#include <ioexp.h>
#include <curdir.h>


char      szHotFixFile[]="C:\\HOTFIX.DAT";

/*
 ***    WriteHotFixData - Write HotFix Data
 *
 *      WriteHotFixData writes data that could not be hotfixed to the hotfix
 *      file on disk. If this fails no other error recovery is possible.
 *
 *      ENTRY   hVPB           - handle to volume
 *              cbCount        - count of bytes to write
 *              pData16        - 16:16 data buffer pointer
 *
 *      RETURNS
 *
 *
 */

int WriteHotFixData(ulong_t hVPB,
                    ulong_t cbCount,
                    ulong_t DataPtr)
{
    ulong_t   SFN;
    int       rc;
    char      DriveLetter;
    ulong_t   filemode;
    ulong_t   openflags;
    UCHAR     Openbuf[PMPATHLEN]; /* temp buffer for TransPath etc. */
    ulong_t   pSeek;
    int       rc2;

    filemode = open_for_both | sharing_deny_none |open_autofail |
               open_no_cache | open_write_through;
    openflags = exist_open | nexist_create;
    DriveLetter = GetDriveLetterFromhVPB(hVPB);
    szHotFixFile[0] = DriveLetter;
    if ((rc = IOSftOpen((vaddr_t)(&szHotFixFile),
                        filemode,
                        openflags,
                        SSToDS(&SFN),
                        (ulong_t)&Openbuf)) != 0){
        return(rc);
    }
    if ((rc = SftFileSize(SFN,
                           SSToDS(&pSeek))) != 0){
        rc2 = IOSftClose(SFN);
        return(rc);
    }
    if ((rc = IOSftWriteAt(SFN,
                           SSToDS(&cbCount),
                           DataPtr,(vaddr_t)0,                   // @SEC
                           pSeek)) != 0){
        rc2 = IOSftClose(SFN);
        return(rc);
    }
    if ((rc = IOSftClose(SFN))!= 0){
        return(rc);
    }
    return(0);
}

/*
 ***    HotFix - Hot Fix write error
 *
 *      HotFix determines the failing cluster assoicated with the failing
 *      sector and attempts to remove the cluster out of the chain and place
 *      a another good cluster in its position. If this fails we write the
 *      data to the HotFix file.
 *
 *      ENTRY   cReqList       - count of request lists remaining
 *              ReqListSel     - Request List Selector
 *              ReqListFailOff - Request List Failing Offset
 *              hVPB           - handle to volume
 *
 *      RETURNS
 *
 *
 */

int HotFix(ulong_t cReqList,
           ulong_t ReqListSel,
           ulong_t ReqListFailOff,
           ulong_t hVPB)
{
  ulong_t       pReqListBase;
  struct        _PB_Read_Write *pPB_Read_Write;
  SG_Descriptor *pSG_Descriptor;
  PB_Read_Write *pPB_Read_Write2;
  SG_Descriptor *pSG_Descriptor2;
  int           i,rc,j,k,found,m,rc2,n,h,l;
  ulong_t       pSG_DescBufOff;
  ulong_t       pSG_DescBufOff2;
  ulong_t       HotFixReqListSel;
  ulong_t       HotFixReqListOff;
  ulong_t       rba;
  ulong_t       rbanew;
  ulong_t       initflags;
  ulong_t       rba_in_cluster;
  ulong_t       cSecsPerClus;
  ulong_t       pBuffer;
  ulong_t       ClusterNum;
  ulong_t       PrevClusterNum;
  ulong_t       NextClusterNum;
  ulong_t       NewClusterNum;
  VMAC          ac;
  VMAC          ac2;
  page_t        pageno;
  SEL           HotFixSel;
  ulong_t       ClusTable[50];
  ulong_t       ClusTableIndex = 0;
  ulong_t       rba2;
  ulong_t       pHotFixBuf16 = 0;
  ulong_t       BufSel;
  ulong_t       BufOff;
  int           fHotFixWrite = FALSE;
  ulong_t       pgoffset;
  int           cPage;
  ulong_t       pHotFixBuffer;
  ulong_t       pPhysHotFixBuffer;
  ulong_t       HotFixBufferSize;
  int           fTK_FF_BUF = FALSE;
  char          TCBBuf[TCBIOCopySize];
  int           HotFixRC = 0;
  int           fksem = FALSE;


  fHotFixing = TRUE;
  if (pTCBCur->TCBForcedActions & TK_FF_BUF){
     fTK_FF_BUF = TRUE;
     pTCBCur->TCBForcedActions &= ~TK_FF_BUF;
  }
  SaveFatTCBInfo(SSToDS(&TCBBuf));
  pTCBCur->TCBCurBuf = 0;
  pTCBCur->TCBJfnFlag = JFN_WRTTHR | JFN_NCACHE;

  if ((pReqListBase = SELConvertToLinear((SEL)(ReqListSel),
                                        pPTDACur)) == (ulong_t)-1){
     panic ("HotFix: ReqList selector bad");

  }
  pPB_Read_Write = (PB_Read_Write *)(pReqListBase + ReqListFailOff);
  ac.ac_va  = 0;
  if ((rc = VMAllocMem (PAGESIZE * 2,PAGESIZE * 2,
                        VMAC_ARENASYS | VMAC_LOCANY | VMAC_ALIGNPAGE |
                        PG_R | PG_W | PG_UVIRT,
                        HOBNULL, FSBUFOWNER, HOBNULL,
                        0, -1, SSToDS(&ac))) != 0){
      goto error;
  }
  pageno = (page_t)ac.ac_va >> PAGESHIFT;

  ac2.ac_sel = 0;
  HotFixBufferSize = GetClusterSize(hVPB);
  if ((rc = SELAllocKHB(VM_HKRH_PUBRW,
                        HotFixBufferSize,
                        PG_CONTIG,
                        0,
                        FSBUFOWNER,
                        SSToDS(&ac2))) != 0){
       goto error;
  }
  pHotFixBuffer = (ulong_t)ac2.ac_va;
  HotFixSel = ac2.ac_sel;
  pHotFixBuf16 = (ulong_t)HotFixSel << 16;

  if (rc = VMLinToPhys((ulong_t)pHotFixBuffer,(ulong_t *)(SSToDS(&pPhysHotFixBuffer)))){
     panic ("HotFix: Cannot convert memory to physical address");
  }
  ReqListAlloc(SSToDS(&HotFixReqListSel),
               SSToDS(&HotFixReqListOff));

  while (KSEMRequestMutex ((HKSEM)&(HotFixKsem),-1));
  fksem = TRUE;

  for (m = 0; m < cReqList; ++m){

     pSG_Descriptor = (SG_Descriptor *)((ulong_t)(pPB_Read_Write) +
                                       (ulong_t)(sizeof(PB_Read_Write)));
     pSG_DescBufOff = 0;
     rba = pPB_Read_Write->Start_Block;

     for (i = 0; i < pPB_Read_Write->Block_Count; ++i){
         if (ClusTableIndex != 0){
           for (h = 0; h < ClusTableIndex; ++h){
              rba_in_cluster = FigRec (ClusTable[h],0,hVPB);
              if ((rba >= rba_in_cluster) &&
                  (rba < (rba_in_cluster + cSecsPerClus))){
                 goto next_sec;
              }
           }
         }
         initflags = RLCB_KEEP_REQ;
         if (!(IsRBAInSysArea(rba,hVPB))){
            initflags |= RLCB_NO_HARDERR;
         }
         ReqListInit(HotFixReqListSel,
                     HotFixReqListOff,
                     hVPB,
                     initflags);
         rc = ReqListAdd_REQ(rba,
                             1,
                             PB_WRITE_X,
                             HotFixReqListSel,
                             HotFixReqListOff,
                             PRIO_FOREGROUND_USER,
                             hVPB);
         BufAssert (!rc);
         ReqListAdd_SG(SECSIZE,
                       HotFixReqListSel,
                       HotFixReqListOff,
                       (PBYTE)pSG_Descriptor->BufferPtr + pSG_DescBufOff);

         if (rc = ReqListExecute(HotFixReqListSel,HotFixReqListOff) != 0){

            pTCBCur->TCBFailErr = 0;

            if (!(initflags & RLCB_NO_HARDERR)){
               goto next_sec;
            }
            // We have a found a failing sector

            ClusterNum = RBAToClus(rba,
                                   hVPB,
                                   SSToDS(&rba_in_cluster),
                                   SSToDS(&cSecsPerClus));
            for (j = 0; j < cSecsPerClus;++j,++rba_in_cluster) {
               pPB_Read_Write2 = (PB_Read_Write *)(pReqListBase +
                                                   ReqListFailOff);
               found = FALSE;
               for (k = 0; k < cReqList; ++k){

                  pSG_Descriptor2 = (SG_Descriptor *)(((ulong_t)(pPB_Read_Write2)) +
                                                   (ulong_t)(sizeof(PB_Read_Write)));
                  pSG_DescBufOff2 = 0;
                  rba2 = pPB_Read_Write2->Start_Block;

                  for (l = 0; l < pPB_Read_Write2->Block_Count; ++l){

                    if (rba2 == rba_in_cluster){
                      found = TRUE;
                      pgoffset = (ulong_t)(((ulong_t)pSG_Descriptor2->BufferPtr +
                                  (ulong_t)pSG_DescBufOff2) & (ulong_t)PAGEMASK);
                      cPage = 1;
                      if ((pgoffset + SECSIZE) > PAGESIZE)
                         ++cPage;
                      PGMapVirt(pageno,
                                (paddr_t)((ulong_t)pSG_Descriptor2->BufferPtr +
                                          (ulong_t)pSG_DescBufOff2),
                                cPage,
                                -1);
                      memcpy ((void *)(pHotFixBuffer +
                                       (ulong_t)(j*SECSIZE)),
                              (void *)((ulong_t)ac.ac_va + (ulong_t)pgoffset),
                              SECSIZE);
                      goto EndFind;
                    }
                    ++rba2;
                    pSG_DescBufOff2 += SECSIZE;
                    if (pSG_DescBufOff2 == pSG_Descriptor2->BufferSize) {
                       pSG_Descriptor2 = (SG_Descriptor *)(((ulong_t)(pSG_Descriptor2)) +
                                         (ulong_t)(sizeof(SG_Descriptor)));
                       pSG_DescBufOff2 = 0;
                    }
                  }
                  pPB_Read_Write2 = (PB_Read_Write *)((ulong_t)(pPB_Read_Write2) +
                                    (ulong_t)(pPB_Read_Write2->RqHdr.Length));
               }
      EndFind:
               if (!found){              // Read Sector from disk
                  if ((pBuffer = GetBuf(rba_in_cluster,
                                        hVPB,
                                        SSToDS(&BufSel),
                                        SSToDS(&BufOff))) !=0){
                     memcpy ((void *)(pHotFixBuffer + (ulong_t) (j*SECSIZE)),
                             (void *)pBuffer,
                             SECSIZE);
                     ReleaseBuf();
                  }
                  else {
                     fHotFixWrite = TRUE;
                     for (n= i; n < pPB_Read_Write->Block_Count; ++n){
                        pgoffset = (ulong_t)(((ulong_t)pSG_Descriptor->BufferPtr +
                                    (ulong_t)pSG_DescBufOff) & (ulong_t)PAGEMASK);
                        cPage = 1;
                        if ((pgoffset + SECSIZE) > PAGESIZE)
                           ++cPage;
                        PGMapVirt(pageno,
                                  (paddr_t)((ulong_t)pSG_Descriptor->BufferPtr +
                                            (ulong_t)pSG_DescBufOff),
                                  cPage,
                                  -1);
                        memcpy ((void *)(pHotFixBuffer),
                                (void *)((ulong_t)ac.ac_va + (ulong_t)pgoffset),
                                SECSIZE);
                        if ((rc = WriteHotFixData(hVPB,
                                                  SECSIZE,
                                                  pHotFixBuf16)) != 0){
                           HotFixRC = rc;
                           goto next_reqlist;
                        }
                        ++rba;
                        pSG_DescBufOff += SECSIZE;
                        if (pSG_DescBufOff == pSG_Descriptor->BufferSize) {
                           pSG_Descriptor = (SG_Descriptor *)(((ulong_t)(pSG_Descriptor)) +
                                             (ulong_t)(sizeof(SG_Descriptor)));
                           pSG_DescBufOff = 0;
                        }
                     }
                     goto next_reqlist;
                  }
               }
            }

            // we now have a cluster's worth of data
            NewClusterNum = 0;
            rc = FindClusInfo(ClusterNum,
                              SSToDS(&PrevClusterNum),
                              SSToDS(&NextClusterNum),
                              hVPB);
            if (!rc) {
               if (PrevClusterNum != 0){
                  if ((rc = AllocateClus(hVPB,
                                         SSToDS(&NewClusterNum))) == 0){
                     if ((rc = ReLinkClus(NewClusterNum,
                                          PrevClusterNum,
                                          NextClusterNum,
                                          ClusterNum,
                                          hVPB)) == 0){

                        // Write Cluster data to new location

                        rbanew = FigRec(NewClusterNum,
                                        0,
                                        hVPB);

                        ReqListInit(HotFixReqListSel,
                                    HotFixReqListOff,
                                    hVPB,
                                    RLCB_KEEP_REQ);
                        rc = ReqListAdd_REQ(rbanew,
                                            cSecsPerClus,
                                            PB_WRITE_X,
                                            HotFixReqListSel,
                                            HotFixReqListOff,
                                            PRIO_FOREGROUND_USER,
                                            hVPB);
                        BufAssert (!rc);
                        ReqListAdd_SG(cSecsPerClus * SECSIZE,
                                      HotFixReqListSel,
                                      HotFixReqListOff,
                                      (PBYTE)(pPhysHotFixBuffer));

                        rc = ReqListExecute(HotFixReqListSel,HotFixReqListOff);
                     }
                  }
               }
               else
                  rc = TRUE;
            }
            if (rc) {
               if (NewClusterNum != 0){
                  FreeCluster(hVPB,NewClusterNum);
               }
               fHotFixWrite = TRUE;
               for (n= i; n < pPB_Read_Write->Block_Count; ++n){
                   pgoffset = (ulong_t)(((ulong_t)pSG_Descriptor->BufferPtr +
                               (ulong_t)pSG_DescBufOff) & (ulong_t)PAGEMASK);
                   cPage = 1;
                   if ((pgoffset + SECSIZE) > PAGESIZE)
                      ++cPage;
                   PGMapVirt(pageno,
                             (paddr_t)((ulong_t)pSG_Descriptor->BufferPtr +
                             (ulong_t)pSG_DescBufOff),
                             cPage,
                             -1);
                  memcpy ((void *)(pHotFixBuffer),
                          (void *)((ulong_t)ac.ac_va + (ulong_t)pgoffset),
                          SECSIZE);
                  if ((rc = WriteHotFixData(hVPB,
                                            SECSIZE,
                                            pHotFixBuf16)) != 0){
                     HotFixRC = rc;
                     goto next_reqlist;
                  }
                  ++rba;
                  pSG_DescBufOff += SECSIZE;
                  if (pSG_DescBufOff == pSG_Descriptor->BufferSize) {
                     pSG_Descriptor = (SG_Descriptor *)(((ulong_t)(pSG_Descriptor)) +
                                       (ulong_t)(sizeof(SG_Descriptor)));
                     pSG_DescBufOff = 0;
                  }
               }
               goto next_reqlist;
            }
            else {
               ClusTable[ClusTableIndex++] = ClusterNum;
               BufAssert (ClusTableIndex < 50);
            }
         }
next_sec:;
         ++rba;
         pSG_DescBufOff += SECSIZE;
         if (pSG_DescBufOff == pSG_Descriptor->BufferSize) {
            pSG_Descriptor = (SG_Descriptor *)(((ulong_t)(pSG_Descriptor)) +
                              (ulong_t)(sizeof(SG_Descriptor)));
            pSG_DescBufOff = 0;
         }
     }
next_reqlist:
     pPB_Read_Write = (PB_Read_Write *)((ulong_t)(pPB_Read_Write) +
                       (ulong_t)(pPB_Read_Write->RqHdr.Length));
  }
  rc = 0;
error:;
  if (ac.ac_va != 0) {
     rc2 = VMFreeMem (ac.ac_va, HOBNULL, 0);
  }
  if (ac2.ac_sel != 0){
     SELFreeKHB(VM_HKRH_PUBRW,0,ac2.ac_sel);
  }
  if (fksem){
     KSEMReleaseMutex ((HKSEM)&(HotFixKsem));
  }
  if (fHotFixWrite && HotFixRC == 0){
     HotFixHardError(hVPB);
     rc = -1;
  }
  if (HotFixRC)
     rc = HotFixRC;
  if (fTK_FF_BUF)
     pTCBCur->TCBForcedActions |= TK_FF_BUF;
  RestoreFatTCBInfo(SSToDS(&TCBBuf));
  fHotFixing = FALSE;
  return(rc);
}
