SCCSID  equ     "@(#)finfo.asm  6.19 92/03/09"
TITLE   FILE_INFO - Internal Get/Set File Info routines

;
;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved
;
; Low level routines for returning file information and setting file
;   attributes
;
;       Global          Local
;
;       FSD_QFileMode
;                       DOS_QFileMode
;       FSD_SetFileMode
;       w_SetFileInfo
;       w_QFileInfo
;       w_QPathInfo
;       w_SetPathInfo
;                       DoPathInfo
;       w_EnumAttribute
;       w_ProtectEnumAttribute
;       w_3xEnumAttrib
;
;       DOS_QFileMode
;       DOS_SetFileMode
;       DOS_SetInfo
;               SetInfoForFile
;       SetInfoForFile
;       DOS_QInfo
;               GetInfoForFile
;       GetInfoForFile
;       bytesToClusters
;
;       18-Nov-95    MLM   Defect 72417, reduce fscrit serialization.
;                          Also converted jumps to LJ* macros.
;
.xlist
include fnofs.inc
include dosseg.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
.xcref
include dossymc.inc
include argframe.inc
include buffer.inc
include dirent.inc
include error.inc
include extattr.inc
include filemode.inc
include filesys.inc
include inbuf.inc
include tkexport.inc
include mft.inc
include vmowner.inc
include mult.inc
include oplock.inc
include fsh.inc
include sft.inc
include vpb.inc
include protmode.inc
include seldesc.inc
include virtsym.inc
include mi.inc
include curdir.inc
include find.inc
include fsc.inc
include proc.inc
include shutdown.inc
.cref
.list
.lall
        ModName , FNO_FINFO_ASM

                                        ; Start Defect 72417 changes
        ReferTask       CurrTCB,WORD            ; Current thread control block
                                        ; End Defect 72417 changes
        ReferTask       ProcType,BYTE

        ReferGDT        GDT_FSC,WORD
        ReferGDT        GDT_VPB,WORD

        EXTRN           AddShare:NEAR
        EXTRN           PathDecVPBRef:NEAR
        EXTRN           FuWord:NEAR
        EXTRN           fValidFATDisk:NEAR
        EXTRN           FStrCpy32:NEAR
        EXTRN           StrCpy:NEAR
        EXTRN           GetFEAInfo:NEAR
        EXTRN           GetFEAlistSize:NEAR
        EXTRN           GetPath:NEAR
        EXTRN           GetSFT:NEAR
        EXTRN           PathIncVPBRef:NEAR
        EXTRN           PhysHandle:NEAR
        EXTRN           ProbeBuff:NEAR
        EXTRN           RemoveShare:NEAR
        EXTRN           SetFEAInfo:NEAR
        EXTRN           SFFromHandle:NEAR
        EXTRN           SFLinkFree:NEAR
        EXTRN           ShSU:NEAR
        EXTRN           SuWord:NEAR
        EXTRN           TransPath:NEAR
        EXTRN           w_ChMod:NEAR

        EXTRNFAR        FuWord
        EXTRNFAR        SuWord
        EXTRNFAR        DOS_PathInfo
        EXTRNFAR        DStrLen
        EXTRNFAR        FlushBuf
        EXTRNFAR        FNotify_Path_Wake
        EXTRNFAR        CNotify_Path_Wake
        EXTRNFAR        FuBuff
        EXTRNFAR        GDTFree
        EXTRNFAR        ProbeBuff
        EXTRNFAR        RlseBuffr
        EXTRNFAR        SegAlloc
        EXTRNFAR        SegFree
        EXTRNFAR        SegMapVirt
        EXTRNFAR        SFLock
        EXTRNFAR        SFUnlock
        EXTRNFAR        ShSU
        EXTRNFAR        SuBuff
ifdef SECURITY
        EXTRNFAR        SecQueryFileInfo                                ;@SEC
        EXTRNFAR        SecSetFileInfo                                  ;@SEC
        EXTRNFAR        SecSetPathInfo                                  ;@SEC

        ReferFlat       _fSec,BYTE                                      ;@SEC
endif

include filemacs.inc

CpuMode 386                                                             ;@SEC
        FARCODE HIGH2
HIGH2CODE SEGMENT
    ASSUME  SS:TASKAREA


Break   <FSD_QFileMode -- Get File Information>

;**     FSD_QFileMode - Get Information about a file
;
;       This is the low level worker for DosQFileMode.
;
;       ENTRY   [WFP_Start] Points to WFP string ("d:\" must be first 3
;                       chars, NUL terminated)
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else Points
;                        to first char after last "\" of current dir part)
;               [THISCDS] Points to CDS being used
;                       (Low word = -1 if NUL CDS (Net direct request))
;               [Sattrib] Is attribute of search, determines what files
;                       can be found
;               [TCBThisFSC] FSC pointer
;
;       EXIT    CARRY CLEAR
;                   AX = Attribute of file
;               CARRY SET
;                   AX is error code
;                       ERROR_FILE_NOT_FOUND
;                               Last element of path not found
;                       ERROR_PATH_NOT_FOUND
;                               Bad path (not in curr dir part if present)
;                       ERROR_BAD_CURR_DIR
;                               Bad path in current directory part of path
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

        ASSUME DS:NOTHING,ES:NOTHING
Procedure   FSD_QFileMode,HYBRID

LocalVar fqfm_Attr,WORD

        EnterProc
;
; If this is a device, we return an error.
;
                                        ; Start Defect 72417 changes
        mov     si,CurrTCB
        test    TASKAREA:[si].TCBPathNameType,PNType_LOCDEV+PNType_PSDEV
        jnz     fqfm30                  ; Device. Not allowed.

; dispatch to fsd or fat

                                        ; End Defect 72417 changes
        lds     si,TASKAREA:[si].TCBThisFSC     ; ds:si -> FSC
        cmp     si,FSC_LOCAL                    ; what file system?
        jb      fqfm3                           ; FSD, go call FSD worker

; must be fat

        CALLFAR DOS_QFileMode                   ; fat worker
fqfm1:  LeaveProc
        ret

; call FS_FileAttribute
; ds:si -> fsc

fqfm3:  push    FSF_QINFO               ; flag = get info
                                        ; Start Defect 72417 changes
        mov     di,TASKAREA:CurrTCB             ; (DI) = current TCB     ;### PTR B720732
        les     di,TASKAREA:[di].TCBThisCDS     ; (ES:DI) = CDS pointer  ;### PTR B720732
;### PTR B720732 les    di,ThisCDS              ; es:di -> cds
        lea     ax,[di].cd_fsi          ; es:ax -> cd_fsi
        push    es                      ; push pointer to cd_fsi
        push    ax
        lea     ax,[di].cd_fsd          ; ds:ax -> cd_fsd
        push    es                      ; push pointer to cd_fsd
        push    ax
        push    ss                      ; push pointer to path
        mov     bx,bp
                                        ; End Defect 72417 changes
        mov     bp,CurrTCB
        push    [bp].TCBWFP_Start
        mov     ax,[bp].TCBCurr_Dir_End
        cmp     ax,-1                   ; is Curr_Dir_End set?
        je      fqfm5                   ; no, pass -1 to FS_FileAttribute
        sub     ax,[bp].TCBWFP_Start    ; ax = Curr_Dir_End - WFP_Start
fqfm5:  push    ax                      ; iCurDirEnd
        push    ss
        mov     bp,bx
        lea     ax,fqfm_Attr            ; ss:ax -> fqfm_Attr
        push    ax
        call    ds:[si].FS_FILEATTRIBUTE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        jc      fqfm10                  ; go error exit if carry set
        mov     ax,fqfm_Attr            ; return file attribute in ax
fqfm10: jmp     short fqfm1

fqfm30: mov     ax,ERROR_FILE_NOT_FOUND
        stc
        jmp     short fqfm1

EndProc FSD_QFileMode


Break   <FSD_SetFileMode -- Set File Attribute>

;**     FSD_SetFileMode - Set File Attributes
;
;       NOTE:   In DOS 3.1 with sharing loaded, FSD_SetFileMode
;               would check for exclusive access to the file by calling
;               REN_DEL_Check.  This call also had the side effect of
;               closing any references to the file by the calling process.
;               This behavior changes in DOS 4 and it is permitted to
;               change the mode of an open file.  A consistency check in
;               DOS_Close must be relaxed to permit this.
;       ENTRY   [WFP_Start] Points to WFP string ("d:\" must be first 3
;                       chars, NUL terminated)
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else Points
;                        to first char after last "\" of current dir part)
;               [THISCDS] Points to CDS being used
;                       (Low word = -1 if NUL CDS (Net direct request))
;               [Sattrib] is attribute of search
;                       (determines what files may be found)
;               [TCBThisFSC] fsc pointer
;               AX is new attributes to give to file
;
;       EXIT    CARRY CLEAR
;                   No error
;               CARRY SET
;                   AX is error code
;                       ERROR_FILE_NOT_FOUND
;                               Last element of path not found
;                       ERROR_PATH_NOT_FOUND
;                               Bad path (not in curr dir part if present)
;                       ERROR_BAD_CURR_DIR
;                               Bad path in current directory part of path
;                       ERROR_ACCESS_DENIED
;                               Attempt to set an attribute which cannot be set
;                               (attr_directory, attr_volume_ID)
;                       ERROR_SHARING_VIOLATION
;                               Sharing mode of file did not allow the change
;                               (this request requires exclusive write/read
;                                access, INT 24H generated)
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

        ASSUME DS:NOTHING,ES:NOTHING
Procedure   FSD_SetFileMode,NEAR
LocalVar        fsfm_attr,WORD  ; new attribute

        EnterProc <ax>
;
; If this is a device, we return an error.
;
                                        ; Start Defect 72417 changes
        mov     si,CurrTCB
        test    TASKAREA:[si].TCBPathNameType,PNType_LOCDEV+PNType_PSDEV
        LJNZ     fsfm30                 ; Device. Not allowed.

        test    ax,not attr_changeable  ; valid attribute?
        LJNZ    fsfm30                  ; no, error exit

; dispatch to fsd or fat

                                        ; End Defect 72417 changes
        lds     si,TASKAREA:[si].TCBThisFSC     ; ds:si -> fsc
        cmp     si,FSC_LOCAL                    ; what file system?
        jb      fsfm3                           ; FSD, go call FSD worker

; call FAT worker using special shutdown gate
; ShutdownGate  function name, exit label, seg with FSC segment (DS or ES)

        ShutdownGate  DOS_SetFileMode, fsfmx, DS

; the above jumps to fsfmx on completion

; ds:si -> fsc
; call FS_FileAttribute

fsfm3:  push    FSF_SETINFO             ; flag = set info
        mov     di,TASKAREA:CurrTCB             ; (DI) = current TCB     ;### PTR B720732
        les     di,TASKAREA:[di].TCBThisCDS     ; (ES:DI) = CDS pointer  ;### PTR B720732
;### PTR B720732 les    di,ThisCDS              ; es:di -> cds
        lea     ax,[di].cd_fsi          ; es:ax -> cd_fsi
        push    es                      ; push pointer to cd_fsi
        push    ax
        lea     ax,[di].cd_fsd          ; es:ax -> cd_fsd
        push    es                      ; push pointer to cd_fsd
        push    ax
        push    ss                      ; push pointer to path
        mov     bx,bp
                                        ; Start Defect 72417 changes
        mov     bp,CurrTCB
        push    [bp].TCBWFP_Start
        mov     ax,[bp].TCBCurr_Dir_End
        cmp     ax,-1                   ; is Curr_Dir_End set?
        je      fsfm5                   ; no, pass -1 to FS_FileAttribute
        sub     ax,[bp].TCBWFP_Start    ; ax = Curr_Dir_End - WFP_Start
fsfm5:  push    ax                      ; iCurDirEnd
        push    ss                      ; push pointer to new attribute
        mov     bp,bx
                                        ; End Defect 72417 changes
        lea     ax,[fsfm_attr]
        push    ax
        call    ds:[si].FS_FILEATTRIBUTE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
fsfmx:
        jc      fsfmy                   ;### Notify
        pushf
        push    ax                      ;### Notify
        mov     ax,7                    ;### Info change
        CALLFAR CNotify_Path_Wake       ;### Notify
        pop     ax                      ;### Notify
        popf
fsfmy:  LeaveProc
        ret

fsfm30: mov     ax,ERROR_ACCESS_DENIED
        stc
        jmp     short fsfmx
EndProc FSD_SetFileMode


;***    w_SetFileInfo - Set a file's information
;
;       w_SetFileInfo performs the DOSSETFILEINFO function call.
;
;       ENTRY   DH - bit mask == API calling worker (00h - 16 bit, 01h - 32 bit)
;               BX - Handle of the file
;               DS:SI - pointer to FileInfoBuf
;               CX - size of FileInfoBuf
;               DL - FileInfoLevel
;               DH - 80h - bit mask == do not check File Handle LockID
;               DI:AX - FileHandleLockID
;                  = SINFOLEV_DATE_TIME -- set date/time of file
;                  = SINFOLEV_FEAL -- set extended attributes
;
;       EXIT    Carry Clear     - no error
;                          information for file is set.
;               Carry Set       - error
;                          AX = ERROR_INVALID_HANDLE
;                               ERROR_INVALID_LEVEL
;                               ERROR_INSUFFICIENT_BUFFER
;                               ERROR_ACCESS_DENIED
;                               ERROR_DIRECT_ACCESS_HANDLE
;                               ERROR_INVALID_PARAMETER
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       NOTES   Setting EAs for devices and named pipes is not currently
;               supported. This may change in the future.
;
;
;       ALGORITHM
;
;       sft = sffromhandle
;       check for proper kind of sft
;       if (FSD)
;         FS_FileInfo
;       else {  /* FAT */
;         DOS_SetFileInfo
;         if (error || level == SINFOLEV_DATE_TIME)
;           return  /* no need to call ShSU; DOS_SetFileInfo did it already */
;       }
;
;       if (error)
;         return error
;       if (level == SINFOLEV_DATE_TIME)
;         ShSU(0)  /* propagate time/date changes to other sfts */
;       return

Procedure SFT_SetFileInfo,far16
        call    SFT_SetFileInfo
        RETP
EndProc   SFT_SetFileInfo

        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_SetFileInfo,HYBRID
