/*static char *SCCSID = "@(#)addhdrs.c	6.2 91/01/14";*/
#define SCCSID "@(#)addhdrs.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 :  ADDHDRS.C
 *    
 *
 *  DESCRIPTION
 *    
 *      Add a group of headers into either a Path Table list or other
 *      sector list.
 *
 *
 *  FUNCTIONS DEFINED
 *
 *      addhdrs ()  --  Add headers to a sector list for sectors to
 *                      be read from disk.
 *  
 * 
 *  REVISION HISTORY (in reverse chronological order)
 *  -----------------------------------------------------------------------
 *      4/9/90      Brian Andrew        Initial version.
 *
 *
 *  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"

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

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

/***    AddHdrs ()  --  Add headers to a sector list for sectors to  
 *                      be read from disk.
 *      
 *  DESCRIPTION
 *
 *      This function is called as part of searching for a sector in
 *      the sector cache.  Specifically, 'AddHdrs' is called when the
 *      sector requested is not in the cache.  This routine will attempt
 *      to add a header for the desired sector as well as for any
 *      additional sectors which may be part of a multiple sector read.
 *
 *      The additional headers are allocated and inserted into the
 *      sector list.  Any added headers are considered to be managed by
 *      the thread that is making this call and are linked together in
 *      an auxilary list for use by this thread.
 *
 *      A sector header is given as a starting point.  The headers added
 *      are connected to this header.  There are four possibilities for
 *      adding headers.
 *
 *          1 - The header is totally missing from the list.  Add a header
 *              into the list.
 *
 *          2 - The header exists but there is no attached buffer and no
 *              other disk reads pending for that sector.  Pretend that
 *              the existing header was added by this routine.  All
 *              other actions will be the same as (1) above.
 *
 *          3 - The header exists but there is a pending disk read.  We
 *              simply step over this header.
 *
 *          4 - The header and buffer are there.  We simply step over this
 *              header.
 *
 *      This function will attempt to allocate enough headers for all
 *      the sectors it will try to read.  Only the first header is needed
 *      to satisfy the actual request made to the file system.  An error
 *      is returned here only if the first request cannot be satisfied.
 *
 *      This thread has exclusive access into the target sector list
 *      during this function.
 *      
 *      This routine is only called if there will be a subsequent disk
 *      read for the starting sector.
 *      
 *      
 *  ENTRY   (Arguements passed to function)
 *      
 *      The following are the fields in the FIND_BUF_LOC structure used here.
 *
 *      pblPrevHdr      --  Pointer to header to attach the new header to.
 *      fBLOwns         --  Is this a BUF_LIST header.
 *      snStartSector   --  Starting sector number.
 *      csSectorCount   --  Offset of last sector from starting sector.
 *      pblAddedHdrs    --  Linked list of added (or assumed) headers.
 *      
 *      
 *  GLOBAL  (Global variables accessed by this function)
 *
 *      NONE.
 *
 *
 *  GOTO    (Jump targets)
 *
 *      AddHdrs_LinkHdrs    --  Common code for dealing with added structure.
 *
 *
 *  RETURN  (Value returned by this function)
 *
 *      Error from Get<sector header> if occurred on first header.
 *      NO_ERROR otherwise.
 *
 *
 *  EXIT    (Changes to variables seen outside this function)
 *
 *      The following 'pfblVars' fields may be modified.
 *
 *      pblAddedHdrs    --  Points to first header added.
 *      csSectorCount   --  Offset of last sector from first sector.
 *
 *      
 *  LOCAL   (Variables declared within this function)    
 *
 *      usRet       --  Error return code.
 *      fMinSuccess --  Flag indicating if at least one header added.
 *      usCount     --  Index count for loop.
 *      pblThisBL   --  Pointer to header to attach next header to.
 *      pblNextBL   --  Pointer to newly allocated header.
 *      snCurrSec   --  Current sector number being looked at.
 *      pblLastBL   --  Pointer to last header added.
 */


