SCCSID equ "@(#)fcbio.asm 6.11 92/05/21"
        PAGE    ,132
TITLE   FCBIO - FCB system calls
NAME    FCBIO

; Ancient 1.0 1.1 FCB system calls
;
;       Global          Local
;
;       SaveFCBInfo
;       ResetLRU
;       SetOpenAge
;       LRUFCB
;       FCBRegen
;       BlastSFT
;       CheckFCB
;       SFTFromFCB
;       FCBHardErr
;       GetRR
;       GetExtent
;       SetExtent
;       GetExtended
;       GetRecSize
;       FCBIO
;                       w_FCB_Rename
;                       w_FCB_Delete
;                       ChkIFSVol
;                       Compare_VolId
;                       w_Get_FCB_File_Length
;       w_FCB_VolId

;  DEFECT  WHO          WHEN       WHAT
;  59428   D.Azzarito   28Jan1993  Save restore data before calling worker  @59428
;  72417   MLM          11/27/95   moved from PTDA to TCB:
;                                  ptda_pSrchBuf, WFP_Start, Curr_Dir_End
;                                  Ren_WFP, DTAddr, WFP_Path_End, LCurDrv, cMeta,
;                                  PathNameType

include fnofs.inc
include dosseg.inc
include defdbug.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
.xcref
include dossymc.inc
include argframe.inc
include cpmfcb.inc
include curdir.inc
include error.inc
include dirent.inc
include filemode.inc
include filesys.inc
include fsc.inc
include harderr.inc
include inbuf.inc
include tkexport.inc
                                      ;72417 moved find.inc after tkexport.inc
include find.inc
include meta.inc
include mft.inc
include mi.inc
include opncreat.inc
include sft.inc
include splitfs.inc
include syscalls.inc
include kmode.inc
include dbcs.inc
.cref
.list

        CpuMode 386
        ModName , FNO_FCBIO_ASM

; BUGBUG danno 23-Mar-1989
; The conditional jumps in this module need to be modified to short
; jumps.

; 58270 - Cathy Bundy 09-Jan-1993
; LRUFCB - Check for sftFCB=0 and if true return -1

                                                ;72417 removed PTDA entries now
                                                ;      in TCB

        ReferTask       SysCallSfcn,WORD        ; user input system call.

        ReferTask       ExtErr,WORD
        ReferTask       CurrTCB,WORD            ; Current thread control block
        ReferTask       CurrentPDB,WORD         ; current PDB segment
        ReferTask       SrchRmp,DWORD           ;#54785

        extrn           Date16:NEAR

        EXTRNFAR        Byte13_PackName
        EXTRNFAR        CheckThisDevice
        EXTRNFAR        ChkVal
        EXTRNFAR        Do_3xSearch_First
        EXTRNFAR        Do_3xSearch_Next
        EXTRNFAR        FFCB3xClose            ;#54785
        EXTRNFAR        FSCFromCDS
        EXTRNFAR        FSD_Close
        EXTRNFAR        FSD_Delete
        EXTRNFAR        FSD_Move
        EXTRNFAR        FSD_OpenCreate
        EXTRNFAR        FSD_Read
        EXTRNFAR        FSD_SetFSInfo
        EXTRNFAR        FSD_Write
        EXTRNFAR        GetThisDrv
        EXTRNFAR        FreeTempBuffer
        EXTRNFAR        GetTempBuffer
        EXTRNFAR        HardError
        EXTRNFAR        MetaMatch
        EXTRNFAR        PathDecVPBRef
        EXTRNFAR        PathIncVPBRef
        EXTRNFAR        RlseBuffr
        EXTRNFAR        SFT_SetFileInfo
        EXTRNFAR        ShSave
        EXTRNFAR        ShChk
        EXTRNFAR        SkipBack
        EXTRNFAR        TransFCB
        EXTRNFAR        UCase
        EXTRNFAR        w_EditName
        EXTRNFAR        w_QPathInfo
        EXTRNFAR        w_StoreFile3x       ;PCR UNDELETE
        EXTRNFAR        w_Unlink            ;PCR UNDELETE DEBUG

        EXTRNF32         FNM
        EXTRNF32        FreeMFTSem
        EXTRNF32        GetMFTSem


MVDMINSTDATA    SEGMENT

                PUBLIC  _sftFCB
_sftFCB         dw      0           ; pointer to SFTs for FCB cache

                PUBLIC  FCBLRU
FCBLRU          dw      0           ; least recently used count

OpenLRU         dw      0           ; open age

                PUBLIC  _KeepCount
_KeepCount      dw      0           ; number of FCBs to keep

MVDMINSTDATA    ENDS


        FARCODE HIGH4
FSSWAPCODE SEGMENT
        ASSUME  SS:TASKAREA

Break <Misbehavior fixers>

;
;   FCBs suffer from several problems.  First, they are maintained in the
;   user's space so he may move them at will.  Second, they have a small
;   reserved area that may be used for system information.  Third, there was
;   never any "rules for behavior" for FCBs; there was no protocol for their
;   usage.
;
;   This results in the following misbehavior:
;
;       infinite opens of the same file:
;
;       While (TRUE) {                  While (TRUE) {
;           FCBOpen (FCB);                  FCBOpen (FCB);
;           Read (FCB);                     Write (FCB);
;           }                               }
;
;       infinite opens of different files:
;
;       While (TRUE) {                  While (TRUE) {
;           FCBOpen (FCB[i++]);             FCBOpen (FCB[i++]);
;           Read (FCB);                     Write (FCB);
;           }                               }
;
;       multiple closes of the same file:
;
;       FCBOpen (FCB);
;       while (TRUE)
;           FCBClose (FCB);
;
;       I/O after closing file:
;
;       FCBOpen (FCB);
;       while (TRUE) {
;           FCBWrite (FCB);
;           FCBClose (FCB);
;           }
;
;   We support the first two types of behavior: infinite opens of the same
;   file, and infinite opens of different files.
;
;   The reserved fields of the FCB (10 bytes worth) is divided up into various
;   structures depending on the file itself and the state of operations of the
;   OS.   The information contained in this reserved field is enough to
;   identify the SFT.  We check this information (CheckFCB) to make sure we
;   have the right SFT.
;
;   Some key items need to be remembered:
;
;       Even though we are caching SFTs, they may contain useful sharing
;       information.  We enforce good behavior on the FCBs.
;
;       Network support must not treat FCBs as impacting the ref counts on
;       open VCs.  The VCs may be closed only at process termination.
;
;
;   The test bits are:
;
;       00 -> local file
;       40 -> sharing local
;       80 -> network
;       C0 -> local device

Break   <SaveFCBInfo - store pertinent information from an SFT into the FCB>

;
;   SaveFCBInfo - given an FCB and its associated SFT, copy the relevant
;       pieces of information into the FCB to allow for subsequent
;       regeneration. Poke LRU also.
;
;       ENTRY   ThisSFT points to a complete SFT.
;               DS:SI point to the FCB (not an extended one)
;       EXIT    The relevant reserved fields in the FCB are filled in.
;               DS:SI preserved
;               ES:DI point to sft
;       USES    All
;

Procedure   SaveFCBInfo,<near,faronly,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT
        Assert  ISSFT,<ES,DI>,"SaveFCBInfo"

SaveLocal:
;
;----- In share support -----
;
        CALLFAR ShSave
;
;----- end in share support -----
;


SaveSFN:
        MOV     AX,ES:[DI].sf_flags
        AND     AL,3Fh                  ; get real drive
        OR      AL,BL
        MOV     [SI].fcb_l_drive,AL
        LEA     AX,[DI-(SIZE sft)]      ; (ax) = offset from start of sf array
;
; Adjust for offset to table.
;
        PUSH    DS
        FlatContext DS
        MOV     BL,SIZE sf_entry
        DIV     BL
        MOV     DL,AL                   ; save last used SFN
        MOV     AX,FCBLRU               ; get lru count
        INC     AX
        MOV     WORD PTR ES:[DI].sf_LRU,AX
        JNZ     short SimpleStuff
;
; lru flag overflowed.  Run through all FCB sfts and adjust:  LRU < 8000h
; get set to 0.   Others -= 8000h.  This LRU = 8000h
;
        MOV     BX,sf_fsi.sfi_position
        call    ResetLRU
;
; Set new LRU to AX
;
SimpleStuff:
        MOV     FCBLRU,AX
        POP     DS
    ASSUME      DS:NOTHING
        MOV     [SI].fcb_sfn,DL         ; last used SFN
        RETP
EndProc SaveFCBInfo

Break   <ResetLRU - reset overflowed lru counts>

;
;   ResetLRU - during lru updates, we may wrap at 64K.  We must walk the
;   entire set of SFTs and subtract 8000h from their lru counts and truncate
;   at 0.
;
;   Inputs:     BX is offset into SFT field where lru firld is kept
;               ES:DI point to SFT currently being updated
;   Outputs:    All FCB SFTs have their lru fields truncated
;               AX has 8000h
;   Registers modified: AX, CX

Procedure   ResetLRU,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        Assert  ISSFT,<ES,DI>,"ResetLRU"
        MOV     AX,8000h
        SaveReg <DS,DI>
        FlatContext DS
        MOV     DS,[_sftFCB]            ; (ds:0) -> sft header
    ASSUME      DS:NOTHING
        MOV     CX,[sft_count]
        MOV     DI,SIZE sft             ; (ds:di) -> sf entry array
ovScan:
        SUB     WORD PTR [DI+BX],AX     ; decrement lru count
        JA      ovLoop
        MOV     WORD PTR [DI+BX],0      ; truncate at 0
ovLoop:
        ADD     DI,SIZE sf_entry        ; advance to next
        LOOP    short ovScan
        RestoreReg  <DI,DS>
        MOV     ES:[DI+BX],AX
        ret
EndProc ResetLRU

Break   <SetOpenAge - update the open age of a SFT>

;
;   SetOpenAge - In order to maintain the first N open files in the FCB cache,
;   we keep the 'open age' or an LRU count based on opens.  We update the
;   count here and fill in the appropriate field.
;
;   Inputs:     ES:DI point to SFT
;   Outputs:    ES:DI has the open age field filled in.
;               If open age has wraparound, we will have subtracted 8000h
;                   from all open ages.
;   Registers modified: AX
;