LocalVar sfi_APILvl,WORD        ; API Level (0-16bit,1-32bit)
LocalVar sfi_level,WORD         ; infolevel
LocalVar sfi_lpSFT,DWORD        ; point to sft entry
LocalVar wsf_dummycds,<SIZE curdir>
LocalVar sfi_InBuf,<SIZE inbuf>
LocalVar sfi_FileHandleLockID,DWORD     ; file handle lockID
LocalVar sfi_UserBuf,DWORD                                              ;@SEC
LocalVar sfi_CheckFileHandleLockID,BYTE ; check file handle lockID flag

        EnterProc

        mov     sfi_FileHandleLockID.LowWord,ax  ; filehandle lockID low word
        mov     sfi_FileHandleLockID.HighWord,di ; filehandle lockID high word
        mov     sfi_CheckFileHandleLockID,dh     ; check filehandle lockID flag

        xor     dh,dh                   ; allow below to still work
        cmp     dx,SINFOLEV_MAX         ; level too large?
        LJA     sfi04                   ; yes, go error exit

        or      dx,dx                   ; level == 0?
                                        ; Start Defect 72417 changes
        LJNZ    sfi04a                  ; no
                                        ; End Defect 72417 changes

sfi04:
        mov     ax,ERROR_INVALID_LEVEL
        stc
        jmp     sfi30                   ; exit with LeaveProc and cy set

sfi04a:

; This code assumes the set file info level in dx is not over 255 and the
; api level is not over 255
        push    cx
        push    dx
        push    dx
        xor     dx,dx
        mov     sfi_level,dx
        mov     sfi_APILvl,dx
        pop     dx
        mov     dh,sfi_CheckFileHandleLockID     ; fhlockID flag and API level
        and     dh,07fh                          ; mask off fhlockID flag bit
        mov     byte ptr sfi_APILvl, dh
        mov     byte ptr sfi_level, dl

        call    SFFromHandle            ; es:[di] -> sft
        pop     dx
        pop     cx
        LJC     sfi30                   ; return if handle deref error

        mov     ah, sfi_CheckFileHandleLockID  ; get check filehandle lockID flag
        test    ah,80h                         ; check filehandle lockID?
                                        ; Start Defect 72417 changes
        LJNZ    sfi05a                         ; no, skip check
                                        ; End Defect 72417 changes

        mov     ax, sfi_FileHandleLockID.LowWord
        cmp     ax, es:[di].sf_LockID.LowWord  ; check access
                                        ; Start Defect 72417 changes
        LJNE    sfi05
                                        ; End Defect 72417 changes

        mov     ax, sfi_FileHandleLockID.HighWord
        cmp     ax, es:[di].sf_LockID.HighWord  ; check access
                                        ; Start Defect 72417 changes
        LJE     sfi05a                          ; allow access
                                        ; End Defect 72417 changes
sfi05:
        mov     ax, ERROR_ACCESS_DENIED        ; access denied
        stc
        jmp     sfi30                          ; exit with cy set

sfi05a:

        mov     ax,sfi_APILvl           ; retrieve API level
        mov     dx,sfi_level            ; retrieve info level

        LeaveProc

        jmp     short sfi10             ; continue processing

; AX = API level
; DX = level
; ES:DI = sft
; CX = length of fileinfobuf
; DS:SI = ptr to file info buf

Entry   SFT_SetFileInfo,HYBRID

sfi10:  EnterProc <ax,dx,es,di>            ; ax = API level, sfi_level = dx
                                           ; sfi_lpSFT = es:di
        lea     ax,wsf_dummycds
        push    bx                                      ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,ax       ; Initialize TCBpTmpCDS ;### PTR B720732
        pop     bx                                      ;### PTR B720732
        call    PhysHandle              ; physical disk handle?
        jc      sfi30                   ; yes - return with error

;
; If handle points to a device or a pipe, it is an error.
;
        test    es:[di].sf_flags,DEVID_DEVICE+SF_PIPE
        mov     ax,ERROR_INVALID_HANDLE ; Assume error. Not currently supported.
        LJNZ    sfi120                  ; It is a device or pipe. Error.

;
; If handle is for a direct disk access, then it is an error.
;
        test    es:[di].sf_fsi.sfi_mode,open_direct ; direct disk access handle?
        mov     ax,ERROR_DIRECT_ACCESS_HANDLE
        LJNZ    sfi120                  ; Yes. Return error code.

; write access is required to set attributes.

        mov     ax,es:[di].sf_fsi.sfi_mode ; get the open mode
        test    al,open_for_write OR open_for_both     ; write access?
        mov     ax,ERROR_ACCESS_DENIED
        stc
        jz      sfi30                   ; no, go return error
;
; We now know that handle is open, that it doesn't point
; do a device, and isn't open for direct access to a disk,
; thus, it makes sense to perform this operation on the handle
;
; CX      = size of Info buf
; DX      = level
; DS:SI   -> date and time user wants to set for the handle/file
; ES:DI   -> SFT
;
        mov     sfi_lpSFT.Segmt,es
        mov     sfi_lpSFT.Offst,di      ; save SFT pointer for later use

        cmp     dx,SINFOLEV_FEAL        ; Set EAs?
                                        ; Start Defect 72417 changes
        LJE     sfi15                   ; yes, skip time/date/attribute stuff
                                        ; End Defect 72417 changes
;
; This need to add this code for so this worker can work both for new 32 bit
; calls and also retain compatibility with the 16-bit API
;
        mov     ax,[sfi_APILvl]         ; retrieve API level
        or      ax,ax
        LJNZ    sfi15
        cmp     cx,SETDATEBUFSIZE
        jge     short sfi13
        mov     ax,ERROR_INSUFFICIENT_BUFFER
        stc
;        jmp short sfi30
        jmp     sfi30                                                   ;@SEC
sfi13:
        mov     cx,SETDATEBUFSIZE
        lea     di,sfi_InBuf
        push    ss                              ; ds:si -> user's inbuf
        pop     es                              ; es:di -> temp inbuf
        rep movsb
        lea     si,sfi_InBuf
        push    es
        pop     ds                              ; ds:si -> temp inbuf nows has
        xor     ax,ax                           ; user's passed in date/time
        mov     ds:[si].inbuf_size.hiword,ax
        mov     ds:[si].inbuf_size.loword,ax
        mov     ds:[si].inbuf_alloc.hiword,ax
        mov     ds:[si].inbuf_alloc.loword,ax
        les     di,sfi_lpSFT                    ; es:di -> SFT
        mov     al,es:[di].sf_fsi.sfi_DOSattr   ; get current file attributes
        mov     [si].inbuf_attr,ax

        mov     cx,SETINFOBUFSIZE               ; cx = size of fileinfo buffer

; dispatch to fsd or fat worker
sfi15:
ifdef   SECURITY
        mov     sfi_UserBuf.Segmt,ds                                    ;@SEC
        mov     sfi_UserBuf.Offst,si                                    ;@SEC
                                                                        ;@SEC
        FlatContext <ds>                                                ;@SEC
        cmp     _fSec,0                                                 ;@SEC
        jz      short sfi17             ; no security. skip the callout.;@SEC
        mov     eax,sfi_UserBuf         ; eax -> user buffer            ;@SEC
                                        ; es:di -> sft
                                        ; cx and dx are still set up    ;@SEC
        FlatContext <ds>                                                ;@SEC
        CALLFAR SecSetFileInfo          ; sets CY and AX if disallowed. ;@SEC
        jc      short sfi30             ; get out. ax should be nonzero.;@SEC
sfi17:                                                                  ;@SEC
        lds     si,sfi_UserBuf          ; ds:si->user buffer (ring0)    ;@SEC
                                        ; es:di -> SFT (not changed)    ;@SEC
                                        ; cx and dx are still set up    ;@SEC
; get_advise macro uses TASKAREA                                        ;@SEC
endif
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA                       ;@SEC

        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL ; what file system?
                                        ; Start Defect 72417 changes
        LJB     sfi50                   ; FSD, go call FSD worker
                                        ; End Defect 72417 changes

sfi20:  CALLFAR DOS_SetInfo             ; call FAT FS worker
                                        ; Start Defect 72417 changes
        LJNC    sfi75                   ; if not error, do post processing
                                        ; End Defect 72417 changes
sfi30:  LeaveProc
sfi35:  ret

; call FS_FileInfo

sfi50:  push    FSF_SETINFO             ; flag = set info
        lea     ax,[di].sf_fsi          ; es:ax -> sft.sf_fsi
        push    es                      ; push sft.sf_fsi
        push    ax
        lea     ax,[di].sf_fsd          ; es:ax -> sft.sf_fsd
        push    es                      ; push sft.sf_fsd
        push    ax
        push    dx                      ; level
        push    ds                      ; push pData
        push    si
        push    cx                      ; push cbData
        get_advise                      ; ax = advisory bits
        push    ax                      ; push IOflag (advisory bits)
        lds     si,es:[di].sf_FSC       ; ds:si -> fsc
        call    ds:[si].FS_FILEINFO     ; ax = error code if not 0
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0

; We have just called FS_FileInfo. If there was an error,
; we exit immediately. If no error, we check to see if post processing
; is necessary.

                                        ; Start Defect 72417 changes
sfi70:  LJC     sfi30                   ; go exit if error
                                        ; End Defect 72417 changes

; since more than one SFT may exist for a file, and since last-write
; date/time is recorded in the SFT, we must propagate changes from this
; SFT to all the other ones.

        les     di,sfi_lpSFT            ; es:di -> SFT

sfi75:  cmp     sfi_level,SINFOLEV_DATE_TIME ; did we changed the date/time?
                                        ; Start Defect 72417 changes
        LJNE    sfi80                   ; No, go exit
                                        ; End Defect 72417 changes

        xor     ax,ax                   ; ax = propagate time/date for ShSU
        call    ShSU
sfi80:  clc
        jmp     short sfi30             ; return

; This code appears to be 'dead' so it is commented out.                ;@SEC
;sfi115: mov     ax,ERROR_INVALID_LEVEL  ; invalid function
;        stc
;        jmp     short sfi35             ;

sfi120: stc                             ; indicate error
        jmp     short sfi30             ; return

EndProc w_SetFileInfo

;***    FSD_QFileInfo - Query a files information                         ;@SEC
; @SEC  - Broke this routine out of w_QFileInfo to make things cleaner    ;@SEC
;                                                                         ;@SEC
;       ENTRY   DS:SI - pointer to FileInfoBuf                            ;@SEC
;               CX    - size of FileInfoBuf                               ;@SEC
;               ES:DI - ptr to SFT                                        ;@SEC
;               DX    - FileInfoLevel                                     ;@SEC
;                  = QINFOLEV_DATE_TIME -- get date/time of file          ;@SEC
;                  = QINFOLEV_EASIZ     -- get date/time+size of FEA list ;@SEC
;                  = QINFOLEV_SUB_FEAL  -- get EA list from GEA list      ;@SEC
;                  = QINFOLEV_FULL_FEAL -- get full EA list               ;@SEC
;                  = QINFOLEV_FSD_NAME  -- get FSD name                   ;@SEC
;                                                                         ;@SEC
;       EXIT    Carry Clear     - no error                                ;@SEC
;                          FileInfoBuf is filled with information about   ;@SEC
;                          the file.                                      ;@SEC
;                                                                         ;@SEC
;               Carry Set       - error                                   ;@SEC
;                          AX = ERROR_BUFFER_OVERFLOW                     ;@SEC
;                               ERROR_DIRECT_ACCESS_HANDLE                ;@SEC
;                               ERROR_ACCESS_DENIED.                      ;@SEC
;                                                                         ;@SEC
;       USES    ax,bx,cx,dx,si,di,ds,es,flags                             ;@SEC
;                                                                         ;@SEC
;       NOTE    Almost all the error checking has been done by the worker ;@SEC
;               routine.  The only validation this does is for a          ;@SEC
;               a direct access handle.  Also check for read access to    ;@SEC
;               query ea's.                                               ;@SEC
;                                                                         ;@SEC
;       PSEUDOCODE:                                                       ;@SEC
;        if sft is for a direct handle                                    ;@SEC
;           return ERROR_DIRECT_ACCESS_HANDLE                             ;@SEC
;        if Infolevel != 1 and SFT not open for write                     ;@SEC
;           return ERROR_ACCESS_DENIED                                    ;@SEC
;        if FAT call DOS_QInfo                                            ;@SEC
;           else call FSD                                                 ;@SEC
;                                                                         ;@SEC
Procedure FSD_QFileInfo,near                                              ;@SEC
        test    es:[di].sf_fsi.sfi_mode,open_direct  ; direct disk access handle? ;@SEC
        mov     ax,ERROR_DIRECT_ACCESS_HANDLE   ; assume guilty           ;@SEC
        jnz     short fqfierr                   ; yes, go report error    ;@SEC
;                                                                         ;@SEC
; Read access is required to query EAs. The three possible access         ;@SEC
; modes are open_for_read, open_for_write, and open_for_both.  to query   ;@SEC
; the attributes, either open_for_read or open_for_both is required.      ;@SEC
;                                                                         ;@SEC
; For Infolevel 1, we don't do this check - this is because we allow this ;@SEC
; operation without read-access.                                          ;@SEC
;                                                                         ;@SEC
        cmp     dx,QINFOLEV_DATE_TIME      ; Infolevel 1?                 ;@SEC
        je      short fqfi20               ; Yes. Don't check access mode ;@SEC
        mov     ax,es:[di].sf_fsi.sfi_mode ; get the open mode            ;@SEC
        and     al,open_access             ; get access                   ;@SEC
        cmp     al,open_for_write          ; correct access?              ;@SEC
        mov     ax,ERROR_ACCESS_DENIED                                    ;@SEC
        je      short fqfierr              ; no, go return error          ;@SEC
;                                                                         ;@SEC
; dispatch to fsd or fat worker                                           ;@SEC
;                                                                         ;@SEC
fqfi20: cmp     es:[di].sf_FSC.Offst,FSC_LOCAL ; what file system?        ;@SEC
        jb      short qfi30                    ; FSD, go call fsd worker  ;@SEC
                                                                          ;@SEC
        CALLFAR DOS_QInfo               ; call FAT worker                 ;@SEC
fqfiexit:                                                                 ;@SEC
        RETP                                                              ;@SEC
fqfierr:                                                                  ;@SEC
        stc                                                               ;@SEC
        RETP                                                              ;@SEC
        ASSUME  DS:NOTHING                                                ;@SEC
                                                                          ;@SEC
; call FS_FileInfo                                                        ;@SEC
                                                                          ;@SEC
qfi30:  push    FSF_QINFO               ; flag = get info                 ;@SEC
        lea     ax,[di].sf_fsi          ; es:ax -> sft.sf_fsi             ;@SEC
        push    es                      ; push sft.sf_fsi                 ;@SEC
        push    ax                                                        ;@SEC
        lea     ax,[di].sf_fsd          ; es:ax -> sft.sf_fsd             ;@SEC
        push    es                      ; push sft.sf_fsd                 ;@SEC
        push    ax                                                        ;@SEC
        push    dx                      ; level                           ;@SEC
        push    ds                      ; push pData                      ;@SEC
        push    si                                                        ;@SEC
        push    cx                      ; push cbData                     ;@SEC
        get_advise                      ; ax = advisory bits              ;@SEC
        push    ax                      ; push IOflag (advisory bits)     ;@SEC
        lds     si,es:[di].sf_FSC       ; ds:si -> fsc                    ;@SEC
        call    ds:[si].FS_FILEINFO                                       ;@SEC
        dec     ax                                                        ;@SEC
        sub     ax,-1                   ; set carry if ax != 0            ;@SEC
        jmp short fqfiexit              ; exit                            ;@SEC
                                                                          ;@SEC
