SCCSID  equ     "@(#)fhandle.asm        6.15 92/03/08"
TITLE   FHANDLE - Handle-related system calls
NAME    FHANDLE
;62131
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       File handle related procedures:
;
;       Global          Local
;
;       api_Close
;       w_Close
;       w_3xBufReset
;       w_Read
;       ReadDo
;       w_Write
;       SpoolWrite
;       w_NewSize
;                       FSD_NewSize
;       w_LSeek
;       sft_lseek
;       FSD_ChgFilePtr
;       FileTimes
;       w_Dup
;       w_Dup2
;       api_Dup
;       w_SetMaxFH
;                       GrowJFNSeg
;                       MovJFNToSeg
;       AllocJFNSeg
;       w_QFHandState
;       w_SetFHandState
;       w_QHandType
;       w_FileIO
;                       FileIORouter
;       CmdPktCopy
;       is_device
;       PhysHandle
;       Ext_Information
;       EAOP_selector_alloc
;       EAOP_selector_free
;
; HISTORY
;
; BN001 CP20 PTR B730987  1/16/92  Mark Rogalski
;       Add call to StrCpy to uppercase path name returned from QpathInfo
;       on calls from DEM.
;
; 62131 r206 Carlos A. Romero - Added check for signal from demCopyEAs
; 62131                         not to convert pointer to selector.
;
;105087 r207 Change Team - Roll r206 defect 82170 into r207 sparse tree.
;                          Change FindNewJFNMax to leave JFN_Max a 0-based
;                          number (was returning JFN_Max as a 1-based number).
;
;105415 r207 ChangeTeam  - On HPFS drives, DosSetFileSize was not returning
;                          an error when the file was locked by another process.
;                          Added code to w_NewSize to check for locks.
;
;131445  Change Team     - Propogate return code from FSD_Close rather than
;                          trashing it with  "xor ax,ax" inside w_close
; 72417   MLM  11/27/95  -
;                          Frees up JFN if an error is detected in
;                            w_Dup (JFN is marked busy when allocated now)
;

.xlist
.xcref
include fnofs.inc
include dosseg.inc
include defdbug.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include osconst.inc
include dossymc.inc
include argframe.inc
include devhdr.inc
include error.inc
include fileio.inc
include filemode.inc
include fsc.inc
include fsh.inc
include inbuf.inc
include mi.inc
include mult.inc
include protmode.inc
include virtsym.inc
include seldesc.inc
include sft.inc
include splmsgp.inc
include syscalls.inc
include proc.inc
include pdb.inc
include vpb.inc
include tkexport.inc
include pgexport.inc
include vmexport.inc
include vmowner.inc
include codelock.inc
include mft.inc
include filemacs.inc

.cref
.list
        ModName , FNO_FHANDLE_ASM

        ReferGDT        GDT_VPB,WORD            ; VPB BMP Selector
        ReferGDT        GDT_RLR,WORD

        EXTRN           FuBuff:NEAR
        EXTRN           SFFromHandle:NEAR
        EXTRNFAR        SFFromHandle
        EXTRN           SFWaitIdle:NEAR
        EXTRN           SFLinkFree:NEAR
        EXTRN           SFLock:NEAR
        EXTRN           SFUnlock:NEAR
        EXTRN           ClrSFLock:NEAR
        EXTRN           FSD_Read:NEAR
        EXTRN           FSD_Write:NEAR
        EXTRN           FSD_Write_NT:NEAR
        EXTRN           FSD_Close:NEAR
        EXTRN           SFFromSFN:NEAR
        EXTRN           pJFNFromHandle:NEAR
        EXTRN           FSD_Commit:NEAR
        EXTRN           ShSU:NEAR
        EXTRN           JFNFindFree:NEAR
        EXTRNFAR        Lock_list
        EXTRNFAR        Unlock_list
        EXTRNFAR        FuBuff
        EXTRNFAR        FuWord
        EXTRNFAR        SuWord
        EXTRN           SuBuff:NEAR
        EXTRNFAR        ProbeBuff
        EXTRN           TransactSpooler:NEAR
        EXTRN           w_QFileInfo:NEAR
        EXTRN           w_QPathInfo:NEAR
        EXTRN           w_SetFileInfo:NEAR
        EXTRN           w_SetPathInfo:NEAR
        EXTRN           StrCpy:NEAR             ;BN001
        EXTRN           Chk_Block:NEAR                                 ;105415
        EXTRNFAR        $TJMP

        EXTRNFAR        DOS_Dup
        EXTRNFAR        SegRealloc
        EXTRNFAR        SegMapVirt
        EXTRNFAR        GDTFree
        EXTRNFAR        ProcRun
        EXTRNFAR        NmPipeWrite
        EXTRNFAR        GetTempBuffer
        EXTRNFAR        FreeTempBuffer
ifdef SECURITY
        EXTRNFAR        SecChgFilePtr                                     ;@SEC
        EXTRNFAR        SecNewSize                                        ;@SEC
endif

        EXTRN32         CodeLockProc
        EXTRN32         CodeUnlockProc
        EXTRN32         _SELAllocKHB
        EXTRN32         _SELReallocKHB
        EXTRN32         _SELFreeKHB

                                                ;72417 removed PTDA entry now
                                                ;      in TCB

        ReferTask       ExtErr_Locus,byte       ; Extended Error Locus
        ReferTask       JFN_Flg_Ptr,DWORD       ; ptr to array of don't
                                                ;    inherit flags
        ReferTask       JFN_Length,WORD         ; Length of JFN table
        ReferTask       JFN_pTable,DWORD        ; pointer to JFN table
        ReferTask       JFN_Max,WORD            ; current max. JFN no IN USE
        ReferTask       ptda_handle,WORD        ; handle of ptda segment
        ReferTask       CurrTCB,WORD            ; current thread control block
        ReferTask       ProcType,BYTE

        ReferTask       JFN_Table,WORD          ;
        ReferTask       JFN_Flags,BYTE

ifdef   SECURITY
        ReferFlat       _fSec,BYTE              ;Is security Enabled?     @SEC
endif

        ReferGlobal     DOS_Abort_Close,BYTE

        FARCODE HIGH2
HIGH2CODE SEGMENT

;*********************** START OF SPECIFICATIONS ********************
; SUBROUTINE NAME: api_Close
;
; DESCRIPTIVE NAME:  return a handle to the system
;
; FUNCTION:   This routine will get the SFT for the process based on
;             the handle passed. It will call PhysHandle to check
;             if the handle is for a physical disk. If it isn't, then
;             w_Close will be called to perform the actual work.
;
; NOTES:
;
; ENTRY POINT:        api_Close
;   LINKAGE:          FAR CALL
;
; INPUT: (PARAMETERS)
;             BX -  HANDLE
;             DX:CX -  FILEHANDLELOCKID
;
; EXIT-NORMAL:
;               Carry Clear     - successful
; EXIT-ERROR:
;               Carry Set       - error
;
;               AX = ERROR_INVALID_HANDLE
;                    ERROR_FILE_NOT_FOUND
;                    ERROR_ACCESS_DENIED
;
; EFFECTS: modifies all registers except BP.
;
; INTERNAL REFERENCES:
;  ROUTINES:
;                     w_Close - close handle
;                     PhysHandle - physical disk handle check
;
; EXTERNAL REFERENCES:
;  ROUTINES:
;
;*********************** END OF SPECIFICATIONS **********************

        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure api_Close,hybrid
        PMONLY

;       MMIOPH Performance Hook Removed
;       Maj/Min = 3063H, Data = bx (handle)

        push    dx
        push    cx
        call    SFFromHandle            ; get system file entry
        pop     cx
        pop     dx
        jnc     acl1                    ; handle okay
        jz      acl2                    ; bad handle - return error
; there was format failure; close is the only operation allowed on such
; a handle
acl1:   call    PhysHandle              ; is physical disk handle?
        jc      acl2                    ; yes - return error

        call    w_Close                 ; else do the close
acl2:
        ret

EndProc  api_Close


;***    w_Close - release a handle, and related SFT entry as appropriate
;
;       This routine will get the SFT for the process based on
;       the handle passed.  The JFN entry will be freed.  The reference
;       to the SFT will be removed, which may cause the SFT to be freed.
;       All buffers for this handle will be committed, the directory entry
;       will updated and the device closed.
;
;       NOTE    no error will be returned to the Device mgr.
;
;       NOTE    Getting proper results w.r.t. SFT entry serialization
;               requires the following algorithm for locking the SFT entry.
;
;               if (sf_refcnt == 1)
;                   We're going to free the sf_entry
;                       Since we're freeing the entry, we want all operations
;                       pending on it to clear, so that the SFT doesn't vanish
;                       out from under them.
;                   Since sf_refcnt is one, our process is the only reference
;                   holder, and we zap the JFN, so we can safely wait for all
;                   current operations on the SFT entry to clear before doing
;                   the close.
;                   ->  Wait for ALL operations on SFT to clear.
;               else (sf_refcnt > 1)
;                   We can just do a normal close, using ordinary SFT entry
;                   serialization, so simply lock the SFT.  However, after
;                   we get the lock, we must check that the reference count
;                   is still >1, since we may have blocked waiting for the
;                   lock, and someone else could have done a close while
;                   we were blocked.
;
;       PSEUDOCODE
;           SFFromHandle
;           if (error)
;               if (NZ) {               /* special flag for SFT without VPB */
;                   if (sf_ref_count != 1)
;                       InternalError
;                   no error
;               }
;           if (!error) {
;               set up TCBThisSFT
;               if (sf_ref_count != 1)
;                   some special 70-Mode checks
;               else {
;                   pJFNFromHandle
;                   free JFN and clear JFN flags
;               }
;               if (!3xbox)
;                   if (sf_ref_count != 1) {
;                       SFLock
;                       if (sf_ref_count != 1) { /* check after lock */
;                           FSD_Close
;                           SFUnlock
;                           Wake_plock
;                           jump to wc20
;                       }
;                       SFUnLock
;                       Wake_plock
;                       if (spooled) CloseSpooled
;                   }
;                   SFWaitIdle
;               }
;               Save hVPB & SF_Flags
;               FSD_Close
;               If (SF_Flags.SF_Format_Mount)   /* format mount never cleared */
;                   reset VPB_Flags.VPBFORMATMOUNT
;               if (!pipe)
;                   SFLinkFree
;           }
;   wc20:   if (!error)
;               AH = Close                      /* bogus multiplan fix */
;           else
;               return error
;
;       NOTE    To test if a SFT has been close by w_close, instead of
;               doing
;
;                       w_close(handle)                       DON'T
;                       if (sf_ref_count == 0) {              DO IT
;                         /* sft has been closed */           THIS WAY!!!!
;                       }
;
;               do this instead:
;
;                       save sf_ref_count
;                       w_close(handle)
;                       if (saved sf_ref_count == 1) {
;                         /* sft has been close */
;                       }
;
;               The first test may fail if w_close closes the sft, blocks,
;               and another process grabs the sft before w_close returns
;               to the caller.
;
;       ENTRY   (BX) = handle (which is an index to the JFN.)
;                DX:CX -  FILEHANDLELOCKID
;
;       EXIT    Carry Clear - success
;               Carry Set   - failure
;                   AX = ERROR_INVALID_HANDLE
;                        ERROR_FILE_NOT_FOUND
;                        ERROR_ACCESS_DENIED
;
;       CALLS   SFFromHandle - get SFT from handle
;               FSD_Close - close file via SFT
;               SFLinkFree - release SFT if (ref. count == 0)
;               pJFNFromHandle - return pointer to JFN from handle number
;
;       EFFECTS Frees JFN, dec SFT ref count, may free SFT.
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,flags
;
Procedure w_Close,<near,faronly,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        PMONLY

LocalVar    wcls_handle,WORD            ; the handle
LocalVar    wcls_fhlockid,DWORD         ; filehandlelockID

        EnterProc

        DBPRT   1,60h,<Close 1:60 >
        DBPRT   1,40h,<Handle = $x\n>,<BX>
;
;   Grab the SFT pointer from the JFN.
;

        mov     wcls_fhlockid.LowWord, cx  ; filehandlelockID (low word)
        mov     wcls_fhlockid.HighWord, dx ; filehandlelockID (high word)
        mov     wcls_handle,bx
        push    bx                      ; why push/pop bx?
        call    SFFromHandle            ; get system file entry
        jnc     wcls10                  ; no error, continue
        jnz     wcls10                  ;
        pop     bx                      ;
        jmp     wcls95                  ; bad handle, error return

;   'C' & 'NZ' indicates that BeginFormat failed.  The SFT is still allocated,
;   but there is no VPB attached.  We reset error, and it is handled below.
;   sf_flags2.sf_beginformat_failed is indicator.

wcls10: DBPRT   40h,1,<Close SFT $x:$x\n>,<es,di>

        DosContext <ds>
        cmp     byte ptr DOS_Abort_Close, -1    ; DOS_Abort request?
        mov     byte ptr DOS_Abort_Close, 0
        je      wcls15b                        ; yes, no filehandlelockid
                                               ; check
        mov     si, bx
        mov     bx, wcls_fhlockid.LowWord
        cmp     bx, es:[di].sf_LockID.LowWord  ; check access
        jne     wcls15

        mov     bx, wcls_fhlockid.HighWord
        cmp     bx, es:[di].sf_LockID.HighWord  ; check access
        je      wcls15a                         ; allow access
wcls15:
        mov     ax, ERROR_ACCESS_DENIED        ; access denied
        pop     bx                             ; clear stack
        stc
        jmp     wcls97                         ; exit with cy set

wcls15a:
        mov     bx, si
wcls15b:
        push    bp
        mov     bp,CurrTCB
        mov     word ptr [bp].TCBThisSFT,DI     ; save offset of pointer
        mov     word ptr [bp].TCBThisSFT+2,ES   ; save segment value
        pop     bp
;
; ES:DI -> SFT
;
;   The JFN must be freed.  Get the pointer to it and replace the contents
;   with -1.  Also zero out the JFN_Flg for this handle.
;
wcls20: call    pJFNFromHandle          ;   d = pJFN (handle);
        DBPRT   40h,1,<Close JFN pointer $x:$x\n>,<es,di>
        MOV     word PTR ES:[DI],SFN_NULL ; release the JFN
        les     di,[JFN_Flg_Ptr]        ; get pointer to flags
        mov     byte ptr es:[di+bx],0   ; also clear flags

IFDEF FSSTRICT
        mov     byte ptr es:[di+bx],-1  ; also clear flags
ENDIF

        TaskContext DS
        mov     di,CurrTCB
        les     di,[di].TCBThisSFT      ; es:di -> SFT

;   (ES:DI)    -> SFT
;   TCBThisSFT -> SFT
;       bx     -> real handle

;   Are we the 3xBox?  If so, just go do direct close.

wcls30: cmp     ProcType,PT_REALMODE
        je      wcls60                  ; We're 3xBox, go do simple close

;   We're not the 3xBox.

        call    SFLock                  ; CY->SFT vanished;NC&ZR ->refcount == 1
        pop     bx                      ; bx = real handle
        mov     wcls_handle,bx
        mov     ax,ERROR_INVALID_HANDLE
        jc      wcls40                  ; SFT vanished from under us; cleanup
                                        ; and return
; SFLOCK sets the ZR flag if refcount == 1
; if We are the last reference (if refcount == 1), we can do the last close
;       and free the sft otherwise do normal close

        je      wcls70                  ; We're last ref, go do last close

;   We've locked the SFT entry, and we're not the last reference,
;   so do a normal close.

;   If BeginFormat failed, we skip the FSD_Close.

        test    es:[di].sf_flags2,SF_BEGINFORMAT_FAILED ; did it fail
        jnz     wcls40                                  ; yes, skip FSD_Close

        call    FSD_Close               ; NOTE: Destroys DS

    ASSUME DS:NOTHING

;   we don't call SFLinkFree here because we know the reference count > 0
;   (the SFT will not be freed)

wcls40: mov     di,TASKAREA:CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT   ; (es:di) -> SFT
        cmp     es:[di].sf_plock, 0     ; pending locks ?
        je      wcls50                  ; no
        xor     ax,ax
        mov     bx, wcls_handle
        call    Wake_plock              ; wake up pending locks
