;/*static char *SCCSID = "@(#)fshelper.asm      6.17 92/02/27";*/
;***    fshelper.asm - IFS helper routines
;
;       IFS Helpers.
;
;       FS helpers provide various kernel services to Installable File
;       Systems (IFS's).  These services can be called directly by the
;       file system driver (FSD).  Parameters are passed on the stack
;       in a left-to-right order.  The helpers clean the stack.  Registers
;       DS, ESI, EDI, EBP, and SS are preserved.  Return conditions appear
;       in AX with the convention that AX == 0 indicates successful
;       completion.  AX != 0 indicates an error with the value of AX
;       being the error code.  All FS helpers should be in this file.
;
;       Therese Stowell
;       November 10, 1987
;
;       FSH_SEGALLOC
;       FSH_SEGFREE
;       FSH_SEGREALLOC
;
;       FSH_FORCENOSWAP
;
;       FSH_SEMSET
;       FSH_SEMSETWAIT
;       FSH_SEMWAIT
;
;       FSH_INTERR
;
;       FSH_SEMREQUEST
;       FSH_SEMCLEAR
;       FSH_IOSEMCLEAR
;
;       FSH_YIELD
;
;       FSH_PROBEBUF
;
;       FSH_GETPRIORITY
;       FSH_FLUSHBUF
;
;       FSH_CRITERROR
;
;       FSH_DEVIOCTL
;
;       FSH_GETVOLPARM
;       FSH_FINDDUPHVPB
;       FSH_DOVOLIO
;       FSH_DOVOLIO2
;
;       FSH_ADDSHARE
;       FSH_REMOVESHARE
;
;       FSH_ISCURDIRPREFIX
;
;       FSH_LOADCHAR
;       FSH_PREVCHAR
;       FSH_STORECHAR
;
;       FSH_GETOVERLAPBUF
;
;       FSH_WILDMATCH
;
;       FSH_FINDCHAR
;       FSH_CANONICALIZE
;       FSH_QSYSINFO
;
;       FSH_NAMEFROMSFN
;
;       FSH_UPPERCASE
;       FSH_CHECKEANAME
;
;       FSH_QUERYOPLOCK
;       FSH_QUERYSERVERTHREAD
;
;       FSH_EXTENDTIMESLICE
;
;       MFS Helpers are similar to FS Helpers.  The differ in that they are
;       for use only by a Mini-FSD during stage one of the boot process when
;       booting from an FSD Managed volume.
;
;       MFSH_DOVOLIO
;       MFSH_INTERR
;       MFSH_SEGALLOC
;       MFSH_SEGFREE
;       MFSH_SEGREALLOC
;       MFSH_SETBOOTDRIVE
;       MFSH_CALLRM
;       MFSH_LOCK
;       MFSH_PHYSTOVIRT
;       MFSH_UNLOCK
;       MFSH_UNPHYSTOVIRT
;       MFSH_VIRT2PHYS
;
;       NOTE:
;       The FSHentry macro is being used to generate an entry in the FSHMTE's
;       entry table.  The design requires that there be no holes in the entry
;       table.  This means, if for some reason, it becomes necessary to remove
;       an FSHelper routine, we need to do one of the following:
;               o leave the routine definition as is and have the routine
;                 return an error code such as ERROR_INVALID_FUNCTION
;               o reuse the ordinal number for a new function
;
;       NOTE:
;
; 61801  24 Feb 93  Correct the transfer size calculation in FSH_DOVOLIO
; 69875  10 Aug 93  Preventing system hang during int10 writeTTY by
;                   writing characters directly to display memory.
; 73941  17 Sep 93  Removing temporary fix defect 69875. Resolved by 73941.
; 76699  15 Dec 93  Make Perfview optional with assembler flag PERFVIEW
; 97886  12 Sept 94 FSH_EXTENDTIMESLICE - Extend time slice if cache hit.
; 102060 14 Oct 94  REGR: defect 72768 took out the return code of -1 from
;                   FSH_WILDMATCH, put it back in in a more orderly fashion.
; 72417  18 Nov-95  MLM moved some helpers to HIGH3CODE to make room in HIGH2CODE
;                   Also moved references to TCB for PathNameType, WFP_Start,
;                   WFP_Path_End, Curr_Dir_End
; 150371 01 Apr-96  System Anchor Block support
;

.xlist
include fnofs.inc
include dosseg.inc
include basemaca.inc
include osconst.inc
include osmaca.inc
include oldmaca.inc
include buffer.inc
include chardd.inc
include curdir.inc
include dbcs.inc
include devsym.inc
include dossymc.inc
include dpb.inc
INCL_ERRORS     EQU     1
include bseerr.inc
include filemode.inc
include filesys.inc
include fshordnl.inc
include tkexport.inc
include mft.inc
include pgexport.inc
include vmexport.inc
include vmowner.inc
include mi.inc
include oplock.inc
include protmode.inc
include seldesc.inc
include selxport.inc
include virtsym.inc
include proc.inc
include sft.inc
include kmode.inc
include sysentry.inc
include vpb.inc
include doshlp.inc
include basemid.inc

ifdef PERFVIEW
include pvwxport.inc
include perfvw.inc
endif

include infoseg.inc
include harderr.inc
include sched.inc
.list
include fsh.inc         ; depends on vmowner.inc, filemode.inc, virtsym.inc and
                        ;  seldesc.inc
include initmac.inc


        CpuMode 386
        ModName fshelper,FNO_FSHELPER_ASM

        ReferFlat       _pTCBCur,DWORD
        ReferFlat       _pPTDACur,dword
        ReferTask       CurrTCB,WORD
        ReferTask       ProcType,BYTE       ; RM or PM
        ReferTask       CurrentPDB,WORD     ;
        ReferTask       ptda_handle,WORD

                                            ;72417 removed PTDA entries now
                                            ;      in TCB

        ReferTask       VerFlg,BYTE         ; Verify on write flag

        ReferGDT        GDT_VPB,WORD

        ReferGlobal     ErrMap24,BYTE
        ReferGlobal     MaxSec,WORD
        ReferGlobal     TaskNumber,WORD
        ReferGlobal     FileBufferAddress,DWORD
        ReferGlobal     FileBufferLength,DWORD
        ReferGlobal     BytesPerSector,WORD
        ReferGlobal     mFSD_FLAG,BYTE          ;Mini-FSD is present
        ReferGlobal     DHF,DWORD
        ReferGlobal     SISData,BYTE            ;global infoseg
        ReferGlobal     FSPrtyIO_Mode,BYTE

        extrn           $TJMP:near
        extrn           ChkBufPtr:near
        extrn           DoVolIO:near
        extrn           DoVolIO2:near
        extrn           EndTransfer:near
        extrn           FindMultiChar:near
        extrn           GetBuffr:near
        extrn           GetOverlapBuf:near
        extrn           MetaMatch:near
        extrn           ProbeBuff:near
        extrn           RawDiskIO:near
        extrn           RlseBuffr:near
        extrn           ScanCDS:near
        extrn           SemClear:near
        extrn           IOSemClear:near
        extrn           SemRequest:near
        extrn           sw_DevIOCtl:near
        extrn           TCYield:near
        extrn           SFFromSFN:near
        extrn           StrCpy:near
        extrn           FStrCpy:near            ; High2code segment
        extrn           FStrCpy32:near          ; High2code segment
        extrn           RealModeIC:near         ; see initmac.inc
        extrn           ProtModeIC:near         ; see initmac.inc
        extrn           WaitVPB:near
        extrn           WaitDPB:near

        EXTRNFAR        AllocateGDTSelector
        EXTRNFAR        CheckEAName
        EXTRNFAR        ChkHEDaemon
        EXTRNFAR        FreeGDTSelector
        EXTRNFAR        FreeTempBuffer
        EXTRNFAR        FuWordNF                ;see setfetum.asm
        EXTRNFAR        FuByteNF                ;see setfetum.asm
        EXTRNFAR        GetTempBuffer
        EXTRNFAR        HardError
        EXTRNFAR        ifs_SegFree
        EXTRNFAR        _InternalError
        EXTRNFAR        PhysToGDTSelector
        EXTRNFAR        ProbeBuff
        EXTRNFAR        SegAlloc
        EXTRNFAR        SegLock
        EXTRNFAR        SegUnlock
        EXTRNFAR        SelToSeg                ;see modesw.asm
        EXTRNFAR        SemSet
        EXTRNFAR        SemSetWait
        EXTRNFAR        SemWait
        EXTRNFAR        StrLen
        EXTRNFAR        StrLen32
        EXTRNFAR        SuWordNF                ;see setfetum.asm
        EXTRNFAR        SYIReadSectors
        EXTRNFAR        VirtToPhys
        EXTRNFAR        WrtToScrn
        EXTRNFAR        WrtToScrnClr
        EXTRNFAR        GetDevEntry
; Defect 72417 changes start
        EXTRNFAR        FlushBufND
        EXTRNFAR        BufInvalidate
        EXTRNFAR        VPBFind
        EXTRNFAR        WhatVolume
        EXTRNFAR        ToggleRequest
; Defect 72417 changes end
; Defect 148538
        EXTRNFAR        AddShare
        EXTRNFAR        RemoveShare
        EXTRNFAR        TransPathNoPCName
; Defect 148538

        EXTRNF32        ioSaveTCB
        EXTRNF32        ioRestoreTCB
        EXTRNF32        _TKSetSSBase            ; see initmac.inc

ifdef PERFVIEW
        EXTRN32         _PVWRegisterPerfCtrs
endif

        EXTRN32         _SELIfsAlloc
        EXTRN32         _SELIfsRealloc
        EXTRN32         _SELIfsFree
        EXTRN32         _SELIfsNoSwap
        EXTRN32         _VMFreeTempBuffer
        EXTRN32         _VMGetTempBuffer
        EXTRN32         _VMGetOwner
        EXTRN32         _ioBeginTransfer
        EXTRN32         _TKUpdatePriority

DATA    segment         ;##### would rather have this in DosInitRMData

mfsh_addr_store         dd      ?

        public  _MFSD_Stage, MFSD_Stage
_MFSD_Stage     label   BYTE        ; D150371
MFSD_Stage      db      ?           ; stage for bootable IFS

DATA    ends            ;##### would rather have this in DosInitRMData

DATA segment

szKBD   db      'KBD$',0
        public  szKBD

DATA ends


        FARCODE INIT
INITCODE segment


;***    MFSH_CALLRM- Put the machine in real mode, call the specified routine,
;       put the machine back intoprotect mode and return.
;
;       ENTRY   push    Proc
;               call    far ptm MFSH_CallRM
;
;       WHERE
;               Proc    DWORD virtual address (selector, offset) of the routine
;                       to call
;
;       EXIT    Ax = 0 if success.
;               Ax = ERROR_PROTECTION_VIOLATION if proc is not in low memory.
;
;       USES    Prior to calling proc:
;               ax, bx, ss, sp, bp, es, flags
;               ds is converted to a segment.
;               ds and si are preserved and may be used to pass parameters
;               to proc.
;               Other registers (cx, dx, di,) are currently preserved but
;               this is not documented and may change
;
;               Overall:
;               ax, flags
;
;       REMARKS Parameters to proc may not be passed on the stack, as a stack
;               switch is done.
;               They should be passed in ds:si.  If this is done, ds
;               must point to low memory.
;
;       PSEUDOCODE
;
;       Save registers
;       Call far SelToSeg to convert Proc to segment offset
;       If no error then
;         save ds in es (it will be converted to a segment by rm)
;         switch to real mode stack
;         switch to real mode (call realmodeirmc)
;         restore ds from es
;         call indirect proc
;         switch back to protect mode (call protmodeirmc)
;         restore stack
;         set ax = 0
;       else
;         Set ax = protection violation
;       restore registers
;       Return
;
FSHentry MFSH_CALLRM, ORD_MFSH_CALLRM
assume ds:nothing, es:nothing, ss:nothing
ArgVar  Proc_Addr,DWORD

        EnterProc
        SaveReg <ds,es,bx,cx,dx,si,di>
        mov     bx,Proc_Addr.hiword
        CALLFAR SelToSeg
        jc      mfc_10
        mov     bx,ds                           ;preserve ds for the call
        mov     es,bx
        DosContext      ds
        mov     mfsh_addr_store.hiword,ax
        mov     ax,Proc_Addr.loword
        mov     mfsh_addr_store.loword,ax
        pushf                                   ;save interrupt state
        cli                                     ;disable interrupts for the duration
        RM      ax,bx
        mov     bx,es                           ;restore ds for the call
        mov     ds,bx
        DosContext es
        call    dword ptr es:mfsh_addr_store
        PM      ax
        POPFF                                   ;restore interrupt state
        xor     ax,ax
        jmp     mfc_20
mfc_10:
        mov     ax,ERROR_PROTECTION_VIOLATION
mfc_20:
        RestoreReg      <di,si,dx,cx,bx,es,ds>
        LeaveProc
        ret     ?aframe                 ; clean args off stack
EndProc         MFSH_CALLRM


;***    MFSH_DOVOLIO - Volume-based sector-oriented transfer
;
;       MFSH_DOVOLIO is the helper that interfaces with the SysInit File System
;       to read sectors from the boot drive.
;
;       ENTRY   push    @Data
;               push    @cSec
;               push    iSec
;               call    far ptm MFSH_DOVOLIO
;
;       WHERE
;               Data    Data Area
;               cSec    Word specifying the number of sectors to read
;               iSec    DWORD initial sector number
;
;       EXIT    cSec contains the number of sectors read
;               (ax) = return code
;                       0 - success
;                       ERROR_PROTECTION_VIOLAION
;
;       USES    EAX,ES
;
;       PSEUDOCODE
;
;       If stage 1 then
;       If dovolio flag set
;       Save Registers
;       If data address valid then
;         Convert data address to physical address
;         Save address and length variables
;         Store physical address
;         Save # sectors to read
;         Calculate # bytes to read
;         If no error reading getting sectors to read then
;           If bytes to read <= 64kb then
;             Store bytes to read in filebufferlength (required by readsectors)
;             Set up isec for readsectors
;             Call SYIReadSectors
;             Calculate sectors read
;             Store sectors read in cSec
;             If no error writing sectors read then
;               Clear ax (good return)
;           Else
;             Set ax = protection violation
;         Restore address and length variables
;       Else
;         Set ax = protection violation
;       Else
;         Set ax = ERROR_INVALID_FUNCTION
;       Restore registers
;       Return
;
FSHentry MFSH_DOVOLIO, ORD_MFSH_DOVOLIO
assume ds:nothing, es:nothing, ss:nothing
ArgVar      iSec,DWORD                      ; first sector to read
ArgVar      cSec,DWORD                      ; buffer for number of sectors
ArgVar      databuf,DWORD                   ; buffer to use for read

        EnterProc
        SaveReg <ds,bx,cx,dx,si,di>
        DosContext      ds
        push    FileBufferAddress.hiword
        push    FileBufferAddress.loword    ; save buffer address
        push    FileBufferLength.hiword
        push    FileBufferLength.loword     ; save buffer length

        cmp     MFSD_Stage, 1               ; Is this still early in booting?
        jne     mfdv90                      ;   no, error to call this routine

        test    mFSD_FLAG,01h               ; Is the DoVolIO flag set?
        jnz     mfdv90                      ;   yes, go return error

        SaveReg <ds>
        mov     bx,cSec.hiword
        mov     si,cSec.loword              ; bx:si -> number of sectors
        CALLFAR FuWordNF                    ; (ax) = number of sectors
        RestoreReg      <ds>
        jc      mfdvx                       ; error, go return

        mov     cx,ax                       ; (cx) = number of sectors to read
        mul     BytesPerSector              ; (dx:ax) = bytes to read
        cmp     dx,0                        ; Is the buffer < 64Kb?
        je      mfdv10                      ;   yes, continue

        cmp     dx,1                        ; Is the buffer < 128Kb?
        jne     mfdv80                      ;   no, go return error

        or      ax,ax                       ; Is the buffer = 64Kb (ax = 0)?
        jnz     mfdv80                      ;   no, go return error

mfdv10: mov     FileBufferLength.loword,ax
        mov     FileBufferLength.hiword,dx  ; save the size of the buffer

        SaveReg <ecx,ds>
        mov     bx,databuf.hiword
        mov     si,databuf.loword           ; bx:si -> buffer to read
        mov     cx,ax                       ; (cx) = buffer size
        mov     dh,ACCTYPE_READ             ; this is a read
        CALLFAR ProbeBuff                   ; Is the buffer OK?
        RestoreReg  <ds,ecx>                ; (cx) = number of sectors to read
        jc      mfdvx                       ;   no, return error to caller

        assume  ds:nothing

        lds     si,databuf                  ; (ds:si) = virtual address of buf
        CALLFAR VirtToPhys                  ; (ax:bx) = physical address of buf
        DosContext      ds
        jc      mfdv80                      ; error, go return it

        mov     FileBufferAddress.hiword,ax ; save physical address of buffer
        mov     FileBufferAddress.loword,bx
        add     bx,FileBufferLength.loword
        adc     ax,FileBufferLength.hiword  ; (ax:bx) = physical end of buffer
        cmp     ax,10h                      ; Is end of buffer < 1Meg?
        jb      mfdv20                      ; yes, continue

        cmp     ax,10h                      ; Could it be exactly 1Meg?
        ja      mfdv80                      ;   no, go return error

        or      bx,bx                       ; Is the end at exactl 1Meg?
        jnz     mfdv80                      ;   no, go return error

mfdv20: mov     dx,iSec.hiword
        mov     ax,iSec.loword              ; (dx:ax) = first sector to read
        CALLFAR SYIReadSectors
        mov     dx,FileBufferLength.hiword
        mov     ax,FileBufferLength.loword  ; (dx:ax) = unused space in buffer
        div     BytesPerSector              ; (ax) = unused sectors in buffer
        sub     cx,ax                       ; (cx) = sectors read

        SaveReg <ds>
        mov     ax,cx                       ; (ax) = sectors read
        mov     bx,cSec.hiword
        mov     di,cSec.loword              ; (bx:di) -> count of sectors
        CALLFAR SuWordNF                    ; write out number of sectors read
        RestoreReg      <ds>
        jc      mfdvx                       ; error, go return it

        xor     ax,ax                       ; (ax) = 0, no error

mfdvx:  pop     FileBufferLength.loword     ; restore buffer length
        pop     FileBufferLength.hiword
        pop     FileBufferAddress.loword    ; restore buffer address
        pop     FileBufferAddress.hiword

        xor     bx,bx
        mov     es,bx
        RestoreReg      <di,si,dx,cx,bx,ds>
        LeaveProc
        ret     ?aframe                     ; clean args off stack

mfdv80: mov     ax,ERROR_PROTECTION_VIOLATION
        jmp     short mfdvx                 ; clean up an exit

mfdv90: mov     ax,ERROR_INVALID_FUNCTION
        jmp     short mfdvx                 ; clean up an exit

EndProc         MFSH_DOVOLIO

;***    MFSH_INTERR - Signal an internal error
;
;       This routine stops the system and prints the passed error message
;       when an internal error is detected.
;       Maximum message length is 128 characters.  Messages longer than
;       this are truncated.  DOES NOT RETURN.
;
;       ENTRY   push@   message text            (2 words)
;               push    word string length      (1 word)
;               call    MFSH_INTERR
;
;       EXIT    none
;
;       USES    doesn't matter
;
;       CALLS   FSH_INTERR
;
;       PSEUDOCODE
;               set up args for FS_INTERR
;               call FS_INTERR
;
FSHentry MFSH_INTERR, ORD_MFSH_INTERR
assume ds:nothing, es:nothing, ss:nothing
ArgVar  cbMsg,WORD
ArgVar  Msg,DWORD

        EnterProc
        push    Msg.hiword
        push    Msg.loword
        push    cbMsg
        CALL FAR PTR FSH_INTERR
EndProc MFSH_INTERR,NoCheck


;***    MFSH_SEGALLOC- Allocate a segment of memory
;
;       The segment allocated will have the following attributes:  GDT,
;       dpl 0, not shared, read/write, movable, non-swappable, not ini-
;       tialized
;
;       ENTRY   push    Flag
;               push    cbSeg
;               push@   Sel
;               call    far ptm MFSH_SegAlloc
;
;       WHERE
;               Flag    Word, 1 of memory must be below 1mb, 0 otherwise
;               cbSeg   Dword length of the segment to be allocated
;               Sel     Word, filled in with the selector of the allocated memory
;
;       EXIT    Sel contains the selector to the allocated memory
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_INVALID_PARAMETER
;                       ERROR_NOT_ENOUGH_MEMORY
;
;       USES    AX,ES
;
;       NOTES:  If low memory is not specified, high memory is allocated.
;               This is because low memory is often scarce when booting
;               with a mFSD...
;
;       PSEUDOCODE
;
;       Save registers
;       Set parameters for SegAlloc
;       If LowMemFlag = 0 or LowMemFlag = 1 then
;         If LowMemFlag = 1 then
;           set lowmem flag
;         Else
;           set himem flag (to conserve low memory)
;         If LowMemFlag
;         If 0 < cbSeg <= 64kb then
;           Call far SegAlloc
;           If no error then
;             store selector
;             If no error storing selector then
;               clear ax
;             Else
;               free selector
;         Else
;           Set ax = invalid parameter
;       Else
;         Set ax = invalid parameter
;       Restore registers
;       Return
;
FSHentry MFSH_SEGALLOC, ORD_MFSH_SEGALLOC
assume ds:nothing, es:nothing, ss:nothing
ArgVar  Sel_Addr,DWORD
ArgVar  cbSeg,DWORD
ArgVar  LowMemFlag,WORD
LocalVar Sel,WORD

        EnterProc
        SaveReg <ds,bx,cx,dx,si,di>
        mov     ax,cbSeg.hiword
        mov     bx,cbSeg.loword
        mov     cl,MFSH_SALLOCSFLAGS
        mov     ch,MFSH_SALLOCAFLAGS
        mov     si,MFSH_SALLOCHFLAGS
        xor     dx,dx                   ;set ptda handle
        mov     di,FSHOWNER             ;set segment owner
        cmp     LowMemFlag,0
        je      mfsa_10
        cmp     LowMemFlag,1
        je      mfsa_05
        jmp     short mfsa_40
mfsa_05:                                ; low memory requested
        or      si,HF_LOWMEM
        jmp     short mfsa_11
mfsa_10:                                ; "don't care," so give high memory
        or      si,HF_HIMEM             ; to conserve precious low memory
mfsa_11:
        cmp     ax,0                    ;if allocate 1-64kb
        jne     mfsa_15
        cmp     bx,0
        jne     mfsa_20                 ;good size, continue
        jmp     mfsa_40                 ;0 size, error
mfsa_15:
        cmp     ax,1
        jne     mfsa_40                 ;>64kb size, error
        cmp     bx,0
        jne     mfsa_40                 ;>64kb size, error
mfsa_20:
        CALLFAR SegAlloc
        jc      mfsa_50                 ; Error from SegAlloc
;       mov     es,Sel_Addr.hiword
;       mov     si,Sel_Addr.loword
;       mov     es:[si],bx
        mov     Sel,bx                  ;save selector
        mov     ax,bx
        mov     bx,Sel_Addr.hiword
        mov     di,Sel_Addr.loword
        CALLFAR SuWordNF
        jc      mfsa_30

        xor     ax,ax
        jmp short mfsa_50
mfsa_30:
        SaveReg <ax>                    ; pass through ax = error code
        mov     ax,Sel                  ; free selector
        mov     cx,1                    ;indicate ifs segfree
        CALLFAR ifs_SegFree
        RestoreReg <ax>
        jmp short mfsa_50
mfsa_40:                                ;else (greater than 64kb requested)
        mov     ax,ERROR_INVALID_PARAMETER
mfsa_50:
        xor     bx,bx
        mov     es,bx
        RestoreReg      <di,si,dx,cx,bx,ds>
        LeaveProc
        ret     ?aframe                 ; clean args off stack

EndProc         MFSH_SEGALLOC


;***    MFSH_SEGFREE- Free a segment allocated with MFSH_SEGALLOC or which is
;       part of the mini-FSD image.
;
;       ENTRY   push    Sel
;               call    far ptm MFSH_SegFree
;
;       WHERE
;               Sel     Word, selector to be freed.
;
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_PROTECTION_VIOLATION
;
;       USES    AX,ES
;
;       PSEUDOCODE
;
;       Save registers
;       Set parameters for ifs_SegFree
;       Call far ifs_SegFree
;       If no error then
;         clear ax
;       Else
;         Set ax = error_protection_violation
;       Restore registers
;       Return
;
FSHentry MFSH_SEGFREE, ORD_MFSH_SEGFREE
assume ds:nothing, es:nothing, ss:nothing
ArgVar  Sel,WORD

        EnterProc
        SaveReg <ds,ecx,edx>
        movzx   eax,Sel
        FlatContext <ds, es>, cx
        CCALL32 _SELIfsFree  <eax>
        or      eax, eax
        jz      short mfsf_20


        mov     ax,ERROR_PROTECTION_VIOLATION   ; anticipate error
mfsf_20:
        xor     cx,cx
        mov     es,cx
        RestoreReg      <edx,ecx,ds>
        LeaveProc
        ret     ?aframe                 ; clean args off stack

EndProc         MFSH_SEGFREE


;***    MFSH_SEGREALLOC- Change the size of a memory segment allocated by
;       MFSH_SegAlloc or which is part of the mini-FSD.
;
;       ENTRY   push    Sel
;               push    cbSeg
;               call    far ptm MFSH_SegRealloc
;
;       WHERE
;               Sel     Word, selector to the segment to be resized.
;               cbSeg   Dword length to resize the segment to.
;
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_INVALID_PARAMETER
;                       ERROR_NOT_ENOUGH_MEMORY
;                       ERROR_PROTECTION_VIOLATION
;
;       USES    AX,BX,ES
;
;       PSEUDOCODE
;
;       Save registers
;       If 0 < cbSeg <= 64kb then
;         Set up parameters for _SELIfsRealloc
;         Call far _SELIfsRealloc
;         If no error then
;           clear ax
;         Else
;           If ax != error_not_enough_memory
;             Set ax = error_protection_violation
;       Else
;         Set ax = invalid parameter
;       Restore registers
;       Return
;
FSHentry MFSH_SEGREALLOC, ORD_MFSH_SEGREALLOC
assume ds:nothing, es:nothing, ss:nothing
ArgVar  mfsr_NewLength,DWORD
ArgVar  mfsr_Selector,WORD

        EnterProc
        SaveReg <ds,ecx,edx>

        FlatContext <ds, es>, ax
        movzx   eax, [mfsr_Selector]
        mov     ecx, [mfsr_NewLength]
        or      ecx, ecx                    ; Make sure 0 < size <= 64Kb
        jz      short mfsr20                ; 0 size, go error

        cmp     ecx, SEL_64K
        ja      short mfsr20                ; size > 64Kb, go error

        CCALL32 _SELIfsRealloc <eax, ecx>
        or      eax, eax
        jz      short mfsr10                ; jmp if error

        cmp     ax, ERROR_NOT_ENOUGH_MEMORY
        jz      short mfsr10

        mov     eax, ERROR_PROTECTION_VIOLATION

mfsr10: RestoreReg      <edx,ecx,ds>
        LeaveProc
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
        ret     ?aframe                 ; clean args off stack

mfsr20: mov     ax,ERROR_INVALID_PARAMETER
        jmp     short mfsr10            ; go ret

EndProc         MFSH_SEGREALLOC





;***    MFSH_SETBOOTDRIVE - Allows the minifsd to change the boot drive number
;                           maintained by the kernel. (as per DCR510)
;
;       ENTRY   push    Drive
;               call    far ptr MFSH_SETBOOTDRIVE
;
;       WHERE
;               Drive   WORD which is the ZERO-BASED new boot drive number
;                       (2=C, 3=D,...,etc)
;
;       EXIT    Ax = 0 if successful
;               Ax = ERROR_INVALID_PARAMETER if a bad drive number was specified
;
;       USES    dx, ds, bx, es, flags
;
;       REMARKS - NUMDRIVE is defined in curdir.h
;
;       WARNING: This should only be called by mfs_init.  If called
;                later, the shared copy (_SIS2) of the system infoseg
;                may not get updated and cause the next infoseg comp-
;                arison check done by _PGVerifyInfoSegs to fail --
;                stopping the system.
;
;       PSEUDOCODE
;
;       Save dx, ds
;       Get the specified drive number
;       Increment the zero-based (as per DCR510) drive number to 1-based
;       If (drive number >= C drive)
;       AND if (drive number <= maximum allowed drive number)
;               save boot drive number in the global infoseg in system arena
;               set ax return code = 0
;       else
;               set ax = bad parameter
;       restore ds, dx
;       clear stack
;       Return
;

C_DRIVE EQU     3

FSHentry MFSH_SETBOOTDRIVE, ORD_MFSH_SETBOOTDRIVE
assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  DriveNum,WORD

        EnterProc
        SaveReg <dx,ds>                 ;borrow some registers

        mov     dx,DriveNum             ;get the specified drive number
        inc     dx                      ;change drive number to one-based

        cmp     dx,C_DRIVE              ;valid drive? (C: drive or greater?)
        jl      mfsb_10
        cmp     dx,NUMDRIVE             ;valid drive? (NUMDRIVE: drive or less?)
        jg      mfsb_10

        DosContext ds
        mov     SISData.SIS_BootDrv,dx  ;change boot drive # in system infoseg
        xor     ax,ax                   ;set successful return code = 0
        jmp     mfsb_20

mfsb_10:
        mov     ax,ERROR_INVALID_PARAMETER      ;set bad return code

mfsb_20:
        RestoreReg      <ds,dx>         ;restore the borrowed registers
        LeaveProc

ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc         MFSH_SETBOOTDRIVE




;***    MFSH_LOCK- Lock a segment in place in physical memory.
;
;       ENTRY   push    Sel
;               push@   Handle
;               call    far ptr MFSH_Lock
;
;       WHERE
;               Sel     Word, selector to the segment to be locked.
;               Handle  Dword to be filled in with the lock handle.
;
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_PROTECTION_VIOLATION
;
;       USES    AX, ES, FLAGS
;
;       PSEUDOCODE
;
;       Save registers
;       Set parameters for SegLock
;       Call far SegLock
;       If no error then
;         Store lock handle
;         If no error storing handle then
;           Clear ax
;         Else
;           Unlock Segment
;       Restore registers
;       Return
;
FSHentry MFSH_LOCK, ORD_MFSH_LOCK
assume ds:nothing, es:nothing, ss:nothing
ArgVar  Handle_Addr,DWORD
ArgVar  Sel,WORD
LocalVar Handle,DWORD
        EnterProc
        SaveReg <ds,bx,cx,dx,si,di>
        mov     ax,Sel
        xor     bh,bh                   ;short term lock, kernel call
        mov     bl,MW_BVALID            ;block until valid and not busy
        CALLFAR SegLock
        jc      mfl_20                  ;pass through ax=error code on error
;       les     di,Handle_Addr          ;store handle
;       mov     es:[di].hiword,ax
;       mov     es:[di].loword,bx

        mov     Handle.hiword,ax        ;save handle for later
        mov     Handle.loword,bx
        mov     bx,Handle_Addr.hiword   ;store high word of handle
        mov     di,Handle_Addr.loword
        add     di,2                    ;address high word
        CALLFAR SuWordNF
        jc      mfl_10
        mov     ax,Handle.loword        ;get low word of handle
        mov     bx,Handle_Addr.hiword   ;store low word of handle
        mov     di,Handle_Addr.loword
        CALLFAR SuWordNF
        jc      mfl_10

        xor     ax,ax
        jmp     mfl_20
mfl_10:
        SaveReg <ax>                    ;pass through ax = error code
        mov     ax,Handle.hiword
        mov     bx,Handle.loword
        CALLFAR SegUnlock
        RestoreReg <ax>
mfl_20:
        xor     bx,bx
        mov     es,bx
        RestoreReg      <di,si,dx,cx,bx,ds>
        LeaveProc
        ret     ?aframe                 ;clean args off of the stack
EndProc         MFSH_LOCK


;***    MFSH_PHYSTOVIRT- Allocate a selector and bind a physical address
;       and length to it.
;
;       ENTRY   push    Addr
;               push    Len
;               push@   Sel
;               call    far ptr MFSH_Phystovirt
;
;       WHERE
;               Sel     Dword physical address.
;               Len     Word length of the segment.
;               Sel     Word to be filled in by the helper with the
;                       selector.
;
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_INVALID PARAMETER
;
;       USES    AX, ES, FLAGS
;
;       PSEUDOCODE
;
;       Save registers
;       Set parameters for AllocateGDTSelector
;       Call far AllocateGDTSelector
;       If no error then
;         Set parameters for PhysToGDTSelector
;         Call far PhysToGDTSelector
;         If no error then
;           Store selector
;           If no error storing selector then
;             Clear ax
;           Else
;             Deallocate segment
;         Else
;           Deallocate segment
;       Restore registers
;       Return
;
FSHentry MFSH_PHYSTOVIRT, ORD_MFSH_PHYSTOVIRT
assume ds:nothing, es:nothing, ss:nothing
ArgVar  Sel_Addr,DWORD
ArgVar  Len,WORD
ArgVar  Addr,DWORD
LocalVar        Temp_Sel,WORD
        EnterProc
        SaveReg <ds,bx,cx,si,di>

        mov     ax,ss
        mov     es,ax
        lea     di,Temp_Sel
        mov     cx,1                    ;allocate 1 selector
        CALLFAR AllocateGDTSelector
        jc      mfpv_20
        mov     ax,Addr.hiword
        mov     bx,Addr.loword
        mov     cx,Len                  ;allocate length, 64kb if 0
;       mov     si,es:[di]              ;load selector

        mov     si,Temp_Sel

        CALLFAR PhysToGDTSelector
        jc      mfpv_10

        mov     ax,Temp_Sel
        mov     bx,Sel_Addr.hiword
        mov     di,Sel_Addr.loword
        CALLFAR SuWordNF
        jc      mfpv_10
        xor     ax,ax
        jmp     mfpv_20
mfpv_10:
        SaveReg <ax>
        mov     ax,Temp_Sel
        CALLFAR FreeGDTSelector
        RestoreReg <ax>
mfpv_20:
        xor     bx,bx
        mov     es,bx
        RestoreReg      <di,si,cx,bx,ds>
        LeaveProc
        ret     ?aframe                 ;clean args off of the stack
EndProc         MFSH_PHYSTOVIRT


;***    MFSH_UNLOCK- Unlock a segment in place in physical memory.
;
;       ENTRY   push    Handle
;               call    far ptr MFSH_Unlock
;
;       WHERE
;               Handle  Dword which contains the lock handle returned from
;                       MFS_Lock.
;
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_PROTECTION_VIOLATION
;
;       USES    AX, ES, FLAGS
;
;       PSEUDOCODE
;
;       Save registers
;       Set parameter for SegUnlock
;       Call far SegUnLock
;       If no error then
;         Clear ax
;       Restore registers
;       Return
;
FSHentry MFSH_UNLOCK, ORD_MFSH_UNLOCK
assume ds:nothing, es:nothing, ss:nothing
ArgVar  Handle,DWORD
        EnterProc
        SaveReg <ds,bx,cx,dx,si,di>
        mov     ax,Handle.hiword
        mov     bx,Handle.loword
        CALLFAR SegUnlock
        jc      mful_10                 ;pass through ax=error code on error
        xor     ax,ax
mful_10:
        xor     bx,bx
        mov     es,bx
        RestoreReg      <di,si,dx,cx,bx,ds>
        LeaveProc
        ret     ?aframe                 ;clean args off of the stack
EndProc         MFSH_UNLOCK


;***    MFSH_UNPHYSTOVIRT- Release the selector allocated by
;       MFSH_Phystovirt.
;
;       ENTRY   push    Sel
;               call    far ptr MFSH_UnPhystoVirt
;
;       WHERE
;               Sel     Word selector to be freed
;
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_PROTECTION_VIOLATION
;
;       USES    AX, ES, FLAGS
;
;       PSEUDOCODE
;
;       Save registers
;       Set parameter for FreeGDTSelector
;       Call far FreeGDTSelector                ;has no error exit
;       Clear ax
;       Restore registers
;       Return
;
FSHentry MFSH_UNPHYSTOVIRT, ORD_MFSH_UNPHYSTOVIRT
assume ds:nothing, es:nothing, ss:nothing
ArgVar  Sel,WORD
        EnterProc
        mov     ax,Sel
        CALLFAR FreeGDTSelector
        xor     ax,ax
        mov     es,ax
        LeaveProc
        ret     ?aframe                 ;clean args off of the stack
EndProc         MFSH_UNPHYSTOVIRT

;***    MFSH_VIRT2PHYS- Translate a virtual address to a physical address.
;
;       ENTRY   push    VirtAddr
;               push@   PhysAddr
;               call    far ptr MFSH_Virt2Phys
;
;       WHERE
;               VirtAddr Dword virtual address to be translated.
;               PhysAddr Dword to be filled in with the physical address.
;
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_INVALID_PARAMETER
;
;       USES    AX, ES, FLAGS
;
;       PSEUDOCODE
;
;       Save registers
;       Set parameters for VirttoPhys
;       Call far VirttoPhys
;       If no error then
;         Store physical address
;         If no error storing physical address then
;           Clear ax
;       Restore registers
;       Return
;
FSHentry MFSH_VIRT2PHYS, ORD_MFSH_VIRT2PHYS
assume ds:nothing, es:nothing, ss:nothing
ArgVar  PhysAddr_Addr,DWORD
ArgVar  VirtAddr,DWORD
        EnterProc
        SaveReg <ds,bx,cx,dx,si,di>
        mov     ds,VirtAddr.hiword
        mov     si,VirtAddr.loword
        CALLFAR VirtToPhys
        jc      mfvp90                  ;pass through ax=error code on error

        mov     dx,bx                   ;save low word of physical address for later
        mov     bx,PhysAddr_Addr.hiword   ;store high word of physical address
        mov     di,PhysAddr_Addr.loword
        add     di,2                      ;address high word
        CALLFAR SuWordNF
        jc      mfvp10

        mov     ax,dx                   ;get low word of physical address
        mov     bx,PhysAddr_Addr.hiword   ;store low word of physical address
        mov     di,PhysAddr_Addr.loword
        CALLFAR SuWordNF
        jc      mfvp10

        xor     ax,ax                   ; (ax) = 0, success

mfvp10: xor     bx,bx
        mov     es,bx                   ; (es) = 0 to get bad minifsds
        RestoreReg      <di,si,dx,cx,bx,ds>
        LeaveProc
        ret     ?aframe                 ;clean args off of the stack

mfvp90: mov     ax,ERROR_PROTECTION_VIOLATION
        jmp     short mfvp10

EndProc         MFSH_VIRT2PHYS

INITCODE ENDS
; Defect 72417 changes start
        FARCODE HIGH3
HIGH3CODE SEGMENT
; Defect 72417 changes end

;***    FSH_SEGALLOC - allocate a GDT or LDT segment for an IFS
;
;       flags:     0002  swappable
;                  0000  GDT
;                  0001  LDT
;                  0000  ring0
;                  2000  ring1
;                  4000  ring2
;                  6000  ring3
;
;       ENTRY   push    word flags              (1 word)
;               push    dword segment length    (2 word)
;               push@   word selector           (2 word)
;               call    FSH_SEGALLOC
;
;       EXIT    (ax) = return code
;                       0 - success
;
;       USES    AX, BX, CX, DX, ES, SP, FLAGS
;
;       CALLS   SegAlloc
;
;       PSEUDOCODE
;               if attempt to allocate Ring 1 segment
;                   error_invalid_parameter
;               call _SELIfsAlloc
;
FSHentry FSH_SEGALLOC, ORD_FSH_SEGALLOC
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fsa_Selector,DWORD
ArgVar  fsa_SegLength,DWORD
ArgVar  fsa_Flags,WORD

LocalVar fsa_sel,WORD

        EnterProc
        SaveReg <di,ds>
        test    fsa_Flags,not (SEL_IFSFLAGS)  ; Test for acceptable flags      ; PTR B718432
                                        ; any reserved bits set?
        jnz     fsa20                   ; yes, go return error

;
; If the attempt is to allocate a Ring 1 selector, we do not allow it.
;
        mov     cx,fsa_Flags
        and     ch,D_PRIV               ; CH = Priviledge level
        cmp     ch,D_DPL1
        jz      fsa20                   ; Ring 1. Return error.

; make sure 0 < size <= 65536
; NOTE: omit this test for 386

        mov     eax, [fsa_SegLength]
        or      eax, eax                ; request 0 size?
        jz      fsa20                   ; 0 size, go error

        cmp     eax, SEL_64K            ; > 64Kb
        ja      short fsa20             ; > 64k return error

;   _VMGetOwner(seg, off)

        FlatContext <ds, es>, cx
        movzx   eax, word ptr ss:[bp+2] ; (eax) = eip of caller
        movzx   ebx, word ptr ss:[bp+4] ; (ebx) = cs of caller
        CCALL32 _VMGetOwner <ebx, eax>

;   _selIfsAlloc(fl, cb, &sel, owner)

fsa5:   SSToDS  edx, [fsa_sel]
        movzx   ebx, [fsa_Flags]
        CCALL32 _SELIfsAlloc <ebx, [fsa_SegLength], edx, eax>

        or      ax, ax
        jnz     short fsa10             ; AX = error code. Go return it.

        les     di,fsa_Selector
        mov     bx, [fsa_sel]
        mov     es:[di],bx              ; return selector
fsa10:  RestoreReg <ds,di>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

fsa20:  mov     ax,ERROR_INVALID_PARAMETER
        jmp     short fsa10             ; go ret

EndProc FSH_SEGALLOC



;***    FSH_SEGFREE - Release a GDT or LDT segment
;
;       ENTRY   push    word selector           (1 word)
;               call    FSH_SEGFREE
;
;       EXIT    (ax) = ERROR_INVALID_ACCESS
;                       0 - success
;
;       USES    AX, BX, CX, DX, ES, SP, Flags
;
;       CALLS   _SELIfsFree
;
FSHentry FSH_SEGFREE,ORD_FSH_SEGFREE
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fsf_Selector,WORD

        EnterProc
        SaveReg <ds>

;   _SELIfsFree(sel)

        FlatContext <ds, es>, ax
        movzx   eax, [fsf_Selector]
        CCALL32 _SELIfsFree <eax>

        or      ax, ax
        jz      short fsf10             ; jmp if success
        mov     ax,ERROR_INVALID_ACCESS ; Anticipate error
fsf10:  RestoreReg <ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_SEGFREE



;***    FSH_SEGREALLOC - change segment size
;
;       ENTRY   push    word selector           (1 word)
;               push    dword new length        (2 word)
;               call    FSH_SEGREALLOC
;
;       EXIT    (ax) = return code
;                       0 - success
;
;       USES    AX, BX, CX, DX, ES, SP, Flags
;
;       CALLS   _SELIfsRealloc
;
;       PSEUDOCODE
;                   call _SELIfsRealloc
;                   if (error)
;                       translate error to DOS error
;
FSHentry FSH_SEGREALLOC,ORD_FSH_SEGREALLOC
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fsr_NewLength,DWORD
ArgVar  fsr_Selector,WORD

        EnterProc
        SaveReg <ds>

; make sure 0 < newsize <= 64k
; NOTE: omit next few instructions for 386

        mov     ecx, [fsr_NewLength]
        or      ecx, ecx
        jz      short fsr20             ; 0 size, go error

        cmp     ecx,SEL_64K             ; for 286, should be <= 64k
        ja      short fsr20             ; > 64K, return error

;   _SELIfsRealloc(sel, cb)

        FlatContext <ds,es>,bx
        movzx   eax, [fsr_Selector]
        CCALL32 _SELIfsRealloc,<eax, [fsr_NewLength]>


fsr10:  RestoreReg <ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

fsr20:  mov     ax,ERROR_INVALID_PARAMETER
        jmp     short fsr10             ; go ret

EndProc FSH_SEGREALLOC


;***    FSH_FORCENOSWAP - force segment permanently into memory
;
;       An FSD may call FSH_FORCENOSWAP to force a segment
;       to be loaded into memory and marked non-swappable.
;       Once FSH_FORCENOSWAP is called, the affected segment
;       will always be present in memory.  There is no way
;       to undo the effect of FSH_FORCENOSWAP.  If an FSD
;       is notified of being the swapper file system, it
;       should make this call for the necessary segments.
;       Applying FORCENOSWAP to a non-swappable segment has no effect.
;
;
;       ENTRY   push    word selector   (1 word)
;               call    FSH_FORCENOSWAP
;
;       EXIT    (ax) = return code
;                       0 - success
;
;       USES    AX, BX, CX, DX, ES, SP, Flags
;
;       CALLS   SegSetSwapState
;
;       PSEUDOCODE
;               if(selector is GDT) and (memory is on heap)
;                   _SELMoveKHB(swappable heap,resident heap,sel,flags)
;               set up args to SegSetSwapState
;               call SegSetSwapState
;
;       NOTE:   changing functionality to FSH_SETSWAPSTATE
;               is trivial; pass swappable flag in cx
;

FSHentry FSH_FORCENOSWAP,ORD_FSH_FORCENOSWAP
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  ffns_Selector,WORD

        EnterProc
        SaveReg <ds>

;   _SELIfsNoSwap(sel)

        FlatContext <ds, es>, ax
        movzx   eax, [ffns_Selector]
        CCALL32 _SELIfsNoSwap <eax>

        RestoreReg <ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_FORCENOSWAP


;***    FSH_SEMSET - Set a semaphore
;
;       FSH_SEMSET allows an FSD to set a semaphore unconditionally.
;
;       ENTRY   push    dword   pSem            (2 word)
;               call    FSH_SEMSET
;
;       EXIT    Error code if invalid semaphore, 0 otherwise.
;
;               ERROR_EXCL_SEM_ALREADY_OWNED - the exclusive semaphore
;               is already owned.
;
;               ERROR_TOO_MANY_SEM_REQUESTS - there are too many
;               semaphore requests in progress
;
;               ERROR_PROTECTION_VIOLATION - the semaphore is inaccessible
;
;       USES    AX, BX, flags
;
;       PSEUDOCODE
;          SemSet
;
FSHentry FSH_SEMSET,ORD_FSH_SEMSET
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fss_pSem,DWORD          ; handle to system semaphore or address of ram
                                ; semaphore
        EnterProc
        SaveReg <ds,si>
        mov     ax,fss_pSem.Segmt
        mov     bx,fss_pSem.Offst       ; (AX:BX) = semaphore handle
        CALLFAR SemSet                  ; set semaphore
        jc      fss1                    ; error, go return
        xor     ax,ax                   ; return no error
fss1:   RestoreReg <si,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_SEMSET

;***    FSH_SEMSETWAIT - Set a semaphore and wait for clear
;
;       FSH_SEMSETWAIT allows an FSD to wait for an event.  The event
;       is signalled by a call to FSH_SEMCLEAR.
;
;       ENTRY   push    dword   pSem            (2 word)
;               push    dword   cmsTimeout      (2 word)
;               call    FSH_SEMSETWAIT
;
;       EXIT    Error code if timeout occurred or invalid semaphore,
;               0 otherwise.
;
;               ERROR_SEM_TIMEOUT - the timeout expired without
;               gaining access to the semaphore.
;
;               ERROR_EXCL_SEM_ALREADY_OWNED - the exclusive semaphore
;               is already owned.
;
;               ERROR_INTERRUPT - the current thread received a signal
;
;               ERROR_PROTECTION_VIOLATION - the semaphore is inaccessible
;
;       USES    AX, BX, CX, DX, ES, flags
;
;       PSEUDOCODE
;          SemSetWait

FSHentry FSH_SEMSETWAIT,ORD_FSH_SEMSETWAIT
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fssw_cmsTimeout,DWORD   ; number of milliseconds to wait
ArgVar  fssw_pSem,DWORD         ; handle to system semaphore or address of ram
                                ; semaphore
        EnterProc
        SaveReg <ds,di,si>
        mov     di,fssw_cmsTimeout.hiword
        mov     cx,fssw_cmsTimeout.loword ; (DI:CX) = timeout
        mov     ax,fssw_pSem.Segmt
        mov     bx,fssw_pSem.Offst      ; (AX:BX) = semaphore handle
        CALLFAR SemSetWait              ; set semaphore and wait for clear
        jc      fssw1                   ; error, go return
        xor     ax,ax                   ; return no error
fssw1:  RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_SEMSETWAIT

;***    FSH_SEMWAIT - Wait for clear
;
;       FSH_SEMWAIT allows an FSD to wait for an event.  The event
;       is signalled by a call to FSH_SEMCLEAR.
;
;       ENTRY   push    dword   pSem            (2 word)
;               push    dword   cmsTimeout      (2 word)
;               call    FSH_SEMWAIT
;
;       EXIT    Error code if timeout occurred or invalid semaphore,
;               0 otherwise.
;
;               ERROR_SEM_TIMEOUT - the timeout expired without
;               gaining access to the semaphore.
;
;               ERROR_INTERRUPT - the current thread received a signal
;
;               ERROR_PROTECTION_VIOLATION - the semaphore is inaccessible
;
;       USES    AX, BX, CX, DX, ES, flags
;
;       PSEUDOCODE
;          SemWait
;
FSHentry FSH_SEMWAIT,ORD_FSH_SEMWAIT
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fsw_cmsTimeout,DWORD    ; number of milliseconds to wait
ArgVar  fsw_pSem,DWORD          ; handle to system semaphore or address of ram
                                ; semaphore
        EnterProc
        SaveReg <ds,di,si>
        mov     di,fsw_cmsTimeout.hiword
        mov     cx,fsw_cmsTimeout.loword ; (DI:CX) = timeout
        mov     ax,fsw_pSem.Segmt
        mov     bx,fsw_pSem.Offst       ; (AX:BX) = semaphore handle
        CALLFAR SemWait                 ; wait for semaphore to clear
        jc      fsw1                    ; error, go return
        xor     ax,ax                   ; return no error
fsw1:   RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_SEMWAIT

; Defect 72417 changes start
HIGH3CODE ENDS
; Defect 72417 changes end

        FARCODE LOW
CODE    SEGMENT

;***    FSH_INTERR - Signal an internal error
;
;       This routine stops the system and prints the passed error message
;       when an internal error is detected.
;       DOES NOT RETURN.
;
;       ENTRY   push@   message text            (2 words)
;               push    word string length      (1 word)
;               call    FSH_INTERR
;
;       EXIT    none
;
;       USES    doesn't matter
;
;       CALLS   _InternalError
;
;       PSEUDOCODE
;               set up args for _InternalError
;               call _InternalError

FSHentry FSH_INTERR,ORD_FSH_INTERR
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fi_MsgLen,WORD
ArgVar  fi_Message,DWORD

        EnterProc
        mov     cx,[fi_MsgLen]          ; (CX) == message length
        lds     si,[fi_Message]         ; (DS:SI) -> message
        CALLFAR _InternalError          ; print message
        LeaveProc                       ; allows debugger tracing
        ret     ?aframe                 ; allows debugger tracing
EndProc FSH_INTERR


;***    FSH_SEMREQUEST - request a system or RAM semaphore
;
;       ENTRY   push    dword semaphore handle or address  (2 word)
;               push    dword timeout           (2 word)
;               call    FSH_SEMREQUEST
;
;       EXIT    (ax) = error code
;                       0 - success
;
;       USES    AX, BX, CX, DX, ES, SP, Flags
;
;       CALLS   SemRequest
;
;       PSEUDOCODE
;               set up args to SemRequest
;               call SemRequest
;

FSHentry FSH_SEMREQUEST,ORD_FSH_SEMREQUEST
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fsr_TimeOut,DWORD
ArgVar  fsr_SemHandle,DWORD

        EnterProc
        SaveReg <ds,di,si>
        mov     ax,fsr_SemHandle.Segmt  ; (AX:BX) = address of semaphore
        mov     bx,fsr_SemHandle.Offst
        mov     di,fsr_TimeOut.HighWord ; (DI:CX) = time to wait for sem
        mov     cx,fsr_TimeOut.LowWord
        call    SemRequest              ; (AX) == error code
        jc      fsr1                    ; error, go return
        xor     ax,ax                   ; return no error
fsr1:   RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack
EndProc FSH_SEMREQUEST



;***    FSH_SEMCLEAR - clear a system or RAM semaphore
;
;       ENTRY   push    dword semaphore handle or address  (2 word)
;               call    FSH_SEMCLEAR
;
;       EXIT    (ax) = return code
;                       0 - success
;
;       USES    AX, BX, CX, DX, ES, SP, Flags
;
;       CALLS   SemClear
;
;       PSEUDOCODE
;               set up args to SemClear
;               call SemClear
;

FSHentry FSH_SEMCLEAR,ORD_FSH_SEMCLEAR
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fsc_SemHandle,DWORD

        EnterProc
        SaveReg <ds,di,si>
        mov     ax,fsc_SemHandle.Segmt  ; (AX:BX) = address of semaphore
        mov     bx,fsc_SemHandle.Offst
        call    SemClear                ; (AX) == error code
        jc      fsc1                    ; error, go return
        xor     ax,ax                   ; return no error
fsc1:   RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_SEMCLEAR

;***    FSH_IOSEMCLEAR - clear an IO-event system or RAM semaphore
;
;       ENTRY   push    dword semaphore handle or address  (2 word)
;               call    FSH_IOSEMCLEAR
;
;       EXIT    (ax) = return code
;                       0 - success
;
;       USES    AX, BX, CX, DX, ES, SP, Flags
;
;       CALLS   IOSemClear
;
;       PSEUDOCODE
;               set up args to IOSemClear
;               call IOSemClear
;

FSHentry FSH_IOSEMCLEAR,ORD_FSH_IOSEMCLEAR
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fiosc_SemHandle,DWORD

        EnterProc
        SaveReg <ds,di,si>
        mov     ax,fiosc_SemHandle.Segmt  ; (AX:BX) = address of semaphore
        mov     bx,fiosc_SemHandle.Offst
        call    IOSemClear                ; (AX) == error code
        jc      fiosc1                    ; error, go return
        xor     ax,ax                     ; return no error
fiosc1: RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_IOSEMCLEAR

;***    FSH_YIELD - Yield CPU to higher priority threads
;
;       FSH_YIELD provides a mechanism for relinquishing the processor
;       to higher-priority threads.  FSDs run under the 4ms dispatch
;       imposed on the OS/2 kernel, meaning that no more than 4ms can
;       be spent in an FSD without an explicit block or yield.
;       FSH_YIELD will test to see if another thread is runnable at the
;       current thread's priority or at a higher priority.  If one exists,
;       that thread will be given a chance to run.
;
;       ENTRY   call    FSH_YIELD
;
;       EXIT    none
;
;       USES    flags
;
;       PSEUDOCODE
;          TCYield
FSHentry FSH_YIELD,ORD_FSH_YIELD
   assume ds:nothing, es:nothing, ss:TASKAREA

        call    TCYield         ; yield CPU to time critical threads
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret

EndProc FSH_YIELD

CODE    ENDS

        FARCODE HIGH2
HIGH2CODE SEGMENT

;***    FSH_PROBEBUF - User address validity check
;
;       FSH_PROBEBUF is passed a buffer and its length to test. If
;       any address in the range is invalid, the calling process is
;       marked to die.  If (operation == 0), the buffer is tested
;       for read access.  If (operation == 1), the buffer is tested
;       for write access.
;
;       ENTRY   push    word operation          (1 word)
;               push    dword start address     (2 word)
;               push    word length             (1 word)
;               call    FSH_PROBEBUF
;
;       EXIT    (ax) = return code
;                       0 - success
;
;       USES    AX, BX, CX, DX, ES, SP, FLAGS
;
;       CALLS   ProbeBuff
;
;       PSEUDOCODE
;               set up args to ProbeBuff
;               call ProbeBuff
;
FSHentry FSH_PROBEBUF,ORD_FSH_PROBEBUF
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fpb_TransLen,WORD
ArgVar  fpb_Address,DWORD
ArgVar  fpb_Operation,WORD              ; 0 = read, 1 = write

        EnterProc
        SaveReg <ds>
        mov     dx,fpb_Operation
        cmp     dx,2                    ; operation valid?
        jae     fpb20                   ; no, go error exit

;
; Call ProbeBuff to validate the buffer and to make it present.
;
        SaveReg <si>
        mov     dh,dl                   ; (DH) == transfer type
        mov     cx,fpb_TransLen         ; (CX) == length of transfer
        mov     bx,fpb_Address.Segmt    ;
        mov     si,fpb_Address.Offst    ; (BX:SI) == beginning of transfer buffer
        call    ProbeBuff               ; ds trashed
        RestoreReg <si>
        jc      fpb10                   ; error, preserve ax error code
        xor     ax,ax                   ; return no error

fpb10:
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        RestoreReg <ds>                 ; Restore caller's DS
        LeaveProc
        ret     ?aframe                 ; Clean args off stack and return

fpb20:  mov     ax,ERROR_INVALID_PARAMETER
        jmp     short fpb10

EndProc FSH_PROBEBUF

; Defect 72417 changes start
HIGH2CODE ENDS

        FARCODE HIGH3
HIGH3CODE SEGMENT
; Defect 72417 changes end

;***    FSH_EXTENDTIMESLICE - extend running thread's time slice
;
;       FSH_EXTENDTIMESLICE extends the running thread's execution time slice.
;       The caller has just taken a cache hit on his target data.
;
;       ENTRY   n/a
;
;               call    FSH_EXTENDTIMESLICE
;
;       EXIT    n/a
;
;       USES    DI,AX, Flags
;
;       CALLS   n/a
;
;       PSEUDOCODE
;
;
FSHentry FSH_EXTENDTIMESLICE ,ORD_FSH_EXTENDTIMESLICE
   assume ds:nothing, es:nothing, ss:TASKAREA

        EnterProc                       ;
        push    di                      ; save di
        mov     di, CurrTCB             ; get addressibility to running thread
        or      ss:[di].TCBSpareFlags,SPFLAGS_XTIME_SLICE  ; extend time slice
        pop     di                      ; restore di
        xor     ax, ax                  ; set return code
        LeaveProc                       ;
        ret                             ;

EndProc FSH_EXTENDTIMESLICE


;***    FSH_GETPRIORITY - get running thread's priority
;
;       FSH_GETPRIORITY returns the running thread's execution priority
;       class: "background vs. foreground" as defined by the schedular .
;
;       ENTRY   n/a
;
;               call    FSH_GETPRIORITY
;
;       EXIT    (ax) = foreground priority indicator
;                       ffffh = yes, assign foreground class
;                       0000h = no, assign background class
;
;       USES    AX, DI, Flags
;
;       CALLS   n/a
;
;       PSEUDOCODE
;
;
FSHentry FSH_GETPRIORITY,ORD_FSH_GETPRIORITY
   assume ds:nothing, es:nothing, ss:TASKAREA

        EnterProc                       ;
        xor     ax, ax                  ; init ax to = background indicator
        push    ds
        DosContext ds                   ; get access to global priority mode
                                        ; flag
        cmp     FSPrtyIO_Mode, 0        ; are we in priority I/O mode?
        Assume  ds:nothing
        pop     ds
        jz      fgpExit                 ; no, exit

        push    di                      ; save di
        mov     di, CurrTCB             ; get addressibility to running thread
        test    ss:[di].TCBSpareFlags,SPFLAGS_FGND_DISKIO  ; foreground?
        pop     di                      ; restore di
        jz      fgpExit                 ; no
        mov     ax, 0ffffh              ; yes, set foreground indicator
fgpExit:
        LeaveProc                       ;
        ret                             ;

EndProc FSH_GETPRIORITY

;***    FSH_IOBOOST - give the running thread an IO-boost
;
;       FSH_IOBOOST gives the running thread an IO-boost.
;
;       ENTRY   n/a
;
;               call    FSH_IOBOOST
;
;       EXIT    n/a
;
;
;       USES    AX, DI, Flags
;
;       CALLS   n/a
;
;       PSEUDOCODE
;
;
FSHentry FSH_IOBOOST,ORD_FSH_IOBOOST
   assume ds:nothing, es:nothing, ss:TASKAREA

        EnterProc                       ;
        SaveReg <cx,ds,es>
        FlatContext <ds,es>,cx
        mov      ecx, [_pTCBCur]
        or      [ecx].TCBPriClassMod, CLASSMOD_DEVICE ; set IO-boost
        CCALL32 _TKUpdatePriority, <ecx> ; update thread's priority
        RestoreReg <es,ds,cx>
        xor     ax,ax                   ; return code = 0
        LeaveProc                       ;
        ret                             ;

EndProc FSH_IOBOOST


;***    FSH_QUERYSERVERTHREAD - query if current thread is a server thread
;
;       FSH_QUERYSERVERTHREAD returns a flag indicating whether the running
;       thread belongs to the server.
;
;       ENTRY   n/a
;
;               call    FSH_QUERYSERVERTHREAD
;
;       EXIT    (ax) = server open indicator
;                       ffffh = yes, server thread
;                       0000h = no, not server thread
;
;       USES    AX, DI, Flags
;
;       CALLS   n/a
;
;       PSEUDOCODE
;
;
FSHentry FSH_QUERYSERVERTHREAD,ORD_FSH_QUERYSERVERTHREAD
   assume ds:nothing, es:nothing, ss:TASKAREA

        EnterProc                       ;
        xor     ax, ax                  ; init ax to = not server thread
        push    di                      ; save di
        mov     di, CurrTCB             ; get addressibility to running thread
        cmp     ss:[di].TCBfSharing,-1  ; server?
        pop     di                      ; restore di
        jnz     qsoExit                 ; no
        mov     ax, 0ffffh              ; yes, set server thread indicator
qsoExit:
        LeaveProc                       ;
        ret                             ;

EndProc FSH_QUERYSERVERTHREAD

;***    FSH_QUERYOPLOCK - query if the running thread has an oplock
;
;       FSH_QUERYOPLOCK returns a flag indicating whether the running
;       thread has an oplock.
;
;       ENTRY   n/a
;
;               call    FSH_QUERYOPLOCK
;
;       EXIT    (ax) = oplock status indicator
;                       ffffh = yes, thread has oplock
;                       0000h = no, thread does not have oplock
;
;       USES    AX, DI, Flags
;
;       CALLS   n/a
;
;       PSEUDOCODE
;
;
FSHentry FSH_QUERYOPLOCK,ORD_FSH_QUERYOPLOCK
   assume ds:nothing, es:nothing, ss:TASKAREA

        EnterProc                       ;
        SaveReg <cx,ds,es,edi>
        xor     ax,ax
        FlatContext <ds,es>,cx
        mov     ecx, [_pTCBCur]
        les     di,TASKAREA:[ecx].TCBThisSFT    ;
        mov     edi, es:[di].sf_MFT    ; (SS:EDI) -> MFT
        test    TASKAREA:[edi].mft_opflags, mft_oplock
        jz      qoplexit
        mov     ax, -1
qoplexit:
        RestoreReg <edi,es,ds,cx>
        LeaveProc                       ;
        ret                             ;

EndProc FSH_QUERYOPLOCK

;***    FSH_FLUSHBUF - flush buffered data to a volume
;
;       FSH_FLUSHBUF flushes all buffers for a particular volume.
;       Normally, all buffers are kept, however, if discard is set,
;       then any clean buffer, or any buffer we could successfully
;       make clean, will be discarded.
;
;       An error returned means that a write error occurred and one
;       or more buffers were not written to the volume.  Those buffers
;       will NOT be discarded, even if the discard bit is set.
;
;       ENTRY   push    word volume handle      (1 word)
;               push    word discard            (1 word)
;               call    FSH_FLUSHBUF
;
;       EXIT    (ax) = return code
;                       0 - success
;
;       USES    AX, BX, CX, DX, ES, SP, Flags
;
;       CALLS   Flush_Buf
;               BufInvalidate
;
;       PSEUDOCODE
;               set up args to FlushBufND
;               call FlushBufND         /* don't discard buf if write fails */
;               if (error)
;                  return
;               if (discard)
;                  call BufInvalidate
;

FSHentry FSH_FLUSHBUF,ORD_FSH_FLUSHBUF
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  ffb_Discard,WORD
ArgVar  ffb_hVolume,WORD

        EnterProc
        mov     ax, ffb_hVolume         ; (AX) == vpb handle
        test    ffb_Discard,NOT(FFL_CACHE_MASK)
        mov     bx,ERROR_INVALID_PARAMETER
        jnz     ffb4                    ; Invalid discard bits set. Return error.
        SaveReg <si,di,ax>
        mov     bx,fFB_FAILNODISCARD    ;
                                        ; Defect 72417 changes start
        CALLFAR FlushBufND
                                        ; Defect 72417 changes end
        RestoreReg <ax>                 ; (AX) == vpb handle
        mov     bx,ERROR_FLUSHBUF_FAILED ; set up error code
        jc      ffb2                    ; error, save error in bx
        xor     bx,bx                   ; no error

; even if error occured, discard buffers for writes which succeeded

        test    ffb_Discard,FFL_CACHE_FLUSH_DISCARD ; see if discard data set
        jz      ffb2                    ; not discard, go return
                                        ; Defect 72417 changes start
        CALLFAR BufInvalidate           ; discard data
                                        ; Defect 72417 changes end
ffb2:   RestoreReg <di,si>              ; (AX) == vpb handle
ffb4:   mov     ax,bx                   ; return error/no error from FlushBuf
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack
EndProc FSH_FLUSHBUF



;***    FSH_CRITERROR - signal a hard error to the daemon
;
;       FSH_CRITERROR is passed a message template and strings to
;       substitute into the message.
;       Format of template is an asciiz string containing
;       text and/or text substitution indicators.
;       Indicators are % signs followed by a number, the
;       order of replacement for elements in the
;       substitution text list.  The maximum length of
;       a template is 128 bytes. The maximum length of
;       the message with text substitutions is 512 bytes.
;
;       template = "Please put %2 in %1";
;
;       Format of substitution text list is each element
;       in the order of substitution separated by a null
;       character.
;
;       subtext = "b:\0system diskette";
;
;       The maximum number of substitutions is 9.
;       The maximum length of a template is 128 bytes.
;       The maximum length of the message with text
;       substitutions is 512 bytes.
;
;       When specifying a newline in a message template, use a \r\n,
;       carriage return and linefeed.  The hex equivalent is 0d0a.
;
;       fAllowed   bit mask of allowed (hard error) actions:
;                    1 H   FAIL allowed
;                   10 H   ABORT allowed
;                  100 H   RETRY allowed
;                 1000 H   IGNORE allowed
;                10000 H   ACKNOWLEDGE allowed  (when this is set, others shd be off)
;
;
;       ENTRY   push    word template length    (1 word)
;               push@   template                (2 word)
;               push    word number of replacement strings (1 word)
;               push@   replacement strings     (2 word)
;               push    word allowed actions    (1 word)
;               call    FSH_CRITERROR
;
;       EXIT    AX = action to be taken
;
;       USES    AX, BX, CX, DX, ES, SP, Flags
;
;       CALLS   HardError
;
;       NOTES   If the user response to the hard error pop-up is ABORT, then
;               this process is marked for termination. In this case, we
;               return FAIL to the FSD.
;
;               If the harderror daemon has not been initialized, as could be
;               the case early on in boot-up time, we need to still put the
;               message out on the screen. This assumes that the FSD has run
;               into some problem and has to display the message. In this case,
;               we just call WrtToScrn to do this for us.
;
;       PSEUDOCODE
;               set up args to HardError
;               if (ChkHEDaem()) {
;                   call HardError
;                   if (user response is ABORT)
;                       convert it to FAIL
;                   return user response
;               else {
;                   WrtToScrn()
;                   CritErrPause()
;                   if (caller allowed ACK)
;                       return(ACK)
;                   else
;                       return(FAIL)
;               }
;
FSHentry FSH_CRITERROR,ORD_FSH_CRITERROR
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fce_Actions,WORD
ArgVar  fce_ReplStrings,DWORD
ArgVar  fce_NumStrings,WORD
ArgVar  fce_MsgTemplate,DWORD
ArgVar  fce_MsgLen,WORD

        EnterProc
        SaveReg <ds,edx,di,si>
        mov     ax,fce_Actions          ; ax = hard error options
        and     ax,NOT ALLOWED_RESERVED ; remove other invalid bits
        jz      fhe1
        cmp     ax,ALLOWED_ACKNOWLEDGE  ;
        jbe     fhe1                    ; bits are valid
        and     ax,NOT ALLOWED_ACKNOWLEDGE  ;remove ACK bit

; instead of returning an error if other bits are on, we are ignoring the
; ACK bit and keep the other bits.

fhe1:   mov     si,CurrTCB
        mov     ss:[si].TCBAllowed,al
        mov     ax,HEFSHMessage     ; Tell daemon to print message if no autoFail
        mov     dx,HEMessage_Template   ; Tell daemon to use template
        mov     cx,fce_NumStrings   ; (CX) == number of strings
        mov     bx,fce_MsgLen       ; (BX) == message template length
        lds     si,fce_MsgTemplate  ; (DS:SI) -> template
;
; Check for the presence of the harderror daemon.
; If present, call HardError, otherwise call WrtToScrn to display the message.
;
        CALLFAR ChkHEDaemon
        jc      fhe20               ; No daemon.

        push    bx
        push    ds
        push    si
        lds     si,fce_ReplStrings  ; (DS:SI) -> strings
        CALLFAR HardError           ; (AX) == response
        cmp     ax,HDERR_ABORT
        jnz     fhe5
        mov     ax,HDERR_FAIL       ; The response was ABORT. Convert to FAIL.
fhe5:   add     sp,6                ; CLEAN OFF ARGS TO HARDERROR
fhe7:   RestoreReg <si,di,edx,ds>
        LeaveProc

ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

;
; There is no harderror daemon. We handle the writing of the message to the
; screen by ourselves.
;
; DS:SI -> message template
; BX = message template length
;
fhe20:  movzx   edx,si              ; DS:EDX -> message
        mov     cx,bx               ; CX = length of message
        CALLFAR WrtToScrn           ; Print out non-fatal message

                                    ; Defect 72417 changes start
        CALLFAR CritErrPause        ; Pause for user input
                                    ; Defect 72417 changes end
        mov     ax,HDERR_ACK        ; (ax) = acknowledge (preset)
        test    fce_Actions,ALLOWED_ACKNOWLEDGE ; Did the caller request Acknowledge?
        jnz     fhe7                ; yes, go return it.

        mov     ax,HDERR_FAIL       ; (ax) = fail
        jmp     short fhe7          ; Return to FSD

EndProc FSH_CRITERROR
; Defect 72417 changes start
HIGH3CODE ENDS

        FARCODE HIGH2
HIGH2CODE SEGMENT
; Defect 72417 changes end

;***    CritErrPause - pause for a user acknowledgement
;
;       This routine prints a message on the screen and waits
;       for the user to hit the "RETURN" or "ENTER" key.  It
;       should only be used when the hard error daemon has
;       not been initialized yet.
;
;       NOTE ***This routine may block***
;
;       ENTRY   None
;
;       EXIT    None
;
;       USES    Flags
;
;       PSEUDOCODE
;           WrtToScrnClr()
;           GetDevEntry(KBD$)
;           do {
;               sw_DevIOCtl(KBD$,ReadOneChar)
;           } while (char != "ENTER")
;
                                    ; Defect 72417 changes start
Procedure CritErrPause,hybrid
                                    ; Defect 72417 changes end
        assume  ds:nothing, es:nothing

LocalVar    cep_CharData,<size CharData>
LocalVar    cep_ReqCount,WORD
LocalVar    cep_DevEntry,DWORD

        EnterProc
        pusha
        SaveReg <ds,es,edx>
        DosContext ds

        mov     ax, MSG_SYSINIT_PRESS_ENTER     ; address of message
        call    DHF.DosHlpProtGetMessage        ; (ds:si) = message length + text
        lodsw                           ; (ax) = length, ds:si -> message text
        mov     cx,ax                   ; (cx) = length of message
        movzx   edx,si                  ; ds:edx -> message text
        CALLFAR WrtToScrnClr            ; Print out non-fatal message

        DosContext ds

        lea     si,szKBD                ; ds:si -> name of keyboard driver
        mov     ax,DEVTYPE_BIMODAL      ; (ax) = bimodal device
        CALLFAR GetDevEntry             ; ds:si -> DevEntry
        jc      cepx                    ; error, bail out

        mov     ax,[si].DES_RoutePtr.Offst
        mov     [cep_DevEntry].Offst,ax
        mov     ax,[si].DES_RoutePtr.Segmt
        mov     [cep_DevEntry].Segmt,ax ; save the pointer to the device

cep10:  mov     [cep_ReqCount],1        ; request one character
        xor     cx,cx                   ; (cx) = 0
        TaskContext <DS>
        lea     dx,cep_CharData         ; ds:dx -> parameter packet
        xor     si,si                   ; (si) = 0
        mov     di,-1                   ; (di) = invalid SFN
        mov     bl,1
        xor     bh,bh                   ; (bh) = 0
        push    ss
        lea     ax,cep_ReqCount
        push    ax                      ; pointer to number of records
        push    [cep_DevEntry].Segmt
        push    [cep_DevEntry].Offst    ; push the DevEntry pointer
        mov     al,04h                  ; keyboard ioctl
        mov     ah,74h                  ; read in characters function
        call    sw_DevIOCtl             ; do the IOCTL
        jc      cepx                    ; error, bail out

    assume ds:nothing

        cmp     [cep_CharData].kbdcharcode,0Dh  ; Is the character a Return?
        jne     cep10                   ; no, go get the next one

cepx:   RestoreReg  <edx,es,ds>
        popa
        LeaveProc
        ret

EndProc CritErrPause


;***    FSH_DEVIOCTL - Helper for FS_IOCTL
;
;       This routine is the helper for an FSD (File System Driver) which can
;       be installed.  It takes an IOCtl request and passes it to the device
;       driver.  There are some special cases which never get to the driver,
;       and some which get convoluted first.
;
;       Only requests for block devices should come from an FSD.  But no
;       checking is done.
;
;       ENTRY   push    WORD    fdi_flag    ==1 if originated by FSD (1 word)
;               push    DWORD   fdi_hDev    device handle (pDPB)    (2 words)
;               push    WORD    fdi_SFN     SFN of original request (1 word)
;               push    WORD    fdi_cat     category (0-255)        (1 word)
;               push    WORD    fdi_func    function (0-255)        (1 word)
;               push(@) DWORD   fdi_pParm   addr of parameter area  (2 words)
;               push    WORD    fdi_cbParm  length of param area    (1 word)
;               push(@) DWORD   fdi_pData   addr of data area       (2 words)
;               push    WORD    fdi_cbData  length of data area     (1 word)
;               call    FSH_DEVIOCTL
;
;       EXIT    (AX) = Return code
;                   0 = success
;
;       ERRORS  Returns errors from device driver,
;
;       USES    AX, BX, CX, DX, flags   (DS, DI, SI are preserved)
;
;       PSEUDOCODE
;       {
;           Set up ArgVars
;           load registers
;           sw_DevIOCtl                 /* call the sub-worker */
;       }
;

FSHentry FSH_DEVIOCTL,ORD_FSH_DEVIOCTL

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

ArgVar  fdi_cbData,WORD     ; length of data area
ArgVar  fdi_pData,DWORD     ; pointer to data area
ArgVar  fdi_cbParm,WORD     ; length of parameter area
ArgVar  fdi_pParm,DWORD     ; pointer to parameter area
ArgVar  fdi_Function,WORD   ; function code (within category, below)
ArgVar  fdi_Category,WORD   ; Category (for generic IOCtl)
ArgVar  fdi_SFN,WORD        ; SFN
ArgVar  fdi_hDev,DWORD      ; handle for device (really pointer to DPB)
ArgVar  fdi_flag,WORD       ; this call raised by FSD itself or passed on

        EnterProc

        mov     bx,fdi_flag                 ; get the flag (fsd raised ?)
        Test    bx,FSH_FSDIOCTL_MASK        ; test for valid values
        jnz     fshDIOC10                   ; invalid parameter ?

        SaveReg <ds,si,di>

        mov     di,fdi_SFN                  ; set up registers
        mov     cx,fdi_cbParm
        mov     si,fdi_cbData
        mov     ds,fdi_pData.Segmt
        mov     dx,fdi_pData.Offst
        mov     al,fdi_Category.LowByte
        mov     ah,fdi_Function.LowByte

        xchg    bh,bl                       ; USING only the low byte of flag
        xor     bl,bl                       ; not a device; just to make sure

        push    fdi_pParm.Segmt             ; set up stack parameters
        push    fdi_pParm.Offst
        push    fdi_hDev.HighWord
        push    fdi_hDev.LowWord

        call    sw_DevIOCtl                 ; call sub-worker (returns AX)
                                            ; (pops parameters off stack)
        jc      fshDIOC                     ; carry means AX = error
        xor     ax,ax                       ; otherwise, clear AX (no error)

fshDIOC:RestoreReg  <di,si,ds>
fshDIOE:LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                     ; clear off parameters
fshDIOC10: mov  ax,ERROR_INVALID_PARAMETER  ;
        jmp     fshDIOE

EndProc FSH_DEVIOCTL
; Defect 72417 changes start
HIGH2CODE ENDS

        FARCODE HIGH3
HIGH3CODE SEGMENT
; Defect 72417 changes end


;***    FSH_GETVOLPARM - Get VPB data from VPB handle
;
;       FSH_GETVOLPARM takes the handle to a VPB and generates pointers to
;       the VPB file system independent and dependent areas and returns those
;       pointers.
;
;       ENTRY   push    word hVPB                       (1 word)
;               push@   dword ptr to file system ind.   (2 word)
;               push@   dword ptr to file system dep.   (2 word)
;               call    FSHGETVOLPARM
;
;       EXIT    (ax) = return code
;                       0 - success
;
;       USES    AX,BX,ES FLAGS
;
;
;       PSEUDOCODE
;
FSHentry FSH_GETVOLPARM,ORD_FSH_GETVOLPARM
   assume ds:nothing, es:nothing, ss:nothing

ArgVar  gvp_FileSysD,DWORD
ArgVar  gvp_FileSysI,DWORD
ArgVar  gvp_hVPB,WORD

        EnterProc
        BeginNoBlock

        push    ds                      ;
        mov     bx,gvp_hVPB             ; handle to VPB
        VPBContext ES,BX                ; es:[bx] = vpb address
        push    bx                      ;
        lea     ax,[bx.vpb_fsi]         ; base of vpb_fsi
        lds     bx,gvp_FileSysI         ; ds:bx -> vpb_fsi ptr
        mov     [bx].Offst,ax           ;
        mov     [bx].Segmt,es           ; es:ax -> vpb_fsi
        pop     bx
        lea     ax,[bx.vpb_fsd]         ; base of vpb_fsd
        lds     bx,gvp_FileSysD         ; ds:bx -> vpb_fsd ptr
        mov     [bx].Offst,ax           ;
        mov     [bx].Segmt,es           ; es:ax -> vpb_fsd
        pop     ds                      ;

; This helper does not return any information but we make AX 0 just for safety

        xor     ax,ax
        EndNoBlock
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_GETVOLPARM

;***    FSH_FINDDUPHVPB - Find duplicate hVPB
;
;       FSH_FINDDUPHVPB takes the input VPB handle, contructs the address to
;       that VPB and scans the list of VPBs' comparing Volume ID and DPB
;       pointer fields for a match. If a match is found the handle to the
;       matching VPB is returned.
;
;       ENTRY   push    word hVPB                       (1 word)
;               push@   dword ptr to store handle       (2 word)
;               call    FSHFINDDUPHVPB
;
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_NO_ITEMS
;
;       USES    AX,BX,CX,ES FLAGS
;
;       PSEUDOCODE
;

FSHentry FSH_FINDDUPHVPB,ORD_FSH_FINDDUPHVPB
   assume ds:nothing, es:nothing, ss:nothing

ArgVar  fdvpb_hVPBPTR,DWORD
ArgVar  fdvpb_hVPB,WORD

        EnterProc
        SaveReg <ds,si,di>
        mov     si,fdvpb_hVPB           ; handle to VPB
        VPBContext DS,SI                ; DS:[SI] -> VPB
        lea     si,[si].vpb_fsi.vpi_ID  ; DS:[SI] -> Volume ID
                                        ; Defect 72417 changes start
        CALLFAR VPBFind                 ; Try to Find Dupilcate VPB
                                        ; Defect 72417 changes end
        mov     bx,ax                   ; save possible hVPB
        mov     ax,ERROR_NO_ITEMS       ; set error just in case
        or      bx,bx                   ; Duplicate VPB ?
        jz      fdvpb0                  ; No
        lds     si,fdvpb_hVPBPTR        ; yes, DS:[SI] -> pointer to store hVPB
        mov     [si],bx                 ; store duplicate vpb handle
        xor     ax,ax
fdvpb0: RestoreReg <di,si,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_FINDDUPHVPB
; Defect 72417 changes start
HIGH3CODE ENDS

        FARCODE HIGH2
HIGH2CODE SEGMENT
; Defect 72417 changes end

;***    FSH_DOVOLIO - Volume-based sector-oriented transfer
;
;       FSH_DOVOLIO is the helper that interfaces with the FSD and the Kernel
;       for I/O transfers. It validates that the transfer size is < 64K and
;       calls BeginTransfer to lock the FSDs' supplied segment and to validate
;       the buffer for proper access and size. The input I/O operation bits
;       are translated into a Device Driver command. If a Volume Mount is in
;       progress on the input VPB we call RawDiskIO. Otherwise, if errors are
;       to be reported through the HardError Daemon we call DoVolIO else we
;       we call DoVolIONE which returns errors. A call is then made to
;       EndTransfer to unlock the buffer segment.
;       If an I/O operation is already in progesss (as could happen in the
;       case of an IFS that supports removable media), we save volitile
;       FAT TCB variables and restore them on exit.
;
;
;       ENTRY   push    word iOperation                 (1 word)
;               push    word fAllowed                   (1 word)
;               push    word ihVPB                      (1 word)
;               push@   dword ipData                    (2 word)
;               push@   dword ipcSec                    (2 word)
;               push    dword iSec                      (2 word)
;               call    FSHDOVOLIO
;
;       fAllowed   bit mask of allowed (hard error) actions:
;                    1 B   FAIL allowed
;                   10 B   ABORT allowed
;                  100 B   RETRY allowed
;                 1000 B   IGNORE allowed
;                10000 B   ACKNOWLEDGE allowed
;                            (when this is set, others shd be off)
;
;       EXIT    (ax) = return code
;                       0 - success
;                       ERROR_NO_ITEMS
;                       ERROR_INVALID_PARAMETER
;
;       USES    AX,BX,CX,ES,FLAGS
;
;       NOTE    The process will marked to die if Probebuf detects a protection
;               violation.
;
;       PSEUDOCODE
;
;FSH_DOVOLIO ();
;
;  VPBContext (&vpb_ptr,hVPB);
;
;  if (fAllowed word is invalid)
;       return (error_invalid_parameter);
;  if (Operation > 0x000Fh) {
;     EndTransfer();
;     return (error_invalid_parameter);
;  }
;
;  TransferSize = pcSec->seccnt * (vpb_ptr->vpbfsi.vpi_cbSector;
;  if (not swapping and I/O operation already in progress) {
;     save FAT volatile TCB variables
;  }
;  if (rc = ioBeginTransfer(pdata,TransferSize,Operation & 0x0001,Contig)!=0)
;     goto return;
;  }
;
;  if (Operation & 0x0001 == false) {
;     cmd = read;
;     if (Operation & 0x0002 == true)
;       cmd = readbypass;
;  }
;  else {
;     cmd = write
;     if ((Operation & 0x0004 == true) & (Operation & 0x0002 == false))
;        cmd = writev;
;     else {
;       if ((Operation & 0x0004 == true) & (Operation & 0x0002 == true))
;         cmd = writevbypass;
;       else
;         if ((Operation & 0x0004 == false) & (Operation & 0x0002 == true))
;           cmd = writebypass;
;     }
;  }
;
;  if ((vpb_ptr -> vpbfsi.vpb_flags & VPBMOUNT) == false)
;
;    if ((Operation & 0x0008 == true)
;       rc = DOVOLIONE(cmd,pData,LogicalSector,pcSec->seccnt,hVPB);
;    else
;       rc = DOVOLIO(cmd,pData,LogicalSector,pcSec->seccnt,hVPB);
;
;  else
;
;    rc = RawDiskIO(cmd,pData,LogicalSector,pcSec->seccnt,vpb_ptr);
;
;  EndTransfer();
;  /* If we got an error, convert it to a DOS error */
;  return:
;  if (rc)
;     rc = ErrMap24[rc]
;  if (FAT TCB vars were saved) {
;     restore FAT TCB variables
;  }
;  return (rc);

FSHentry FSH_DOVOLIO,ORD_FSH_DOVOLIO
   assume ds:nothing, es:nothing, ss:TASKAREA

ArgVar  dov_LogicalSector,DWORD
ArgVar  dov_pcSec,DWORD
ArgVar  dov_pData,DWORD
ArgVar  dov_hVPB,WORD
ArgVar  dov_fAllowed,WORD
ArgVar  dov_Operation,WORD

LocalVar        dov_SaveTCBFlag,BYTE    ; whether we've saved TCB vars
LocalVar        dov_OldAllowed,BYTE
LocalVar        dov_tbufFlat,DWORD
LocalVar        dov_vmlock_flags,DWORD  ;
LocalVar        dov_TCBfIOLock,BYTE     ; Lock value                 ;### B729839

        EnterProc <0>                   ; indicate we haven't saved TCB vars
        mov     dov_vmlock_flags,VML_CONTIG
        SaveReg <ds,edi,esi>

        SaveReg <di,bx>                                              ;### B729839
        mov     di, TASKAREA:[CurrTCB]        ; address current TCB  ;### B729839
        mov     bl, TASKAREA:[di].TCBfIOLock  ; Save lock value      ;### B729839
        mov     dov_TCBfIOLock, bl            ; in local variable    ;### B729839
        RestoreReg <bx,di>                                           ;### B729839

        mov     ax,dov_fAllowed
        test    ax, ALLOWED_RESERVED    ; any other bits set ?
        jnz     dovInvP                 ; then invalid_param error
        cmp     ax,ALLOWED_ACKNOWLEDGE  ; ACK bits and other bits are
        jbe     dovChkOp                ; mutually exclusive
                                        ; both of them are set so error

dovInvP: mov    ax,ERROR_INVALID_PARAMETER
        jmp     dovx1

dovChkOp: test  dov_Operation,FSH_DOV_OPRESERVED ; reserved bits zero ?
        jnz     dovInvP                          ; no, return an error

; Calulate size of transfer and see if its > 64K if so return an error

        lds     si,dov_pcSec            ; ds:si-> sector count
        mov     ax,[si]                 ; ax = sector count
        mov     di,ax                   ; save sector count for later
        xor     dx,dx                   ; dx = 0
        mov     si,dov_hVPB             ; handle to vpb
        VPBContext ES,SI                ; ES:[SI] -> VPB
        test    es:[si].vpb_fsi.vpi_flags,VPB_ABOVE16M_ALLOWED
        jnz     dov00                   ;

        or      dov_vmlock_flags,VML_16M


; **************************** 61801 ********************************

; 61801 dov00:  les     si,es:[si].vpb_fsi.vpi_pDPB ; ES:[SI] -> DPB
; 61801         mov     cx,es:[si].dpb_cbSector   sector size

dov00:  mov     cx,es:[si].vpb_fsi.vpi_cbSector ; sector size   *** 61801 ***
; **************************** 61801 ********************************
; Make sure that the transfer size minus one can still fit in a word
; (actual transfer size <= 64K)  we have to work with the transfer size
; minus one because 64K is one too big to fit in a word
        mul     cx                      ; ax = number of bytes to transfer
        mov     cx,ax                   ; cx = number of bytes to transfer
        LJC     dov_xfrbig              ; use carry to detect 64K or greater
        or      cx,cx                   ; transfer is < 64K
        LJZ     dov0                    ; Is transfer 0?
        jmp     dov_xfrok               ; continue past if not zero
dov_xfrbig:                             ; transfer is 64K or greater
        or      cx,cx                   ; is it exactly 64K?
        LJNZ    dov30                   ; jump to error-out if > 64K
dov_xfrok:
        dec     cx                      ; (cx) = bytes to transfer - 1
        dec     ax                      ; (ax) = bytes to transfer - 1

; Determine if filesystem is telling us to DOVOLIO to resident memory

        test    dov_Operation,FSH_DOV_OPRESMEM  ; Memory is already resident?
        jz      dov01                           ; No, then allow vm_locking
        and     dov_Operation,FSH_DOV_OPCLEAR   ; turn off bit after use
        or      dov_vmlock_flags,PG_RESIDENT    ; set bit so begintransfer
                                                ; doesn't do vmlock on buffer
; Determine transfer type read or read/write access

dov01:  xor     dh,dh                   ; assume read only access
        test    dov_Operation,FSH_DOV_OPIOACCESS  ; Read operation ?
        jz      dov0                    ; yes, then dh is correct
        inc     dh                      ; no, set dh = read/write access

;       (cx)    = transfer size - 1
;       (dh)    = transfer type flag
;       (di)    = sector count

; Carry clear for success

dov0:   xor     ax,ax                   ; No error if cx = 0
        or      cx,cx                   ; 0 length transfer ?
        LJZ     dovx1                   ; Yes, Do not probe on 0 len transfers

                                        ;Preserve Lock Handle
        mov     bx,[CurrTCB]                        ;### PTR B721311  Cutter PTR b713302
        push    TASKAREA:[bx].TCBLockHndl.HighWord  ;### PTR B721311  Cutter PTR b713302
        push    TASKAREA:[bx].TCBLockHndl.LowWord   ;### PTR B721311  Cutter PTR b713302

        mov     bx,dov_pData.Segmt      ; pick up the selector and offset of
        mov     si,dov_pData.Offst      ; the pointer to the transfer area.


        SaveReg <dx,di,si,bx>

        mov     di,ss:[CurrTCB]         ; address current TCB
        mov     ss:[di].TCBfIOLock,al   ; AX = 0, No lock, No error yet.
        test    ss:[di].TCBfSwapping,SM_TCB_SWAPPING ; swapping?
        jnz     dov1a                   ; yes, memory guaranteed to be locked

;       (di)    = sector count
;       (bx:si) = transfer address
;       (cx)    = transfer size
;       (dh)    = transfer type flag 0 = read 1 = read/write
;
; We must lock the segment and validate the buffer for proper access and size.

        SaveAlignESP
        xor     eax,eax
        mov     dl,dh

        PushP   -1                      ; arg7: we don't want page list info.
        PushP   -1                      ; arg6: we don't want page list info.
        mov     eax,dov_vmlock_flags    ;
        or      dx,dx
        jnz     dov0a                   ;
        or      eax,VML_DIRTY           ;
dov0a:  PushP   eax                     ; arg4: dirty/16MB/contig flags
        mov     ax,cx                   ; (eax) = transfer count - 1
        inc     eax                     ; add one to get transfer count (DWORD)
        PushP   eax                     ; arg3: transfer count
        xor     eax,eax                 ; zero possible 1 in high order
        mov     ax,si                   ; (eax) = offset
        PushP   eax                     ; arg2: offset
        mov     ax,bx                   ; (eax) = segment
        PushP   eax                     ; arg1: segment
        FlatContext <ds,es>,ax

        mov     di,ss:[CurrTCB]         ; address current TCB
        cmp     ss:[di].TCBVolIONest,0  ; io already in progress?
        je      dov1                    ; no, don't save TCB vars
        inc     dov_SaveTCBFlag         ; tell ourselves we've saved TCB vars

        CCallNest                       ; preserve _ioBeginTransfer parms
        SSToDS  esi,dov_tbufFlat
        CCALL32 _VMGetTempBuffer,<esi, 0>

        CCallNest                       ; preserve _ioBeginTransfer parms
        mov     edi,dov_tbufFlat
        CALLF32  ioSaveTCB

dov1:   CCALL32 _ioBeginTransfer,,,PushP; (eax) = error code
        pop     esp
    assume ds:nothing,es:nothing

dov1a:
        RestoreReg <ds,bx,cx,dx>


;       (ds:bx) = transfer address
;       (cx)    = sector count
;       (dh)    = transfer type flag 0 = read 1 = read/write

        or      ax,ax
        LJNZ    dovx

        mov     si,dov_Operation        ; store operation word into register
        dec     dh                      ; read or write operation ?
        jz      dov2                    ; write operation go process write

; Read Operation determine Cache bypass option

        mov     al,CMDINPUT             ; assume normal read operation
        test    si,FSH_DOV_OPCACHEBP    ; read with cache bypass ?
        jz      dov4_5                  ; no, normal read
        mov     al,CMDInputBypass       ; yes, read with cache bypass
        jmp     short dov4_5

; Write Operation determine Cache bypass and verify options

dov2:   mov     al,CMDOUTPUT            ; assume normal write operation
        test    si,FSH_DOV_OPCACHEBP    ; write with cache bypass ?
        jz      dov3                    ; no, normal write
        mov     al,CMDOutputBypass      ; yes, write with cache bypass
dov3:   test    si,FSH_DOV_OPVERIFY     ; Verify needed on write ?
        jnz     dov4                    ; No
        test    VerFlg,01h              ; PerProcess Verify set ?
        jz      dov4_5                  ; No
dov4:   inc     al                      ; yes, set verify command

dov4_5: mov     dx,dov_LogicalSector.LowWord   ; Log. Starting Sector # low
        mov     di,dov_LogicalSector.HighWord  ; Log. Starting Sector # hi
        push    bp                      ; save stack frame pointer

        push    ax                      ; save device driver command

        mov     ax,dov_fAllowed         ; set the TCBAllowed var

        push    bp                      ; save the stack frame
        mov     bp,TASKAREA:CurrTCB     ;
        xchg    al,TASKAREA:[bp.TCBAllowed]     ; set TCBAllowed value
                                        ; and also get old value so that
                                        ; we can save it and restore it
        pop     bp                      ; bp <- stack frame
        mov     dov_OldAllowed,al       ; save the current HE option bits

        mov     bp,dov_hVPB             ; handle to vpb
        pop     ax                      ; ax <- dev.driver command

        VPBContext ES,BP                ; ES:[BP] -> VPB

;       (ds:bx) = transfer address
;       (cx)    = sector count
;       (al)    = Device Driver command
;       (si)    = Operation bits 1=read/write,2=cachebypass,4=verify,etc.
;       (di:dx) = Logical Starting sector
;       (es:bp) = VPB

        test    es:[bp].vpbfsi.vpb_flags,VPBMOUNT ;Mount in progress ?
        jnz     dov5                    ; yes then do raw I/O

; Perform Volume-based sector-oriented I/O.
; We need to pass the harderror, write-thru, and no-cache bits to DoVolIO.

FSHDOV_SHIFT    equ     3

        .errnz  (FSH_DOV_OPAUTOFAIL shr FSHDOV_SHIFT) - DOV_AUTOFAIL
        .errnz  (FSH_DOV_OPWRTHRU   shr FSHDOV_SHIFT) - DOV_WRTHRU
        .errnz  (FSH_DOV_OPNCACHE   shr FSHDOV_SHIFT) - DOV_NCACHE

        shr     si,FSHDOV_SHIFT         ; put advisory bits in place
        call    DoVolIO
        jmp     short dov7

; A Volume Mount is in Progress by an FSD we Must perform Raw I/O

dov5:   call    RawDiskIO
        mov     bx,ax                   ; BX = request packet status
        xor     ax,ax                   ; assume no error
        test    bx,STERR                ; any error?
        jz      dov7                    ; No error, go return I/O count
;
; We got an error from the device. We need to convert the error code to
; a DOS error code that we can return to the FSD.
; AX = I24 error code
;
        xor     bh,bh                   ; Yes, translate I24 error code
        DosContext ds
        mov     al,ErrMap24[bx]         ; get dos error code
    Assume DS:NOTHING
dov7:   pop     bp                      ; bp <- stack frame
                                        ; ax has error code
        mov     bl,dov_OldAllowed       ;
        push    bp                      ; save the stack frame again
        mov     bp,TASKAREA:CurrTCB     ;
        xchg    bl,TASKAREA:[bp.TCBAllowed] ; restore TCBAllowed value
        pop     bp                      ; restore stack frame pointer

        lds     si,dov_pcSec            ; ds:si-> place to store sector count
        mov     [si],cx                 ; store # of sectors transfered OK !

; We have completed the I/O we now must unlock the users transfer via
; Endtransfer

dov9:   call    EndTransfer

dovx:                                        ;##54699
                                             ;Restore Lock Handle
        mov     bx,[CurrTCB]                        ;### PTR B721311    Cutter PTR b713302
        pop     TASKAREA:[bx].TCBLockHndl.LowWord   ;### PTR B721311  Cutter PTR b713302
        pop     TASKAREA:[bx].TCBLockHndl.HighWord  ;### PTR B721311    Cutter PTR b713302

        test    dov_SaveTCBFlag,-1      ; need to restore TCB variables?
        jz      dovx1                   ; no, just exit

        SaveReg <ax>
        FlatContext <es,ds>,si
        mov     esi,dov_tbufFlat
        CALLF32 ioRestoreTCB
        CCALL32 _VMFreeTempBuffer,<[dov_tbufFlat]>
        assume  ds:nothing, es:nothing
        RestoreReg <ax>
dovx1:
        SaveReg <bx>                                                ;### B729839
        mov     bl, dov_TCBfIOLock           ; Restore original     ;### B729839
        mov     di,TASKAREA:[CurrTCB]        ; lock value in the    ;### B729839
        mov     TASKAREA:[di].TCBfIOLock, bl ; TCB.                 ;### B729839
        RestoreReg <bx>                                             ;### B729839


        RestoreReg <esi,edi,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

dov30:  mov     ax,ERROR_TRANSFER_TOO_LONG
        jmp     short dovx1

EndProc FSH_DOVOLIO


;***    FSH_DOVOLIO2 - Helper for FS_DOVOLIO2
;
;       This routine is the helper for an FSD (File System Driver) which can
;       be installed.  It takes an IOCtl request and insures that the correct
;       volume is in the drive before passing the request to the device
;       driver.  There are some special cases which never get to the driver,
;       and some which get convoluted first.
;
;       Only requests for block devices should come from an FSD.  But no
;       checking is done.
;
;       ENTRY   push    DWORD   fdvio2_hDev    device handle (pDPB)   (2 words)
;               push    WORD    fdvio2_SFN     SFN of original request(1 word)
;               push    WORD    fdvio2_cat     category (0-255)       (1 word)
;               push    WORD    fdvio2_func    function (0-255)       (1 word)
;               push(@) DWORD   fdvio2_pParm   addr of parameter area (2 words)
;               push    WORD    fdvio2_cbParm  length of param area   (1 word)
;               push(@) DWORD   fdvio2_pData   addr of data area      (2 words)
;               push    WORD    fdvio2_cbData  length of data area    (1 word)
;               call    FSH_DOVOLIO2
;
;       EXIT    (AX) = Return code
;                   0 = success
;
;       ERRORS  Returns errors from device driver,
;
;       USES    AX, BX, CX, DX, flags   (DS, DI, SI are preserved)
;
;       PSEUDOCODE
;       {
;           Set up ArgVars
;           load registers
;           DoVolIO2                 /* call the worker */
;       }
;

FSHentry FSH_DOVOLIO2,ORD_FSH_DOVOLIO2

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

ArgVar  fdvio2_cbData,WORD     ; length of data area
ArgVar  fdvio2_pData,DWORD     ; pointer to data area
ArgVar  fdvio2_cbParm,WORD     ; length of parameter area
ArgVar  fdvio2_pParm,DWORD     ; pointer to parameter area
ArgVar  fdvio2_Function,WORD   ; function code (within category, below)
ArgVar  fdvio2_Category,WORD   ; Category (for generic IOCtl)
ArgVar  fdvio2_SFN,WORD        ; SFN
ArgVar  fdvio2_hDev,DWORD      ; handle for device (really pointer to DPB)

        EnterProc

        SaveReg <ds,si,di>
        push    fdvio2_pParm.Segmt          ; set up stack parameters
        push    fdvio2_pParm.Offst
        push    fdvio2_hDev.HighWord
        push    fdvio2_hDev.LowWord
        push    fdvio2_SFN                  ; set up registers
        push    fdvio2_cbParm
        push    fdvio2_cbData
        push    fdvio2_pData.Segmt
        push    fdvio2_pData.Offst
        mov     al,fdvio2_Category.LowByte
        mov     ah,fdvio2_Function.LowByte
        push    ax
        push    FSH_FSD_IOCTL       ; raised by FSD always; this means the
                                    ; pointers are all prot mode and hence the
                                    ; device driver will be called only in prot
                                    ; mode
        call    DoVolIO2                    ; call worker (returns AX)
                                            ; (pops parameters off stack)
        jc      fshVIO2                     ; carry means AX = error
        xor     ax,ax                       ; otherwise, clear AX (no error)

fshVIO2:RestoreReg  <di,si,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                     ; clear off parameters

EndProc FSH_DOVOLIO2
; Defect 148538 changes start
HIGH2CODE ENDS
        FARCODE HIGH3
HIGH3CODE SEGMENT
; Defect 148538 changes end

;***    FSH_ADDSHARE - Add a name to the share set
;
;       FSH_ADDSHARE adds a name to the currently active sharing
;       set.  FSDs will use this call when executing deletes or
;       renames with wildcard characters.  FSH_ADDSHARE returns a
;       share handle that must be used with a succeeding
;       FSH_REMOVESHARE.  The name must be canonicalized.  Do not call
;       this routine with a char device.
;
;       ENTRY   push    dword   pName           (2 word)
;               push    word    mode            (1 word)
;               push    word    hVPB            (1 word)
;               push    dword   phShare         (2 word)
;               call    FSH_ADDSHARE
;
;       EXIT    (ax) = return code
;                        ERROR_SHARING_VIOLATION
;                        ERROR_TOO_MANY_OPEN_FILES
;                        ERROR_SHARING_BUFFER_EXCEEDED
;                        ERROR_INVALID_PARAMETER
;                        ERROR_FILENAME_EXCED_RANGE
;                      0 - success
;
;       USES    AX, BX, CX, DX, ES, flags
;
;       PSEUDOCODE
;          check for valid mode
;          TCBThishVPB = hVPB
;          AddShare
;          if (error)
;             return error
;          store SFT ptr in phShare
;          return no error
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
FSHentry FSH_ADDSHARE,ORD_FSH_ADDSHARE
ArgVar   fas_phShare,DWORD      ; pointer to location to return share handle
ArgVar   fas_hVPB,WORD          ; handle to volume where named object is
ArgVar   fas_mode,WORD          ; sharing and access mode
ArgVar   fas_pName,DWORD        ; pointer to asciiz name to add to sharing
LocalVar fas_dummy,WORD         ; KLUDGE: see gshare.asm
LocalVar fas_openbuf,PMPATHLEN  ; temp buffer

        EnterProc
        SaveReg <ds,di,si>
        mov     si,TASKAREA:CurrTCB      ;### PTR B720732
        push    TASKAREA:[si].TCBpOpenBuf ; Save current contents of ptda_pOpenBuf ;### PTR B720732
;### PTR B720732        push    TASKAREA:[ptda_pOpenBuf] ; Save current contents of ptda_pOpenBuf
        lea     ax,fas_openbuf
        mov     TASKAREA:[si].TCBpOpenBuf,ax            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],ax ; Initialize ptda_pOpenBuf
        test    fas_mode,FSH_SHARE_RESERVED     ; reserved bits set?
        mov     ax,ERROR_INVALID_PARAMETER
        LJNZ    fas2_5                          ; yes, go return error
;
; set up TCBThishVPB.
;
        mov     ax,fas_hVPB             ; (AX) = hVPB
        TaskContext <ds>,si
        mov     si,CurrTCB
        mov     TASKAREA:[si].TCBThishVPB,ax

;
; AddShare needs [WFP_Start] = offset into TASKAREA so we must copy the
; path into fas_openbuf.  This has the nice side effect of checking the path
; length
;
        lds     si,fas_pName    ; (DS:SI) -> name to add to sharing
        ASSUME DS:NOTHING
        TaskContext <es>,di
        lea     di,fas_openbuf  ; (ES:DI) -> buffer for pathname
                                ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     [bp].TCBWFP_Start,di
        UnSetBP_CurrTCB
                                ;72417 end
        mov     cx,MAXPATHLEN   ; (CX) = max path length
;
; this is OK for DBCS
;
fas1:   lodsb
        stosb
        or      al,al                   ; end of string?
        jz      fas1_5                  ; yes, go call the sharer
        loop    fas1                    ; no, copy another byte if not max length
        mov     ax,ERROR_FILENAME_EXCED_RANGE
        jmp short fas2_5                ; max length - go return error

fas1_5: TaskContext <ds>,di
;
; AddShare will allocate an SFT and enter it into sharing.
;
; AddShare parameters:
;   [WFP_START] -> pathname
;   TCBThishVPB -> hVPB
;   DS = TASKAREA
;   (AX) = mode
;
        mov     si,CurrTCB
        push    TASKAREA:[si].TCBThisSFT.Offst
        push    TASKAREA:[si].TCBThisSFT.Segmt ; save TCBThisSFT
                                        ;72417 moved from PTDA to TCB
        mov     cl,TASKAREA:[si].TCBPathNameType
        SaveReg <cx>                    ; save PathNameType
        mov     TASKAREA:[si].TCBPathNameType,PNType_LOCFILE ; restore PathNameType
                                        ;72417 end
        mov     cx,opbatchbreak OR oplockbreak  ; break opbatch and oplock
        mov     ax,fas_mode             ; (AX) = mode
        CALLFAR AddShare
;
;   TCBThisSFT -> initialized SFT
;
        mov     bx,CurrTCB
        jc      fas2                    ; error, go free buffer and return
        xor     ax,ax                   ; no error
        lea     si,TASKAREA:[bx].TCBThisSFT ; (DS:SI) -> SFT handle
        les     di,fas_phShare              ; (ES:DI) -> where to return SFT
        movsw
        movsw                           ; return sft ptr as share handle

fas2:   RestoreReg <cx>
                                        ;72417 moved from PTDA to TCB
        mov     TASKAREA:[bx].TCBPathNameType,cl ; restore PathNameType
                                        ;72417 end
        pop     TASKAREA:[bx].TCBThisSFT.Segmt ; restore TCBThisSFT
        pop     TASKAREA:[bx].TCBThisSFT.Offst

fas2_5: mov     si,TASKAREA:CurrTCB      ;### PTR B720732
        pop     TASKAREA:[si].TCBpOpenBuf ; Save current contents of ptda_pOpenBuf ;### PTR B720732
;### PTR B720732        fas2_5: pop     SS:[ptda_pOpenBuf]
        RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_ADDSHARE

;***    FSH_REMOVESHARE - Remove a share entry
;
;       FSH_REMOVESHARE is used to remove a previously-entered name
;       from the sharing set.
;
;       ENTRY   push    dword   hShare          (2 word)
;               call    FSH_REMOVESHARE
;
;       EXIT    none
;
;       USES    AX, BX, CX, DX, ES, flags
;
;       PSEUDOCODE
;          RemoveShare
;

FSHentry FSH_REMOVESHARE,ORD_FSH_REMOVESHARE
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  frs_hShare,DWORD        ; share handle returned by ADDSHARE

        EnterProc
        SaveReg <ds,di,si>
        les     di,frs_hShare   ; (ES:DI) -> SFT
        CALLFAR RemoveShare     ; remove from sft from sharing and free it
        RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_REMOVESHARE
; Defect 148538 changes start
HIGH3CODE ENDS
        FARCODE HIGH2
HIGH2CODE SEGMENT
; Defect 148538 changes end

;***    FSH_ISCURDIRPREFIX - Test for a prefix of a current directory
;
;       Since the kernel manages the text for each current directory
;       for each kernel process, the FSD must disallow any modification of
;       any directory that is either a current directory of some process
;       or the parent of any current directory of some process.  FSD's
;       call FSH_ISCURDIRPREFIX to achieve this.
;
;       FSH_ISCURDIRPREFIX will take the supplied path name,
;       enumerate all current directories in use, and test to see if
;       the specified path name is a prefix or is equal to some
;       current directory.  The name must be canonicalized and uppercase.
;
;       ENTRY   push    dword   pName           (2 word)
;               call    FSH_ISCURDIRPREFIX
;
;       EXIT    (ax) = return code
;               Error code if pName is the prefix of or equal to
;               the current directory of a process, 0 if otherwise.
;
;               ERROR_CURRENT_DIRECTORY
;
;       USES    AX, CX, ES, flags
;
;       PSEUDOCODE
;          ScanCDS

FSHentry FSH_ISCURDIRPREFIX,ORD_FSH_ISCURDIRPREFIX
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  ficp_pName,DWORD        ; path name

        EnterProc
        SaveReg <ds,di,si>
        lds     si,ficp_pName           ; (DS:SI) -> pathname
        cmp     WORD PTR ds:[si+2],'\' + (0 SHL 8)  ; is path = d:\?
        jz      ficp2                   ; yes, always a prefix so go return
        xor     di,di
        call    ScanCDS
        jnc     ficp2                   ; error, go set error code
        xor     ax,ax                   ; return no error
ficp1:  RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

ficp2:  mov     ax,ERROR_CURRENT_DIRECTORY  ; assume error
        jmp short ficp1                   ; go return

EndProc FSH_ISCURDIRPREFIX

;***    FSH_LOADCHAR - Load a character
;
;       FSH_LOADCHAR provides mechanism for loading a character from a string
;       taking into account DBCS considerations.  If the character is non-DBCS,
;       the second byte is zeroed.
;
;       ENTRY   push    dword   ppStr           (2 word)
;               push    dword   pChar           (2 word)
;               call    FSH_LOADCHAR
;
;       EXIT    none
;
;       USES    AX, ES, flags
;
;       PSEUDOCODE
;          TestDBCS(**ppStr)
;          *pChar = **ppStr
;          if dbcs
;             (*ppStr+=2)
;          else
;             (*ppStr++)
;

FSHentry FSH_LOADCHAR,ORD_FSH_LOADCHAR
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  flc_pChar,DWORD         ; pointer to char to return
ArgVar  flc_ppStr,DWORD         ; pointer to pointer to string to load char from

        EnterProc
        SaveReg <ds,di,si>
        les     di,flc_ppStr            ; (ES:DI) -> ptr to string
        lds     si,es:[di]              ; (DS:SI) -> string
        lodsb                           ; (AL) = lead byte
        TestDBCS                        ; DBCS?
        mov     ah,0                    ; assume non-DBCS, preserve flags!
        jnz     flc1                    ; non-DBCS, go return
        mov     ah,al                   ; (AH) = lead byte
        lodsb                           ; (AL) = second byte
        xchg    ah,al                   ;
flc1:   mov     es:[di].Offst,si        ; return updated string pointer
        les     di,flc_pChar            ; (ES:DI) -> char
        stosw                           ; return char
        RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_LOADCHAR

;***    FSH_PREVCHAR - Decrement character pointer
;
;       FSH_PREVCHAR provides mechanism for decrementing a character pointer
;       taking into account DBCS considerations.  If the pointer to be
;       decremented points to the second byte of a DBCS, the returned pointer
;       points to the first byte of the DBCS.
;
;       ENTRY   push    dword   pBeg            (2 word)
;               push    dword   ppStr           (2 word)
;               call    FSH_PREVCHAR
;
;       EXIT    none
;
;       USES    AX, BX, DX, ES, flags
;
;       PSEUDOCODE
;           pPrev = pCurr = pBeg;
;           while (pCurr < *ppStr) {
;               pPrev = pCurr;
;               if (TestDBCS (*pCurr))
;                   pCurr += 2;
;               else
;                   pCurr += 1;
;               }
;           *ppStr = pPrev;
;

FSHentry FSH_PREVCHAR,ORD_FSH_PREVCHAR
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fpc_ppStr,DWORD         ; pointer to pointer to decrement
ArgVar  fpc_pBeg,DWORD          ; pointer to beginning of string
;
; (DS:SI) is pCurr
; (BX) is pPrev
; (DX) is *ppStr
;
        EnterProc
        SaveReg <ds,di,si>
        lds     si,fpc_pBeg             ; (DS:SI) -> beginning of string
        mov     bx,si                   ; (DS:BX) -> beginning of string
        les     di,fpc_ppStr            ; (ES:DI) -> pointer to decrement
        mov     dx,es:[di].Offst        ; (DS:DX) -> char to decrement from
fpc1:   cmp     si,dx                   ; (pCurr < *ppStr)?
        jae     fpc3                    ; no, go return updated pointer
        mov     bx,si                   ; pPrev = pCurr
        lodsb                           ; pCurr++
        TestDBCS                        ; DBCS?
        jnz     fpc1                    ; non-DBCS, don't get another byte
        inc     si                      ; pCurr++
        jmp short fpc1

fpc3:   mov     es:[di].Offst,bx        ; *ppStr = pPrev
        RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_PREVCHAR

;***    FSH_STORECHAR - Load a character
;
;       FSH_STORECHAR provides mechanism for storing a character into a string
;       taking into account DBCS considerations.
;
;       ENTRY   push    word    chDBCS          (1 word)
;               push    dword   ppStr           (2 word)
;               call    FSH_STORECHAR
;
;       EXIT    none
;
;       USES    AX, BX, ES
;
;       PSEUDOCODE
;          TestDBCS(chDBCS)
;          if dbcs {
;             (unsigned int)**ppStr = chDBCS
;             *ppStr+=2)
;          }
;          else {
;             **ppStr = chDBCS
;             *ppStr++
;          }
;
FSHentry FSH_STORECHAR,ORD_FSH_STORECHAR
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fsch_ppStr,DWORD        ; pointer to pointer to string
ArgVar  fsch_chDBCS,WORD        ; character to store

        EnterProc
        SaveReg <ds,di,si>
        mov     ax,fsch_chDBCS  ; (AX) = char
        mov     bx,ax           ; (BX) = char
        lds     si,fsch_ppStr   ; (DS:SI) -> ptr to string
        les     di,ds:[si]      ; (ES:DI) -> where to store char
        stosb                   ; assume non-DBCS
        TestDBCS                ; DBCS?
        jnz     fsch1           ; no
        mov     al,bh           ; (AL) = second byte
        stosb                   ; store another byte
fsch1:  mov     ds:[si].Offst,di ; return updated ptr to string
        RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_STORECHAR

;***    FSH_GETOVERLAPBUF - Locates buffer overlapping range
;
;       FSH_GETOVERLAPBUF provides mechanism for handling buffers that
;       overlap a multi-sector transfer region within a file.  When performing
;       transfers, the FSD needs to identify all buffers that contain data
;       that overlap a particular region of the media.  When writing to
;       the media, these overlapping buffers must be filled in with the new
;       data as they contain old data while when reading these buffers need
;       to be copied over the data transferred from the media.
;
;       ENTRY   push    word    hVPB            (1 word)
;               push    dword   isecFirst       (2 word)
;               push    dword   isecLast        (2 word)
;               push    dword   pisecBuf        (2 word)
;               push    dword   ppBuf           (2 word)
;               call    FSH_FINDFIRSTBUF
;
;       EXIT    (ax) = return code
;               Error code is no overlapping buffer found. 0 otherwise.
;               ERROR_NO_ITEMS - no overlapping buffer was found.
;
;       USES    AX, BX, CX, DX, ES, flags
;
;       PSEUDOCODE
;          GetOverlapBuf
;
FSHentry FSH_GETOVERLAPBUF,ORD_FSH_GETOVERLAPBUF
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fgob_ppBuf,DWORD        ; pointer to pointer to buffer to return
ArgVar  fgob_pisecBuf,DWORD     ; pointer to sector number of buffer
ArgVar  fgob_isecLast,DWORD     ; last sector in range
ArgVar  fgob_isecFirst,DWORD    ; first sector in range
ArgVar  fgob_hVPB,WORD          ; volume handle

        EnterProc
        SaveReg <ds,di,si>
        mov     si,fgob_hVPB            ; (SI) = hVPB
        mov     ax,fgob_isecLast.hiword
        mov     bx,fgob_isecLast.loword ; (AX:BX) = last sector
        mov     cx,fgob_isecFirst.hiword
        mov     dx,fgob_isecFirst.loword ; (CX:DX) = first sector
        call    GetOverlapBuf
        mov     ax,ERROR_NO_ITEMS
        jc      fgob1                   ; error, go return
;
; (DS:SI) -> buffer
; (CX:DX) -> sector returned
;
        les     di,fgob_ppBuf           ; (ES:DI) -> ptr to buffer to return
        mov     es:[di].Offst,si
        mov     es:[di].Segmt,ds        ; return pointer to buffer
        les     di,fgob_pisecBuf        ; (ES:DI) -> ptr to sector number
        mov     es:[di].hiword,cx
        mov     es:[di].loword,dx       ; return buffer sector number
        xor     ax,ax                   ; return no error
fgob1:  RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

EndProc FSH_GETOVERLAPBUF

;***    FSH_WILDMATCH - Match using OS/2 wildcards
;
;       FSH_WILDMATCH provides a mechanism for using OS/2 wildcards
;       semantics to form a match between an input string and a pattern,
;       taking into account DBCS considerations.
;
;
;       ENTRY   push    dword   pPat            (2 word)
;               push    dword   pStr            (2 word)
;               call    FSH_WILDMATCH
;
;       EXIT    (ax) = return code
;               Error code if match failed, 0 otherwise.
;
;               ERROR_NO_META_MATCH - the wildcard match failed
;
;       USES    AX, BX, CX, DX, ES, flags
;
;       PSEUDOCODE
;           If test string does not have a '.' in it,
;               replace the null by '.'
;           if Pattern string does not have any wild char and no '.'
;               replace the null by '.'
;           MetaMatch (source, pattern,sourceStrLen,PatStrLen)
;           If '.' added to test or pattern string,
;               replace them by null
;
FSHentry FSH_WILDMATCH,ORD_FSH_WILDMATCH
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  fwm_pStr,DWORD          ; pointer to test string
ArgVar  fwm_pPat,DWORD          ; pointer to asciiz pattern string
LocalVar fwm_tststrlen,WORD     ;
LocalVar fwm_patstrlen,WORD     ;
LocalVar fwm_realpatstrlen,WORD ;
LocalVar fwm_error,WORD         ;#72768 Hold error code from metamatch

        EnterProc
        SaveReg <ds,di,si>
        mov    fwm_error, 0     ;#72768 Assume no error
; look at the test string; if the string does not have a '.' , replace
; the null at end by '.'; and 'not' the string length to remember that
; we added a '.'
        les     di,fwm_pStr     ; (ES:DI) -> string
        mov     cx,-1           ;
        mov     al,0            ; find end of string
        repne   scasb           ;
        not     cx              ; CX = length of string including null
        dec     cx              ; don't count null
        mov     fwm_tststrlen,cx ; save it for later use
        mov     di,fwm_pStr.Offst       ;
        mov     al,'.'          ; look for '.' in string
        repne   scasb           ;
        mov     bx,fwm_tststrlen ;
        jz      fwm10           ; '.' found; we don't need to add one
        mov     di,fwm_pStr.Offst ; no '.'in name ; add one
        add     di,bx ;
        mov     byte ptr es:[di],'.'    ;
        inc     bx              ; to account for '.'
        not     fwm_tststrlen   ; To remember that we added a '.'
;
; if the pattern string does not have a '.' in it, then
;       if there are no wild char in it, we replace null by '.'
;       else if the last char in teststring was a '.' then also
;               we replace null by '.'
;       and when we replace null by '.', we increment the string length
;          to account for '.' and also do a 'not' to remember that we
;          replaced null by '.'
fwm10:  les     di,fwm_pPat     ; take the pattern and look for '.'  char ?
        mov     cx,-1           ;
        xor     al,al           ; find string length
        repne   scasb           ;
        not     cx              ; length including null
        dec     cx              ; don't count null
        mov     fwm_patstrlen,cx ;
        mov     fwm_realpatstrlen,cx ;
        mov     di,fwm_pPat.Offst ; now look for '.'
        mov     al,'.'          ;
        repne   scasb           ;
        LJNZ    fwm60           ; no '.'; further processing needed

fwm15:  mov     cx,fwm_patstrlen;

; bx = length of string not including null
; cx = length of expression  not including null
; ES = fwm_pPat.Segmt

fwm20:  lds     si,fwm_pStr     ; (DS:SI) -> string
fwm25:  mov     di,fwm_pPat.Offst       ; (ES:DI) -> pattern
;        push    ax             ;#72768 Why?
        call    MetaMatch
;        pop     ax             ;#72768 Why?
        mov     ax,ERROR_NO_META_MATCH
        jc      fwm30           ; error, go return
        xor     ax,ax           ; return no error
        jmp     fwm40

; strings didn't match.  return matching info:
;
; > 0x0000 no match.  pattern  is  lexicographically  greater  than
; string.
;
; <  0x0000 no match.  string  is  lexicographically  greater  than
; pattern.
;
; 0x8000   no  match.  pattern begins with  wild card character  or
; the pattern and string match up to the first wildcard character.

fwm30:  mov     fwm_error, ax           ;#72768 save error from metamatch
        SaveReg <di>
        mov     di,fwm_pPat.Offst       ; (ES:DI) -> pattern
        mov     cx,fwm_realpatstrlen ; cx = length of pattern string
        mov     al,'*'
        repne   scasb           ; look for '*'
        jne     fwm32           ; not found, go look for '?'
        RestoreReg <bx>
        cmp     bx,di           ; is miss-matched character after metachar?
        jge     fwm39d          ; yes, go return error (matching info irrelevant)

        SaveReg <bx>
fwm32:  mov     di,fwm_pPat.Offst       ; (ES:DI) -> pattern
        mov     cx,fwm_realpatstrlen ; cx = length of pattern string
        mov     al,'?'
        repne   scasb           ; look for '?'
        jne     fwm37           ; not found, go return matching info
        RestoreReg <bx>
        cmp     bx,di           ; is miss-matched character after metachar?
        jge     fwm39d          ; yes, go return error (matching info irrelevant)
        SaveReg <bx>            ;
; ds:si -> one past failed match in string
; subtract miss-matched string char from miss-matched pattern char to get
; matching info.
; we have to special case strings that have had implied dots appended to
; them.  the following case illustrates why:
;   string = config     -> config.
;   exp    = config.sys -> config.sys
; on return from metamatch, the string pointer -> after the '.', which is
; garbage.  the correct value to return is *exp - *str where *str == 0.
; so in cases where an implied dot has been appended and the mismatch occurs
; at the end of the string, we need to use 0 as the comparison character.
;
; ds:si -> one past miss-matched char in string.

fwm37:  RestoreReg <di>         ; es:di -> miss-matched character
        dec     si              ; ds:si -> miss-matched char
        cmp     fwm_tststrlen,0 ; implied dot in string?
        jge     fwm38           ; no, go check exp
        mov     ax,fwm_pStr.Offst
        sub     ax,fwm_tststrlen ; ax -> one past last char in string (@NULL)
        cmp     si,ax           ; does si point past last char?
        jb      fwm39           ; no. both strings can't have implied dots
                                ;  and match past the dot without matching
                                ;  completely, so we don't need to check exp.
; we know that *exp > 0 and *str == 0, so we can just say that the
; *exp - *str > 0.

        mov     ax,1
        jmp short fwm40

; es:di -> miss-matched char in exp.

fwm38:  cmp     fwm_patstrlen,0 ; implied dot in exp?
        jge     fwm39           ; no, go do regular comparison
        mov     ax,fwm_pPat.Offst
        sub     ax,fwm_patstrlen ; ax -> one past last char in string (@NULL)
        cmp     di,ax           ; does di point past last char?
        jb      fwm39           ; no, go do regular comparison

; we know that *exp == 0 and *str > 0, so we can just say that the
; *exp - *str < 0.

        mov     ax,-1

        mov     fwm_error,ax    ;                                       ;102060

        jmp short fwm40

fwm39:  xor     bx,bx
        mov     al,byte ptr [si]
        mov     bl,al
        TestDBCS fwm39a         ; DBCS?
        jnz     fwm39a          ; no, go do regular subtract
        mov     bx,word ptr [si]

; bx = *pStr

fwm39a: mov     al,byte ptr es:[di]
        TestDBCS fwm39b         ; DBCS?
        jnz     fwm39b          ; no, go do regular subtract
        mov     ax,word ptr es:[di]
        jmp short fwm39c

fwm39b: xor     ah,ah
fwm39c: sub     ax,bx           ; ax = *pPat - *pStr

        jnc     fwm40           ;patten is not > string                 ;102060
        mov     fwm_error,-1    ;-1 == METAMATCH_DONE                   ;102060

        jmp short fwm40

;fwm39d: mov     ax,8000h        ; matching info useless
fwm39d: mov     fwm_error,8000h  ;#72768 matching info useless

; Now check the test and pattern string lengths; if they are negative, then
; we have replaced the null by '.' earlier, so we have to remove them now.
; if positive, we don't have to do anything.
fwm40:  cmp     fwm_tststrlen,0 ;
        jge     fwm45           ; '.' not added to teststring
        mov     cx,fwm_tststrlen ;
        not     cx
        les     di,fwm_pStr     ;
        add     di,cx           ;
        mov     byte ptr es:[di],0;
fwm45:  cmp     fwm_patstrlen,0 ;
        jge     fwm50           ; '.' not added to pattern string
        mov     cx,fwm_realpatstrlen ;
        les     di,fwm_pPat     ;
        add     di,cx           ;
        mov     byte ptr es:[di],0;
fwm50:  RestoreReg <si,di,ds>
        cmp     fwm_error,0     ;#72768 Check for error from metamatch
        jz      fwm55           ;#72768 If zero ok
        stc                     ;#72768
        mov     ax, fwm_error   ;#72768 Restore error condition
fwm55:                          ;#72768
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

; Pattern string does not have a '.' . We check to see if it has any
; '*' in it. If we find any '*' then if the last char in test string is
;  '.' we add a '.';if we dont find any '*' then also we add a '.'
fwm60:  mov     cx,fwm_patstrlen
        mov     di,fwm_pPat.Offst ; now look for '*'
        mov     al,'*'          ;
        repne   scasb           ;
        jnz     fwm70           ; no '*' in string; add '.'
; pattern has '*' but no '.'. check the last char in test string
; if that happens to be a '.', then add '.' to pattern also.
        lds     si,fwm_pStr     ; (DS:SI) -> string
        cmp     fwm_tststrlen,0 ; (-) if we added a '.'
        jl      fwm70           ;
        add     si,fwm_tststrlen        ;
        cmp     byte ptr [si-1],'.'     ;
        LJNE    fwm15
fwm70:  mov     cx,fwm_patstrlen ;
        mov     di,fwm_pPat.Offst;
        add     di,cx           ;
        mov     byte ptr es:[di],2eh    ; put a '.'
        not     fwm_patstrlen   ; remember that we added a '.'
        inc     cx              ; to account for '.'
        jmp     fwm20           ;

EndProc FSH_WILDMATCH


;***    FSH_FINDCHAR - find first occurence of a char
;
;       Finds the first occurrence of any one of a set of characters in an
;       ASCIIZ text taking into account DBCS in the source string. NULL
;       and DBCS characters cannot be searched for. This routine assumes the
;       input pointers are valid.
;
;       ENTRY   push    word    nChars          ; chars in search list
;               push    dword   pChars          ; ptr to search list
;               push    dword   ppStr           ; ptr to ptr to text
;               call    FSH_FINDCHAR
;
;       EXIT    ax = 0 if successful
;                 *ppstr points to character found
;               ax = ERROR_CHAR_NOT_FOUND if nothing found
;
;       USES    AX, BX, CX, DX, ES, flags

FSHentry FSH_FINDCHAR,ORD_FSH_FINDCHAR
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar  ffc_ppStr,DWORD                 ; text for searching
ArgVar  ffc_pChars,DWORD                ; characters to search for
ArgVar  ffc_nChars,WORD                 ; number of characters in *pChar

        EnterProc
        SaveReg <ds,di,si>

; setup parameters for FindMultiChar

        mov     bx,ffc_nChars           ; bx = # chars to search for
        lds     si,ffc_ppStr            ; ds:si -> -> text
        lds     si,ds:[si]              ; ds:si -> text
        les     dx,ffc_pChars           ; es:dx -> chars to search for
        call    FindMultiChar
        jc      ffc20                   ; not found, return error

; found, now put result ds:si into user's pointer

        les     di,ffc_ppStr            ; es:di -> -> text
        mov     es:[di].Offst,si        ; update offset portion
                                        ; segment portion is the same
        xor     ax,ax                   ; no error

ffc10:  RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

ffc20:  mov     ax,ERROR_CHAR_NOT_FOUND
        jmp     short ffc10

EndProc FSH_FINDCHAR
; Defect 148538 changes start
HIGH2CODE ENDS
        FARCODE HIGH3
HIGH3CODE SEGMENT
; Defect 148538 changes end


;***    FSH_CANONICALIZE - canonicalize a path
;
;       FSH_CANONICALIZE is used to convert a pathname to a canonical form
;       by processing '.'s and '..'s, uppercasing, and prepending the
;       current directory to non-absolute paths.
;
;       ENTRY   push    dword   pPathName
;               push    word    cbPathBuf
;               push    dword   pPathBuf
;               push    dword   pFlags
;               call    FSH_CANONICALIZE
;
;       EXIT    ax = error code if error detected, 0 otherwise.
;               ERROR_PATH_NOT_FOUND - invalid pathname, too many '..'s
;               ERROR_BUFFER_OVERFLOW - pathname is too long
;
;       USES    AX, BX, CX, DX, ES, flags
;
;       PSEUDOCODE
;         TransPathNoPCName
;         if (CY)
;           return ERROR_PATH_NOT_FOUND
;         if (StrLen(WFP_Start) > cbPathBuf)
;           return ERROR_BUFFER_OVERFLOW
;         copy WFP_Start to pPathBuf

        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
FSHentry FSH_CANONICALIZE,ORD_FSH_CANONICALIZE
ArgVar   fcc_pFlags,DWORD       ; flag indicating non-8.3 filename
ArgVar   fcc_pPathBuf,DWORD     ; result buffer
ArgVar   fcc_cbPathBuf,WORD     ; size of result buffer
ArgVar   fcc_pPathName,DWORD    ; path
LocalVar fcc_dummy,WORD         ; KLUDGE: see gshare.asm
LocalVar fcc_openbuf,PMPATHLEN  ; temp buffer

        EnterProc
        SaveReg <ds,di,si>

        mov     bx,TASKAREA:CurrTCB      ;### PTR B720732
        push    TASKAREA:[bx].TCBpOpenBuf ; Save current contents of ptda_pOpenBuf ;### PTR B720732
;### PTR B720732        push    TASKAREA:[ptda_pOpenBuf] ; Save current contents of ptda_pOpenBuf
        lea     si,fas_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,si ; Initialize ptda_pOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],si ; Initialize ptda_pOpenBuf

        lds     si,fcc_pPathName        ; ds:si -> path
        SaveReg <bp>