Procedure   SetOpenAge,<near,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        PUSH    DS
        FlatContext DS
        Assert  ISSFT,<ES,DI>,"SetOpenAge"
        MOV     AX,OpenLRU
        INC     AX
        MOV     ES:[DI].sf_OpenAge,AX
        JNZ     short SetDone
        MOV     BX,sf_fsi.sfi_position+2
        call    ResetLRU
SetDone:
        MOV     OpenLRU,AX
        POP     DS
    ASSUME      DS:NOTHING
        RETP
EndProc SetOpenAge

Break   <LRUFCB - perform LRU on FCB sfts>

;
;   LRUFCB - find LRU fcb in cache.  Set ThisSFT and return it.   We preserve
;       the first keepcount sfts if they are network sfts or if sharing is
;       loaded.   If carry is set then NO BLASTING is NECESSARY.
;
;   Inputs:     none
;   Outputs:    ES:DI -> SFT
;               ThisSFT points to SFT
;                   sf_flags = SF_FCB
;                   sf_ref_count = 0
;                   sf_mft = SF_MFT_NULL
;                   sf_FSC = invalid value
;                   rest of SFT is zeroed
;               Carry set of closes failed
;   Registers modified: ES, DI
;

Procedure   LRUFCB,<near,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        pushad
        push    ds

; Find nth oldest NET/SHARE FCB.  We want to find its age for the second scan
; to find the least recently used one that is younger than the open age.  We
; operate by scanning the list n times finding the least age that is greater
; or equal to the previous minimum age.
;
;   BP is the count of times we need to go through this loop.
;   AX is the current acceptable minimum age to consider
;
        FlatContext DS                  ; Use DS to access DGroup

        MOV     BX,[_sftFCB]            ; 58270 move FCB count
        CMP     BX,0                    ; 58270 check if FCB is 0;
        JZ      short  lru65            ; 58270        return -1;

        mov     bp,_KeepCount           ; k = keepcount;
        XOR     AX,AX                   ; low = 0;