EndProc FSD_QFileInfo                                                     ;@SEC

;***    w_QFileInfo - Get a file's information
;
;       w_QFileInfo performs the DOSQFILEINFO function call.
;
;       ENTRY   BX - Handle of the file
;               DS:SI - pointer to FileInfoBuf
;               CX - size of FileInfoBuf
;               DL - FileInfoLevel
;               DH - 80h ==> do not check File Handle LockID
;               DI:AX - FileHandleLockID
;                  = QINFOLEV_DATE_TIME -- get date/time of file
;                  = QINFOLEV_EASIZ     -- get date/time + size of FEA list
;                  = QINFOLEV_SUB_FEAL  -- get EA list from GEA list
;                  = QINFOLEV_FULL_FEAL -- get full EA list
;                  = QINFOLEV_FSD_NAME  -- get FSD name
;
;       EXIT    Carry Clear     - no error
;                          FileInfoBuf is filled with information about
;                          the file.
;
;               Carry Set       - error
;                          AX = ERROR_INVALID_HANDLE
;                               ERROR_INVALID_HANDLE
;                               ERROR_BUFFER_OVERFLOW
;                               ERROR_INVALID_LEVEL
;                               ERROR_DIRECT_ACCESS_HANDLE
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       NOTE    Devices and Pipes do not currently have EAs. We handle those
;               cases in DiskReadFEAList in extattr.asm where we just return
;               an empty list. This may change in the future. For remote
;               devices and pipes, it is up to the FSD to handle it.
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure SFT_QFileInfo,far16
        call    FSD_QFileInfo                                             ;@SEC
        RETP
EndProc   SFT_QFileInfo

Procedure w_QFileInfo,<near,faronly,far16>

LocalVar qfi_UserBuf, DWORD             ; Ptr to use buffer               ;@SEC
LocalVar qfi_UserBufSize , WORD         ; Size of user buffer             ;@SEC
LocalVar qfi_InfoLevel   , WORD         ; Info level requested            ;@SEC
LocalVar qfi_FileHandleLockID,DWORD     ; file handle lockID
LocalVar qfi_CheckFileHandleLockID,BYTE ; check file handle lockID flag
LocalVar qfi_pSFT,    DWORD             ; Ptr to SFt                      ;@SEC

        EnterProc    <ds,si,cx,dx,di,ax>        ; UserBuf,BufSize,Infolev,lock ;@SEC

        mov     qfi_CheckFileHandleLockID,dh    ; check filehandle lockID flag

        xor     dh,dh                           ; allow below to still work
        cmp     dx,QFILEINFOLEV_MAX             ; asking for valid levels?
        ja      short qfi8                      ;   no, go error exit

        or      dx,dx                           ; level 0?
        jz      short qfi8                      ;   yes, go error exit

        call    SFFromHandle                    ; ES:DI -> SF_Entry for file
        mov     ax,ERROR_INVALID_HANDLE         ; anticipate error
        jc      short qfi10                     ; if no handle return error

ifdef SECURITY
        mov     qfi_pSFT.hiword, es             ; Save SFT entry          ;@SEC
        mov     qfi_pSFT.loword, di             ;                         ;@SEC
endif

        call    PhysHandle                      ; physical disk handle?
        jc      short qfi10                     ; yes - return with error

        mov     ah, qfi_CheckFileHandleLockID  ; get check filehandle lockID flag
        test    ah,80h                         ; check filehandle lockID?
        jnz     short qfi10                    ; no, skip check

        mov     ax, qfi_FileHandleLockID.LowWord
        cmp     ax, es:[di].sf_LockID.LowWord  ; check access
        mov     ax, ERROR_ACCESS_DENIED        ; assume access denied
        jne     short qfi9                     ; access denied

        mov     ax, qfi_FileHandleLockID.HighWord
        cmp     ax, es:[di].sf_LockID.HighWord  ; check access
        mov     ax, ERROR_ACCESS_DENIED         ; assume access denied
        jne     short qfi9                      ; access denied
        clc                                     ; make sure carry is clear
        jmp     short qfi10

qfi8:   mov     ax,ERROR_INVALID_LEVEL  ; no, invalid function
qfi9:   stc                             ; indicate error
qfi10:  jc      short qfiexit           ; if error, exit                  ;@SEC
                                        ; otherwise, continue
; BX      = handle of interest
; CX      = size of Info buf
; DX      = level
; DS:SI   -> user buffer
; ES:DI   -> SFT
;
        call    FSD_QFileInfo                                             ;@SEC
        jc      qfiexit                                                   ;@SEC
ifdef SECURITY

        FlatContext <ds>,cx             ;                                 ;@SEC
        cmp     _fSec,0                 ;                                 ;@SEC
        jz      qfiexit                 ; no security.. get outta dodge   ;@SEC
                                        ; AX = 0 and Carry Clear          ;@SEC
        ; restore parameters and call security subsystem.                 ;@SEC
        mov     eax,qfi_UserBuf         ;                                 ;@SEC
        mov      dx,qfi_UserBufSize     ;                                 ;@SEC
        mov      cx,qfi_InfoLevel       ;                                 ;@SEC
        mov     esi,qfi_pSFT            ;                                 ;@SEC

        CALLFAR SecQueryFileInfo        ; Carry and AX will be set        ;@SEC
endif

qfiexit: LeaveProc                                                        ;@SEC
         ret                                                              ;@SEC

EndProc  w_QFileInfo                                                      ;@SEC

;***    w_QPathInfo - Get information for a path
;
;       w_QPathInfo performs the DOSQPATHINFO function call.
;
;       ENTRY   DS:DX -> Pathname
;               ES:DI -> InfoBuf
;               BX -     InfoLevel
;               CX -     size of InfoBuf
;
;       EXIT    'NC' if success
;                   InfoBuf is filled with information about
;                   the path.
;
;               'C' if error
;                   AX = ERROR_BUFFER_OVERFLOW
;                        ERROR_INVALID_LEVEL
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
;       ALGORITHM
;
;           if (InfoLevel > QPATHINFOLEV_MAX)
;               return(invalid level)
;           DoPathInfo(GET)
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_QPathInfo,<HYBRID,far16>    ;01/12/94 Marc P.Louis 76963, add far16
LocalVar wspi_openbuf,PMPATHLEN
LocalVar wspi_dummy,WORD                ; KLUDGE: see gshare.asm
LocalVar wspi_dummycds,<SIZE curdir>
        EnterProc

        cmp     bx,QPATHINFOLEV_MAX
        mov     ax,ERROR_INVALID_LEVEL
        ja      qpi10                   ; Invalid level. Return error.
        lea     si,wspi_dummycds
        push    bx                                      ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,si       ; Initialize TCBpTmpCDS ;### PTR B720732
;       mov     TASKAREA:[bx].TCBpTmpCDS.Segmt,ss       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],si ; Initialize ptda_pCDS
        lea     si,wspi_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,si            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],si ; Initialize ptda_pOpenBuf
        pop     bx                                      ;### PTR B720732
        mov     ax,fGSPI_GETINFO
        xor     si,si                   ; no pathinfo flags for GET
        call    DoPathInfo              ; Get the information
        jmp     short qpix

qpi10:  stc                             ; Indicate error
;### PTR B720732qpix:   mov     SS:[ptda_pOpenBuf],0    ; Clear ptr to Temp Buf
;### PTR B720732        mov     SS:[ptda_pCDS],0        ; Clear ptda_pCDS
qpix:   LeaveProc
        ret

EndProc     w_QPathInfo


;***    w_SetPathInfo - Get information for a path
;
;       w_SetPathInfo performs the DOSSETPATHINFO function call.
;
;       ENTRY   AX -     API calling worker (0 - 16 bit, 1 - 32 bit)
;               DS:DX -> Pathname
;               ES:DI -> InfoBuf
;               BX -     InfoLevel
;               CX -     size of InfoBuf
;               SI -     info flags
;
;       EXIT    'NC' if no error
;                   Info for the pathname set
;
;               'C' if error
;                   AX = ERROR_BUFFER_OVERFLOW
;                        ERROR_INVALID_LEVEL
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
;
;       ALGORITHM
;
;           if (InfoLevel > SINFOLEV_MAX)
;               return(invalid level)
;           if (invalid bits in pathinfo flags)
;               return (invalid parameter)
;           DoPathInfo(SET)
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_SetPathInfo,HYBRID
LocalVar wspi_InfoFlags,WORD
LocalVar wspi_ErrorAx,WORD
LocalVar wspi_openbuf,PMPATHLEN
LocalVar wspi_dummycds,<SIZE curdir>
LocalVar wspi_InfoBuf,<SIZE inbuf>

        EnterProc <SI>                  ; Save local copy of info flags
        cmp     bx,SINFOLEV_MAX
        LJA     spi10                     ; Invalid level. Return error.
        test    si,fSPI_FLAGS_RESERVED    ; any invalid bit set in pathinfo flags ?
        LJNZ    spi30
        cmp     bx,SINFOLEV_FEAL          ; Set EAs?
        je      short spi5                ; yes, skip time/date/attribute stuff
        or      ax,ax
        jnz     short spi5
        push    ds
        push    es
        pusha
        xor     ax,ax                         ; (ax) = query file attribute
        call    w_ChMod                       ; get the attribute
        mov     [wspi_ErrorAx],ax             ; save return code
        and     cl,attr_changeable
        mov     [wspi_InfoBuf].inbuf_attr,cx
        popa
        pop     es
        pop     ds
        jnc     spi5
        mov     ax,[wspi_ErrorAx]             ; get return code
        jmp short spix
spi5:
        push    bx                                      ;### PTR B720732
        lea     si,wspi_dummycds
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,si       ; Initialize TCBpTmpCDS ;### PTR B720732
        lea     si,wspi_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,si            ; Initialize TCBpOpenBuf ;### PTR B720732
        pop     bx                                      ;### PTR B720732
        cmp     bx,SINFOLEV_FEAL          ; Set EAs?
        je      short spi8                ; yes, skip time/date/attribute stuff
        or      ax,ax
        jnz     short spi8
        cmp     cx,SETDATEBUFSIZE
        jge     spi6
        mov     ax,ERROR_INSUFFICIENT_BUFFER
        stc
        jmp short spix
spi6:
        mov     cx,SETDATEBUFSIZE
        push    ax                            ; ax on stack for safe keeping
        push    ds                            ; ds on stack for safe keeping
        push    ss                            ; ss into es (wspi_InfoBuf sel)
        mov     ax,es                         ; load es into temp register
        mov     ds,ax                         ; load ds with former value of es
        pop     es                            ; load es with value of ss
        mov     si,di
        lea     di,wspi_InfoBuf

; DS:SI -> user's passed in InfoBuf
; ES:DI -> our locally defined InfoBuf
; CX    -> user's InfoBufSize

        rep movsb
        lea     di,wspi_InfoBuf
        xor     ax,ax                           ; user's passed in date/time
        mov     es:[di].inbuf_size.hiword,ax
        mov     es:[di].inbuf_size.loword,ax
        mov     es:[di].inbuf_alloc.hiword,ax
        mov     es:[di].inbuf_alloc.loword,ax
        mov     cx,SETINFOBUFSIZE               ; cx = size of fileinfo buffer
        pop     ds                              ; restore ds and ax
        pop     ax
spi8:
        mov     si,[wspi_InfoFlags]     ; retrieve info flags
        mov     ax,fGSPI_SETINFO
        call    DoPathInfo              ; Set the information for the file/dir
        jmp     short spix

spi10:  mov     ax,ERROR_INVALID_LEVEL
        jmp     short spie
spi30:  mov     ax,ERROR_INVALID_PARAMETER
spie:   stc
spix:   LeaveProc
        ret
EndProc     w_SetPathInfo


;***    DoPathInfo - Do the operations for GET/SET path info
;
;       DoPathInfo does the work for both w_QPathInfo and w_SetPathInfo.
;
;       ENTRY   DS:DX -> Pathname
;               ES:DI -> InfoBuf
;               AX -     Flag to indicate what operation to perform
;                           = fGSPI_GETINFO if we are to GET information
;                           = fGSPI_SETINFO if we are to SET information
;               BX -     InfoLevel
;               CX -     size of InfoBuf
;               SI -     Pathinfo flags (in case of setpathinfo only)
;                        = 0 in case of GetPathInfo
;
;       EXIT    'NC' if no error
;                   Info for the pathname set
;
;               'C' if error
;                   AX = ERROR_BUFFER_OVERFLOW
;                        ERROR_INVALID_LEVEL
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
;
;       ALGORITHM
;
;           TransPath(OpenBuf)
;           if (error)
;               return
;           if (Infolevel == QINFOLEV_TRANSPATH) {
;               if (InfoBufLen >= Transformed path)
;                   SuBuff(OpenBuf,InfoBuf)
;               else
;                   ERROR_BUFFER_OVERFLOW
;           }
;           else {
;               if (device OR pipe OR named pipe)
;                   return(invalid access)
;               IncVPBRef()
;               if (FSD)
;                   Set up parms on stack
;                   FS_PathInfo()
;               else
;                   DOS_PathInfo()
;               DecVPBRef()
;           }
;           return any errors
;
;       Change history:
;       ---------------
;       68167  LRN  06/07/93 Must always free the SFT before exiting.
;       69817  LRN  06/10/93 Also set oplockbreak flag before calling Addshare.
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure DoPathInfo,NEAR,LOCAL
LocalVar    dpi_InfoBuf,DWORD           ; Pointer to InfoBuf for returned info
LocalVar    dpi_InfoBufSiz,WORD         ; Size of InfoBuf
LocalVar    dpi_InfoLevel,WORD          ; Level of information requested
LocalVar    dpi_GetSetFlag,WORD         ; Flag for GET or SET
LocalVar    dpi_pathinfoFlag,WORD       ; Flag for write thro bit for SET
LocalVar    dpi_dummycds,<SIZE curdir>

        EnterProc <ES,DI,CX,BX,AX,SI>   ; Initialize local variables
        lea     di,dpi_dummycds
        push    bx                                      ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,di       ; Initialize TCBpTmpCDS ;### PTR B720732
        mov     di,TASKAREA:[bx].TCBpOpenBuf            ; TASKAREA:DI -> openbuf;### PTR B720732
        pop     bx                                      ;### PTR B720732
        mov     si,dx                   ; DS:SI -> PathName
        SaveReg <BP>                    ; Save BP. Destroyed by TransPath.
        call    TransPath               ; Transform the path.
    ASSUME   DS:TASKAREA                ; DS = TASKAREA set by TransPath
        RestoreReg <BP>
        LJC     dpi40                   ; Error. Return it.