; save and restore PTDA vars that will be spoiled by TransPath
        assume  ds:nothing
                                        ;72417 moved from PTDA to TCB
        mov     bp,TASKAREA:CurrTCB     ;
        push    [bp].TCBWFP_Start       ;
        push    [bp].TCBWFP_Path_End    ;
        push    [bp].TCBCurr_Dir_End    ;
                                        ;72417 end
        push    [bp.TCBThisCDS.Segmt]   ;  CDS pointer  ;### PTR B720732
        push    [bp.TCBThisCDS.Offst]   ;  CDS pointer  ;### PTR B720732
;### PTR B720732        push    [ThisCDS.Segmt]         ;
;### PTR B720732        push    [ThisCDS.Offst]         ;
;### PTR B720732        mov     bp,TASKAREA:CurrTCB     ;
        push    [bp.TCBThishVPB]        ;
        push    [bp.TCBThisFSC.Segmt]   ;
        push    [bp.TCBThisFSC.Offst]   ;
        push    bp                      ; save this bp value also

        lea     di,fcc_openbuf          ; ss:di -> fcc_openbuf
        CALLFAR TransPathNoPCName

        pop     bp                      ; bp = CurrTCB
        pop     [bp.TCBThisFSC.Offst]   ;restore PTDA variables
        pop     [bp.TCBThisFSC.Segmt]   ;
        pop     [bp.TCBThishVPB]                ;
        pop     [bp.TCBThisCDS.Offst]   ;  CDS pointer  ;### PTR B720732
        pop     [bp.TCBThisCDS.Segmt]   ;  CDS pointer  ;### PTR B720732