wcls50: call    SFUnlock
        jmp     wcls90                  ; almost done, go return

;   If BeginFormat failed, we skip the FSD_Close.

wcls60: pop     bx

wcls70: test    es:[di].sf_flags2,SF_BEGINFORMAT_FAILED ; did it fail
        jz      wcls71                                  ; no, go on
        dec     es:[di].sf_ref_count    ; remove our reference
        pushf                           ; Push flags for later popping at wcls80
        jmp     short wcls75            ; skip FSD_Close

;   If a format mount was done with this SFT, we must reset the flag in the VPB,
;   but only after the close has finished.  This forces a volume check on the
;   next volume reference.
;
;   We always push the hVPB, in case we will need it.  We push the SF_Flags to
;   tell if a format mount was done, and not cleared (by RedetermineMedia).

wcls71: push    es:[di].sf_fsi.sfi_hVPB ; save hVPB (in case we need it later)
        push    es:[di].sf_flags2       ; save flags for later check

wcls73: call    FSD_Close

        pop     cx                      ; retrieve sf_flags
        pop     bx                      ; and hVPB
        push    ax                      ; 131445 - save return code
        pushf                           ; save error state

        test    cx,SF_FORMAT_MOUNT      ; was a format mount un-cleared
        jz      wcls75                  ; no, just go on

;   We now reset the flag in the vpb which allows us to do a volume check
; the next time this vpb is used. As long as this flag is set a new VPB will
; not be generated. We want to avoid this in this case, and so we reset the
; flag.

        VPBContext  ds,bx               ; DS:BX -> VPB
        and     ds:[bx].vpb_flags,(NOT VPBFORMATMOUNT)
                                        ; reset VPBFORMATMOUNT bit
                                        ; this enables us to generate a new
                                        ; VPB next time we use this vpb.

wcls75: test    es:[di].sf_flags,SF_PIPE
        jz      wcls77                  ; not pipe, free SFT
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL
        jb      wcls77                  ; not local pipe, free SFT
        call    SFUnlock
        jmp     wcls80

wcls77: xor     ax,ax
        mov     bx, wcls_handle
        call    Wake_plock              ; Wake up threads with pending locks
        SaveReg <cx>
        call    SFUnlock
        test    es:[di].sf_flags2, SFF2_SPOOLED     ; spooled?
        jz      wcls78                              ; no, proceed
        SaveReg <es, di>
        call    CloseSpooled                        ; tell spooler
        RestoreReg <di, es>
wcls78: RestoreReg <cx>
        jnc     wcls78a

        ; An error occurred, we patch the flags word on the stack
        popf
        add     sp,2                    ; 131445 - pop original error code
        stc
        push    ax                      ; 131445 - save CloseSpooled error code
        pushf
wcls78a:
;       cmp     cx,0
;### AR11589
;### Test flags2 for lock or freed sft                                  ;### DATE 012792
        test    cx, SFF2_LOCKED_DRIVE or sff2_isfree                    ;### DATE 012792

;       jne     wcls80                  ; pending locks                ;### DATE 012792
        jnz     wcls80                  ; pending locks                ;### DATE 012792


wcls79: call    SFWaitIdle              ; wait till SFT is idle
        call    SFLinkFree              ; put SFT on free list, if free

wcls80: popf                            ; restore error state
        pop     ax                      ; 131445 - restore error code

;   FSD_Close may return an error.  If we see such an error, we report it
;   but the JFN stays closed because we always free the SFT!
;
wcls90:
IFDEF FSSTRICT
        pushf
        mov     bx,wcls_handle          ; set jfn flags back to zero
        les     di,[JFN_Flg_Ptr]        ; get pointer to flags
        mov     byte ptr es:[di+bx],0   ; also clear flags
        popf
ENDIF
        jnc      wcls97                 ; no error

        ASSUME   DS:NOTHING

wcls95: DBPRT   1,40h,<Close error $x\n>,<AX>
        jmp short wcls97                        ; return with CY

;   special return value in AH for multiplan

        DBPRT   1,40h,<Close ok\n>
        MOV     AH,CLOSE                ; MZ Bogus multiplan fix
wcls97: LeaveProc
        RETP

EndProc w_Close



;***    CloseSpooled
;
;       Tells the spooler that the file can be printed
;
;       ENTRY   ES:DI -> SFT
;
;       EXIT    CY if error.
;               NC if everything ok.
;
;       USES    ax, bx, cx, dx, ds, si, es, di, Flags
;
Procedure CloseSpooled, near, local
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
LocalVar  wcls_MsgOut, <size MSG_HDR_OUT>   ; Message for spooler
LocalVar  wcls_MsgIn, <size MSG_HDR_IN>     ; Message from spooler
LocalVar  wcls_NmPipeStruc, <size DosTransactNmPipeStruc>

        EnterProc

;   Prepare the message for the spooler

        mov     [wcls_MsgOut].MSGOUT_Header.MHO_Type, HDR_FILECLOSE
        mov     dx, es:[di].sf_fsi.sfi_selfsfn
        mov     [wcls_MsgOut].MSGOUT_Header.MHO_uHandle, dx
        mov     dx, es:[di].sf_NmPipeSfn            ; DX: SFN (pipe)

;   Fill in the fields of the structure for TransactSpooler

        TaskContext <ds>
        mov     [wcls_NmPipeStruc].tnmp_InBufferLen, size MSG_HDR_OUT
        mov     [wcls_NmPipeStruc].tnmp_InBuffer.Segmt,  ds
        lea     di, [wcls_MsgOut]
        mov     [wcls_NmPipeStruc].tnmp_InBuffer.Offst,  di
        mov     [wcls_NmPipeStruc].tnmp_OutBufferLen, size MSG_HDR_IN
        mov     [wcls_NmPipeStruc].tnmp_OutBuffer.Segmt, ds
        lea     di, [wcls_MsgIn]
        mov     [wcls_NmPipeStruc].tnmp_OutBuffer.Offst, di

        lea     si, [wcls_NmPipeStruc]              ; SS:SI -> struct

;   Communicate with spooler
;     SS:SI -> TransactNmPipe structure
;     DX    == SFN (named pipe)

        call    TransactSpooler
        jc      csp90                               ; Error, get out

        mov     ax, [wcls_MsgIn].MSGIN_Header.MHI_Error
        cmp     ax, 0                               ; check error code
        jnz     csp95
csp80:  LeaveProc
        ret

; If TransactSpooler failed, we  return an error code
; meaningful for a Close.

csp90:  mov     ax, ERROR_ACCESS_DENIED             ; NmPipe error
csp95:  stc
        jmp short csp80

EndProc CloseSpooled



;***    Wake_plock
;
;       Wakes up all threads that have pending locks for this SFT
;
;       ENTRY   ES:DI -> SFT
;               AX = 0 -> Wake up only threads belonging to this process
;                         blocked with the handle specified in BX
;                          BX = handle
;               AX != 0 -> Wake up all threads belonging to this process
;                          with pending locks for this SFT
;
;       EXIT    CX = number of pending locks found
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

Procedure Wake_plock, hybrid
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
LocalVar wp_handle,WORD

        EnterProc

        mov     si, es:[di].sf_plock
        cmp     si,0
        je      wp90                         ; no pending locks

        mov     wp_handle,bx
        mov     cx,0
        mov     bx,CurrTCB
        mov     dx,TASKAREA:[bx].TCBProc_ID
        RLRCONTEXT <ds>                 ; ds:si = first pending lock

wp05:   push    ax
        cmp     ax,0
        jne     wp07
        mov     ax, ds:[si].rlr_handle
        cmp     ax, wp_handle           ; is the lock for same handle?
        jne     wp10                    ; no, skip it
wp07:   mov     ax, ds:[si].rlr_PID
        cmp     ax,dx                   ; is the lock for same PID ?
        jne     wp10                    ; no, skip it
        mov     ax, ds
        mov     bx, ds:[si].rlr_block   ; AX:BX -> lock
        inc     cx
        SaveReg <cx>
        CALLFAR ProcRun
        RestoreReg <cx>
        pop     ax

wp10:   mov     si, ds:[si].rlr_next
        cmp     si,0
        jne     wp05

wp90:   LeaveProc
        RETP

EndProc Wake_plock


;***    w_3xBufReset - worker for $BufReset
;
;       ENTRY   (BX) = real handle
;
;       EXIT    Carry Clear
;                       no error
;               Carry Set
;                       AX = error code
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

Procedure w_3xBufReset,far16
    ASSUME      DS:NOTHING,ES:NOTHING,SS:TASKAREA
        PMONLY

        cmp     bl,SFN_NULL             ; free handle?
        je      w3b30                   ; yes, go error exit

        SaveReg <bp>
        call    SFFromHandle              ; es:di -> sft
        jc      w3b10                   ; error, go error exit

        mov   bp,CurrTCB
        mov   [bp].TCBThisSFT.Offst,di
        mov   [bp].TCBThisSFT.Segmt,es

        mov     ax,FS_COMMIT_ONE        ; this commit is for one specific hand.
        call    FSD_Commit

w3b10:  RestoreReg <bp>
w3bx:   RETP

w3b30:  mov     ax,ERROR_INVALID_HANDLE
        stc
        RETP
EndProc   w_3xBufReset


;***    w_Read - read from a handle
;
;       ENTRY   AX      =  non-zero -> check file handle lockID
;               BX      =  handle to read from
;               CX      =  length of buffer = requested transfer length
;               DS:DX   -> target buffer to put data in (may be in user space)
;               DI:SI   =  filehandle lockID
;
;       EXIT    (cx)    number of bytes actually written
;               Carry Clear
;                       AX = number of bytes actually read
;               Carry Set
;                       AX = ERROR_INVALID_HANDLE
;                          = ERROR_ACCESS_DENIED
;
;       CALLS   SFFromHandle, FSD_Read, PhysHandle
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure ReadDo,<far16>

        push    bp                      ; same as EnterProc but keeps MASM
        mov     bp,sp                   ; from complaining about no LeaveProc
        sub     sp,6                    ; make room for 6 bytes of local var's
        call    ReadDo
        mov     sp,bp                   ; same as LeaveProc but keeps MASM
        pop     bp                      ; from complaining about no EnterProc
        RETP
EndProc   ReadDo

Procedure w_Read,<near,faronly,far16>
        PMONLY
LocalVar rdd_checkLockID, WORD
LocalVar rdd_fhLockID, DWORD

        push    bp                      ; same as EnterProc but keeps MASM
        mov     bp,sp                   ; from complaining about no LeaveProc
        sub     sp,6

        mov     rdd_fhLockID.LowWord, si    ; save filehandle lockID
        mov     rdd_fhLockID.HighWord, di
        mov     rdd_checkLockID,ax          ; save check lockID flag

        DBPRT   1,60h,<Read 1:60 >
        DBPRT   1,40h,<Handle $x Cnt $x Buf $x:$x\n>,<BX,CX,DS,DX>

        mov     si,offset DOSHIGH2CODE:FSD_Read
        jmp short rdo10

Entry   WriteDo,<hybrid>

        push    bp                      ; same as EnterProc but keeps MASM
        mov     bp,sp                   ; from complaining about no LeaveProc
        sub     sp,6

        mov     rdd_fhLockID.LowWord, si    ; save filehandle lockID
        mov     rdd_fhLockID.HighWord, di
        mov     rdd_checkLockID,ax          ; save check lockID flag
        mov     si,offset DOSHIGH2CODE:FSD_Write_NT ; API version, CX=0 -> noop

        jmp short rdo10

Entry   ReadDo,<hybrid>

  ; entered only from MVDM code which doesn't
  ; know about protected file handles

        push    bp                  ; same as EnterProc but keeps MASM
        mov     bp,sp               ; from complaining about no LeaveProc
        sub     sp,6

        xor     ax,ax
        mov     rdd_fhLockID.LowWord, ax   ; fake filehandle lockID
        mov     rdd_fhLockID.HighWord, ax
        mov     rdd_checkLockID,1          ; force filehandle lockID check

rdo10:
        push    bp

        call    SFFromHandle            ; get the handle
        jc      rdd6                    ; jump to EXIT if error

;       Check for physical disk handle

        call    PhysHandle              ; do we have a physical disk handle?
        jc      rdd6                    ; yes - return with error

        mov     ax, rdd_checkLockID     ; get lockID
        or      ax,ax                   ; do lockID check
        jz      rdd0                    ; no

        mov     ax, rdd_fhLockID.LowWord
        cmp     ax, es:[di].sf_LockID.LowWord  ; check access
        jne     rdd0a

        mov     ax, rdd_fhLockID.HighWord
        cmp     ax, es:[di].sf_LockID.HighWord  ; check access
        je      rdd0                            ; allow access
rdd0a:
        mov     ax, ERROR_ACCESS_DENIED         ; access denied
        jmp short rdd6                          ; error exit
rdd0:
        mov     bp,CurrTCB
        mov     word ptr [bp].TCBThisSFT,DI     ; save offset of pointer
        mov     word ptr [bp].TCBThisSFT+2,ES   ; save segment value
        mov     word ptr [bp].TCBDMAAdd,DX      ; use user DX as offset
        mov     word ptr [bp].TCBDMAAdd+2,DS    ; use user DS as segment for DMA

        TaskContext DS                  ; go for task addressability

        call    SFLock                  ; Lock the SFT entry
        mov     ax,ERROR_INVALID_HANDLE ; did the SFT vanish ?
        jc      rdd1

;*      Do the I/O

        call    si                      ; call FSD_Read or FSD_Write
    assume ds:nothing

rdd1:   mov     bp,CurrTCB
        les     di,[bp].TCBThisSFT      ; restore pointer to SFT entry
        call    SFUnlock                ; Unlock the SFT entry

;*      Exit processing

        jc      rdd5                    ; if error, EXIT
        mov     ax,cx                   ; get correct return in correct reg
rdd5:
        jmp short rdd7                  ; exit

;*      The error path requires that CX contain the number of bytes
;       read.  For logical errors (as opposed to device errors) CX
;       still contains the count read.  We clear it out and return the
;       error in AX

rdd6:   xor     cx,cx
        stc
rdd7:
        pop     bp
        mov     sp,bp                   ; same as LeaveProc but keeps MASM
        pop     bp                      ; from complaining about no EnterProc
        RETP

EndProc   w_Read


;***    w_Write - write to handle
;
;       ENTRY   AX      =  non-zero -> check file handle lockID
;               BX      =  handle to write to
;               CX      =  number of bytes to write
;               DS:DX   -> source buffer to write from
;               DI:SI   =  filehandle lockID
;
;       EXIT    Carry Clear
;                       No error, but check AX!
;                       AX = number of bytes actually written
;                            (AX != CX at entry should be considered an error)
;               Carry Set
;                       AX = ERROR_INVALID_HANDLE
;                          = ERROR_ACCESS_DENIED
;
;       CALLS   FSD_Write_NT
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       NOTE    Shares code with w_Read
;
;       Algorithm:
;           {
;               WriteDo(data);
;           }
;
Procedure w_Write,<near,faronly,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        PMONLY

        DBPRT   1,60h,<DosWrite 1:60 >
        DBPRT   1,40h,<Handle $x Cnt $x Buf $x:$x\n>,<BX,CX,DS,DX>

        EnterProc

        call    WriteDo

        LeaveProc
        ret
EndProc w_Write



;***    w_NewSize - Force set the size of file associated with a handle
;
;       w_NewSize updates the length field of the SFT for the handle.
;       Note that this change will not be reflected in the directory
;       until the file is closed.
;
;       ENTRY   BX      = Handle of interest
;               CX:DX   = Length to set file to
;               DI:SI   = File Handle LockID
;
;       EXIT    Carry Clear
;                       No error
;               Carry Set
;                       AX = ERROR_INVALID_HANDLE
;                            ERROR_INVALID_PARAMETER
;                            ERROR_DISK_FULL
;
;       CALLS   SFFromHandle, FSD_Write, PhysHandle
;
;       USES    [ThisSFT], [DMAAdd], ax, bx, cx, dx, di, bp(!), es, flags
;
;       WARNING **NO NETWORK SUPPORT IN THIS CODE**
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure w_NewSize,hybrid
        PMONLY
