/*static char *SCCSID = "@(#)findbuf.c	6.2 91/01/14";*/
#define SCCSID "@(#)findbuf.c	6.2 91/01/14"
/*
 *	IBM/Microsoft Confidential
 *
 *	Copyright (c) IBM Corporation 1987, 1989
 *	Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	All Rights Reserved
 *
 */

/*  NAME :  FINDBUF.C
 *    
 *
 *  DESCRIPTION
 *    
 *      This file contains the routines that initiate the search through
 *      a sector list.
 *
 *
 *  FUNCTIONS DEFINED
 *
 *      FindBuf ()      --  Search through a sector list.
 *  
 * 
 *  REVISION HISTORY (in reverse chronological order)
 *  -----------------------------------------------------------------------
 *  4/11/90     Brian Andrew        Initial version of 'FindBuf'.
 *
 *
 *  TO DO
 *  -------------------------------------------------------------------------
 */

#define INCL_DOSERRORS
#define INCL_NOPMAPI

#include <os2.h>
#include <fsh.h>

#include "cdfs.h"
#include "sem.h"
#include "path.h"
#include "vol.h"
#include "heap.h"
#include "mem.h"
#include "buffer.h"

#ifdef STATS_H
#include "stats.h"
#endif 

#include "message.h"
#include "fsdata.h"
#include "stdlib.h"
#include "cache.h"

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

#include "pr_cache.h"
#include "pr_memin.h"


/***    FindBuf ()      --  Search through a sector list.
 *      
 *  DESCRIPTION
 *
 *      This is the entry point to search through a sector list.
 *      On entry, this function allocates a structure to store the
 *      the variables used in the search.
 *
 *      The number of sectors to read is computed, using the value 0 for
 *      for a single sector read.
 *
 *      First this thread becomes a reader of the sector list and checks
 *      whether the desired sector is in the list.  If so, it yields
 *      its reader status and returns the desired buffer.
 *
 *      Otherwise the thread becomes a writer and checks the list again
 *      in case the sector was found in the process of becoming a writer.
 *      If the sector is not found, 'ResearchList' will have added the
 *      appropriate headers for the list.  Next the starting sector and
 *      number of sectors to read based on the physical properties of the
 *      volume are stored in the FIND_BUF structure.
 *
 *      Writer status is yielded while the read is performed with
 *      'PerformRead'.  This read will perform either a multiple sector
 *      read or a single sector read.  If a successful multiple sector
 *      read was done, 'GetBufChain' is called to allocate the buffers
 *      for the data from the disk and 'FillBuf' is called to store the
 *      data in them.
 *      
 *      If there have been no errors, the thread regains writer status.
 *      The buffers with sector data are added to the list with a call
 *      to 'AttachBuf'.
 *
 *      In all cases the desired buffer is copied to the formal arguement
 *      and any error encountered is returned.
 *      
 *      
 *  ENTRY   (Arguements passed to function)
 *      
 *      pvddVolData     --  Volume data for this volume.
 *      fBLOwns         --  Flag indicating whether this is a path table
 *                          or a data sector list.
 *      snStartSector   --  Starting 2K sector.
 *      hVPB            --  Handle to VPB.
 *      ppbThisBuf      --  Address to store pointer to buffer.
 *      snLastSector    --  Last sector in range to search for.
 *      usThisSecType   --  Type of sector (for statistics version).
 *      
 *
 *  GLOBAL  (Global variables accessed by this function)
 *
 *      cFileSectors    --  Number of file sectors to read at a time.
 *      CurSt           --  Statistics block (for statistics version).
 *      
 *
 *  GOTO    (Jump targets)
 *
 *      NONE.
 *
 *
 *  RETURN  (Value returned by this function)
 *
 *      NO_ERROR if sector is found.
 *      Error code from within 'ResearchList' if fatal error.
 *      Error code from 'GetBufChain' if fatal error.
 *
 *
 *  EXIT    (Changes to variables seen outside this function)
 *
 *      ppThisBuf has address of buffer if successfully found.
 *      CurSt is updated for hits or misses (statistics version only).
 *      
 *      
 *  LOCAL   (Variables declared within this function)    
 *      
 *      pfblVars    --  FIND_BUF_LOC structure.
 *      usRet       --  Return error code.
 */
USHORT FindBuf (pvddVolData, fBLOwns, snStartSector, hVPB, ppbThisBuf,
				snLastSector
#ifdef STATS_H
				, usThisSecType
#endif                
				)
PVOL_DEP_DATA   pvddVolData;
BOOL            fBLOwns;
SECT_NUM        snStartSector; 
USHORT          hVPB;          
PPBUF           ppbThisBuf;    
SECT_NUM        snLastSector;  
				
#ifdef STATS_H
USHORT          usThisSecType;
#endif