USHORT  AddHdrs (pfblVars)
PFIND_BUF_LOC   pfblVars;
{
	/* Local Variables */
	USHORT          usRet;
	BOOL            fMinSuccess;
	USHORT          usCount;
	PBUF_LIST       pblThisBL;
	PBUF_LIST       pblNextBL;
	SECT_NUM        snCurrSec;
	PBUF_LIST       pblLastBL;

	/* Initialize the local variables */
	fMinSuccess = FALSE;
	usRet = NO_ERROR;
	usCount = pfblVars->csSectorCount;
	pblThisBL = pfblVars->pblPrevHdr;
	pblLastBL = NULL;
	snCurrSec = pfblVars->snStartSector;

	/*
	 *  do loop :
	 *
	 *      This loop is traversed for each header which lies in the range
	 *      of sectors being considered.  We either add a new header,
	 *      assume ownership of an existing header or skip any headers
	 *      which have pending reads.
	 *
	 *      Exit conditions are either an allocation error occurred or
	 *      all the sectors have been traversed.
	 */
	do
	{
		/* Do we need to add a header */
		if (!pblThisBL->pblNext ||
			pblThisBL->pblNext->snSectNum != snCurrSec)
		{
			/* Try to allocate the appropriate header */
			if (!(usRet = ( pfblVars->fBLOwns ?
							GetBufList (&pblNextBL) :
							GetPathTable ((PPPATH_TABLE) &pblNextBL))))
			{
				/* Attach into the main sector list */
				if (pblNextBL->pblNext = pblThisBL->pblNext)
				{
					pblNextBL->pblNext->pblPrev = pblNextBL;
				}

				pblNextBL->pblPrev = pblThisBL;
				pblThisBL->pblNext = pblNextBL;

				pblNextBL->snSectNum = snCurrSec;
				
				pblNextBL->plsbSemBlk = pblThisBL->plsbSemBlk;

/* Common code for linking new hdrs */
AddHdrs_LinkHdrs:

				/* Attach to private header list */
				if (pblLastBL)
				{
					pblLastBL->pblNextHdr = pblNextBL;

				}

				/* Remember this as last header added and increment pending */
				(pblLastBL = pblNextBL)->usCountPend++;

				/* Grab the header semaphore */
				SEM_REQ (pblNextBL->sHeaderSem);

				/* Indicate minimal success achieved */
				fMinSuccess = TRUE;

			} /* end if allocate appropriate header */

			/* Else the allocation was unsuccessful */
			else
			{
				/* If minimal success, then clear the error code */
				if (fMinSuccess)
				{
					usRet = NO_ERROR;

					/* Exit the do looop */
					break;

				} /* end if minimal success */

			} /* end else unsuccessful header allocation */

		} /* end if adding a header */

		/*
		 *  Remember the next header and check if it has a buffer with no
		 *  pending reads.
		 */
		else if (!( (pblNextBL = pblThisBL->pblNext)->pbBuf ||
					pblNextBL->usCountPend))
		{
			/* Jump to common code above */
			goto AddHdrs_LinkHdrs;

		} /* end else if no-pending reads or existing buffer */

		/* Move to the next header */
		pblThisBL = pblNextBL;

		/* Increment the current sector number */
		snCurrSec++;

	/* end do loop for each header */
	} while (usCount--);

	/* If mimimal success */
	if (fMinSuccess)
	{
		/* Null terminate the list of headers */
		pblLastBL->pblNextHdr = NULL;

		/* Store the start of the added headers in 'pfblVars' */
		pfblVars->pblAddedHdrs = pfblVars->pblPrevHdr->pblNext;

		/* Store the actual range of sectors in 'pfblVars' */
		pfblVars->csSectorCount = (USHORT) ((ULONG) (
												pblLastBL->snSectNum -
												pfblVars->snStartSector));

	} /* end if mimimal success */

	/* Return the result */
	return usRet;

} /* end AddHdrs */