;
; If we've scanned the table n times, then we are done.
;
lru1:
        CMP     bp,0                    ; while (k--) {
        JZ      short lru75
        DEC     bp
;
; Set up for scan.
;
;   AX is the minimum age for consideration
;   BX is the minimum age found during the scan
;   SI is the position of the entry that corresponds to BX
;
        MOV     BX,-1                   ;     min = 0xffff;
        MOV     SI,BX                   ;     pos = 0xffff;
        MOV     ES,[_sftFCB]            ;     for (CX=FCBCount; CX>0; CX--)
        MOV     CX,ES:[sft_count]
        MOV     DI,SIZE sft
;
; Innermost loop.  If the current entry is free, then we are done.  Or, if the
; current entry is busy (indicating a previous aborted allocation), then we
; are done.  In both cases, we use the found entry.
;
lru2:
        cmp     es:[di].sf_ref_count,0
        jnz     short lru3

;
; The entry is usable without further scan.  Go and use it.
;
lru25:
        MOV     si,DI                   ;             pos = i;
        JMP     SHORT lru11             ;             goto got;
;
;       if the age < current minimum AND >= allowed minimum then
;           this entry becomes current minimum
;
; See if it less than the current minimum.
;
lru3: MOV       DX,ES:[DI].sf_OpenAge
        CMP     DX,AX                   ;         if (age[i] >= low &&
        JB      short lru5
        CMP     DX,BX
        JAE     short lru5              ;             age[i] < min) {
;
; entry is new minimum.   Remember his age.
;
        mov     bx,DX                   ;             min = age[i];
        mov     si,di                   ;             pos = i;
;
; End of loop.  Go back for more
;
lru5:
        add     di,SIZE sf_entry
        loop    short lru2              ;             }
;
; The scan is complete.   If we have successfully found a new minimum (pos != -1)
; set then threshold value to this new minimum + 1.  Otherwise, the scan is
; complete.  Go find LRU.
;
lru6:   cmp     si,-1                   ; position not -1?
        jz      short lru75             ; no, done with everything
        lea     ax,[bx+1]               ; set new threshold age
        jmp     short lru1              ; go and loop for more
lru65:  stc
        jmp     short   LRUDead         ;         return -1;
;
; Main loop is done.  We have AX being the age+1 of the nth oldest sharer
; entry.  We now make a second pass through to find the LRU entry
; that has age >= AX
;
lru75:
        mov     bx,-1                   ; min = 0xffff;
        mov     si,bx                   ; pos = 0xffff;
        MOV     ES,[_sftFCB]            ; for (CX=FCBCount; CX>0; CX--)
    ASSUME      ES:NOTHING
        MOV     CX,ES:[sft_count]
        MOV     DI,SIZE sft
;
; if age >= threshold then check for lru.
; Check age
;
lru8: cmp       es:[di].sf_OpenAge,ax
        jb      short lru9              ; age is before threshold, skip it
;
; Check LRU
;
lru86:
        cmp     es:[di].sf_LRU,bx       ; is LRU less than current LRU?
        jae     short lru9              ; no, skip this
        mov     si,di                   ; remember position
        mov     bx,es:[di].sf_LRU       ; remember new minimum LRU
;
; Done with this entry, go back for more.
;
lru9:
        add     di,size sf_entry
        loop    short lru8
;
; Scan is complete.  If we found NOTHING that satisfied us then we bomb
; out.  The conditions here are:
;
;   all entries are older than threshold
;
        cmp     si,-1                   ; if no one f
        jz      short lru65             ;     return -1;
lru11:
        TaskContext     DS              ; FSD_Close needs DS = TASKAREA
        mov     di,si

        push    bp
        mov     bp,CurrTCB
        mov     word ptr [bp].TCBThisSFT,di     ; set ThisSFT
        mov     word ptr [bp].TCBThisSFT+2,es
        pop     bp

; bugbug 23-jun-88 peterwu - lose date/time when LRU-ing FCB SFT
; We really should copy the date/time info from the FCB before we FSD_Close
; the SFT. However, there's no way to get the FCB. This probably won't
; affect any existing program.

;
; Repeat close until ref count is 0
;
LRUClose:
        mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT
        cmp     es:[di].sf_ref_count,0  ; is ref count still <> 0?
        jz      short LRUDone           ; nope, all done

        mov     bx,-1                   ; fcb close
        CALLFAR FSD_Close

;   We don't call SFLinkFree here, because we're dealing with an FCB

        jnc     LRUClose                ; no error => clean up
        cmp     ax,ERROR_INVALID_HANDLE
        jz      short LRUClose
        stc
        jmp     short LRUDead
LRUDone:
        xor     al,al
        call    BlastSFT                ; fill SFT with 0 (AL)
LRUDead:
        pop     ds
    ASSUME      DS:NOTHING
        popad
        mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT
        Assert  ISSFT,<ES,DI>,"LRUFCB return"
        jnc     short LRUX
        mov     ax,ERROR_FCB_UNAVAILABLE
LRUX:   RETC
EndProc LRUFCB

Break   <FCBRegen - generate a hard error>
;
;
;   if close operation, return.  else generate a hard error.
;
;   Inputs:     DS:SI point to FCB
;   Outputs:    Carry set unrecoverable error
;   Registers modified: ax

Procedure   FCBRegen,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING

        mov     ax,SysCallSfcn
        cmp     ah,FCB_CLOSE
        jz      short RegenDead         ; it's a close, go return simple error
        pusha
        push    ds
        push    es
        call    FCBHardErr              ; massive hard error.
        pop     es
        pop     ds
        popa
RegenDead:
        stc
        ret                             ; carry set
EndProc FCBRegen

;**     BlastSFT - ZAP an FCB SFT so it won't be reused by accident.
;
;   BlastSFT - fill SFT with garbage, but preserve fields we need to manage
;              the SFT, and to identify it as an SFT FCB.
;
;   Inputs: ES:DI point to SFT
;           AL has fill
;   Outputs: SFT is filled with nonsense
;           *FLAGS PRESERVED*
;   Registers modified: CX

Procedure   BlastSFT,<near,far16>
        mov     ah,0
        push    es:[di].sf_fsi.sfi_selfsfn      ; preserve sfn
        pushf
        SaveReg <AX,BX,DI>
        mov     ah,al
        mov     cx,ax
        shl     eax,16
        mov     ax,cx                           ; EAX has all ALs
        mov     cx,SIZE sf_entry
        mov     bx,di
        neg     bx          ; d1d0 = count to quad-align.
        and     bx,3        ; Is dst quad-aligned ?
        jz      bsft0       ; Yes.
        sub     cx,bx       ; cx must be > 5.
        xchg    bx,cx       ; cx = # of bytes to copy to make quad-aligned.
        rep stosb
        mov     cx,bx       ; cx = reminder of byte count.
bsft0:
        mov     bx,cx       ; set bx in case we took a jump to bf0.
        shr     cx,2        ; cx = reminder of dword count.
        and     bx,3        ; bx = reminder byte count after dword move.
        rep stosd
        mov     cx,bx       ; cx = Remaining byte count.
        rep stosb
        RestoreReg  <DI,BX,AX>
        popf
        pop     es:[di].sf_fsi.sfi_selfsfn      ; restore the sfn
        mov     es:[di].sf_flags,SF_FCB         ; clear all flags except st_fcb
        mov     es:[di].sf_flags2,cx            ; clear all flags2
        mov     es:[di].sf_fsi.sfi_tstamp,cl    ; clear tstamp
        mov     es:[di].sf_ref_count,cx         ; set ref count
        mov     es:[di].sf_LRU,cx               ; set lru
        mov     es:[di].sf_OpenAge,-1           ; Set open age
        mov     es:[di].sf_MFT,SF_MFT_NULL      ; no MFT entry
        mov     es:[di].sf_FSC.Offst,cx
        mov     es:[di].sf_FSC.Segmt,-1         ; invalid selector
        mov     es:[di].sf_plock,0              ; Make sure lock field is set ;### b731554
        RETC
EndProc BlastSFT

Break   <CheckFCB - see if the SFT pointed to by the FCB is still OK>

;   CheckFCB - examine an FCB and its contents to see if it needs to be
;   regenerated.
;
;   Inputs:     DS:SI point to FCB (not extended)
;               AL is SFT index
;   Outputs:    Carry Set - FCB needs to be regened
;               Carry clear - FCB is OK. ES:DI point to SFT
;   Registers modified: AX and BX

Procedure   CheckFCB,<near,far16>
        ASSUME  DS:NOTHING,ES:NOTHING

        FlatContext ES
        MOV     ES,[_sftFCB]
    ASSUME      ES:NOTHING
        CMP     BYTE PTR ES:[sft_count],AL
        JC      short BadSFT
        MOV     BL,SIZE sf_entry
        MUL     BL
        MOV     DI,SIZE sft
        ADD     DI,AX

        MOV     AX,CurrentPDB
        CMP     ES:[DI].sf_fsi.sfi_PDB,AX
        JNZ     short BadSFT            ; must match PDB

        push    bp
        mov     bp,CurrTCB
        mov     ax,[bp].TCBProc_ID
        pop     bp
        CMP     ES:[DI].sf_fsi.sfi_PID,AX
        JNZ     short BadSFT            ; must match process id

        CMP     ES:[DI].sf_ref_count,0
        JZ      short BadSFT            ; must also be in use

        MOV     AL,[SI].fcb_l_drive
        TEST    AL,FCBMASK              ; a special FCB?
        JZ      short BadSFT
;
; Since we are a special FCB, try NOT to use a bogus test instruction.
;
        CALLFAR ShChk
        JC      short BadSFT
;
        AND     AL,3Fh
        MOV     AH,BYTE PTR ES:[DI].sf_flags
        AND     AH,3Fh
        CMP     AH,AL
        jz      short cf1               ; carry is clear
BadSFT: STC
cf1:    RETP                            ; carry is clear

EndProc CheckFCB

Break   <SFTFromFCB - take a FCB and obtain a SFT from it>

;
;   SFTFromFCB - the workhorse of this compatability crap.  Check to see if
;       the SFT for the FCB is Good.  If so, make ThisSFT point to it.  If not
;       good, generate a harderror.

;   Inputs:     DS:SI point to FCB
;   Outputs:    ThisSFT point to appropriate SFT
;               Carry clear -> OK ES:DI -> SFT
;               Carry set -> error in ax
;   Registers modified: ES,DI, AX

Procedure   SFTFromFCB,<near,faronly,far16>
        ASSUME  DS:NOTHING,ES:NOTHING
        SaveReg <AX,BX>
        MOV     AL,[SI].fcb_sfn         ; set SFN for check
        call    CheckFCB
        RestoreReg  <BX,AX>
        push    bp
        mov     bp,CurrTCB
        mov     word ptr [bp].TCBThisSFT,di     ; set ThisSFT
        mov     word ptr [bp].TCBThisSFT+2,es
        pop     bp
        JNC     short SetSFT            ; no problems, just set ThisSFT

        DBPRT   80h,4h,<"FCB $x:$x does not match SFT $x:$x\n">,<DS,SI,ES,DI>

        call    FCBRegen                ; this always fails
        MOV     AX,ExtErr
        jmp short sffx

SetSFT:
        push    ax
        mov     di,CurrTCB
        mov     ax,TASKAREA:[di].TCBProc_ID
        les     di,TASKAREA:[di].TCBThisSFT
        mov     ES:[DI].sf_fsi.sfi_PID,ax
        mov     ax,CurrentPDB              ; set PDB
        mov     ES:[DI].sf_fsi.sfi_PDB,ax
        pop     ax
sffx:   RETP                            ; carry is clear
EndProc SFTFromFCB

Break   <FCBHardErr - generate INT 24 for hard errors on FCBS>

;
;   FCBHardErr - signal to a user app that he is trying to use an
;       unavailable FCB.
;
;   Inputs:     none.
;   Outputs:    none.
;   Registers modified: all
;

Procedure   FCBHardErr,<near,far16>
        ASSUME  DS:NOTHING,ES:NOTHING
        mov     ax,HEFCB
        mov     ExtErr,ERROR_FCB_UNAVAILABLE
        push    bp
        mov     bp,CurrTCB
        mov     [bp].TCBAllowed,ALLOWED_FAIL
        pop     bp

        CALLFAR HardError
        STC
        RETP
EndProc FCBHardErr


BREAK <FindSFT - find SFT that matches file being opened/created>
;
;   Inputs:     OpenBuf contains path
;   Outputs:    NC - no error
;                (ES:DI) -> SFT
;               CY - error
;                SFT not found
;
Procedure FindSFT,near,local

        CALL32  GetMFTSem

        TaskContext <DS>,si
        mov     si,TASKAREA:[CurrTCB]                                ;### PTR B720732
        mov     si,TASKAREA:[si].TCBpOpenBuf ; (DS:SI) -> pathname   ;### PTR B720732
;### PTR B720732        mov     si,TASKAREA:[ptda_pOpenBuf] ; (DS:SI) -> pathname
        xor     al,al                   ; don't create MFT
        CALL32  FNM                     ; find matching MFT
    ASSUME DS:NOTHING
        jc      short fsx               ; not found, go exit
;
; matching MFT found.  loop through SFTs to find one for this process.
; DS:BX -> MFT
;
IFDEF FSSTRICT
        cmp     [bx].mft_sptr.Segmt,0   ; any SFT connected?
        jnz     short fs10              ; yes, go continue
    InternalError <FindSFT: MFT not connected to any SFT>
fs10:
ENDIF
        les     di,[bx].mft_sptr        ; (ES:DI) -> first SFT
fs20:   test    es:[di].sf_flags,SF_FCB ; FCB SFT?
        jz      short fs30              ; no, go get pointer to next SFT

; found FCB SFT.  now check for correct PDB and PID.

        cmp     ES:[DI].sf_ref_count,0  ; someone using it?
        stc
        je      short fsx               ; no, go return

        MOV     AX,CurrentPDB
        CMP     ES:[DI].sf_fsi.sfi_PDB,AX ; same PDB?
        JNZ     short fs30              ; no, go get pointer to next SFT

        push    bp
        mov     bp,CurrTCB
        mov     ax,[bp].TCBProc_ID
        pop     bp
        CMP     ES:[DI].sf_fsi.sfi_PID,AX ; same PID?
        JZ      short fsx               ; yes, go return

fs30:   cmp     es:[di].sf_chain.Segmt,0 ; end of SFT list?
        stc
        je      short fsx               ; yes, go return
        les     di,es:[di].sf_chain     ; no, get pointer to next SFT
        jmp short fs20

fsx:    CALL32  FreeMFTSem
        ret

EndProc FindSFT

FSSWAPCODE ends

        FARCODE LOW
CODE    SEGMENT
        ASSUME  SS:TASKAREA


; Defintions for FCBOp flags

Random  =   2                           ; random operation
FCBRead =   4                           ; doing a read
Block   =   8                           ; doing a block I/O



Break <GetRR - return the random record field in DX:AX>

;
;   GetRR - correctly load DX:AX with the random record field (3 or 4 bytes)
;       from the FCB pointed to by DS:SI
;
;   Inputs:     DS:SI point to an FCB
;               BX has record size
;   Outputs:    DX:AX contain the contents of the random record field
;   Registers modified: none

Procedure   GetRR,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        MOV     AX,WORD PTR [SI.fcb_RR] ; get low order part
        MOV     DX,WORD PTR [SI.fcb_RR+2]       ; get high order part
        CMP     BX,64                   ; ignore MSB of RR if recsiz > 64
        JB      GetRRBye
        XOR     DH,DH
GetRRBye:
        ret
EndProc GetRR

Break <GetExtent - retrieve next location for sequential IO>

;
;   GetExtent - Construct the next record to perform I/O from the EXTENT and
;       NR fields in the FCB.
;
;   Inputs:     DS:SI - point to FCB
;   Outputs:    DX:AX contain the contents of the random record field
;   Registers modified: none

Procedure   GetExtent,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        MOV     AL,[SI.fcb_NR]          ; get low order piece
        MOV     DX,[SI.fcb_EXTENT]      ; get high order piece
        SHL     AL,1
        SHR     DX,1
        RCR     AL,1                    ; move low order bit of DL to high order of AH
        MOV     AH,DL
        MOV     DL,DH
        XOR     DH,DH
        ret
EndProc GetExtent

Break <SetExtent - update the extent/NR field>

;
;   SetExtent - change the position of an FCB by filling in the extent/NR
;       fields
;
;   Inputs:     DS:SI point to FCB
;               DX:AX is a record location in file
;   Outputs:    Extent/NR fields are filled in
;   Registers modified: CX

Procedure SetExtent,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        SaveReg <AX,DX>
        MOV     CX,AX
        AND     AL,7FH                  ; next rec field
        MOV     [SI.fcb_NR],AL
        AND     CL,80H                  ; save upper bit
        SHL     CX,1
        RCL     DX,1                    ; move high bit of CX to low bit of DX
        MOV     AL,CH
        MOV     AH,DL
        MOV     [SI.fcb_EXTENT],AX      ; all done
        RestoreReg  <DX,AX>
        ret
EndProc SetExtent

Break <GetExtended - find FCB in potential extended fcb>

;***    GetExtended - Make DS:SI point to FCB from DS:DX
;
;       ENTRY   DS:DX point to a possible extended FCB
;       EXIT    DS:SI point to the FCB part
;               zeroflag set if not extended fcb
;       USES    SI

Procedure   GetExtended,<near,faronly,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        MOV     SI,DX                   ; point to Something
        CMP     BYTE PTR DS:[SI],-1     ; look for extention
        JNZ     short GetBye            ; not there
        ADD     SI,7                    ; point to FCB
GetBye:
        CMP     SI,DX                   ; set condition codes
        RETP
EndProc GetExtended

Break <GetRecSize - return in BX the FCB record size>

;
;   GetRecSize - return in BX the record size from the FCB at DS:SI
;
;   Inputs:     DS:SI point to a non-extended FCB
;   Outputs:    BX contains the record size
;   Registers modified: None

Procedure   GetRecSize,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        MOV     BX,[SI.fcb_RECSIZ]      ; get his record size
        OR      BX,BX                   ; is it nul?
        JNZ     short grsx
        MOV     BX,128                  ; use default size
        MOV     [SI.fcb_RECSIZ],BX      ; stuff it back
grsx:   RET
EndProc GetRecSize


BREAK <FCBIO - do internal FCB I/O>

;
;   FCBIO - look at FCBOp and merge all FCB operations into a single routine.
;
;   Inputs:     AL    flags which operations need to be performed
;               DS:DX point to FCB
;               CX    may have count of number of records to xfer
;               EBX   pvsf (client register frame)
;
;   Outputs:    if CY = 0, no errors, AX = 0
;               if CY = 1, AX = error code
;               VSF(DL) = FCBErr code
;               VSF(CX) = records xfer'ed
;
;   Registers modified: all
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

FEOF    EQU     1
FTRIM   EQU     2

Procedure   FCBIO,far16

LocalVar    pvsf,DWORD
LocalVar    FCBErr,BYTE
LocalVar    cRec,WORD
LocalVar    RecPos,DWORD
LocalVar    RecSize,WORD
LocalVar    bPos,DWORD
LocalVar    cByte,WORD
LocalVar    cResult,WORD
LocalVar    cRecRes,WORD
LocalVar    usError,WORD
LocalVar    FCBOp,BYTE

        EnterProc <ebx>
        MOV     usError,NO_ERROR
        MOV     FCBOp,AL
        MOV     FCBErr,0                ;   FCBErr = 0;
        call    GetExtended             ;   FCB = GetExtended ();
        TEST    FCBOp,BLOCK             ;   if ((OP&BLOCK) == 0)
        JNZ     GetPos
        MOV     CX,1                    ;       cRec = 1;
GetPos:
        MOV     cRec,CX                 ;*Tail coalesce
        call    GetExtent               ;   RecPos = GetExtent ();
        call    GetRecSize              ;   RecSize = GetRecSize ();
        MOV     RecSize,BX
        TEST    FCBOp,RANDOM            ;   if ((OP&RANDOM) <> 0)
        JZ      short GetRec
        call    GetRR                   ;       RecPos = GetRR ();
GetRec:
        MOV     RecPosl,AX              ;*Tail coalesce
        MOV     RecPosh,DX
        call    SetExtent               ;   SetExtent (RecPos);

;;; BUGBUG - 32 bit mul can be done ?
        MOV     AX,RecPosh              ;   bPos = RecPos * RecSize;
        MUL     BX
        MOV     DI,AX
        MOV     AX,RecPosl
        MUL     BX
        ADD     DX,DI
        MOV     bPosl,AX
        MOV     bPosh,DX
;;; END BUGBUG - nagara

        MOV     AX,cRec                 ;   cByte = cRec * RecSize;
        MUL     BX
        MOV     cByte,AX
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        ADD     AX,WORD PTR [bp].TCBDTAddr      ;   if (cByte+DTA > 64K) {
        UnSetBP_CurrTCB
                                        ;72417 end
        ADC     DX,0
        JZ      short DoOper
        MOV     FCBErr,FTRIM            ;       FCBErr = FTRIM;
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     AX,WORD PTR [bp].TCBDTAddr ;       cRec = (64K-DTA)/RecSize;
        UnSetBP_CurrTCB
                                        ;72417 end
        NEG     AX
        JNZ     short DoDiv
        DEC     AX
DoDiv:
        XOR     DX,DX
        DIV     BX
        MOV     cRec,AX
        MUL     BX                      ;       cByte = cRec * RecSize;
        MOV     cByte,AX                ;       }
DoOper:
        XOR     BX,BX
        MOV     cResult,BX              ;   cResult = 0;
        CMP     cByte,BX                ;   if (cByte <> 0 ||
        JNZ     short DoGetExt
        TEST    FCBErr,FTRIM            ;       (FCBErr&FTRIM) == 0) {
        LJNZ    SkipOp

DoGetExt:
        CALLFAR SFTFromFCB              ;       if (!SFTFromFCB (SFT,FCB))
        JNC     short ContinueOp
FCBDeath:
        MOV     usError,AX
        MOV     cRecRes,0               ; no bytes transferred
        MOV     FCBErr,FEOF             ;           return FTRIM;
        JMP     FCBSave                 ; bam!
ContinueOp:
        Assert  ISSFT,<ES,DI>,"ContinueOP"
        MOV     AX,WORD PTR [SI].fcb_FILSIZ
        MOV     WORD PTR ES:[DI].sf_fsi.sfi_size,AX
        MOV     AX,WORD PTR [SI].fcb_FILSIZ+2
        MOV     WORD PTR ES:[DI].sf_fsi.sfi_size+2,AX
        push    bp
        mov     bp,CurrTCB              ; copy DTA to DMAAdd
                                        ;72417 moved from PTDA to TCB
        mov     ax,word ptr [bp].TCBDTAddr
        mov     word ptr [bp].TCBDMAAdd,ax
        mov     ax,word ptr [bp].TCBDTAddr+2
                                        ;72417 end
        mov     word ptr [bp].TCBDMAAdd+2,ax
        pop     bp
        MOV     AX,bPosl
        MOV     DX,bPosh
        MOV     WORD PTR ES:[DI.sf_fsi.sfi_position],AX
        XCHG    WORD PTR ES:[DI.sf_fsi.sfi_position+2],DX
        PUSH    DX                      ; save away Open age.
        MOV     CX,cByte                ;       cResult =
        MOVFAROFFSET DI,FSD_Read,DOSHIGH2CODE      ; *(OP&FCBREAD ? FSD_Read
        TEST    FCBOp,FCBREAD           ;                : FSD_Write)(cRec);
        JNZ     short DoContext
        MOVFAROFFSET DI,FSD_Write,DOSHIGH2CODE
DoContext:
        SaveReg <BP,DS,SI>
        TaskContext DS
        callhireg DOSHIGH2CODE,DI       ; Call FSD_Read or FSD_Write
        RestoreReg  <SI,DS,BP>
        JC      short FCBDeath
    ASSUME      DS:NOTHING
        MOV     cResult,CX
        CALLFAR SaveFCBInfo             ;       SaveFCBInfo (FCB);
        Assert  ISSFT,<ES,DI>,"FCBIO/SaveFCBInfo"
.errnz       (sf_fsi.sfi_position+2)-sf_OpenAge
        POP     WORD PTR ES:[DI].sf_fsi.sfi_position+2  ; restore open age
        MOV     AX,WORD PTR ES:[DI].sf_fsi.sfi_size
        MOV     WORD PTR [SI].fcb_FILSIZ,AX
        MOV     AX,WORD PTR ES:[DI].sf_fsi.sfi_size+2
        MOV     WORD PTR [SI].fcb_FILSIZ+2,AX
                                        ;       }
SkipOp: MOV     AX,cResult              ;   cRecRes = cResult / RecSize;
        XOR     DX,DX
        DIV     RecSize
        MOV     cRecRes,AX
        ADD     RecPosl,AX              ;   RecPos += cRecResult;
        ADC     RecPosh,0
;
; If we have not gotten the expected number of records, we signal an EOF
; condition.  On input, this is EOF.  On output this is usually disk full.
; BUT...  Under 2.0 and before, all device output IGNORED this condition.  So
; do we.
;
        CMP     AX,cRec                 ;   if (cRecRes <> cRec)
        JZ      short TryBlank
        MOV     FCBErr,FEOF             ;       FCBErr = FEOF;
TryBlank:                               ;
        OR      DX,DX                   ;   if (cResult%RecSize <> 0) {
        JZ      short SetExt
        ADD     RecPosl,1               ;       RecPos++;
        ADC     RecPosh,0
        TEST    FCBOp,FCBREAD           ;       if(OP&FCBREAD) <> 0) {
        JZ      short SetExt
        INC     cRecRes                 ;       cRecRes++;
        MOV     FCBErr,FTRIM + FEOF     ;       FCBErr = FTRIM | FEOF;
        MOV     CX,RecSize              ;       Blank (RecSize-cResult%RecSize,
        SUB     CX,DX                   ;              DTA+cResult);
        XOR     AL,AL

                                        ;72417 moved from PTDA to TCB
fcbio6: SetBP_CurrTCB
        MOV     ES,[bp].TCBDTAddr.Segmt ; (ES) = selector
        UnSetBP_CurrTCB
fcbio7: SetBP_CurrTCB
        MOV     DI,[bp].TCBDTAddr.Offst ; (ES:DI) = DTA buffer
        UnSetBP_CurrTCB
                                        ;72417 end

        ADD     DI,cResult
        REP     STOSB                   ;   }   }
