/*static char *SCCSID = "@(#)setea.c    6.2 92/01/31";*/
#define BSWAP0 BSWAP0

#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.h>

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

#include "pr_debug.h"
#include "pr_alloc.h"
#include "pr_ea.h"
#include "pr_fssub.h"
#include "pr_heap.h"
#include "pr_misc.h"
#include "pr_cache.h"
#include "assert.h"

/*
 ***    SetEA - Set EAs for an OFT
 *
 *      SetEA does the actual work for SetFileEA and SetPathEA.
 *
 *      ENTRY   pfeal (esi) = address of FEA LIST
 *              poft (EDI) = OFT address MUST BE LOCKED (see below)
 *              *pcbLen = length of EA list to be modified
 *              fReplace = TRUE iff all old EA's are to be deleted (for open)
 *                         FALSE other wise
 *
 *
 *      RETURNS
 *              error code if error
 *                  *pusRet = offset of faulty FEA into FEA buffer
 *
 *              else 0 if no error
 *                  *pcbLen (ecx) = new EA size value for DIR_EALEN
 *                  *pusRet = DF_NEEDEAS if the resulting FEA list has any
 *                                      FEA's marked 'need'
 *                            0 if no need FEA's.
 *
 *      NOTES
 *          The POFT has to be locked because it is used to serialize
 *          access to the FNode.
 *
 *
 *      BUGBUG - There is a problem with allocation.  We could reserve
 *      space up front, in which case take it out of EAA, or we've
 *      got to build the new list without deleting the old.  This
 *      latter scheme also requires excess disk space.  We can compute
 *      our max grow by adding some code the the stuff where we do a size
 *      computation.
 *
 *      HISTORY
 *  @76193  23-Nov-1993 by D.Azzarito - Change holds for LOG support
 *          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).
 *          19-Sep-89 by JR (John Rogers, JohnRo@Microsoft)
 *              - Avoid warnings from Microsoft C 5.2 (on slash-slash comments).
 *         24-Jan-1989 16:34:47 by Jon Parati [jonpa]  Rewrote it for atomicity
 *
 */

/*
 * BUGBUG - this function assumes that EAA is atomic,
 *  IE either EAA will return the entire allocation requested,
 *  or it will not allocate anything.
 */

