/*static char *SCCSID = "@(#)tfa.c      6.2 92/01/31";*/
#define ASWAP0 ASWAP0
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.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 "frame.h"
#include "log.h"                                                 // @76193

#ifdef DEBUG_HOLDS
    #include "lw.h"
    #include "fsdata.h"
#endif

#include "pr_ALLOC.h"
#include "pr_BITMP.h"
#include "pr_CACHE.h"

// @76193  #ifdef DEBUG
#include "pr_DEBUG.h"
// @76193  #endif


/***    TFA - Truncate File Allocation
 *
 *      TFA truncates a file's allocation to some non-zero number of
 *      sectors.  (Use DelAlloc to truncate to zero)
 *
 *      TFA isn't quite symmetric with EFA - TFA doesn't update the OFT
 *      or anything else - it just deals with the allocation list.
 *
 *      ENTRY   palb = pointer to ALBLK
 *              csec = new length in sectors
 *              fWT = TRUE iff write-through specified
 *      EXIT    bitmask of:
 *                  TFA_RET_BIT_ERROR   error occured
 *                      Caller must mark file corrupt
 *                  TFA_RET_BIT_BLKDEL  entire tree is empty
 *              Yields CPU
 *  HISTORY:
 *  @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 */

USHORT
TFA (PALBLK palb,
     ULONG  csec,
     BOOL   fWT,
     PFRAME pfrm)
{
    USHORT i;
    USHORT usRet;

    if ((i = palb->cUsed) == 0)
        return TFA_RET_BIT_BLKDEL;

    if ((palb->bFlag & ABF_NODE) == 0) {
        /*  This is a leaf block.  Find extent which contains first
         *  sector to drop
         */
        ALLEAF far * pall;
        ULONG crel;

        pall = PALLFROMPALB (palb);

        /*  Gordon's code does not handle sparse leaves. We will.
         *  Find partially freeable extent.
         */
        do {
            /*  (pall) = pointer to a leaf
             *  (i) = number of leaves including pall to go
             */
            if (csec <= pall->lsnLog)
                goto tfa_full_leaf_free;
            if (csec < pall->lsnLog + pall->csecRun)
                goto tfa_part_leaf_free;

            /*  pall is totally below free point.  Advance to next
             */
            pall++;
        } while (--i);

        /*  No eligible leaves found.  All done here.
         */
        return FALSE;

        /*  We have a partial leaf to free.
         *  (pall) = pointer to leaf
         *  (i) = number of leaves including pall to go
         */
tfa_part_leaf_free:
        csec -= pall->lsnLog;           /*  (csec) = new length of run */
        crel = pall->csecRun - csec;    /*  (crel) = # sec to free */
        pall->csecRun = csec;
                                                         //#85759
        if (!SetBit (pall->lsnPhys + csec, crel,pfrm))
          return TFA_RET_BIT_ERROR;

        pall++;                         /*  advance to next run               */
        i--;                            /*  one less run to go                */

        /*  We wipe a contiguous block of extents
         *  (pall) = extent to wipe
         *  (i) = number of extents to wipe, may be zero!
         */
tfa_full_leaf_free:
        palb->oFree = OFFSETOF (pall) - OFFSETOF (palb);
        palb->cUsed -= (BYTE) i;
        palb->cFree += (BYTE) i;

        while (i--) {
                                                            //#85759
         if (!SetBit (pall->lsnPhys, pall->csecRun,pfrm))
           return TFA_RET_BIT_ERROR;
         pall++;
   }

        return palb->cUsed == 0 ? TFA_RET_BIT_BLKDEL : 0;
    }
    else {
        /*  We have a multilevel allocation tree.  At the top level,
         *  identify any branches beyond the truncate point and
         *  release those branches.
         *
         *  Then for the branch containing the first block to be
         *  released, read it's top block and again prune the
         *  subtrees beyond the truncate point and recurse on the
         *  branch containing the truncate point.
         *
         *  When we finally reach a leaf node, do as we did above and
         *  trim it out.
         *
         *  Again, original code didn't handle sparse allocation but we do.
         *
         */
        ALNODE far *paln;

        /*  Find node that contains wipe point
         */
        paln = PALNFROMPALB (palb);

        do {
            if (csec < paln->lsnLog)
                goto tfa_node_free;
            paln++;
        } while (--i);

        /*  No eligible nodes found.
         */
        return FALSE;

        /*  We've found the node that contains the wipe point.
         *  (paln) = node that contains the wipe point
         *  (i) = number of nodes to go including paln
         */
tfa_node_free:
        {
            PBUFNODE pbuf;
            ALSEC far *pals;

            if ((pals = RdBuf(paln->lsnPhys,BP_KEEP,&pbuf,pfrm)) == NULL) {
                /* hard error reading the allocation tree */
#ifdef DEBUG
                dprintf( __FILE__"(%d): hard error code may not be tested\n", __LINE__ );
                INT3;
#endif
                return TFA_RET_BIT_ERROR;
            }

//#114986
#ifndef MINIFSD
            if (pbuf->fFlag & BFL_LZREAD) {
              if (LazyRead (pbuf, pfrm))
                return TFA_RET_BIT_ERROR;
            }
#endif

            IncrHoldsBUF (pbuf);  // @76193

            if( (usRet = TFA(&(pals->alb), csec, fWT, pfrm)) & TFA_RET_BIT_ERROR ) {
                /* Hard error from TFA */
#ifdef DEBUG
                dprintf( __FILE__"(%d): hard error code may not be tested\n", __LINE__ );
                INT3;
#endif
                SetDirt((PBYTE)pals, pbuf, fWT, pfrm);
                DecrHoldsBUF (pbuf);  // @76193
                return TFA_RET_BIT_ERROR;
            }

            if (usRet & TFA_RET_BIT_BLKDEL) {
                /*  Fully deleted node pointed to by paln,
                 *  Wipe sector signature release it.
                 *  The current paln will become the first
                 *  free paln.
                 */
                pals->sig = 0;
            }
            else {
                /*
                 *  The current paln is now the last, so  set its
                 *  lsnLog to 0xFFFFFFFF.  Then move the current paln
                 *  pointer (which will be used to calculate oFree)
                 *  to the next node entry (which will become the first
                 *  free) and decrement i
                 */
                paln->lsnLog = 0xFFFFFFFF;
                paln++;
                i--;
            }

            SetDirt(pals, pbuf, fWT, pfrm);
            DecrHoldsBUF (pbuf);  // @76193
        }

        /*  Call FreeAS to release each following node
         *  (paln) = pointer to first node to begin freeing
         *  (i) = number of nodes to be deleted  (may be zero)
         */
        palb->oFree = OFFSETOF (paln) - OFFSETOF (palb);
        palb->cUsed -= (BYTE) i;
        palb->cFree += (BYTE) i;

        while (i--) {
            if (!FreeAS (paln->lsnPhys,pfrm)) {
                /* Hard error releasing data */
#ifdef DEBUG
                dprintf( __FILE__"(%d): hard error code may not be tested\n", __LINE__ );
                INT3;
#endif
                return TFA_RET_BIT_ERROR;
            }
            paln++;
        }

        if (palb->cUsed == 0) {
            /*  Node was completely freed.  Promote to leaf. */
            palb->bFlag &= ~ABF_NODE;
            palb->cFree = palb->cFree * (BYTE)LEAFPERFNODE / (BYTE)NODEPERFNODE;
            return TFA_RET_BIT_BLKDEL;
        }
        else {
            /* Tree is pruned.  Make the last lsnLog (AN_LOF) value -1; */
            paln = PALNFROMPALB (palb);
            paln += palb->cUsed - 1;
            paln->lsnLog = 0xFFFFFFFF;
            return FALSE;
        }
    }
}



