/*static char *SCCSID = "@(#)rdbuf.c    6.6 92/01/31";*/
#define  INCL_DOSINFOSEG
#define  INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include "const.h"
#include "misc.h"
#include "chain.h"
#include "buf.h"
#include "dir.h"
#include "volume.h"
#include "storage.h"
#include "tables.h"
#include "pb_ifs.h"
#include "pbmacros.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "log.h"
#include "pr_CACHE.h"
#include "pr_DEBUG.h"
#include "pr_DSKIO.h"
#include "pr_MISC.h"
#include "pr_heap.h"
#include "pr_FSSUB.h"
#include "pr_READ.h"
#include "mmph.h"

/*
 ***    RdBuf - Read Data Into Buffer
 *
 *      RdBuf does a synchronous read of a sector into a buffer.
 *      Of course, ideally the sector is already in a buffer and we
 *      just locate it.
 *
 *      First, we search the hash table chain to see if the sector
 *      is already in a buffer.  We're using a modified form of the
 *      double chain list here; it makes it a bit more hassle taking
 *      guys off of the list, but it allows twice as many hash chains
 *      in the same space and is probably worth it.
 *      BUGBUG - note that we don't put a found guy on the front
 *      of the hash chain, we're assuming that the chains are short
 *      enough and that the advisorys take care of most references,
 *      so we think that this isn't worth the fuss.
 *
 *      If the buffer is locked, RdBuf blocks on it, and returns when
 *      the buffer is available.
 *
 *      ENTRY   lsnDesired = sector number (can be a Vsector)
 *              priBuf = indicates where to put buffer in LRU chain.
 *
 *      EXIT    Returns pointer to data (if ok)
 *               *ppbufResult = address of buffer header for buffer w/data
 *              Returns NULL (if error)
 *
 *      HISTORY
 *     20Sep95 D.Azzarito @132506  Count LZRD buffers
 *     10Jul95 D.Azzarito @129233  Check frame flag so we don't allocate
 *                                 memory during a swapper operation.
 *     01-Feb-1994 by Skip Nizinski, defect 73714: set ppbuf for caller
 *        when SCR returns false. Also, must first remove buffer from hash
 *        chain on SCR error path before calling Addhash for LSN_BOGUS
 *        buffer to avoid system hang (otherwise, buffer would be on
 *        two different hash chains).
 *          23-Feb-1990 by JR (John Rogers, JohnRo@Microsoft)
 *              - PTR B788262: fix bug in debug check of vol bits.
 *          26-Sep-1988 13:07:54 by Bill McJohn [t-billm]
 *                              brought up to date with 9/23/88 assembly code
 *
 *      RdBuf is a macro which invokes RdBufOrGetInBuf.  See pr_cache.h.
 */

/*
 ***    GetInBuf - Get Sectors into Into Buffer
 *
 *      GetInBuf is called to get a cache buffer setup and labeled
 *      as containing a sector, without actually reading that sector.
 *      For example, if we've just allocated a sector for an FNODE
 *      and we want to initialize it, we don't want to call RdBuf
 *      because it will read in the previous sector value which
 *      is a waste.
 *
 *      IFF the specified sector is not in the cache already, and
 *      IFF all of the other sectors in that cache block are unallocated
 *      then we just declare a free buffer as holding the values of those
 *      sectors, thereby saving a read.
 *
 *      The sector count passed in is just to allow the checking to
 *      see that all the non-`read' sectors are free; GetInBuf
 *      won't read multiple cache blocks if the Request # +
 *      sector count overflows the contents of a cache block.
 *
 *      ENTRY   lsn = sector number (can be a Vsector)
 *              cusSec = number of sectors to be `read' (max of SPB)
 *              priBuf = indicates where buffer goes in
 *                              LRU chain
 *      EXIT    Returns pointer to data (if ok)
 *               *ppbuf = address of buffer header for buffer w/data
 *              Returns NULL (if error)
 *
 *      GetInBuf is a macro which invokes RdBufOrGetInBuf.  See pr_cache.h.
 */