LocalVar FileHandleLockID,DWORD

        EnterProc

        push    bp                      ; save bp; need it for LeaveProc

        or      cx,cx                   ; High order word of length signed?
        jns     wNews1                  ; We have a legal length (non-negative)
        mov     ax,ERROR_INVALID_PARAMETER
wNews0: stc
wNews0a:
        pop     bp
        LeaveProc
        ret                             ; Error exit

wNews1:
        mov     FileHandleLockID.LowWord, si  ; filehandle lockID lowword
        mov     FileHandleLockID.HighWord, di  ; filehandle lockID highword

        call    SFFromHandle            ; (ES:DI) -> SFT
        jc      wNews0                  ; error, go complain

;(@PhysDisk start)    check for physical disk handle

        call    PhysHandle              ; do we have a physical disk handle?
        jc      wNews0                  ; yes - return with error

;(@PhysDisk end)

        mov     ax, FileHandleLockID.LowWord   ; get filehandle lockID
        cmp     ax, es:[di].sf_LockID.LowWord  ; check access
        mov     ax, ERROR_ACCESS_DENIED        ; assume access denied
        jne     wNews0                         ; access is denied

        mov     ax, FileHandleLockID.HighWord   ; get filehandle lockID
        cmp     ax, es:[di].sf_LockID.HighWord  ; check access
        mov     ax, ERROR_ACCESS_DENIED        ; assume access denied
        jne     wNews0                         ; access is denied

;       Check for, and complain about, attempts to set size of direct handles

        test    es:[di].sf_fsi.sfi_mode,open_direct     ; resize on direct handle?
        mov     ax,ERROR_DIRECT_ACCESS_HANDLE   ; assume guilty
        jnz     wNews0                          ; bad operation, go report error

        test    es:[di].sf_flags,SF_PIPE or DEVID_DEVICE
        mov     ax,ERROR_INVALID_HANDLE         ; assume guilty
        jnz     wNews0                          ; bad operation, go report error

        call    SFLock                  ; Lock down the SFT entry
        mov     ax,ERROR_INVALID_HANDLE         ; assume guilty
        jc      wNews90

;       Save the current file position from the SFT

        push    word ptr es:[di].sf_fsi.sfi_position+2
        push    word ptr es:[di].sf_fsi.sfi_position

;       Check FSD file for locks.                                      ;105415

        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL ; what file system?     ;105415
        je      wNews20                 ; FAT, skip lock check         ;105415

        SaveReg <ds,si>                 ;                              ;105415
        lds     si,es:[di].sf_FSC       ; DS:SI-> FSC                  ;105415
        lds     si,[si].FS_ATTRIBUTE    ; DS:SI-> Attr word for FSD    ;105415
        test    word ptr [si],FS_ATTR_LOCKINFO ; FSD handles locking?  ;105415
        RestoreReg <si,ds>              ;                              ;105415
        jnz     wNews20                 ; yes, skip lock check         ;105415

        SaveReg <bx,cx,dx>              ;                              ;105415
        mov     al,RLR_WRITE_ACCESS     ; AL = write access            ;105415
        mov     cx,word ptr es:[di].sf_fsi.sfi_size ; CX = byte count  ;105415
        mov     word ptr es:[di].sf_fsi.sfi_position,0                 ;105415
        mov     word ptr es:[di].sf_fsi.sfi_position+2,0               ;105415
        call    Chk_Block               ; check for locks on file      ;105415
        RestoreReg <dx,cx,bx>           ;                              ;105415
        jnc     wNews20                 ; no locks, go set newsize     ;105415
        mov     ax,ERROR_LOCK_VIOLATION                                ;105415
        jmp     wNews30                 ; return lock violation        ;105415

wNews20:                                                               ;105415
;       Set SFT's current position to match target size

        mov     word ptr es:[di].sf_fsi.sfi_position,dx
        mov     word ptr es:[di].sf_fsi.sfi_position+2,cx

;       Set [ThisSFT] for use by FSD_NewSize

        mov     bp,CurrTCB
        mov     word ptr [bp].TCBThisSFT,di
        mov     word ptr [bp].TCBThisSFT+2,es

;       Set [DMAAdd] so routines internal to FSD_NewSize don't get confused

        mov     word ptr [bp].TCBDMAAdd,0
        mov     word ptr [bp].TCBDMAAdd+2,0

;       FSD_NewSize will do actual truncate/extend for us

        call    FSD_NewSize             ; go do the actual work
                                        ; restore file pointer and return
wNews30:                                                               ;105415
        pop     word ptr es:[di].sf_fsi.sfi_position
        pop     word ptr es:[di].sf_fsi.sfi_position+2

;       Carry flag and AX have been set by FSD_NewSize, or where set by
;       SFFromHandle, thus, we can simply return.
;       If Carry Clear (AX) = TRASH

wNews90: call   SFUnlock                ; release SFT
        jmp     wNews0a               ; exit

EndProc   w_NewSize


;**     FSD_NewSize - dispatch NewSize to proper file system
;
;       For Fat, FSD_NewSize calls FSD_WRITE with cx = 0 (truncate)
;        FSD_WRITE checks for file system and calls FS_NEWSIZE
;       But to be safe,and consistent, we check for file system
;       and route ourselves here
;
;       ENTRY   es:di -> sft            (locked by caller)
;               sf_fsi_sfi_position - set to new file size
;               TCBThisSFT      - set to this SFT
;               TCBDMAAdd set
;
;       EXIT    if 'NC' - success
;               if 'C' - error
;                 ax = error code
;
;       USES    ax,bx,cx,DS,si,flags

Procedure FSD_NewSize,<near,far16>

ifdef SECURITY  ;@SEC
        CpuMode 386
        FlatContext  <DS>,ax
        cmp     _fSec,0             ; security enabled?
        jz      fnsz2               ; no, skip it.
        CALLFAR SecNewSize
        or      ax,ax               ; bad rc?
        jnz     fnsz21              ; yes - set carry and exit
fnsz2:
endif
; dispatch to proper file system

        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL ; what file system?
        jb      fnsz40                  ; fsd, go call fsd worker

; FAT code

; FSD_WRITE also calls FS_NEWSIZE in this case but only after lot of code.
;
; We need to check to make sure that the SFT was opened with write access.
; We only do the checking for the FSD case here since FSD_Write will check
; it for us for the FAT FS case.
;
        xor     cx,cx                   ; we want truncate, not real transfer
        TaskContext DS                  ; set up addressabilty FSD_Write needs
        ASSUME  DS:NOTHING
        SaveReg <es,di>                 ; save sft pointer
        call    FSD_Write
        RestoreReg      <di,es>         ; restore SFT pointer
        jc      fnsz30                  ; some unexpected error on FSD_Write

;       FSD_Write will not complain if there isn't enough space on the disk
;       for the file's new size, it just won't change the size file in the
;       SFT.  Therefore, we check here to see if the file's new size is what
;       we want.  This reveals whether the operation failed due to lack of
;       space.

fnsz10: mov     ax,word ptr es:[di].sf_fsi.sfi_position
        cmp     ax,word ptr es:[di].sf_fsi.sfi_size
        jnz     fnsz20                  ; error, complain about lack of space

        mov     ax,word ptr es:[di].sf_fsi.sfi_position+2
        cmp     ax,word ptr es:[di].sf_fsi.sfi_size+2
        jz      fnsz30                  ; size and position match -> success

;       Set error code and carry flag before restoring file pointer

fnsz20: mov     ax,ERROR_DISK_FULL      ; insufficient space for user's request
fnsz21: stc
fnsz30: RETP

; Check for write access on the handle.
;
; Set up parameters and call fs_newsize
;
fnsz40: mov     al,BYTE PTR ES:[DI.sf_fsi.sfi_mode]
        and     al,open_access
        cmp     al,open_for_read
        je      fnsz50                  ; invalid access, go error exit
        SaveReg <es,di>                 ; save pointer to SFT
        lea     ax,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
                                        ; push the new length desired
        push    word ptr es:[di].sf_fsi.sfi_position.hiword
        push    word ptr es:[di].sf_fsi.sfi_position.loword
        get_advise                      ; ax = advisory bits
        push    ax                      ; push advisory bits
        lds     si,es:[di].sf_FSC       ; ds:si -> fsc
        call    ds:[si].FS_NEWSIZE
        RestoreReg <di,es>              ; es:di -> sft
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        jc      fnsz30                  ; error, go error exit
        jmp     short fnsz10            ; go check whether newsize was
                                        ; successful

fnsz50: mov     ax,ERROR_ACCESS_DENIED  ; Return access denied error
        stc
        jmp     short fnsz30

EndProc FSD_NewSize

;***    w_LSeek - move r/w pointer for a handle
;
;       w_LSeek moves the r/w pointer for a handle, and returns the
;       new value of the pointer.
;
;       ENTRY   AX      - method
;                         0 = absolute position
;                         1 = seek relative to current position
;                         2 = seek relative to eof
;               BX      - handle to perform seek on
;               CX:DX   - distance to move in bytes (or absolute position)
;               SI:DI   - file handle lock ID
;
;       EXIT    if 'NC' - success
;                 dx:ax = current absolute file position
;               if 'C' - error
;                 ax = error code
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       NOTE    may block

Procedure w_LSeek,<near,faronly,far16>
    ASSUME      DS:NOTHING,ES:NOTHING,SS:TASKAREA
        PMONLY
LocalVar wlsSeekType,WORD               ; seek type
LocalVar wlsDistance,DWORD              ; distance to move (seek)
LocalVar wlsfhlockid,DWORD              ; file handle lockID

        EnterProc

        mov     wlsfhlockid.HighWord, si ; si:di = file handle lockID
        mov     wlsfhlockid.LowWord, di
        mov     wlsSeekType, ax          ; save seek type
        mov     wlsDistance.HighWord,cx  ; save distance to move (highword)
        mov     wlsDistance.LowWord,dx  ; save distance to move (lowword)

;       MMIOPH Performance Hook Removed.
;       Maj/Min = 3062H, Data = bx (handle)

; bx = handle

        call    SFFromHandle            ; es:di -> sft
        jc      wlsx                    ; error return

        call    PhysHandle              ; do we have a physical disk handle?
        jc      wlsx                    ; yes - return with error

        mov     ax, ERROR_ACCESS_DENIED        ; anticipate error
        mov     bx, wlsfhlockid.LowWord
        cmp     bx, es:[di].sf_LockID.LowWord  ; check access
        je      wls10
wls05:  stc
        jmp short wlsx                        ; access denied
wls10:
        mov     bx, wlsfhlockid.HighWord
        cmp     bx, es:[di].sf_LockID.HighWord  ; check access
        jne     wls05                           ; deny access
        mov     cx, wlsDistance.HighWord        ; get distance to move (highword)
        mov     dx, wlsDistance.LowWord         ; get distance to move (lowword)
        mov     ax, wlsSeekType                 ; ax = seek type (i.e. 0,1,2)
        call    sft_lseek
wlsx:
        LeaveProc
        RETP

EndProc w_LSeek


;**     sft_lseek - lseek worker
;
;       ENTRY   AX      = method
;                         0 = absolute position
;                         1 = seek relative to current position
;                         2 = seek relative to eof
;               CX:DX   = distance to move in bytes (or absolute position)
;               ES:DI  -> sft
;
;       EXIT    if 'NC' - success
;                 dx:ax = current absolute file position
;               if 'C' - error
;                 ax = error code
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       NOTE    may block
;
;       ALGORITHM
;
;       if (pipe or device)
;         return ERROR_SEEK_ON_DEVICE
;       if (seek method > 2)
;         return ERROR_INVALID_FUNCTION
;       SFLock
;       if (seek method == 1)  /* seek relative to current */
;         abs_position = distance + sfi_position
;       else if (seek method == 2) {  /* seek relative to eof */
;         abs_position = distance + sfi_size
;       }
;       if (abs_position < 0)
;         if (! 3xbox) {
;           SFUnlock
;           return ERROR_NEGATIVE_SEEK
;         }
;       if (direct access device)
;         if (abs_position > device size) {
;           SFUnlock
;           return ERROR_DIRECT_ACCESS_HANDLE
;         }
;       call FSD_ChgFilePtr
;       SFUnlock

Procedure sft_lseek,<hybrid, far16>
    assume ds:nothing,es:nothing

LocalVar sfl_distance,DWORD             ; signed distance (offset)
LocalVar sfl_type,WORD                  ; seek type (0, 1, or 2)
LocalVar sfl_lpSFT,DWORD                ; -> sft

        EnterProc <cx,dx,ax>            ; distance = cx:dx, sfl_type = ax

        cmp     ax,2                    ; seek type valid?
        ja      sfl200                  ; no, go error exit

        test    es:[di].sf_flags,SF_PIPE or DEVID_DEVICE ; seek on device?
        jnz     sfl240                  ; yes, go error exit

        call    SFLock                  ; lock sft
        mov     ax,ERROR_INVALID_PARAMETER      ; did the SFT vanish ?
        jc      sfl50
        mov     ax,sfl_distance.loword
        mov     dx,sfl_distance.hiword  ; dx:ax = distance
        mov     bx,sfl_type             ; bx = bl = seek type

        cmp     bl,1                    ; fast test for 0, 1, 2
        jb      sfl20                   ; 0, go do absolute seek
        ja      sfl10                   ; 2, go do eof-relative seek

;       do seek relative to current position
;       (bx)    = type of seek
;       (dx:ax) = relative distance to current pos
;       (es:di) -> sft

        add     ax,es:[di].sf_fsi.sfi_position.loword
        adc     dx,es:[di].sf_fsi.sfi_position.hiword ; dx:ax = abs offset
        jmp     short sfl20             ; go do absolute seek

;       do eof-relative seek
;       (bx)    = type of seek
;       (dx:ax) = relative distance to EOF
;       (es:di) -> sft

sfl10:  mov     ax,sfl_distance.loword
        mov     dx,sfl_distance.hiword  ; dx:ax = distance
        add     ax,es:[di].sf_fsi.sfi_size.loword
        adc     dx,es:[di].sf_fsi.sfi_size.hiword ; dx:ax = abs offset

;       do absolute seek
;       (bx)    = type of seek
;       (dx:ax) = absolute distance
;       (es:di) -> sft

sfl20:  or      dx,dx                   ; negative absolute offset?
        js      sfl210                  ; yes, go error exit

;       Check for trying to "extend" direct access devices.

sfl30:
sfl40:  mov     si,sfl_distance.loword
        mov     cx,sfl_distance.hiword  ; cx:si = distance

;       (bx)    = type of seek
;       (es:di) -> locked SFT
;       (dx:ax) = absolute position
;       (cx:si) = distance of seek

        call    FSD_ChgFilePtr

sfl50:  call    SFUnlock                ; release SFT
sflx:   LeaveProc
        RETP

sfl200: mov     ExtErr_Locus,ERRLOC_UNK ; Extended Error Locus
        mov     ax,ERROR_INVALID_FUNCTION  ; invalid method
sfl205: stc
        jmp     short sflx

; process negative seek error
; Seeks to negative offsets in a file are:
;     Protect mode:  not allowed, return(ERROR_NEGATIVE_SEEK)
;     Real mode:     allowed for DOS 3.x compatability, some apps depend on
;                    being able to seek to negative offset.
;                    subsequent read will return 0 byte
;                    subsequent write will return ERROR_DISK_FULL

sfl210: cmp     ProcType,PT_REALMODE    ; 3xbox?
        je      sfl30                   ; ok, let it pass (sigh)

        mov     ax,ERROR_NEGATIVE_SEEK
sfl220: stc
        jmp     short sfl50


sfl240: mov     ax,ERROR_SEEK_ON_DEVICE
        jmp     short sfl205

EndProc sft_lseek



;**     FSD_ChgFilePtr - dispatch lseek to proper file system
;
;       Note that there are redundancy in the parameters passed in. Given
;       seek type and distance we can compute absolute offset. But we want
;       to avoid re-doing the computation again. Seek type and distance are
;       passed directy to FSD worker instead of the absolute offset so it
;       may do optimization.
;
;       ENTRY   es:di -> sft            (locked by caller)
;               dx:ax = absolute offset (used by fat worker)
;               cx:si = distance        (used by fsd worker)
;               bx    = seek type       (used by fsd worker)
;               TCBJfnFlag = handle flags (write thru and no-cache)
;
;       EXIT    if 'NC' - success
;                 dx:ax = current absolute offset
;               if 'C' - error
;                 ax = error code
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

