;       SCCSID = @(#)extattr.asm        6.1 90/11/17
;      SCCSID = @(#)extattr.asm 13.16 90/07/17
PAGE    ,132
TITLE   EXTATTR - Extended Attribute support
NAME    EXTATTR

;*** Extended attribute support for the file system.
;
;
;       Global          Local
;
;       GetFEAInfo
;       SetFEAInfo
;       DoSetFEAInfo
;                       DeleteFEA
;                       AppendFEA
;                       GetFEAsizes
;                       UpdateFEAlist
;                       FindGEAbyFEA
;                       FindFEAbyGEA
;                       FindFEAbyFEA
;                       GetFEAFromGEAlist
;                       MakeDummyFEA
;                       DiskReadFEAlist
;       DiskWriteFEAlist
;       GetFEAlistSize
;                       CopyEANameString
;       CheckEAName
;
;
;   Call Graphs for routines in this file
;
;       DoGetInfo     (in FINFO.ASM)
;           GetFEAInfo
;               DiskReadFEAlist
;               GetFEAFromGEAlist
;                   FindGEAbyFEA
;                   DeleteFEA
;                       FindFEAbyFEA
;                   FindFEAbyGEA
;                   MakeDummyFEA
;                   AppendFEA
;                       GetFEAsizes
;
;
;       DoSetInfo     (in FINFO.ASM)
;           SetFEAInfo
;               DiskReadFEAlist
;               UpdateFEAlist
;                   GetFEAsizes
;                   DeleteFEA
;                       FindFEAbyFEA
;                   AppendFEA
;                       GetFEAsizes
;               DiskWriteFEAlist
;
;
;
; Implications of Implementation
; ==============================
;
; There are several implications of the overall design of the extended
; attribute support for the FAT file system.
;
;     - When an FEAlist is returned to the user, the minimum length MUST
;       be 4 (which is the size of the feal_cblist field). It is incorrect
;       to return a size of zero since this is contrary to the definition
;       of the feal_cblist field. Therefore, even if there is no disk space
;       occupied by the extended attributes for an object, we will return a
;       size of 4.
;
;     - The order that FEAs are seen during a "set" operation will determine
;       in what order they are stored on disk. This order is purely an
;       implementation order, and has no relevance whatsoever to the nature
;       of the FEAs.  This is entirely specific to the current version and
;       may change in the future.
;
;     - During a "set" operation, because we "delete" first and then
;       "add/replace", the effect is that deletes are overshadowed by
;       add/replace operations.
;
;  Modification History
;  Date      Defect   Discription
;  -------------------------------------------------------------------------
;  08/09/95  127725   Changed DoSetFEAInfo to allocate temporary segment as
;                     HF_FIXED instead of HF_SWAPPABLE.
;
.xlist
.xcref
include fnofs.inc
include dosseg.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include dossymc.inc
include buffer.inc
include dbcs.inc
include dirent.inc
include error.inc
include extattr.inc
include filesys.inc
include inbuf.inc
include vmowner.inc
include mi.inc
include virtsym.inc
include seldesc.inc
include sft.inc
include tkexport.inc
include vpb.inc
include osconst.inc
include pgexport.inc
include vmexport.inc
include codelock.inc
.cref
.list

        ModName extattr,FNO_EXTATTR_ASM


        ReferTask       CurrTCB,WORD    ; Current thread control block

        EXTRN           GDT_VPB:WORD    ; VPB BMP Selector


        EXTRN       AllocateEA:NEAR
        EXTRN       DirFromSFT:NEAR
        EXTRN       DoDiskRead:NEAR
        EXTRN       DoDiskWrite:NEAR
        EXTRN       LockMFT:NEAR
        EXTRN       LockMFTShar:NEAR
        EXTRN       Release:NEAR
        EXTRN       SetupFatEA:NEAR
        EXTRN       SetupForIO_EA:NEAR
        EXTRN       UCase:NEAR
        EXTRN       UnlockMFT:NEAR
        EXTRN       DiskWriteFEAlist:NEAR
        EXTRN       DiskReadFEAlist:NEAR

        EXTRNFAR    EndTransfer
        EXTRNFAR    FlushBuf
        EXTRNFAR    FuByte
        EXTRNFAR    FuWord
        EXTRNFAR    FuBuff
        EXTRNFAR    fVolumeWriteable
        EXTRNFAR    GetTempBuffer
        EXTRNFAR    FNotify_Handle_Wake
        EXTRNFAR    FreeTempBuffer
        EXTRNFAR    ProbeBuff
        EXTRNFAR    RlseBuffr
        EXTRNFAR    SegAlloc
        EXTRNFAR    SegFree
        EXTRNFAR    ShSU
        EXTRNFAR    StrCmp
        EXTRNFAR    SuBuff
        EXTRNFAR    SuWord

        EXTRN32     CodeLockProc
        EXTRN32     CodeUnlockProc


          FARCODE HIGH4
FATEASWAPCODE SEGMENT

;***    GetFEAInfo - Get the extended attributes for an object
;
;       GetFEAInfo gets the extended attributes for an object. The exact
;       attributes returned depend on the InfoLevel requested of the
;       "find" operation.
;       InfoLevel = QINFOLEV_SUB_FEAL means that the FEAlist returned is
;                   selected from the FEAlist of the object by using the
;                   GEAlist passed in by the user.
;       InfoLevel = QINFOLEV_FULL_FEAL means that the full FEAlist for the
;                   object is to be returned.
;
;       NOTE    We have to allocate a buffer of maximum size since we
;               do not know the size of the data to be returned. WE cannot
;               just allocate a buffer as big as the FEAlist since it is
;               possible for the user to pass in a long GEAlist which has
;               very few matching FEAs, and we would have to return "dummy"
;               FEAs with the feal_cbValue field set to zero.
;
;               We lock the MFT in sharing mode to allow more than one
;               "getter". This is OK since we do not modify any of the
;               data for the object.
;
;
;       ENTRY   ES:DI -> SFT
;               DX =     Info Level (Already validated by caller)
;               DS:SI -> Buffer for returned information (InfoBuf)
;                        InfoBuf is an EAOP structure
;                           - eaop_fpGEAlist -> list of GEAs
;                           - eaop_fpFEAlist -> buffer for returned data
;                                          (first WORD of buffer contains
;                                           its size)
;
;       EXIT    'C' if error
;                   AX = error code
;                   if AX = ERROR_BUFFER_OVERFLOW
;                       feal_cblist = size of FEAlist of object
;               'NC' if successful
;                   Information returned in buffer
;
;       USES    AX, BX, CX, DX, SI, DS, Flags
;
;       ALGORITHM
;
;           LockMFTShar()
;           Buf = SegAlloc(64K-1)            /* Max size of buffer needed */
;           if (!error) {
;               DiskReadFEAlist(EAsize,DirEntPtr,SFT,2) /* Get size of FEAlist */
;               if (!error) {
;                   DiskReadFEAlist(Buf,DirEntPtr,SFT,EASize)
;                   if (!error) {
;                       if (infolevel == QINFOLEV_SUB_FEAL) {
;                           GetFEAFromGEAlist(Buf,InfoBuf.GEAlist)
;                           if (error == ERROR_INVALID_EA_NAME)
;                               Set up offError in EAOp
;                       }
;                       DestBuf = InfoBuf.FEAlist
;                       if (DestBuf.feal_cblist >= Buf.feal_cblist) {
;                           SuWord(Buf,DestBuf,Buf.feal_cblist)
;                       }
;                       else {
;                           DestBuf.feal_cblist = Buf.feal_cblist
;                           Error = "buffer overflow"
;                       }
;                   }
;               }
;           SegFree(Buf)
;           }
;           UnlockMFT()
;
Procedure GetFEAInfo,NEAR
    ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar    gfea_sft,DWORD              ; Pointer to SFT
LocalVar    gfea_infobuf,DWORD          ; Pointer to buffer for returned info
LocalVar    gfea_infolev,WORD           ; Infolevel requested
LocalVar    gfea_FEAsiz,DWORD           ; Location for size of FEAlist
LocalVar    gfea_buf,DWORD              ; Pointer to allocated (64K-1) buffer
LocalVar    gfea_bufhandle,WORD         ; Handle of (64K-1) byte segment
LocalVar    gfea_GEABase,WORD           ; offset part of address of GEA List

        CodeLock FATEA_GROUP
        EnterProc   <ES,DI,DS,SI,DX,0,0> ; Init. gfea_sft, gfea_buf
                                            ; gfea_infolev and gfea_FEAsiz
        call    LockMFTShar             ; Put share lock on mft entry for SFT
;
; Set up TCBThisSFT for DiskReadFEAlist and UnlockMFT
;
        mov     bx,TASKAREA:[CurrTCB]
        mov     TASKAREA:[bx].TCBThisSFT.Segmt,ES
        mov     TASKAREA:[bx].TCBThisSFT.Offst,DI
;
; Allocate a (64K-1) byte buffer to read in the FEA list for the object
; We allocate the segment as non-swappable because we already are dealing
; with user selectors that may get swapped out. This way, we can assure
; that our segment is always in memory when we load the selector for it
; into DS or ES.
;
        xor     ax,ax                   ; size, high order word (= 0)
        mov     ds,ax                   ; clear out user sel in seg register
        mov     bx,MAXEABUFSIZE         ; size, low order word (= 64K-1)
        mov     ch,D_DATA0
        mov     cl,SA_GDT               ; SFlags and AFlags

        mov     si,HF_SWAPPABLE OR HF_NONCONTIG
        mov     di,FSEATMPOWNER
        CALLFAR SegAlloc
        mov     ax,ERROR_NOT_ENOUGH_MEMORY  ; In case SegAlloc returned error
        LJC     gfeax                   ; Error. Release buffer and exit
        mov     [gfea_buf].Segmt,bx     ; Save selector to buffer
        xor     di,di
        mov     [gfea_buf].Offst,di     ; Offset is always zero
;
; We have allocated a buffer to hold the FEAlist for the object. Now go and
; read in the FEAlist off the disk.
;
; Read in the size of the FEAlist on disk. This is stored in the first dword
; of the FEAlist. We will then allocate a buffer big enough to hold the
; FEAlist that will be written out to disk. If there is no FEAlist for the
; object, then the value of sfea_FEAsiz will remain zero.
;


        SaveReg <BX,DI>                 ; Save pointer to buffer
        lea     di,TASKAREA:[gfea_FEAsiz]
        mov     bx,ss                   ; BX:DI -> location for size
        les     si,[gfea_sft]
        call    GetFEAlistSize
    assume es:nothing
        RestoreReg <DI,BX>              ; BX:DI -> buffer allocated for FEAlist
        LJC     gfea40                  ; Error. Clean up and return.
;
; Now read in the FEAlist, since we know its size.
;
; sf_fsd.sfdFAT_firEAclus contains the first cluster for the EA list
; BX:DI -> buffer for READ
;
        les     si,[gfea_sft]

.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        mov     cx,[gfea_FEAsiz].loword ; CX = size of FEAlist

;###46681   if info. level is QINFOLEV_FULL_FEAL8 and no eas are present
;###46681   then return an empty fealist.

        cmp     [gfea_infolev], QINFOLEV_FULL_FEAL8   ;###46681 Level 8
        jne     gfea20                                ;###46681 No, normal processing
        cmp     cx, 4                                 ;###46681 If cblist is 4, do not try and match eas
        jne     gfea20                                ;###46681 No, normal processing
        SaveReg <ds,si>                               ;###
        lds     si,[gfea_buf]                         ;###46681 DS:SI -> "New" FEAlist
                                                      ;###46681 Set cblist value to 4
        mov     WORD PTR [si].feal_cblist.hiword, 0   ;###46681 Save cblist
        mov     WORD PTR [si].feal_cblist.loword, cx  ;###46681 Save cblist
        RestoreReg <si,ds>                            ;###

        jmp     gfea25                                ;###46681 Yes, don't match eas

gfea20:
        call    DiskReadFEAlist
        LJC     gfea40                  ; Error. Clean up and return.
;
; Validate the InfoBuffer to make sure that we can get access to the EAOP
; structure in it. Since we don't need to use OffError, we only validate
; access to eaop_fpGEAlist and eaop_fpFEAlist.
;
        mov     bx,gfea_infobuf.Segmt
        mov     si,gfea_infobuf.Offst   ; BX:SI -> InfoBuffer
        mov     dh,ACCTYPE_RW
        mov     cx,(eaop_OffError - eaop_fpGEAlist) ; CX = size to validate.
    .errnz (eaop_fpGEAlist)             ; Ensure eaop_fpGEalist is first field.
        CALLFAR ProbeBuff
        LJC     gfea40                  ; Buffer is not valid. Return error.
;
; If the level of information required is QINFOLEV_SUB_FEAL, we need to
; extract an FEAlist using the FEAlist of the object and the GEAlist passed
; in by the user.
;
        cmp     [gfea_infolev], QINFOLEV_FULL_FEAL8   ;###46681 Level 8
        je      gfea23                                ;###46681 Continue processing
        cmp     [gfea_infolev],QINFOLEV_SUB_FEAL
        jne     gfea30                  ; Return whole FEAlist
;
; The pointer in fpGEAlist has not been verified to be a good pointer. The
; code in GetFEAFromGEAlist will have to be careful to not assume this.
;
gfea23:
        les     di,[gfea_infobuf]
        mov     bx,es:[di].eaop_fpGEAlist.Segmt
        mov     si,es:[di].eaop_fpGEAlist.Offst ; BX:SI -> user GEAlist
        mov     [gfea_GEABase],si       ; save offset of GEA list
        les     di,[gfea_buf]           ; ES:DI -> FEAlist
        call    GetFEAFromGEAlist
        LJC     gfea50                  ; Some sort of error. Process it.
;
; The pointer in fpFEAlist has not been validated yet. The call to FuWord
; will verify it.  GetFEAFromGEAlist could have blocked, so verify gfea
; pointer again.
;
;###
gfea25:
        mov     bx,gfea_infobuf.Segmt
        mov     si,gfea_infobuf.Offst   ; BX:SI -> InfoBuffer
        mov     dh,ACCTYPE_RW
        mov     cx,(eaop_OffError - eaop_fpGEAlist) ; CX = size to validate.
   .errnz (eaop_fpGEAlist)              ; Ensure eaop_fpGEalist is first field.
        CALLFAR ProbeBuff
        jc      gfea40                  ; error, go return
gfea30: mov     ds,[gfea_infobuf].Segmt ; (DS:SI) -> info buffer
        mov     bx,[si].eaop_fpFEAlist.Segmt
        mov     di,[si].eaop_fpFEAlist.Offst    ; BX:DI -> user FEAlist buffer
        lds     si,[gfea_buf]           ; DS:SI -> "New" FEAlist

.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        mov     cx,WORD PTR [si].feal_cblist.loword

;
; Call FuWord to extract the size of the user FEAlist buffer, and see if the
; buffer is big enough to hold the FEAlist that is to be returned.
; NOTE that we assume that the user buffer is as big as the user claims it
; to be. If it is not (i.e. the feal_cblist value is incorrect), then we will
; get a GPFault when copying the FEAlist to the user buffer.
;
        SaveReg <DS,SI,DI,BX>
        xchg    si,di                   ; BX:SI -> User buffer
        push    bx
        CALLFAR FuWord                  ; AX = feal_cblist.loword from user buffer
        pop     bx
        jc      gfea32                  ; Got a GPFault reading user buffer
        mov     dx,ax                   ; DX = feal_cblist.loword from user buffer
        CALLFAR FuWord                  ; AX = feal_cblist.hiword from user buffer
        jc      gfea32                  ; Got a GPFault reading user buffer
        xchg    dx,ax                   ; AX = user's feal_cblist.loword
                                        ; DX = user's feal_cblist.hiword
        dec     dx                      ; user's feal_cblist > 64K?
        sub     dx,-1                   ; set carry if BX != 0
        jnc     gfea32
        mov     ax,ERROR_EA_LIST_TOO_LONG ; set error code
gfea32: RestoreReg <BX,DI,SI,DS>        ; BX:DI -> user buf; DS:SI -> "New" FEAlist
        jc      gfea40
;
; The pointer in fpFEAlist has been "validated".
; AX = user's feal_cblist
; CX = real feal_cblist
;
        cmp     cx,ax
        ja      gfea35                  ; The user buffer is too small

;
; The user buffer is big enough to hold the returned FEAlist. This is assuming
; that the size specified by the user in feal_cblist is correct. Copy over
; the FEAlist from our buffer to the user buffer.
;
; DS:SI -> source buffer
; BX:DI -> user buffer (destination)
; CX = byte count (size of FEAlist)
;
        CALLFAR SuBuff                  ; Copy over the data to the user buffer
        jmp     short gfea40            ; SuBuff sets 'C' if any error

;
; The user buffer is too small. We return the size of the FEAlist
; requested in feal_cblist, and return an error_buffer_overflow.
;
; [gfea_FEAsiz] = FEAlist to return
; BX:DI -> user buffer.
;
gfea35: mov     ax,[gfea_FEAsiz].loword ; AX = loword of size of FEAlist
        push    bx
        CALLFAR SuWord
        pop     bx
        jc      gfea40
        mov     ax,[gfea_FEAsiz].hiword ; AX = hiword of size of FEAlist
        CALLFAR SuWord
        jc      gfea40
        mov     ax,ERROR_BUFFER_OVERFLOW
        STC
;
; Carry is set if any error encountered earlier
;
gfea40: PUSHF                           ; Save any error indication
        SaveReg <AX>                    ; Save any error code
        mov     ax,[gfea_buf].Segmt
        CALLFAR SegFree                 ; Free the (64K-1) byte segment
        RestoreReg <AX>                 ; AX = error code
        POPF
gfeax:  les     di,[gfea_sft]
        call    UnlockMFT               ; Release MFT
        LeaveProc
        CodeUnlock FATEA_GROUP
        ret

;
; We got an error from GetFEAFromGEAlist. If it is ERROR_INVALID_EA_NAME,
; then SI points to the offending GEA. We need to return this in offError
; in the user's EAOp structure.
;
; AX = error code
;
gfea50: cmp     ax,ERROR_INVALID_EA_NAME
        jne     gfea55                  ; Not the error we want. Return.
        SaveReg <AX>                    ; Save error code
        mov     di,[gfea_infobuf].Offst ;
        mov     bx,[gfea_infobuf].Segmt ; BX:DI -> EAOp structure
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        lea     di,[di].eaop_OffError   ; BX:DI -> OffError in user buffer
        xchg    ax,si                   ; AX = offset of bad GEA
        sub     ax,[gfea_GEABase]       ; make OffError an offset into GEAList
        push    bx
        CALLFAR SuWord                  ; Set loword of OffError for return
        pop     bx
        jc      gfea54                  ; error, go return
        xor     ax,ax
        CALLFAR SuWord                  ; Set hiword of OffError for return
gfea54: RestoreReg <AX>

gfea55: stc
        jmp     short gfea40            ; Return to caller

EndProc GetFEAInfo


;***    SetFEAInfo - Set the FEA list for the object onto disk
;
;       NOTE    We just lock the MFT and call DoSetFEAInfo which does all
;               the work for us. So this routine is really just a "stub"
;               that we use to provide a uniform interface for QFileInfo.
;
;       ENTRY   ES:DI -> SFT
;               BX:SI -> Buffer passed as a parameter (InfoBuf)
;                   InfoBuf is an EAOP structure
;                       - eaop_fpGEAlist -> list of GEAs
;                       - eaop_fpFEAlist -> buffer for returned data
;                                      first WORD of buffer contains its size
;                       - eaop_OffError is ignored
;               The MFT should have been locked in EXCLUSIVE mode on entry
;
;       EXIT    'C' indicates an error of some sort
;                   FEAlist is not written to disk
;                   if error = "invalid FEA"
;                       Return offset of invalid FEA in eaop_OffError
;               'NC' indicates success
;                   FEAlist written to disk
;                   SFT marked as "dirty" (for COMMIT)
;
;       USES    AX, BX, CX, DX, SI, DS, Flags
;
;       ALGORITHM
;
;           if (fVolumeWriteable) {
;               LockMFT()
;               DoSetFEAInfo()
;               UnlockMFT()
;           }
;           else
;               return error
;
Procedure SetFEAInfo,NEAR
    ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

        CodeLock FATEA_GROUP
;
; Check to see if this volume is writeable. If not, we don't bother trying
; to set the FEAinfo for the object.
;
; Set TCBThishVPB for fVolumeWriteable
;
        SaveReg <bp>
        mov     bp,CurrTCB
        mov     ax,es:[di].sf_fsi.sfi_hVPB  ; (ax) = hVPB
        mov     TASKAREA:[bp].TCBThishVPB,ax ; Set TCBThishVPB
        CALLFAR fVolumeWriteable        ; Can we write to the volume?
        RestoreReg <bp>
        jc      sfeai10                 ;   NO, exit with error
        call    LockMFT
        mov     ax,EASEXIST             ; EAs for the file/dir may exist
        call    DoSetFEAInfo
        call    UnlockMFT
sfeai10: CodeUnlock FATEA_GROUP
        ret
EndProc SetFEAInfo


;***    DoSetFEAInfo - Set the FEA list for the object onto disk
;
;       DoSetFEAInfo takes the FEA list passed in by the user and applies the
;       necessary operations to the (existing) FEA list for the object.
;       Operations possible are Add, Replace and Delete. The new FEAlist
;       is written out to disk if no errors are encountered.
;       This routine is called by both SETFILEINFO and SETPATHINFO routines.
;
;
;       NOTE    The MFT should have been locked in exclusive mode since
;               we will be changing the data for the object.
;
;
;       ENTRY   AX = EAs exist flag (EASEXIST/EASDONTEXIST)
;                  if AX = 0 then no existing EAs
;                     this is used by NewEntry and by Open(replace)
;                  if AX = 1 then EAs exist for the file/directory
;               ES:DI -> SFT
;               BX:SI -> Buffer passed as a parameter (InfoBuf)
;                   InfoBuf is an EAOP structure
;                       - eaop_fpGEAlist -> list of GEAs
;                       - eaop_fpFEAlist -> buffer for returned data
;                                      first WORD of buffer contains its size
;                       - eaop_OffError is ignored
;               The MFT should have been locked in EXCLUSIVE mode on entry
;
;       EXIT    'C' indicates an error of some sort
;                   FEAlist is not written to disk
;                   if error = "invalid FEA"
;                       Return offset of invalid FEA in eaop_OffError
;               'NC' indicates success
;                   FEAlist written to disk
;                   SFT marked as "dirty" (for COMMIT)
;
;       USES    AX, BX, CX, DX, SI, DS, Flags
;
;       ALGORITHM
;
;           if EASEXIST
;               DiskReadFEAlist(EASize,Dirent,SFT,2)/* Read in size of FEAlist */
;               BufSize += UserFEA.cblist           /* Worst case buffer size */
;           else
;               BufSize = UserFEA.cblist
;           Buf = SegAlloc(BufSize)
;           if (!error) {
;               if EASEXIST
;                   DiskReadFEAlist(Buf,Dirent,SFT,EASize)
;                   if (error)
;                       SegFree(Buf)
;                       return
;               else
;                   initialize Buf to empty EA list
;               UpdateFEAList(Buf,UserFEA)
;               if (error)
;                   eaop_OffError is set
;               else
;                   DiskWriteFEAlist(Buf,SFT,Buf.fea_cblist)
;               SegFree(Buf)
;           }
;
Procedure DoSetFEAInfo,NEAR
    ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar    sfea_eaflag,WORD            ; EAs exist flag
LocalVar    sfea_sft,DWORD              ; Pointer to SFT
LocalVar    sfea_infobuf,DWORD          ; Pointer to buffer for returned info
LocalVar    sfea_FEAsiz,DWORD           ; Location for size of FEAlist
LocalVar    sfea_noof_needeas,WORD      ; count of need eas
LocalVar    sfea_buf,DWORD              ; Pointer to allocated (64K-1) buffer
LocalVar    sfea_bufsize,WORD           ; Size of buffer allocated
LocalVar    sfea_FEABase,WORD           ; offset part of address of FEA list

        CodeLock FATEA_GROUP
;
; Initialize the local variables as follows:
;           eaflag set to entry parameter
;           sft set to entry parameter
;           infobuf set to entry parameter
;           FEAsiz set to 0. This is used if no FEAlist exists for the object.
;
        EnterProc   <AX,ES,DI,BX,SI,0,MINFEALISTSIZE,0>; Init. sfea_eaflag,
                                              ; sfea_sft, sfea_infobuf,
                                              ; sfea_FEAsiz,sfea_noof_needeas
;
; Set up TCBThisSFT for DiskReadFEAlist, DiskWriteFEAlist, and UnlockMFT
;
        mov     bx,TASKAREA:[CurrTCB]
        mov     TASKAREA:[bx].TCBThisSFT.Segmt,ES
        mov     TASKAREA:[bx].TCBThisSFT.Offst,DI
        cmp     ax,EASEXIST             ; EAs?
        jne     sfea08                  ; no, leave MINFEALISTSIZE in sfea_FEAsiz
;
; Read in the size of the FEAlist on disk. This is stored in the first word
; of the FEAlist. We will then allocate a buffer big enough to hold the
; FEAlist that will be written out to disk. If there is no FEAlist for the
; object, then the value of sfea_FEAsiz will remain zero.
;
        lea     di,TASKAREA:[sfea_FEAsiz]
        mov     bx,ss                   ; BX:DI -> location for size
        les     si,[sfea_sft]
        call    GetFEAlistSize
    assume es:nothing
        LJC     sfea50                  ; Error. Clean up and return.

        mov     sfea_noof_needeas,dx    ; save need EA count

; We add the size of the FEAlist passed in by the user to the size of the
; FEAlist for the object. This is the maximum size of the resultant FEAlist
; that we will be writing out to disk during this "set" operation.
; We can then allocate a buffer that big to hold the FEAlist as we build it.
;
sfea08: mov     si,[sfea_infobuf].Offst
        mov     bx,[sfea_infobuf].Segmt ; (BX:SI) -> InfoBuffer
;
; Validate the InfoBuffer to make sure that we can get access to the EAOP
; structure in it. We need access to all the fields in the EAOP structure.
;
        SaveReg <ES>
        mov     dh,ACCTYPE_RW
        mov     cx,size EAOP            ; CX = size to validate.
        CALLFAR ProbeBuff
        RestoreReg <ES>
        jc      sfea06                  ; Buffer is not valid. Return error.
        mov     ds,[sfea_infobuf].Segmt ; (DS:SI) -> InfoBuffer
        mov     bx,[si].eaop_fpFEAlist.Segmt
        mov     si,[si].eaop_fpFEAlist.Offst    ; BX:SI -> User FEAlist
        mov     [sfea_FEABase],si               ; save offset of FEA list
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code

        push    bx
        CALLFAR FuWord                  ; AX = feal_cblist.loword
        pop     bx
        jc      sfea06                  ; GPFault reading user buffer
        mov     dx,ax                   ; DX = feal_cblist.loword
        CALLFAR FuWord                  ; AX = feal_cblist.hiword
        jc      sfea06                  ; GPFault reading user buffer
        dec     ax                      ; user's feal_cblist > 64K?
        sub     ax,-1                   ; set carry if BX != 0
        mov     ax,ERROR_EA_LIST_TOO_LONG ; set error code
        jnc     sfea07                  ; no error, go continue

sfea06: jmp     sfea50                  ; error, go return
;
; The pointer in eaop_fpFEAlist has been "validated".
;
sfea07: mov     cx,[sfea_FEAsiz].loword

;
; CX = size of FEAlist
; DX = size of user FEAlist
; Add the size of the user FEAlist to see what the maximum buffer size will
; be.
;
        add     cx,dx
        jnc     sfea10                  ; CX has the maximum size we will need
;
; Our max size is > 64K. We allocate a buffer of size 64K-1 and hope for the
; best.
;
        mov     cx,MAXEABUFSIZE
;
; Allocate a buffer to set up the FEA list to be written out
; We allocate the segment as non-swappable because we already are dealing
; with user selectors that may get swapped out. This way, we can assure
; that our segment is always in memory when we load the selector for it
; into DS or ES.
;
sfea10: mov     [sfea_bufsize],cx       ; Save size of buffer
        xor     ax,ax
        mov     bx,cx                   ; AX:BX = size of segment to alloc
        mov     ch,D_DATA0
        mov     cl,SA_GDT               ; SFlags and AFlags
                                        ; dx is not used (?)
;127725        mov     si,HF_SWAPPABLE OR HF_NONCONTIG
        mov     si,HF_FIXED             ;127725
        mov     di,FSEATMPOWNER
        CALLFAR SegAlloc
        mov     ax,ERROR_NOT_ENOUGH_MEMORY  ; In case SegAlloc returned error
        LJC     sfea50                  ; Error. go exit
        xor     di,di
        mov     [sfea_buf].Segmt,bx     ; Save selector to buffer
        mov     [sfea_buf].Offst,di     ; Offset is always zero
;
; We have allocated a buffer to hold the FEAlist for the object. Now go and
; read in the FEAlist off the disk.
;
; BX:DI -> buffer for READ
;
        cmp     sfea_eaflag,EASEXIST    ; EAs exist?
        je      sfea20                  ; yes, go read them in
        mov     ds,bx                   ; DS:DI -> FEAlist
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        mov     [di].feal_cblist.loword,MINFEALISTSIZE ; set up empty FEA list
        mov     [di].feal_cblist.hiword,0
        jmp short sfea30                ; go call UpdateFEAlist

sfea20: les     si,[sfea_sft]

.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code

        mov     cx,[sfea_FEAsiz].loword ; Read in whole FEAlist
        call    DiskReadFEAlist
        jc      sfea40                  ; Error. Clean up and return.
;
; Set up the new (modified) FEAlist that we will be writing out to disk.
; DiskReadFEAlist blocked, so we have to verify pointers again.
;
sfea30: mov     si,[sfea_infobuf].Offst
        mov     bx,[sfea_infobuf].Segmt ; (BX:SI) -> InfoBuffer
;
; Validate the InfoBuffer to make sure that we can get access to the EAOP
; structure in it. We need access to all the fields in the EAOP structure.
;
        mov     dh,ACCTYPE_RW
        mov     cx,size EAOP            ; CX = size to validate.
        CALLFAR ProbeBuff
        jc      sfea40                  ; Buffer is not valid. Return error.
        mov     ds,[sfea_infobuf].Segmt ; (DS:SI) -> InfoBuffer
        mov     bx,[si].eaop_fpFEAlist.Segmt
        mov     di,[si].eaop_fpFEAlist.Offst ; BX:DI -> User FEAlist
        lds     si,[sfea_buf]           ; DS:SI -> FEAlist of object
;***** get no of need feas in the original fea list and pass it as input
; to this procedure ; This returns the no of need eas after update
; correctly ---nagara
;*****
        mov     cx,sfea_noof_needeas    ;
        call    UpdateFEAlist
        jc      sfea35                  ; Some type of error. DI -> bad FEA
        les     si,[sfea_sft]           ; ES:SI -> SFT
        lds     di,[sfea_buf]
        mov     bx,ds                   ; BX:DI -> FEAlist to write out
        mov     dx,cx                   ; dx = new no of need eas

.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        mov     cx,[di].feal_cblist.loword ; CX = Length of FEAlist to write out


        call    DiskWriteFEAlist        ; Write it out to disk
        jmp     short sfea40

;
; We got an error returned from UpdateFEAlist. We assume that it was because
; of an invalid FEA in the user FEAlist.
;
; DI = offset of invalid FEA. 'C' set
;
sfea35: SaveReg <AX>                    ; Save error code.
        mov     bx,[sfea_infobuf].Segmt
        mov     ax,[sfea_infobuf].Offst
        xchg    ax,di                   ; BX:DI -> InfoBuf; AX = offset of bad FEA
        sub     ax,[sfea_FEABase]       ; make ax=offset into FEA list
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        lea     di,[di].eaop_OffError   ; BX:DI -> eaop_OffError in InfoBuf
        push    bx
        CALLFAR SuWord                  ; Set loword of offset in user buffer
        pop     bx
        jc      sfea37                  ; error, go return
        xor     ax,ax
        CALLFAR SuWord                  ; Set hiword of OffError for return
sfea37: RestoreReg <AX>
        stc

;
; Carry is set if any error encountered earlier
;
sfea40: PUSHF                           ; Save any error indication
        SaveReg <AX>                    ; Save any error code
        mov     ax,[sfea_buf].Segmt
        CALLFAR SegFree                 ; Free the (64K-1) byte segment
        RestoreReg <AX>                 ; AX = error code
        POPF
sfea50: les     di,[sfea_sft]
        LeaveProc
        CodeUnlock FATEA_GROUP
        ret

EndProc DoSetFEAInfo


;***    DeleteFEA - Delete an FEA from an FEAlist
;
;       DeleteFEA removes an FEA from the FEAlist, packing up the buffer
;       by moving the remaining FEAs up to fill up the empty slot.
;
;       ENTRY   DS:SI -> FEAlist (FEAlist)
;               BX:DI -> FEA whose matching entry to remove (FEA)
;                        (May be in USER space)
;
;       EXIT    'NC' if no error
;                   FEAlist.feal_cblist updated
;               AX = 1 if the fea deleted was a need ea
;               else zero
;               'C' if error (probably GP fault reading USER buffer)
;                   AX = error code
;
;       USES    AX, CX, ES, Flags
;
;       ALGORITHM
;
;           EAPtr = FindFEAbyFEA(FEAlist.feal_list,FEA)
;           if (EAPtr) {
;               Copy all trailing FEAs over one to be deleted
;           }
;
Procedure   DeleteFEA,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <BX,DI,SI>             ; Save pointer to FEAlist and FEA
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        push    word ptr [si].feal_cblist.loword
        call    FindFEAbyFEA
        pop     cx                  ; CX = size of FEAlist

        jc      dfea40              ; Error. Probably GPFault.
        mov     ax,0                ; AX = size of FEA to delete. (Assume FEA not found).
                                        ; DON'T use XOR. Preserve 'Z' flag.
        jnz     dfea20              ; No match found. Cannot delete.
;
; DS:SI -> FEA in FEAlist to delete
; AX = 0
;
        mov     bx,ax
        test    [si].fea_fEA,fea_NeedEA ; is this a need ea ?
        jz      dfea10                  ;
        inc     bx                      ;
; bx = 0 if the ea to be deleted is not a need ea
; bx = 1 if it is a need ea
dfea10: mov     al,[si].fea_cbName
        add     ax,[si].fea_cbValue
        add     ax,FEAHDRLEN+1      ; AX = size of this FEA (to be deleted)
        mov     di,si
        add     di,ax               ; DS:DI -> Next FEA in FEAlist (if any)
        jc      dfea20              ; End of FEAlist
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        sub     cx,di               ; CX = number of bytes (to end of FEAlist) to move

        jbe     dfea20              ; FEA was last one in the FEAlist. No need to move bytes.
;
; CX = number of bytes to transfer to copy over the deleted FEA.
; This transfer is to and from a kernel buffer (no need to worry about selector
; validity.
;
        xchg    si,di               ; DS:SI -> next FEA (source of transfer)
        push    ds
        pop     es                  ; ES:DI -> destination of transfer
        rep     movsb
;
; AX = size of FEA deleted. We need to update the size of the FEAlist to
; take care of any FEA deleted.
;
dfea20: pop     si              ; ds:si ->fealist
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        sub     [si].feal_cblist.loword,ax ; Reduce size by size of FEA just deleted
        or      ax,ax           ;any fea got deleted ?
        jz      dfea30          ; no, return ax = 0
        mov     ax,bx           ;  ax = ((deleted fea == need ea))
dfea30: pop     di
        pop     bx             ;  BX:DI -> FEA
        ret

;
; We got an error and were unable to delete the FEA. In this case,
; there is no need to update feal_cblist.
;
; 'C' set to indicate error. AX has error code.
;
dfea40: RestoreReg      <si,di,bx>      ; DS:SI -> FEAlist; BX:DI -> FEA
        ret

EndProc DeleteFEA



;***    AppendFEA - Append an FEA to an FEAlist
;
;       AppendFEA appends an FEA to the FEAlist. It ensures that the
;       fea_cbName field is not zero (which is invalid).
;       If we are appending to an empty list, the feal_cblist field is
;       already set up correctly to be 4. So, no special-casing need
;       be done for that situation.
;
;       ENTRY   DS:SI -> FEAlist (FEAlist)
;               BX:DI -> FEA that is to be appended (FEA)
;                        (May be in USER space)
;
;       EXIT    'C' if error
;                   Buffer overflow
;                   Name in FEA is invalid
;                   AX = error code
;               'NC' if success
;                   FEAlist.feal_cblist updated
;                   AX = 1 if a need ea was added else 0
;       USES    AX, BX, CX, Flags
;
;
;       ALGORITHM
;
;           if (FEA.fea_cbName == 0)
;               return("error invalid FEA")
;           Copy new FEA at end of FEAlist
;           if (!error) {
;               CopyEANameString(MvDestPtr,MvDestPtr)   /* Upper-case name */
;               FEAlist.feal_cblist = FEAlist.feal_cblist + MvBytCnt
;           }
;
Procedure   AppendFEA,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <DX>                ; Preserve DX around the call
        SaveReg <SI,DI,BX>          ; Save entry pointers
        SaveReg <DS>
;
; Get the values of fea_cbName and fea_cbValue for the FEA that is to be
; appended.
;
        call    GetFEAsizes
        jc      afea50              ; Protection fault
;
; AX = fea_cbValue for the FEA to be appended
; CX = fea_cbName
; DS:SI -> FEAlist (FEAlist)
; BX:DI -> FEA that is to be appended (FEA)
;
        JCXZ    afea45              ; fea_cbName = 0. This is invalid.
        push    ds
        pop     es
        xchg    si,di               ; BX:SI -> FEA, ES:DI -> FEAlist
        add     cx,ax               ; CX = fea_cbName + fea_cbValue
        add     cx,FEAHDRLEN+1      ; CX = size of FEA to append
;
; Check to make sure that the final FEAlist will not exceed 64K-1 bytes.
;
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        mov     ax,es:[di].feal_cblist.loword

        add     ax,cx               ; AX = final size of FEAlist
        mov     ax,ERROR_EA_LIST_TOO_LONG
        jc      afea50              ; Too many FEAs. Return error.
;
; Size will be within limits. Copy the FEA over to the end of the FEAlist.
;
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        mov     di,es:[di].feal_cblist.loword  ; ES:DI -> End of FEAlist

                                    ; BX:SI -> source of transfer (FEA to append)
        SaveReg <bx,cx,si,di,ds>
        CALLFAR FuByte              ; AL = fea_fEA; BX destroyed; SI advanced
        RestoreReg      <ds,di,SI,cx,BX>                ;
        jc      afea50              ; GPFault. return to caller

        xor     ah,ah               ; (ax) = fea_fEA
        SaveReg <AX,DI,CX>          ; Save offset of append position
        CALLFAR FuBuff              ; Copy the FEA over to the end of FEAlist
        RestoreReg <CX,DI,AX>       ; Restore pointer to FEA just appended
        jc      afea50              ; GPFault. Return to caller.

        RestoreReg <DS>             ; DS:DI -> FEA at end of FEAlist
;
; We have copied over the FEA to the end of the FEAlist. We now convert the
; name to upper-case before we store it out on disk. This gives us a consistent
; state, since names are not case-sensitive.
; We achieve the upper-casing by calling CopyEANameString with the same
; source and destination pointers.
;
; DS:DI -> FEA to be transformed
; CX = size of FEA just appended
;
; AX = fea_fEA of the newly added FEA
;
        push    ds
        pop     es
        mov     dx,ax               ; (dx) = fea_fEA
        mov     al,[di].fea_cbName  ; AL = length of string excluding NULL
        lea     di,[di].fea_szName
        mov     si,di               ; DS:SI = ES:DI -> FEA.fea_szName
        CALLFAR CopyEANameString    ; Upper-case the name. (AX = any error code)
        RestoreReg <BX,DI,SI>       ; DS:SI -> FEAlist, BX:DI -> FEA of input
        jc      afeax               ; Invalid char found in FEA name

;
; Now update the length of the FEAlist.
;
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        add     [si].feal_cblist.loword,cx ; Increase size by size of FEA just added

; set ax = 1 if the FEA added was a need EA

        xor     ax,ax               ; (ax) = 0, assume no NEED EAs
        test    dx,fea_NeedEA       ; Is the EA a NEED EA?
        jz      afeax               ; not a need EA, go exit

        mov     ax,1
        jmp     short afeax         ; need EA, go exit

;
; We found that the fea_cbName field of the FEA to be appended was zero. This
; is invalid.
;
afea45: mov     ax,ERROR_EA_LIST_INCONSISTENT
        stc
;
; This is the exit point for any errors encountered. Errors include "invalid
; FEA", "GPFault reading user data".
; We clean up the stack and return to the caller.
;
;;;;BUGBUG 11-Feb-1988 Rajens - Do we need to return these or can we discard
;;;;                            them?
afea50: pop     ds
        pop     bx
        pop     di
        pop     si
afeax:  RestoreReg <dx>
        ret

EndProc AppendFEA


;***    GetFEAsizes - Get fea_cbName and fea_cbValue of an FEA in user space
;
;       GetFEAsizes uses FuByte and FuWord to get the cbName and cbValue
;       fields in an FEA. The FEA may be in USER space.
;
;       ENTRY   BX:DI -> FEA
;
;       EXIT    'C' if protection violation
;                   AX = error code
;               'NC' if success
;                   AX = fea_cbValue
;                   CX = fea_cbName
;
;       USES    AX, CX, Flags
;
Procedure   GetFEAsizes,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <DS,SI,DI>
        xchg    di,si
        lea     si,[si].fea_cbName      ; BX:SI -> fea_cbName field
        push    bx
        CALLFAR FuByte                  ; AL = fea_cbName; BX destroyed; SI advanced
        pop     bx
        jc      gfsx                    ; Protection fault
;
; AL = fea_cbName from FEA
; BX:SI -> fea_cbValue (i.e. SI was advanced
;
        xor     cx,cx
        xchg    cl,al                   ; CX = fea_cbName (for return)
    .errnz (fea_cbValue - fea_cbName - 1)
        push    bx
        CALLFAR FuWord                  ; AX = fea_cbValue
        pop     bx
gfsx:   RestoreReg <DI,SI,DS>
        ret
EndProc GetFEAsizes

CpuMode 386

;***    UpdateFEAlist - Update an FEA list with another one
;
;       UpdateFEAlist is used to update an existing FEAlist for an item
;       with one passed by the user during a "set info" operation. It also
;       handles the case where the existing list is empty.
;
;
;       IMPORTANT NOTES
;
;       The "update" operation is done in two stages:
;
;           - Delete -  The SetFEAlist is examined, one FEA at a time, to see
;                       if the fea_cbValue field is 0. If it is then the
;                       matching FEA has to be deleted.
;           - Add/Replace - Once the deletions have been completed, then we
;                       scan the SetFEAlist again and, for each FEA that does
;                       not have an fea_cbValue field of 0, we try and delete
;                       the matching FEA. The new one is appended to the
;                       FEAlist. (If there was no match, then the delete would
;                       not do anything.
;
;       A side-effect of this method is that if we have multiple FEAs in the
;       SetFEAlist with the same name, then the deletes will be done before
;       the add/replace. Also, the last matching FEA for an add/replace will
;       be the one that is set in the FEAlist.
;
;
;       ENTRY   DS:SI -> Current FEAlist for item (OrigFEA)
;               BX:DI -> User FEA list (SetFEA) (In USER space)
;                        (The pointer has already been "validated", i.e.
;                         the selector is valid)
;               CX    = NO OF NEED EAS IN ORIGINAL FEA
;
;       EXIT    'NC' if success
;                   OrigFEA -> transformed FEAlist
;                   CX = no of need eas after update
;               'C' if error of some sort
;                   DI = Offset of invalid FEA in SetFEA
;                   Error code
;
;       USES    EAX, CX, EDX, ES, Flags
;
;       ALGORITHM
;
;           /*
;            * Do the "delete" operations first
;            */
;           for each FEA in SetFEA {
;               if (fea_cbName == 0)
;                   return(ERROR_EA_LIST_INCONSISTENT,SetFEAPtr)
;               if (fea_cbValue == 0) {
;                   DeleteFEA(OrigFEA,FEA)
;               }
;           }
;           /*
;            * Now do the "add" and "replace" operations
;            */
;           For each FEA in SetFEA {
;               if (fea_cbValue != 0) {
;                   DeleteFEA(OrigFEA,FEA)
;                   AppendFEA(OrigFEA,FEA)
;                   if (error)
;                       return error
;               }
;           }
;
Procedure   UpdateFEAlist,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar        ufl_needea_count,WORD   ; count of need eas  in original list

        EnterProc       <cx>

        SaveReg <SI>
        SaveReg <EDI>

.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        push    si
        lea     si,[di].feal_cblist         ; BX:SI -> user FEAlist.feal_cblist
        SaveReg <ds,bx>
        CALLFAR FuWord                      ; AX = size of SetFEA
        RestoreReg <bx,ds>                  ; BX = segment of user FEAlist
        pop     si
        jc      ufea80                      ; PRotection violation
        movzx   eax,ax
        mov     edx,eax                     ; DX = size of SetFEA
        movzx   edi,di
        add     edx,edi                     ; EDX = offset of end of FEAlist
        lea     di,[di].feal_list           ; BX:DI -> first FEA to set
ufea10: cmp     edx,edi                     ; Are we at end of FEAlist?

        je      ufea40                      ; Yes. Done with "deletes".
        jb      badFEA                      ; cblist is too small. Return error
        call    GetFEAsizes                 ; AX = cbValue, CX = cbName
        jc      ufea80                      ; GPFault reading user space
        JCXZ    badFEA                      ; fea_cbName = 0. Report error.
        or      ax,ax                       ; Is this FEA to be deleted?
        jnz     ufea20                      ; No. Get next one.
        SaveReg <cx,ax>
        call    DeleteFEA
        jc      ufeaerr
        sub     ufl_needea_count,ax         ; ax = 1 if a need ea was deleted
        RestoreReg <ax,cx>                  ; Restore sizes of FEA fields
ufea20: add     ax,cx                       ; AX = cbName + cbValue
        add     ax,FEAHDRLEN+1              ; (FEA header + NULL)
        movzx   eax,ax
        add     edi,eax                     ; ES:DI -> next FEA if any
        jmp     short ufea10

;
; Now scan the list for any FEAs to add
;
; DX = offset of end of FEAlist
;
ufea40: RestoreReg <DI>                     ; ES:DI -> SetFEA
        SaveReg <DI>
        lea     di,[di].feal_list           ; ES:DI -> first FEA to set
        movzx   edi,di
ufea50: cmp     edi,edx
        jae     ufea80                      ; End of list. Carry clear. Exit.
        call    GetFEAsizes                 ; AX = cbValue, CX = cbName
        jc      ufea80                      ; GPFault reading user space
        or      ax,ax                       ; Is this FEA to be deleted?
        jz      ufea60                      ; No. Get next one.
        SaveReg <ax,cx>
        call    DeleteFEA
        jc      ufeaerr                     ; Error. Probably GPFault.
        sub     ufl_needea_count,ax         ; ax = 1 if a need ea was deleted
        call    AppendFEA
        jc      ufeaerr                     ; We have a bad FEA in SetFEA (AX = error code)
        add     ufl_needea_count,ax         ;
        RestoreReg <cx,ax>                  ; Restore AX = cbValue, CX = cbName
ufea60: add     ax,cx
        add     ax,FEAHDRLEN+1              ; (FEA header + NULL)
        movzx   eax,ax
        add     edi,eax                     ; ES:DI -> next FEA if any
        jmp     short ufea50

ufea80: RestoreReg <EDI>                    ; ES:DI -> SetFEA
        RestoreReg <SI>                     ; DS:SI -> FEAlist
ufeaxt: mov     cx,ufl_needea_count         ;
        LeaveProc
        RET

;
; We got an error. Preserve the error code in AX, and clean stack.
;
ufeaerr:pop     ecx
        pop     cx
        jmp     short errx
;
; ES:DI -> invalid FEA
;
badFEA: mov     ax,ERROR_EA_LIST_INCONSISTENT
errx:   pop     esi                         ; Clean stack
        pop     si                          ; DS:SI -> FEAlist
        STC                                 ; Return error indication
        jmp     short ufeaxt

EndProc UpdateFEAlist


;***    FindGEAbyFEA - Find a GEA that matches a particular FEA name
;
;       FindGEAbyFEA scans a GEAlist looking for a name that matches a
;       particular FEA name.
;       Names are not considered case-sensitive. Since the GEAlist is
;       in USER space, we copy over the name into a temporary buffer.
;       There is no need to upper-case the name since StrCmp does a
;       case-insesitive compare.
;
;       ENTRY   BX:SI -> GEAlist (GEAlist) (In USER space - not validated)
;               ES:DI -> FEA to match (FEA)
;
;       EXIT    'C' if error
;                   AX has error code
;               'NC' if no error
;                   'NZ' if not found
;                   'Z' if found
;                       DS:SI -> GEA found
;
;       USES    EAX, BX, CX, EDX, DS,ESI, Flags
;
;       ALGORITHM
;
;           FoundFlg = FALSE
;           NameBuf = GetTempBuffer()
;           for each GEA in GEAlist {
;               if ( GEA.gea_cbName == FEA.fea_cbName) {
;                   Copy GEA.szName into NameBuf    /* Include the NULL */
;                   if (!error) {
;                       if (StrCmp(NameBuf, FEA.fea_szName)) {
;                           FoundFlg = TRUE
;                           break
;                       }
;                   }
;                   else
;                       break                   /* Error - Probably GPFault */
;               }
;           }
;           FreeTempBuffer(NameBuf)
;           return(FoundFlg,GEA)
;
Procedure FindGEAbyFEA,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar    fgbf_GEAlist,DWORD
LocalVar    fgbf_FEA,DWORD
LocalVar    fgbf_tempbuf,DWORD

        EnterProc <BX,SI,ES,DI>     ; Initialize local vars
        SaveReg <ES,DI>             ; Save pointer to FEA
        CALLFAR GetTempBuffer
        mov     [fgbf_tempbuf].Segmt,ES
        mov     [fgbf_tempbuf].Offst,DI
;
; Get the size of the GEAlist and, at the same time, this will verify that
; the user selector is valid.
;
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code

        SaveReg <BX,ES,SI>
   .errnz (geal_cblist)             ; BX:SI -> geal_cblist
        push    bx
        CALLFAR FuWord              ; AX = geal_cblist.loword
        pop     bx
        jc      fgbf5               ; Got a GP fault reading user data
        mov     dx,ax               ; DX = geal_cblist.loword
        CALLFAR FuWord              ; AX = geal_cblist.hiword
        jc      fgbf5               ; Got a GP fault reading user data
        dec     ax                  ; user's feal_cblist > 64K?
        sub     ax,-1               ; set carry if BX != 0
        mov     ax,ERROR_EA_LIST_TOO_LONG ; set error code
fgbf5:  RestoreReg <SI,ES,BX>
        jc      fgbf40              ; Got a GP fault reading user data
;
; (DX) = geal_cblist.loword
;
        movzx   esi,si
        movzx   edx,dx
        add     edx,esi             ; EDX = offset of end of GEAlist
        lea     si,[si].geal_list   ; BX:SI -> first GEA in list
        les     di,[fgbf_FEA]       ; ES:DI -> FEA
        xor     cx,cx
        mov     cl,es:[di].fea_cbName
;
; DX = offset of end of GEAlist
; CL = CX = length of name to match (excluding NULL)
; BX:SI -> GEA in GEAlist
;
fgbf10: cmp     esi,edx             ; Reached end of GEAlist?
        je      fgbf30              ; Yes. FEA not found. 'C' clear.
        ja      fgbf60              ; cblist too small. Return error.
;
; In order to compare the lengths of the name strings, we need to get the
; gea_cbName field for this GEA. Since this is in user space, we use FuByte
; to do it.
;
        SaveReg <BX,ES,ESI>
        lea     si,[si].gea_cbName
        CALLFAR FuByte              ; AL = gea_cbName
        RestoreReg <ESI,ES,BX>
        jc      fgbf40              ; Got a GP fault reading user data
        cmp     cl,al
        jnz     fgbf20              ; Length of name does not match
;
; The name lengths match. We now copy over the GEA into the temp kernel
; buffer before we do the comparison. We have to use FuBuff to do it
; since the GEA is in USER space. Further manipulations will be easy
; because the GEA will be in kernel space.
;
        SaveReg <CX,ESI,BX>         ; BX:SI -> GEA to copy to kernel space
        les     di,[fgbf_tempbuf]
        add     cx,2                ; CX = size of GEA (including NULL)
        CALLFAR FuBuff              ; Copy GEA into kernel space
        RestoreReg <BX,ESI,CX>      ; BX:SI -> GEA in GEAlist
        jc      fgbf40              ; Got a GP fault reading user data
        SaveReg <ESI>
        lds     si,[fgbf_tempbuf]
        lea     si,[si].gea_szName  ; DS:SI -> GEA name to examine
        les     di,[fgbf_FEA]
        lea     di,es:[di].fea_szName   ; ES:DI -> FEA name to match
        CALLFAR StrCmp              ; Do a case-insensitive compare
        RestoreReg <ESI>
; this is ok because we haven't blocked since calling fubyte above.
        mov     ds,bx               ; DS:SI -> GEA in GEAlist
        mov     al,[si].gea_cbName  ; AL = Length of name from kernel buffer
        CLC
        jz      fgbf40              ; Name matches. Found GEA
;
; BX:SI -> GEA just examined.
; AL = gea_cbName of GEA examined.
;
; Check to ensure that gea_cbName is not zero - this means error.
;
fgbf20: xor     ah,ah               ; AX = length of name
        or      ax,ax
        jz      fgbf50              ; Invalid length of name. Return error.
        add     ax,2                ; sizeof(gea_cbname+NULL at end of name) = 2
        movzx   eax,ax
        add     esi,eax             ; DS:SI -> next GEA
        jmp     short fgbf10        ; Try next GEA if any

fgbf30: or      sp,sp               ; No match found. Return 'NZ'
fgbf40: PUSHF
        les     di,[fgbf_tempbuf]
        CALLFAR FreeTempBuffer
        POPF                        ; Restore any error indications
        RestoreReg <DI,ES>          ; ES:DI -> FEA
        LeaveProc
        ret

fgbf50: mov     ax,ERROR_INVALID_EA_NAME
        jmp     short fgbf70

fgbf60: mov     ax,ERROR_EA_LIST_INCONSISTENT
fgbf70: STC                                 ; Return error indication
        jmp     short fgbf40

EndProc FindGEAbyFEA



;***    FindFEAbyGEA - Find an FEA that matches a particular GEA name
;
;       FindFEAbyGEA scans an FEAlist looking for a name that matches a
;       particular GEA name.
;       Names are not considered case-sensitive. Since the GEA to match is
;       in USER space, we copy over the name into a temporary buffer. There
;       is no need to upper-case the name since StrCmp does a case-insesitive
;       compare.
;
;       ENTRY   ES:DI -> FEAlist (FEAlist)
;               BX:SI -> GEA to match (MatchGEA) (In USER space)
;
;       EXIT    'C' if error
;               'NC' if no error
;                   'NZ' if not found
;                   'Z' if found
;                       ES:DI -> FEA found
;
;       USES    AX, CX, DI, DS, ES, Flags
;
;       ALGORITHM
;
;           FoundFlg = FALSE
;           NameBuf = GetTempBuffer()
;           NmLen = GEA.gea_cbName
;           Copy GEA.gea_szName into NameBuf    /* Include the NULL */
;           if (!error) {
;               for each FEA in FEAlist {
;                   if (FEA.fea_cbName == NmLen) {
;                       if (StrCmp(NameBuf, FEA.fea_szName)) {
;                           FoundFlg = TRUE
;                           break
;                       }
;                   }
;               }
;           }
;           FreeTempBuffer(NameBuf)
;           return(FoundFlg,FEA)
;
Procedure FindFEAbyGEA,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <BX,SI>             ; Save pointer to GEA for return
        SaveReg <ES,DI>
        CALLFAR GetTempBuffer       ; ES:DI -> temporary buffer
;
; Get the value of gea_cbName by using FuByte. This is necessary since the
; GEA is in user space.
;
        SaveReg <ES,BX>
        lea     si,[si].gea_cbName  ; BX:SI -> gea_cbName
        CALLFAR FuByte              ; AL = gea_cbName; SI advanced; BX destroyed
        RestoreReg <BX,ES>
        jc      ffbg05              ; Got a GP fault. Clean stack and return.
        xor     ah,ah               ; AX = gea_cbName
;
; Copy over the name from the GEA into the temporary buffer. Include the NULL.
;
; BX:SI -> GEA.gea_szName (user space)
; ES:DI -> temp buffer for name (kernel space)
; CX = length of gea_szName (excluding NULL)
;
        mov     cx,ax
        inc     cx                  ; CX = length of GEA name including NULL
    .errnz (gea_szName - gea_cbName - 1)
        SaveReg <ES,DI,CX>
        CALLFAR FuBuff
        RestoreReg <CX,DI,ES>       ; ES:DI -> temp buffer
ffbg05: RestoreReg <SI,DS>          ; DS:SI -> FEAlist
        jc      ffbg40              ; Error. Probably a GPFault.
;
; ES:DI -> NULL terminated name to match (temp buffer)
; DS:SI -> FEAlist
; CX = length of name (including NULL)
;
        dec     cx                  ; Make CX = length excluding NULL
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code

        mov     bx,[si].feal_cblist.loword ; BX = size of FEAlist
        add     bx,si               ; BX = offset of end of FEAlist

        lea     si,[si].feal_list   ; DS:SI -> first FEA in list
;
; Loop through the FEAlist, looking for a name that matches the name in
; the temp buffer.
;
; DS:SI -> FEA to examine
; BX = offset of end of FEAlist
;
ffbg10: cmp     si,bx               ; Reached end of FEAlist?
        je      ffbg30              ; Yes. FEA not found. 'C' clear.
        ja      ffbg60              ; cblist too small. Return error.
        cmp     cl,[si].fea_cbName
        jnz     ffbg20              ; Length of name does not match
        SaveReg <si>
        lea     si,[si].fea_szName  ; DS:SI -> FEA name to examine
        CALLFAR StrCmp              ; Do a case-insensitive compare
        RestoreReg <si>             ; DS:SI -> FEA
        jz      ffbg40              ; Found a match. Return it to user.
ffbg20: xor     ax,ax
        mov     al,[si].fea_cbName
        add     ax,[si].fea_cbValue
        add     ax,FEAHDRLEN+1      ; FEA header + NULL at end of name
        add     si,ax               ; DS:SI -> next FEA
        jmp     short ffbg10        ; Try next FEA

;
; There was no match found. Return 'NZ' and 'NC' to the caller.
;
ffbg30: or      sp,sp

;
; ES:DI -> temp buffer
; 'C' set if error ('NC' if no error)
; 'Z' if match found ('NZ' if no match)
; DS:SI -> FEA that matched (if any)
;
ffbg40: PUSHF                       ; Save any error indications
        CALLFAR FreeTempBuffer
        POPF
        jc      ffbg50              ; No match found
        jnz     ffbg50              ; No match found
;
; We found a matching FEA. Return the pointer in ES:DI to the caller.
;
        push    ds
        pop     es
        xchg    di,si               ; ES:DI -> FEA that matched
ffbg50: RestoreReg <SI,BX>          ; BX:SI -> GEA
        ret

ffbg60: mov     ax,ERROR_EA_LIST_INCONSISTENT
        STC                                 ; Return error indication
        jmp     short ffbg40

EndProc FindFEAbyGEA



;***    FindFEAbyFEA - Find an FEA that matches a particular FEA name
;
;       FindFEAbyFEA scans an FEAlist looking for a name that matches a
;       particular FEA name.
;       Names are not considered case-sensitive. Since the FEA to match
;       is in USER space, we copy over th name into a temporary buffer.
;       There is no need to upper-case the name since StrCmp does a
;       case-insesitive compare.
;
;       ENTRY   DS:SI -> FEAlist (FEAlist)
;               BX:DI -> FEA to match (MatchFEA) (May be in USER space)
;
;       EXIT    'C' if error
;               'NC' if no error
;                   'Z' if found
;                       DS:SI -> FEA found
;                   'NZ' if not found
;
;       USES    EAX, CX, ESI, ES, Flags
;
;       ALGORITHM
;
;           FoundFlg = FALSE
;           NameBuf = GetTempBuffer()
;           NmLen = MatchFEA.fea_cbName         /* Include the NULL */
;           Copy MatchFEA.fea_szName into NameBuf
;           if (!error) {
;               for each FEA in FEAlist {
;                   if (FEA.fea_cbName == MatchFEA.fea_cbName) {
;                       if (StrCmp(NameBuf, FEA.fea_szName)) {
;                           FoundFlg = TRUE
;                           break
;                       }
;                   }
;               }
;           }
;           FreeTempBuffer(NameBuf)
;           return(FoundFlg,FEA)
;
Procedure FindFEAbyFEA,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <EBX,DI>            ; Save pointer to FEA for return
        SaveReg <DS,SI>
        xchg    di,si               ; BX:SI -> FEA to match
        CALLFAR GetTempBuffer       ; ES:DI -> temporary buffer
;
; Get the value of fea_cbName by using FuByte. This is necessary since the
; FEA is in user space.
;
        SaveReg <BX,ES>
        lea     si,[si].fea_fEA     ; BX:SI -> fea_fEA
   .errnz (fea_cbName - fea_fEA - 1)  ; Ensure that they are adjacent
        CALLFAR FuWord              ; AH = fea_cbName; AL = fea_fEA
                                    ; SI advanced; BX destroyed
        xchg    ah,al               ; AL = fea_cbName; AH = fea_Reserved (must be zero)
        RestoreReg <ES,BX>
        jc      ffbf05              ; Got a GP fault. Clean stack and return.

        test    ah,NOT fea_NeedEA   ; only this bit can be set now
        jz      ffbf03              ; fea_fEA is OK.
        stc                         ; fea_fEA contains illegal value
        mov     ax,ERROR_EA_LIST_INCONSISTENT
        jmp     short ffbf05        ; fea_Reserved not equal to zero. Return error.
;
; Copy over the name from the FEA into the temporary buffer. Include the NULL.
;
; BX:SI -> fea_cbValue of FEA to match
; AL = fea_cbName
; ES:DI -> temp buffer to hold name
;
ffbf03: add     si,(fea_szName - fea_cbValue)   ; BX:SI -> fea_cbName to copy
        xor     ah,ah               ; AX = fea_cbName
        mov     cx,ax
        inc     cx                  ; CX = length of FEA name including NULL
        SaveReg <ES,DI,CX>
        CALLFAR FuBuff
        RestoreReg <CX,DI,ES>       ; ES:DI -> temp buffer
ffbf05: RestoreReg <SI,DS>          ; DS:SI -> FEAlist
        jc      ffbf40              ; Error. Probably a GPFault.
;
; ES:DI -> NULL terminated name to match (temp buffer)
; DS:SI -> FEAlist
; CX = length of name (including NULL)
;
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        dec     cx                  ; Make CX = length excluding NULL
        mov     bx,[si].feal_cblist.loword ; BX = size of FEAlist
        movzx   ebx,bx
        movzx   esi,si
        add     ebx,esi             ; BX = offset of end of FEAlist
        lea     si,[si].feal_list   ; DS:SI -> first FEA in list

;
; Loop through the FEAlist, looking for a name that matches the name in
; the temp buffer.
;
; DS:SI -> FEA to examine
; BX = offset of end of FEAlist
;
ffbf10: cmp     esi,ebx             ; Reached end of FEAlist?
        je      ffbf30              ; Yes. FEA not found. 'C' clear.
        ja      ffbf60              ; cblist too small. Return error.
        cmp     cl,[si].fea_cbName
        jnz     ffbf20              ; Length of name does not match
        SaveReg <esi>
        lea     si,[si].fea_szName  ; DS:SI -> FEA name to examine
        CALLFAR StrCmp              ; Do a case-insensitive compare
        RestoreReg <esi>            ; DS:SI -> FEA
        CLC
        jz      ffbf40              ; Name matches. Found FEA
ffbf20: xor     ax,ax
        mov     al,[si].fea_cbName
        add     ax,[si].fea_cbValue
        add     ax,FEAHDRLEN+1      ; FEA header + NULL at end of name
        movzx   eax,ax
        add     esi,eax             ; DS:SI -> next FEA
        jmp     short ffbf10

ffbf30: OR      sp,sp               ; No match found. Return 'NZ'
;
; ES:DI -> temp buffer
; 'C' set if error ('NC' if no error)
; 'Z' if match found ('NZ' if no match)
;
ffbf40: PUSHF                       ; Save any error indications
        CALLFAR FreeTempBuffer
        POPF
        RestoreReg <DI,EBX>         ; ES:DI -> FEA
        ret

ffbf60: mov     ax,ERROR_EA_LIST_INCONSISTENT
        STC                                 ; Return error indication
        jmp     short ffbf40

EndProc FindFEAbyFEA


;***    GetFEAFromGEAlist - Extract an FEAlist given a GEAlist
;
;       GetFEAFromGEAlist extracts an FEAlist from a given FEAlist using
;       a GEAlist. The extracted list is returned in the same buffer as
;       the original FEA list.
;       For GEAs for which there is no matching FEA with the same name,
;       we return an FEA with the name but with a zero-length value field.
;
;
;       ENTRY   BX:SI -> GEAlist (GEAlist) (In USER space)
;                        (The pointer has not been "validated" and could
;                         be a bogus pointer).
;               ES:DI -> FEAlist (FEAlist)
;
;       EXIT    'NC' if successful
;                   FEAPtr -> Extracted list
;               'C' if error
;                   AX = error code
;                   if AX == ERROR_INVALID_EA_NAME
;                       SI -> Offending GEA (to be returned in offError)
;
;       USES    EAX, BX, CX, EDX, EDI, ESI, DS, ES, Flags
;
;       ALGORITHM
;
;           /*
;            * Delete all FEAs that do not match the GEAs in GEAlist
;            */
;           for each FEA in FEAlist {
;               FindGEAbyFEA(FEA,GEAlist)
;               if (not found)
;                   DeleteFEA(FEAlist,FEA)
;           }
;           /*
;            * Return FEAs with zero in fea_cbValue for non-matched GEAs
;            */
;           DummyFEA = GetTempBuffer()
;           for each GEA in GEAlist {
;               FindFEAbyGEA(GEA,FEAlist)
;               if (not found) {
;                   MakeDummyFEA(DummyFEA)
;                   AppendFEA(FEAlist,DummyFEA)
;                   if (error)
;                       break
;               }
;           }
;           FreeTempBuffer(DummyFEA)
;           return any error indication
;
Procedure   GetFEAFromGEAlist,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar    gffg_GEAlist,DWORD          ; Pointer to user GEAlist
LocalVar    gffg_FEAlist,DWORD          ; Pointer to FEAlist
LocalVar    gffg_TempBuf,DWORD          ; Pointer to kernel buffer

        EnterProc <BX,SI,ES,DI>         ; Initialize local vars
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
        mov     dx,es:[di].feal_cblist.loword  ; DX = size of FEAlist
        movzx   edx,dx
        movzx   edi,di
        add     edx,edi                 ; DX = offset of end of FEAlist

        lea     di,es:[di].feal_list    ; ES:DI -> first FEA
;
; Loop through the FEAlist examining each FEA and trying to find a matching
; GEA in the GEAlist. These are the ones that the user is interested in. The
; ones that have no matching GEA have to be deleted.
;
gffg10: cmp     edi,edx
        je      gffg30                  ; End of list. 'C' clear.
        LJA     gffg90                  ; cblist too small. Return error
        xor     cx,cx
        mov     cl,es:[di].fea_cbName
        add     cx,es:[di].fea_cbValue
        add     cx,FEAHDRLEN+1          ; CX = size of this FEA
        SaveReg <BX,SI,EDX,CX>
        call    FindGEAbyFEA            ; See if there is a matching FEA
        RestoreReg <CX,EDX,SI,BX>
        LJC     gffg80                  ; Error. AX = error code.
        jnz     gffg20                  ; No match. Delete the FEA.
;
; There was a match. We advance the pointer to the next FEA and continue.
; BX = size of this FEA
;
        movzx   ecx,cx
        add     edi,ecx                 ; ES:DI -> Next FEA if any
        jmp     short gffg10            ; Try next FEA

;
; We did not find a match. Delete the FEA at the current position.
; Since we are deleting the "current" FEA from the list, we will be pointing
; to the next one (which will have been moved to the current position) so
; there is no need to advance the pointer after the delete has been done.
; We have to adjust the size of the FEAlist to reflect the change in size.
;
; CX = size of FEA to be deleted
; DX = size of FEAlist (before "delete" operation)
; BX:SI -> GEAlist
; ES:DI -> FEA that matched
;
gffg20: SaveReg <es,bx,si>
        sub     dx,cx                   ; DX = new size of FEAlist
        lds     si,[gffg_FEAlist]
        mov     bx,es                   ; BX:DI -> FEA that matched
        call    DeleteFEA               ; ES:DI -> "current" FEA (== "next" FEA)
        RestoreReg <si,bx,es>           ; BX:SI -> GEAlist
        jc      gffg80
        jmp     short gffg10            ; Check next FEA

;
; For the GEAs that have no matching FEA in the FEAlist, we create dummy
; FEAs with a fea_cbValue of zero. These are appended to the FEAlist that
; is to be returned to the user.
;
gffg30: CALLFAR GetTempBuffer
        mov     [gffg_TempBuf].Segmt,ES
        mov     [gffg_TempBuf].Offst,DI
        mov     bx,[gffg_GEAlist].Segmt
        mov     si,[gffg_GEAlist].Offst ; BX:SI -> GEAlist
        mov     dx,si                   ; DX = offset of GEAlist
.errnz  (MAXEALISTSIZE - 65535)
; when >64K EA lists allowed, need to update code
     .errnz (geal_cblist)               ; BX:SI -> geal_cblist
        push    bx
        CALLFAR FuWord                  ; AX = size of GEAlist; SI advanced
        pop     bx
        jc      gffg35                  ; Got a GP fault reading user data
        mov     di,ax                   ; DI = loword size of GEAlist
        push    bx
        CALLFAR FuWord                  ; AX = size of GEAlist; SI advanced
        pop     bx
        jc      gffg35                  ; Got a GP fault reading user data
        dec     ax                      ; user's feal_cblist > 64K?
        sub     ax,-1                   ; set carry if BX != 0
        mov     ax,ERROR_EA_LIST_TOO_LONG ; set error code
gffg35: jc      gffg70                  ; Got a GP fault reading user data
        movzx   edx,dx
        movzx   edi,di
        add     edx,edi                 ; DX = offset of end of GEAlist
   .errnz (geal_list - geal_cblist - 4) ; BX:SI -> first GEA in GEAlist

;
; Loop through all the GEAs making matching FEAs as needed.
;
; BX:SI -> GEA in GEAlist
; DX = offset of end of GEAlist
;
        movzx   esi,si
gffg40: cmp     esi,edx
        je      gffg70                  ; End of list. Done. 'C' clear
        ja      gffg90                  ; cblist too small. Return error
        les     di,[gffg_FEAlist]       ; ES:DI -> FEAlist
        call    FindFEAbyGEA
        jc      gffg70                  ; Error. Probably GPFault.
        jz      gffg50                  ; Entry found. No need to create a dummy.
;
; There is no corresponding FEA. We create a dummy one with value length of
; zero and append it to the FEAlist that we are going to return.
;
        les     di,[gffg_TempBuf]
        call    MakeDummyFEA
        jc      gffg70                  ; Error. Probably GPFault.
        SaveReg <BX,ESI>
        lds     si,[gffg_FEAlist]
        mov     bx,es                   ; BX:DI -> FEA to be appended
        call    AppendFEA
        mov     es,bx                   ; ES:DI -> FEA
        RestoreReg <ESI,BX>
        jc      gffg70                  ; Error. Invalid GEA or buffer overflow.
;
; BX:SI -> GEA in GEAlist
; ES:DI -> FEA that matches GEA (may have just been added)
;
; We use the cbName in the FEA since we won't have to worry about accessing
; user space in that case.
; (It is OK to assume that fea_cbName is the same size as gea_cbName)
;
gffg50: mov     al,es:[di].fea_cbName
        xor     ah,ah                   ; AX = gea_cbName
        add     ax,2
        movzx   eax,ax
        add     esi,eax                 ; DS:SI -> Next GEA if any
        jmp     gffg40

;
; Free the temporary buffer, preserving any error code and flag, and then exit.
;
gffg70: PUSHF
        SaveReg <AX>
        les     di,[gffg_TempBuf]
        CALLFAR FreeTempBuffer
        RestoreReg <AX>
        POPF
gffg80: LeaveProc
        RET

gffg90: mov     ax,ERROR_EA_LIST_INCONSISTENT
        STC                                 ; Return error indication
        jmp     short gffg70

EndProc GetFEAFromGEAlist



;***    MakeDummyFEA - Make a dummy FEA from a GEA
;
;       MakeDummyFEA makes a dummy FEA, i.e. with fea_cbvalue = 0
;       from a GEA. It is used to return an FEA for a call where the
;       GEA supplied by the user did not match any FEA in the FEAlist
;       for the object. The name is upper-cased.
;       We copy over the GEA into a temp buffer before doing any
;       manipulation.
;
;       ENTRY   BX:SI -> GEA (GEA) (In USER space)
;               ES:DI -> Buffer to place FEA (DummyFEA)
;
;       EXIT    'C' if error (GP fault or invalid GEA name)
;               'NC' if success
;                   DummyFEA filled in
;
;       USES    AX, BX, CX, Flags
;
;       ALGORITHM
;
;           Set fea_fEA to 0
;           Set fea_cbName = GEA.gea_cbName+2)
;           Set fea_cbValue  to 0
;           Copy and uppercase GEA.gea_szName to fea_cbName
;           Set fea_cbName   to TempBuf.gea_cbName
;
Procedure   MakeDummyFEA,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <BX,SI,ES,DI>           ; Save pointers of input
        SaveReg <BX,ES,SI>
        lea     si,[si].gea_cbName      ; BX:SI -> gea_cbName
        xor     ah,ah
        CALLFAR FuByte                  ; AX = gea_cbName
        RestoreReg <SI,ES,BX>
        jc      mdf40                   ; Got a GP fault reading user data
        SaveReg <ES,DI>                 ; Save pointer to buffer for Dummy FEA
        CALLFAR GetTempBuffer           ; ES:DI -> Temp buffer
        mov     cx,ax
        add     cx,2                    ; (sizeof (gea_cbName) + NULL)
;
; ES:DI -> temp buffer (destination)
; BX:SI -> GEA in user buffer
;
        SaveReg <ES,DI>                 ; Save pointer to Temp buffer
        CALLFAR FuBuff                  ; Copy GEA into Temp buffer
        RestoreReg <SI,DS>              ; DS:SI -> Temp buffer
        RestoreReg <DI,ES>              ; ES:DI -> Buffer for Dummy FEA
        jc      mdf40                   ; Error from SuBuff. Maybe GPfault.
        xor     ax,ax
        SaveReg <DI,SI>
        mov     es:[di].fea_fEA,al
        mov     es:[di].fea_cbValue,ax
        mov     al,[si].gea_cbName
        mov     es:[di].fea_cbName,al   ; Copy gea_cbName to fea_cbName
        lea     si,[si].gea_szName      ; DS:SI -> Name in GEA
        lea     di,es:[di].fea_szName   ; ES:DI -> Name in FEA
        CALLFAR CopyEANameString        ; Upper-case name and copy it over
        RestoreReg <SI,DI>              ; ES:DI -> Dummy FEA
                                        ; DS:SI -> Temp buffer
;
; ES:DI -> Buffer for dummy FEA
; DS:SI -> Temp buffer
; 'C' if error encountered by SuBuff or CopyEANameString
;       AX = error code
;
mdf40:  PUSHF
        SaveReg <AX>                    ; Save any error code
        push    ds
        pop     es
        xchg    di,si                   ; ES:DI -> Temp buffer
        CALLFAR FreeTempBuffer
        RestoreReg <AX>
        POPF
        RestoreReg <DI,ES,SI,BX>        ; Restore entry pointers
        ret

EndProc MakeDummyFEA


;***    GetFEAlistSize - Get the size of the FEAlistfor this object
;
;       GetFEAlistSize gets the first word of the FEAlist for the
;       object and returns it in the WORD at the location specified.
;
;       NOTE    If there are no extended attributes for the object,
;               the size of the FEAlist returned will be 4. This
;               represents the size of the field that contains the
;               size of the FEAlist (which is a DWORD).
;
;
;       ENTRY   ES:SI -> SFT for object
;               BX:DI -> DWORD where size is to be placed
;               TCBThisSFT -> SFT
;
;       EXIT    'NC' if success
;                   DWORD at BX:DI filled with size of FEAlist
;                   dx = need EA count
;               'C' if error
;                   AX = error code
;
;       USES    AX, BX, CX, DX, DI, SI, ES, Flags
;
Procedure   GetFEAlistSize,NEAR
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        CodeLock FATEA_GROUP
        mov     cx,4                    ; Only read in the size (DWORD)
   .errnz (feal_list - feal_cblist - 4) ; Assert feal_cblist is a DWORD
        call    DiskReadFEAlist
        CodeUnlock FATEA_GROUP
        ret                             ; Return any error indications
EndProc     GetFEAlistSize

FATEASWAPCODE ENDS

    FARCODE HIGH2
HIGH2CODE   SEGMENT


;;; WARNING !!!!!
;;;
;;; Leave this routing in High2Code since it uses TestDBCS
;;; and CharHighType2

;***    CopyEANameString - Copy and upper-case the name string
;
;       CopyEANameString copies across the string, upper-casing the name
;       and making sure that no delimiters are present in the name.
;       It ensures that the characters found are legal characters to
;       have in a file name or in an extended attribute name.
;       We don't allow '*' or '?' in names.
;       This routine correctly handles DBCS characters.
;
;       ENTRY   DS:SI - pointer to source string
;               ES:DI - pointer to destination string
;               AL    = Length of name string to copy (excluding NULL)
;
;       EXIT    'NC' if valid name
;                   ES:DI point byte after nul byte at end of dest string
;                   DS:SI point byte after nul byte at end of source string
;               'C' if invalid character in name
;                   AX = ERROR_INVALID_EA_NAME
;
;       USES    AX,BX, DI, SI, Flags
;
Procedure   CopyEANameString,faronly
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar    cns_NameLen,WORD    ; Length of string to copy

        xor     ah,ah           ; AX = length of string
        inc     ax              ; Include NULL at the end of the name
        EnterProc <ax>          ; Set up LocalVar
        MOV     BX,OFFSET CharTypeHIGH2
cns10:  LODSB                   ;
        call    UCase
        mov     ah,al           ; save the upper cased character
        XLAT    BYTE PTR CS:[BX] ; check for validity
        TEST    AL,fFCB
        mov     al,ah           ; get back the uppercase character
        jbe     cns40           ; Invalid character found
        CMP     AL,"*"          ; Check for ambiguous file specifier
        jz      cns40
        cmp     al,"?"
        jz      cns40
        STOSB                   ; Copy byte to destination
        TestDBCS
        JNZ     cns20           ; Not double byte char
        dec     [cns_NameLen]
        jz      cns48           ; Name length does not match string
        LODSB                   ; Get second byte
cns15:  STOSB                   ; Copy to destination
;
; We chack to make sure that the string and the length passed in are consistent.
; If this is the end of string (AL = NULL), then the value in [cns_NameLen]
; should also indicate that this is the end. If not, then it is an error.
; It is also an error if AL != NULL, but [cns_NameLen] indicates that it
; should be the end of the string.
;
cns20:  or      al,al           ; Clears 'Carry' flag
        jnz     cns23           ; Not end of string. See if there are more chars
        dec     [cns_NameLen]
        jnz     cns48           ; Name length is longer than name string
; Name length matches string

cns30:  LeaveProc
        ret

cns23:  dec     [cns_NameLen]
        jz      cns48           ; Name length not as long as the name
        jmp     short cns10     ; Get the next character

;;BUGBUG 22-Apr-1988 RajenS - Is the list of legal chars complete?
;; Have we got all the delimiters and ctrl-chars covered here?
;
; We found a character that is either a delimiter or a control character.
; Out of these, we allow a NULL. If it is a NULL, we just
; copy it and assume that this is the end of the string.
; Otherwise, we return 'C' and "error_invalid_EA_name".
;
cns40:  or      al,al
        jz      cns15           ; NULL terminator. Copy it and return no error.
;
; We had an invalid character in the name. We return ERROR_INVALID_EA_NAME
; to the caller.
;
        mov     ax,ERROR_INVALID_EA_NAME
cns45:  stc                     ; Return error indication
        jmp     short cns30

;
; We had an invalid length of string. We return ERROR_EA_LIST_INCONSISTENT
; to the caller.
;
cns48:  mov     ax,ERROR_EA_LIST_INCONSISTENT
        jmp     short cns45

EndProc     CopyEANameString

;***    CheckEAName - check the given ea name for validity
;
;       CheckEAName maks sure that no delimiters are present in the name.
;       It ensures that the characters found are legal characters to
;       have in a file name or in an extended attribute name.
;       We don't allow '*' or '?' in names.
;       This routine correctly handles DBCS characters.
;
;       ENTRY   DS:SI - pointer to source string
;               AX    = Length of name string to copy (excluding NULL)
;
;       EXIT    'NC' if valid name
;                   DS:SI point byte after nul byte at end of source string
;               'C' if invalid character in name
;                   AX = ERROR_INVALID_EA_NAME
;                      = ERROR_EA_LIST_INCONSISTENT
;       USES    AX, BX, SI, Flags
;
;;; WARNING !!!!!
;;;
;;; Leave this routing in High2Code since it uses TestDBCS
;;; and CharHighType2


Procedure   CheckEAName,faronly
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar    cean_NameLen,WORD   ; Length of string to copy

        inc     ax              ; Include NULL at the end of the name
        EnterProc <ax>          ; Set up LocalVar
        MOV     BX,OFFSET CharTypeHIGH2
cean10: LODSB                   ;
        mov     ah,al
        XLAT    BYTE PTR CS:[BX] ; check for validity
        TEST    AL,fFCB
        mov     al,ah           ; get back the uppercase character
        jbe     cean40          ; Invalid character found
        CMP     AL,"*"          ; Check for ambiguous file specifier
        jz      cean40
        cmp     al,"?"
        jz      cean40
        TestDBCS
        JNZ     cean20          ; Not double byte char
        dec     [cean_NameLen]
        jz      cean48          ; Name length does not match string
        LODSB                   ; Get second byte
; We chack to make sure that the string and the length passed in are consistent.
; If this is the end of string (AL = NULL), then the value in [cean_NameLen]
; should also indicate that this is the end. If not, then it is an error.
; It is also an error if AL != NULL, but [cean_NameLen] indicates that it
; should be the end of the string.
;
cean20: or      al,al           ; Clears 'Carry' flag
        jnz     cean35          ; Not end of string. See if there are more chars
cean25: dec     [cean_NameLen]
        jnz     cean48          ; Name length is longer than name string
                                ; Name length matches string
cean30: LeaveProc
        ret

cean35: dec     [cean_NameLen]
        jz      cean48          ; Name length not as long as the name
        jmp     short cean10    ; Get the next character

;;BUGBUG 22-Apr-1988 RajenS - Is the list of legal chars complete?
;; Have we got all the delimiters and ctrl-chars covered here?
;
; We found a character that is either a delimiter or a control character.
; Out of these, we allow a NULL. If it is a NULL, we just
; copy it and assume that this is the end of the string.
; Otherwise, we return 'C' and "error_invalid_EA_name".
;
cean40: or      al,al
        jz      cean25          ; NULL terminator. return no error.
;
; We had an invalid character in the name. We return ERROR_INVALID_EA_NAME
; to the caller.
;
        mov     ax,ERROR_INVALID_EA_NAME
cean45: stc                     ; Return error indication
        jmp     short cean30

;
; We had an invalid length of string. We return ERROR_EA_LIST_INCONSISTENT
; to the caller.
;
cean48: mov     ax,ERROR_EA_LIST_INCONSISTENT
        stc
        jmp     short cean30

EndProc     CheckEAName


HIGH2CODE ENDS

        END