;### B720732    pop     [ThisCDS.Offst]         ;
;### B720732    pop     [ThisCDS.Segmt]         ;
                                        ;72417 moved from PTDA to TCB
        pop     [bp].TCBCurr_Dir_End    ;
        pop     [bp].TCBWFP_Path_End    ;
        pop     [bp].TCBWFP_Start       ;
                                        ;72417 end

        RestoreReg <bp>
        jc      fcc20

        les     di,fcc_pFlags           ; es:di -> flags
        xor     ah,ah
        mov     es:[di],ax              ; return non-8.3 indicator

        TaskContext es,,noassume
                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        mov     di,TASKAREA:[di].TCBWFP_Start ; es:di -> WFP
                                        ;72417 end
        CALLFAR StrLen                  ; cx = length of WFP
        cmp     fcc_cbPathBuf,cx        ; is result buffer large enough?
        jb      fcc30                   ; no, go error exit

; es:di -> WFP
; cx = length of WFP including null

        TaskContext ds,,noassume
        mov     si,di                   ; ds:si -> WFP
        les     di,fcc_pPathBuf         ; es:di -> result buffer
        rep     movsb                   ; copy WFP into result buffer
        xor     ax,ax                   ; no error

fcc10:  mov     bx,TASKAREA:CurrTCB      ;### PTR B720732
        pop     TASKAREA:[bx].TCBpOpenBuf ; Save current contents of ptda_pOpenBuf ;### PTR B720732
