/*static char *SCCSID = "@(#)readdir.c  6.9 92/02/12";*/
#define  INCL_DOSERRORS
#include <os2def.h>
#include <doscalls.h>
#include <bse.h>
#include <fsh.h>
#include <strat2.h>
#include <pvwxport.h>
#include "filemode.h"
#include "const.h"
#include "chain.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "storage.h"
#include "volume.h"
#include "tables.h"
#include "pb_ifs.h"
#include "lw.h"
#include "frame.h"
#include "mmphiseg.h"
#define  INCL_DOSINFOSEG               /* for mmph                          */
#include "fsdata.h"
#include "pbmacros.h"
#include "log.h"
#include "asyncio.h"
#include "assert.h"
#include "pvw.h"
#include "mmph.h"
#include "pr_cache.h"
#include "pr_dskio.h"
#include "pr_read.h"
#include "pr_misc.h"
#include "pr_debug.h"
//**************************************************************
//* Prototypes for local routines
//**************************************************************

/*
 ***    ReadDir - Read Direct from Disk
 *
 *  ReadDir is a captive of SDR.  It's called to read
 *  data as directly from disk as possible.  It is the
 *  parallel of WriDir.
 *
 *  For each contiguous run, we read as many sectors directly
 *  to disk as we can until we find one in the cache, we then copy
 *  those from the cache, etc., until the run is exhausted.
 *
 *  We've got a contiguous run of N sectors to transfer, where
 *  N is non-zero.
 *
 *  We want to check each sector in this run to see if it's
 *  in the cache.  We only need to check every SPB sector,
 *  so we'll round our physical sector down to an SPB
 *  multiple (increasing our count to cover this) so that
 *  we can conveniently check every SPBth sector for
 *  cache presense.  When it's time to do the transfer
 *  we'll be careful so that we don't transfer the
 *  extra sectors we may have put on the front of the
 *  run by this trick.  Note that these are "virtual sectors" -
 *  we know that they exist on the disk, and it doesn't matter
 *  if they are part of this file or not, since we won't try
 *  to read them.
 *
 *
 *  ENTRY   vlsnStart = physical sector # at start of run
 *      culRunLen = run length in sectors
 *      ppbDest = pointer to Destination address of read
 *      pcusXfer = pointer to remaining transfer count
 *      pNoteRec = pointer to notification record
 *
 *  EXIT    *ppDest updated
 *      *pcusXfer updated
 *      *pNoteRec updated
 *
 *  HISTORY
 * @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 *         21-Sep-1988 10:11:54 by Bill McJohn [t-billm]
 *              brought up to date with 9/20/88 assembly code
 *         07-Feb-90 by JR (John Rogers, JohnRo@MICROSOFT)
 *      - ADDED NOTEREC ASSERTION CHECKS.
*/