/**     FreeAS - Free Allocation Sector
 *
 *      FreeAS is called to release the contents of an allocation
 *      sector.
 *
 *      ENTRY   (lsn) = sector # of ALSEC
 *      EXIT    Yields CPU
 *              Returns TRUE if OK;
 *              Returns FALSE if hard error - caller should mark file corrupt
 */

BOOL
FreeAS (LSN lsn, PFRAME pfrm)
{
    PBUFNODE pbuf;
    ALSEC far * pals;
    USHORT i;

    if ((pals = RdBuf (lsn, BP_NOOPINION, &pbuf, pfrm)) == NULL) {
        return (FALSE);
    }

//#114986
#ifndef MINIFSD
    if (pbuf->fFlag & BFL_LZREAD) {
      if (LazyRead (pbuf, pfrm))
        return (FALSE);
    }
#endif

    i = pals->alb.cUsed;

    IncrHoldsBUF (pbuf);  // @76193

    if (pals->alb.bFlag & ABF_NODE) {
        ALNODE far * paln = pals->a.aaln;

        while (i--) {
            if (!FreeAS (paln->lsnPhys,pfrm)) {
                /* hard error */
#ifdef DEBUG
                dprintf( "FreeAS(%d): hard error code may not be tested\n", __LINE__ );
                INT3;
#endif
                DecrHoldsBUF (pbuf);  // @76193
                return (FALSE);
            }
            paln++;
        }
    }
    else {
        ALLEAF far * pall = pals->a.aall;

        while (i--) {
         if (!SetBit (pall->lsnPhys, pall->csecRun,pfrm))
           return TFA_RET_BIT_ERROR;
         pall++;
        }
    }

    pals->sig = 0;
    DecrHoldsBUF (pbuf);  // @76193
    SetDirt(pals, pbuf, FALSE, pfrm);
                                                         //#85759
    if (!SetBit (lsn, 1L,pfrm))
      return TFA_RET_BIT_ERROR;

    return (TRUE);
}