SetExt:
        MOV     DX,RecPosh
        MOV     AX,RecPosl
        TEST    FCBOp,RANDOM            ;   if ((OP&Random) == 0 ||
        JZ      short DoSetExt
        TEST    FCBOp,BLOCK             ;       (OP&BLOCK) <> 0)
        JZ      short TrySetRR
DoSetExt:
        call    SetExtent               ;       SetExtent (RecPos, FCB);
TrySetRR:
        TEST    FCBOp,BLOCK             ;   if ((op&BLOCK) <> 0)
        JZ      short TryReturn
        MOV     WORD PTR [SI.fcb_RR],AX ;       FCB->RR = RecPos;
        MOV     BYTE PTR [SI.fcb_RR+2],DL
        CMP     [SI.fcb_RECSIZ],64
        JAE     short TryReturn
        MOV     [SI+fcb_RR+2+1],DH      ; Set 4th byte only if record size < 64
TryReturn:
        TEST    FCBOp,FCBREAD           ;   if (!(FCBOp & FCBREAD)) {
        JNZ     short FCBSave
        call    Date16
        MOV     [SI].fcb_FDATE,AX       ;       FCB->FTime = time;
        MOV     [SI].fcb_FTIME,DX       ;       }
FCBSave:
        FlatContext <ds>,ebx
        mov     ebx,[pvsf]              ; (DS:EBX) = pvsf
        TEST    FCBOp,BLOCK             ; if ((op&BLOCK) <> 0)
        JZ      short DoReturn
        MOV     CX,cRecRes              ;      VSF(CX) = cRecRes;
        mov     [ebx].vsf_ecx.loword,cx