Procedure FSD_ChgFilePtr,<hybrid, far16>
       LocalVar         FakeAbs,DWORD
       EnterProc
    assume ds:nothing,es:nothing

ifdef SECURITY ;@SEC
        CpuMode  386
        FlatContext <ds>
        cmp     _fSec,0
        jz      fcf01
        SaveReg <di>              ; preserve di around call
        CALLFAR SecChgFilePtr
        mov     FakeAbs,edi       ; Save "fake" offset for later
        RestoreReg <di>
        CpuMode  reset
    assume ds:nothing
fcf01:
endif

; dispatch to proper file system

        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL ; what file system?
        jb      fcf10                   ; fsd, go call fsd worker
CpuMode 386
; fat worker

        SaveReg <ecx,ebx>
        mov     ecx,es:[di].sf_fsi.sfi_position

        mov     es:[di].sf_fsi.sfi_position.loword,ax
        mov     es:[di].sf_fsi.sfi_position.hiword,dx

        or      dx,dx                   ; negative absolute offset?
        js      short fcf2              ; yes, leave rahead alone

        mov     ebx,es:[di].sf_fsi.sfi_position

; If the new positon is below our previous position then turn off rahead.

        cmp     ebx,ecx
; If the new position is equal to our previous then leave rahead alone.

        je      short fcf2
        ja      short fcf0

        sub     ecx,ebx
        cmp     ecx,MAX_READLEN
        ja      short fcf1
        jmp     short fcf2

; If the difference between the new postion and the old position is > 4k turn
; rahead off.

fcf0:   sub     ebx,ecx
        cmp     ebx,MAX_READLEN
        ja      short fcf1

; If the difference between the new postion and the old position is > 512 turn
; off big reads.

        cmp     ebx,MAX_READLEN
        jbe     fcf2
        mov     es:[di].sf_fsd.sfdFAT_bRABigReads,0
        and     es:[di].sf_flags2,NOT (sff2_RA_BIG)
        jmp     short fcf2

fcf1:   mov     es:[di].sf_fsd.sfdFAT_bRAReads,0
        mov     es:[di].sf_fsd.sfdFAT_bRABigReads,0
        and     es:[di].sf_flags2,NOT (sff2_RA_ON + sff2_RA_BIG)

fcf2:   RestoreReg <ebx,ecx>
        clc
CpuMode reset
        jmp short fcf15

; setup parameters and call FS_CHGFILEPTR
; es:di -> sft
; cx:si = distance
; bx = seek type

fcf10:  SaveReg <es,di>                 ; save pointer to SFT

        lea     ax,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    cx
        push    si                      ; push distance
        push    bx                      ; push seek type
        get_advise                      ; ax = advisory bits
        push    ax                      ; push advisory bits
        lds     si,es:[di].sf_FSC       ; ds:si -> fsc
        call    ds:[si].FS_CHGFILEPTR
        RestoreReg <di,es>              ; es:di -> sft
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        jc      fcfx                    ; error, go error exit

        mov     ax,es:[di].sf_fsi.sfi_position.loword
        mov     dx,es:[di].sf_fsi.sfi_position.hiword ; dx:ax = abs position

fcf15:
ifdef SECURITY   ;@SEC
        FlatContext <ds>
        cmp     _fSec,0
        jz      fcfx
        ;  Some security subsystems prepend data to the user file
        ;  in this case we need to fix it up so the application doesn't
        ;  see this.
        mov     ax,FakeAbs.loword
        mov     dx,FakeAbs.hiword
endif

fcfx:   LeaveProc
        ret

EndProc FSD_ChgFilePtr


;***    FileTimes - get/set write times on a handle
;
;       ENTRY   AL      - Get/Set selector
;                         AL = 0 -> get, write date/time returned
;                         AL = 1 -> set, write date/time set
;               BX      - (real) handle of interest
;               CX      - if (AL = 1) time to set
;               DX      - if (AL = 1) date to set
;
;       EXIT    NC      - no error
;                   CX      - if (AL = 0) last write time from handle
;                   DX      - if (AL = 0) last write date from handle
;               CY      - error
;                   AX      - error code in { ERROR_INVALID_FUNCTION,
;                                             ERROR_INVALID_HANDLE    }
;
;       CALLS   w_SetFileInfo,w_QFileInfo
;       USES    ax,es,di,si,ds,cx,dx
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure FileTimes,<near,faronly,far16>
        PMONLY
LocalVar        ft_dttime,<size inbuf>  ; structure needed for qfileinfo
                                        ; and setfileinfo
        cmp     al,2                    ; correct subfunction?
        jb      fts10                   ; Yes, continue
        mov     ExtErr_Locus,ERRLOC_UNK ; Extended Error Locus
        mov     ax,ERROR_INVALID_FUNCTION  ; give bad return
        stc
        RETP                            ; return with CY -> error
fts10:  EnterProc                       ;
        SaveReg <ds,si>
        push    ss
        pop     ds                      ;
        assume  ds:nothing
        lea     si,ft_dttime            ;ds:si -> ft_dttime - fileinfo buffer
        or      al,al                   ; get or set ?
        je      ftsGet                  ;

; set the file (write) times by calling w_setFileInfo

        xor     ax,ax                   ;
        mov     [si].inbuf_lwdate,dx    ; set the date and time properly
        mov     [si].inbuf_lwtime,cx    ;
        mov     [si].inbuf_cdate,ax     ; other fields = 0 -> no change
        mov     [si].inbuf_ctime,ax     ;
        mov     [si].inbuf_ladate,ax    ;
        mov     [si].inbuf_latime,ax    ;
        mov     cx,SETDATEBUFSIZE       ; cx = size of datetime buffer
        mov     dx,SINFOLEV_DATE_TIME   ; level 1
    ; ax already set to 0 so not done here
    ;    xor   ax,ax                     ; fake filehandlelockID lowword
        xor   di,di                     ; fake filehandlelockID highword
        call    w_SetFileInfo           ; ds:si ->fileinfobuffer; bx = handle

fts20:  RestoreReg      <si,ds>
        LeaveProc
        RETP

ftsGet: mov     dx,QINFOLEV_DATE_TIME   ; level 1
        mov     cx,size inbuf           ;
        xor   ax,ax                     ; fake filehandlelockID lowword
        xor   di,di                     ; fake filehandlelockID highword
        call    w_QFileInfo             ;ds:si->fileinfobuf; bx = handle
        jc      fts20                   ; error return
        mov     dx,ft_dttime.inbuf_lwdate       ; get date and time from
        mov     cx,ft_dttime.inbuf_lwtime       ; inbuf struc
        jmp     short fts20             ;

EndProc   FileTimes


;***    w_Dup - duplicate a JFN
;
;       ENTRY   BX      - file handle to duplicate
;
;       EXIT    NC      - no error
;                         (AX) = new file handle
;               CY      - error
;                         (AX) = error code in { dup_invalid_handle,
;                                                dup_too_many_open_files }
;
;       USES    AX, BX, CX, DS, ES, DI, SI, Flags
;
;       CALLS   JFNFindFree, SFFromHandle, DOS_Dup, pJFNFromHandle, PhysHandle
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure w_Dup,<HYBRID,far16>
        PMONLY

        cmp     bx,JFN_Length           ; Check if handle is out of range      ;### B726145
        ja      DupHdlErr               ;  if YES, exit w/ CARRY and error code;### B726145
        MOV     AX,BX                   ; AX = BX = old handle
        call    JFNFindFree             ; ES:DI -> New JFN, BX = new handle
DupErrorCheck:
        LJC     DupErr                  ; None available. Return error.
        SaveReg <ES,DI>                 ; save away JFN ptr
        lds     si, [JFN_Flg_Ptr]       ; DS:SI -> JFN flags
        SaveReg <bx>                    ; Save new handle temporarily
        mov     bx,ax                   ; BX = old handle
        mov     cl,byte ptr [si+bx]     ; CL = JFN flags for old handle
;;;
;;;BUGBUB RajenS Dec-16-1989 - We should DUP the don't inherit bit but CMD.EXE
;;;breaks when we do that. Se, we'll just zero out the JFN flags for the DUPed
;;;handle for now (what we used to do before).
;;;
;;;     and     cl,JFN_DIH              ; Mask out all except no-inherit bit
;;;

; DCR MS 2056 - nagara 8/24/90
;       mov     cl,0                    ; zero out the JFN bits for duped handle

        and     cl,JFN_ISCON OR JFN_RAWIO; inherit console bit and io mode bit
;

        RestoreReg <bx>                 ; BX = new handle
        mov     byte ptr [si+bx],cl     ; Set flags for new handle
        RestoreReg  <SI,DS>             ; DS:SI -> JFN for new handle
        XCHG    AX,BX                   ; AX = new handle, BX = old handle
        call    SFFromHandle            ; ES:DI -> SFT for old handle
                                        ;72417 changed jump pt from DupErr
                                        ; to DupHdlErr2 so it will free up
                                        ; JFN entry
        jc      DupHdlErr2              ; errors go home

        call    PhysHandle              ; Physical disk?
                                        ;72417 changed jump pt from DupErr
                                        ; to DupHdlErr2 so it will free up
                                        ; JFN entry
        jc      DupHdlErr2              ; Yes. DUP not allowed.

        mov     dx, es:[di].sf_LockID.LowWord
        or      dx, es:[di].sf_LockID.HighWord
        jz      Dup10
        mov     ax, ERROR_ACCESS_DENIED
        jmp short DupHdlErr2

Dup10:
        CALLFAR DOS_Dup
        jc      DupErr
        call    pJFNFromHandle          ; ES:DI -> JFN
        MOV     bx,ES:[DI]              ; BX = SFN
        MOV     DS:[SI],bx              ; stuff in new JFN
        ret                             ; and go home with NC

DupErr: ret                             ; exit with CY -> error

DupHdlErr:
        mov     ax, ERROR_INVALID_HANDLE                            ;### B726145

DupHdlErr2:
        stc                                                         ;### B726145
        MOV     word ptr DS:[SI],SFN_NULL ; 72417 - free up JFN entry
        jmp     short DupErr                                        ;### B726145

EndProc w_Dup


;***    w_Dup2 - force a dup on a particular JFN
;
;       ENTRY   BX      - file handle to duplicate
;               CX      - file handle to force to match the handle in BX
;
;       EXIT    NC      - no error
;                          AX = new dup'ed handle (value passed in CX).
;               CY      - error
;                          AX = error code in { ERROR_INVALID_HANDLE }
;                          AX = ERROR_INVALID_TARGET_HANDLE
;
;       CALLS   JFNFindFree, SFFromHandle, DOS_Dup, pJFNFromHandle, PhysHandle
;
;       NOTE    Shares code with w_Dup
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure w_Dup2,HYBRID
        PMONLY

        cmp     bx,cx                   ; error if both handles are same
        je      w_Dup2a
        call    SFFromHandle            ; get ptr to SFT
        jc      DupErr                  ; go return invalid_handle error
        call    PhysHandle              ; else, do we have a phys dsk handle?
        jc      DupErr                  ; yes - return with invalid_handle err.

        mov     ax, es:[di].sf_LockID.LowWord
        or      ax, es:[di].sf_LockID.HighWord ; protected handle?
        mov     ax, ERROR_ACCESS_DENIED        ; anticipate error
        stc
        jnz     DupErr                         ; yes, can't dup

;       check for physical disk target handle

        push    bx                      ; save handle
        mov     bx,cx                   ; check target handle
        call    SFFromHandle            ; get pointer to SFT
        pop     bx                      ; (restore handle)
        jc      w_Dup2b                 ; go close handle, if not open stream
        call    PhysHandle              ; else, do we have a phys dsk handle?
        jc      w_Dup2a                 ; yes - return with error

        mov     ax, es:[di].sf_LockID.LowWord
        or      ax, es:[di].sf_LockID.HighWord ; protected handle?
        mov     ax, ERROR_ACCESS_DENIED        ; anticipate error
        jnz     DupErr                         ; yes, can't dup to

w_Dup2b:

        SaveReg <bx,cx>                 ; save source
        mov     bx,cx                   ; get one to close
        push    dx
        xor     cx,cx                   ; fake file handle lockID lowword
        xor     dx,dx                   ; fake file handle lockID highword
        call    w_Close                 ; close destination handle
        pop     dx

        RestoreReg  <bx,ax>             ; old in AX, new in BX
        call    pJFNFromHandle          ; get pointer
        jc      w_Dup2a                 ; invalid target handle -- get out
        jmp     DupErrorCheck           ; check error and do dup

w_Dup2a:stc
        mov     ax,ERROR_INVALID_TARGET_HANDLE
        ret                             ; return with carry set
EndProc   w_Dup2


;***    api_Dup - dup a handle
;
;       api_Dup preforms the dup for DOSDUPHANDLE.  On entry BX is the
;       handle to be duplicated.  CX is either 0FFFFh or another handle.
;       If CX is 0FFFh preform a dup on the handle in BX.  If CX is not
;       0FFFh close the handle in CX and dup the handle in BX into CX (do
;       a force dup).
;
;       ENTRY   BX      - file handle to duplicate
;               CX      - FFFF or file handle to force to match the handle in BX
;
;       EXIT    NC      - no error
;                          AX = new dup'ed handle.
;               CY      - error
;                          AX = ERROR_INVALID_HANDLE
;                          AX = ERROR_INVALID_TARGET_HANDLE
;
;       CALLS   w_Dup, w_Dup2
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure api_Dup,HYBRID
        PMONLY
        cmp     cx,0FFFFh               ; do w_Dup or w_Dup2?
        jz      adp1
        call    w_Dup2                  ; do force dup
        ret

adp1:   call    w_Dup                   ; do normal dup
        ret
EndProc   api_Dup

;***    w_SetRelMaxFH   - sets the max no of file handles relative to current
;
;       w_SetRelMaxFH increases/decreases the maximum number of file handles
;       allowed for a process. As of Ver 2.0, we don't support decreasing
;       max file handles.
;
;       ENTRY   (BX:DI)  --> ptr to Requested ChangeCount  (input/output)
;               (DX:SI)  --> ptr to Max no of File Handles (output)
;
;       EXIT    if carry clear
;                 JFN_Length = new size of JFN table
;                 pChgCount -> change in max no of file handles
;                 pMaxFH -> Current max no of file handles
;               if carry set
;                 ax = error code
;                       ERROR_INVALID_PARAMETER
;                       ERROR_NOT_ENOUGH_MEMORY
;
;       USES    eax,ebx,ecx,dx,si,di,ds,es,flags
;
;       ALGORITHM:
;               if (ChgCount == (-)ve )
;                       ChgCount = 0;
;
;               if (ChgCount == 0) {
;                  return (MaxCnt = Current Max no of file handles)
;               }
;
;               if (chgcount + Current_max_FH > System Max FH) {
;                  return (ERROR_INVALID_PARAMETER);
;               }
;               else {
;                  call w_SetMaxFH (new max FH);
;                  if (NO RC)
;                       set MaxCnt,ChgCount;
;               }

CpuMode 386

        assume  ds:nothing,es:nothing,ss:TASKAREA

Procedure       w_SetRelMaxFH,faronly

LocalVar        srmh_pCntMax,DWORD
LocalVar        srmh_pChgCount,DWORD
LocalVar        srmh_ChgCount,DWORD
LocalVar        srmh_CurrentMax,DWORD

        EnterProc <dx,si,bx,di>
        mov     si,di           ; bx:si -> ptr to requested change count
        push    ss
        pop     es
        lea     di,srmh_ChgCount ; es:di -> change count
        mov     cx,4            ; cx = no of bytes to transfer (sizeof ULONG)
        call    FuBuff          ; (es:di) <= (bx:si)
        jc      short wsrmhx
        movzx   ecx,JFN_Length
        mov     srmh_CurrentMax,ecx     ; save current max
        mov     ebx,srmh_ChgCount
        mov     srmh_ChgCount,0

        cmp     ebx,0
        je      short wsrmh30           ; User asking for Cur.Max Handles ?
