/*static char *SCCSID = "@(#)write.c    6.18 92/02/13";*/
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <pvwxport.h>

#include "api.h"
#include "filemode.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 "fnode.h"
#include "pb_ifs.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "assert.h"
#include "pbmacros.h"
#include "log.h"
#include "pvw.h"

#include "pr_ALLOC.h"
#include "pr_CACHE.h"
#include "pr_DIR.h"
#include "pr_FCN.h"
#include "pr_FSSUB.h"
#include "pr_MISC.h"
#include "pr_WRITE.h"
#include "pr_debug.h"

#define LARGE_SECTOR_IO 126
#define LARGE_IO 126*SECSIZE

/*
 ***    WRITE - Write Data To File
 *
 *      If this write will extend the size of the file, we'll allocate
 *      the necessary additional sectors and extend the file before
 *      we do the write, so that all write operations are overwrites.
 *
 *      When we extend the file, we may extend it beyond it's official
 *      length.  This gets trimmed off at close time.  It's not normally
 *      updated to the FNODE unless write through is set; in that case
 *      if we crash before the close the clean program will prune the
 *      extra allocated sectors.
 *
 *      Write holds the OFT.  This is sufficient if the file is being
 *      overwritten;  if the file is to be extended, EFA converts the
 *      hold into a lock.
 *
 *      Entry   pfht = handle (FHT address)
 *              cchWrite = number of bytes to write
 *              ulPosition = seek position
 *              pbSource = pointer to source for write
 *              fsType = Write Type flags (see misc.h WT_????)
 *
 *      EXIT    Returns 0 if OK
 *              Returns error code if error
 *
 *      BUGBUG - we don't lock this baby
 *
 *      HISTORY
 *          19-Sep-89 by JR (John Rogers, JohnRo@Microsoft)
 *              - Avoid warnings from Microsoft C 5.2 (on slash-slash comments).
 *          15-May-1989 by John Rogers [JohnRo@Microsoft]
 *              - Changed names of rounding macros in <pbmacros.h>.
 *              - PTR B783576: prevent overflow.
 *              - Corrected SCCSID (said write.h instead of write.c).
 *
 */


