/*static char *SCCSID = "@(#)efa.c	6.10 92/01/31";*/
#define ASWAP0 ASWAP0

#include <os2def.h>
#include "chain.h"
#include "const.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "storage.h"
#include "volume.h"
#include "tables.h"
#include "pbmacros.h"
#include "pb_ifs.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"

#include "log.h"

#include "pr_ALLOC.h"
#include "pr_BITMP.h"
#include "pr_CACHE.h"
#include "pr_DEBUG.h"
#include "pr_FSSUB.h"
#include "pr_MISC.h"


/*
 ***	EFA - Extend File Allocation
 *
 *	EFA is called to extend the allocation size of an open file.
 *
 *	The caller guarantees that the file needs at least one
 *	more sector; we'll blow up if no growth is needed.
 *
 *	Note that any extra growth for "preallocation" has to be
 *	added to the request before EFA is called.  EFA will allocate
 *	just the amount that he was asked to.
 *
 *	BUGBUG - we still screw up when we run out of space
 *
 *	First, we allocate sectors contiguous with our current last
 *	sector.  If that's insufficient, we take a fresh block to
 *	satisfy our remaining needs.  We'll add a fudge factor and
 *	get that many.  Fresh blocks are allocated:
 *
 *
 *	l1:	Get them contiguous to the current space, if possible
 *		else get them contiguous in another band, if possible
 *		else
 *		cut the request length in half, goto l1
 *
 *		when we find a section, take as many extra contigous
 *			sectors as we can
 *
 *	Note that it's rare that EFA is called to extend a zero length
 *	file.  When we create a file we try to get the first allocation
 *	ajacent to the FNODE.  When we truncate a file we leave some of
 *	it's first extent allocated.  EFA would create the first allocation
 *	only if the file were created and closed zero length, then reopened,
 *	or only if we couldn't get the GROWDELT+1 sectors when we setup the
 *	FNODE.
 *
 *	EFA updates OFT_ALEN to indicate the allocated amount.  We
 *	do this piecemeal as we allocate each item.
 *
 *	The OFT must be held upon entry to EFA, EFA will conver the hold
 *	to a lock and leave it locked.
 *
 *	BUGBUG - if we fail to extend the current extent and have to
 *		 break away, check to see if the last extent is totally
 *		 preallocation (not used) and if so, discard it.  This
 *		will optimize short files being overwritten by large data.
 *
 *	WARNING - the caller must guarantee that EFA is called to grow
 *		the file by at least one sector!  We blow up if we're to
 *		grow <= 0 sectors
 *
 *	ENTRY	ulNewLen	= new length
 *		poft		= OFT pointer (held)
 *		fsWrite		= write type flags
 *
 *	RETURNS OFT is locked
 *		TRUE if space allocated
 *		    OFT is updated
 *		FALSE if space not findable
 *		Yields CPU
 *		OFT marked corrupt for some hard errors
 *
 *	HISTORY
 *	    19-Sep-89 by JR (John Rogers, JohnRo@Microsoft)
 *		- Avoid warnings from Microsoft C 5.2 (on slash-slash comments).
 *	    27-Oct-1988 10:03:58 by Mark Hitch [markh] - Update to JGL's 10/24
 *	    21-Apr-1989 by John Rogers [JohnRo@Microsoft]
 *		- Changed names of rounding macros in <pbmacros.h>.
 *		- PTR B783576: prevent overflow.
 */