{
	/* Local variables */
	PFIND_BUF_LOC   pfblVars;
	USHORT          usRet;

	/* Confirm the target sector is valid */
	if (snStartSector < 0)
	{
		return ERROR_SECTOR_NOT_FOUND;
	}

	/* Try to allocate local structure */
	if (!(usRet = GetFindBufLoc (&pfblVars)))
	{
		/* Initialize the local structure */

		/* Remember the starting sector */
		pfblVars->snStartSector = snStartSector;

		/* Check for which list to use */
		if (pfblVars->fBLOwns = fBLOwns)
		{
			pfblVars->pblPrevHdr = pvddVolData->pblSectorList;

		} /* end if BUF_LIST structure */

		/* Else remember path table list */
		else
		{
			pfblVars->pblPrevHdr = (PBUF_LIST) pvddVolData->pptPathTable;
		}

		/* Remember the list semaphore block and become reader of the list */
		BecomeReader (  pfblVars->plsbThisSemBlk =
						pfblVars->pblPrevHdr->plsbSemBlk);

		/* Check if sector is already in the list */
		if (FindExistBuf (pfblVars))
		{
			/* Yield read status */
			DepartReader (pfblVars->plsbThisSemBlk);

#ifdef STATS_H
			/* Register cache hit */
			CurSt.st_hits[usThisSecType]++;
#endif

		} /* end if sector exists */

		/* Else become a writer and modify sector list */
		else
		{
			/* Yield read status */
			DepartReader (pfblVars->plsbThisSemBlk);

			/* Finish initializing the local structure */

			/* Initialize the offset of the last sector */
			if (snLastSector < 0)
			{
				snLastSector = snStartSector;
			}

			pfblVars->csSectorCount =
				(USHORT) (min ( (   (snLastSector == MAX_SECTOR) ?
									MAX_DIR_SECTORS : cFileSectors),
								max (0L, snLastSector - snStartSector)));

			/* Become a writer */
			BecomeWriter (pfblVars->plsbThisSemBlk);

			/* Check if sector exists, or modify header list */
			switch (ResearchList (pfblVars))
			{
				/* Case I : no sector but headers added */
				case ADDED_HDRS :
					
					/* Complete initialization of local structure */

					/* Remember the volume data and VPB */
					pfblVars->pvddVolData = pvddVolData;
					pfblVars->hVPB = hVPB;

					/* Compute physical base sector dependent values */
					pfblVars->snAdjStartSect =
						snStartSector * pvddVolData->usSectorBase;

					pfblVars->csReadSectors =
						(pfblVars->csSectorCount + 1) * pvddVolData->usSectorBase;

#ifdef STATS_H
					/* Show that we had a cache miss */
					CurSt.st_misses[usThisSecType]++;
#endif

					/* Try to read from disk */
					/* Check for single sector read or successful BUF alloc */
					if (!   (   (usRet = PerformRead (pfblVars))  ||
								(   pfblVars->fMulti &&
									(usRet = GetBufChain (pfblVars)))))
					{
						/* If multisector then fill in the new buffers */
						if (pfblVars->fMulti)
						{
							FillBuf (pfblVars);

						} /* end if multisectors */

						/* Become a writer */
						BecomeWriter (pfblVars->plsbThisSemBlk);

						/* Attach the new data */
						AttachBuf (pfblVars);

					} /* end if single sector or successful BUF alloc */

					/* Else clear the unneeded headers */
					else
					{
						/* Become a writer */
						BecomeWriter (pfblVars->plsbThisSemBlk);

						CleanUpHdrs (   pfblVars->pblAddedHdrs,
										pfblVars->fBLOwns);

						/* Exit as a writer */
						DepartWriter (pfblVars->plsbThisSemBlk);

					} /* end else clear unneeded headers */

					break;

				/* If 'ResearchError' then copy it into the return value */
				case ADD_ERROR :

					/* Copy the error value */
					usRet = pfblVars->usErrCode;

					break;

#ifdef STATS_H
				/* Otherwise buffer was found, no action unless statistics */
				default:

					/* Register cache hit */
					CurSt.st_hits[usThisSecType]++;
#endif

			} /* end switch on 'ResearchList' */

		} /* else modify list */

		/* Copy the address of the buffer */
		*ppbThisBuf = pfblVars->pbBufChain;

		/* Free the structure */
		FREE_FIND_BUF (pfblVars);

	} /* end if allocated local structure */

#ifdef DEBUG
	/* Else print debug message */
	else
	{
		dprintf ("\tFindBuf: GetFindBufLoc failed -- %d\n", usRet);
	}

	/* Check for successful with buffer locked */
	if (!usRet)
	{
		if (!((*ppbThisBuf)->cUsers))
		{
			dprintf ("\tFindBuf: Buffer not locked\n");
			Int3();

		}
	}
#endif

	return usRet;

} /* end FindBuf */


