/*static char *SCCSID = "@(#)zerobyte.c	6.3 92/01/31";*/
#include <os2def.h>

#include "const.h"
#include "misc.h"
#include "buf.h"
#include "chain.h"
#include "dir.h"
#include "volume.h"
#include "storage.h"
#include "tables.h"
#include "fnode.h"
#include "ea.h"
#include "frame.h"
#include "lw.h"
#include "fsdata.h"
#include "pbmacros.h"
#include "log.h"

#include "pr_write.h"
#include "pr_read.h"
#include "pr_ea.h"
#include "pr_misc.h"
#include "pr_fssub.h"
#include "pr_debug.h"

/*
 ***	 ZeroBytes - Zero File Bytes
 *
 *	 ZeroBytes is called to zero a range of bytes in a file.
 *	 When new sectors are allocated to a file we don't initialize
 *	 them, since they'll likely be written over.  We keep track of
 *	 this trailing "allocated but invalid" range via FN_VLEN.
 *	 If a user tries to do an I/O operation beyond this point
 *	 we'll need to zero the intervening bytes, since we don't
 *	 want to keep track of an arbitrary set of invalid ranges within
 *	 a random access file.
 *
 *	 ZeroBytes is called to do the zeroing.  This is an uncommon
 *	 situation, so we won't spend too much effort doing the world's
 *	 fastest job.
 *
 *	 Like the regular write code, we find the extent that we're to
 *	 start in.  We RdBuf the first, incomplete cache block and zero
 *	 that.  We then GetInBuf the remaining cache blocks, zeroing
 *	 them, until we're done.  This will hit the cache pretty heavily
 *	 if we have a really large range, but it works optimally for
 *	 smaller ranges since the stuff will lazywrite out, and perhaps
 *	 the user will seek back and rewrite the stuff before it goes out
 *	 anyway.
 *
 *
 *	 NOTE: the above comments are not accurate
 *
 *
 *	 NOTE that OFTs are normally locked just when they are being
 *	 extended, but we lock for this operation to prevent two guys
 *	 from doing parallel zeroings, and to prevent the case where
 *	 one guy is zeroing and another guy is writing into that area.
 *	 If we have to wait to get our lock, someone else may have changed
 *	 the file allocation or the OFN_VLEN value, so we return without
 *	 doing anything.  Our caller must call us back if we're still needed.
 *
 *	 NOTE - ZeroBytes may return without having done the work, if
 *		 the file was in some transition state.  The caller must
 *		 check to see if the OFN_VLEN value is now appropriate and,
 *		 if not, recall ZeroBytes.
 *
 *	 ENTRY	 cbZero	 = bytes to zero (non-zero)
 *		 offSeek = first logical byte to zero
 *		 poft	 = OFT locked or held
 *
 *	 EXIT	 OFT in its called (locked or held) state
 *		 poft->fst.ulVLen updated if the bytes were successfully zeroed
 *
 *		returns FALSE if hard error occured,
 *			TRUE for anything else including having to yield
 *
 *	SWAP	This routine must not be swapped-out, or call any swapped code.
 *
 * BUGBUG - should we add a write through parameter?
 *
 *	HISTORY
 *	    08-Mar-1990 by JR (John Rogers, JohnRo@Microsoft)
 *		- PTR B788488: avoid non-async I/O swap deadlock.
 *	    23-Feb-1990 by JR (John Rogers, JohnRo@Microsoft)
 *		- PTR B788262: fix FS_DoPageIO probs (e.g. nested swap).
 */

BOOL ZeroBytes(ULONG cbZero, ULONG offSeek, POFT poft, PFRAME pfrm)
{
    PBUFNODE pbuf;
    PBYTE pbZeros;
    BOOL fWeLocked;
    BOOL fRet;
    register UINT cb;
    USHORT fsType = 0;          /* file type flags */
    USHORT usRet;

    /* Lock the file if it's not locked already. */
    if ((poft->bFlag & OTF_LCK) == 0) {

	/* remember that we locked it */
	fWeLocked = TRUE;

	if( !HoldToLock( poft,pfrm ) ) {
	    /* we yielded, return early */
	    if( poft->cWaits != 0 ) {
		WakeupOFT( poft );
	    }
	    HoldOFT( poft,pfrm );
	    IncrHoldsOFT( poft );
	    return TRUE;
	}

    } else {
	/* it was always locked */
	fWeLocked = FALSE;
    }

    /* get us a working buffer to hold zeros */
    pbZeros = GetBuf(SPB * SECSIZE, &pbuf, pfrm);

    /* fill it with zeros */
    BYTE_FILL( pbZeros, CH_SECURE_FILL, SPB * SECSIZE );

    /*
     * Find out where and how long the extent is so that we can
     * optimize our calls to SDW.  We want to write one cache block
     * at a time, cache aligned.
     *
     * offSeek = logical first byte to zero
     */
    do{
	LSN lsn;
	ULONG csecContig;

	if( (lsn = CPS(offSeek, poft, &csecContig, pfrm)) == LSN_BOGUS ) {
	    /* hard error */
	    fRet = FALSE;
	    goto zbExit;
	}

	/* compute how many sectors until we're aligned */
	if( (ULONG)(cb = SPB - ((UINT)lsn % SPB)) > csecContig ) {
	    cb = (UINT)csecContig;
	}

	if( (ULONG)(cb = (cb * SECSIZE) - ((UINT)offSeek % SECSIZE))
								> cbZero ) {
	    cb = (UINT)cbZero;
	}

	/*
	 * OK, we've computed a byte count which when written at
	 * Z_LEN will either finish us off or leave us aligned, in the
	 * target file, at a physical sector which is a multiple of SPB
	 * (unless we're badly fragmented)
	 *
	 * cbZero = transfer count.
	 */

	/* SDW() calls ProbeAddress to verify pbZeros validity.
	 * Since we know this is a valid buffer we reset fRecheckAddr
	 * in order to avoid this check.
	 *
	 */
	pfrm->fRecheckAddr = FALSE;
#ifdef DEBUG
	pfrm->fRecheckAddr = TRUE;
	/* Debug version of ProbeAddress does extra checking !!! */
#endif

   if (offSeek + cb > poft->fst.ulVLen)
     fsType |= WT_EXT;     /* tell SDW we are extending the file */

	usRet = SDW(fsType,cb,offSeek,pbZeros,poft,pfrm);

	offSeek += cb;

    } while( (cbZero -= cb) != 0 );

    /* We used a cache buffer to write zeros.  Our caller probably uses
     * some other buffer for the actual writes.  We set fRecheckAddr
     * in order to force ProbeAddress to check for that buffer.
     */
    pfrm->fRecheckAddr = TRUE;

    poft->fst.ulVLen = offSeek; /* tells caller that we did our job */

    /*
     * Release our work buffer, clean up, and return
     *
     * If fWeLocked is non-zero, change the lock into a hold.
     *
     * poft = OFT address
     */
    fRet = TRUE;

zbExit:
    if( fWeLocked ) {
	/* Hold the OFT and return */
	UnLockOFT( poft );
	IncrHoldsOFT( poft );
    }

    RelBuf(pbZeros, pbuf, pfrm);

    return fRet;
}