USHORT
Write (pfht, cchWrite, ulPosition, pbSource, fsType, pfrm)
PFHT pfht;              /* file handle */
USHORT cchWrite;        /* number of bytes to write */
ULONG ulPosition;       /* seek postion */
PBYTE pbSource;         /* source address */
USHORT fsType;          /* file type flags */
PFRAME pfrm;
{
  ULONG ulNewPos;         /* Offset of last byte to be */
                          /* written + 1               */
  ULONG ulNewLen;         /* New Length             */
  USHORT usRet;
  POFT poft = pfht->poft;

        ASSERT_OFT (poft);


        if ((poft->bSFlag & (OFS_SECTORIO | OFS_ONLYSECIO)) == OFS_ONLYSECIO) {
           /* This handle is valid only with sector I/O, and the application */
           /* has not requested it. Disallow the operation because this means */
           /* the application does not support large volumes */

           return (ERROR_ACCESS_DENIED);
        }

        pfrm->pvol = poft->pVolume;     /* set pvol for lower level IO */
        pfrm->flDMask = poft->flDMask;  /* set DMask to this file */

        pfht->bFlush = HIBYTE (OPEN_WRITE_THROUGH & fsType);

        /* Check if the OFT is marked corrupted, and punt if it is. */
        if (poft->bCorrupt) {
#ifdef DEBUG
            dprintf ("Write: oft is marked corrupt\n");
            Int3 ();
#endif
            return GetHardErr(ERROR_READ_FAULT);
        }

        /*  If the OFT is locked, wait for it.  Then, put
         *  our hold on it.
         */
        if ((poft->bFlag & (OTF_LCK + OTF_PLK)) != 0)
            HoldOFT (poft,pfrm);

        IncrHoldsOFT (poft);

        /* See if we're overwriting or if we're extending.
         * If the file's seek position is >= to the
         * length of the file, then we are extending
         * the file; therefore turn on this flag.
         */

        ulNewPos = cchWrite + ulPosition ; /* seek position after write */

	     if (ulNewPos > poft -> ulFActualLen) {

          if (poft->bSFlag & OFS_DASD) {

              /*
               *  Trying to do a DASD write past
               *  the end of the disk.
               */
              DecrHoldsOFT ( poft );

              if ( poft->bFlag & OTF_PND ) {
                  ChkHoldOFT ( poft );

              }
              return( ERROR_WRITE_FAULT );
          }

          fsType |= WT_EXT;
        }

        /*
         * Remember if we are doing Sector I/O on large volume DASD
         * writes
         */
        /* See if we are above the cache threshhold (i.e. if we
         * have a large enough request to consider skipping the
         * cache.
         *
         * If the request is larger than the number of free blocks in
         * cache then write it direct.  Add 2 to account for
         * partial cache buffer at head and tail
         */

        if (poft->bSFlag & OFS_SECTORIO) {

           fsType |= WT_SECTORIO;

#ifdef MINIFSD
           fsType |= WT_DIR;
#endif

#ifndef MINIFSD
           if (((ULONG)cchWrite >= LWData.cbWriteCache / SECSIZE) ||
               (!fLWDaemon)  || (pfht->usMode & OPEN_NO_CACHE) ||
               ((cchWrite / SPB) > (cBufs / 4))  ||
               ((cchWrite > (USHORT)LARGE_SECTOR_IO) &&
                (ulPosition > (ULONG)LARGE_SECTOR_IO)) ||
               ((cchWrite / SPB + 2) > (cBufs - cbufDirty)))
               /*  Write direct if possible  */
               fsType |= WT_DIR;
#endif

        } else {

#ifndef MINIFSD
           if (((ULONG) cchWrite >= LWData.cbWriteCache) ||
               (!fLWDaemon)  || (pfht->usMode & OPEN_NO_CACHE) ||
               ((cchWrite / (SPB * SECSIZE)) > (cBufs / 4))  ||
               ((cchWrite / (SPB * SECSIZE) + 2) > (cBufs - cbufDirty)) ||
	            ((poft -> ulFActualLen > 0) &&
                (cchWrite > (USHORT)(LARGE_IO)) &&
                (ulPosition > (ULONG)(0x0001ffff))))
               /*  Write direct if necessary  */

               fsType |= WT_DIR;
#endif
        }

/* wri6: */
        /*
         * Make sure the new file size does not grow beyond the 2 gigabyte
         * limit placed on us by the DosSetFilePtr API.
         */

#ifndef MINIFSD
        if (ulNewPos >= CCH_MAXFILESIZE) {
           DecrHoldsOFT(poft);
           return (GetHardErr(ERROR_DISK_FULL));
        }

        /*
         * If the amount needed + position is greater than the file's
         * allocated length then we need to grow the file.  We are extending!
         */

        if (ulNewPos > poft -> ulFAllocLen) {

            /*
             * Extend to the new allocation which includes a slush
             * amount.
             */
//#127671
           if (cchWrite >= (SPB * SECSIZE)) {

              ulNewLen = ROUND_TO_POW_OF_2 (ulNewPos + (4 * cchWrite) +
                                          (SPB * SECSIZE), SPB * SECSIZE);
           } else {

               ulNewLen = ROUND_TO_POW_OF_2 (ulNewPos + GROWDELT * SECSIZE,
                                          SPB * SECSIZE);
           }

           if (!EFA (ulNewLen,
                     poft,
                     (BOOL)(fsType & OPEN_WRITE_THROUGH),
                     pfrm)) {

               /*
                * We failed to get the extra space, so
                * try the exact amount we need
                *
                * Convert lock back to hold
                */
                UnLockOFT ( poft );
                IncrHoldsOFT (poft);

                if (!EFA (ROUND_TO_SECSIZE (ulNewPos),
                          poft,
                          (BOOL)(fsType & OPEN_WRITE_THROUGH),
                          pfrm)) {

                    UnLockOFT ( poft );
#ifdef DEBUG
                    VBS ();
#endif
                    return GetHardErr(ERROR_DISK_FULL);
                }
           }

#ifdef DEBUG
            /*  Check that the OFT is locked  */
            if ( (poft->bFlag & OTF_LCK) == 0 ) {
              INTERR("Write.c:  The OFT entry is not locked.");
            }
#endif

        }

/* wri20: */

        while ( ulPosition > poft->fst.ulVLen ) {
            /*
             * We tried to seek over uninitialized data.  We need to zero
             * those bytes up to the point where we'll start to write.
             */
            if (!ZeroBytes(ulPosition - poft->fst.ulVLen,
                           poft->fst.ulVLen,poft,pfrm))

                goto wri70; /* hard error */

            /* ZeroBytes will return without doing anything if has to
             * wait for the lock so we need to loop and check
             * if it succeeded
             */
        }
#endif

        if ( cchWrite ) {

            pfht->offSeek = ulNewPos;

#ifdef PERFVIEW
            /*
             * File is not exclusively read, so strip the flags in bHint
             */

       //   pfht->bHint ^= (FHH_READ & FHH_READSEQ);                   //aa04398

            pfht->bHint &= ~(FHH_READ | FHH_READSEQ);                  //aa04398
            /* Increment the counter of files being written if
             * necessary
             */

            if (!(pfht->bHint & FHH_WRITE)) {
                pfht->bHint |= FHH_WRITE;
                if (!(poft->cWrite))
      /*25106       PVWINC(FW, CurCnt);  */
                    PVWADD(FW, CurCnt,1);
                poft->cWrite++;
            }

#endif

            usRet = SDW (fsType, cchWrite, ulPosition,
                         pbSource, poft, pfrm);


            /*  If the new position is beyond the old length,
             *  update these numbers.
             */

#ifndef MINIFSD
            if (ulNewPos > poft->fst.ulVLen) {
              poft->fst.ulVLen = ulNewPos;
            }

            if (ulNewPos > poft->ulFActualLen) {
              poft->ulFActualLen = ulNewPos;
            }

            /* Set fWritten (so close will update date/time) */
            poft->bMFlag |= OFM_Written;
#endif

/* wri25: */
        } else {
            /* can't get an error if we did't need to read any bytes! */
            usRet = NO_ERROR;
        }

        /*
         * Unhold or unlock the OFT
         */
        if ( poft->bFlag & OTF_LCK ) {

            /*  The OFT is locked--we must have locked it, since we
             *  have been freely using it.  Release the lock and return.
             *
             *  NOTE - this used to be  "poft->bFlag ^= OTF_LCK;"
             */
            ClrLockOFT(poft);

            if ( poft->cWaits != 0 ) {
                WakeupOFT( poft );
            }

        } else {

            DecrHoldsOFT ( poft );

            if ( poft->bFlag & OTF_PND ) {
                ChkHoldOFT ( poft );
#ifdef DEBUG
                VBS ();
#endif
            }
        }

#ifdef DEBUG
        VBS ();
#endif
#ifndef MINIFSD
        if ( cchWrite ) {
            if (!(poft->bSFlag & OFS_DASD)) {

                /* Direct Access do not use directory structures
                 * so Fnode & Dir exist only if not direct access
                 */
                                                           //#83299
                if (fsType & OPEN_WRITE_THROUGH) {
                  if (fsType & WT_EXT)
                    UpdateDisk (poft,TRUE,pfrm);
                }
            }
        }
#endif

#ifdef DEBUG
        VBS ();
#endif
        return GetHardErr(usRet);

        /*
         * We got a hard error reading in the FNODE.
         * Mark the OFT as corrupt.
         */

wri70:

#ifdef DEBUG
        dprintf ("Write: mark oft as corrupt\n");
#endif
        return OFTCorrupt(poft,pfrm);        /* mark OFT as corrupt */
}