; If this is a call to transform the path into a "well formed path", then
; we process it here since the FSD does not need to see the request.
;
        cmp     [dpi_InfoLevel],QINFOLEV_TRANSPATH
                                        ; Start Defect 72417 changes
        LJNE    dpi5                    ; Not the call to transform. Process the request further.
                                        ; End   Defect 72417 changes

;**** Transform the path into a "well formed path"
;
; TASKAREA:[WFP_Start] -> beginning of transformed pathname
;
; Determine the length of the transformed path.
; If it will fit in the user buffer, copy it over.
;
                                        ; Start Defect 72417 changes
        mov     si,CurrTCB
        mov     si,ss:[si].TCBWFP_Start ; DS:SI -> beginning of well formed path
        CALLFAR DStrLen                 ; CX = length of string
        cmp     cx,[dpi_InfoBufSiz]
        LJA     dpi80                   ; Buffer too small. Return error.
        mov     si,CurrTCB
        mov     si,ss:[si].TCBWFP_Start ; DS:SI -> Start of string to copy
                                        ; End   Defect 72417 changes
        mov     bx,[dpi_InfoBuf].Segmt
        mov     di,[dpi_InfoBuf].Offst  ; BX:DI -> User buffer (destination for string)
        CALLFAR SuBuff
        jmp     dpi40                   ; Return any error indications

;**** Process infolevels [1-4,6,7]
;
; The InfoLevel is such that the FSD has to see the request.
;
; We disallow any path that represents a device EXCEPT for
; QINFOLEV_PATHSYNTAX.
; We also don't care to check if any meta characters exist in the last
; component of the path if we are only checking the syntax.
;
dpi5:   cmp     [dpi_InfoLevel],QINFOLEV_PATHSYNTAX
                                        ; Start Defect 72417 changes
        LJE     dpi7                    ; Do not care if path is for a device.
                                        ; End   Defect 72417 changes
;
; For the rest of the infolevels, a device is not allowed. The path must
; either represent a file or a directory.
;
                                        ; Start Defect 72417 changes
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_LOCDEV+PNType_PSDEV
        UnSetBP_CurrTCB
                                        ; End   Defect 72417 changes
        LJNZ    dpi88                   ; Device. Not allowed.
;
; For infolevels 1-4, it does not make sense to have meta-characters in the
; path. TransPath detected any meta chars in the rest of the path, and cMeta
; indicates if any metas are present in the last component of the path.
;

                                        ; Start Defect 72417 changes
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; meta chars present?
        UnSetBP_CurrTCB
                                        ; End   Defect 72417 changes
        mov     ax, ERROR_INVALID_PATH
        LJNE    dpi90                   ; yes, go return error
;
; Add the path to sharing if this is NOT QPathInfo level 1.
; Do not do sharing checks for QPathInfo levels 5 and 6 since they are only
; for transforming/checking paths.
;
dpi7:   cmp     [dpi_InfoLevel],QINFOLEV_TRANSPATH
    ERRNZ <SINFOLEV_MAX GT QINFOLEV_TRANSPATH>
                                        ; Start Defect 72417 changes
        LJB     dpi7a
        cmp     [dpi_InfoLevel],QINFOLEV_FULL_FEAL8   ;###46681 level 8
        LJE     dpi7a                                 ;###46681 Treat as a 3
        cmp     [dpi_InfoLevel],QINFOLEV_CSPRSRVNG
        LJNE    dpi9
dpi7a:
        mov     ax,open_for_write OR sharing_deny_both ; set open mode for set
        mov     cx,opbatchbreak OR oplockbreak   ; break opbatch (and oplock 69817)

        cmp     dpi_GetSetFlag,fGSPI_GETINFO  ; query info?
        LJNE    dpi8                          ; No. Mode is fine.
        cmp     dpi_InfoLevel,QINFOLEV_DATE_TIME    ; QInfo level 1?
        LJE     dpi7b                   ; Yes. Don't enter into sharer for level 1
        cmp     dpi_InfoLevel,QINFOLEV_CSPRSRVNG    ; QInfo level 7?
        LJNE    dpi7c
; for qinfo levels 1 or 7, we need an SFT but we shouldn't enter it into
; sharing we just get an SFT and fill the minimum data in it
dpi7b:
        call    GetSFT                  ; TCBThisSFT -> SFT, BX = SFN
        LJNC    dpi9                    ;
                                        ; End   Defect 72417 changes
        jmp     dpi40
dpi7c:  mov     ax,open_for_read OR sharing_deny_write ; set open mode for query
dpi8:   call    AddShare                ; TCBThisSFT -> SFT, BX = SFN
        LJC     dpi40                   ; sharing failed, go return error
;
; Since this is a "path-based" call, there has been no referent put in the
; VPB for this call. We need to increment the ref-count in the VPB in order
; to ensure that the VPB does not go away while we are working on it.
;
dpi9:   call    PathIncVPBRef

;
; Since we may have blocked above, we need to ensure that the user buffer is
; still valid and in memory.
;
        mov     cx,[dpi_InfoBufSiz]     ; CX = size of Info Buffer
        mov     bx,[dpi_InfoBuf].Segmt
        mov     si,[dpi_InfoBuf].Offst  ; BX:SI -> user InfoBuf; CX = length of buffer
        SaveReg <cx,ds>
;
; If the user passed in a buffer length of zero, we cannot probe for zero
; since ProbeBuff regards that as 64K. So, what we do is to probe for a
; length of 1. This will work since it is not possible to allocate a segment
; of size zero - i.e. we are guaranteed to have at least one byte in the
; segment. This will have the effect of validating the user pointer.
; If the ISS wishes to modify the buffer, it must first check           ;@SEC
; for read/write access (via FSH_ProbeBuff).                            ;@SEC
;
        or      cx,cx                   ; Length of zero?
                                        ; Start Defect 72417 changes
        LJNZ    dpi10                   ; No. Probe length specified.
                                        ; End   Defect 72417 changes
        inc     cx                      ; Yes. Probe only one byte.
dpi10:  mov     dh,ACCTYPE_READ         ; Only need read access for now
        CALLFAR ProbeBuff
        RestoreReg <ds,cx>              ; CX = length of buffer; DS = TASKAREA
    SegAssert SS,<DS>,"DoPathInfo"      ; Ensure DS is still TASKAREA
        LJC     dpi30                   ; GP fault. Remove from sharer and return error.
        mov     dx,[dpi_InfoLevel]      ; DX = Info level requested
        les     di,[dpi_InfoBuf]        ; ES:DI -> InfoBuf for returned info
    ASSUME ES:NOTHING
        mov     bx,[dpi_GetSetFlag]     ; BX = fGSPI_GETINFO or fGSPI_SETINFO
        or      bx,dpi_pathinfoFlag     ;add write thro if needed in case of SET
        mov     si,TASKAREA:[CurrTCB]

ifdef   SECURITY
                                                                        ;@SEC
        test    bx,fGSPI_SETINFO        ; SetPathInfo?                  ;@SEC
        jz      short dpi12             ; No. Skip security callout.    ;@SEC
                                                                        ;@SEC
        FlatContext <ds>                                                ;@SEC
        cmp     _fSec,0                                                 ;@SEC
        jz      short dpi12             ; no security. skip the callout.;@SEC
                                                                        ;@SEC
        SaveReg <bx>
        mov     bx,[dpi_pathinfoFlag]   ; write through flag            ;@SEC
        mov     cx,[dpi_InfoBufSiz]     ; CX = size of Info Buffer      ;@SEC
                                        ; es:di->InfoBuf                ;@SEC
                                        ; WFP_Start->pathname           ;@SEC
                                        ; ds is flat                    ;@SEC
        CALLFAR SecSetPathInfo          ; sets CY and AX if disallowed. ;@SEC
        RestoreReg <bx>
        jc      dpi30                   ; get out. ax should be nonzero.;@SEC
dpi12:                                                                  ;@SEC
        TaskContext <ds>                                                ;@SEC
endif
        cmp     TASKAREA:[si].TCBThisFSC.Offst,FSC_LOCAL    ; what file system?
                                        ; Start Defect 72417 changes
        LJE     dpi20                   ; FAT FS, go call FAT FS worker
                                        ; End   Defect 72417 changes
;
; This is for an FSD. We set up the parameters on the stack and call
; FS_PathInfo.
;
; BX = Flag ("get" or "set")
; SI -> Current TCB
; CX = size of InfoBuf
; DX = Level of information requested
; ES:DI -> InfoBuf
;

        cmp     dpi_InfoLevel,QINFOLEV_CSPRSRVNG    ; QInfo level 7 ?
                                        ; Start Defect 72417 changes
        LJNE    dpi14
                                        ; End   Defect 72417 changes
        push    ds
        push    bx
        lds     bx,TASKAREA:[si].TCBThisFSC
    assume ds:nothing
        lds     bx,ds:[bx].FS_ATTRIBUTE ; (ds:bx) -> FSD attributes
        .errnz  (FS_ATTR_LVL7 GT 0ffh)
        test    byte ptr ds:[bx],FS_ATTR_LVL7
        pop     bx
        pop     ds
    ASSUME   DS:TASKAREA                ; DS = TASKAREA set by TransPath
                                        ; Start Defect 72417 changes
        LJZ     dpi23                   ; Don't call FSD for this level 7 query
                                        ; End   Defect 72417 changes
dpi14:
        push    bx                      ; Flag ("get" or "set")
        mov     bx,TASKAREA:CurrTCB             ; (bx) = current TCB     ;### PTR B720732
        lds     bx,TASKAREA:[bx].TCBThisCDS     ; (ds:bx) = CDS pointer  ;### PTR B720732
;### PTR B720732 lds    bx,[ThisCDS]
    assume ds:nothing
        lea     ax,[bx].cd_fsi
        push    ds
        push    ax                      ; pcdfsi (Pointer to fs independent part of CDS)
        lea     ax,[bx].cd_fsd
        push    ds
        push    ax                      ; pcdfsd (Pointer to fs dependent part of CDS)
        push    ss
        mov     bx,bp
                                        ; Start Defect 72417 changes
        mov     bp,CurrTCB
        push    [bp].TCBWFP_Start       ; pName (Pointer to canonicalized name)
        mov     ax,[bp].TCBCurr_Dir_End
        cmp     ax,-1
        LJZ     dpi18                   ; No current directory in pName
        sub     ax,[bp].TCBWFP_Start    ; AX = offset into pName of end of curdir
dpi18:  push    ax                      ; iCurDirEnd (Offset of current dir in pName)
        mov     bp,bx
                                        ; End   Defect 72417 changes
        push    dx                      ; level (of information)
        push    es
        push    di                      ; pData (address of app. data area)
        push    cx                      ; cbData (length of data area)
        mov     bx,CurrTCB
        lds     bx,TASKAREA:[bx].TCBThisFSC
        call    [bx].FS_PATHINFO
        dec     ax
        sub     ax,-1                   ; Set carry if ax != 0
        jmp     short dpi30             ; Return any errors.

; ShutdownGate  function name, exit label, seg with FSC segment (DS or ES)

dpi20:
        cmp     dpi_InfoLevel,QINFOLEV_CSPRSRVNG    ; QInfo level 7?
                                        ; Start Defect 72417 changes
        LJNE    dpi25
                                        ; End   Defect 72417 changes

; TASKAREA:[WFP_Start] -> beginning of transformed pathname
;
; Determine the length of the transformed path.
; If it will fit in the user buffer, copy it over.
;
dpi23:
                                        ; Start Defect 72417 changes
        mov     si,CurrTCB
        mov     si,ss:[si].TCBWFP_Start ; DS:SI -> beginning of well formed path
        CALLFAR DStrLen                 ; CX = length of string
        cmp     cx,[dpi_InfoBufSiz]
        LJA     dpi80                   ; Buffer too small. Return error.
        mov     si,CurrTCB
        mov     si,ss:[si].TCBWFP_Start ; DS:SI -> Start of string to copy
                                        ; End   Defect 72417 changes
        push    es
        les     di,[dpi_InfoBuf]
        call    StrCpy
        pop     es
        jmp     short dpi30             ; 68167 (formerly jumped to dpi40)

dpi25:  ShutdownGate  DOS_PathInfo, dpi30, DS

; the above jumps to dpi30 on completion

;
; Remove the entry from sharing if this is not a query infolevel 1 call.
;
dpi30:  TaskContext DS
        call    PathDecVPBRef           ; Remove our referent in the VPB
        push    ax                      ; preserve error code
        pushf                           ; save flags
;
; Remove entry from the sharer.
;
; We did not enter paths for InfoLevels 5 and 6 in the sharer, so no need to
; try to remove them.
;
        cmp     [dpi_InfoLevel],QINFOLEV_TRANSPATH
    ERRNZ <SINFOLEV_MAX GT QINFOLEV_TRANSPATH>
                                        ; Start Defect 72417 changes
        LJB     dpi32
        cmp     [dpi_InfoLevel],QINFOLEV_FULL_FEAL8   ;###46681 level 8
        LJE     dpi32                                 ;###46681 Treat as a 3
        cmp     [dpi_InfoLevel],QINFOLEV_CSPRSRVNG
        LJNE    dpi35

dpi32:
        mov     bx,CurrTCB
        les     di,TASKAREA:[bx].TCBThisSFT ; (ES:DI) -> SFT

        cmp     dpi_GetSetFlag,fGSPI_GETINFO  ; query info?
        LJNE    dpi34                   ; No. Remove from sharer.
        cmp     dpi_InfoLevel,QINFOLEV_DATE_TIME    ; QInfo level 1?
        LJE     dpi33                   ; Yes. We did not enter it into the sharer
        cmp     dpi_InfoLevel,QINFOLEV_CSPRSRVNG    ; QInfo level 7?
        LJNE    dpi34                   ; Yes. We did not enter it into the sharer
                                        ; End   Defect 72417 changes

; for qfileinfo levels 1 or 7, we just got an SFT and didn' t enter it into
; sharing So we have to release the SFT alone.
dpi33:
        mov     es:[di].sf_ref_count,0
        call    SFLinkFree              ; release this sft
        jmp     short dpi35
dpi34:  call    RemoveShare
dpi35:  popf                            ; Restore any error indications
        pop     ax                      ; Restore any error code
;### PTR B720732 dpi40: mov     SS:[ptda_pCDS],0        ; Clear ptda_pCDS
dpi40:  LeaveProc
        ret

dpi80:  mov     ax,ERROR_BUFFER_OVERFLOW    ; Buffer too small
        jmp     short dpi90

dpi88:  mov     ax,ERROR_INVALID_ACCESS
dpi90:  stc
        jmp     short dpi40

EndProc     DoPathInfo


; @SEC Move w_EnumAttribute to HIGH3CODE to make room in HIGH2CODE
HIGH2CODE ENDS

        FARCODE HIGH3
