/*static char *SCCSID = "@(#)hotfix.c	6.3 92/01/31";*/
#define _BTXT _BTXT
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>

#include <fsd.h>
#include <fsh.h>

#include "const.h"
#include "misc.h"
#include "pbmacros.h"
#include "buf.h"
#include "chain.h"
#include "dir.h"
#include "volume.h"
#include "storage.h"
#include "tables.h"
#include "superb.h"
#include "shutdown.h"
#include "message.h"		/* MESSAGE, PMESSAGE. */

#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "pbmsg.h"

#include "pr_cache.h"
#include "pr_misc.h"

#ifdef DEBUG
#include "pr_debug.h"
#endif

extern MESSAGE _TXT_MSG_HPFS_NEW_SECTOR;


/***	HotFix - HotFix a bad sector
 *
 *	HotFix substitutes a known good SPB sector for a bad one.
 *
 *	HotFix can't use RdBuf because pfrm->pvol (TDB_VOL) may not be
 *	valid for the guy we're hot fixing.  Also, we can't request any
 *	buffers because there may be none available and we may be called
 *	by the buffer flush code itself.  We call RdHF which reads into
 *	a special reserved 1-sector area (abHotFixBuf).
 *
 *	BUGBUG - put up hot fix popup? (from Gordon's code)
 *
 *	ENTRY	lsn - lsn of bad sector w/ volbits
 *
 *	EXIT	none
 *
 *	Yields CPU (IO)
 *
 *	HISTORY
 *	    16-Jun-1989 by JR (John Rogers, JohnRo@Microsoft)
 *		- PTR B784106: Changed to use copy of spare block flags in
 *		  volume table, instead of yet another flag.
 */

VOID
HotFix (LSN lsn, PFRAME pfrm)

{
    USHORT iHotFix;
    PVOID pvoid;		    /* used as PSPAREB & PLSN, below */

    PVOLTAB pvoltab = VolPtr [IVOLFROMLSN (lsn)]; /* don't need volbits now */

#ifdef DEBUG
    dprintf ("HotFix: HotFixing LSN: 0x%lx\n", lsn);
#endif

    if (fShuttingDown) {
	/* HotFixing is not allowed during ShutDown */

	PANIC(PBMSG_HOTFIX_SHUTDOWN);
    }

//#85759
    if (pvoltab->cHotFixInUse >= pvoltab->cHotFixMax) {
	/* no unused HotFix sectors available, */
   /* indicate hard error */
      SETHARDERR(ERROR_WRITE_FAULT);
      return;
    }

//#85759
   if ( pvoltab->cHotFixInUse >= ((pvoltab->cHotFixMax * 3)/4) ) {

      {
	    CHAR chDriveLetter [3];

	    chDriveLetter [0] = (CHAR)pvoltab->chDriveLetter;
	    chDriveLetter [1] = ':';
	    chDriveLetter [2] = 0;

	    MAYBLOCK (FSH_CRITERROR(_TXT_MSG_HPFS_NEW_SECTOR.cchMessage,
               _TXT_MSG_HPFS_NEW_SECTOR.pszMessage,
               1,
			   	chDriveLetter,
				   CE_ALLACK));
      }

   }

    lsn &= SECMASK;		    /* get rid of volbits */

    /* this is a critical section (only one reserved buffer) */
    SemRequestFar (&semHotFix,pfrm);

    /* Set bit in copy of spare block flags in volume table. */
    pvoltab->bSpareFlags |= SPF_HFUSED;

    /* check to see if any HotFixes in effect now for this volume
     * (if not, just add this new one)
     */

    if (pvoltab->cHotFixInUse != 0) {

	UINT cHotFixInUse = pvoltab->cHotFixInUse;
	PLSN plsnHotFix = pvoltab->pHotFixArray;

	/* at least one hot fix in effect */

	/* search to see if this sector is already hot fixed */
	for (iHotFix = 0; iHotFix < cHotFixInUse;
	     iHotFix++, plsnHotFix++) {
	    LSN lsnHFList;

	    if (*plsnHotFix == lsn) {
		/*
		 *  This sector has already been hotfixed.  Wipe out the old
		 *  reference, orphaning the defective hotfix sector.
		 *  Wipe it in memory and off the disk as well.
		 */
#ifdef DEBUG
		dprintf ("HotFix: LSN is already HotFixed - replace it\n");
#endif
		*plsnHotFix = -1;

#define pspb MAKETYPE(pvoid, PSPAREB)
		if ((pspb = RdHF (SEC_SPAREB | pvoltab->ulVolBits, pfrm)) == NULL)
        goto ErrHotFix;

		lsnHFList = pspb->lsnHotFix +
			    (iHotFix / (SECSIZE / sizeof (LSN))) |
			    pvoltab->ulVolBits;
#undef pspb

#define pdata MAKETYPE(pvoid, PLSN)

		if ((pdata = RdHF (lsnHFList,pfrm)) == NULL)
        goto ErrHotFix;

		pdata [iHotFix % (SECSIZE / sizeof (LSN))] = -1;
#undef pdata

		WtHF(lsnHFList,pfrm);
	    }
	}
    }

    /* Assign a new spare block */
    if ((iHotFix = pvoltab->cHotFixInUse++) >= pvoltab->cHotFixMax)
//#85759
      goto ErrHotFix;

    pvoltab->pHotFixArray[iHotFix] = lsn;

#ifdef DEBUG
    dprintf ("HotFix: HotFixed to LSN: 0x%lx\n", pvoltab->pHotFixNew[iHotFix]);
#endif

    /*
     *	Allright, now we've got to update the SpareB and the hot fix list
     *	on the volume.  We can't use RdBuf et. al. because they're
     *	keyed to the volume the current operation is working under, and
     *	we can be called when we're flushing cache blocks which might
     *	belong to any volume.  We can't just take a buffer header and
     *	fill it in because the sectors we need might already be in the
     *	cache, so we can use a special form of RdBuf
     */

#define pspb MAKETYPE(pvoid, PSPAREB)

    if ((pspb = RdHF (SEC_SPAREB | pvoltab->ulVolBits, pfrm)) == NULL )
     goto ErrHotFix;

#ifdef DEBUG
    if (pspb->culHotFixes != iHotFix)
	INTERR (__FILE__": Hot Fix index did not increment");
#endif

    pspb->culHotFixes++;
    WtHF(SEC_SPAREB | pvoltab->ulVolBits,pfrm);

    {
	LSN lsnHFList = pspb->lsnHotFix +
			(iHotFix / (SECSIZE / sizeof (LSN))) |
			pvoltab->ulVolBits;
#undef pspb

#define pdata MAKETYPE(pvoid, PLSN)

//	pdata = RdHF (lsnHFList, pfrm);
   if ((pdata = RdHF (lsnHFList, pfrm)) == NULL)
     goto ErrHotFix;

	pdata [iHotFix % (SECSIZE / sizeof (LSN))] = lsn;

#undef pdata

	WtHF(lsnHFList,pfrm);
    }	/* end of scope for lsnHFList */

   goto NoErrHotFix;

ErrHotFix:
//#85759
   /* Disallow further I/O to volume */
   pvoltab->cHotFixInUse = pvoltab->cHotFixMax;

   /* Indicate hard error */
   SETHARDERR(ERROR_WRITE_FAULT);

NoErrHotFix:

    /* end of critical section */
    SemClear (&semHotFix);
}