;
        jl      short wsrmh50           ; decrease max handles ?
;
; increase the max no of handles
                                        ; max handle count
        add     ebx,ecx                 ; new max no of handles
        cmp     ebx,MAXJFN              ; more than system max ?
        jle     short wsrmh10           ; no, acceptable
        mov     ebx,MAXJFN              ; limit this to System max

; call the worker to set the current max file handles
wsrmh10: call   w_SetMaxFH              ;

; increase/decrease of max handles over; If there was no error in this ,
; then let use find out the actual change in max handles

wsrmh20: jc     short wsrmhx            ; if err, no need to set output params
;
        mov     ebx,srmh_CurrentMax     ; previous max
        movzx   ecx,JFN_Length          ; current max
        sub     ebx,ecx                 ;
        neg     ebx                     ; ebx = change in max file handles

; at this point
; ebx = change in max file handle count
; ecx = current max file handle count

        mov     srmh_ChgCount,ebx

;Return the output params - Current Max file handles and the change

wsrmh30: mov    di,srmh_pChgCount.Offst
        mov     bx,srmh_pChgCount.Segmt
        TaskContext     DS
        ASSUME  DS:NOTHING
        lea     si,srmh_ChgCount
        push    ecx
        mov     cx,4
        call    SuBuff
        pop     ecx
        jc      short wsrmhx
        mov     srmh_CurrentMax,ecx
        mov     cx,4
        TaskContext     DS
        ASSUME  DS:NOTHING
        lea     si,srmh_CurrentMax
        mov     bx,srmh_pCntMax.Segmt
        mov     di,srmh_pCntMax.Offst
        call    SuBuff
wsrmhx: LeaveProc
        Ret

; decrease the no of max handles
; we cannot decrease below the default system value of max handles - FILPERPROC
; so we will limit the decrease to this min value
; at this point:
;       ecx = Current MAX JFN
;       ebx = the requested change ( Negative value)

wsrmh50:
        cmp     ecx, FILPERPROC ; are we already at the system min limit ?
        jz      wsrmh20         ; yes, don't decrease.
        add     ecx,ebx         ; ecx = the new max JFN
        cmp     ecx,FILPERPROC  ; is this less than the system min limit ?
        jg      wsrmh60         ; no , let us go shrink the JFNS
        mov     ecx,FILPERPROC

; ecx = new max JFN to be set

wsrmh60: call   ShrinkJFNs
        jmp     short wsrmh20

CpuMode reset

EndProc w_SetRelMaxFH

;**     w_SetMaxFH - Sets the maximum number of file handles.
;
;       w_SetMaxFH increases the maximum number of file handles allowed
;       for a process. By default the handle table and flag table are
;       of size FILPERPROC and lives in the PTDA. This procedure allows
;       the user to expand it by allocating new segments to hold handle
;       table and flag table. This is for PM process only.
;
;       ENTRY   BX - new maximum number of file handles.
;
;       EXIT    if carry clear
;                 JFN_Length = new size of JFN table
;               if carry set
;                 ax = error code
;                       ERROR_INVALID_PARAMETER
;                       ERROR_NOT_ENOUGH_MEMORY
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       ALGORITHM:
;
;       if (request > MAXJFN || request < JFN_Length)
;         return ERROR_INVALID_PARAMETER
;       if (current handle table is in PTDA)
;         copy handle table to a segment
;       grow segment to request size

        assume  ds:nothing,es:nothing,ss:TASKAREA

Procedure w_SetMaxFH,<hybrid,far16>

        LocalVar        wsmfh_sizeNew,WORD ; new size of JFN table

        PMONLY
        EnterProc       <bx>    ; wsmfh_sizeNew = bx
        mov     ax,ERROR_INVALID_PARAMETER      ; ax = error code
        mov     cx,MAXJFN       ; cx = system handles limit
        cmp     cx,bx           ; #handles exceeds system limit?
        jb      wsmfhx          ;   YES, exit w/ CARRY and error code

        cmp     bx,JFN_Length   ; caller tries to shrink JFN table?
        jbe     wsmfhx          ;  if YES, exit w/ CARRY and error code
                                ;  if bx == JFN_Length, we're done so
                                ;  exit w/ carry clear

; bx = new max. # of file handles

        cmp     JFN_Length,FILPERPROC   ; already have JFN segments?
        jne     wsmfh1          ;   YES, grow the segments

; before we grow the segments, we must move them into segments
        call    MovJFNToSeg     ; move JFN table into segments
        jc      wsmfhx          ;   ERROR, exit w/ carry and error code

        mov     bx,wsmfh_sizeNew ; bx = new size of JFN table