DoReturn:
        mov     dl,FCBErr               ; VSF(DL) = FCBErr;
        mov     [ebx].vsf_edx.lobyte,dl
        mov     ax,usError              ; return (usError);
        cmp     ax,1                    ; CY = 1 if AX == 0
        cmc                             ; CY = 0 if AX == 0
        LeaveProc
        RETP
EndProc FCBIO

CODE    ENDS



        FARCODE HIGH4
FSSWAPCODE SEGMENT
        ASSUME  SS:TASKAREA

;***    w_FCB_Delete - fcb delete worker
;
;       ENTRY   DS:DX -> FCB
;
;       EXIT    directory entries matching FCB are deleted
;               Carry clear (no error)
;               Carry set
;                   (AX) = error code
;
;       PSEUDOCODE
;          if (label on ifs drive) {
;             w_FCB_VolID
;             return
;          }
;          GetTempBuffer()
;          TransFCB
;          if (error)
;             return
;          if (device)
;             return error
;          if (metas in name) {
;             Do_3xSearch_First
;             if (error)
;                return
;    wfd1:    copy found entry to wfp_start
;             CheckThisDevice
;             if (device)
;                return error
;          }
;          FSD_Delete
;          if (metas in name) {
;             Do_3xSearch_Next
;             if (error)
;                return
;             goto wfd1
;          }
;
        ASSUME DS:NOTHING,ES:NOTHING
Procedure w_FCB_Delete,far16
LocalVar    wfd_deleted,WORD            ; any files deleted?
LocalVar    wfd_cMeta,WORD              ; metas in name?
LocalVar    wfd_pTempBuf,DWORD          ; Pointer to temp buffer
LocalVar    wfd_searchbuf,SRCHBUFSIZE+1
LocalVar    wfd_dummycds,<SIZE curdir>
LocalVar    wfd_openbuf,PMPATHLEN
LocalVar    wfd_dummy,WORD              ; KLUDGE: see gshare.asm
LocalVar    wfd_EnvSeg, WORD            ; PCR UNDELETE
LocalVar    wfd_IsVol, WORD             ; PCR UNDELETE
LocalVar    wfd_fcboff, WORD            ; PCR UNDELETE
LocalVar    wfd_fcbseg, WORD            ; PCR UNDELETE
LocalVar    wfd_Path,PMPATHLEN          ; PCR UNDELETE

        EnterProc <0,-1>                ; no files deleted
        push    es                                               ;PCR UNDELETE
        pop     wfd_EnvSeg              ; Save environment seg   ;PCR UNDELETE


        mov     wfd_fcboff, dx          ;                        ;PCR UNDELETE
        mov     wfd_fcbseg, ds          ; save fcb address       ;PCR UNDELETE
wfd_begin:                                                       ;PCR UNDELETE

        mov     wfd_IsVol, 0            ; init volume label flag ;PCR UNDELETE
        mov     di, dx                  ;                        ;PCR UNDELETE
        cmp     byte ptr ds:[di+6], 8   ; volume label?          ;PCR UNDELETE
        jne     wfd03                   ; no, continue           ;PCR UNDELETE
        mov     wfd_IsVol, 1            ; set volume label flag  ;PCR UNDELETE
wfd03:                                                           ;PCR UNDELETE

        lea     di,wfd_searchbuf
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     TASKAREA:[bp].TCB_pSrchBuf,di ; Initialize ptda_pSrchBuf
        UnSetBP_CurrTCB
                                        ;72417 end
        lea     di,wfd_dummycds
        push    bx                                      ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,di       ; Initialize TCBpTmpCDS ;### PTR B720732
        lea     di,wfd_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,di            ; Initialize TCBpTmpCDS ;### PTR B720732
        pop     bx                                      ;### PTR B720732

        CALLFAR ChkIFSVol               ; check if a label delete on ifs volume.
        jc      wfd90                   ; any error? yes, go report it.
        jz      short wfd05             ; no, is it label delete on ifs volume?
                                        ; no, go process it normally.

        mov     ax,fvd_flag_delete      ; yes, delete the
        CALLFAR w_FCB_VolId             ; volume id.
        jmp     wfd90                   ; error or not, go tell caller.

; Handle normal case (not a label on IFS vol).

wfd05:  CALLFAR GetTempBuffer
        mov     wfd_pTempBuf.Segmt,es   ; Save away the pointer
        mov     wfd_pTempBuf.Offst,di

        lea     di,wfd_openbuf
        push    bp
        CALLFAR TransFCB                ; convert FCB to path
        pop     bp
        LJC     wfd50                   ; error, go return
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; meta chars present?
        UnSetBP_CurrTCB
                                        ;72417 end
        jne     wfd10                   ; yes, go call FindFirst
        lea     si,wfd_openbuf          ; PCR X
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_LOCDEV OR PNType_PSDEV ; device path?
        UnSetBP_CurrTCB
                                        ;72417 end
        jz      short wfd20             ; no, go do delete
        jmp     wfd55                   ; yes, go error exit     ;PCR UNDELETE (short was out of range)

wfd10:  CALLFAR Do_3xSearch_First       ; search for file
        LJC     wfd50

        CALLFAR RlseBuffr               ; release buffer

; copy found name to end of wfp_start and put in asciiz form

wfd15:  TaskContext <DS>,si
        lea     si,wfd_searchbuf
        lea     si,[si].fbs_dir_entry.dir_name ; DS:SI -> entry (FCB style name )
        CALLFAR Byte13_PackName ; Transfer name from entry to ASCIIZ tail
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start ; (DS:SI) -> source name
                                        ;72417 end
        les     di,wfd_pTempBuf         ; (ES:DI) -> temp buffer
        CALLFAR CheckThisDevice
        LJNC    wfd55                   ; device, return error   ;PCR UNDELETE (short jump out of range)

        mov     wfd_cMeta,0             ; remember metas found

                                        ;72417 moved from PTDA to TCB
wfd20:  SetBP_CurrTCB
        mov     [bp].TCBcMeta,-1
        UnSetBP_CurrTCB
                                        ;72417 end

        cmp     wfd_IsVol, 1             ; volume label?         ;PCR UNDELETE
        je      wfd23                    ; yes, do fsd delete    ;PCR UNDELETE

        cmp     wfd_EnvSeg, 0            ; PCR UNDELETE AA05005
        je      wfd23                    ; PCR UNDELETE AA05005

        push    wfd_EnvSeg               ;                       ;PCR UNDELETE
        pop     es                       ; ES:0 = ENVIRONMENT    ;PCR UNDELETE

        push    dx                       ;                       ;PCR UNDELETE
        push    bx                       ;                       ;PCR UNDELETE
        push    di                       ; save regs             ;PCR UNDELETE
        lea     di, wfd_Path             ; get buffer address    ;PCR UNDELETE
        mov     bx, 0                    ; initialize index      ;PCR UNDELETE
wfd21:  mov     dl, byte ptr ss:[si][bx] ; get byte from path    ;PCR UNDELETE
        mov     byte ptr ss:[di][bx], dl ; copy to local buffer  ;PCR UNDELETE
        inc     bx                       ; point to next byte    ;PCR UNDELETE
        cmp     dl, 0                    ; null terminator?      ;PCR UNDELETE
        jne     wfd21                    ; no, copy another byte ;PCR UNDELETE
        lea     dx, wfd_Path             ; ds:dx = path          ;PCR UNDELETE
                                         ;72417 moved from PTDA to TCB
        mov     bx,CurrTCB
        push    TASKAREA:[bx].TCBWFP_Start ; save filename start   ; @59428
        push    TASKAREA:[bx].TCB_pSrchBuf ; save search buf addr  ; @59428
        push    bx
        CALLFAR w_StoreFile3x            ; store/delete file     ;PCR UNDELETE
        pop     bx
        pop     TASKAREA:[bx].TCB_pSrchBuf ; save search buf addr  ; @59428
        pop     TASKAREA:[bx].TCBWFP_Start ; save filename start   ; @59428
                                         ;72417 end
        pop     di                       ;                       ;PCR UNDELETE
        pop     bx                       ;                       ;PCR UNDELETE
        pop     dx                       ; restore regs          ;PCR UNDELETE
        jmp     short wfd25              ; continue              ;PCR UNDELETE
wfd23:                                   ;                       ;PCR UNDELETE
        CALLFAR FSD_Delete               ; delete volume label   ;PCR UNDELETE
wfd25:                                   ;                       ;PCR UNDELETE
        jnc     short wfd30              ; no error
;
; The following error processing is in support of compatibility.  When sharing
; errors and errors caused by attempted deletes of read-only files are
; encountered, the delete should continue on any other files which match the
; filename.  DOS_Delete sets bx=1 when it encounters a read-only file and
; returns ERROR_ACCESS_DENIED.
;

        cmp     ax,ERROR_SHARING_VIOLATION ; sharing error?
        je      short wfd35             ; yes, go continue deletion
        cmp     ax,ERROR_ACCESS_DENIED  ; read-only file or dir?
        stc
        jne     short wfd50             ; no, go return error
        cmp     bx,1                    ; read-only file?
        je      short wfd35             ; yes, go continue deletion
        stc                             ; no, go return error
        jmp short wfd35
    ASSUME DS:NOTHING

wfd30:  mov     wfd_deleted,1           ; file deleted
wfd35:  cmp     wfd_cMeta,-1            ; meta chars present?
        je      short wfd42             ; no, go return (no error)

        TaskContext <DS>,si             ;

        mov     si,CurrTCB              ;
        lea     si,[si].TCBThisFSC      ;(DS:SI) -> FSC address
        CALLFAR Do_3xSearch_Next        ;search for file
        jc      short wfd40             ;no more files, go return
        CALLFAR RlseBuffr               ;release buffer

        jmp     wfd15                   ;go delete file

wfd40:                                  ;#54785
        cmp     ax,ERROR_NO_MORE_FILES  ; end of search?  ;#54785
        stc                                               ;#54785
        JNE     wfd42                   ; no, continue    ;#54785