USHORT far
SetEA(PFEALIST pfeal, POFT poft, PUSHORT pusRet, PUSHORT pcbLen,
               BOOL fReplace, PFRAME pfrm)
{
    PAUXHAND    paxhEA;         /* addr of AUXHAND for reading EAs */
    PFNODE      pfnSave;
    PFNODE      pfn;            /* address of fnode */
    PBUFNODE    pbufFNB;        /* address of fnode's buffer */
    USHORT      erc = NO_ERROR;
    PFEA        pfeaDelLst = NULL;
    PBUFNODE    pbufDelLst;
    PFEA        pfeaDelEnd;
    PFEALIST    pfealEABuf = NULL;
    PBUFNODE    pbufBH = NULL;  /* buffer header if SEA_BUF is cache blk */
    PFEA        pfeaAppend;     /* address of work fea buf */
    USHORT      cbEES;          /* expanded EA size */
    PFEA        pfeaNew;
#ifdef DEBUG2
    PBUFNODE    pbufDebug;
    PBYTE       pbDebug = NULL;
#endif

    /* default to no error, no need fea's */
    *pusRet = 0;


    /* check if valid pfeal->cbList */
    if( pfeal->cbList > CB_MAX_EALIST )
        return ERROR_EA_LIST_TOO_LONG;
    else if( pfeal->cbList < FIELD_SIZE( FEALIST, cbList ) )
        return ERROR_EA_LIST_INCONSISTENT;


    /* Alloc space for saved copy of fnode data */
    if( (pfnSave = (PFNODE)GHS( sizeof(FNODE),pfrm )) == NULL ) {
        /* No space! set error and return */
        return ERROR_NOT_ENOUGH_MEMORY;
    }


    /* get heap space for AUXHAND */
    CHECKSWAP(&swapBSWAP0);
    if( (paxhEA = AAH(pfrm)) == NULL ) {
        /* No Space! */
        RetHeap( pfnSave );
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    /* read in and hold the FNode */
    pfn = (PFNODE) LSD (& (poft->scpFNode),
                        BP_KEEP,
                        &pbufFNB,pfrm);

    if (pfn == NULL) {

        erc = ERROR_GEN_FAILURE;
        goto ErrorExit;
    }

    IncrHoldsBUF (pbufFNB);  // @76193

/* sea2: */
    {   /*
         * Either *.usFNL or *.cbRun will be 0, so cbOldList will equal the
         * length of the current unexpanded EA's for this file.
         */
        USHORT cbOldList = (USHORT)pfn->aiEA.sp.cbRun + pfn->aiEA.usFNL;
        UINT cb;

        /* Alloc Buffer big enough for old EA list (size = old list size) */
        if((pfeaDelLst = (PFEA)GetBuf(cbOldList,&pbufDelLst,pfrm)) == NULL) {
            /* Can't get the buffer so set erc and exit */
            erc = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }
        pfeaDelEnd = pfeaDelLst;

        /*
         * Alloc Buffer for new EA list (size = max( old + new, CB_MAX_EALIST))
         *
         * Note that CSS eiter returns CSS_RET_INCONS_EA (==1) if inconsistent
         * ea list, or else it returns the size of the compressed
         * new ea list ( == 0 OR >= CB_MIN_EA )
         */
        CHECKSWAP(&swapBSWAP0);
        if( (cb = CSS( pfeal )) == CSS_RET_INCONS_EA ) {
            /* FEA list is inconsistant, so return error */
            erc = ERROR_EA_LIST_INCONSISTENT;
            goto ErrorExit;
        }

        cb += FIELD_SIZE( FEALIST, cbList );

        if( !fReplace ) {
            /*
             * updating the new old list to the new,
             * max list size = min( old + new, 0xFFFF )
             */
            if( cb > CB_MAX_EALIST - cbOldList )
                cb = CB_MAX_EALIST;
            else
                cb += cbOldList;
        }


        /* Get a buffer to build the new ea list into */
        if ((pfealEABuf = (PFEALIST)GetBuf(cb,&pbufBH,pfrm)) == NULL) {
            /* Can't get the buffer so set erc and exit */
            erc = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }


        /* set append list to be empty */
        pfeaAppend = pfealEABuf->list;
        pfealEABuf->cbList = sizeof( pfealEABuf->cbList );


/* sea15: */

        /*
         * We have our buffer.
         * Setup AUXHAND to read our stuff from the file and
         * read the existing EAs in.
         */
        if (( paxhEA->cbFile = paxhEA->cbRem = cbOldList ) != 0){

            if (pfn->aiEA.usFNL != 0) {

                /*
                 * Have some EA stuff, and it's in the fnode
                 *
                 * pfn       (eax) = fnode address
                 * pfn->ai.cbFNL (ebx) = size of EA stuff
                 * paxhEA    (ecx) = address of AUXHAND structure
                 */
                paxhEA->pbDataBuf = (PBYTE)pfn +
                            pfn->usACLBase + pfn->aiACL.usFNL;

            } else { /* <else sea25> */

                /*
                 * Have some EA stuff, and it's in an allocation tree
                 */
/* sea20: */
                CHECKSWAP(&swapBSWAP0);
                paxhEA->palb =
                    BuildAlBlk(&(pfn->aiEA.sp), pfn->aiEA.bDat == 0,pfrm);
                paxhEA->cbRem = 0;

            }


            /*
             * if we are to replace the list, then
             *      read the entire old list into the delete buffer
             * else
             *      read the entire existing list into the append buffer
             *
             * for each new ea
             *      scan the existing append buffer for the new ea name
             *      if the new ea is found in the append buffer then
             *          move the old copy of the ea to the delete buffer
             *          and BYTE_COPY the rest of the list down to fill
             *          in the hole
             */


            if( fReplace ) {
                /* read the old list into the del buffer, and set EA len to 0 */
                CHECKSWAP(&swapBSWAP0);
                RdAH(paxhEA,&cbOldList, &(paxhEA->pbDataBuf),
                             (PBYTE FAR *)&pfeaDelEnd, pfrm);
                cbEES = 0;
            } else {
                PFEA pfeaNext = pfealEABuf->list;

                /* read the old list into the app buffer */
#ifdef DEBUG2
                USHORT cbDebug = cbOldList;

                if ((pbDebug = (PBYTE)GetBuf(cbOldList,&pbufDebug,pfrm)) == NULL) {
                    dprintf (__FILE__": pbDebug is NULL!\n");
                    Int3();
                }
#endif
                pfealEABuf->cbList += (cbEES = cbOldList);
                CHECKSWAP(&swapBSWAP0);
                RdAH(paxhEA,&cbOldList, &(paxhEA->pbDataBuf),
                                  (PBYTE FAR *)&pfeaAppend, pfrm);
#ifdef DEBUG2
                memcpy (pbDebug, pfealEABuf->list, cbDebug);
#endif
                /* compute the length of the existing EA list */
                cbEES = 0;

                while( cbOldList != 0 ) {

                    /* get the Virtual Value length */
                    if( pfeaNext->fEA & FF_BIGD ) {
                        cbEES +=
                            (USHORT)(((PEAAT)FEA_AVALUE(*pfeaNext))->cbRun);
                    } else {
                        cbEES += pfeaNext->cbValue;
                    }

                    /* now for the rest of the EA */
                    cbEES += FEA_SIZE + pfeaNext->cbName + 1;

                    cbOldList -= SIZEOF_FEA( *pfeaNext );
                    pfeaNext = NEXT_PFEA( pfeaNext );
                }

            }
        } else {

            /* No old data, so set cbEES to empty list size */
            cbEES = 0;
        }
    } /* scope cbOldList */

    /* remember the start of the new EA's */
    pfeaNew = pfeaAppend;


    {   UINT cbLeft = (USHORT)pfeal->cbList - sizeof( pfeal->cbList );
        PFEA pfeaNext = pfeal->list;
        UINT cfeaNeed = fReplace ? 0 : (UINT)pfn->ulRefCount;
        PFEA pfeaBuf;

        if( (pfeaBuf = (PFEA)GHS( FEA_SIZE + CB_MAXEANAML +
                                      sizeof( (BYTE)'\0' ),pfrm )) == NULL ) {
            /* No space! set error and return */
            erc = ERROR_NOT_ENOUGH_MEMORY;
            *pusRet = POINTER_SUBTRACT (pfeaNext, pfeal);
            goto ErrorBackOut;

        }

#if( CB_MAX_EALIST != 0xFFFF )
#   error cfeaNeed needs to be a ULONG
#endif

#ifdef DEBUG
        if( pfn->ulRefCount > CB_MAX_EALIST ) {
            INTERR( __FILE__": pfn->ulRefCount > CB_MAX_EALIST" );
        }
#endif


        while( cbLeft != 0 ) {
            UINT cb;

            /*
             * copy the FEA over to the end of the append list, but
             * don't include it in pfealEABuf->cbList yet.
             *
             * IE. Build the new FEA at the append point of the ea list,
             * but don't recognize it as part of the ea list until
             * it has been completely built.
             */

            /* copy the fEA field but clear our reserved bits */
            pfeaBuf->fEA = pfeaNext->fEA & ~(FF_BIGD | FF_DAT);

            /* now copy the name */
            cb = pfeaBuf->cbName = pfeaNext->cbName;
            CHECKSWAP(&swapBSWAP0);
            if( !CopyEAName(pfeaBuf->szName, pfeaNext->szName, cb ) ) {
                /* bad ea name, return error */
                erc = ERROR_INVALID_EA_NAME;
                *pusRet = POINTER_SUBTRACT (pfeaNext, pfeal);
                RetHeap( pfeaBuf );
                goto ErrorBackOut;
            }

            {
                UINT oEA;

                /* check to see if this EA is already in the list */
                CHECKSWAP(&swapBSWAP0);
                if((oEA = ScanEA(pfeaBuf, (PEALIST)pfealEABuf, TRUE)) != 0){
                    /*
                     * it is in the old list, remove the old version
                     * and compress the list.
                     */
                    PFEA pfeaOld = (PFEA)BUMP_PTR( pfealEABuf, oEA );

                    /*
                     * if this was a 'need' ea then remove it from
                     * the 'ulRefCount' in the fnode
                     */
                    if( pfeaOld->fEA & FF_NEED ) {
                        /* is a 'need' FEA, record that it got deleted */
                        cfeaNeed--;
                    }

                    /*
                     * set oEA to be the offset to the abValue field
                     */
                    oEA = FEA_SIZE + pfeaOld->cbName + sizeof((char)'\0');

                    if( (pfeaOld->fEA & FF_BIGD) != 0 ) {
                        /*
                         * EA has big data, we have to account for the
                         * disk space somehow.
                         */
                        cbEES -= (oEA +
                                (USHORT)((PEAAT)BUMP_PTR(pfeaOld,oEA))->cbRun);

                        /* make oEA the complete size of the stored EA */
                        oEA += sizeof( EAAT );

                        if( OFFSETOF(pfeaOld) < OFFSETOF(pfeaNew) ) {
                            /*
                             * It is an original EA, we can't delete
                             * it until we are sure that everything
                             * else has gone OK, so we have to put
                             * it on the delete list
                             */
                            memcpy( pfeaDelEnd, pfeaOld, oEA );
                            pfeaDelEnd = (PFEA)BUMP_PTR( pfeaDelEnd, oEA );

                            /*
                             * prepare pointer to new FEA's for when
                             * we sqeeze out this FEA
                             */
                            pfeaNew = (PFEA)BUMP_PTR( pfeaNew, -oEA );

                        } else {
                            /*
                             * This is one of the EA's the user gave us,
                             * he must have used the same EA name twice
                             * so we will delete the first one we found
                             * and replace it with the second EA.
                             */
                            CHECKSWAP(&swapBSWAP0);
                            DelEAData( pfeaOld, NEXT_PFEA(pfeaOld), pfrm );
                        }
                    } else {
                        /*
                         * This is FEA is to be removed, but it does not have
                         * big data
                         *
                         * just account for it's size being removed from the
                         * list and possibly fix the pointers up
                         *
                         */
                        cbEES -= (oEA += pfeaOld->cbValue);

                        if( OFFSETOF(pfeaOld) < OFFSETOF(pfeaNew) ) {

                            /*
                             * pfeaNew is after pfeaOld, so prepare
                             * it for when we sqeeze out this FEA
                             */
                            pfeaNew = (PFEA)BUMP_PTR( pfeaNew, -oEA );
                        }
                    }

                    /*
                     * Now squeeze out the hole that is left behind.
                     * Length of bytes to move is the difference between
                     * the end of the current FEA list and the beginning
                     * of the deleted FEA record, minus the length of the
                     * FEA to delete, but adding in the size of the incomplete
                     * FEA record pointed to by pfeaBuf.
                     *
                     * oEA  = size of the FEA record to be sqeezed out
                     * cb   = size of the name in the new FEA record
                     * pfeaOld      = address of beginning of deleted FEA
                     * pfeaAppend   = 1st byte after last complete FEA in the
                     *              list
                     * pfeaBuf      = address of Uppercased copy of new FEA
                     *
                     */
                    BYTE_COPY( ((PBYTE)pfeaOld + oEA),
                               pfeaOld,
                               POINTER_SUBTRACT( pfeaAppend, pfeaOld ) - oEA );

                    /*
                     * Fix up the pointers
                     *
                     * Note: pfeaNew has been conditionally moved down
                     * back in the if( POINTER_SUBTRACT(...) ) conditional
                     */
                    pfeaAppend = (PFEA)BUMP_PTR( pfeaAppend, -oEA );
                    pfealEABuf->cbList -= oEA;

                }
            } /* scope oEA */
            {
                UINT cbValue;

                /*
                 * Every thing is just fine... situation normal:
                 * either the new EA was not to replace one of the
                 * EA's already in the list, or the old ea has been
                 * deleted and buffer and pointers are as if it had
                 * never existed.
                 *
                 * Finish copying over the new EA and save it's data
                 * to disk if it is Big enough.
                 *
                 * pfeaAppend = 1st byte after last complete FEA in the list
                 *
                 * pfeaBuf    = the upper case copy of the new FEA
                 *
                 * pfeaNew    = start of the EA's we have added to the list.
                 * pfeaNext   = address of input FEA from user.
                 *
                 * cbEES      = size of EA list with big data expanded.
                 * cb         = size of name of current EA.
                 *
                 * pfealEABuf->cbList does not yet include the size for
                 * the FEA we are building on the end of the list.
                 */

                /*
                 * make cb into an offset to the value field
                 */
                cb += FEA_SIZE + sizeof( (char)'\0' );

                if( (cbValue = pfeaNext->cbValue) == 0 ) {
                    /*
                     * This EA is to be deleted, so ignore it
                     *
                     * Since cbValue == 0, cb is the size of the
                     * entire FEA!
                     */

                    cbLeft -= cb;
                    pfeaNext = (PFEA)BUMP_PTR( pfeaNext, cb );
                    continue;

                }

                /*
                 * We need this name, so copy it from our work buffer
                 * over to the real FEA list
                 */
                if( cbEES > (CB_MAX_EALIST - FIELD_SIZE(FEALIST, cbList)) -
                                                            (cb + cbValue) ) {
                    /* This FEA won't fit in the Expanded EA list! */
                    erc = ERROR_EA_LIST_TOO_LONG;
                    *pusRet = POINTER_SUBTRACT( pfeaNext, pfeal );
                    RetHeap( pfeaBuf );
                    goto ErrorBackOut;
                }

                memcpy( pfeaAppend, pfeaBuf, cb );

                if( cbValue <= EADATLIM) {
                    pfeaAppend->cbValue = cbValue;

                    /*
                     * After this copy, set pfeaAppend to the
                     * 1st free byte after this new FEA.
                     */
                    pfeaAppend = BYTE_COPY( (PBYTE)pfeaNext + cb,
                                            (PBYTE)pfeaAppend + cb,
                                            cbValue );

                    /*
                     * make cb = total size of saved EA and
                     * make cbValue = difference in size of data saved
                     * and writen to disk
                     */
                    cb += cbValue;
                    cbValue = 0;

                } else {
                    /*
                     * The incoming guy has a lot of data.  Put it in it's own
                     * allocation tree
                     *
                     * We'll put the data portion in an allocation chain and
                     * put an EAAT record in the target guy.
                     *
                     * cb = offset in FEA to aValue field
                     */

                    /* peaat = FEA_AVALUE( *pfeaAppend ), only quicker */
                    PEAAT peaat = (PEAAT)BUMP_PTR( pfeaAppend, cb );
                    PALBLK  palbTmp;


                    /* set 1 of our reserved bits to say Big Data */
                    pfeaAppend->fEA |= FF_BIGD;

                    /*
                     * Clear cbRun so BuildAlBlk won't think garbage
                     * is a valid run
                     */
                    peaat->cbRun = 0;

                    CHECKSWAP(&swapBSWAP0);
                    palbTmp = BuildAlBlk(peaat, TRUE, pfrm);

                    CHECKSWAP(&swapBSWAP0);
                    if(!EAA(palbTmp,
                            poft->scpFNode.lsn & SECMASK,
                            cbValue,
                            pfrm)) {

                        /* can't get the disk space for him */
                        /*  BUGBUG -- revisit after EAA's return values are
                         *  fixed up.
                         */
                        erc = ERROR_DISK_FULL;
                        *pusRet = POINTER_SUBTRACT (pfeaNext, pfeal);
                        RetHeap( pfeaBuf );
                        goto ErrorBackOut;
                    }


                    CHECKSWAP(&swapBSWAP0);
                    if(!WriteALB(cbValue,(PBYTE)pfeaNext + cb,palbTmp,pfrm)) {

                        erc = ERROR_READ_FAULT;
                        RetHeap( pfeaBuf );
                        goto ErrorBackOut;
                    }

                    /*
                     * update the EAAT struct in the abValue field,
                     * and clean up unneeded heap space
                     */
                    CHECKSWAP(&swapBSWAP0);
                    pfeaAppend->fEA |= BuildEAAT( peaat, palbTmp );

                    RetHeap( palbTmp );
                    peaat->cbRun = cbValue;
                    pfeaAppend->cbValue = sizeof( EAAT );
                    /*
                     * make cb = total size of saved EA and
                     * make cbValue = difference in size of data saved
                     * and writen to disk
                     */
                    cb += sizeof( EAAT );
                    cbValue -= sizeof( EAAT );

                    /*
                     * done with this FEA output pointer, bump it to
                     * the next space.
                     */
                    pfeaAppend = (PFEA)BUMP_PTR( pfeaAppend, cb );

                } /* else */


                /*
                 * The new FEA is complete in the append list, with
                 * it's data either in the list, or written to disk.
                 *
                 * Now we need to bump all the pointers and account for
                 * the new FEA in the cbList field of the FEAL.
                 *
                 * cb       = total size of EA saved in list
                 * cbValue  = difference in size of data saved in list
                 *            and written to disk
                 *
                 * pfeaAppend   = address of 1st byte after newly completed FEA
                 * pfeaNext     = address of FEA we just ported over
                 * pfealEABuf->cbList does not account for new FEA yet.
                 * but cbList still has the original FEA size added in it.
                 *
                 */
                pfealEABuf->cbList += cb;

                /*
                 * make cb = total size of original FEA
                 * and use it to update pfeaNext and cbList
                 */
                cb += cbValue;
            } /* scope cbValue */

            /* check if is a need fea and count it if so */
            if( pfeaNext->fEA & FF_NEED )
                cfeaNeed++;

            pfeaNext = (PFEA)BUMP_PTR( pfeaNext, cb );
            cbLeft -= cb;

            cbEES += cb;

        } /* while */


        /*
         ** OK, we've now built, into our buffer, the final resultant
         *  EA list that we'll store on the disk.  Purge any existing
         *  EAs for this file and write the new stuff out.
         *
         */

/* sea100: */

        /* Get rid of our EA Name work buffer */
        RetHeap( pfeaBuf );

        /* save the info in the fnode incase we have to back out */
        BYTE_COPY( pfn, pfnSave, sizeof( FNODE ) );

        /* 'clear' the old EA stuff */
        pfn->aiEA.usFNL = 0;
        pfn->aiEA.sp.cbRun = 0;

        /* write out the new list */
        CHECKSWAP(&swapBSWAP0);
        if( (erc = AppendAux( &(pfn->aiEA),
                   pfn,
                   ( (USHORT)pfealEABuf->cbList - sizeof(pfealEABuf->cbList) ),
                   (PBYTE)(pfealEABuf->list),
                   poft->scpFNode.lsn & SECMASK,
                   pfrm )) != NO_ERROR ) {

            /* restore the old fnode */
            BYTE_COPY( pfnSave, pfn, sizeof( FNODE ) );
            goto ErrorBackOut;
        }

        /* clear any allocation for the previouse list */
        CHECKSWAP(&swapBSWAP0);
        if( !DelAux( &(pfnSave->aiEA), pfnSave, pfrm ) ) {
            /* an error occured during deltion of the old EA list */
            SETHARDERR( erc = ERROR_READ_FAULT );
        }

        /* clear the old big ea allocations */
        CHECKSWAP(&swapBSWAP0);
        DelEAData( pfeaDelLst, pfeaDelEnd, pfrm );

        /* set the new 'need' fea count */
        pfn->ulRefCount = cfeaNeed;
        *pusRet = (cfeaNeed != 0) ? DF_NEEDEAS : 0;

        /* mark the fnode block dirty */
        DecrHoldsBUF( pbufFNB );  // @76193
        SetDirt((PBYTE)pfn, pbufFNB, pfrm->u.fWrtThru, pfrm);

    } /* scope cbLeft et al */

#ifdef DEBUG
    {
        UINT cb = (USHORT)pfealEABuf->cbList - FIELD_SIZE( FEALIST, cbList );
        USHORT cbRealList = FIELD_SIZE( FEALIST, cbList );
        PFEA pfeaNext = pfealEABuf->list;

        /*
         * sanity check to make sure our computed EA len == real ea length
         */

        while( cb != 0 ) {

            /* get the Virtual Value length */
            if( pfeaNext->fEA & FF_BIGD )
                cbRealList += (USHORT)(((PEAAT)FEA_AVALUE(*pfeaNext))->cbRun);
            else
                cbRealList += pfeaNext->cbValue;

            /* now for the rest of the EA */
            cbRealList += FEA_SIZE + pfeaNext->cbName + 1;

            if( SIZEOF_FEA( *pfeaNext ) > cb ) {
                dprintf(
            __FILE__"(%d): list about to write to disk is inconsistent!\n",
                                                                    __LINE__ );
                Int3();
                break;
            }

            cb -= SIZEOF_FEA( *pfeaNext );
            pfeaNext = NEXT_PFEA( pfeaNext );
        }

        if( cbRealList != (cbEES + FIELD_SIZE (FEALIST, cbList)) ) {
            dprintf(
    __FILE__"(%d): EA length sanity check failed, comp len:%d, real len:%d\n",
                    __LINE__, cbEES, cbRealList );

            Int3();
        }

    }
#endif
#ifdef DEBUG2
    if (pbDebug!= NULL) {
        RelBuf(pbDebug,pbufDebug,pfrm);
    }
#endif

    /*
     * Release our working buffers
     */
    RelBuf((PBYTE)pfealEABuf, pbufBH, pfrm);
    RelBuf((PBYTE)pfeaDelLst, pbufDelLst, pfrm);

    /*
     * Set the new EA length.  Note that on the disk the size of cbList
     * is not included.
     */
    *pcbLen = cbEES;

    RelAuxHand( paxhEA );
    RetHeap( pfnSave );
    return (erc);

/*
 *
 *  Error handling Code
 *
 */

ErrorBackOut:
    /*
     * An error occured after we had already written the new Big Data
     * fields to disk, so delete them!
     */
    CHECKSWAP(&swapBSWAP0);
    DelEAData( pfeaNew, pfeaAppend, pfrm );


    /*  Note that ErrorBackOut falls through to ErrorExit */

ErrorExit:
    /*
     * An error occured before any disk activity has taked place
     */
    ASSERT_OFT(poft);

    if( pfealEABuf != NULL ) {
        RelBuf((PBYTE)pfealEABuf, pbufBH, pfrm);
    }

    if( pfeaDelLst != NULL ) {
        RelBuf((PBYTE)pfeaDelLst, pbufDelLst, pfrm);
    }

    if( pfn != NULL )
        DecrHoldsBUF( pbufFNB );  // @76193

    RelAuxHand( paxhEA );
    RetHeap( pfnSave );

#ifdef DEBUG2
    if (pbDebug!= NULL) {
        RelBuf(pbDebug, pbufDebug, pfrm);
    }
#endif

    return( erc );
}