HIGH3CODE SEGMENT
    ASSUME  SS:TASKAREA

;***    w_EnumAttribute - Enumerate a file or directory's extended attributes
;
;       w_EnumAttribute performs the DOSENUMATTRIBUTE function call.
;
;       ENTRY   (SS:BX) - parameter packet
;               (AH) = InfoLevel
;               (AL) = RefType
;               If RefType = pathname
;                  (DS:SI) - pointer to path
;               (DX:CX) = File handle lock ID
;
;       EXIT    Carry Clear     - no error
;                          FileInfoBuf is filled with information about
;                          the file.
;
;               Carry Set       - error
;                          AX = ERROR_INVALID_HANDLE
;                               ERROR_ACCESS_DENIED
;                               ERROR_BUFFER_OVERFLOW
;                               ERROR_INVALID_LEVEL
;                               ERROR_DIRECT_ACCESS_HANDLE
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       PSEUDOCODE
;          check ref type and infolevel
;          allocate 64K buffer
;          if reftype = handle
;             w_QFileInfo(level 4)
;          else
;             w_QPathInfo(level 4)
;          copy EnumCnt entries starting at the EntryNumth entry
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_EnumAttribute,FARONLY
LocalVar    ea_ArgFrame,WORD            ; Offset on stack of argument frame
LocalVar    ea_RefType_InfoLevel,WORD   ; Ref Type in low byte
                                        ; InfoLevel in high byte
LocalVar    ea_PathPtr,DWORD            ; PathName
LocalVar    ea_BufPtr,DWORD             ; kernel buffer
LocalVar    ea_UsrBufPtr,DWORD          ; user buffer
LocalVar    ea_FEAListEnd,WORD          ; offset of end of FEA list
LocalVar    ea_UsrBufEnd,WORD           ; offset of end of user buffer (one past last byte)
LocalVar    ea_NumEntries,WORD          ; number of entries to return
LocalVar    ea_EAOP,<size EAOP>         ; number of entries to return
LocalVar    ea_dummycds,<SIZE curdir>
LocalVar    ea_fhlockID,DWORD           ; file handle lockID

        EnterProc <BX,AX,DS,SI>

        mov     ea_fhlockID.LowWord,cx   ; file handle lockID low word
        mov     ea_fhlockID.HighWord,dx  ; file handle lockID high word

        cmp     al,ENUMEA_REFTYPE_MAX          ; invalid ref type?
        LJA     ea120                   ; yes, go return

        cmp     ah,ENUMEA_LEVEL_NO_VALUE ; invalid infolevel?
        LJNE    ea130                   ; yes, go return

        lea     bx,ea_dummycds
        push    si
        mov     si,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[si].TCBpTmpCDS.Offst,bx       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],bx ; Initialize ptda_pCDS
        pop     si

        mov     bx,TASKAREA:ea_ArgFrame ; point to the callers parameter packet.
        mov     ax,TASKAREA:[bx].EEA_iEntryNum.hiword ; is the entry number the
        or      ax,TASKAREA:[bx].EEA_iEntryNum.loword ; reserved value of zero?
        LJZ     ea120                   ; yes, go report the error.

; allocate a 64K-1 buffer to read entire EA list into.

        xor     ax,ax
        mov     bx,MAXEABUFSIZE         ; AX:BX = size of segment to alloc
        mov     ch,D_DATA0
        mov     cl,SA_GDT               ; SFlags and AFlags
                                        ; dx is not used (?)
        mov     si,HF_SWAPPABLE OR HF_NONCONTIG
        mov     di,FSEATMPOWNER
        CALLFAR SegAlloc
        mov     ax,ERROR_NOT_ENOUGH_MEMORY
        LJC     ea110
        mov     ea_BufPtr.Segmt,bx      ; Save selector to buffer
        mov     ea_BufPtr.Offst,0       ; Offset is always zero
;
; set up EAOP in beginning of data buffer
;
        mov     ds,bx                   ;
        xor     si,si                   ; (DS:SI) -> buffer
        lea     di,ea_EAOP              ; (SS:DI) -> EAOP
        mov     TASKAREA:[di].eaop_fpFEAlist.Segmt,bx
        mov     TASKAREA:[di].eaop_fpFEAlist.Offst,si ; set up fpFEAlist
        mov     [si].feal_cblist.hiword,0 ; cblist contains buffer size
        mov     [si].feal_cblist.loword,MAXEABUFSIZE
;
; call w_QPathInfo or w_QFileInfo
;
        cmp     ea_RefType_InfoLevel.LowByte,ENUMEA_REFTYPE_PATH ; get EAs for path?
        jne     ea10                    ; no, go do handle case
        lds     dx,ea_PathPtr           ; (DS:DX) -> pathname
        TaskContext <es>,,NoAssume      ; (ES:DI) -> buffer
        mov     cx,size EAOP            ; (CX) = buffer size
        mov     bx,QINFOLEV_FULL_FEAL   ; (BX) = info level
        CALLFAR w_QPathInfo                                               ;@SEC
        jnc     ea20                    ; go copy FEA list
        jmp     ea100                   ; error, go return

ea10:   mov     bx,TASKAREA:ea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     si,TASKAREA:[bx].EEA_pFileRef.Offst
        mov     bx,TASKAREA:[bx].EEA_pFileRef.Segmt ; (BX:SI) -> handle
        CALLFAR FuWord                  ; (AX) = file handle              ;@SEC
        LJC     ea100                   ; error, go return

        mov     bx,ax                   ; (BX) = file handle
        TaskContext <ds>,,NoAssume
        lea     si,ea_EAOP              ; (DS:SI) -> buffer
        mov     cx,size EAOP            ; (CX) = buffer size
        mov     dx,QINFOLEV_FULL_FEAL   ; (DX) = info level

        push    di
        mov     di,ea_fhlockID.HighWord ; di = filehandle lockID high word
        mov     ax,ea_fhlockID.LowWord  ; ax = filehandle lockID low word

        CALLFAR w_QFileInfo                                               ;@SEC
        pop     di

        LJC     ea100                   ; error, go return
;
; walk EA chain and copy everything but value.
; RPL stamp?
;
ea20:   lds     si,ea_BufPtr            ; (DS:SI) -> FEA list
.errnz feal_cblist
        lodsw                           ; (AX) = list length
        cmp     ax,MINFEALISTSIZE       ; null list?
        je      ea40                    ; yes, go return no entries
        add     si,2                    ; hiword of size must be zero in 1.2
.errnz (feal_list - feal_cblist - 4)    ; (DS:SI) -> first FEA
        mov     ea_FEAListEnd,ax        ; remember end of FEA list
        mov     bx,TASKAREA:ea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     cx,TASKAREA:[bx].EEA_iEntryNum.hiword ; (CX) = entry num
        or      cx,cx
        jnz     ea40                    ; past end of FEA list
ea25:   mov     cx,TASKAREA:[bx].EEA_iEntryNum.loword ; (CX) = entry num
;
; Find the specified starting entry (entry num).
; (AX) -> end of list
; (DS:SI) -> first FEA
; (CX) = entry num
;
ea30:   cmp     si,ax                   ; end of list?
        jae     ea40                    ; yes, go return no entries
        dec     cx                      ; start with this entry?
        jcxz    ea45                    ; yes, go do it
        xor     bh,bh
        mov     bl,[si].fea_cbName      ; no, get pointer to next FEA
        add     bx,[si].fea_cbValue
        add     bx,FEAHDRLEN+1          ; BX = size of this FEA
        add     si,bx                   ; (DS:SI) -> next FEA
        jmp short ea30
;
; if we get here, the entry num specified was beyond the end of the FEA list
; or the FEA list was empty. return zero entries.
;
ea40:   xor     dx,dx                   ; zero entries returned
        jmp     ea90

;
; get the number of entries to return and the length of the user's buffer
; (DS:SI) -> first FEA to copy
;
ea45:   mov     ea_BufPtr.Offst,si      ; save FEA pointer
        mov     bx,TASKAREA:ea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     si,TASKAREA:[bx].EEA_pEnumCnt.Offst ; (BX:SI) -> EnumCnt
        mov     bx,TASKAREA:[bx].EEA_pEnumCnt.Segmt
        push    bx
        CALLFAR FuWord                  ; ax = EnumCnt.loword             ;@SEC
        pop     bx
        LJC     ea100                   ; error, go return
        mov     ea_NumEntries,ax        ; save EnumCnt.loword
        CALLFAR FuWord                  ; ax = EnumCnt.hiword             ;@SEC
        LJC     ea100                   ; error, go return

; For simplicify, we like to use one word to store EnumCnt. So we assume there
; can't be more than 64k EAs (valid for v12). If EnumCnt is > 64k, we set it
; to 65535. The case where EnumCnt = -1 will be taken cared of automatically
; (we will return as many EAs as possible). Actually, EnumCnt = -1 is
; confusing (v12 DCR 105), it should be documented as 2^31-1 since EnumCnt
; should be an unsigned 32-bit number.
; ax = EnumCnt.hiword

        cmp     ax,0                    ; EnumCnt >= 65536?
        je      ea47                    ; no, check if EnumCnt == 0

        mov     ea_NumEntries,65535     ; yes, return as many EAs as possible
        jmp     short ea48

ea47:   cmp     ea_NumEntries,0         ; 0 entries to return?
        LJE     ea100                   ; yes, go return no error

ea48:   mov     bx,TASKAREA:ea_ArgFrame ; (TASKAREA:BX) -> arg frame
        test    TASKAREA:[bx].EEA_cbEnumBuf.hiword,-1 ; > 64K buffer?
        jz      ea50                    ; no, get loword
        mov     dx,-1                   ; (DX) = 64K
        jmp short ea60
ea50:   mov     dx,TASKAREA:[bx].EEA_cbEnumBuf.loword ; (DX) = buffer length

; (DX) = length of user buffer
; get pointer to user buffer

ea60:   mov     bx,TASKAREA:ea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     si,TASKAREA:[bx].EEA_pEnumBuf.Offst
        mov     ea_UsrBufPtr.Offst,si
        mov     bx,TASKAREA:[bx].EEA_pEnumBuf.Segmt  ; (BX:SI) -> user buffer
        mov     ea_UsrBufPtr.Segmt,bx   ; save user buffer pointer
        add     dx,si                   ; (DX) = offset of end of buffer
        jnc     ea70
        mov     dx,-1                   ; overflow, so set offset to 64K
ea70:   mov     ea_UsrBufEnd,dx         ; save pointer to end
        xor     dx,dx                   ; (DX) = number of entries returned

; top of loop to copy entries.
; (DX) = number of entries copied
;
ea80:   cmp     dx,ea_NumEntries        ; enough entries copied?
        je      ea90                    ; yes, go return
        lds     si,ea_BufPtr            ; (DS:SI) -> FEA
        cmp     ea_FEAListEnd,si        ; at end of FEA list?
        jbe     ea90                    ; yes, go return

        mov     cl,[si].fea_cbName
        xor     ch,ch
        add     cx,FEAHDRLEN+1          ; (CX) = size of this FEA without value
        mov     bx,ea_UsrBufPtr.Segmt
        mov     si,ea_UsrBufPtr.Offst   ; (BX:SI) -> user buffer
        mov     ax,si
        add     ax,cx                   ; (AX) -> end of FEA in user buffer
        cmp     ax,ea_UsrBufEnd         ; past end of buffer?
        ja      ea85                    ; yes, go return

        SaveReg <dx,cx>
        mov     dh,ACCTYPE_RW           ; (DH) = access type - read/write
        CALLFAR ProbeBuff               ;                                 ;@SEC
        RestoreReg <cx,dx>              ; (CX) = size of this FEA without value
        jc      ea100                   ; error, go return

; copy the entry to the user's buffer.

        lds     si,ea_BufPtr            ; (DS:SI) -> FEA
        les     di,ea_UsrBufPtr         ; (ES:DI) -> user's buffer
        rep     movsb                   ; copy FEA
        inc     dx                      ; one more FEA copied

        mov     bx,ea_BufPtr.Offst      ; (DS:BX) -> beginning of FEA just copied
        add     si,[bx].fea_cbValue     ; (DS:SI) -> next FEA
        mov     ea_BufPtr.Offst,si      ; save new FEA pointer
        mov     ea_UsrBufPtr.Offst,di   ; save new user buffer pointer
        jmp short ea80

;
; we get here if we overflow the buffer.  if no FEAs copied yet, return
; ERROR_BUFFER_OVERFLOW.  otherwise return the number.
; (DX) = number copied
;
ea85:   or      dx,dx                   ; any FEAs copied?
        stc
        mov     ax,ERROR_BUFFER_OVERFLOW
        jz      ea100                   ; no, go return error
;
; we successfully copied some FEAs to the user buffer
; (DX) = number copied
;
ea90:   mov     ax,dx                   ; (DX) = number copied
        mov     bx,TASKAREA:ea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     di,TASKAREA:[bx].EEA_pEnumCnt.Offst ; (BX:DI) -> EnumCnt
        mov     bx,TASKAREA:[bx].EEA_pEnumCnt.Segmt
        push    bx
        CALLFAR SuWord                  ; return zero entries             ;@SEC
        pop     bx
        jc      ea100                   ; error, go return
        xor     ax,ax                   ; zero upper word
        CALLFAR SuWord                  ; return zero entries             ;@SEC

ea100:  pushf
        SaveReg <AX>                    ; Save any error code
        mov     ax,ea_BufPtr.Segmt
        CALLFAR SegFree                 ; Free the (64K-1) byte segment
        RestoreReg <AX>                 ; AX = error code
        popf

;### PTR B720732 ea110: mov     SS:[ptda_pCDS],0        ; Clear ptda_pCDS
ea110:  LeaveProc
        ret

ea120:  mov     ax,ERROR_INVALID_PARAMETER
        stc
        jmp short ea110

ea130:  mov     ax,ERROR_INVALID_LEVEL
        stc
        jmp short ea110

EndProc w_EnumAttribute

;***    w_ProtectEnumAttribute - Enumerate a file or directory's extended attributes
;
;       w_ProtectEnumAttribute performs the DOSPROTECTENUMATTRIBUTE function call.
;
;       ENTRY   (SS:BX) - parameter packet
;               (AH) = InfoLevel
;               (AL) = RefType
;               If RefType = pathname
;                  (DS:SI) - pointer to path
;               (DX:CX) = File handle lock ID
;
;       EXIT    Carry Clear     - no error
;                          FileInfoBuf is filled with information about
;                          the file.
;
;               Carry Set       - error
;                          AX = ERROR_INVALID_HANDLE
;                               ERROR_ACCESS_DENIED
;                               ERROR_BUFFER_OVERFLOW
;                               ERROR_INVALID_LEVEL
;                               ERROR_DIRECT_ACCESS_HANDLE
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       PSEUDOCODE
;          check ref type and infolevel
;          allocate 64K buffer
;          if reftype = handle
;             w_QFileInfo(level 4)
;          else
;             w_QPathInfo(level 4)
;          copy EnumCnt entries starting at the EntryNumth entry
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_ProtectEnumAttribute,FARONLY
LocalVar    pea_ArgFrame,WORD           ; Offset on stack of argument frame
LocalVar    pea_RefType_InfoLevel,WORD  ; Ref Type in low byte
                                        ; InfoLevel in high byte