USHORT ReadDir(LSN vlsnStart,          /* sector num. at beginning of run   */
               ULONG culRunLen,        /* run length in sectors             */
               PBYTE far *ppbDest,     /* destination address of read       */
               PUSHORT pcusXfer,       /* remaining byte transfer count     */
               PNOTEREC pNoteRec,      /* pointer to notification record    */
               PBYTE pbEventFlags,     /* Pointer to event flags            */
               PFRAME pfrm)
{
   PBUFNODE pbufCache;                 /* ptr to buffer for cache read      */
   PBUFNODE pbufLastUsed;
   ULONG culRemaining;                 /* sectors remaining after subrun    */
   USHORT cusSectsToRead;              /* sectors in current subrun         */
   LSN vlsnNextRun;                    /* beginning of next subrun          */
   USHORT cSec;
   USHORT usRet = 0;

/*   USHORT iVol=IVOLFROMLSN(vlsnStart);   Get access to ...hotfix flag      */
/*   PVOLTAB pVol=VolPtr[iVol];               "                                */

   MMPHPreReadDir();                   /* performance hook (3308)           */

   while (culRunLen)
  {
      vlsnNextRun = vlsnStart-vlsnStart%SPB;
      culRemaining = culRunLen+vlsnStart%SPB;

      do
         {

#ifdef   PERFVIEW2
         PVWSTART(Rd, CCH);
#endif
          pbufCache = CCH(vlsnNextRun,pfrm);

#ifdef   PERFVIEW2
         PVWSTOP(Rd, CCH);
#endif

rddir120:
          if (pbufCache == NULL) {
            if (culRemaining >= SPB) {

               vlsnNextRun += SPB;
               culRemaining -= SPB;
            }
            else {

               vlsnNextRun += culRemaining;
               culRemaining = 0;
            }
          } else {

        if (pbufCache->cLocked) {
          if (WBU (pbufCache,pfrm) == WBU_NEW) {
            /*  when we got the buffer back, it had
             *  a different data block in it.
             */
             pbufCache = NULL;
             goto rddir120;
          }
        }
     }
         }
                while ((pbufCache == NULL) && (culRemaining));

      /**********************************************************************/
      /* We've found the end of a subrun, either because we * reached the   */
      /* end of the run or because there was a * cache hit. * * If the read */
      /* is non-zero length, build a request * block for it. *              */
      /**********************************************************************/

      if (culRunLen > culRemaining) {
        cusSectsToRead = (USHORT)(culRunLen-culRemaining);
           }
      else {
            cusSectsToRead = 0;
           }

  if (cusSectsToRead) {

         /*******************************************************************/
         /* If we found a cache hit, hold the buffer in question * so it    */
         /* doesn't get away while we're setting up the * direct read. *    */
         /*******************************************************************/

   if (pbufCache)
     IncrHoldsBUF(pbufCache);  // @76193

//test. force strat1
// goto strat1;

   if (IsAsyncIOable(vlsnStart)) {

         ASSERT_NOTEREC(pNoteRec);

#ifdef   PERFVIEW2
         PVWSTART(Rd, QRead);
#endif
         //*****************************************************************
         //* IF QRead OK
         //* THEN copy sectors to cache if crecl flag is on
         //*****************************************************************
         usRet = QRead(vlsnStart, cusSectsToRead,
                                *ppbDest, pNoteRec, pbEventFlags, pfrm);

         // Fill out pNoteRec for MoveToCache stuff
         if ((pNoteRec->byCreclOn) && (usRet == NO_ERROR)) { /* 108445 */

                 BYTE iu;

                 //
                 // Fill out pNoteRec List
                 //
                 iu=pNoteRec->byELIdx;
                 pNoteRec->aExtentList[iu].ulSecFrs = vlsnStart;
                 pNoteRec->aExtentList[iu].usSecCnt = cusSectsToRead;
                 pNoteRec->aExtentList[iu].pbyClntBuf = *ppbDest;
                 // Increment index
                 pNoteRec->byELIdx++;

         }

#ifdef   PERFVIEW2
         PVWSTOP(Rd, QRead);
#endif
         ASSERT_NOTEREC(pNoteRec);

   } else {

strat1:

    cSec =  cusSectsToRead;
    usRet = DoVolIO (DVIO_OPREAD, vlsnStart, &cSec, *ppbDest, pfrm);
   }

   /*******************************************************************/
   /* Decrement the holds on our cache buffer, if we found one.       */
   /*******************************************************************/

    if (pbufCache)
      DecrHoldsBUF(pbufCache);  // @76193

    if (usRet) {
      MMPHPostReadDir();         /* performance hook (3388)           */
      return (usRet);
    }
                                       /* re-order below 2 LOCs for #108445 */
                                       /* FXM                               */
    *ppbDest += cusSectsToRead *SECSIZE;
    *pcusXfer -= cusSectsToRead *SECSIZE;


  }  /* end (cusSectsToRead > 0) */

      /**********************************************************************/
      /* We've posted our read, if any. If there are sectors left in this   */
      /* run, we know that the next MIN (SPB, culRunLen - cusSectsToRead)   */
      /* sectors are in a cache block. Note that we may not be at the       */
      /* first sector in the block (if the beginning of our original run    */
      /* length is in the cache).                                                                   */
      /**********************************************************************/

      vlsnStart += cusSectsToRead;
      culRunLen -= cusSectsToRead;

      if (culRunLen) {

#ifdef   PERFVIEW2
         PVWSTART(Rd, CsecXfer);
#endif

         usRet = CSecXfer(&vlsnStart, &culRunLen, pbufCache, ppbDest,
                          pcusXfer, &pbufLastUsed, pfrm);

#ifdef   PERFVIEW2
         PVWSTOP(Rd, CsecXfer);
#endif
      }

      if (usRet) {
        MMPHPostReadDir();        /* performance hook (3388)           */
        return (usRet);
      }

      /**********************************************************************/
      /* Note that Csecxfer will update vlsnStart, culRunLen, *ppbDest,     */
      /* and *ppbufLastUsed for us. In particular, if Csecxfer is able to   */
      /* complete the run length for us, it will return with culRunLen ==   */
      /* 0, which ends the loop.                                                                    */
      /**********************************************************************/

  }  /* end while (culRunLen) */

   MMPHPostReadDir();                  /* performance hook (3388)           */
   return (NO_ERROR);
}