BOOL
EFA (ulNewLen, poft, fWriteThru,pfrm)
ULONG ulNewLen;		/* New Length */
POFT poft;		/* Pointer to Open File Table */
BOOL fWriteThru;	/* Write Through flag*/
PFRAME pfrm;		/* frame pointer */
{
    LSN		lsn;
    LSN		lsnLog; /* work lsn's */
    ULONG	ul;
    ULONG	ulTry;	/* work ulongs */
    PVOLTAB	pvol;

    /*
     *	 We've got to convert our hold to a lock.  We know that it's not
     *	 locked now, of course, because we're holding it.
     */
    DecrHoldsOFT (poft);

    if( poft->cusHolds == 0 && (poft->bFlag & OTF_PND) ) {

	WakeupOFT( poft );
    }

    /* wait until it's lockable */
    LockOFT( poft,pfrm );

    /* Lock it for us! */
    SetLockOFT(poft);

#ifdef DEBUG
    poft->bSFlag |= OFS_SAC;
#endif

    /*
     * Check to see if requested length will force the ulFAllocLen to
     * go beyond the MaxFileSize limit and if yes disallow the allocation
     * on the assumption most callers will try again with a bare minimum
     */

    if ((ulNewLen = ROUND_TO_SECSIZE(ulNewLen)) > CCH_MAXFILESIZE) {
       return(FALSE);
    }

    /* figure out how many sectors to grow */
    ulNewLen = (ulNewLen - poft->ulFAllocLen)/SECSIZE;

    if( ( (pvol = poft->pVolume)->csecSpace -= ulNewLen ) < 0 ) {
	/* no space on volume */
	goto efa900;
    }

    /*
     * locate last existing sector of the file
     *
     * ulNewLen (ecx) = sectors to grow
     * poft	(edi) = OFT pointer
     */

    if( (lsn = poft->lsnLast) == 0 && poft->ulFAllocLen == 0 ) {

	/*
	 *  This is an initial allocation.  There isn't an initial extent
	 *  to grow on so we'll use the FNODE sector as "the last sector
	 *  allocated" and jump down to where we create a new extent.
	 *
	 * ulNewLen (ecx) = sectors to grow
	 * poft	    (edi) = OFT pointer
	 */

	poft->lsnLast = (poft->scpFNode.lsn) & SECMASK;

	poft->LastRun.pallLastRun = &poft->fst.a.aall[0];

    } else {

	/* didn't have an OFT_LAST value, but file is non-zero. */
	if( lsn == 0 ) {
	    /* do this line if above conditional failed on 2nd term */
/* efa4: */
	    /* compute oft last sector */
	    if ((lsn = COLS(poft,pfrm)) == LSN_BOGUS) {
		goto efa900;
	    }
	}

	/*
	 *  We know the last allocated block for this file.
	 *
	 *  compute the # of sectors that we have to allocate and try
	 *  to get them contiguously.
	 *
	 *  ulNewLen	(ecx) = sectors to grow
	 *  lsn		(ebx) = last sector allocated
	 */

/* efa5: */

	if ((ulNewLen < BANDSIZE) && VBR((USHORT)ulNewLen, ++lsn, pfrm)) {

	    /*
	     ***  The sectors that we want are available contiguously.
	     *
	     *	  Then update the band pointer to show which band we're
	     *	  growing in
	     *
	     *	  ulNewLen  (esi) = sectors that we will grow
	     *	  lsn	    (ebx) = first sector # that we'll allocate
	     */

	    if (!GroExt(poft, fWriteThru, ulNewLen, lsn, pfrm)) {

		/* hard error */
#ifdef DEBUG
		dprintf (__FILE__ "(%d): GroExt returned error\n", __LINE__);
#endif
		goto efa120;
	    }

	    if (poft->pbtLastAlloc == NULL ) {
		(poft->pbtLastAlloc =
		    poft->pVolume->apbndList [USSHIFTRUL (lsn,
				  BANDSHIFT)])->cFilesInBand++;
	    }

#ifdef DEBUG
	    poft->bSFlag &= ~(OFS_SAC);
#endif

	    return TRUE;
	}


	/**
	 * We couldn't get our whole allocation contiguously to the current
	 * allocation.  See if we can get it in one piece in another band.
	 *
	 * We haven't modified OFT_ALEN yet, as we haven't taken any sectors
	 * yet.
	 *
	 * lsn	    (ebx) = last file sector +1
	 * ulNewLen (esi) = sectors that we wanna grow
	 */

/* efa30: */
	if( (ul = CBR(lsn,pfrm) ) != 0 ) {

	    /*
	     * We can grow the last extent a little.  Simplest thing to do
	     * is to call ourselves recursively for that growth.
	     *
	     * lsn	(ebx) = last file sector +1, start of contig run
	     *			we've found
	     * ul	(esi) = sectors that we have at (ebx)
	     * ulNewLen (TOS) = original growth request
	     */

       if (ul > ulNewLen)
         ul = ulNewLen;

	    if (!GroExt( poft, fWriteThru, ul, lsn, pfrm)) {
		/*
		 * hard error
		 */
#ifdef DEBUG
		dprintf (__FILE__ "(%d): GroExt returned error\n", __LINE__);
#endif
		goto efa120;
	    }

	}	/* end EFA30   */
/* efa35: */

	/* ulNewLen (eax) = remaining request */
	ulNewLen -= ul;

    }

    /*
     *	We've taken as much contiguous space as we could.  Look for
     *	the remainder in a band.  If we can't get it as a single
     *	block, divide our try amount in half and try again.
     *	When we get a block, don't just allocate the block-request
     *	size, allocate as much of the total amount as we can.
     *
     *	We look for each block starting at the end of the
     *	previously allocated block, wrapping around to the
     *	front if necessary.  We know that there is enough free
     *	space for this allocation, so we don't have to worry about
     *	overrunning.		    BUBUG - No we dont!
     *
     *	Note that we'll multiply the try_amount by two to simplify
     *	our loop structure.  If the request amount is > 1099
     *	BILLION bytes then this will drop the high order bit, so
     *	we're incapable of allocating a contiguous extent larger than
     *	1099 billion bytes, such a file will take two or more
     *	extents.  Sue me.  And besides, it'll take 134 seconds
     *	to scan the bit map!  Also, as the code now stands, we can't have
     *	a single extent larger than a band (about 8 meg) anyway.
     *
     *		try_amount = amount_left
     *	l1:	IF (found (try_amount))
     *			allocate as much as we can here
     *			adjust amount left
     *			try_amount = min(try_amount, amount_left)
     *			goto l1
     *		try_amount /= 2
     *		goto l1
     *
     *	ulNewLen (eax) = sectors to grow
     */

/* efa50: */
    ulTry = ulNewLen;

    /*
     * ulTry	  (eax) = try_amount
     * ulNewLen	  (esi) = amount_left
     */

/* efa60: */
/* efa62: */
    if( ulTry == 0 ) {
#ifdef DEBUG
	dprintf (__FILE__ "(%d): Ran out of victims\n", __LINE__);
#endif
	goto efa120;
    }

    for( ; ; ) {	/* middle logic loop */
	PBANDTAB pbtb;
/* efa64: */
	lsn = poft->lsnLast;

       /*
	*  We keep track of how many files are growing in each band.
	*  If this file is included in a count, we gotta decrement it
	*  so that we don't avoid allocating in our own band because
	*  we're in it!  There's a race condition in that we might block
	*  while we're allocating and someone else might then start
	*  allocating from our band because our "use count" was low, but
	*  this should be rare.
	*/
	if ((pbtb = poft->pbtLastAlloc) != NULL) {

	    /* we are counted in this band */
	    poft->pbtLastAlloc = NULL;
	    pbtb->cFilesInBand--;

#ifdef DEBUG
	    if (MAKETYPE (pbtb->cFilesInBand, SHORT) < 0) {
		dprintf (__FILE__ "(%d): culFilesInBand going negative\n",
			 __LINE__);
		Int3 ();
	    }
#endif

	}

	if (!AFB (ulTry, lsn, &lsn, pfrm) ) {
	    /* Halve try amount and try again */
	    ulTry /= 2;

	    if( ulTry == 0 )
	    {
	      /* If we run out of space entirely then give up */
	      /* In a debug build, just halt */
	      /* In a production build, fail */
#ifdef DEBUG
		INTERR( "efa: Ran out of victims!!!" );
#endif
	      goto efa120;
	    }

	    continue;	/* jump back to top of loop */
	}

	/*
	 * We have an allocation.  If it's not as big as our requested
	 * amount, see how big it can be made.  It's possible, due to
	 * blocks to create new SIBs, that the whole amount is now available.
	 *
	 * ulTry    (eax) = size of allocation
	 * lsn	    (ebx) = first sector allocated
	 * ulNewLen ((TOS)) = request amount
	 */

	if ((ul = ulTry) != ulNewLen ) {
	    if( (ul = CBR(lsn,pfrm)) > ulNewLen ) {
		ul = ulNewLen;
	    }
	}

	/*
	 * We have the size of this block of sectors.  Claim the maps
	 * and then add an extent record
	 *
	 * ul	    (eax) = size of allocation
	 * lsn	    (ebx) = first sector of allocation
	 * ulTry    ((TOS)) = try amount
	 * ulNewLen ((TOS)+4) = request amount
	 */

/* efa85: */
	/* subtract amount we got from amount we need */
	ulNewLen -= ul;

	/* add sectors back in so ClrBit can subtract them out again */
	pvol->csecSpace += ul;
                                                        //#85759
	if (!ClrBit(lsn, ul, TRUE, pfrm))
     goto efa120;

	lsnLog = poft->ulFAllocLen / SECSIZE;

	poft->ulFAllocLen += ul * SECSIZE;

	/* reset last sector */
	poft->lsnLast = lsn + ul - 1;

	if (!AddExt (poft->scpFNode.lsn & SECMASK,
		     &poft->fst.alb, &poft->LastRun,
		     fWriteThru, lsn, lsnLog, ul, pfrm)) {
#ifdef DEBUG
	    dprintf (__FILE__ "(%d): AddExt returned error\n", __LINE__);
#endif
	    goto efa120;
	}

	if( ulNewLen == 0 ) {
	    /* all done */
	    break;
	}

	/* don't ask for more than needed */
	if( ulTry > ulNewLen ) {	     /* or if( be ) continue; */
	    ulTry = ulNewLen;
	}
    }


/* efa100: */
    /*
     * Completed our allocation.
     *
     * poft (edi) = OFT pointer
     */

#ifdef DEBUG
    poft->bSFlag &= ~(OFS_SAC);

    if( poft->pbtLastAlloc != NULL )
	INTERR (__FILE__": pbtLastAlloc is not NULL");
#endif

    (poft->pbtLastAlloc =
	poft->pVolume->apbndList[ USSHIFTRUL (poft->lsnLast,
					      BANDSHIFT)])->cFilesInBand++;

    return  TRUE;

/*
 * BUGBUG - we can reduce the amount of thrashing when we get
 * here with write through by keeping a "dirty sector" pointer
 * and not writing through if that one is changed.  We'd have
 * it be the first one that we find, and this top guy would
 * write it and zero it if it found that it differed from the
 * last SIB pointer guy
 */

efa120:

    /* Hard error - Can't read allocation blocks, or out of space
     *
     * out of space isn't supposed to happen
     * because we certify the space before we start, but if we get a hard
     * error on the bitmap, it's possible.
     */

    /* Mark the file corrupt and return "no space" */

#ifdef DEBUG
    dprintf (__FILE__": Hard error-not debugged yet");
    Int3 ();
#endif

    /* 386 ASM code:
     *
     * efa120:	int	3; hard error
     *		mov	edi,E_OFT[ebp]
     *		MLEAVE
     *		jmp	OFTF...
     */

    OFTCorrupt(poft,pfrm);

#ifdef DEBUG
    goto efaDBG;
#endif

    /*
     * common error exit code
     */
efa900:


#ifdef DEBUG
    poft->bSFlag &= ~(OFS_SAC);

efaDBG:

#endif

    /* return space not used to free count */
    pvol->csecSpace += ulNewLen;
    return (FALSE);

}