LocalVar    pea_PathPtr,DWORD           ; PathName
LocalVar    pea_BufPtr,DWORD            ; kernel buffer
LocalVar    pea_UsrBufPtr,DWORD         ; user buffer
LocalVar    pea_FEAListEnd,WORD         ; offset of end of FEA list
LocalVar    pea_UsrBufEnd,WORD          ; offset of end of user buffer (one past last byte)
LocalVar    pea_NumEntries,WORD         ; number of entries to return
LocalVar    pea_EAOP,<size EAOP>                ; number of entries to return
LocalVar    pea_dummycds,<SIZE curdir>
LocalVar    pea_fhlockID,DWORD           ; file handle lockID

        EnterProc <BX,AX,DS,SI>

        mov     pea_fhlockID.LowWord,cx   ; file handle lockID low word
        mov     pea_fhlockID.HighWord,dx  ; file handle lockID high word

        cmp     al,ENUMEA_REFTYPE_MAX          ; invalid ref type?
        LJA     pea120                  ; yes, go return

        cmp     ah,ENUMEA_LEVEL_NO_VALUE ; invalid infolevel?
        LJNE    pea130                  ; yes, go return

        lea     bx,ea_dummycds
        push    si
        mov     si,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[si].TCBpTmpCDS.Offst,bx       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],bx ; Initialize ptda_pCDS
        pop     si

        mov     bx,TASKAREA:pea_ArgFrame ; point to the callers parameter packet.
        mov     ax,TASKAREA:[bx].PEEA_iEntryNum.hiword ; is the entry number the
        or      ax,TASKAREA:[bx].PEEA_iEntryNum.loword ; reserved value of zero?
        LJZ     pea120                  ; yes, go report the error.

; allocate a 64K-1 buffer to read entire EA list into.

        xor     ax,ax
        mov     bx,MAXEABUFSIZE         ; AX:BX = size of segment to alloc
        mov     ch,D_DATA0
        mov     cl,SA_GDT               ; SFlags and AFlags
                                        ; dx is not used (?)
        mov     si,HF_SWAPPABLE OR HF_NONCONTIG
        mov     di,FSEATMPOWNER
        CALLFAR SegAlloc
        mov     ax,ERROR_NOT_ENOUGH_MEMORY
        LJC     pea110
        mov     pea_BufPtr.Segmt,bx     ; Save selector to buffer
        mov     pea_BufPtr.Offst,0      ; Offset is always zero
;
; set up EAOP in beginning of data buffer
;
        mov     ds,bx                   ;
        xor     si,si                   ; (DS:SI) -> buffer
        lea     di,pea_EAOP             ; (SS:DI) -> EAOP
        mov     TASKAREA:[di].eaop_fpFEAlist.Segmt,bx
        mov     TASKAREA:[di].eaop_fpFEAlist.Offst,si ; set up fpFEAlist
        mov     [si].feal_cblist.hiword,0 ; cblist contains buffer size
        mov     [si].feal_cblist.loword,MAXEABUFSIZE
;
; call w_QPathInfo or w_QFileInfo
;
        cmp     pea_RefType_InfoLevel.LowByte,ENUMEA_REFTYPE_PATH ; get EAs for path?
        jne     pea10                   ; no, go do handle case
        lds     dx,ea_PathPtr           ; (DS:DX) -> pathname
        TaskContext <es>,,NoAssume      ; (ES:DI) -> buffer
        mov     cx,size EAOP            ; (CX) = buffer size
        mov     bx,QINFOLEV_FULL_FEAL   ; (BX) = info level
        CALLFAR w_QPathInfo
        jnc     pea20                   ; go copy FEA list
        jmp     pea100                  ; error, go return

pea10:  mov     bx,TASKAREA:pea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     si,TASKAREA:[bx].PEEA_pFileRef.Offst
        mov     bx,TASKAREA:[bx].PEEA_pFileRef.Segmt ; (BX:SI) -> handle
        CALLFAR FuWord          ; (AX) = file handle
        LJC     pea100                  ; error, go return

        mov     bx,ax                   ; (BX) = file handle
        TaskContext <ds>,,NoAssume
        lea     si,pea_EAOP             ; (DS:SI) -> buffer
        mov     cx,size EAOP            ; (CX) = buffer size
        mov     dx,QINFOLEV_FULL_FEAL   ; (DX) = info level

        push    di
        mov     di,pea_fhlockID.HighWord ; di = filehandle lockID high word
        mov     ax,pea_fhlockID.LowWord  ; ax = filehandle lockID low word

        CALLFAR w_QFileInfo
        pop     di

        LJC     pea100                  ; error, go return
;
; walk EA chain and copy everything but value.
; RPL stamp?
;
pea20:  lds     si,pea_BufPtr           ; (DS:SI) -> FEA list
.errnz feal_cblist
        lodsw                           ; (AX) = list length
        cmp     ax,MINFEALISTSIZE       ; null list?
        je      pea40                   ; yes, go return no entries
        add     si,2                    ; hiword of size must be zero in 1.2
.errnz (feal_list - feal_cblist - 4)    ; (DS:SI) -> first FEA
        mov     pea_FEAListEnd,ax       ; remember end of FEA list
        mov     bx,TASKAREA:pea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     cx,TASKAREA:[bx].PEEA_iEntryNum.hiword ; (CX) = entry num
        or      cx,cx
        jnz     pea40                   ; past end of FEA list
pea25:  mov     cx,TASKAREA:[bx].PEEA_iEntryNum.loword ; (CX) = entry num
;
; Find the specified starting entry (entry num).
; (AX) -> end of list
; (DS:SI) -> first FEA
; (CX) = entry num
;
pea30:  cmp     si,ax                   ; end of list?
        jae     pea40                   ; yes, go return no entries
        dec     cx                      ; start with this entry?
        jcxz    pea45                   ; yes, go do it
        xor     bh,bh
        mov     bl,[si].fea_cbName      ; no, get pointer to next FEA
        add     bx,[si].fea_cbValue
        add     bx,FEAHDRLEN+1          ; BX = size of this FEA
        add     si,bx                   ; (DS:SI) -> next FEA
        jmp short pea30
;
; if we get here, the entry num specified was beyond the end of the FEA list
; or the FEA list was empty. return zero entries.
;
pea40:  xor     dx,dx                   ; zero entries returned
        jmp     pea90

;
; get the number of entries to return and the length of the user's buffer
; (DS:SI) -> first FEA to copy
;
pea45:  mov     pea_BufPtr.Offst,si     ; save FEA pointer
        mov     bx,TASKAREA:pea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     si,TASKAREA:[bx].PEEA_pEnumCnt.Offst ; (BX:SI) -> EnumCnt
        mov     bx,TASKAREA:[bx].PEEA_pEnumCnt.Segmt
        push    bx
        CALLFAR FuWord                  ; ax = EnumCnt.loword
        pop     bx
        LJC     pea100                  ; error, go return
        mov     pea_NumEntries,ax       ; save EnumCnt.loword
        CALLFAR FuWord                  ; ax = EnumCnt.hiword
        LJC     pea100                  ; error, go return

; For simplicify, we like to use one word to store EnumCnt. So we assume there
; can't be more than 64k EAs (valid for v12). If EnumCnt is > 64k, we set it
; to 65535. The case where EnumCnt = -1 will be taken cared of automatically
; (we will return as many EAs as possible). Actually, EnumCnt = -1 is
; confusing (v12 DCR 105), it should be documented as 2^31-1 since EnumCnt
; should be an unsigned 32-bit number.
; ax = EnumCnt.hiword

        cmp     ax,0                    ; EnumCnt >= 65536?
        je      pea47                   ; no, check if EnumCnt == 0

        mov     pea_NumEntries,65535    ; yes, return as many EAs as possible
        jmp     short pea48

pea47:  cmp     pea_NumEntries,0        ; 0 entries to return?
        LJE     pea100                  ; yes, go return no error

pea48:  mov     bx,TASKAREA:pea_ArgFrame ; (TASKAREA:BX) -> arg frame
        test    TASKAREA:[bx].PEEA_cbEnumBuf.hiword,-1 ; > 64K buffer?
        jz      pea50                   ; no, get loword
        mov     dx,-1                   ; (DX) = 64K
        jmp short pea60
pea50:  mov     dx,TASKAREA:[bx].PEEA_cbEnumBuf.loword ; (DX) = buffer length

; (DX) = length of user buffer
; get pointer to user buffer

pea60:  mov     bx,TASKAREA:pea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     si,TASKAREA:[bx].PEEA_pEnumBuf.Offst
        mov     pea_UsrBufPtr.Offst,si
        mov     bx,TASKAREA:[bx].PEEA_pEnumBuf.Segmt  ; (BX:SI) -> user buffer
        mov     pea_UsrBufPtr.Segmt,bx  ; save user buffer pointer
        add     dx,si                   ; (DX) = offset of end of buffer
        jnc     pea70
        mov     dx,-1                   ; overflow, so set offset to 64K
pea70:  mov     pea_UsrBufEnd,dx                ; save pointer to end
        xor     dx,dx                   ; (DX) = number of entries returned

; top of loop to copy entries.
; (DX) = number of entries copied
;
pea80:  cmp     dx,pea_NumEntries       ; enough entries copied?
        je      pea90                   ; yes, go return
        lds     si,pea_BufPtr           ; (DS:SI) -> FEA
        cmp     pea_FEAListEnd,si       ; at end of FEA list?
        jbe     pea90                   ; yes, go return

        mov     cl,[si].fea_cbName
        xor     ch,ch
        add     cx,FEAHDRLEN+1          ; (CX) = size of this FEA without value
        mov     bx,pea_UsrBufPtr.Segmt
        mov     si,pea_UsrBufPtr.Offst  ; (BX:SI) -> user buffer
        mov     ax,si
        add     ax,cx                   ; (AX) -> end of FEA in user buffer
        cmp     ax,pea_UsrBufEnd                ; past end of buffer?
        ja      pea85                   ; yes, go return

        SaveReg <dx,cx>
        mov     dh,ACCTYPE_RW           ; (DH) = access type - read/write
        CALLFAR ProbeBuff               ;
        RestoreReg <cx,dx>              ; (CX) = size of this FEA without value
        jc      pea100                  ; error, go return

; copy the entry to the user's buffer.

        lds     si,pea_BufPtr           ; (DS:SI) -> FEA
        les     di,pea_UsrBufPtr        ; (ES:DI) -> user's buffer
        rep     movsb                   ; copy FEA
        inc     dx                      ; one more FEA copied

        mov     bx,pea_BufPtr.Offst     ; (DS:BX) -> beginning of FEA just copied
        add     si,[bx].fea_cbValue     ; (DS:SI) -> next FEA
        mov     pea_BufPtr.Offst,si     ; save new FEA pointer
        mov     pea_UsrBufPtr.Offst,di  ; save new user buffer pointer
        jmp short pea80

;
; we get here if we overflow the buffer.  if no FEAs copied yet, return
; ERROR_BUFFER_OVERFLOW.  otherwise return the number.
; (DX) = number copied
;
pea85:  or      dx,dx                   ; any FEAs copied?
        stc
        mov     ax,ERROR_BUFFER_OVERFLOW
        jz      pea100                  ; no, go return error
;
; we successfully copied some FEAs to the user buffer
; (DX) = number copied
;
pea90:  mov     ax,dx                   ; (DX) = number copied
        mov     bx,TASKAREA:pea_ArgFrame ; (TASKAREA:BX) -> arg frame
        mov     di,TASKAREA:[bx].PEEA_pEnumCnt.Offst ; (BX:DI) -> EnumCnt
        mov     bx,TASKAREA:[bx].PEEA_pEnumCnt.Segmt
        push    bx
        CALLFAR SuWord                  ; return zero entries
        pop     bx
        jc      pea100                  ; error, go return
        xor     ax,ax                   ; zero upper word
        CALLFAR SuWord                  ; return zero entries

pea100: pushf
        SaveReg <AX>                    ; Save any error code
        mov     ax,pea_BufPtr.Segmt
        CALLFAR SegFree                 ; Free the (64K-1) byte segment
        RestoreReg <AX>                 ; AX = error code
        popf

pea110: LeaveProc
        ret

pea120: mov     ax,ERROR_INVALID_PARAMETER
        stc
        jmp short pea110

pea130: mov     ax,ERROR_INVALID_LEVEL
        stc
        jmp short pea110

EndProc w_ProtectEnumAttribute

                                        ; Start Defect 72417 changes
HIGH3CODE ENDS
                                        ; End   Defect 72417 changes


        FARCODE HIGH2
 HIGH2CODE SEGMENT
    ASSUME  SS:TASKAREA

;***    w_3xEnumAttrib - Enumerate a file or directory's extended attributes
;               for 3xbox
;
;       w_3xEnumAttrib performs the DOSENUMATTRIBUTE function call for 3xbox.
;
;       ENTRY   (ds:si) - parameter packet
;               bx = real handle if ref_type is handle based
;
;       EXIT    Carry Clear     - no error
;                          FileInfoBuf is filled with information about
;                          the file.
;
;               Carry Set       - error
;                          AX = ERROR_BUFFER_OVERFLOW
;                               ERROR_INVALID_LEVEL
;                               ERROR_DIRECT_ACCESS_HANDLE
;
;       USES    ax,bx,cx,dx,si,di,es,flags
;
;       PSEUDOCODE
;               copy the user parameter packet to stack
;               map the 3xbox selectors to protmode GDT sels
;               call the prot mode worker w_enumattribute
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_3xEnumAttrib,far16
LocalVar w3ea_realHandle,WORD   ; 'prot mode' handle for handle based calls
LocalVar w3ea_ArgFrame,<size EnumEAStruc> ; Argument frame to pass to w_EnumAttribute

        EnterProc       <bx>

; copy the frame to the stack.

        push    si
        TaskContext <ES>,di
        lea     di,w3ea_ArgFrame        ; (ES:DI) -> stack argument frame
        mov     bx,di                   ;
        mov     cx,size EnumEAStruc
        rep     movsb                   ; copy argument frame to stack
        pop     si                      ; ds:si -> ptr to parameter packet
        mov     w3ea_realHandle,0       ; initialize to 0 for handle base

; fix up the argument frame.  convert the FileRef ,EnumBuf ,EnumCnt pointers to
; protect mode. (convert fileref only if path based, else use the stackvar for
; fileref)
; SS:BX -> local argframe on stack
; (DS:SI) -> parameter packet
;
        SaveReg <ds,si>

        push    [si].EEA_pFileRef.Segmt  ; fileref segment and offset
        push    [si].EEA_pFileRef.Offst  ;  to use them later
        push    [si].EEA_pEnumBuf.Segmt  ; store enumbug segment and