;#54785 wfd40:  xor     ax,ax                             ;#54785
        pushf                                             ;#54785
        push    ds                                        ;152882
        TaskContext <ES,DS>,di,si                                 ;#54785
        mov     si,CurrTCB              ;                         ;#54785
        lds     si,[si].TCBThisFSC      ;(DS:SI) -> FSC address   ;#54785
        lea     di,wfd_searchbuf        ; ES:DI -> Search buf     ;#54785
        CALLFAR FFCB3xClose                                       ;#54785
        pop     ds                                        ;152882
        popf                                                      ;#54785

wfd42:  xor     ax,ax                   ; clear error code #54785
        cmp     wfd_deleted,1           ; any files deleted
        je      short wfd50
wfd55:  mov     ax,ERROR_ACCESS_DENIED
        stc

wfd50:  les     di,wfd_pTempBuf         ; ES:DI -> temp buffer
        pushf
        CALLFAR FreeTempBuffer          ; Free the temp buffer
        popf                            ; Restore any error indications
;### PTR B720732 wfd90: mov     SS:[ptda_pCDS],0        ; Clear ptda_pCDS
wfd90:
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     SS:[bp].TCB_pSrchBuf,0  ; Clear ptda_pSrchBuf
        UnSetBP_CurrTCB
                                        ;72417 end
;### PTR B720732        mov     SS:[ptda_pOpenBuf],0    ; Clear ptda_pOpenBuf
        LeaveProc

        RETP

EndProc w_FCB_Delete

;**     w_FCB_Rename - fcb rename worker
;
;       ENTRY   DS:DX point to an FCB.  The normal name field is the source
;                   name of the files to be renamed.  Starting at offset 11h
;                   in the FCB is the destination name.
;       EXIT    if 'NC' - success
;               if 'C' - error
;                   (AX) = error code
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       PSEUDOCODE
;          if (label on ifs drive) {
;             w_FCB_VolID
;             return
;          }
;          GetTempBuffer()
;          TransFCB(dest)
;          if (error)
;             return
;          TransFCB(source)
;          if (error)
;             return
;          if (source is device)
;             return error
;          if (metas in name) {
;             Do_3xSearch_First
;             if (error)
;                return
;    wfr1:    copy found entry to wfp_start
;             CheckThisDevice(source)
;             if (device)
;                return error
;             get ptrs to last components of source and edit strings
;             w_EditName
;             CheckThisDevice(dest)
;             if (device)
;                return error
;          }
;          FSD_Move
;          if (metas in name) {
;             Do_3xSearch_Next
;             if (error)
;                return
;             goto wfr1
;          }
;
Procedure w_FCB_Rename,far16
    assume ds:nothing,es:nothing

LocalVar dfr_pTempBuf,DWORD             ; Pointer to temp buffer
LocalVar dfr_lpFCB,DWORD                ; pointer to FCB
LocalVar dfr_dcde,WORD                  ; curr_dir_end for destination
LocalVar dfr_savebuf,SRCHBUFSIZE+1      ; temporary searchbuf (DWORD align)
LocalVar dfr_metas,BYTE                 ; metas in source if != FF
LocalVar dfr_edit,BYTE                  ; metas in dest if != FF
LocalVar dfr_destptr,WORD               ; offset of last component in dest
LocalVar dfr_srcptr,WORD                ; offset of last component in source
LocalVar dfr_buflen,WORD                ; length of space for last component of dest
LocalVar dfr_editbuf,MAXPNAME           ; buffer for edit string
LocalVar dfr_renbuf,PMPATHLEN           ; temp buffer for Unpack
LocalVar dfr_dummy1,WORD                ; KLUDGE: see gshare.asm
LocalVar dfr_searchbuf,SRCHBUFSIZE+1
LocalVar dfr_dummycds,<SIZE curdir>
LocalVar dfr_openbuf,PMPATHLEN
LocalVar dfr_dummy2,WORD                ; KLUDGE: see gshare.asm

        EnterProc
                                        ;72417 moved from PTDA to TCB
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        lea     ax,dfr_searchbuf
        mov     SS:[bx].TCB_pSrchBuf,ax ; Initialize ptda_pSrchBuf
                                        ;72417 end
        lea     ax,dfr_dummycds
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,ax       ; Initialize TCBpTmpCDS ;### PTR B720732
;       mov     TASKAREA:[bx].TCBpTmpCDS.Segmt,ss       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     SS:[ptda_pCDS],ax       ; Initialize ptda_pCDS
        lea     ax,dfr_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,ax            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     SS:[ptda_pOpenBuf],ax ; Initialize ptda_pOpenBuf

        CALLFAR ChkIFSVol               ; check if a label rename on ifs volume.
        jc      dfr90                   ; any error? yes, go report it.
        jz      short dfr05             ; no, is it label rename on ifs volume?
                                        ; no, go process it normally.

        mov     ax,fvd_flag_rename      ; yes, rename the
        CALLFAR w_FCB_VolId             ; volume id.
        jmp     dfr90                   ; error or not, go tell caller.

dfr05:  CALLFAR GetTempBuffer           ; ES:DI -> temp buffer
        mov     dfr_pTempBuf.Segmt,es   ; Save away the pointer
        mov     dfr_pTempBuf.Offst,di

        CALLFAR GetExtended             ; ds:si -> real FCB
        mov     dfr_lpFCB.Segmt,ds
        mov     dfr_lpFCB.Offst,dx      ; save pointer to original FCB
        SaveReg <si>                    ; save offset to real FCB
        mov     al,[si]                 ; al = source drive #
        add     si,10h                  ; ds:si -> destination drive and name
        xchg    [si],al                 ; set destination drive = source drive
        SaveReg <ax>                    ; save original dest. drive#, and

; call TransFCB for destination

        lea     di,dfr_renbuf           ; ss:di -> buffer for TransFCB
        mov     dx,si                   ; ds:dx -> drive and name
        SaveReg <bp>
        CALLFAR TransFCB
        RestoreReg <bp>

; restore original dest. drive#

        lds     dx,dfr_lpFCB            ; ds:dx -> original FCB
        RestoreReg <ax>                 ; al = original destination drive#
        RestoreReg <si>                 ; ds:si -> real FCB

        mov     [si+10h],al             ; restore original dest. drive#

        LJC     dfr50                   ; TransFCB error, go error exit

; save info about destination

                                        ;72417 moved from PTDA to TCB
        SaveReg <ax>                    ; 160113 - use AX instead of stack
        SetBP_CurrTCB
        push    [bp].TCBWFP_Start
        pop     [bp].TCBRen_WFP         ; save well-form destination path
;        push    [bp].TCBCurr_Dir_End    ; (160113 - change to mov AX)
;        pop     dfr_dcde                ; save Curr_Dir_End for destination
                                         ;  (160113 - do after BP retored)
        mov     ax,[bp].TCBCurr_Dir_End ; put the Dir_End in AX for later (160113)

        mov     cl,[bp].TCBcMeta
        UnSetBP_CurrTCB
        mov     dfr_dcde,ax             ; save Curr_Dir_End for destination (160113)
        RestoreReg <ax>                 ; 160113
                                        ;72417 end
        mov     dfr_edit,cl
        inc     cl                      ; meta chars present?
        jnz     short dfr10             ; yes, skip device check
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_LOCDEV OR PNType_PSDEV ; device path?
        UnSetBP_CurrTCB
                                        ;72417 end
        LJNZ    dfr35                   ; yes, go error exit


; call TransFCB for source

dfr10:  lea     di,dfr_openbuf           ; ss:di -> buffer for TransFCB

        SaveReg <bp>
        CALLFAR TransFCB
        RestoreReg <bp>
        LJC     dfr50

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     cl,[bp].TCBcMeta
        UnSetBP_CurrTCB
                                        ;72417 end
        mov     dfr_metas,cl            ; dfr_metas == FF if metas not seen
        inc     cl                      ; meta chars present?
        jnz     short dfr15             ; yes, call FindFirst
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_LOCDEV OR PNType_PSDEV ; device path?
        UnSetBP_CurrTCB
                                        ;72417 end
        LJNZ    dfr35                   ; yes, go error exit
        cmp     dfr_edit,-1             ; metas in edit string?
        LJZ     dfr25                   ; no, go call FSD_Move

; get ptr to last component of source and edit string

dfr15:  TaskContext <DS,ES>,di
                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        mov     di,TASKAREA:[di].TCBRen_WFP ; (ES:DI) -> dest
                                        ;72417 end
        mov     bx,di
        add     bx,2                    ; skip "d:" in the WFP
        xor     al,al                   ; (AL) = 0
        mov     cx,PMPATHLEN            ; (CX) = 260
        repne   scasb                   ; find end of string
        dec     di                      ; (ES:DI) -> end of string
        CALLFAR SkipBack
        inc     di                      ; (ES:DI) -> last component
        mov     dfr_destptr,di          ; save offset of last component of dest
        mov     si,di                   ; (DS:SI) -> last component
        mov     cx,MAXPNAME SHR 2
        lea     di,dfr_editbuf
        rep     movsd                   ; save edit string
        .errnz  MAXPNAME AND 2
        movsb
        .erre   MAXPNAME AND 1

                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        mov     di,TASKAREA:[di].TCBWFP_Start ; (ES:DI) -> source
                                        ;72417 end
        mov     bx,di
        add     bx,2                    ; skip "d:" in the WFP
        xor     al,al                   ; (AL) = 0
        mov     cx,PMPATHLEN            ; (CX) = 260
        repne   scasb                   ; find end of string
        dec     di                      ; (ES:DI) -> end of string
        CALLFAR SkipBack
        inc     di                      ; (ES:DI) -> end of last component
        mov     dfr_srcptr,di           ; save offset of last component of source

        mov     ax,dfr_destptr          ; (AX) = offset of last component in dest
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        sub     ax,[bp].TCBRen_WFP      ; (AX) = length of all but last component
        UnSetBP_CurrTCB
                                        ;72417 end
        mov     cx,PMPATHLEN            ; (CX) = length of entire buffer
        sub     cx,ax                   ; (CX) = length of component space
        mov     dfr_buflen,cx           ; save length of component space
