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

#include "const.h"
#include "misc.h"
#include "chain.h"
#include "buf.h"
#include "dir.h"
#include "storage.h"
#include "ea.h"
#include "fnode.h"
#include "volume.h"
#include "tables.h"
#include "pbmacros.h"
#include "log.h"
#include "lw.h"
#include "frame.h"
#include "fsdata.h"
#include "heap.h"
#include "assert.h"
#include "pr_debug.h"
#include "pr_ea.h"
#include "pr_cache.h"
#include "pr_dir.h"
#include "pr_heap.h"
#include "pr_misc.h"
#include "pr_read.h"

/*
 ***    GetEA - Get Extended Attributes from a File
 *
 *      GetEA does the actual work for QueryEA and QPathEA.
 *
 *      ENTRY   pfeal = FEA list address for return info
 *              pgeal = GEA list address to qualify names, NULL if all
 *              poft  = OFT address, locked
 *
 *      RETURNS NO_ERROR        - if OK
 *              error code      - if error occured and
 *                *poError = offset of error in GEA list
 *
 *      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).
 *          12-May-1989 16:38:08 by Jon Parati [jonpa]  minor rewrite
 *
 */
USHORT far
GetEA(PFEALIST pfeal,PGEALIST pgeal,POFT poft,PUSHORT poError,BOOL fLevel,PFRAME pfrm)
{
    USHORT      erc;
    PFNODE      pfn;
    PBUFNODE    pbufFNode;
    PGEALIST    pgealInPut;
    ULONG       cbTotal;
    ULONG       ulEALen = FIELD_SIZE( FEALIST, cbList );
    UINT        cbList;
    PBUFNODE    pbufInPut;
    PBYTE       pbOut;

    /* assume no error */
    erc = NO_ERROR;
    *poError = 0;

    /* Read the FNODE and hold it  */
    pfn = LSD(&poft->scpFNode, BP_KEEP, &pbufFNode, pfrm);

    if (pfn == NULL) {
        return( ERROR_READ_FAULT );
    }

    /* make sure disk is not trashed */
    ASSERT_FN( pfn );

    IncrHoldsBUF( pbufFNode );  // @76193
    /*
     * if the user gave us a GEA list, get space to copy it over so
     * we can uppercase it and otherwise manipulate it.
     */
    if( IS_USER_NULL( pgeal ) ) {
        /* Empty input list, means match all FEA's */
        pgealInPut = NULL;
    } else {
        /*
         * The user gave us a list, so copy it over
         * to a work buffer, and upper case it.  While were
         * at it, we will check for consistency
         */
        USHORT cbLeft, us;
        PGEA pgeaDst, pgeaSrc;

        /*
         * Note that we don't need to worry about Huge GEA lists
         * because LockEAs will reject them for us
         */

        /* Get a buffer */
        if( (pgealInPut = (PGEALIST)GetBuf((USHORT)pgeal->cbList,
                            &pbufInPut, pfrm)) == NULL ){
            /*
             * Can't get the buffer so set erc, unhold fnode and exit
             */
            erc = ERROR_NOT_ENOUGH_MEMORY;
            goto GEAErrExitUhFn;
        }

        /* Now copy the list over */
        pgeaDst = pgealInPut->list;
        pgeaSrc = pgeal->list;

        cbLeft = (USHORT)(pgealInPut->cbList = pgeal->cbList) -
                     FIELD_SIZE( GEALIST, cbList );

        while( cbLeft >= CB_MIN_GEA ) {

            /*
             * copy the size of the name and set us to be the size of
             * the complete gea (including name and '\0')
             */
            us = sizeof(GEA) + (pgeaDst->cbName = pgeaSrc->cbName);

#ifdef DEBUG
            if( sizeof(GEA) != FIELD_SIZE(GEA, cbName) + sizeof(char) ) {
                INTERR( __FILE__": GEA size assumption is wrong" );
            }
#endif

            /*
             * check if gea is too big to fit in list
             */
            if( us > cbLeft ) {
                /* inconsistent GEA list */
                erc = ERROR_EA_LIST_INCONSISTENT;
                *poError = POINTER_SUBTRACT( pgeaSrc, pgeal );
                goto GEAErrExit;
            }

            /*
             * copy name over, uppercasing it and checking if valid name
             * as we go.
             */
            CHECKSWAP(&swapBSWAP0);
            if( !CopyEAName( pgeaDst->szName, pgeaSrc->szName,
                     (us - FIELD_SIZE(GEA,cbName) - 1) ) ) {
                /* Bad Name */
                erc = ERROR_INVALID_EA_NAME;
                *poError = POINTER_SUBTRACT( pgeaSrc, pgeal );
                goto GEAErrExit;
            }

            /*
             * bump the pointers and count them as being processed
             */
            pgeaSrc = (PGEA)BUMP_PTR( pgeaSrc, us );
            pgeaDst = (PGEA)BUMP_PTR( pgeaDst, us );
            cbLeft -= us;
        }

        /* check for list overflow */
        if( cbLeft != 0 ) {
            /* inconsistent GEA list */
            erc = ERROR_EA_LIST_INCONSISTENT;
            *poError = POINTER_SUBTRACT( pgeaSrc, pgeal );
            goto GEAErrExit;
        }

    }

    /*
     * At this point we have copied over the user's GEA list into
     * our own buffer and upper cased it.  The GEA list is also known
     * to be consistant at this point.
     *
     * pgealInPut = the address of our copy of the GEA list
     *
     *  If the target buffer is less than four bytes, there's
     *  no room to do any work.  Otherwise, set up the
     *  count of remaining bytes available in the buffer
     *  and the buffer address.
     *
     *  The case of cbList==4 may be okay if there are no EAs to
     *  return.  This was a bug when using copy from real mode.
     */
    if ( pfeal->cbList < sizeof (pfeal->cbList) ) {

        /*      No room.  Set up return values in preparation
         *      for general return processing below.
         */
        erc = ERROR_INVALID_PARAMETER;
        goto GEAErrExit;
    }

    /* allocate and zero out EA and Data handles */
    { PAUXHAND paxhEA, paxhData;

    paxhEA = NULL;
    paxhData = NULL;

    CHECKSWAP(&swapBSWAP0);
    if( (paxhEA = AAH(pfrm)) == NULL || (paxhData = AAH(pfrm)) == NULL ) {
        /*
         * No Heap space
         */
        erc = ERROR_NOT_ENOUGH_MEMORY;
        *poError = 0;

        if (paxhEA)
            RetHeap( paxhEA );

        if (paxhData)
            RetHeap( paxhData );

        goto GEAErrExit;
    }

    /* Allocate a buffer that is big enough for the largest possible EA
     * saved in line.
     * ( (1 + 1 + 2) + (255 + 1) + 128 bytes long)
     */
    { PFEA pfeaBuf;

    if( (pfeaBuf = GHS(CB_MAX_DISKFEA,pfrm)) == NULL ) {
        /* no more heap space */
        erc = ERROR_NOT_ENOUGH_MEMORY;
        goto GEAErrExit2;
    }

    /* init other vars */
    cbTotal = FIELD_SIZE( FEALIST, cbList );

    /* if the output list is huge, just treat it as 0xFFFF bytes long */
    cbList = (pfeal->cbList > CB_MAX_EALIST) ?
                                       CB_MAX_EALIST : (USHORT)pfeal->cbList;

    pbOut = (PBYTE)pfeal->list;

    paxhEA->cbFile = (USHORT)pfn->aiEA.sp.cbRun + pfn->aiEA.usFNL;
    paxhEA->cbRem = paxhEA->cbFile;

    if ( paxhEA->cbFile == 0 ) {

        /*
         * File has no extended attributes.  Remember, the cbList
         * field includes itself in the byte count.
         */
        goto GetEAExit;
    }

    if ( paxhEA->cbFile != (USHORT)pfn->aiEA.sp.cbRun ) {

        /*  We have some EA stuff in the FNODE.  It begins
         *  after the ACL stuff, in the abFree area of the
         *  FNODE.
         */
        paxhEA->pbDataBuf = (PBYTE)pfn + pfn->usACLBase + pfn->aiACL.usFNL;

    } else {
        /*  gea20:  */
        /*  We have some EA stuff in an allocation tree.  */

        paxhEA->cbRem = 0;
        CHECKSWAP(&swapBSWAP0);
        paxhEA->palb = BuildAlBlk( &pfn->aiEA.sp,
                                   (pfn->aiEA.bDat == 0), pfrm);
    }

    /*
     *  for each fea on disk {
     *      ReadEA the next fea into pfeaBuf
     *      search for it in the GEA list
     *      if it's there {
     *          copy it over to the output list
     *          and add its size to cbTotal
     *      }
     *  }
     */
    while( !EndOfEA( paxhEA ) ) {
        USHORT cbEA;
        union {
            USHORT off;
            PEAAT  peaat;
        } uPtr; /* union to multiplex stack space */

        /* ReadEA the next fea into pfeaBuf */
        cbEA = CB_MAX_DISKFEA;
#ifdef DEBUG
        {
            USHORT us;

            ASSERT_FN( pfn );
            CHECKSWAP(&swapBSWAP0);
            us = ReadEA(paxhEA, (PBYTE)pfeaBuf, &cbEA, pfrm);

            if ( us == READEA_NO_MORE ) {
                /*
                 * Something is very wrong... We were not at the end
                 * of the EA list in the File, but ReadEA returned EOF
                 */
                INTERR( __FILE__": ReadEA and EndOfEA do not agree\n" );

                /*
                 * KLUDGE!!! just ignore the extra stuff at end of list
                 * if tester wants to continue after interr
                 */
                break;
            }
        }   /* end scope us */
#else
        CHECKSWAP(&swapBSWAP0);
        ReadEA(paxhEA, (PBYTE)pfeaBuf, &cbEA, pfrm);
#endif

        /*
         * Count the total ea length incase of ERROR_BUFFER_OVERFLOW
         *
         * BUGBUG - Remove the code that maintains ulEALen if
         * we get the spec fixed.
         */
        ulEALen += cbEA;

        if( pfeaBuf->fEA & FF_BIGD ) {
            /* big ea data, correct total EA size */
            ulEALen += (USHORT)((PEAAT)FEA_AVALUE( *pfeaBuf ))->cbRun
                                                            - sizeof( EAAT );
        }

        CHECKSWAP(&swapBSWAP0);
        if( pgealInPut == NULL ||
            (uPtr.off = ScanEA( pfeaBuf, (PEALIST)pgealInPut, FALSE )) != 0 ) {

            /* we want this FEA, copy it over and mark the GEA as found */

            if ( pgealInPut != NULL ) {
                /* mark matched GEA as being used */
                ((PGEA)BUMP_PTR(pgealInPut, uPtr.off ))->szName[0] = '\0';
            }


            /* add full size of FEA to cbTotal */
            cbTotal += cbEA;

            if( pfeaBuf->fEA & FF_BIGD ) {
                uPtr.peaat = (PEAAT)FEA_AVALUE( *pfeaBuf );

                /*
                 * put the real cbValue into the aux handle,
                 * the source fea, and also add it to cbTotal
                 */
                cbTotal +=
          (paxhData->cbFile = pfeaBuf->cbValue = (USHORT)uPtr.peaat->cbRun) -
                                                         sizeof( EAAT );

                /* make FEA just equal number of bytes we need to copy */
                cbEA -= sizeof( EAAT );

            } else {
                uPtr.peaat = 0;
            }


            if( cbTotal > cbList ) {

                erc = ERROR_BUFFER_OVERFLOW;

            } else {

                /* copy pfeaBuf name and const fields to pfeal */
                memcpy( pbOut, pfeaBuf, cbEA );

                /* reset our reserved fields */
#ifdef DEBUG
                if( FIELDOFFSET( FEA, fEA ) != 0 ||
                    FIELD_SIZE( FEA, fEA ) != sizeof( BYTE ) ) {
                    INTERR( __FILE__": fEA field has changed... fix following code" );
                }
#endif
                *pbOut &= ~( FF_BIGD | FF_DAT );

                /* bump the pointer to the abValue part (or end if not BIGD)*/
                pbOut += cbEA;

                if( uPtr.peaat != 0 ) {
                    /* FEA has Big Data... RdAH it into pfeal */
                    paxhData->cbRem = 0;
                    paxhData->usSeek = 0;


                    CHECKSWAP(&swapBSWAP0);
                    if ((paxhData->palb =
                         BuildAlBlk(uPtr.peaat,
                                    !(pfeaBuf->fEA & FF_DAT),pfrm)) == NULL) {

#ifdef DEBUG
                        dprintf (__FILE__": BuildAlBlk returned null\n");
#endif
                        erc = ERROR_NOT_ENOUGH_MEMORY;
                        RetHeap (pfeaBuf);
                        goto GEAErrExit2;
                    }
#ifdef DEBUG
                    /* if it is a leaf */
                    if (!(paxhData->palb->bFlag & ABF_NODE)) {
                        if (((PFILESTORAGE) paxhData->palb)->
                                                a.aall [0].csecRun == 0) {
                            dprintf (__FILE__": BuildAlBlk returned csecRun=0\n");
                            Int3 ();
                        }
                    } else {
                        dprintf (__FILE__": BuildAlBlk returned a node\n");
                    }
#endif


                    ASSERT_FN( pfn );
                    CHECKSWAP(&swapBSWAP0);
                    if( RdAH( paxhData,
                              &(pfeaBuf->cbValue),
                              &paxhData->pbDataBuf,
                              &pbOut, pfrm) != NO_ERROR) {

                        /* gea45:  */
                        RetHeap( paxhData->palb );
                        RetHeap( pfeaBuf );
                        erc = ERROR_READ_FAULT;
                        goto GEAErrExit2;
                    }

#ifdef DEBUG
                    if( pfeaBuf->cbValue != (USHORT)uPtr.peaat->cbRun ) {
                       INTERR(__FILE__":RDAH data read != amount requested\n");
                    }
#endif

                    RetHeap(paxhData->palb);
                }
            }
        }

    }


    /*
     *  We have copied all reqested FEA's from the disk to the output
     *  fea list, now copy all the unmatched GEA's
     */

GetEAExit:

    RetHeap( pfeaBuf );
    }   /* end scope pfeaBuf */

    /*
     *  for each gea in pgealInPut {
     *      if this GEA was never matched {
     *          copy the GEA to the output FEA list making it into
     *          an FEA with no data
     *      }
     *  }
     */
    if( (pgealInPut != NULL) && (fLevel == FALSE) ) {
        PGEA pgea;
        USHORT cbLeft;

        pgea = pgealInPut->list;
        cbLeft = (USHORT)pgealInPut->cbList - FIELD_SIZE( FEALIST, cbList );

        while( cbLeft != 0 ) {
            BYTE cbName = pgea->cbName;
            USHORT cbFEA = FEA_SIZE + cbName + sizeof( char );

            if( pgea->szName[0] != '\0' ) {

#ifndef IGNORE_DUP_EA
                /* set up size of list for ScanEA */
                pfeal->cbList = cbTotal;
#endif

                if( (cbTotal += cbFEA) <= cbList ){
                    /*
                     * We never matched this GEA, so copy over to pfeal
                     * as an empty FEA
                     */
#ifndef IGNORE_DUP_EA
                    #define pfeaOut ((PFEA)pbOut)

                    pfeaOut->fEA = 0; /* not found so is not a 'NEED' fea */
                    pfeaOut->cbName = cbName;
                    pfeaOut->cbValue = 0;

                    /* copy name over and make it zero terminated */
                    memcpy( pfeaOut->szName, pgea->szName, cbName + 1 );

                    /* make sure this GEA is not a duplicate */
                    CHECKSWAP(&swapBSWAP0);
                    if( ScanEA( pfeaOut, (PEALIST) pfeal, TRUE ) == 0 ) {
                        /* not a duplicate so keep it in the list */
                        pbOut += cbFEA;
                    } else {
                        cbTotal -= cbFEA;
                    }

                    #undef pfeaOut
#else
                    *pbOut++ = 0;               /* fEA (Not marked need) */
                    *pbOut++ = cbName;          /* cbName */
                    *pbOut++ = 0;               /* cbValue (low)*/
                    *pbOut++ = 0;               /* cbValue (hi) */

                    /* copy name over and make it zero terminated */
                    memcpy( pbOut, pgea->szName, cbName );
                    pbOut += cbName;

                    *pbOut++ = '\0';
#endif
                } else {
                    erc = ERROR_BUFFER_OVERFLOW;
                }
            }

            /* bump pointer and remove from count */
            pgea = (PGEA)BUMP_PTR( pgea, sizeof(GEA) + cbName );
            cbLeft -= sizeof(GEA) + cbName;

        }
    } else {
       if (fLevel) {
         pfeal->cbList = FIELD_SIZE( FEALIST, cbList );
         goto GEAErrExit2;
       }
    }

    if( (pfeal->cbList = cbTotal) > CB_MAX_EALIST ) {
        erc = ERROR_EA_LIST_TOO_LONG;
    }


GEAErrExit2:
    /* gea990: */

    if ( paxhData->pbuf != NULL ) {
        DecrHoldsBUF( paxhData->pbuf );  // @76193
    }

    /*  Release the Data and EA AUXHANDs.  */
    RetHeap( paxhData );

    if ( paxhEA->palb != NULL )
        RetHeap( paxhEA->palb );

    if ( paxhEA->pbuf != NULL ) {
        DecrHoldsBUF( paxhEA->pbuf );  // @76193
    }

    RetHeap( paxhEA );
    } /* end scope paxhEA paxhData */

GEAErrExit:

    /* release the uppercase input buffer if we got one */
    if( pgealInPut != NULL ) {
        RelBuf((PBYTE)pgealInPut, pbufInPut, pfrm);
    }

GEAErrExitUhFn:
    /* release the FNode buffer */
    DecrHoldsBUF( pbufFNode );  // @76193

    if (pbufFNode->pWaitChain != 0) {
         WakeupMisc(&(pbufFNode->pWaitChain),
            LOB_BUFWAIT);
    }

    if( erc == ERROR_BUFFER_OVERFLOW ) {
        /*
         * Error code for buffer overflow.
         *
         *  Due to a spec screw-up, we are supposed to return the size
         * of the entire EA list for the file on ERROR_BUFFER_OVERFLOW,
         * and not just the ones requested.
         *
         * BUGBUG - remove this code if we win the 2.0 spec battle!
         * or else do this the right way
         *
         */
        pfeal->cbList = ulEALen;
    }

    return erc;

}