;### PTR B720732        fcc10:  pop     SS:[ptda_pOpenBuf]
        RestoreReg <si,di,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

fcc20:  mov     ax,ERROR_PATH_NOT_FOUND
        jmp     short fcc10

fcc30:  mov     ax,ERROR_BUFFER_OVERFLOW
        jmp     short fcc10

EndProc FSH_CANONICALIZE

; Defect 148538 changes start
HIGH3CODE ENDS
        FARCODE HIGH2
HIGH2CODE SEGMENT
; Defect 148538 changes end

;***    FSH_QSYSINFO - query system information
;
;       ENTRY   push    word    index   ; 1 = get max sector size
;               push    dword   pData   ; pointer to buffer
;               push    word    cbData  ; buffer size
;               call    FSH_QSYSINFO
;       index == 1-> max sector size
;                2-> process identity
;                3-> abs. thread no
;                4-> per process verify-on-write flag
;
;       EXIT    ax = 0 if successful
;               ax = ERROR_INVALID_PARAMETER - bad index
;                  = ERROR_BUFFER_OVERFLOW - buffer too small
;
;       USES    ax,bx,es, flags

FSHentry FSH_QSYSINFO,ORD_FSH_QSYSINFO
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar fqs_cbData,WORD
ArgVar fqs_pData,DWORD
ArgVar fqs_index,WORD

        EnterProc

        mov     ax,fqs_index            ; ax = index
        dec     ax                      ; make it 0 relative for $TJMP
        call    $TJMP                   ;

