/*static char *SCCSID = "@(#)setbit.c   6.4 92/01/31";*/
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>

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

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

extern BOOL fTrapLowWrite;  /* @96054 Trap on any write to LOW sectors  */

/*
 ***    SetBit - Set bits into a bit map
 *
 *      BUGBUG - use less registers?  Make callers faster?
 *
 *      SetBit is called to set bits in the filesys bit map to
 *      show that the sectors are free.
 *
 *      ENTRY   lsnFirstToFree = number of first bit/sector to free
 *              culToFree = # of bits to free (i.e., set)
 *
 *      EXIT
 *                FALSE if error
 *              Yields CPU
 *
 *      HISTORY
 *          23-Jun-1989 23:04:27 by Jon Parati [jonpa]
 *                                  Added support for runs > BANDSIZE
 *          29-Sep-1988 18:30:14 by Jon Parati [jonpa]  Added Banding
 */

BOOL
SetBit (lsnFirstToFree, culToFree, pfrm)
LSN lsnFirstToFree;             /* first sector to free */
ULONG culToFree;                /* number of bits to free */
PFRAME pfrm;
{
    PBYTE pbWork;
    USHORT ui;
    PBUFNODE pbuf;
    PBANDTAB pbtb;

#ifdef DEBUG
    if (lsnFirstToFree >= pfrm->pvol->cSectsOnVol )
        INTERR( "SetBit: starting sector not on volume" );

    if (lsnFirstToFree + culToFree > pfrm->pvol->cSectsOnVol )
        INTERR( "SetBit: ending sector not on volume" );
#endif

                                                          //#101488 start
    if ( (lsnFirstToFree >= pfrm->pvol->cSectsOnVol) ||
         (lsnFirstToFree + culToFree > pfrm->pvol->cSectsOnVol) ||
         (lsnFirstToFree <= SEC_SPAREB) ) {
      SETHARDERR (MSG_HPFS_FNODE_ERROR_WARN);
      if ( fTrapLowWrite )                 // @96054 are we trapping low writes?
      {                                    // @96054 yes - IPE!
         INTERR("SetBit: tried to set low sector bitmap.");
      }
      return (FALSE);
    }
                                                          //#101488 end

    /*
     * While there are bits to be freed, carve the run into
     * band size pieces and free them 1 band at a time
     */
    while( culToFree ) {
        UINT csecToFree;        /* # of sectors to free in this band*/
        UINT olsnInBand;        /* offset into band of starting lsn */

        /* get first bit in this band */
        olsnInBand = (UINT)lsnFirstToFree % ((UINT) BANDSIZE);

        /* Get the correct band */
        pbtb = pfrm->pvol->apbndList [USSHIFTRUL (lsnFirstToFree, BANDSHIFT)];

        /* clear only those bits in this band */
        csecToFree = (pbtb->cbMapLen * 8) - olsnInBand;
        if( (ULONG)csecToFree > culToFree ) {
            /* run ends in this bitmap */
            csecToFree = (UINT)culToFree;
        }

        /* Indicate more free sectors */
        pfrm->pvol->csecSpace += csecToFree;

        pbtb->csecFree += csecToFree;

        /* check hi water mark and reset if new is lower */
        if ((ui = olsnInBand / 8) < pbtb->ibHighWaterOffset ) {
#ifdef DEBUG
            if( (USHORT)(lsnFirstToFree - pbtb->lsnBase) != olsnInBand ) {
                dprintf( "setbit: quick hack does not compute.. change back\n" );
                Int3();
            }
#endif
            pbtb->ibHighWaterOffset = ui;
        }

        /*
         * Read in the bitmap and mark it dirty
         */
        /* DEBUG - This is a good place for statistical stuff */
                                                   //#85759
        if ((pbWork = LSDBM(&pbtb->scpMap, BP_KEEP, &pbuf, pfrm)) == NULL) {
        /* Reset number of free sectors */
          pfrm->pvol->csecSpace -= csecToFree;
          pbtb->csecFree -= csecToFree;
     return (FALSE);
   }

        pbWork = &pbWork[ olsnInBand / 8 ];

        /* if we are setting less than 1 byte then, do it now */
        if( (ui = olsnInBand % 8) + csecToFree < 9 ) {

            *pbWork |= (~abFillMask[ui] & abFillMask[ui+ csecToFree]);

        } else {

            /* Set bits in the first byte */
            *pbWork |= ~abFillMask[ui];

            /* Set all the middle bytes */
            memset( ++pbWork, 0xFF, (ui = (csecToFree - (8 - ui))/8) );
            pbWork += ui;

            /* Set any remaining bits in the last byte */
            ui = (olsnInBand + csecToFree) % 8;
            if( ui != 0 ) {
                *pbWork |= abFillMask[ ui ];
            }
        }

        pbuf->bDirtMask = SPBBITS;

        AddLW(pbuf,pfrm);

        /*
         * Update the total with the sectors we just freed
         * and point to the start of the next band
         */
        culToFree -= csecToFree;
        lsnFirstToFree += csecToFree;

    }

    return (TRUE);
}