/*
 ***    MkFEAFromGEA - make a GEA list into an FEA list
 *
 *      ENTRY   pfeal = FEA list address for return info
 *              pgealInPut = GEA list address to qualify names, NULL no GEA's
 *
 *      RETURNS NO_ERROR        - if OK
 *              error code      - if error occured and
 *                *poError = offset of error in GEA list
 *
 *      ROUGH ALGORYTHM
 *
 *      for each gea in pgealInPut {
 *          if this GEA was never matched {
 *              copy the GEA to the output FEA list making it into
 *              an FEA with no data
 *          }
 *      }
 */
USHORT far MkFEAFromGEA( PGEALIST pgealInPut, PFEALIST pfeal, PUSHORT poError, BOOL fLevel )
{
    UINT        cbList;
    ULONG       cbTotal;
    PBYTE       pbOut;

    cbTotal = FIELD_SIZE( FEALIST, cbList );

    /* if the output list is huge, just treat it as 0xFFFF bytes long */
    cbList = (pfeal->cbList > CB_MAX_EALIST) ?
                                       CB_MAX_EALIST : (USHORT)pfeal->cbList;
    pbOut = (PBYTE)pfeal->list;

    if ( (!IS_USER_NULL(pgealInPut)) && (fLevel == FALSE) ) {

        PGEA pgea;
        USHORT cbLeft;

        pgea = pgealInPut->list;
        cbLeft = (USHORT)pgealInPut->cbList - FIELD_SIZE( FEALIST, cbList );

        while( cbLeft != 0 ) {
            BYTE cbName = pgea->cbName;
            USHORT cbFEA = FEA_SIZE + cbName + sizeof( char );

            if( pgea->szName[0] != '\0' ) {

                /* set up size of list for ScanEA */
                pfeal->cbList = cbTotal;

                if( (cbTotal += cbFEA) <= cbList ){
                    /*
                     * We never matched this GEA, so copy over to pfeal
                     * as an empty FEA
                     */
                    #define pfeaOut ((PFEA)pbOut)

                    pfeaOut->fEA = 0; /* not found so is not a 'NEED' fea */
                    pfeaOut->cbName = cbName;
                    pfeaOut->cbValue = 0;

                    /* copy name over and make it zero terminated */
                    CHECKSWAP(&swapBSWAP0);
                    if( !CopyEAName( pfeaOut->szName, pgea->szName, cbName ) ) {
                        *poError = POINTER_SUBTRACT( pgea, pgealInPut );
                        return ERROR_INVALID_EA_NAME;
                    }

                    /* make sure this GEA is not a duplicate */
                    CHECKSWAP(&swapBSWAP0);
                    if( ScanEA( pfeaOut, (PEALIST) pfeal, TRUE ) == 0 ) {
                        /* not a duplicate so keep it in the list */
                        pbOut += cbFEA;
                    } else {
                        cbTotal -= cbFEA;
                    }

                    #undef pfeaOut
                } else {
                    return ERROR_BUFFER_OVERFLOW;
                }
            }

            /* bump pointer and remove from count */
            pgea = (PGEA)BUMP_PTR( pgea, sizeof(GEA) + cbName );

            if( cbLeft < (cbName += sizeof(GEA)) ) {
                *poError = POINTER_SUBTRACT( pgea, pgealInPut );
                return ERROR_EA_LIST_INCONSISTENT;
            }

            cbLeft -= cbName;
        }

        /* set the size of the FEA list */
        pfeal->cbList = cbTotal;

    } else {
        /* make it an empty FEA list */
        pfeal->cbList = FIELD_SIZE( FEALIST, cbList );
    }


    return NO_ERROR;
}