;
        SaveReg <bx>
        mov     ax,[si].EEA_pEnumCnt.Segmt ; ax = enumcnt segment
        xor     bx,bx
        mov     cx,[si].EEA_pEnumCnt.Offst  ; cx = segment offset
        add     cx,4                        ; (CX) = size of segment
        CALLFAR SegMapVirt                  ; ax=sel = SegMapVirt(ax=seg,bx=0,cx=sz)
    ASSUME DS:NOTHING, ES:NOTHING
        RestoreReg      <bx>
        mov     TASKAREA:[bx].EEA_pEnumCnt.Segmt,ax

        pop     ax                         ; ax = enumbuf segment
        SaveReg <bx>
        xor     bx,bx
        xor     cx,cx                       ; (CX) = size of segment (64K)
        CALLFAR SegMapVirt                  ; ax=sel = SegMapVirt(ax=seg,bx=0,cx=sz)
    ASSUME DS:NOTHING, ES:NOTHING
        RestoreReg <bx>
        TaskContext     <ds>                ; Get DS=TASKAREA to reduce SS: overrides
        mov     TASKAREA:[bx].EEA_pEnumBuf.Segmt,ax

        pop     cx                         ; cx = offset of file ref
        pop     ax                         ; ax = fileref segment

        cmp     TASKAREA:[bx].EEA_iRefType,ENUMEA_REFTYPE_PATH
        je      w3ea10

; getting a fileref ptr for handle ref; we use the stack variable that stores
; the real handle for fileref ptr in case of handle based calls

        lea     si,TASKAREA:w3ea_realHandle     ;
        mov     TASKAREA:[bx].EEA_pFileRef.Offst,si
        mov     TASKAREA:[bx].EEA_pFileRef.Segmt,ss
        jmp     short w3ea20

; path based calls; map the fileref ptr to protect mode

w3ea10: SaveReg <bx>
        xor     bx,bx
        add     cx,MAXPATHLEN           ; (CX) = size of segment
        CALLFAR SegMapVirt                  ; ax=sel = SegMapVirt(ax=seg,bx=0,cx=sz)
    ASSUME DS:NOTHING, ES:NOTHING
        RestoreReg      <bx>
        mov     TASKAREA:[bx].EEA_pFileRef.Segmt,ax
        mov     w3ea_realHandle,-1          ; remember that we are path based

        lds     si,TASKAREA:[bx].EEA_pFileRef   ; (DS:SI) -> pathname
;
; ds:si -> file ref (ptr to file name string or handle)
;
;w3ea20: mov    cx,TASKAREA:[bx].EEA_iInfoLevel.hiword ;

w3ea20:
        mov     dx,TASKAREA:[bx].EEA_iInfoLevel.loword ; (CX:DX) = infolevel
        mov     ax,TASKAREA:[bx].EEA_iRefType ; (AX) = reftype
        mov     ah,dl                         ;  AH = infolevel; AL = reftype
        xor     dx,dx                         ; fake filehandle lockID highword
        xor     cx,cx                         ; fake filehandle lockID lowword
        CALLFAR w_EnumAttribute
        RestoreReg <si,ds>
;
; Free selectors
;
    ASSUME  DS:NOTHING, SS:TASKAREA

        pushf
        push    ax              ; Save possible error code

        lea     bx,w3ea_ArgFrame
        cmp     w3ea_realHandle,-1      ; no need to free gdt sel for
        jne     w3ea30                  ; handle based calls
        mov     ax,TASKAREA:[bx].EEA_pFileRef.Segmt ; AX=selector
        xor     dx,dx           ; DX=default TaskPTDA
        CALLFAR GDTFree         ; GDTFree(ax=sel,dx=TaskPTDA)

w3ea30: mov     ax,TASKAREA:[bx].EEA_pEnumBuf.Segmt ; AX=selector
        xor     dx,dx           ; DX=default TaskPTDA
        CALLFAR GDTFree         ; GDTFree(ax=sel,dx=TaskPTDA)

        mov     ax,TASKAREA:[bx].EEA_pEnumCnt.Segmt ; AX=selector
        xor     dx,dx           ; DX=default TaskPTDA
        CALLFAR GDTFree         ; GDTFree(ax=sel,dx=TaskPTDA)

        pop     ax              ; Restore possible error code
        popf
        LeaveProc
        RETP
EndProc w_3xEnumAttrib

HIGH2CODE ENDS

        FARCODE HIGH4
HIGH4CODE SEGMENT
    ASSUME  SS:TASKAREA

;**     DOS_QFileMode - Get file mode
;
;       This is the low level FAT worker for DosQFileMode.
;
;       ENTRY   [WFP_Start] Points to WFP string ("d:\" must be first 3
;                       chars, NUL terminated)
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else Points
;                        to first char after last "\" of current dir part)
;               [THISCDS] Points to CDS being used
;                       (Low word = -1 if NUL CDS (Net direct request))
;               [Sattrib] Is attribute of search, determines what files
;                       can be found
;
;       EXIT    CARRY CLEAR
;                   AX = Attribute of file
;               CARRY SET
;                   AX is error code
;                       ERROR_FILE_NOT_FOUND
;                               Last element of path not found
;                       ERROR_PATH_NOT_FOUND
;                               Bad path (not in curr dir part if present)
;                       ERROR_NOT_DOS_DISK
;                               Drive not formatted correctly for FAT
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
        ASSUME DS:NOTHING,ES:NOTHING
Procedure DOS_QFileMode,faronly

        call    fValidFATDisk           ; Valid format for FAT FS?
        STC                             ; Assume not valid.
        jz      dqfm30                  ; No. AX = error code. Return error.

                                        ; Start Defect 72417 changes
        SetBP_CurrTCB
        mov     [bp].[TCBNoSetDir],1    ; if we find a dir, don't change to it
        UnSetBP_CurrTCB
                                        ; End   Defect 72417 changes
        TaskContext <ds>
        SaveReg <bp>
        call    GetPath                 ; get directory entry
        RestoreReg <bp>
        jc      dqfm10                  ; error, go report error

        shl     ah,1                    ; device?
        jc      dqfm20                  ; yes, report error

        mov     si,CurrTCB              ; ss:si -> TCB
        cmp     TASKAREA:[si].TCBCurBuf.Offst,-1 ; root directory?
        jz      dqfm8                            ; yes, special case

        mov     ds,TASKAREA:[si].TCBCurBuf.Segmt ; ds:bx -> directory entry
        xor     ah,ah
        mov     al,[bx.dir_attr]        ; ax = attribute

dqfm4:  CALLFAR RlseBuffr
        clc
        ret

dqfm8:  mov     ax, attr_directory      ; ax = root directory attribute
        jmp     short dqfm4

dqfm10: mov     ax,ERROR_PATH_NOT_FOUND ; assume bad path
        jnz     dqfm30                  ; bad path, error exit
        mov     ch,0                    ; prepare for jcxz, carry still set
        jcxz    dqfm30                  ; bad path, error exit
dqfm20: mov     ax,ERROR_FILE_NOT_FOUND ; bad name
dqfm30: ret

EndProc DOS_QFileMode


;**     DOS_SetFileMode - Set File Attributes
;
;       ENTRY   [WFP_Start] Points to WFP string ("d:\" must be first 3
;                       chars, NUL terminated)
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else Points
;                        to first char after last "\" of current dir part)
;               [THISCDS] Points to CDS being used
;                       (Low word = -1 if NUL CDS (Net direct request))
;               [Sattrib] is attribute of search
;                       (determines what files may be found)
;               AX is new attributes to give to file
;
;       EXIT    CARRY CLEAR
;                   No error
;               CARRY SET
;                   AX is error code
;                       ERROR_FILE_NOT_FOUND
;                               Last element of path not found
;                       ERROR_PATH_NOT_FOUND
;                               Bad path (not in curr dir part if present)
;                       ERROR_BAD_CURR_DIR
;                               Bad path in current directory part of path
;                       ERROR_ACCESS_DENIED
;                               Attempt to set an attribute which cannot be set
;                               (attr_directory, attr_volume_ID)
;                       ERROR_SHARING_VIOLATION
;                               Sharing mode of file did not allow the change
;                               (this request requires exclusive write/read
;                                access, INT 24H generated)
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

        ASSUME DS:NOTHING,ES:NOTHING
Procedure DOS_SetFileMode,faronly
LocalVar        dsfm_attr,WORD  ; attribute

        EnterProc <ax>

        call    fValidFATDisk           ; Valid format for FAT FS?
        jz      dsfm50                  ; No. AX = error code. Return error.

                                        ; Start Defect 72417 changes
        SetBP_CurrTCB
        mov     [bp].[TCBNoSetDir],1    ; if we find a dir, don't change to it
        UnSetBP_CurrTCB
                                        ; End   Defect 72417 changes
        TaskContext <ds>
        SaveReg <bp>
        call    GetPath
        RestoreReg <bp>
        jc      dsfm10                  ; error, go error exit

        shl     ah,1                    ; device?
        jc      dsfm20

        mov     ax,dsfm_attr
        mov     di,CurrTCB
        cmp     word ptr TASKAREA:[di].TCBCurBuf,-1 ; root directory?
        jz      dsfm40                              ; yes - return error

        les     di,TASKAREA:[DI].TCBCurBuf
        mov     dl,byte ptr es:[bx].dir_attr        ; dl = old attribute
        and     byte ptr es:[bx].dir_attr,NOT attr_changeable
        or      byte ptr es:[bx].dir_attr,AL
        or      ES:[DI.buf_flags],BUF_DIRTY
        CALLFAR RlseBuffr
        mov     bx,CurrTCB
        mov     ax,TASKAREA:[bx].TCBThishVPB
        push    dx                                  ; DL = attribute
        CALLFAR FlushBuf
        pop     dx
        push    ax
        mov     al,dl                               ; AL = attribute
        mov     ah,fFNY_NODELETE                    ; AH = not for del/ren
        CALLFAR FNotify_Path_Wake
        pop     ax
    assume ds:nothing
        jc      dsfm20

dsfmx:  LeaveProc
        ret

dsfm10: mov     ax,ERROR_PATH_NOT_FOUND ; assume bad path
        jnz     dsfmx                   ; bad path, error exit
        mov     ch,0                    ; prepare for jcxz, carry still set
        jcxz    dsfmx                   ; bad path, error exit
dsfm20: mov     ax,ERROR_FILE_NOT_FOUND ; bad name
        jmp     short dsfmx
dsfm40: mov     ax,ERROR_ACCESS_DENIED
dsfm50: stc
        jmp     short dsfmx
EndProc DOS_SetFileMode


;***    DOS_SetInfo - Sets information for the object depending on the infolevel
;
;       DOS_SetInfo sets the relevant information for the object based on the
;       infolevel passed as a parameter. This routine is called for both the
;       SetInfoForFile and SetPathInfo functions for the FAT file system.
;
;       NOTE
;           This routine assumes that an MFT for the SFT has been
;           already entered into the sharing set.
;
;
;       ENTRY   ES:DI -> SFT
;               DX = Info Level
;               DS:SI -> Buffer for returned information (InfoBuf)
;               For infolevel 2, InfoBuf is an EAOP structure
;                   - eaop_fpGEAlist -> list of GEAs
;                   - eaop_fpFEAlist -> buffer for returned data
;                                  first WORD of buffer contains its size
;               CX = size of InfoBuf
;
;       EXIT    'C' if error
;                   Return error code
;               'NC' if successful
;                   Information returned in buffer
;
;       USES    AX, BX, CX, DX, DS, SI, Flags
;
;       ALGORITHM
;
;           if (infolevel == SINFOLEV_DATE_TIME)
;               SetInfoForFile()
;           else
;               SetFEAInfo()
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure DOS_SetInfo,hybrid

        cmp     dx,SINFOLEV_DATE_TIME
        jne     dsi10                       ; We need to get the extended attributes
        call    SetInfoForFile
        jmp     short dsi30

;
; This is a call to set the extended attributes.
; Make sure the InfoBuf is the correct size
;
dsi10:  cmp     cx,size EAOP
        mov     ax,ERROR_INSUFFICIENT_BUFFER
        jb      dsi30                   ; Infobuf is wrong size. 'C' already set.
        mov     bx,ds                   ; BX:DI -> buffer
        xor     ax,ax
        mov     ds,ax                   ; Do not keep user selector in DS.
        call    SetFEAInfo
;
; Carry flag set if any error encountered.
; AX = error code
;
dsi30:  ret

EndProc DOS_SetInfo


;***    SetInfoForFile - Set the information for the file
;
;       SetInfoForFile sets the information for the file. It performs the
;       operation for w_SetFileInfo when the info-level is 1.
;       We now know that handle is open, that it doesn't point
;       do a device, and isn't open for direct access to a disk,
;       thus, it makes sense to perform this operation on the handle
;
;       ENTRY   CX      = size of FileInfoBuf
;               DS:SI   -> date and time user wants to set for the handle/file
;               ES:DI   -> SFT
;
;       EXIT    Carry Clear     - no error
;                          information for file is set.
;               Carry Set       - error
;                          AX = error code
;
;       USES    ax,bx,cx,dx,si,ds,flags
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure   SetInfoForFile,NEAR
LocalVar        siff_SFTLockAddr,DWORD          ; address of SFT
LocalVar        siff_tmpbuf,SETINFOBUFSIZE      ; tem.buffer for user info

        cmp     cx,SETINFOBUFSIZE               ; buffer big enough?
        mov     ax,ERROR_INSUFFICIENT_BUFFER    ; ax=error code if no
        jnc     siff2                           ; yes, continue
        ret                                     ;buffer not enough

siff2:  EnterProc <es,di>                       ; set up for LocalVar
        push    ds                              ; save the user buf sel
        SaveReg <ax,cx>
        xor     bx,bx
        mov     ds,bx                           ; SFLock may block, clear ds
        CALLFAR SFLock                          ; Lock the sft
        RestoreReg <cx,ax>
        LJC     siff8
        mov     bx,CurrTCB
        mov     word ptr TASKAREA:[bx].TCBThisSFT.Offst,di  ; set ThisSFT
        mov     word ptr TASKAREA:[bx].TCBThisSFT.Segmt,es

; check for protection fault in user buffer
; only way here is to try to copy data from user buffer to local var

        pop     bx                              ; set up for FuBuff call
                                                ; bx:si ->user buff
        TaskContext     ES                      ; get es:di -> siff_tmpbuf
        lea     di, siff_tmpbuf
        mov     cx, SETINFOBUFSIZE              ; count of bytes to transfer
        CALLFAR FuBuff                          ; get the user data
        jc      siff7
        TaskContext     DS                      ;
        lea     si, siff_tmpbuf                 ; get ds:si -> siff_tmpbuf