/*
 *** RdBufOrGetInBuf -- gets buffer with desired sector
 *
 *      This performs the work for either RdBuf or GetInBuf.
 *
 *      First, the function looks in the cache to see if the sector
 *      is available.  If not, it gets a free buffer and sets it up
 *      to hold the data block with the desired sector.  What happens
 *      next depends on the state of fOmitRead:
 *
 *        If fOmitRead is FALSE, the data block is read into the buffer's
 *      data area.  This is the action desired by RdBuf.
 *
 *        If fOmitRead is TRUE, we see if we can avoid reading the block.
 *      This corresponds to GetInBuf.  The block need not be read unless
 *      one of the sectors in the block, other than the cusSec sectors
 *      starting at lsn, is allocated.
 *
 *      ENTRY lsn = lsn for which we are looking (can be a Vsector)
 *            cusSec = number of sectors to be 'read' (used only if
 *                     fOmitRead is TRUE.  If lsn + cusSec overflows
 *                     into the next block, the overflow is ignored.
 *                     This count is used only to determine if a read
 *                     is necessary.)  Zero indicates SPB or more.
 *             priBuf = where buffer goes in LRU chain
 *             fOmitRead = flag indicating whether sector read may be
 *                         omitted.
 *
 *      EXIT    Returns pointer to data (if ok)
 *               *ppbuf = address of buffer header with desired data
 *              Returns NULL (if error)
 *
 */

/*  abGIBMask is used to inspect the appropriate Bitmap byte to determine
 *  whether the buffer must be read or may be created by fiat.  The first
 *  index to abGIBMask is lsn % 8 (the offset of lsn into the appropriate
 *  Bitmap byte);  the second is cusSec (where cusSec cannot exceed SPB-1,
 *  and a value of 0 indicates a run of SPB sectors.)  Hence, if we
 *  request SPB-1 sectors beginning at bit two of the bitmap byte, bits two
 *  through SPB are set to indicate that we don't care whether the
 *  corresponding sectors have been allocated, (overflow into the next
 *  block is ignored).
 *
 *  For SPB = 4 :
 *  All four upper bits are set, since we
 *  don't care about the other block, so abGIBMask[2][3] = 0xfc.
 *
 *  Note that this array is the conflation of GIBA and GIBB in the
 *  assembly code.  Note also that abGIBMask[0][0] and abGIBMask [4][0]
 *  have the special
 *  value zero, indicating that the entire block is to be overwritten
 *  (which saves us from consulting the bitmap).
 *
 */

BYTE abGIBMask[8][SPB] = {
#if SPB == 4
          {0x00, 0xf1, 0xf3, 0xf7},
          {0xfe, 0xf2, 0xf6, 0xfe},
          {0xfc, 0xf4, 0xfc, 0xfc},
                 {0xf8, 0xf8, 0xf8, 0xf8},
                    {0x00, 0x1f, 0x3f, 0x7f},
                    {0xef, 0x2f, 0x6f, 0xef},
                    {0xcf, 0x4f, 0xcf, 0xcf},
          {0x8f, 0x8f, 0x8f, 0x8f}

#elif SPB == 8
                         {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f},
                         {0xfe, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e, 0xfe},
                         {0xfc, 0x04, 0x0c, 0x1c, 0x3c, 0x7c, 0xfc, 0xfc},
                         {0xf8, 0x08, 0x18, 0x38, 0x78, 0xf8, 0xf8, 0xf8},
                         {0xf0, 0x10, 0x30, 0x70, 0xf0, 0xf0, 0xf0, 0xf0},
                         {0xe0, 0x20, 0x60, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0},
                         {0xc0, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0},
                         {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}
#else
#error GetInBuf:  SPB is not 4 or 8.  GetInBuf will fail.
#endif
                      };


PVOID
RdBufOrGetInBuf (lsn, cusSec, priBuf, ppbuf, fOmitRead,
                 poft, ulSeekPtr, pfrm)