; GrowJFNSeg(bx=new max. # of file handles)
wsmfh1: call    GrowJFNSeg      ; grow JFN segments
wsmfhx: LeaveProc
        ret
EndProc w_SetMaxFH

;**     GrowJFNSeg - grow JFN table and JFN flags segments
;
;       This procedure assumes MAXJFN >= bx > FILPERPROC and JFN table
;       and JFN flags already have their own segments.
;
;       This is also called for shrinking the JFN table
;
;       ENTRY   bx = new number of file handles to accomodate
;
;       EXIT    if carry clear - success
;                 JFN_Length = new number of file handles
;               if carry set - failed
;                 ax = error code
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

Procedure GrowJFNSeg,near,local
    assume      ds:nothing,es:nothing,ss:TASKAREA

        LocalVar        gjs_sizeNew,WORD ; new size of JFN table
        LocalVar        gjs_linaddr,DWORD ; new size of JFN table

IFDEF FSSTRICT
; make sure handle table and flag table are in segments
        test    JFN_pTable.Offst,-1     ; is offset = 0?
        jnz     gjs1                    ;   No, go panic

        test    JFN_Flg_Ptr.Offst,-1    ; is offset = 0?
        jz      gjs2                    ;   Yes, continue
gjs1:   InternalError <GrowJFNSeg: Offst NZ>
gjs2:
ENDIF

        EnterProc       <bx>            ; gjs_sizeNew = bx

    CpuMode 386
        FlatContext <ds,es>,ax
        lea     eax,gjs_linaddr         ; (eax) = flat offset of sel return area
        SSToDS  eax
        movzx   ecx,ss:JFN_pTable.Segmt ; (ecx) = JFN segment to grow
        movzx   ebx,bx                  ; (ebx) = number of JFN entries
        shl     ebx,1                   ; (ebx) = bytes in JFN table
        CCALL32 _SELReallocKHB,<VM_HKH_PUB_RESRW,ecx,ebx,PG_RESIDENT,eax>
        or      eax,eax                 ; clears carry
        jnz     gjs90

        lea     eax,gjs_linaddr         ; (eax) = flat offset of sel return area
        SSToDS  eax
        movzx   ecx,ss:JFN_Flg_Ptr.Segmt; (ecx) = JFN segment to grow
        movzx   ebx,gjs_sizeNew         ; (ebx) = bytes in JFN flags
        CCALL32 _SELReallocKHB,<VM_HKH_PUB_RESRW,ecx,ebx,PG_RESIDENT,eax>
        or      eax,eax                 ; clears carry
        jz      short gjs80             ; continue, no error
;
; Defect 108035 Fix
; If first SELReallockHB succeeds and second one fails
; first one has to be reversed. Use gjs_linaddr again
; for return address.

        lea     eax,gjs_linaddr         ; (eax)=flat offset of sel return area
        SSToDS  eax
        movzx   ecx,ss:JFN_pTable.Segmt    ; (ecx) = JFN segment to grow
        movzx   ebx,ss:JFN_Length          ; (ebx) = original JFN length  
        shl     ebx,1                      ; (ebx) = bytes in JFN table
        CCALL32 _SELReallocKHB,<VM_HKH_PUB_RESRW,ecx,ebx,PG_RESIDENT,eax>
        jmp     short gjs90                ; go set error code and return
gjs80:

    CpuMode reset

    assume      ds:nothing,es:nothing

; both segments are expanded successfully. Now initialize new JFN table
; and JFN flags entries.

; mark new JFN table entries unused
gjs3:   mov     cx, gjs_sizeNew         ; cs = new size of JFN table
        sub     cx,JFN_Length           ; cx = number of new entries
        js      gjs10                   ; segment was shrunk, no
                                        ; initialization is necessary.
        mov     ax,SFN_NULL             ; for marking JFN table entries unused
        les     di,JFN_pTable           ; es:di -> JFN table
        add     di,JFN_Length
        add     di,JFN_Length           ; es:di -> first new entry
        mov     dx,cx                   ; dx = number of new entries
        cld                             ; go forward
        rep     stosw                   ; all new handles = SFN_NULL

; set new JFN flags to 0 (is this necessary?)
        xor     al,al                   ; al = 0, carry clear
        les     di,JFN_Flg_Ptr          ; es:di -> JFN flags
        add     di,JFN_Length           ; es:di -> first new flag entry
        mov     cx,dx                   ; cx = number of new entries
        rep     stosb                   ; all new flags = 0

gjs10:  mov     bx,gjs_sizeNew          ; bx = new size of JFN table
        mov     JFN_Length,bx           ; update JFN_Length
        clc                             ; exit with carry clear

gjsx:   LeaveProc
        ret

; error exit because SelReallocKHB failed

    CpuMode 386

gjs90:  mov     ax,ERROR_NOT_ENOUGH_MEMORY
        stc
        jmp     short gjsx

    CpuMode reset

EndProc GrowJFNSeg


;**     MovJFNToSeg - move JFN table from PTDA into segments
;
;       Allocates separate segments for JFN table and JFN flags, then
;       copies JFN table and JFN flags from PTDA into the new segment.
;
;       ENTRY   none
;
;       EXIT    if carry clear
;                 JFN_pTable -> new file handle table
;                 JFN_Flg_Ptr -> new JFN flag table
;               if carry set
;                 ax = ERROR_NOT_ENOUGH_MEMORY
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

Procedure MovJFNToSeg,near,local
    assume ds:nothing,es:nothing,ss:TASKAREA

        mov     bx,JFN_Length           ; bx = JFN_Length
        mov     dx,ptda_handle          ; dx = pdta handle
; bx=selFlag,dx=selTab = AllocJFNSeg(bx=size, dx=ptda handle)
        call    AllocJFNSeg             ; allocate segments for JFN table
        jc      mjtsx                   ;   ERROR, exit w/ CY and error code

; bx = selector to JFN flag segment
; copy JFN flags
        lds     si,JFN_Flg_Ptr          ; ds:si -> old JFN flag table
        mov     cx,JFN_Length           ; cx = size of JFN flag table
        mov     es,bx                   ; es = selector to JFN flag segment
        xor     di,di                   ; es:di -> new JFN flag table
        mov     JFN_Flg_Ptr.Segmt,bx    ; save new JFN flag segment
        mov     JFN_Flg_Ptr.Offst,di    ; JFN_Flg_Ptr.Offst = 0
        cld                             ; copy forward
        rep     movsb                   ; copy JFN flags to new segment

; copy JFN table
        lds     si,JFN_pTable           ; ds:si -> old JFN table
        mov     cx,JFN_Length           ; cx = size of JFN table
        mov     es,dx                   ; es = selector to JFN table segment
        xor     di,di                   ; es:di -> new JFN table; carry clear
        mov     JFN_pTable.Segmt,dx     ; save new JFN segment
        mov     JFN_pTable.Offst,di     ; JFN_pTable.Offst = 0
        rep     movsw                   ; copy old JFN table to new segment
                                        ; exit with carry clear
mjtsx:  ret
EndProc MovJFNToSeg


;**     AllocJFNSeg - allocate segments for JFN table and JFN flags
;
;       Allocates separate segments for JFN table and JFN flags.
;
;       ENTRY   bx = number of handles to allocate
;               dx = PTDA handle of owner
;
;       EXIT    if carry clear
;                 bx = selector to new JFN flag segment
;                 dx = selector to new JFN table segment
;               if carry set
;                 ax = ERROR_NOT_ENOUGH_MEMORY
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

Procedure AllocJFNSeg,NEAR
    assume      ds:nothing,es:nothing,ss:TASKAREA

        LocalVar        ajs_sizeJFN,WORD        ; number of JFN table entries
        LocalVar        ajs_hPTDA,WORD          ; ptda handle
        LocalVar        ajs_vmacJFNTab,<size vmac_s> ; selector to JFN table seg
        LocalVar        ajs_vmacFlagTab,<size vmac_s>; selector to Flag table seg
        LocalVar        ajs_hJFNTab,WORD        ; handle to JFN table segment

        EnterProc       <bx,dx>         ; ajs_sizeJFN = bx, ajs_hPTDA = dx

        CpuMode 386
        FlatContext <ds,es>,ax
        SSToDS  ecx,[ajs_vmacJFNTab]    ; (ecx) = flat offset of sel return area
        movzx   ebx,bx
        shl     ebx,1                   ; (ebx) = number of bytes of handles
        CCALL32 _SELAllocKHB,<VM_HKH_PUB_RESRW,ebx,0,0,FSJFNTABLEOWNER,ecx>
        or      eax,eax                 ; clears carry
        jnz     short ajs90

ajs10:  SSToDS  ecx,[ajs_vmacFlagTab]   ; (ecx) = flat offset of sel return area
        movzx   ebx,[ajs_sizeJFN]       ; (ebx) = number of bytes of flags
        CCALL32 _SELAllocKHB,<VM_HKH_PUB_RESRW,ebx,0,0,FSJFNFLAGSOWNER,ecx>
        or      eax,eax                 ; clears carry
        jnz     short ajs80

ajs30:  mov     bx,[ajs_vmacFlagTab].ac_sel ; (bx) = selector to Flags segment
        mov     dx,[ajs_vmacJFNTab].ac_sel  ; (dx) = selector to JFN segment

ajsx:   LeaveProc
        ret

; error in allocation, clean up and leave.

ajs80:  movzx   eax,[ajs_vmacJFNTab].ac_sel ; (eax) = selector to new JFN table
        CCALL32 _SELFreeKHB,<VM_HKH_PUB_RESRW,0,eax>

ajs90:  mov     ax,ERROR_NOT_ENOUGH_MEMORY
        stc
        jmp     short ajsx

        ASSUME  DS:NOTHING,ES:NOTHING
        CpuMode reset

EndProc AllocJFNSeg


;**     FreeJFNSeg - frees segments allocated for JFN table and JFN flags
;
;       Frees segments allocated for JFN table and JFN flags.
;
;       ENTRY   ax = JFN table selector
;               bx = JFN flags selector
;
;       EXIT    if carry clear
;                 success
;               if carry set
;                 ax = error code
;
;       USES    eax,ebx,ds,es,flags

Procedure FreeJFNSeg,NEAR
    assume      ds:nothing,es:nothing,ss:TASKAREA

ifdef FSSTRICT
        mov     cx,ss                   ; (cx) = stack segment
        cmp     cx,bx                   ; Are we about to free the stack?
        je      short fjsie             ;   yes, internal error

        cmp     cx,ax                   ; Are we about to free the stack?
        je      short fjsie             ;   yes, internal error
endif
        CpuMode 386
        SaveReg <ax>
        movzx   ebx,bx                  ; (ebx) = jfn flags segment
        FlatContext <ds,es>,,noassume
        CCALL32 _SELFreeKHB,<VM_HKH_PUB_RESRW,0,ebx>
        RestoreReg  <bx>

        movzx   ebx,bx                  ; (ebx) = jfn table segment
        CCALL32 _SELFreeKHB,<VM_HKH_PUB_RESRW,0,ebx>
        xor     eax,eax                 ; always success
        ret

ifdef FSSTRICT
fjsie:  InternalError   <FreeJFNSeg: About to free stack>
endif

        ASSUME  DS:NOTHING,ES:NOTHING
        CpuMode reset

EndProc FreeJFNSeg


;**     ShrinkJFNs - shrink JFN table and JFN flags segments
;
;       This procedure assumes MAXJFN >= ecx >= FILPERPROC and JFN table
;       and JFN flags already have their own segments.
;
;       ENTRY   ecx = new number of max file handles
;
;       EXIT    if carry clear - success
;                 JFN_Length = new number of file handles
;               if carry set - failed
;                 ax = error code
;
;       USES    ax,ebx,ecx,dx,si,di,ds,es,flags
;
;       ALGORITHM:
;               if (new max < JFN_Max )
;                       call FindNewJFNMax
;               /* else */      // no need to change new max
;               if (new max == FILPERPROC) {
;                       mov jfns and flags to PTDA
;                       release the JFN and Flag segments
;               }
;               else call GrowJFNSeg // to shrink the segments
;
Procedure       ShrinkJFNs,near,local
        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA

        cmp     cx,JFN_Max              ; JFN_Max is the max jfn handle that may
                                        ; be in use
        ja      sjfn20                  ;
        movzx   ebx,JFN_Max             ;
        call    FindNewJFNMax           ; ecx = new max jfns
sjfn20: cmp     ecx,FILPERPROC          ; are we switching back to
        je      sjfn50                  ;system minLimit
sjfn30: mov     ebx,ecx                 ; bx = new max no of file handles
        call    GrowJFNSeg              ; shrink the segs by reallocing
        ret
;
; we have to move the jfns back to PTDA and then free the segments
;
sjfn50: TaskContext     es              ; move the jfns back to PTDA
        lea     di,TASKAREA:JFN_Table
        mov     cx,FILPERPROC
        lds     si,JFN_pTable
        push    ds                      ; save the current JFN sel
        mov     [JFN_pTable].Segmt,ss
        mov     [JFN_pTable].Offst,di
        rep     movsw

        lea     di,TASKAREA:JFN_Flags ; move the jfn flags to PTDA
        mov     cx,FILPERPROC
        lds     si,JFN_Flg_Ptr
        push    ds                      ; save the current JFN_Flg sel
        mov     [JFN_Flg_Ptr].Segmt,ss
        mov     [JFN_Flg_Ptr].Offst,di

        rep     movsb

; update Cur max to system min limit

        mov     JFN_Length,FILPERPROC

; release the jfn and flag segments
        pop     bx              ; bx = JFN flag seg sel
        pop     ax              ; ax = JFN Seg sel
        call    FreeJFNSeg
        ret

EndProc ShrinkJFNs

;**     FindNewJFNMax - Find the min MAX_JFN that we can shrink the JFNs to
;
;       ENTRY   ecx = requested new number of max file handles (1-based)
;               ebx = The highest JFN that is POSSIBLY in use (0-based)
;
;       EXIT    ecx = new number of max file handles
;
;       USES    ax,ebx,ecx,dx,si,di,ds,es,flags
;
;       NOTES:  This will not work correctly if ecx == ebx + 1.  In this
;               case, there is no reason to call FindNewJFNMax, as the
;               current MAX_JFN fits (barely) into the req. new num of file
;               handles.
;
;               JFN_FindFree keeps track of the highest Free JFN it finds
;               in a PTDA variable JFN_Max; This value only keeps track of
;               the max. JFN value that was EVER used by this process. Since
;               there is no JFNFree, we don't have any way of knowing what
;               is the currently in-use-max-handle. So we find out ourselves
;               by looking at the [JFN_pTable] from the requested max no of
;               file handle to the JFN_Max handle to see whether any handle
;               is in use. If any handle is in use, then we should not
;               shrink the JFN table segment to any no that is below that
;               handle.
;
;       ALGORITHM:
;               no_of_handles to look for = Max_JFN - req.new no of max handles
;               JFN_Table_starting_point = [JFN_pTable][req.new max handles]
;               while (no_of_handles != 0) {
;                       look for a handle in use
;                       if handle in use {
;                               new max handle = this handle;
;                       }
;               }
;               return (new max handle)
;

CpuMode 386

Procedure FindNewJFNMax,near,local

        les     di, [JFN_pTable]        ; (es:di) -> JFN table
        add     di, bx
        add     di, bx                  ; (es:di) -> LAST known used entry in JFN table
        xchg    ebx, ecx                ; ebx = requ.max; ecx = JFN_Max

; requ.max is 1 based, JFN_Max is 0 based.  So we will add one to the
; difference to find the number of handles to check.

        sub     ecx, ebx
        inc     ecx                     ; ecx = number of handles to check
        mov     ax, SFN_NULL            ; Unused handle value... search for this
        std                             ; scan down the table, not up
        repe    scasw                   ; Stop when a used handle is found,
                                        ; or count is used up.
        je      fnjm10                  ; If ZF, none of the handles are in use.  ebx =
                                        ; correct max number of FHs.

; Here we know that there is a handle in use, ecx contains (the number of
; entries that are in use in the range) - 1.

        inc     ecx                     ; number of entries in range that are in use.
        add     ebx, ecx                ; New max number of FHs.

fnjm10: mov     ecx, ebx                ; Put the new max number of FHs in ecx for return
        mov     JFN_Max, cx             ; Set the system's value to what we now know.
        cmp     JFN_Max,+00    ; ebx had the requested-max (1-based)    ;82170;
        je      short fnjm20   ; moved into it, but now was used to get ;82170;
        sub     JFN_Max,+01    ; JFN_Max which is 0-based => subtrct 1  ;82170;
                               ; to leave JFN_Max 0-based (but still    ;82170;
                               ; retn ecx as 1-based).                  ;82170;

fnjm20: cld                             ; Most code expects direction flag to be clear.
        ret
EndProc FindNewJFNMax

CpuMode reset
;***    w_QFHandState - Set [query] the state of the file handle.
;
;       w_QFHandState preforms the DOSSETFHANDSTATE api call.
;
;       ENTRY   AL - 0 => 16bit call
;                  - 1 => 32 bit call extended qfhandstate- includes
;                               the console, raw/cooked mode bits also
;                       (this is Internal call and used only by doscall1)
;               AH - 80h => no filehandle LockID check
;               AH - 00h => yes filehandle LockID check
;               BX - handle
;               SI:DI => file handle lockID
;
;       EXIT    Carry Clear     - no error
;                  CX = File handle state (see DosQFHandState in dos 5 spec).
;                  DX = extended file handle state (for 32 bit call ONLY)
;
;               Carry Set       - error
;                          AX = ERROR_INVALID_HANDLE
;
;       CALLS
;               FSFromHandle, PhysHandle
;       USES
;               All.
;       NOTES
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure w_QFHandState,<HYBRID,FAR16>
        PMONLY
LocalVar wqfhs_calltype,WORD
LocalVar wqfhs_fhlockID,DWORD

        EnterProc

        mov     wqfhs_calltype,ax             ;
        mov     wqfhs_fhlockID.LowWord,di     ; filehandle lockID lowword
        mov     wqfhs_fhlockID.HighWord,si    ; filehandle lockID highword

        call    SFFromHandle                    ; ES:DI -> sf_entry for file
        jnc     wqfhs1                          ; jump down if valid handle
        mov     ax,ERROR_INVALID_HANDLE         ;
wqfhs0:
        LeaveProc
        ret                                     ; return with carry set

;(@PhysDisk start)    check for physical disk handle

wqfhs1: call    PhysHandle              ; do we have a physical disk handle?
        jc      wqfhs0                  ; yes - return with error

;(@PhysDisk end)
        mov     ax,wqfhs_calltype
        and     wqfhs_calltype,7fffh
        test    ah, 80h                 ; do filehandle lockID check?
        jnz     wqfhs1b                 ; no
        mov     ax, es:[di].sf_LockID.LowWord
        cmp     ax, wqfhs_fhlockID.LowWord     ; check access
        jne     wqfhs1a                        ; access denied

        mov     ax, es:[di].sf_LockID.HighWord
        cmp     ax, wqfhs_fhlockID.HighWord    ; check access
        je      wqfhs1b                        ; access ok
wqfhs1a:
        mov     ax, ERROR_ACCESS_DENIED        ; access denied
        stc
        jmp short wqfhs0                       ; return with error

wqfhs1b:
        mov     ax,wqfhs_calltype
        mov     cx, es:[di.sf_fsi.sfi_mode]     ; get flag word from sft
                                                ; zero reserved and flag bits
        and     cx, NOT (o_mode_reserved OR o_mode_in_flags)

;       Inherit, fail-error, and write through are
;       all defined in the flags.  There are similar
;       fields in sf_fsi.sfi_mode, but these are for 3.X box

        lds     si,[JFN_Flg_Ptr]                ; get JFN flags
        mov     al, [SI + BX]                   ; get flags for this handle
        test    al, JFN_WRTTHR                  ; write through
        jz      wqfhs2                          ; not set -- skip down
        or      cx, open_write_through          ; set -- set in cx
wqfhs2: test    al, JFN_FAIL                    ; fail error
        jz      wqfhs3                          ; not set -- skip down
        or      cx, open_autofail               ; set -- set in cx
wqfhs3: test    al, JFN_DIH                     ; inherit flag
        jz      wqfhs4                          ; not set -- skip down
        or      cx, open_no_inherit             ; set -- set in cx
wqfhs4: test    al,JFN_NCACHE                   ; cache bit
        jz      wqfhs5
        or      cx,open_no_cache
wqfhs5:
        mov     ax,wqfhs_calltype
        or      ax,ax                           ;
        jz      wqfhs9                          ; extended call (32 bit) ?

; ds:si -> JFN_Flg_Ptr
; es:di -> SFT
; bx -> handle (JFN)

wqfhs6: mov     al,[SI + BX]                    ; get flags for this handle
        mov     dx,es:[di].sf_flags2
        and     dx,SFF2_Consistency
        .errnz  (open2_consist_bits -1)
        shr     dx,7                            ; dx = consistency bits
        test    es:[di].sf_flags2,SFF2_SPOOLED
        jz      wqfhs7
        or      dx,open2_nonspooled
wqfhs7: test    al,JFN_ISCON
        jz      wqfhs9                          ; not a console device
        or      dx,open2_console                ; this is a console device
        test    al,JFN_RAWIO                    ; check on the io mode
        jz      wqfhs9
        or      dx,open2_io_mode                ; raw io mode
wqfhs9:                                 ; successful completion
        jmp     wqfhs0                ; exit

EndProc w_QFHandState


;***    w_SetFHandState - Set the state of the file handle.
;
;       w_SetFHandState preforms the DOSSETFHANDSTATE api call.
;
;       ENTRY   BX - handle
;               CX - new file handle state
;               AX - 0 => 16 bit api call
;                  - 1 => 32 bit internal call (from Doscall1)
;               DX - high word of new file handle state ( IFF AX != 0 )
;               SI:DI => file handle lockID
;
;       EXIT    Carry Clear     - no error
;
;               Carry Set       - error
;                          AX = ERROR_INVALID_HANDLE
;
;       CALLS
;               SFFromHandle, PhysHandle
;       USES
;               All.
;       NOTES
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure w_SetFHandState,hybrid
        PMONLY
LocalVar wsfhs_calltype,WORD
LocalVar wsfhs_fhlockID,DWORD

        EnterProc
        mov     wsfhs_calltype,ax             ;
        mov     wsfhs_fhlockID.LowWord,di     ; filehandle lockID lowword
        mov     wsfhs_fhlockID.HighWord,si    ; filehandle lockID highword

        push    cx
        push    dx
        call    SFFromHandle                    ; ES:DI -> sf_entry
        pop     dx
        pop     cx
        jnc     wsfhs1                          ; valid handle, jump down
        mov     ax,ERROR_INVALID_HANDLE         ; set error code and carry flag
wsfhs0:
        LeaveProc
        ret

;(@PhysDisk start)    check for physical disk handle

wsfhs1: call    PhysHandle              ; do we have a physical disk handle?
        jc      wsfhs0                  ; yes - return with error

;(@PhysDisk end)

        mov     ax, es:[di].sf_LockID.LowWord
        cmp     ax, wsfhs_fhlockID.LowWord     ; check access
        jne     wsfhs1a                        ; ok so far

        mov     ax, es:[di].sf_LockID.HighWord
        cmp     ax, wsfhs_fhlockID.HighWord    ; check access
        je      wsfhs1b                        ; access ok
wsfhs1a:
        mov     ax, ERROR_ACCESS_DENIED        ; access denied
wsfhs1aa:
        stc
        jmp short wsfhs0                       ; return with error

wsfhs1b:
        mov     ax,wsfhs_calltype
                                                ; are reserved fields zero?
        test    cx, (o_mode_reserved OR o_mode_in_sft)
        jz      wsfhs3                          ; yes, skip down

wsfhs2: mov     ax,ERROR_INVALID_PARAMETER      ; no, return error
        jmp short wsfhs1aa                      ; return with error

wsfhs3: or      ax,ax
        jz      wsfhs4                          ; 16 bit call
;
        test    dx, NOT open2_io_mode
        jnz     wsfhs2

        push    ax
        lds     si,[JFN_Flg_Ptr]                ; get point to JFN flags
        mov     al, byte ptr [si+bx]            ; get the jfn flags again
        and     al, JFN_RAWIO                   ; isolate raw/cooked io mode
.errnz (JFN_RAWIO - open2_io_mode)
        xor     ax, dx                          ; see if the mode changes
        jz      wsfhs4a                         ; no mode change, go on

; user is changing the raw/cooked IO mode
; this is valid only if the sft is for CON

        test    es:[di].sf_flags,DEVID_ISCOT or DEVID_ISCIN
        pop     ax
        jz      wsfhs2                          ; error

wsfhs4: push    ax
        lds     si,[JFN_Flg_Ptr]                ; get point to JFN flags

; ds:si -> JFN_Flg_Ptr
; (TOS) = ax (16bit/32bit)

wsfhs4a: mov    al, [si+bx]                     ; get flags for this handle
        and     al, NOT JFN_USED                ; zero flags set by call
        test    cx, open_write_through          ; should we set write through
        jz      wshfs5                          ; no, skip down
        or      al, JFN_WRTTHR                  ; yes, set bit in flags
wshfs5: test    cx, open_autofail               ; auto fail flag?
        jz      wshfs6                          ; no, skip down
        or      al, JFN_FAIL                    ; yes, set bit in flags
wshfs6: test    cx, open_no_inherit             ; inherit flag?
        jz      wshfs7                          ; no, skip down
        or      al, JFN_DIH                     ; yes, set bit in flags
wshfs7: test    cx,open_no_cache                ; cache flag?
        jz      wshfs8                          ; no, skip down
        or      al,JFN_NCACHE                   ; yes, set bit in flags
wshfs8: mov     byte ptr [si+bx], al            ; put flag back
        pop     ax

        or      ax,ax                           ; if 16 bit call, return
        jz      wshfs20                         ; success
;
; for 32 bit call, also set the raw/cooked mode depending on the input
;
        mov     al, byte ptr [si+bx]            ; get the jfn flags again
        and     al, NOT JFN_RAWIO               ; zero out  raw/cooked io mode
        or      ax, dx                          ; change the io mode
        mov     byte ptr [si+bx],al             ; update the flags
wshfs20:                                ; successful completion
        jmp     wsfhs0                ; return

EndProc w_SetFHandState


;***    w_QHandType - Query a handle type
;
;       w_QHandType determines whether a handle references a file or a
;               device.
;
;       ENTRY   BX - handle
;
;       EXIT    Carry Clear     - no error
;                   AL - handle type
;                        0 = file handle
;                        1 = device handle, BX = attribute word
;                        2 = pipe
;
;                   AH - handle bits
;                        80h = set if remote/network handle
;
;                   BX - flag word.  If AX == 1 then BX contains device driver
;                        attribute word, else BX is garbage.
;
;               Carry Set       - error
;                          AX = ERROR_INVALID_HANDLE
;
;       CALLS   SFFromHandle, PhysHandle
;
;       USES    AX, BX, CX, DI, ES

QHT_IS_FILE     equ     0
QHT_IS_DEVICE   equ     1
QHT_IS_PIPE     equ     2
QHT_IS_PROTECTED equ    4000h

Procedure w_QHandType, <hybrid, far16>
    assume  ds:nothing,es:nothing,ss:TASKAREA
        PMONLY

        call    SFFromHandle            ; (es:di) -> sf_entry for file
        jc      qhtx                    ;   Invalid handle

        call    PhysHandle              ; do we have a physical disk handle?
        jc      qhtx                    ;   Yes, return with error

.errnz (SF_ISNET - 8000h)               ; enforce network bit location
        mov     ax,es:[di].sf_flags     ; (ax) = sft flags
        and     ax,SF_ISNET             ; (ah) = network bit
                                        ; (al) = assume file handle

        mov     cx, es:[di].sf_LockID.LowWord
        or      cx, es:[di].sf_LockID.HighWord ; protected file handle?
        jz      qht1a                          ; no
        or      ax, QHT_IS_PROTECTED
qht1a:

.errnz (QHT_IS_FILE)
        test    es:[di].sf_flags2, SFF2_SPOOLED ; spool file?
        jz      qht1                    ; no, continue

; We have a handle for a spooled file. When the process opened it, it
; opened a spooled device, but the kernel-spooler opened a file instead
; (a "spool file"). Since the process that did the opened thinks that
; it is dealing with a device, we return QHT_IS_DEVICE.

        mov     ax, QHT_IS_DEVICE       ; return type == device
        mov     bx, DEV_CHAR_DEV        ; attributes == character device

        or      cx, cx                  ; protected file handle?
        jz      qhtx                    ; no
        or      ax, QHT_IS_PROTECTED    ; reset it
        jmp short qhtx

qht1:   test    es:[di].sf_flags,SF_PIPE+DEVID_DEVICE  ; File handle?
        jz      qhtx                    ;   Yes, done

        mov     al,QHT_IS_PIPE          ; (al) = assume pipe
        test    es:[di].sf_flags,SF_PIPE ; Pipe?
        jnz     qhtx                    ;   Yes, done
;
; We have a handle for a device. If it is a remote device (pseudo-char dev)
; then we do not return any device attributes since we do not have a device
; header to get them.
;
        dec     al                      ; (al) = device
.errnz (QHT_IS_PIPE - QHT_IS_DEVICE - 1) ; make sure dec works
        xor     bx,bx
        test    ax,SF_ISNET             ; Is this a remote device?
        jnz     qhtx                    ; Yes. No device header. Return 0 in BX.
        les     di, es:[di].sf_devptr   ; (es:di) -> device driver header
        mov     bx, es:[di.SDevAtt]     ; (bx) = device attribute word

qhtx:   ret

EndProc w_QHandType



;***    is_device - verify valid handle is not device
;
;       is_device verifies handle is valid and determines if handle is
;       device
;
;       ENTRY   BX = real handle
;       EXIT    Carry Clear => handle is not device
;               Carry Set => handle is invalid or is device
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure is_device,hybrid
        PMONLY

        call    SFFromHandle            ; ES:DI -> SFT
        jc      isd2                    ; handle is invalid?
        test    byte ptr es:[di.sf_flags],DEVID_DEVICE
        jz      isd2                    ; no error - carry clear
        stc                             ; return error
isd2:   ret

EndProc  is_device


BREAK <PhysHandle - check if physical disk handle>

;*********************** START OF SPECIFICATIONS ********************
; SUBROUTINE NAME: PhysHandle
;
; DESCRIPTIVE NAME:  check if physical disk handle
;
; FUNCTION:   PhysHandle checks if the handle is for
;             a physical disk, by getting the SFT entry and checking
;             if the open_phys_disk bit is set. If it is, then
;             the carry flag will be set and AX = ERROR_INVALID_HANDLE
;
; NOTES:
;
; ENTRY POINT:        PhysHandle
;   LINKAGE:          Near/Far call
;
; INPUT: (PARAMETERS)
;             ES:DI -> SFT
;
; EXIT-NORMAL:
;               Carry Clear     - not a physical disk handle
; EXIT-ERROR:
;               Carry Set       - physical handle or invalid handle
;
;               AX = ERROR_INVALID_HANDLE
;
; INTERNAL REFERENCES:
;  ROUTINES:      none
;
; EXTERNAL REFERENCES:
;  ROUTINES:
;                 none
;*********************** END OF SPECIFICATIONS **********************

        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

Procedure PhysHandle,<near,faronly,far16>
        PMONLY

;       check for physical disk handle

        test    word ptr es:[di].sf_fsi.sfi_mode2,open2_phys_disk   ; Clear CY flag
        jnz     ph2                     ; phys disk handle - return error

ph1:    ret


ph2:    stc                             ; return error
        mov     ax,ERROR_INVALID_HANDLE
        jmp     short ph1

EndProc   PhysHandle


BREAK <Ext_Information - Extended information>
;********************* START OF SPECIFICATIONS *******************
;
; SUBROUTINE NAME:  Ext_Information
;
; DESCRIPTIVE NAME:  Extended Information
;
; FUNCTION: This routine returns the extended information for
;           a file handle or ASCIIZ name.  It calls the protect
;           mode workers w_QFileInfo, w_QPathInfo, w_SetFileInfo,
;           and w_SetPathInfo.
;
; NOTES:    Protect Mode Only
;
; ENTRY POINT: Ext_Information
;    LINKAGE: Call Near
;
; INPUT:         AL = 02h - Get info
;                     03h - Set info
;                BX = File handle
;                CX = Length of Infobuf
;                DX = Info level
;                DS:SI -> ASCIIZ name (if BX=FFFF)
;                ES:DI -> Infobuf
;
; EXIT-NORMAL:   NC
;
; EXIT-ERROR:    CY, AX has error code
;
; EFFECTS:       AX
;
; INTERNAL REFERENCES: none
;
;    ROUTINES:    none
;
; EXTERNAL REFERENCES: none
;
;    ROUTINES:    w_QFileInfo
;                 w_SetFileInfo
;                 w_QPathInfo
;                 w_SetPathInfo
;                 EAOP_selector_alloc
;                 EAOP_selector_free
;
;********************** END OF SPECIFICATIONS ********************

        assume  DS:nothing, ES:nothing, SS:TASKAREA
Procedure Ext_Information,far16
LocalVar SkipEAOPconv,WORD
        PMONLY

        push    di                      ; Preserve offset
        add     di,cx                   ; Check for segment overrun
        pop     di                      ; Restore offset
        jnc     eif1                    ; No overrun, jmp
        mov     ax,ERROR_BAD_LENGTH     ; give bad return
        jmp     short eif2
eif1:   cmp     al,3                    ; Valid subfunction?
        jbe     eif3                    ; Yes, jmp
        mov     ax,ERROR_INVALID_FUNCTION  ; give bad return
eif2:   mov     ExtErr_Locus,ERRLOC_UNK ; Extended Error Locus
        stc
        RETP                            ; return to caller

; Figure out if infobuf contains EAOP structure.  If so change seg to sel.
eif3:
        EnterProc <ax>
        mov  ah,0               ; EAOP Flag=0 (set by EAOP_seg_sel)
        je   eif4               ; Set EA call? Yes, jmp
        cmp  dl,4               ; Get EA call?
        je   eifEA              ; Yes, jmp
        cmp  dl,3               ; Get EA call?
        je   eifEA              ; Yes, jmp
        jmp  short eifIn        ; Not an EA call, jmp
eif4:   cmp  dl,2               ; Is it a set EA call?
        jne  eifIn              ; No, jmp

                                ; Infobuf contains EAOP structure.
eifEA:  push es:[di+2]          ; Save segment values
        push es:[di+6]

;62131 Check if called from demCopyEAs
        test [SkipEAOPconv], 8000H ;called from demCopyEAs?     62131
        jnz  eifEA_1               ;yes, then skip conversion   62131
        call EAOP_selector_alloc ; Change pointers to selectors.
eifEA_1:                                                       ;62131

        mov  ah,1               ; Set EAOP flag=1 to indicate EA call
eifIn:  SaveReg <ax,es,di>      ; Preserve EAOP flag and buf ptr
        cmp  bx,-1              ; Invalid handle?
        je   eifQPath           ; Yes, use path call

eifQFile:
        push es                 ; Move infobuf ptr from ES:DI to DS:SI
        pop  ds
        mov  si,di
        cmp  al,3               ; Set call?
        je   eifSetFile         ; Yes, jmp
        mov  dh,80h             ; no filehandle lockID check
        call w_QFileInfo        ; Call QFileInfo worker
        jmp  short eifOut

eifSetFile:
        xor  ax,ax              ; Tell worker that we are a 16 bit call
        mov  dh,80h             ; no filehandle lockID check
        call w_SetFileInfo      ; Call SetfileInfo worker
        jmp  short eifOut

eifQPath:
        mov  bx,dx              ; BX=infolevel
        mov  dx,si              ; DS:DX points to pathname
        cmp  al,3               ; Set call?
        je   eifSetPath
        cmp  bx,5               ; BN001 see if this was call to get curr dir
        je   eifcap             ; BN001 yes? capitalize path name

        call w_QPathInfo        ; Call QPathInfo worker
        jmp  short eifOut


eifcap: call w_QPathInfo        ;BN001 call QPathInfo worker

        pop  di                 ;BN001 get regs off the stack
        pop  es                 ;BN001
        push es                 ;BN001 now put them back
        push di                 ;BN001
        push es                 ;BN001 set up DS:SI to point to ES:DI buffer
        pop  ds                 ;BN001
        mov  si,di              ;BN001
        pushf                   ;BN001 save flags from QpathInfo call

        call StrCpy             ;BN001 upper case string, don't move it
        popf                    ;BN001 restore flags

        jmp  short eifOut       ;BN001

eifSetPath:
        xor  ax,ax              ; Tell worker that we are a 16 bit call
        push si                 ; just for safety sake
        xor  si,si              ; no pathinfo flags
        call w_SetPathInfo      ; Call SetPathInfo worker
        pop  si

eifOut: RestoreReg <di,es,bx>   ; Restore EAOP flag and buf ptr
        pushf                   ; Preserve carry flag
        cmp  bh,1               ; EAOP structure?
        jne  eifBye             ; No, jmp

;62131 Check if called from demCopyEAs
        test [SkipEAOPconv], 8000H ;called from demCopyEAs? 62131
        jnz  eifEA_2               ;yes, then skip          62131
        call EAOP_selector_free ; Free selectors

eifEA_2:                                                   ;62131
        popf                    ; Restore carry flag
        pop  es:[di+6]          ; Restore segment values
        pop  es:[di+2]

_eifExit:                       ;64515
        LeaveProc               ;62131
        RETP                    ; Return to caller (EA call)

eifBye: popf                    ; Restore carry flag
        jmp   _eifExit

EndProc  Ext_Information


BREAK <EAOP_selector_alloc>
;********************* START OF SPECIFICATIONS ********************
;
; SUBROUTINE NAME:  EAOP_selector_alloc / EAOP_selector_free
;
; DESCRIPTIVE NAME:  Change EAOP pointers to from segments to sels
;
; FUNCTION: This routine changes the FEAList and GEAList pointers
;           in an EAOP structure to/from segments or selectors.
;
; NOTES:    Protect Mode Only
;
; ENTRY POINT: EAOP_selector_alloc
;              EAOP_selector_free
;    LINKAGE: Call Near
;
; INPUT:         ES:DI -> Infobuf
;
; EXIT-NORMAL:   NC
;
; EXIT-ERROR:    Note: no errors will be returned since the workers
;                      SegMapVirt and GDTFree don't return errors.
; EFFECTS:       AX
;
; INTERNAL REFERENCES: none
;
;    ROUTINES:    none
;
; EXTERNAL REFERENCES: none
;
;    ROUTINES:    SegMapVirt
;                 GDTFree
;
;********************** END OF SPECIFICATIONS *********************

        assume  DS:nothing, ES:nothing, SS:nothing
Procedure   EAOP_selector_free,far16
        call    EAOP_selector_free
        RETP
EndProc     EAOP_selector_free

        assume  DS:nothing, ES:nothing, SS:nothing
Procedure EAOP_selector_alloc,<near,faronly,far16>
        PMONLY

        pusha
        push    ds

; SegMapVirt needs the following parms in the following regs (virtseg.asm)
        mov     bx,0            ; BX=selector
        mov     ax,es:[di+2]    ; AX=fpGEAList segment
        xor     cx,cx           ; CX=length (0=64k)
        SaveReg <es>
        CALLFAR SegMapVirt      ; SegMapVirt(ax=seg,bx=sel,cx=size)
        RestoreReg <es>
        mov     es:[di+2],ax    ; Save sel for fpGEAList

; SegMapVirt needs the following parms in the following regs (virtseg.asm)
        mov     bx,0            ; BX=selector
        mov     ax,es:[di+6]    ; AX=fpFEAList segment
        xor     cx,cx           ; CX=length (0=64k)
        SaveReg <es>
        CALLFAR SegMapVirt      ; SegMapVirt(ax=seg,bx=sel,cx=size)
        RestoreReg <es>
        mov     es:[di+6],ax    ; Save sel for fpFEAList

        pop     ds
        popa
        jmp short eaBye

;** EAOP_selector_free  entry point
Entry EAOP_selector_free, hybrid

        pushf
        push    ax              ; Save possible error code
; GDTFree - Free a GDT Selector
        mov     ax,es:[di+2]    ; AX=selector for fpGEAList
        xor     dx,dx           ; DX=default TaskPTDA
        SaveReg <es>
        CALLFAR GDTFree         ; GDTFree(ax=sel,dx=TaskPTDA)
        RestoreReg <es>
; GDTFree - Free a GDT Selector
        mov     ax,es:[di+6]    ; AX=selector for fpFEAList
        xor     dx,dx           ; DX=default TaskPTDA
        SaveReg <es>
        CALLFAR GDTFree         ; GDTFree(ax=sel,dx=TaskPTDA)
        RestoreReg <es>
        pop     ax              ; Restore possible error code
        popf
eaBye:  RETP                    ; Return to caller

EndProc  EAOP_selector_alloc


HIGH2CODE ENDS

        FARCODE HIGH4
NMPIPESWAPCODE SEGMENT

        PUBLIC _StartFILEIOGROUP
_StartFILEIOGROUP label byte

;***    w_FileIO - Worker for Multiple lock, unlock, seek, read and write I/O.
;
;       w_FileIO verifies that the handle is valid and processes the commands
;       in the buffer verifing that they are valid and calling the local
;       routines to process the individual command. If the handle is for a
;       remote file system we give the entire buffer to the FSD to process.
;
;       ENTRY   DX      =  handle to read from
;               CX      =  Length of command buffer
;               SS:BX   =  Agrument frame on stack (see argframe.inc);
;                          contains pointers to command buffer and
;                          pointer to command error offset
;               DI:SI   =  Protected filehandle lockID
;               AH      =  00h -> filehandle lockID check;
;                          80h -> no filehandle lockID check
;
;       EXIT
;               command error offset filled in
;               Carry Clear
;                       No error
;               Carry Set
;                       AX = ERROR_INVALID_HANDLE
;                          = ERROR_ACCESS_DENIED
;
;       CALLS   SFFromHandle,PhysHandle,FileIORouter
;
;       USES    ALL
;
;      if ((rc = SFFromHandle(handle,&sft_ptr)) != 0 )
;         return (rc);
;
;      if ((rc = physhandle(sft_ptr)) != 0 )
;         return (rc);
;
;      if this sft is for a device or pipe
;         return (ERROR_INVALID_HANDLE);
;
;      Store sft pointer in TCB;
;
;      if this sft is for FAT file system or local FSD {
;        rc = FileIORouter();
;        return (rc);
;      }
;
;      if this sft is for a FSD && the FSD is a REMOTE file system {
;         rc = call FSDs' FS_FILEIO entry point
;         return (rc);
;      }

Procedure   w_FileIO,HYBRID
    ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar    wfio_cmderror_ptr,DWORD
LocalVar    wfio_cmdbuffer_ptr,DWORD
LocalVar    wfio_FileHandleLockID,DWORD
LocalVar    wfio_CheckFileHandleLockID,BYTE

        CodeLock FILEIO_GROUP
        EnterProc

; save check FileHandleLockID flag
        mov     byte ptr wfio_CheckFileHandleLockID,ah

; save file handle lockID
        mov     wfio_FileHandleLockID.LowWord, si
        mov     wfio_FileHandleLockID.HighWord, di

        CpuMode 386
        mov     eax,ss:[bx].fio_ErrorOffset
        mov     wfio_cmderror_ptr,eax
        mov     eax,ss:[bx].fio_CommandList
        mov     wfio_cmdbuffer_ptr,eax
        CpuMode reset

        mov     bx,dx
        push    cx
        CALLFAR SFFromHandle            ; es:di -> sft entry
        pop     cx
        jc      wfio6                   ; if error, go return
        CALLFAR PhysHandle              ; do we have a physical disk handle ?
        jc      wfio6                   ; yes, go return

        mov     ah, byte ptr wfio_CheckFileHandleLockID
        test    ah,80h                         ; verify access?
        jnz     wfio0                          ; no, flag bit = 1

        mov     ax, es:[di].sf_LockID.LowWord
        cmp     ax, wfio_FileHandleLockID.LowWord  ; check access
        mov     ax, ERROR_ACCESS_DENIED        ; anticipate error
        jne     wfio7                          ; access denied

        mov     ax, es:[di].sf_LockID.HighWord
        cmp     ax, wfio_FileHandleLockID.HighWord ; check access
        mov     ax, ERROR_ACCESS_DENIED        ; anticipate error
        jne     wfio7                          ; access denied
wfio0:
        push    bp
        mov     bp,CurrTCB
        mov     word ptr [bp].TCBThisSFT,DI     ; save offset of pointer
        mov     word ptr [bp].TCBThisSFT+2,ES   ; save segment value
        pop     bp

wfio1:  cmp     es:[di].sf_FSC.Offst,FSC_LOCAL  ; FAT?
        jb      wfio3                           ; no, check for Remote FSD

wfio2:  mov     dx,[wfio_cmderror_ptr].Segmt
        mov     di,[wfio_cmderror_ptr].Offst    ; dx:di -> cmd error offset
        mov     ax,[wfio_cmdbuffer_ptr].Segmt
        mov     si,[wfio_cmdbuffer_ptr].Offst   ; ax:si -> cmd buffer
        call    FileIORouter
        jmp     short wfio5


wfio3:  SaveReg <es,di>                 ; ES:DI-> SFT
        les     di,es:[di].sf_FSC       ; ES:DI-> FSC
        les     di,es:[di].FS_ATTRIBUTE ; ES:DI-> Attribute word for FSD
        test    word ptr es:[di],FS_ATTR_LOCKINFO   ; FSD does lock processing?
        RestoreReg <di,es>
        jz      wfio2                   ; No, go break up for local FSD
        push    es
        lea     bx,es:[di].sf_fsi
        push    bx                      ; ES:BX -> SFT FS independent area
        push    es
        lea     bx,es:[di].sf_fsd
        push    bx                      ; ES:BX -> SFT FS dependent area
        push    [wfio_cmdbuffer_ptr].Segmt
        push    [wfio_cmdbuffer_ptr].Offst
        push    cx                      ; CX = length of command buffer
        push    [wfio_cmderror_ptr].Segmt
        push    [wfio_cmderror_ptr].Offst
        get_advise                      ; ax = advisory bits
        push    ax                      ; push IOflag (advisory bits)
        les     di,es:[di].sf_FSC       ; ES:DI->FSC
        call    es:[di].FS_FILEIO       ; call remote FSD
        or      ax,ax                   ; any error
        jz      wfio5                   ; no, return with carry clear
        stc                             ; yes, set carry flag

wfio5:  LeaveProc
        CodeUnlock FILEIO_GROUP
        ret

wfio6:  mov     ax,ERROR_INVALID_HANDLE
wfio7:  stc
        jmp     short wfio5

EndProc w_FileIO


;***    FileIORouter - Route Command To Appropriate File System Routine
;
;       FileIORouter scans through the command buffer calling the appropriate
;       File System routine with the parameters specified in the command buffer
;       We use the Protected Transfer routines to fetch and store information
;       in the command buffer.
;
;       ENTRY   BX      =  handle to read from
;               CX      =  Length of command buffer
;               AX:SI   -> pointer command buffer
;               DX:DI   -> pointer to command error offset
;       EXIT
;               Carry Clear
;                   no error
;               Carry Set
;                   AX = error code
;
;       CALLS   w_Read,w_Write,sft_lseek,Lock_list,Unlock_list,CmdPktCopy
;               SuWord,FuWord
;
;       USES    ALL
;
;      endbufptr = cmdbufptr + cmdbuflen;
;      startbufptr = cmdbufptr;
;
;      while (cmdbufptr < endbuffer) {
;
;          switch (cmdbufptr -> cmd) {
;             case read :
;                rc = w_read(handle,bufferlen,bufferptr,&actual);
;                if (rc == 0)
;                  cmdbufptr += size (readpacket);
;                break;
;             case write :
;                rc = w_write(handle,bufferlen,bufferptr,&actual);
;                if (rc == 0)
;                  cmdbufptr += size (writepacket);
;                break;
;            case seek:
;                rc = sft_lseek(method,position,&actual);
;                if (rc == 0)
;                  cmdbufptr += size (seekpacket);
;                break;
;            case lock_cmd:
;                rc = lock_list(cmdbufptr,size(lockheader),lockcnt);
;                if (rc == 0)
;                  cmdbufptr += (lockcnt * size(lockpacket)) + size lockhdr;
;                break;
;            case unlock_cmd:
;                rc = unlock_list(cmdbufptr,&unlockcnt,size unlockpacket);
;                if (rc == 0)
;                  cmdbufptr +=(unlockcnt * size(lockpacket)) + size unlockhdr;
;                break;
;          }                    /* end switch   */
;          if (rc != 0)
;            return (rc,cmdbufptr);
;      }                        /* end while    */
;
;      cmderroffset = cmdbufptr - startbufptr;

Procedure FileIORouter,NEAR,LOCAL
    ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar    fior_cmderror_ptr,DWORD     ; ptr of place to store error offset
LocalVar    fior_startoffset,WORD       ; starting offset in command buffer
LocalVar    fior_endoffset,WORD         ; ending offset in command buffer
LocalVar    fior_offset,WORD            ; current working offset
LocalVar    fior_handle,WORD            ; file handle for command buffer
LocalVar    fior_selector,WORD          ; selector of user command buffer
LocalVar    fior_IORoutine,DWORD        ; IO routine to invoke
LocalVar    fior_LockCnt,WORD           ; lock count for lock/unlock
LocalVar    fior_tempbuffer,MAXFILEIOSIZE ; buffer to hold max FIO cmd and its
                                          ; parameters

        EnterProc <dx,di,si,si,si,bx,ax>

        add     fior_endoffset,cx
        or      cx,cx                           ; 0 length buffer ?
        LJZ     fior8                           ; yes, return with carry clear
fior1:  mov     bx,fior_selector                ; BX:SI->cmd
        CALLFAR FuWord                          ; AX = command
        LJC     fior9                           ; if protection fault return
        sub     si,2                            ; BX:SI->cmd
        or      ah,ah                           ; trap special condition where
        LJNZ    fior11                          ; user gives command > 255.

        CALLFAR $TJMP
        db      FIO_MAXCMD
        dw      NMPIPESWAPCODE:fior11           ; Invalid command
        dw      NMPIPESWAPCODE:fior2            ; LOCK
        .errnz  FIO_CMD_LOCK
        dw      NMPIPESWAPCODE:fior3            ; UNLOCK
        .errnz  FIO_CMD_UNLOCK-1
        dw      NMPIPESWAPCODE:fior4            ; SEEK
        .errnz  FIO_CMD_SEEK-2
        dw      NMPIPESWAPCODE:fior5            ; READ
        .errnz  FIO_CMD_READ-3
        dw      NMPIPESWAPCODE:fior6            ; WRITE
        .errnz  FIO_CMD_WRITE-4

;**     Lock command
;
;       (fior_selector:si -> lock command packet

fior2:  mov     di,CurrTCB
        les     di,ss:[di].TCBThisSFT            ; es:di -> sft
        test    es:[di.sf_flags],DEVID_DEVICE   ; lock on device ?
        LJNZ    fior12                          ; yes, report error
        mov     cx,FIO_LockListOff              ; number of bytes to copy
        call    CmdPktCopy
        LJC     fior9

        mov     cx,es:[di].FIO_LockCnt              ; CX = number of locks
        mov     fior_LockCnt,cx
        mov     dx,es:[di].FIO_Lock_Timeout.LowWord
        mov     di,es:[di].FIO_Lock_Timeout.HighWord ;DI:DX Timeout of locklist
        mov     si,fior_offset
        add     si,FIO_LockListOff               ; BX:SI ->pointer to lock list
        mov     ax, fior_handle                  ; file handle

; BX:SI -> pointer to list of regions to be locked
; DI:DX -> Timeout value for entire lock list to complete
; CX    =  number of locks to be locked

        CALLFAR Lock_list               ; lock the list of locks
        LJC     fior9                   ; if any errors go return
        mov     ax,size FIOLock         ; AX = size of lock record
        mul     fior_LockCnt            ; AX = space occupied by locks alone
        add     ax,FIO_LockListOff      ; add in lock list header size
        add     fior_offset,ax          ; AX = total space occupied locks
        jmp     fior8

;**     UnLock command
;
;       (fior_selector:si -> unlock command packet

fior3:  mov     di,CurrTCB
        les     di,ss:[di].TCBThisSFT           ; es:di -> sft
        test    es:[di.sf_flags],DEVID_DEVICE   ; unlock on device ?
        LJNZ    fior12                          ; yes, report error
        mov     cx,FIO_UnLockListOff
        call    CmdPktCopy
        LJC     fior9

        mov     cx,es:[di].FIO_LockCnt           ; cx = # of locks to unlock
        mov     fior_LockCnt,cx
        mov     si,fior_offset
        add     si,FIO_UnLockListOff             ; bx:si->ptr to unlock list

; BX:SI -> pointer to list of regions to be unlocked
; CX    =  number of locks to be unlocked
; AX    =  size UnLock command packet

        mov     ax,size FIOUnlock       ; AX = size of unlock packet
        CALLFAR Unlock_list             ; unlock list of locks
        LJC     fior9                   ; if any errors go return
        mul     fior_LockCnt            ; AX = space occupied by locks alone
        add     ax,FIO_UnLockListOff    ; add in unlock list header
        add     fior_offset,ax          ; AX = total space occupied be locks
        jmp     fior8

;**     Seek command
;
;       (fior_selector:si -> Seek command packet

fior4:  mov     cx,size FIOCmdSeek
        call    CmdPktCopy
        LJC     fior9

        mov     cx,es:[di].FIO_Seek_Position.HighWord  ;
        mov     dx,es:[di].FIO_Seek_Position.LowWord   ; cx:dx = seek position
        mov     ax,es:[di].FIO_Seek_Method             ; ax = method to seek
        mov     di,CurrTCB
        les     di,ss:[di].TCBThisSFT            ; es:di -> sft

; CX:DX = distance to move in bytes
; ES:DI -> sft
; AX    = seek method

        CALLFAR sft_lseek               ; seek to desired position
        LJC     fior9                   ; if any errors go return
        mov     si,fior_offset
        lea     di,[si].FIO_Seek_Actual
        mov     bx,fior_selector        ; bx:di -> pointer to actual position
                                        ; dx:ax -> actual position seeked to
        CALLFAR SuWord                  ; store lowword of actual position
        LJC     fior9
        mov     ax,dx
        CALLFAR SuWord                  ; store highword of actual position
        LJC     fior9

        add     fior_offset,size FIOCmdSeek
        jmp     short fior8

;**     Read command
;
;       (fior_selector:si -> Read command packet

fior5:  MOVFAROFFSET [fior_IORoutine].Offst,w_Read,DOSHIGH2CODE
        jmp     short fior7

;**     Write command
;
;       (fior_selector:si -> write command packet

fior6:  MOVFAROFFSET [fior_IORoutine].Offst,w_Write,DOSHIGH2CODE
fior7:  mov     cx,size FIOCmdIO
        call    CmdPktCopy
        jc      fior9

; We have to pass the buffer address in DS:DX. In order to do this we have to
; gurantee that DS is a valid selector so we'll have to call ProbeBuf. Probebuf
; will bring the segment into memory if it is not present. If ProbeBuf returns
; with no error we know we can load the selector into DS without a protection
; fault. We also have to stamp the selector with RPL_RING3 .

        lea     di,fior_tempbuffer          ; ES:DI -> temp buffer on stack
        mov     bx,es:[di].FIO_IO_BufferPtr.Segmt  ; BX = buffer selector
        or      bx,RPL_RING3                ; stamp the selector
        mov     si,es:[di].FIO_IO_BufferPtr.Offst  ; BX:SI -> buffer pointer
        mov     cx,bx
        mov     dx,si                   ; CX:DX -> buffer pointer
        SaveReg <cx,dx,es>
        xor     dh,dh                   ; only need read access
        mov     cx,1                    ; for only 1 byte
        CALLFAR ProbeBuff               ;
        RestoreReg <es,dx,cx>
        jc      fior9                   ; if protection fault go return
        mov     ds,cx                   ; load selector
        mov     cx,es:[di].FIO_IO_BufferLen  ; CX = number of bytes to transfer
        mov     bx,fior_handle          ; BX = file handle
        push    bp                      ; save stack frame
        mov     [fior_IORoutine].Segmt,seg DOSHIGH2CODE:w_Write
        xor     ax,ax                   ; no file handle lockID check
        call    dword ptr [fior_IORoutine]
        pop     bp                      ; restore stack frame
        mov     bx,cx                   ; save bytes actually transfered
        mov     cx,ax                   ; save possible error code
        lahf                            ; get carry flag
        mov     dh,ah                   ; save it away
        mov     si,fior_offset
        lea     di,[si].FIO_IO_Actual   ;
        mov     ax,bx                   ; AX = # bytes transfered successfully
        mov     bx,fior_selector        ; BX:DI -> ptr to # bytes transfered
        CALLFAR SuWord                  ; store # bytes transfered
        jc      fior9                   ; if protection fault go return
        mov     ah,dh                   ; get flags back from dh
        sahf                            ; store flags from ah
        mov     ax,cx                   ; restore possible error code
        jc      fior9                   ; if any errors from IO go return
        add     fior_offset,size FIOCmdIO  ; increment past IO packet

fior8:  mov     si,fior_offset          ;
        cmp     si,fior_endoffset       ; finisihed all commands ?
        LJB     fior1                   ; no, go process next command
        xor     ax,ax                   ; set no error
fior9:  mov     cx,ax                   ; save possible error code
        lahf                            ; get carry flag
        mov     dh,ah                   ; save it away
        mov     ax,fior_offset
        sub     ax,fior_startoffset     ; relative offset into cmd buffer
        mov     bx,fior_cmderror_ptr.HighWord
        mov     di,fior_cmderror_ptr.LowWord ; BX:DI -> error offset
        CALLFAR SuWord                  ; store error offset
        jc      fior10
        mov     ah,dh                   ; get flags back from dh
        sahf                            ; store flags from ah
        mov     ax,cx                   ; restore possible error code
fior10: LeaveProc
        ret

fior11: mov     ax,ERROR_INVALID_PARAMETER
        stc
        jmp     short fior9
fior12: mov     ax,ERROR_INVALID_HANDLE
        stc
        jmp     short fior9

EndProc FileIORouter

;***    CmdPktCopy
;
;       CmdPktCopy copies the command packet onto the stack using the protected
;       transfer routine FuBuff.
;
;       ENTRY   CX =  number of bytes to copy
;               fior_selector:si -> pointer to command buffer
;       EXIT
;               Carry Clear
;                       ES:DI -> temporary buffer on stack
;                       BX = fior_selector
;                       SI = SI + CX
;               Carry Set
;                       AX=ERROR_PROTECTION_VIOLATION
;                       ES:DI -> temporary buffer on stack
;       CALLS   FuBuff
;
;       USES    BX,CX,SI,DI,ES
;
Procedure   CmdPktCopy,NEAR
    ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

        lea     di,fior_tempbuffer
        TaskContext ES
        mov     bx,fior_selector

; BX:SI -> command buffer
; ES:DI -> temporary buffer on stack
; CX    =  size of packet

        SaveReg <es,di,bx>
        CALLFAR FuBuff
        RestoreReg <bx,di,es>
        ret
EndProc CmdPktCopy

        PUBLIC _EndFILEIOGROUP
_EndFILEIOGROUP label byte

NMPIPESWAPCODE ENDS
        END