; Since the spec says create and last access times are not affected, we
; should not generate an error. This way doing a copy from one file system to
; another would not require the copy program to figure out which time
; stamps is supported. However there seemed to be a forgotten reason for
; doing this error check.
ifdef never
        mov     ax, ds:[si].inbuf_cdate         ; make sure first four fields
        or      ax, ds:[si].inbuf_ctime         ;    are all zero
        or      ax, ds:[si].inbuf_ladate
        or      ax, ds:[si].inbuf_latime
        jz      siff6                           ; all zero -- continue on
        mov     ax, ERROR_INVALID_PARAMETER     ; not zero -- error
        stc
        jmp     short siff7
endif

siff6:  mov     dx, ds:[si].inbuf_lwdate        ; put date in dx
        mov     cx, ds:[si].inbuf_lwtime        ; put time in CX
        cmp     TASKAREA:[ProcType],PT_REALMODE ; 3xBox?
        je      siff6a                          ; If yes, then skip zero check
        mov     ax, cx                          ; are date & time zero?
        or      ax, dx
        jnz     siff6a            ; if both zero do not set time, but since
                                  ; we set the attr, we get the time/date out
                                  ; of the sft and continue processing
        les     di,siff_SFTLockAddr
        mov     cx,es:[di].sf_fsi.sfi_mtime
        mov     dx,es:[di].sf_fsi.sfi_mdate
siff6a:
        call    ValidateTmDt                    ; cx = time dx = date
        jnc     siff6b

siff6a1:                                        ; #94997
        stc                                     ; #108995 -- set carry to ensure
                                                ;  error is reported
        mov     ax, ERROR_INVALID_PARAMETER     ; not zero -- error
        jmp     short siff7
siff6b:
        les     di,siff_SFTLockAddr             ;
        mov     es:[di].sf_fsi.sfi_mtime,cx     ; drop in new time
        mov     es:[di].sf_fsi.sfi_mdate,dx     ; and date
        and     es:[di].sf_fsi.sfi_tstamp,NOT ST_SWRITE ; don't stamp mtime
        or      es:[di].sf_fsi.sfi_tstamp,ST_PWRITE ; but do propagate mtime
        xor     ax,ax
        mov     ax,ds:[si].inbuf_attr
        and     al,attr_directory               ; #94997- al contains directory
                                                ;            attribute setting of
                                                ;            input

        jz      short siff6b2                   ; #94997- jump if dir attr not set in input
        mov     al,es:[di].sf_fsi.sfi_DOSattr   ; #108995- get current dir attr setting
        and     al,attr_directory               ; #108995- if current is not a dir, since
                                                ;          the input attrs did have
                                                ;          DIR set, go report an error
        jz      siff6a1

siff6b2:
        mov     ax,ds:[si].inbuf_attr           ; #94997
        mov     es:[di].sf_fsi.sfi_DOSattr,al

        xor     ax,ax
        CALLFAR ShSU                    ; propagate write time to other SFTs

;       mark file as dirty
        and     es:[di].sf_flags,NOT DEVID_FILE_CLEAN
        mov     ah, 0                           ; clear byte and preserve flags
siff7:  les     di,siff_SFTLockAddr
        CALLFAR SFUnlock                        ; Release the SFT
        LeaveProc                               ; clear off LocalVar storage
        ret

siff8:  pop     ax                      ; throw the saved user selector away
        mov     ax,ERROR_INVALID_HANDLE ; CY set already
        jmp     short siff7


EndProc     SetInfoForFile

;       Validates the time and date
;
;       ENTRY   CX      = time
;               DX      = date
;
;       EXIT    Carry Clear     - no error
;                          time and date are valid
;               Carry Set       - error
;
;       USES    none

MASKMONTH       EQU     1E0H            ; bits 8-5
MASKDAY         EQU     1FH             ; bits 4-0
MASKHOUR        EQU     0F800H          ; bits 15-11
MASKMIN         EQU     7E0H            ; bits 10-5
MASKSEC         EQU     1FH             ; bits 4-0

        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure   ValidateTmDt,NEAR
LocalVar        time,WORD
LocalVar        date,WORD

        EnterProc <cx,dx>                       ; set up for LocalVar
        SaveReg <ax>
        mov     ax,date
        and     ax,MASKMONTH                    ; check if month > 12
        cmp     ax,180H
        ja      notvalid
        mov     ax,time
        and     ax,MASKHOUR                     ; check if hours > 23
        cmp     ax,0B800H
        ja      notvalid
        mov     ax,time
        and     ax,MASKMIN                      ; check if minutes > 59
        cmp     ax,760H
        ja      notvalid
        mov     ax,time
        and     ax,MASKSEC                      ; check if seconds > 29
        cmp     ax,1DH
        ja      notvalid
        clc
clearvar:
        RestoreReg <ax>
        LeaveProc                               ; clear off LocalVar storage
        ret
notvalid:
        stc
        jmp     clearvar

EndProc     ValidateTmDt



;***    DOS_QInfo - Gets information for the object depending on the infolevel
;
;       DOS_QInfo gets the relevant information for the object based on the
;       infolevel passed as a parameter. This routine is called for both the
;       GetInfoForFile and GetPathInfo functions for the FAT file system.
;
;       NOTE
;           This routine assumes that an MFT for the SFT has been
;           already entered into the sharing set.
;
;       ENTRY   ES:DI -> SFT
;               CX = size of user buffer
;               DX = Info Level (Already validated by caller)
;               DS:SI -> Buffer for returned information (InfoBuf)
;                        For infolevel 3 and 4, 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
;               'NC' if successful
;                   Information returned in buffer
;
;       USES    AX, BX, CX, DX, DS, SI, Flags
;
;       ALGORITHM
;
;           if (infolevel <= QINFOLEV_EASIZ)
;               GetInfoForFile()
;           else if (infolevel <= QINFOLEV_FULL_FEAL)
;               if (buffer big enough to hold an EAOP)
;                   GetFEAInfo()
;               else
;                   return(ERROR_BUFFER_OVERFLOW)
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure DOS_QInfo,hybrid

        cmp     dx,QINFOLEV_EASIZ
        ja      dgi10                       ; We need to get the extended attributes
        call    GetInfoForFile
        jmp     short dgi30

;
; This is a call to get the extended attributes. We call the routine that will
; differentiate between levels 2 and 3.
;
dgi10:  cmp     cx,size EAOP                ; Buffer should hold EAOP
        jb      dgi40                       ; Buffer not big enough. Return error.
        cmp     dx,QINFOLEV_FULL_FEAL5      ;###46681 If level five we must convert it
        jne     dgi12                       ;###46681 to a level 8 for consistency
        mov     dx,QINFOLEV_FULL_FEAL8      ;###46681 Set dx to 8
dgi12:  call    GetFEAInfo
;
; Carry flag set if any error encountered.
; AX = error code
; ES:DI -> SFT
;
dgi30:  ret

;
; The buffer passed in is not big enough to hold an EAOP. This is an error.
;
dgi40:  mov     ax,ERROR_BUFFER_OVERFLOW
dgi45:  stc
        jmp     short dgi30

EndProc DOS_QInfo


;***    GetInfoForFile - Get the information for the file
;
;       GetInfoForFile gets the information for the file. It performs the
;       operation for w_QFileInfo when the info-level is 1.
;       We know that handle is open, that it doesn't point to a device,
;       and isn't open for direct access to a disk, thus, it makes sense
;       to perform this operation on the handle
;
;       ENTRY   BX      = Handle of interest
;               CX      = size of FileInfoBuf
;               DX      = info level of information requested
;               DS:SI   -> buffer for returned data
;               ES:DI   -> SFT
;
;       EXIT    Carry Clear     - no error
;                          information for file is in InfoBuf.
;               Carry Set       - error
;                          AX = error code
;
;       USES    ax,bx,cx,dx,si,ds,flags
;
         ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure   GetInfoForFile,NEAR
LocalVar        giff_datalen,WORD
LocalVar        giff_sft,DWORD
LocalVar        giff_selector,WORD
LocalVar        giff_offset,WORD
LocalVar        giff_infolevel,WORD
LocalVar        giff_struc,<SIZE inbuf>

;
; OS/2 v1.1 did not allow this call for pipes or devices in protected mode.
;
        test    es:[di].sf_flags,DEVID_DEVICE OR SF_PIPE
        jz      giff5                           ; Not a device or pipe. Continue.
;
; The handle is for a pipe or device. In protected mode, this is an error.
; In the 3xBox, we return the date/time info in the SFT. Command.com relies
; on this.
;
        cmp     TASKAREA:[ProcType],PT_REALMODE ; 3xBox?
        LJNE    giff95                          ; No. Return error.
giff5:  mov     ax,SIZE inbuf                   ; Assume infolevel = 2
        cmp     dx,QINFOLEV_DATE_TIME
        jnz     giff10                          ; Need whole structure
        sub     ax,(SIZE inbuf - inbuf_cblist)  ; Don't need inbuf_cblist
;
; AX = minimum size of infobuf needed to return the information requested
;
giff10: EnterProc  <AX,ES,DI,DS,SI,DX>          ; Init datalen, sft, selector/offset, infolevel
        cmp     cx, ax
        mov     ax,ERROR_BUFFER_OVERFLOW        ; ax = error code if no
        LJB     giff90                          ; Buffer not big enough. Error.

        CALLFAR SFLock                          ; Lock the SFT
        mov     ax,ERROR_INVALID_HANDLE
        LJC     giff70                          ; invalid handle;  return error
        TaskContext DS
        lea     si,giff_struc                   ; DS:SI -> inbuf temp on stack
        xor     ax,ax
        mov     [si].inbuf_ctime,ax             ; the following 4 items are
        mov     [si].inbuf_cdate,ax             ; defined so we return zero
        mov     [si].inbuf_latime,ax
        mov     [si].inbuf_ladate,ax
        mov     al,es:[di].sf_fsi.sfi_DOSattr   ; get file attributes
        mov     [si].inbuf_attr,ax
        mov     ax,es:[di].sf_fsi.sfi_mtime     ; get time
        mov     [si].inbuf_lwtime,ax
        mov     ax,es:[di].sf_fsi.sfi_mdate     ; get date
        mov     [si].inbuf_lwdate,ax
        mov     ax,es:[di].sf_fsi.sfi_size.LowWord      ; get end of data size
        mov     [si].inbuf_size.LowWord,ax
        mov     ax,es:[di].sf_fsi.sfi_size.HighWord
        mov     [si].inbuf_size.HighWord,ax
        mov     bx,es:[di].sf_fsi.sfi_hVPB              ; (BX) = hVPB
;
; If we need to return the size of the FEAlist for this object (infolevel = 2),
; we need to read in the first two bytes off the FEAlist on the disk. These
; will be the size to be returned.
;
        cmp     [giff_infolevel],QINFOLEV_DATE_TIME
        je      giff40                          ; Don't return FEA size
        SaveReg <BX>
        mov     bx,TASKAREA:[CurrTCB]
        mov     TASKAREA:[bx].TCBThisSFT.Segmt,ES   ; Set up for GetFEAlistSize
        mov     TASKAREA:[bx].TCBThisSFT.Offst,DI
        SaveReg <si>
        xchg    si,di                           ; ES:SI -> SFT, DS:DI -> giff_struc
        mov     bx,ds
        lea     di,[di].inbuf_cblist            ; BX:DI -> where to put size of FEAlist
        call    GetFEAlistSize
        RestoreReg <si>                         ; DS:SI -> giff_struc
        RestoreReg <BX>                         ; BX = hVPB
        jc      giff70                          ; Got an error reading disk
;
; BX = hVPB obtained from SFT
;
giff40: or      bx,bx                           ; Is there an hVPB?
    .errnz NULLVPB
        jz      giff50                          ; No. Must be a device or pipe in 3xBox.
        VPBContext es,bx                        ; (ES:BX) -> VPB
        mov     dx,[si].inbuf_size.HighWord     ; DX:AX = file size
        mov     ax,[si].inbuf_size.LowWord
        call    bytesToClusters
        mov     [si].inbuf_alloc.LowWord,ax     ; Invalid if bToC failed
        mov     [si].inbuf_alloc.HighWord,dx    ; Invalid if bToC failed
        jc      giff70                          ; bytesToClusters failed
;
; giff_struc now contains all the relevent information, attempt
; to copy it to user space via subuff.
;
giff50: mov     bx,giff_selector
        mov     di,giff_offset                  ; BX:DI -> user space structure
        mov     cx,giff_datalen                 ; CX = length of data to transfer
        CALLFAR SuBuff                          ; CF = fault status
;
; Carry flag and AX set for us by SuBuff
;
giff70: les     di,giff_sft
        CALLFAR SFUnlock                        ; Release the SFT
giff90: LeaveProc
        ret

;
; The handle passed is invalid. Return error.
;
giff95: mov     ax,ERROR_INVALID_HANDLE
        STC
        ret

EndProc GetInfoForFile



;***    bytesToClusters - calculate allocated bytes from file size
;
;       Round up file size in bytes to multiple of cluster size.
;
;       ENTRY   ES:BX -> file VPB
;               DX:AX = file size
;
;       EXIT    DX:AX = Allocation size
;
;       USES    AX, CX, DX, FLAGS
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure bytesToClusters,NEAR
;       We now calculate the blocking factor.
;       blocking = clusters per sector * bytes per sector

        SaveReg <ax,dx>
        xor     ax,ax
        mov     al,es:[bx].vpb_fsd.vpdFAT_cluster_mask ; (al) = sectors per cluster - 1
        inc     ax                      ; (ax) = sectors per cluster
        mul     es:[bx].vpb_fsi.vpi_cbSector    ; (ax) = bytes per cluster
        mov     cx,ax                   ; (cx) = bytes per cluster

;   alloc size = ((size + blocking - 1) div blocking ) * blocking

        RestoreReg <dx,ax>              ; (dx:ax) = file size
        dec     cx                      ; (cx) = blocking - 1
        add     ax,cx                   ; (ax) = size + blocking - 1
        adc     dx,0                    ; (dx:ax) = size + blocking - 1
        inc     cx                      ; (cx) = blocking
        cmp     dx,cx                   ; will the divide succeed?
        jae     btc1                    ;   NO! exit with error
        div     cx                      ; (ax) = clusters in allocation
        mul     cx                      ; (dx:ax) = bytes in allocation
        clc
        ret

; BUGBUG 07-Dec-1987 philipl  Returning an error is a temporary fix.
; What is really needed here is a hard error popup instructing the user
; to take the appropriate steps to correct the error in his file system
; (run CHKDSK).

btc1:   mov     ax,ERROR_NOT_DOS_DISK   ; dividend too large for divide
        stc
        ret

EndProc bytesToClusters



HIGH4CODE ENDS

        END