; jmp table based on qsysinfo index

        db      (fqsi_end - fqsi_strt)/2        ; max index

        dw      DOSHIGH2CODE:fqsi10             ; invalid index jump addr

fqsi_strt: dw   DOSHIGH2CODE:fqsiMaxSec         ; max sector

        dw      DOSHIGH2CODE:fqsiPrId           ; procid

        dw      DOSHIGH2CODE:fqsiThNo           ; thread no

fqsi_end: dw    DOSHIGH2CODE:fqsiVrf            ;  per process verify flag

fqsi10: mov     ax,ERROR_INVALID_PARAMETER
fqsi15: LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

; query for MaxSector size; returns DOSGROUP:MaxSec value in fsd's
; buffer

fqsiMaxSec: cmp fqs_cbData,size MaxSec
                                        ; buffer large enough for a word?
        jb      fqsi30                  ; no, go error
        DosContext <es>
        mov     ax,es:MaxSec            ; ax = max sector size

; common code for both maxsec and taskno;
; ax = value to be stored into the given (fsd's) buffer
;        (DOSGROUP:MaxSec/DOSGROUP:TaskNumber)

fqsi20:  SaveReg <di>
        les     di,fqs_pData            ; es:di -> buffer
    assume es:nothing
        mov     es:[di],ax              ; save in caller's buffer
        RestoreReg <di>
        xor     ax,ax                   ; no error
        jmp     fqsi15

; store absolute thread no into fsd's buffer

fqsiThNo: cmp   fqs_cbData,size TaskNumber
        jb      fqsi30                  ; buffer large enough for a word ?
        DosContext <es>
        mov     ax,es:TaskNumber        ; ax = abs. thread no.
        jmp     fqsi20                  ;
;
; stores the process id into the fsd's buffer in the following format:
;       unsigned short PID  (TCBProc_ID)
;       unsigned short UID  (TCBUser_ID)
;       unsigned short PDB  (CurrentPDB)
        assume  es:nothing
fqsiPrId: cmp   fqs_cbData, size procid_struc
        jb      fqsi30          ; buffer large enough for the struct ?
        SaveReg <ds,si,di>
        TaskContext <ds>,si
        mov     si,CurrTCB
        les     di,fqs_pData            ; es:di -> buffer
        mov     ax,[si].TCBProc_ID
        mov     es:[di].ProcId,ax
        mov     ax,[si].TCBUser_ID      ; user Id
        mov     es:[di].UserId,ax
        mov     ax,CurrentPDB           ;
        mov     es:[di].CurPDB,ax       ;
        RestoreReg      <di,si,ds>      ;
        xor     ax,ax
        jmp     fqsi15                  ;
;
fqsi30: mov     ax,ERROR_BUFFER_OVERFLOW
        jmp     short fqsi15

fqsiVrf: cmp    fqs_cbData, size VerFlg
        jb      fqsi30          ; buffer large enough for the struct ?
        SaveReg <ds,di>
        TaskContext <ds>,ax
        les     di,fqs_pData            ; es:di -> buffer
        mov     al,VerFlg
        mov     byte ptr es:[di],al
        RestoreReg      <di,ds> ;
        xor     ax,ax
        jmp     fqsi15                  ;

EndProc FSH_QSYSINFO

;***    FSH_NAMEFROMSFN - return file name for the given SFN
;
;       ENTRY   push    word    SFN     ;
;               push    dword   pName   ; pointer to buffer
;               push    dword   pcbName ; pointer to buffer size
;               call    FSH_NAMEFROMSFN
;
;       EXIT    ax = 0 if successful
;               ax = ERROR_INVALID_HANDLE - invalid SFN
;                  = ERROR_BUFFER_OVERFLOW - buffer too small
;
;       USES    ax,bx,ecx,ds,es,flags
;
FSHentry FSH_NAMEFROMSFN,ORD_FSH_NAMEFROMSFN
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar fnfs_pcbName,DWORD
ArgVar fnfs_pName,DWORD
ArgVar fnfs_sfn,WORD

        EnterProc
        SaveReg <es,edi,ds,esi>

        mov     bx,fnfs_sfn             ;
        call    SFFromSFN               ; (es:di)-> SFT if NC
        jc      fnfs20                  ; invalid handle ?

        CpuMode 386

        mov     edi,es:[di].sf_MFT      ; get the mft pointer
        cmp     edi,SF_MFT_NULL         ; NO  MFT ?
        je      SHORT fnfs20            ;
        FlatContext     es,ax           ; es:edi -> MFT
        lea     edi,es:[edi].mft_name   ; es:edi -> MFT_name
        CALLFAR StrLen32                ; ecx = len (MFT_NAME) including null
        lds     si,fnfs_pcbName         ; get given length
        cmp     cx,[si]                 ; shd be enough to accom. null also
        ja      SHORT fnfs30            ; buffer overflow ?

        mov     [si],cx                 ; pass the count to FSD
        dec     word ptr [si]           ; len = len - 1 (for null)
        push    es
        pop     ds
        mov     esi,edi                 ; ds:esi -> MFT_NAME
        les     di, fnfs_pName
        movzx   edi,di                  ; es:edi -> fsd_buffer

        CpuMode reset

        call    FStrCpy32               ; copy the name onto the buffer
        xor     ax,ax                   ; no error
fnfs10: RestoreReg      <esi,ds,edi,es>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe                 ; clean args off stack

fnfs20: mov     ax,ERROR_INVALID_HANDLE
        jmp     short fnfs10
fnfs30: mov     ax,ERROR_BUFFER_OVERFLOW
        jmp     short fnfs10

EndProc FSH_NAMEFROMSFN

;***    FSH_UPPERCASE - convert an asciiz (EA names) to upper case
;
;       ENTRY   push    dword   pName   ; ptr to input name
;               push    word    cbBuf   ; size of output buffer
;               push    dword   pBuf    ; pointer to buffer
;               call    FSH_UPPERCASE
;
;       EXIT    ax = 0 if successful
;                  = ERROR_BUFFER_OVERFLOW - buffer too small
;
;       USES    ax,cx,flags
;
FSHentry FSH_UPPERCASE,ORD_FSH_UPPERCASE
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar fuc_pbuf,DWORD
ArgVar fuc_cbbuf,WORD
ArgVar fuc_pname,DWORD

        EnterProc
        SaveReg <ds,si,di>

; The output buffer length must be atleast equal to the length of the input
; string; otherwise return an error_buffer_overflow

        les     di,fuc_pname
        xor     al,al
        cld
        mov     cx,-1                   ;
        repne   scasb                   ;
        not     cx                      ; CX = count including null
        cmp     cx,fuc_cbbuf            ;
        ja      fuc20                   ;
; lengths are ok; call strcpy; this takes care of DBCS; but does not do
; anything for DBCS characters. ( Uppercase for DBCS ??? we don't know)
        les     di,fuc_pbuf             ;
        lds     si,fuc_pname            ;
        call    StrCpy                  ;
        xor     ax,ax                   ; no error
fuc10:  RestoreReg      <di,si,ds>
        LeaveProc
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe

fuc20:  mov     ax,ERROR_BUFFER_OVERFLOW ;
        jmp     fuc10                   ;

EndProc FSH_UPPERCASE
; Defect 72417 changes start
HIGH2CODE ENDS

        FARCODE HIGH3
HIGH3CODE SEGMENT
; Defect 72417 changes end

;***    FSH_CHECKEANAME - Check the EA name for invalid chars
;
;       ENTRY   push    word    level   ; level to check
;               push    dword   cleaname ; length of ea name not including null
;               push    dword   peaname ;  ptr to ea name
;               call    FSH_CHECKEANAME
;
;       EXIT    ax = 0 if successful
;                  = ERROR_INVALID_LEVEL - invalid level for this version
;                  = ERROR_INVALID_NAME - name too long,has invalid char or wild
;                       char
;
;       USES    ax,bx,cx,flags
;
FSHentry FSH_CHECKEANAME,ORD_FSH_CHECKEANAME
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar fcen_pEAName,DWORD
ArgVar fcen_cleaname,DWORD
ArgVar fcen_level,WORD

        EnterProc
        SaveReg <ds,si>

; check the level
        test    fcen_level,FCEAN_INVALID_LEVELS ; only level 1 allowed right now
        jnz     fcen20                  ;
        cmp     fcen_level,0            ;
        jz      fcen20                  ; level 0 also not allowed
        mov     ax,fcen_cleaname.loword ;
; check the length
        or      ax,ax                   ; zero length is bad
        jz      fcen30                  ;
        cmp     ax,MAXCOMPLEN           ; ea name should not be above 255 for
        ja      fcen30                  ; ver 1.2
        mov     ax,fcen_cleaname.hiword ;
        or      ax,ax                   ;
        jnz     fcen30                  ;
; check the ea name
        lds     si,fcen_pEAName         ;
        mov     ax,fcen_cleaname.loword ; check the ea name against valid char.
        CALLFAR CheckEAName             ; set for file names
        jc      fcen30                  ;
        xor     ax,ax
fcen10: RestoreReg      <si,ds>
        LeaveProc                       ;
ifdef FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
endif
        ret     ?aframe

fcen20: mov     ax,ERROR_INVALID_LEVEL  ;
        jmp     fcen10

fcen30: mov     ax,ERROR_INVALID_NAME   ;
        jmp     fcen10

EndProc FSH_CHECKEANAME

; Defect 72417 changes start
HIGH3CODE ENDS

        FARCODE HIGH2
HIGH2CODE SEGMENT
; Defect 72417 changes end

;***    FSH_CALLDRIVER - call DD's Strategy2 entry point
;
;       ENTRY   push    dword   pPkt    ; ptr to dd request packet
;               push    word    hVPB            ; hVPB for the volume
;               push    word    fControl        ;  flags
;               call    FSH_CALLDRIVER
;
;       EXIT    ax = 0 if successful
;                       else errorcode
;
;       USES    ax,bx,cx,flags
;
;       PSEUDOCODE
;               If drive supports REMOVEABLE_MEDIA {
;                       WaitVPB
;                       WaitDPB
;                       if we blocked for DPB, then go back to WaitVPB
;                       if given hVPB is not the one in DPB
;                               call FSH_SETVOLUME
;                               if error quit
;               }
;               call Dev.driver Strategy2 entry point
;
FSHentry FSH_CALLDRIVER,ORD_FSH_CALLDRIVER
   assume ds:nothing, es:nothing, ss:TASKAREA

ArgVar fcd_fControl,WORD
ArgVar fcd_hVPB,WORD
ArgVar fcd_pPkt,DWORD

        EnterProc
        SaveReg <es,ds,si,bx,bp>
        mov     bx,fcd_hVPB
        VPBContext      es,bx
        lds     si,es:[bx].vpb_fsi.vpi_pDPB
        test    ds:[si].dpb_flags,DPBNONREMOV   ;
        jnz     fcd30

; the drive has a removable media volume on it; do a volume check before
; proceeding

fcd10:  mov     ax,fcd_hVPB             ; retrieve VPB handle
        mov     bl,VPBCHECK
        SaveReg <bp>
        call    WaitVPB                 ; wait for VPB to become free
        chkvselhd es,bp
        lds     si,es:[bp].vpb_fsi.vpi_pDPB     ; get the last-used drive
;
; The volume is free and ES:BP points to the VPB.  See if the last-seen drive
; is free.  If not, wait and start over.
;
        RestoreReg <bp>
        call    WaitDPB                 ; wait for that drive to be free
        jc      fcd10                   ; oops, we blocked, go check VPB
;
; The VPB and drive are free.  Is the volume in the drive?
;
        cmp     ax,[si].dpb_hVPB        ; Is the volume in the drive?
        jz      fcd30                   ; yes, go call the driver

        push    ax                      ; hVPB
        push    fcd_fControl            ; fControl
        CALL    FAR PTR FSH_SETVOLUME   ;
        or      ax,ax                   ;
        jnz     fcdx                    ;

; ds:SI -> dpb

fcd30:  les     bx,     fcd_pPkt        ; es:bx -> request packet

        call    DWORD PTR ds:[si].dpb_strategy2 ;

        xor     ax,ax                   ; success ?? we need to look at pkt status for errors

fcdx:   RestoreReg      <bp,bx,si,ds,es>
        LeaveProc
        ret     ?aframe

EndProc FSH_CALLDRIVER

; Defect 72417 changes start
HIGH2CODE ENDS

        FARCODE HIGH3
HIGH3CODE SEGMENT
; Defect 72417 changes end

;***    FSH_SETVOLUME - force the volume to be mounted on the drive
;
;       ENTRY   push    word    hVPB    ; hVPB for the volume
;               push    word    fControl ;  flags
;               call    FSH_SETVOLUME
;
;       EXIT    ax = 0 if successful
;
;       USES    ax,flags
;
;       PSEUDOCODE:
;               check for invalid bits in fControl
;               while (true) {
;                       call WhatVolume
;                       if (newvol == old vol)
;                               return success
;                       else if (fControl set)
;                               return VOLUME_CHANGED error;
;                           else {
;                               call ToggleRequest to put up the pop up
;                               if abort/fail return error;
;                               /*else  go back to the while loop */
;                           }
;               }
;
FSHentry FSH_SETVOLUME,ORD_FSH_SETVOLUME
   assume ds:nothing, es:nothing, ss:TASKAREA
ArgVar fsv_fControl,DWORD
ArgVar fsv_hVPB,WORD

        EnterProc
        test    fsv_fControl,FSV_INVALID_CONTROLS       ; invalid bits set
        jnz     fsverr                  ; in fControl ?

        SaveReg <es,ds,si>
;
; top of the retry loop;
;

fsv10:  mov     si,fsv_hVPB
        VPBContext      es,si
        lds     si,es:[si].vpb_fsi.vpi_pDPB
        CALLFAR WhatVolume
        jc      fsv20
        mov     ax,fsv_hVPB
        cmp     ax,[si].dpb_hVPB        ; same volume
        jnz     fsv30                   ;
        xor     ax,ax
fsv20:  RestoreReg      <si,ds,es>
fsvx:   LeaveProc
        ret     ?aframe

fsv30:  test    fsv_fControl,FSV_NOVOL_POPUP    ;
        jnz     fsv40
        CALLFAR ToggleRequest           ; prompt user for action and wait
        cmp     ax,HDERR_RETRY          ; RETRY?
        jz      fsv10                   ; yes, no need to generate error code

; abort/fail

fsv40:  mov     ax,ERROR_VOLUME_CHANGED
        jmp     short fsv20

fsverr: mov     ax,ERROR_INVALID_PARAMETER
        jmp     short fsvx

EndProc FSH_SETVOLUME


IFDEF FSSTRICT

;***    FSH_STACKSPACE - Determine how much space available on the stack
;                        Only available in STRICT kernel, and not present
;                        in retail build.
;
;       ENTRY   PUSH    @WORD   StackSpace  ; Place for returned space
;               call    FSH_STACKSPACE
;
;       EXIT    ax = 0
;               StackSpace set to available space
;
;       USES    AX, ES, Flags
;
;       NOTES   Remember that the stack is a grow-down stack so that the
;               offset of the beginning of the stack is bigger than the
;               offset of the end. (When you push something on the stack,
;               the SP is decremented).
;
FSHentry FSH_STACKSPACE,ORD_FSH_STACKSPACE
   assume ds:nothing, es:nothing, ss:TASKAREA

    ArgVar fssp_pStackSpace,DWORD       ; Pointer to place for returned space

        EnterProc
        mov     ax,sp
        add     ax,10                               ; AX = SP before FSH_StackSpace
                                                    ; was called from FSD
        SaveReg <si,bx>
        mov     si,CurrTCB
        lea     bx,TASKAREA:[si].TSDUserStack + 2     ; BX = end of stack
        sub     ax,bx                               ; AX = Amount of stack space left
        les     si,fssp_pStackSpace
        mov     es:[si],ax                          ; Return stack space to FSD
        xor     ax,ax                               ; Indicate no error
        RestoreReg <bx,si>
        LeaveProc
        ret     ?aframe

EndProc FSH_STACKSPACE

ENDIF


;***    FSH_REGISTERPERFCTRS - Register a FSD with PERFVIEW: get the
;                              addresses of the FSD's counter and text blocks.

;       ENTRY   push    dword pDataBlk  ;address of counter block.
;               push    dword pTextBlk  ;address of text block.
;               push    word  fsFlags   ;flags (specifies 16-bit OR 32-bit
;                                       ;  addressing)
;               call    FSH_REGISTERPERFCTRS

;       EXIT    word ax (return code)
;                       NO_ERROR (=0) -- success
;                       ERROR_INVALID_PARAMETER
;                       ERROR_PVW_INVALID_COUNTER_BLK
;                       ERROR_PVW_INVALID_TEXT_BLK
;
;       USES    ax, ebx, es

FSHentry FSH_REGISTERPERFCTRS,ORD_FSH_REGISTERPERFCTRS
   assume ds:nothing, es:nothing, ss:TASKAREA

ArgVar frpc_fsFlags,WORD
ArgVar frpc_pTextBlk,DWORD
ArgVar frpc_pDataBlk,DWORD

        EnterProc

ifdef PERFVIEW

        SaveReg <ds>
        movzx   ebx,frpc_fsFlags
        or      ebx,RPC_FL_FSD
        FlatContext <ds,es>,ax
        CCALL32 _PVWRegisterPerfCtrs,<frpc_pDataBlk,frpc_pTextBlk,ebx>

        RestoreReg <ds>
        assume ds:nothing, es:nothing

endif ;PERFVIEW

        LeaveProc
IFDEF FSSTRICT
        xor     bx,bx
        mov     es,bx                   ; Zero out ES before return to FSD
ENDIF
        ret     ?aframe                 ; clean args off stack

EndProc FSH_REGISTERPERFCTRS


; Defect 72417 changes start
HIGH3CODE  ENDS
; Defect 72417 changes end
        END