LSN lsn;                        /* desired sector */
USHORT cusSec;                  /* number of sectors (used iff fOmitRead) */
USHORT priBuf;                  /* where buffer goes in LRU chain         */
PBUFNODE far *ppbuf;            /* ptr to addr of returned buffer header */
BOOL fOmitRead;                 /* flag to determine whether the read
                                                may be omitted  */
POFT   poft;
ULONG  ulSeekPtr;
PFRAME pfrm;
{
        LSN lsnFirstInBlock;            /* First LSN in block containing lsnDesired
                                                   * (includes the volume information
                                                   */
        register PBUFNODE pbuf;
        BYTE MustRead = 0;      /* saved MustRead bits */
        BOOL bGetInBuf = fOmitRead;
 /* remember whether RdBuf or GetInBuf
  * (for error handling)
  */
        LSN lsnPhysStart;
        ULONG culRunLen;
        LSN lsnPhysStartRun;
        ULONG culTotalRunLen;
        LSN lsnFirstInCacheBlock;
   PBUFNODE pbuf1;

        MMPHPreRDBufOrGetInBuf(); /* performance hook (3307) */

#ifdef DEBUG
        if( (lsn & ~(LONG)SECMASK) != 0 &&
            (lsn & ~(LONG)SECMASK) != pfrm->pvol->ulVolBits ) {

            /*
             *  LSN has bad volume bits.  This may occur during
             *  a volume mount if the elementary structures have
             *  been corrupted.  Otherwise, it indicates an internal
             *  error in HPFS.
             */
            dprintf( "RdBuf:  Writing lsn 0x%lx to wrong volume.\n", lsn );
            Int3();
        }
#endif

        /*  lsn is truncated to a multiple of SPB (Sectors per Block)
         *  to yield lsnFirstInBlock.  If cusSec is greater than SPB,
         *  set it to zero (indicating SPB sectors to be overwritten).
         */

        lsn &= SECMASK; /* zero out volume bits */
        lsnFirstInBlock = (lsn & ~(LONG)SPBMASK) | (pfrm->pvol->ulVolBits);

        if (cusSec >= SPB) {
            cusSec = 0;
        }

        for (;;) {

            /*  Look for lsn in the cache.
             */

            pbuf = CCH (lsnFirstInBlock, pfrm);

            /*  Either we have our buffer in pbuf, or there are no more people
             *  in GFB and our data is not in the cache
             */

            if (pbuf == NULL) {

             if (poft == NULL)  {

                /*  The sector we want is not in the cache.
                 *  Get a free buffer, lock it, mark it as
                 *  holding the block we need, and put it
                 *  on the appropriate hash chain.
                 *
                 */
                 pbuf = GFB(pfrm);

       /* Make sure the sector(s) didn't sneak into the cache
        * if GFB blocked
        */
       if (pbuf1 = HashFind(lsnFirstInBlock) ) { /* Sectors sneaked in */
        pbuf->lsnSec = LSN_BOGUS;   /* get rid of new cache block */
        AddHash (pbuf);
                  PlaceBuf (pbuf, BP_TOSS);
        pbuf = pbuf1;   /* use this cache buffer */
        goto rdbuf100;
       }

                 pbuf->lsnSec = lsnFirstInBlock;

#ifdef DEBUG
                 if (pbuf->bDirtMask) {
                     INTERR ("RdBufOrGetInBuf:  GFB got a dirty buffer.\n");
                 }
#endif

       CreateBUF (pbuf);  // @114986  log new buffer creation
       LockBUF (pbuf);                // @114986
                 AddHash (pbuf);

#ifdef DEBUG2
                 ChkHashChain ();
#endif
        }

                /*  If fOmitRead is true we will try to avoid reading
                 *  the block.  However, if the appropriate abGIBMask
                 *  entry is non-zero (indicating that we will not
                 *  overwrite the entire block), and the bitmap
                 *  indicates a sector in the block has been allocated
                 *  and will not be overwritten, we must read the
                 *  block to preserve that sector's data.  This
                 *  latter condition is true if any bit is zero in
                 *  both the mask byte and the appropriate bitmap
                 *  byte.
                 *
                 */


                if (fOmitRead) {

                    if ( abGIBMask[ lsn % 8 ][ cusSec ] ) {

                        PBANDTAB pbt;

                        pbt = pfrm->pvol->apbndList[USSHIFTRUL(lsn, BANDSHIFT)];

                        if ((pbt->scpMap.lsn != pbt->scpMap.pbufHint->lsnSec) ||
                                 (pbt->scpMap.pbufHint->cLocked != 0)) {

                            /*  The corresponding band of the bitmap is
                             *  not in memory or does not yet have valid
                             *  data in it. Rather than read it (or wait
                             *  for it to be read),
                             *  just go ahead and read the block.
                             *
                             */

                            fOmitRead = FALSE;

                        } else { /* corresponding band of the bitmap is in storage */

                            PBYTE pbBitMap;
             PBUFNODE pbufBand;

                       pbufBand = pbt->scpMap.pbufHint;

                  if (pbufBand->dcbpLru.pbufFwd != pbufBand) {

                /*  Take pbufBand off the LRU chain
                 */
                    DCREM (pbufBand, dcbpLru);

//              /*  Put on LRU chain in MRU position if not dirty */
//                 if (pbufBand->bDirtMask == NULL)

                     DCADDF (pbufBand, dcbpLru, bufLRUHead);
                  }

                            pbBitMap = (PBYTE) pbufBand->pblkData;

                            if ((MustRead =
                                ~(BYTE)( pbBitMap[ (lsn % BANDSIZE) / 8 ] |
                                    abGIBMask[ lsn % 8 ][ cusSec ]))) {

                                /*  A sector which we will not overwrite
                                 *  has been allocated.  Cannot skip read.
                                 *
                                 */

                                   fOmitRead = FALSE;
                            }
                        }
                    }
                } /* end (fOmitRead) */

                if (!(fOmitRead)) {
                   /* Our flag indicates we need to hit the disk.
                    * Check to see if we really have to.
                    */

       if (poft)  {

                      lsnPhysStart =
                              RdBufCPS(ulSeekPtr, poft, &culRunLen,
                                       &lsnPhysStartRun, &culTotalRunLen, pfrm);

                      if (lsnPhysStart != LSN_BOGUS) {

                         /* Back-up to see if the entire cache block
                          * belongs to this file. Must be true if we're
                          * to avoid disk I/O
                          */

                         lsnFirstInCacheBlock = lsnPhysStart - (lsnPhysStart % SPB);

                         if (((lsnPhysStartRun + culTotalRunLen) >=
                            (lsnFirstInCacheBlock + SPB)) &&
                            (lsnFirstInCacheBlock >= lsnPhysStartRun) )  {

                            /* Established ownership of SPB sectors in the cache block */

                           if (lsnPhysStart == lsnFirstInCacheBlock) {

             if ( (ulSeekPtr % SECSIZE) == NULL &&
                  poft->fst.ulVLen <= ulSeekPtr) {

               fOmitRead = TRUE;

             }

                           }
                         }
                      } /* end if (lsnPhysStart != LSN_BOGUS) */
                   }  /* end if (poft != NULL ....) */
                }   /* end if (!(fomitRead)) */

        if (poft) {

              if (((pbuf = CCH (lsnFirstInBlock, pfrm))) != NULL)
           goto rdbuf100;    /* The sector is in the cache */

                 /*  The sector we want is not in the cache.
                  *  Get a free buffer, lock it, mark it as
        *  holding the block we need, and put it
                  *  on the appropriate hash chain.
                  */

                  pbuf = GFB(pfrm);

       /* Make sure the sector(s) didn't sneak into the cache
        * if GFB blocked
        */
       if ((pbuf1 = HashFind(lsnFirstInBlock)) != NULL) {

        pbuf->lsnSec = LSN_BOGUS;
        AddHash (pbuf);
                  PlaceBuf (pbuf, BP_TOSS);
        pbuf = pbuf1;   /* use this cache buffer */
        goto rdbuf100;
       }

                  pbuf->lsnSec = lsnFirstInBlock;

#ifdef DEBUG
                  if (pbuf->bDirtMask) {
                      INTERR ("RdBufOrGetInBuf:  GFB got a dirty buffer.\n");
                  }
#endif

        CreateBUF (pbuf);  // @114986  log new buffer creation
        LockBUF (pbuf);                // @114986
                  AddHash (pbuf);

#ifdef DEBUG2
                  ChkHashChain ();
#endif
        }

#ifndef MINIFSD
      if (poft)  {

                 if (!(fOmitRead)) {
                   /* Our flag indicates we need to hit the disk.
                    * Let's do a lazy read instead.
                    */

        if (fLWDaemon &&                    /* Lazy writer must be active */
            !(pfrm->fFlag & FFL_NOLZREAD))  // Lazy read allowed @129233
        {
          if ((pbuf->pLzRdBitMap =
              GHS(LZ_RD_BIT_MAP_SIZE,pfrm)) != NULL) {

            pbuf->fFlag |= BFL_LZREAD;
            cBufLZRD++;                          // @132506 keep count

            memset (pbuf->pLzRdBitMap, 0xFF,
                    LZ_RD_BIT_MAP_SIZE);
          }

          UnlockBUF (pbuf);                // @114986
          if (pbuf->pWaitChain != 0) {
            WakeupMisc2 (&(pbuf->pWaitChain), LOB_BUFWAIT);
          }

          *ppbuf = pbuf;
          PlaceBuf (pbuf, BP_KEEP);

               MMPHPostRDBufOrGetInBuf(); /* performance hook (3387) */

               return ((PSECTOR)(pbuf->pblkData)) + (lsn % SPB);

        }   /* end Lazy writer active */
       }
      }
#endif

                if (!fOmitRead) {

                    if (VolPtr [IVOLFROMLSN (pbuf->lsnSec)] == NULL) {

                        /*
                         *  This is a bad lsn.  If we pass it to SCR,
                         *  DoVolIO will GP-Fault.  Force an error.
                         */
#ifdef DEBUG
                        dprintf ("RdbufOrGetInBuf: bad lsn in pbuf=%p, ", pbuf);
                        dprintf ("null volptr\n");
                        Int3 ();
#endif

                        pbuf->bBadMask = (1 << SPB) -1;

                    } else if (!SCR(pbuf,(USHORT)0,(USHORT)SPB,pfrm)) {

#ifdef DEBUG
                        dprintf ("RdBufOrGetInBuf: bad SCR call, ");
                        dprintf ("lsn=0x%lx, pbuf=0x%x\n",
                                 pbuf->lsnSec,
                                 pbuf);
#endif

#ifndef MINIFSD

            _disable();   /* shouldn't be needed but let's play it safe */

            /* Take *pbuf off its hash chain since we did an AddHash for the
               lsnSec for which we're going to soon overlay with LSN_BOGUS.
               This code is the same as in gfb.c.                      73714
             */
            if (pbuf->dcbpHash.pbufFwd == pbuf) {   /* Start of 73714 code */

               /* If it points to itself, it's the only member in its
                *  hash chain. All we need to do is set the hash table
                *  entry to NULL.
                */
               *(pbuf->ppbufHTA) = NULL;
            }
            else {
               /* There were other buffers on the hash chain; the buffer
                * we want has to be detached from the chain.
                */
               if (*(pbuf->ppbufHTA) == pbuf) {

                  /* If it's at the head of the chain, we need to
                   * update the Hash Table entry.
                   */

                  *(pbuf->ppbufHTA) = pbuf->dcbpHash.pbufFwd;
               }

               /* Detach the hash chain connection */
               DCREM (pbuf, dcbpHash);

            }
            /* End of 73714 code */

            _enable();

                      pbuf->lsnSec = LSN_BOGUS;
                      AddHash (pbuf);
            ClrLockBUF (pbuf);                // @114986
            pbuf->bBadMask = SPBBITS;

                      if (pbuf->pWaitChain != 0) {
                        WakeupMisc2 (&(pbuf->pWaitChain), LOB_BUFWAIT);
                      }

                      PlaceBuf (pbuf, BP_TOSS);
            *ppbuf = pbuf;              /* Set *ppbuf so caller can check
                                           for bBadMask bits. 73714 */
         /* Indicate hard error */
            SETHARDERR(ERROR_GEN_FAILURE);
            return (NULL);
#endif

                    }

                }
#ifdef DEBUG
                else {
                    BYTE_FILL (pbuf->pblkData, 'Z', SPB * SECSIZE);
                }
                RSC (pbuf);
#endif


                /*  Now that the buffer is set up, and we've read
                 *  it if we needed to, unlock the buffer.
                 */

        UnlockBUF (pbuf);                // @114986

                  if (pbuf->cLocked == 0) {
                   if (pbuf->pWaitChain != 0)
                     WakeupMisc2 (&(pbuf->pWaitChain), LOB_BUFWAIT);
        }
            } /* end pbuf == NULL (not in cache) */

rdbuf100:

            /*  OK.  We've got the buffer we want.  Check to see
             *  if it's locked.
             */

            if (pbuf->cLocked == 0) {
                if (pbuf->bBadMask != 0) {

                    /* we got a hard error */
#ifdef DEBUG
                    dprintf ("RdBuf: got a hard error\n");
#endif
          /* Indicate hard error */              //#85759
          SETHARDERR(ERROR_GEN_FAILURE);
                    PlaceBuf (pbuf, priBuf);

                    if (bGetInBuf) {
                        /*
                         *  GetInBuf got an error.
                         *
                         *  Since the principal target of GetInBuf
                         *  is a sector we plan to overwrite, we
                         *  won't return the error.  In fact, we'll
                         *  even clear the bits that correspond to
                         *  the 'don't care' sectors, since we just
                         *  don't care about them.
                         *
                         */
#ifdef DEBUG
                        dprintf("Exercising GetInBuf's error handling.\n");
#endif
                        pbuf->bBadMask &=
                            ~( SPBBITS & abGIBMask[ lsn % SPB ][ cusSec ] );
                        *ppbuf = pbuf;

                        MMPHPostRDBufOrGetInBuf(); /* performance hook (3387) */

                        return ((PSECTOR)(pbuf->pblkData)) + (lsn % SPB);

                    }
                    else {

                        /*
                         *  RdBuf got a read error.  Set the
                         *  SPF_BADSEC (Corrupted disk) flag
                         *  in the volume table and the spare
                         *  block.  If the requested sector
                         *  is in error, return NULL to notify
                         *  the caller; otherwise, carry on
                         *
                         */
#ifdef DEBUG
                        dprintf("Exercising RdBuf's error handling.\n");
#endif
//#85759
//                      SetDiskErr_( pbuf, ERROR_GEN_FAILURE, pfrm );
                        *ppbuf = pbuf;

                        if ( pbuf->bBadMask & ( 1 << (lsn % SPB) ) ) {

                            return NULL;
                        } else {

                            MMPHPostRDBufOrGetInBuf(); /* performance hook (3387) */

                            return ((PSECTOR)(pbuf->pblkData)) + (lsn % SPB);
                        }
                    }
                }

                /* it's free and available.  Position it
                 * correctly in the LRU chain and set up
                 * return values.
                 */

                *ppbuf = pbuf;
                PlaceBuf (pbuf, priBuf);

                MMPHPostRDBufOrGetInBuf(); /* performance hook (3387) */

                return ((PSECTOR)(pbuf->pblkData)) + (lsn % SPB);
            }
            else {
                /* Locked out.  After the buffer becomes
                 * available, we go back to the top of the
                 * loop because someone might have put new
                 * data into that buffer.
                 */

                WBU (pbuf,pfrm);

            }
   }

}
