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

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

#include "pr_CACHE.h"
#include "pr_FSSUB.h"
#include "pr_HEAP.h"
#include "pr_MISC.h"
#include "pr_debug.h"

/*
 **     GOFT - Get Open File Table Entry
 *
 *      GOFT locates an existing OFT entry for a file.  If none exists, it
 *      creates one.
 *
 *      Note - the caller must clear the OFT_BANDP field
 *
 *      NOTE: when we're debugging and we've created an OFT for a file we've
 *            just created, the OFT_ALEN might not match the extent tree.
 *            We set the OFS_SAC bit to supress debug checks which would
 *            trap on this; our caller fixes this immediately upon our return.
 *
 *      If the OFT is marked Corrupt it means that due to some hard error
 *      this file has become corrupted so that we can't try to read it.
 *
 *      ENTRY   pde = address of directory entry
 *              pdb = address of start of directory block
 *              psbd = directory's SUBDIR address
 *
 *      EXIT    Returns address of OFT, OFT held.
 *              Returns NULL if hard error (OFT marked corrupt)
 *              or if could't get heap space.
 *
 *      HISTORY:
 *  @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 *          19-Jul-1988 18:30:51 by jonpa translated it from GordonL's 386 code
 */

POFT
GOFT (pde, pdb, psbd, pfrm)
PDIRENT pde;            /* pointer to directory entry */
PDIRBLK pdb;            /* pointer to dir block */
PSUBDIR psbd;           /* pointer to subdir struct */
PFRAME  pfrm;
{
    POFT        poft;
    PBUFNODE pbuf = BH (pdb);

    /*  Check if any OFTs exist,
     *  if so then search for the one we need
     */

    /* Get fnode lsn */

    {
        LSN lsnFNode;

        if ((poft = OFTFromFNode ((lsnFNode =
                            pde->lsnFNode | pfrm->pvol->ulVolBits))) != NULL) {

            /* goft100: */

            /*  Lock it so no one evaporates it
             */
            poft->cusClientFHTs++;

            IncrHoldsBUF(pbuf);  // @76193

            if ((poft->bFlag & (OTF_LCK + OTF_PLK)) != 0)
                   HoldOFT (poft,pfrm);

            DecrHoldsBUF(pbuf);  // @76193

            IncrHoldsOFT (poft);

#ifdef DEBUG
            /* this check is to prevent later "RDE: dirent not found" errors */
            if (poft->cchName != pde->cchName)
                INTERR (__FILE__ ": GOFT found poft, name different length");
            else
                if (memcmp (&poft->cchName, &pde->cchName, poft->cchName) != 0)
                    INTERR (__FILE__ ": GOFT found poft, different name");

#endif


            if (poft->bCorrupt) {                              //#86865
              SETHARDERR (ERROR_READ_FAULT);
              UnHoldOFT (poft);
              poft->cusClientFHTs--;
              return NULL;
            }

            return poft;
        }

        /*
         * Not found, so create one.
         * In order to prevent race conditions, we must put the thing on the
         * chain, locked, before we do any yields.
         */

        {
            USHORT          cb;

            /* alloc space for the OFT and name and copy name over */
            cb = pde->cchName + 1;

            /* Allocate the memory needed */
            IncrHoldsBUF(pbuf);  // @76193
            poft = (POFT)GHS(sizeof(OFT) + cb - 1,pfrm);
            DecrHoldsBUF(pbuf);  // @76193

            if (poft == NULL) {
#ifdef DEBUG
                dprintf(__FILE__"GHS returned NULL\n");
#endif
                return(NULL) ;
            }
            memcpy (&poft->cchName, (PBYTE) &pde->cchName, cb);
        }

#ifdef BUFZAP
        DoZap ();
#endif

        /* Initialize all the fields */
        poft->scpFNode.lsn = lsnFNode;  /* store Vsector (lsn of fnode+volid) */

    }   /* end scope of lsnFnode */

    poft->pVolume = pfrm->pvol;
    poft->flDMask = pfrm->flDMask;
    poft->bCorrupt = 0;
    poft->cWriDir = 0;       //#132506 write-direct in progress count
    poft->scpFNode.pbufHint = (PBUFNODE)NULL;
    poft->psbdDir = psbd;
    poft->psbdDir->cOpens++;
    poft->culFreeDB = psbd->culFreeDB;
    poft->lsnLast = 0;
    poft->pbtLastAlloc = 0;

#ifdef PERFVIEW                                                        //aa04398
    poft->cWrite = 0;   // clear number of writes for ctfwcurcnt       //aa04398
#endif                  // perfview counter                            //aa04398
    {
        POFT poftScratch;

        /* insert it into the OFT list */
        poft->dcpOFTList.pFwd = poftScratch = oftHead.pFwd;
        oftHead.pFwd = poft;
        poft->dcpOFTList.pBak = (POFT) &oftHead;
        poftScratch->dcpOFTList.pBak = poft;
        cOFT++;
    }

    /* clear Hold count, Flags, & Wait count */
    poft->cusHolds = 0;
    ClrFlagsOFT(poft, ~0 );     /* clear all flag bits */
    poft->bMFlag = 0;
    poft->bSFlag = 0;
    poft->cWaits = 0;
    poft->pWaitChain = 0;

    /*
     * Compute the directry entry sector number
     * from the address of the start of the dir block.
     */
    poft->scpDirBlk.lsn = pdb->lsnThisDir;
    poft->scpDirBlk.pbufHint = pbuf;

    poft->culChange = pdb->culChange;

    poft->offusDirEnt = DIRENT_OFFSET(pdb,pde);

    /* we've locked the OFT */
    SetFlagsOFT(poft, OTF_LCK);

    /* set FHT count to 1 */
    poft->cusClientFHTs = 1;


    /*
     * Transfer stuff from the directory and Fnode to the OFT
     */

    /* set true, alloc, & last valid data file size */
    poft->ulFActualLen = pde->cchFSize;
    poft->ulFAllocLen =
        (poft->ulFActualLen + SECSIZE - 1) & -(ULONG)SECSIZE;

    /* remember the code page (in case RDE has to upcase name) */
    poft->iCPVol = pde->bCodePage;

#ifdef DEBUG
    /* suppress debug check */
    poft->bSFlag |= OFS_SAC;
#endif

    {
        PFNODE      pfn;
        PBUFNODE    pbufScratch;

        IncrHoldsBUF(pbuf);  // @76193

        /* locate and copy fnode sector */
        pfn = LSD(&(poft->scpFNode), BP_KEEP, &pbufScratch, pfrm);

        DecrHoldsBUF(pbuf);  // @76193

        if (pfn == NULL) {

            /* hard error reading FNODE.  mark it corrupt */
            poft->bCorrupt = TRUE;
            UnLockOFT (poft);
            return (NULL);                  //#86865
        }

        ASSERT_FN (pfn);

                                              //#86865
   if (pfn->sig != FNSIGVAL) {

   do {
        pbufScratch->bBadMask = SPBBITS;
        pbufScratch->lsnSec = LSN_BOGUS;


        /*
         * If it's on the LRU list already remove it and place in the
         * LRU position.
         */

        if (pbufScratch->dcbpLru.pbufFwd != pbufScratch) {
          /*  Remove from LRU chain
           */
          DCREM (pbufScratch, dcbpLru);

          /*  Make buffer LRU (first to recycle)
           */
          DCADDB (pbufScratch, dcbpLru, bufLRUHead);
        }

        IncrHoldsBUF(pbuf);

        /* locate and copy fnode sector */
        pfn = LSD(&(poft->scpFNode), BP_KEEP, &pbufScratch, pfrm);

        DecrHoldsBUF(pbuf);

        if (pfn == NULL) {

            /* hard error reading FNODE.  mark it corrupt */
            poft->bCorrupt = TRUE;
            UnLockOFT (poft);
            return (NULL);                  //#86865
        }

        ASSERT_FN (pfn);

   } while (pfn->sig != FNSIGVAL);

   }

        poft->fst = pfn->fst;

    }   /* end scope of pfn, pbufScratch */

    /* Convert lock into a hold and wake up all candidates */
    ASSERT_OFT (poft);
    UnLockOFT (poft);
    IncrHoldsOFT (poft);

#ifdef DEBUG
    /* reinstate debug check */
    poft->bSFlag &= ~OFS_SAC;
#endif

    /* set up exit params and return */

    return poft;
}


/**     OFTFromFNode - find an OFT given an FNODE LSN
 *
 *      OFTFromFNode is used to determine if a particular file is
 *      already open, given the FNODE LSN.  We do this as the data in the
 *      OFT is more up-to-date than that in the DIRENT/FNODE
 *
 *      ENTRY   (lsn) = lsn of FNODE
 *      EXIT    returns NULL if no OFT found, pointer to matching OFT
 *              otherwise
 */
POFT
OFTFromFNode (LSN lsn)
{
    POFT poft;

    poft = (POFT) oftHead.pFwd;

    while (TRUE) {

        /*  If we've returned to the head of the list
         */
        if (poft == (POFT) & oftHead)
            /*  Return that we didn't find it.
             */
            return NULL;

        /*
         *  If we've found an OFT with the right FNODE, and it's
         *  not lined up for a pending solo lock, take it.
         *  (If it does have a pending solo, somebody is trying
         *  to shut it down, so we'll pretend it isn't there.)
         */
        if ((poft->scpFNode.lsn == lsn) && !(poft->bFlag & OTF_PSO))

            return poft;

        /*  No luck, go to next
         */

        poft = poft->dcpOFTList.pFwd;
    }
}