;
; Decide what meta processing is necessary.  we know there are metas in at least
; one of the source or edit string because we routed straight to FSD_Move for
; the no meta case above.
;
        cmp     dfr_metas,-1            ; meta chars present in source?
        je      short dfr22             ; no, so metas in dest.  go call w_EditName

        CALLFAR Do_3xSearch_First       ; search for file
        LJC     dfr50                   ; error, go return
        CALLFAR RlseBuffr               ; release buffer

; save searchbuf contents

dfr20:  TaskContext <DS,ES>,si
        lea     di,dfr_savebuf          ; (ES:DI) -> dfr_savebuf
        lea     si,dfr_searchbuf        ; (DS:SI) -> dfr_searchbuf
        mov     cx,(SRCHBUFSIZE+1)/4
        .errnz ((SRCHBUFSIZE + 1) AND 3)
        rep     movsd                   ; save searchbuf contents

; copy found name to end of wfp_start and put in asciiz form

        lea     si,dfr_searchbuf
        lea     si,[si].fbs_dir_entry.dir_name ; DS:SI -> entry (FCB style name )
        CALLFAR Byte13_PackName ; Transfer name from entry to ASCIIZ tail
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start ; (DS:SI) -> source name
                                        ;72417 end
        les     di,dfr_pTempBuf         ; (ES:DI) -> temp buffer
        CALLFAR CheckThisDevice
        LJNC    dfr35                   ; device, go return error

; get ptr to last component of source and edit string

        TaskContext <DS,ES>,di
dfr22:  lea     si,dfr_editbuf          ; (DS:SI) -> beginning of last component in dest
        mov     di,dfr_srcptr           ; (ES:DI) -> beginning of last component in source
        mov     cx,dfr_buflen           ; (CX) = length of component space
        mov     ax,es
        mov     dx,dfr_destptr          ; (AX:DX) -> dest buf
;
; create the destination name and store in place of the edit string in Ren_WFP.
;
        mov     bx,EDITLEVEL1           ; (BX) = edit level
        CALLFAR w_EditName
        jc      short dfr50             ; error, go return
;
; check to see that we're not creating a device
;
        TaskContext <DS>,si
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBRen_WFP ; (DS:SI) -> destination name
                                        ;72417 end
        les     di,dfr_pTempBuf         ; (ES:DI) -> temp buffer
        CALLFAR CheckThisDevice
        jnc     short dfr35             ; device, go return error

dfr25:  mov     dx,dfr_dcde
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     [bp].TCBcMeta,-1
        UnSetBP_CurrTCB
                                        ;72417 end
        CALLFAR FSD_Move                ; do rename
        jc      short dfr50             ; error, go return

        cmp     dfr_metas,-1            ; meta chars present?
        je      short dfr40             ; no, go return (no error)

; restore searchbuf contents

        TaskContext <DS,ES>,si
        lea     si,dfr_savebuf          ; (DS:SI) -> dfr_savebuf
        lea     di,dfr_searchbuf        ; (ES:DI) -> dfr_searchbuf
        mov     cx,(SRCHBUFSIZE+1)/4
        .errnz ((SRCHBUFSIZE + 1) AND 3)
        rep     movsd                   ; restore searchbuf contents
        mov     si,CurrTCB
        lea     si,[si].TCBThisFSC      ; (DS:SI) -> FSC address
        CALLFAR Do_3xSearch_Next        ; search for file
        jc      dfr40                   ; no more files, go return
        CALLFAR RlseBuffr               ; release buffer

        jmp     dfr20                   ; go rename file

dfr35:  mov     ax,ERROR_ACCESS_DENIED
        stc
        jmp short dfr50

dfr40:  xor     ax,ax                   ; no error

dfr50:  les     di,dfr_pTempBuf         ; (ES:DI) -> temp buffer
        pushf
        CALLFAR FreeTempBuffer          ; Free the temp buffer
        popf                            ; Restore any error indication
dfr90:  LeaveProc
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     SS:[bp].TCB_pSrchBuf,0  ; Clear ptda_pSrchBuf
        UnSetBP_CurrTCB
                                        ;72417 end
;### PTR B720732        mov     SS:[ptda_pCDS],0        ; Clear ptda_pCDS
;### PTR B720732        mov     SS:[ptda_pOpenBuf],0    ; Clear ptda_pOpenBuf
        RETP

EndProc w_FCB_Rename



;***    w_Get_FCB_File_Length - set the random record field to the length of
;       the file in records (rounded up if partial).
;
;       ENTRY   DS:DX - point to a possible extended FCB
;       EXIT    Random record field updated to reflect the number of records
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_Get_FCB_File_Length,far16
LocalVar gfl_InfoBuf,<size inbuf>
LocalVar gfl_renbuf,PMPATHLEN
LocalVar gfl_dummy,WORD                 ; KLUDGE: see gshare.asm
LocalVar gfl_dummycds,<SIZE curdir>
LocalVar gfl_searchbuf,SRCHBUFSIZE+1

        EnterProc

        lea     ax,gfl_searchbuf
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     TASKAREA:[bp].TCB_pSrchBuf,ax ; Initialize ptda_pSrchBuf
        UnSetBP_CurrTCB
                                        ;72417 end
        lea     ax,gfl_dummycds
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,ax       ; Initialize TCBpTmpCDS ;### PTR B720732
;       mov     TASKAREA:[bx].TCBpTmpCDS.Segmt,ss       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],ax ; Initialize ptda_pCDS

        CALLFAR GetExtended             ; get real FCB pointer
                                        ; DX points to Input FCB
        lea     di,gfl_renbuf           ; appropriate buffer
        SaveReg <DS,SI>                 ; save ptr to true FCB
        SaveReg <bp>
        CALLFAR TransFCB                ; sets openbuf, Sattrib, etc.
        RestoreReg <bp>
        jc      short LengthFailed

; call FSD_QPathInfo to get file size
; bugbug 27 may 88 peterwu - TransPath called twiced
; Transpath is called by TransFCB and w_QPathInfo. One can be eliminted by
; creating a procedure FSD_QPathInfo that doesn't call Transpath.

        lea     di,gfl_InfoBuf
        TaskContext <es>,,noassume      ; es:di -> gfl_InfoBuf
        mov     cx,size inbuf           ; cx = size of gfl_InfoBuf
        mov     bx,QINFOLEV_DATE_TIME   ; bx = infolev
        TaskContext <ds>,,noassume
        lea     dx,gfl_renbuf           ; ds:dx -> path
        CALLFAR w_QPathInfo             ; get file info
        jc      short LengthFailed      ; it failed
        RestoreReg <SI,DS>              ; ds:si -> FCB

        MOV     DX,gfl_InfoBuf.inbuf_size.hiword ; get high order size
        MOV     AX,gfl_InfoBuf.inbuf_size.loword ; get low order size
        MOV     BX,[SI.fcb_RECSIZ]      ; get his record size
        OR      BX,BX                   ; empty record => 0 size for file
        JNZ     short GetSize           ; not empty
        MOV     BX,128
GetSize:
        MOV     DI,AX                   ; save low order word
        MOV     AX,DX                   ; move high order for divide
        XOR     DX,DX                   ; clear out high
        DIV     BX                      ; wham
        PUSH    AX                      ; save dividend
        MOV     AX,DI                   ; get low order piece
        DIV     BX                      ; wham
        MOV     CX,DX                   ; save remainder
        POP     DX                      ; get high order dividend
        JCXZ    short LengthStore       ; no roundup
        ADD     AX,1
        ADC     DX,0                    ; 32-bit increment
LengthStore:
        MOV     WORD PTR [SI.fcb_RR],AX ; store low order
        MOV     [SI.fcb_RR+2],DL        ; store high order
        OR      DH,DH
        jz      short len3              ; not storing insignificant zero
        MOV     [SI.fcb_RR+3],DH        ; save that high piece
len3:   xor     ax,ax

LengthDone:
;### PTR B720732        mov     ss:[ptda_pCDS],0        ; Clear ptda_pCDS
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     ss:[bp].TCB_pSrchBuf,0  ; Clear ptda_pSrchBuf
        UnSetBP_CurrTCB
                                        ;72417 end
        LeaveProc
        RETP

LengthFailed:                           ; The system call failed.  Clean the
        pop     si                      ;  stack, load the original error, set
        pop     ds                      ;  it and return.
        stc
        jmp     short LengthDone

EndProc w_Get_FCB_File_Length




;***    ChkIFSVol - routine to check if this is a volume label
;                   operation on an ifs owned volume
;
;       ENTRY   ds:dx => unopened fcb
;
;       EXIT    if 'C'  = an error was detected. error code is in ax
;
;               if 'NC' = no error - check the zero flag
;
;               if 'NZ' = this is a volume label operation on
;                         an ifs owned volume
;
;               if 'Z'  = either this is not a volume label
;                         operation, or this is not an ifs
;                         owned volume
;
;       USES    flags - all registers are preserved, except for ax in
;                       the error cases
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure ChkIFSVol,<near,faronly,far16>
        push    ds                      ; save the pointer to
        push    dx                      ; the callers fcb.
        push    di                      ; save the callers di register.
        push    si                      ; save the callers si register.
        push    ax                      ; save the callers ax register.

        SaveReg <DS,DX,CX,BX>           ; save various registers.
        mov     di,CurrTCB                                                     ;### PTR B720732
        MOV     DI,TASKAREA:[di].TCBpOpenBuf ; point to the destination buffer.;### PTR B720732
;### PTR B720732        MOV     DI,TASKAREA:[ptda_pOpenBuf] ; point to the destination buffer.
        CALLFAR TransFCB                ; crunch the FCB.
        RestoreReg  <BX,CX,DX,DS>       ; restore the registers.
        jc      short civ050            ; any error? yes, go return. AX = error code.

        CALLFAR GetExtended             ; is it an extended FCB?
        jz      short civ020            ; no, go set appropriate return code.

        mov     di,CurrTCB              ; yes, point to the process's tcb.
        cmp     TASKAREA:[di].TCBThisFSC.Offst,FSC_LOCAL; ifs owned volume?
        jnb     short civ020            ; no, go set appropriate return code.

        mov     al,[si-1]               ; yes, pick up the attributes.
        and     al,not attr_ignore      ; is this a volume label
        cmp     al,attr_volume_id       ; operation?
        jne     short civ020            ; no, go set appropriate return code.

        or      ax,1                    ; show that this is a volume label
                                        ; operation on an ifs owned volume.
        jmp     short civ030            ; go return to our caller.

civ020: xor     ax,ax                   ; show that this is either not a
                                        ; volume label operation, or not
                                        ; an ifs owned volume.

civ030: clc                             ; show that there was no error.
        pop     ax                      ; restore the callers ax register.
civ040: pop     si                      ; restore the callers si register.
        pop     di                      ; restore the callers di register.
        pop     dx                      ; recover the pointer to
        pop     ds                      ; the callers fcb.

        ret                             ; return to our caller.

;
; Error from TransFCB.
; 'C' set and AX = error code
;
; Unstack caller's AX into SI since SI gets set to caller's SI on exit anyway.
;
civ050: pop     si                      ; unstack the callers ax register
        jmp     short civ040            ; and go return to our caller.

EndProc ChkIFSVol



;***    w_FCB_VolId - worker for w_FCB_Delete, w_FCB_Rename, and
;                     w_FCB_Create to delete, rename or create
;                     the volume label of an ifs volume
;
;       ENTRY   ds:dx => unopened extended fcb
;                  ax    delete/rename/create flag. 0 = delete
;                                                   1 = rename
;                                                   2 = create
;
;       EXIT    if 'NC' = volume label deleted, renamed or created
;
;               if 'C'  = error detected - delete/rename not performed.
;                         ax contains an error code.
;
;                  ERROR_SHARING_VIOLATION is translated to ERROR_ACCESS_DENIED
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, flags
;
;       PSEUDOCODE:
;
;          PathIncVPBRef
;          if (ax=1=rename or ax=2=create) ChkVal to check new label
;          if (ax=0=delete or ax=1=rename)
;             FS_FSINFO to read the volume label
;             if (volume label equals filename in FCB)
;                FS_FSINFO to delete the volume label
;                if (ax=1=rename)
;                   FS_FSINFO to create the volume label
;             else
;                set up an error code
;          else
;             FS_FSINFO to create the volume label
;          PathDecVPBRef
;
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure w_FCB_VolId,<faronly,far16>

cvi             struc                   ; this structure defines the usage
                                        ; of "cvi_temp_label" when used with
                                        ; "FSF_SETINFO".

cvi_label       db  MAXLABLEN dup (?)   ; volume label.

cvi             ends

fvd             struc                   ; this structure defines the usage
                                        ; of "dvi_temp_label" when used
                                        ; with "FSF_QINFO"

fvd_q_vol_ser   db  4 dup (?)           ; volume serial number.
fvd_q_lab_len   db  ?                   ; length of volume label.
fvd_q_label     db  MAXLABLEN+1 dup (?) ; volume label in asciiz format.

fvd           ends

fvd_flag_delete equ 0                   ; indicates a volume label delete.
fvd_flag_rename equ 1                   ; indicates a volume label rename.
fvd_flag_create equ 2                   ; indicates a volume label create.

LocalVar fvd_flag,WORD                  ; delete/rename/create flag.
LocalVar dvi_temp_label <size fvd>      ; local temporary label buffer.
LocalVar cvi_temp_label <size cvi>      ; temporary label buffer.

        EnterProc

        push    bp                      ; save the bp register.

        mov     fvd_flag,ax             ; save the delete/rename/create flag.

        CALLFAR PathIncVPBRef           ; Bump ref-count to stop VPB going away.
;
; if this is a rename or create, check that the new label contains only
; valid characters
;
        cmp     fvd_flag,fvd_flag_delete ; is this a delete?
        je      short fvd15                     ; yes, bypass new label verification.

        mov     cx,size cvi             ; no, set up the loop control.

        mov     si,dx                   ; point to the fcb.
        add     si,8                    ; move down to the filename.

        cmp     fvd_flag,fvd_flag_rename ; is this a rename?
        jne     short fvd05             ; no, bypass switching filenames.
        add     si,16                   ; yes, move down to alternate filename.

fvd05:  push    dx                      ; save the offset to the fcb.
        CALLFAR ChkVal                  ; are all characters in new label legal?
        pop     dx                      ; recover the offset to the fcb.
        jnz     short fvd10             ; yes, go check if the old label matches.
        mov     ax,ERROR_INVALID_NAME   ; no, set up the error code.
        jmp     fvd70                   ; go return to caller showing an error.
;
; process the ifs delete of the volume id - start by getting current volume id
;
fvd10:  cmp     fvd_flag,fvd_flag_create ; is this a create?
        je      short fvd51             ; yes, go write the new label to the disk.

fvd15:  push    dx                      ; no, save the pointer to the fcb.

        mov     di,CurrTCB              ; point to this process's tcb.
        les     si,TASKAREA:[di].TCBThisFSC ; point to the fsc.

        push    FSF_QINFO               ; pass a flag showing this is a 'query'.
        push    TASKAREA:[di.TCBThishVPB] ; pass the volume's handle.

        lea     bx,dvi_temp_label       ; point to the temporary label buffer.
        push    ss                      ; pass the address of the
        push    bx                      ; temporary label buffer.

        push    type fvd                ; pass size of temporary label buffer.
        push    FSINFOLEV_LABEL         ; pass indicator showing this is a label.

        call    es:[si].FS_FSINFO       ; call the ifs to read the label.

        pop     dx                      ; recover the pointer to the fcb.

        or      ax,ax                   ; did an error occur?
        LJNZ    fvd60                   ; yes, go return the error to our caller.
;
; remove any embedded '.' from the volume id processed by TransFCB
;
        SaveReg <ax, bx, cx, dx, si, di, ds, es> ; save the callers registers.

        TaskContext <DS,ES>,di,noassume ; set up the segment registers.

                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Path_End ; point to the volume id from
                                        ;72417 end
        mov     di,si                      ; the fcb.

fvd30:  lodsb                              ; pick up a byte.
        cmp     al,'.'                     ; is it a period?
        jne     short fvd35                ; no, go see if its a delimiter.
        lodsb                              ; yes, get the next byte (it
                                           ; will not be a period).
fvd35:  stosb                              ; store the current byte away.
        or      al,al                      ; is it the null terminator?
        jne     short fvd30                ; no, go round again.
;
; compare the current volume label with that specified in the fcb
;
        lea     si,dvi_temp_label.fvd_q_label   ; point to the disk's label.
        mov     bl,dvi_temp_label.fvd_q_lab_len ; pick up its
        xor     bh,bh                           ; length.

                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        mov     di,TASKAREA:[di].TCBWFP_Path_End ; point to the volume id from
                                           ; the fcb.
                                        ;72417 end

        mov     cx,MAXLABLEN            ; set up the expression length.

        CALLFAR MetaMatch               ; compare the string and the expression.

        RestoreReg <es, ds, di, si, dx, cx, bx, ax> ; restore callers registers.

        jnc     short fvd50             ; does the fcb's label match the disk's?
                                        ; yes, go check if uppercasing is needed.

        mov     ax,ERROR_FILE_NOT_FOUND ; no, set up the appropriate error
        jmp     short fvd70             ; and go return it to our caller.
;
; if this is a create or rename, move the new label to a buffer
; and write it onto the volume
;
fvd50:  cmp     fvd_flag,fvd_flag_delete ; is this a delete?
        je      short fvd56             ; yes, go do the delete.

fvd51:  mov     cx,size cvi             ; no, set up the loop control.
        lea     bx,cvi_temp_label       ; point to the temporary label buffer.

        mov     si,dx                   ; point to the fcb.
        add     si,8                    ; move down to the filename.
        cmp     fvd_flag,fvd_flag_rename ; is this a rename?
        jne     short fvd53             ; no, bypass switching filenames.
        add     si,16                   ; yes, move down to alternate filename.

fvd53:  lodsb                           ; pick up a character for the label.
        CALLFAR UCase                   ; translate it to upper case.
        mov     TASKAREA:[bx.cvi_label],al ; store the character away.
        TestDBCS                        ; is this a double byte character?
        jnz     short fvd55             ; no, bypass processing the second byte.

        inc     bx                      ; yes, move down to the next byte.
        dec     cx                      ; down the remaining length by one.
        lodsb                           ; pick up the second byte
        mov     TASKAREA:[bx.cvi_label],al ; and store it away.


fvd55:  inc     bx                      ; move down to the next character.
        loop    short fvd53             ; more to do? yes, go round again.

        lea     dx,cvi_temp_label       ; point to the temporary
        mov     ax,ss                   ; label buffer.

        mov     cx,size cvi             ; pass the label's length.

        mov     bx,FSINFOLEV_LABEL      ; pass indicator showing this is a label.

        CALLFAR FSD_SetFSInfo           ; write the new volume label to disk.

        jmp     short fvd60             ; go prepare to return to our caller.
;
; if this is a delete, delete the label from the ifs volume
;
fvd56:  mov     di,CurrTCB              ; point to this process's tcb.
        les     si,TASKAREA:[di].TCBThisFSC ; point to the fsc.

        push    FSF_SETINFO             ; pass a flag showing this is a 'set'.
        push    TASKAREA:[di.TCBThishVPB] ; pass the volume's handle.
        lea     bx,dvi_temp_label       ; point to the temporary label buffer.

        mov     byte ptr ss:[bx+0],0    ; show that its a zero length label.
        mov     byte ptr ss:[bx+1],0    ; build the zero length asciiz string.

        push    ss                      ; pass the address of the
        push    bx                      ; new (null) label.
        push    1+1                     ; pass the null label's length.
        push    FSINFOLEV_LABEL         ; pass indicator showing this is a label.

        call    es:[si].FS_FSINFO       ; call the ifs to delete the label.

fvd60:  cmp     ax,ERROR_SHARING_VIOLATION ; is it a sharing violation?
        jne     short fvd70                ; no, bypass translating the error.
        mov     ax,ERROR_ACCESS_DENIED     ; yes, change the error code.

fvd70:  xor     bx,bx                   ; is there an error?
        cmp     bx,ax                   ; yes, set the carry flag.

fvd90:  CALLFAR PathDecVPBRef           ; Remove our referent in the VPB
        pop     bp

        LeaveProc

        ret
EndProc w_FCB_VolId


FSSWAPCODE ends
        END
