SCCSID  equ     "@(#)dosfs.asm  6.23 92/03/17"
PAGE    ,132
TITLE   DOSFS - MSDOS file system interface
NAME    DOSFS

;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved

;
;   Various internal calls which interface to the file system, independent
;   of whether a request is for the local file system or over the network.
;   There are a couple of other modules which also interact with the
;   file system:
;
;       disk - general read/write code
;       rom - disk allocation, cluster chaining
;       dir - general directory searching
;
;   Routines in this module:
;
;       Global          Local
;       ======          =====
;
;                       PhysClose
;       DOS_Abort
;                       FSD_Exit
;       FSD_Close
;                       DOS_Close
;       DirFromSFT
;       FSD_Commit
;                       DOS_Commit
;                       FileCommit
;                       SetSFTTimes
;       FSD_Delete
;       Byte13_PackName
;       DOS_ChDir
;       DOS_Dup
;       DupJFN
;                       DupPMJFN
;                       CopyJFNTab
;                       Dup1Handle
;                       CopyJFNFlag
;       FSD_Move
;                       Ren_Check
;
;
; History:
;
; B001 - 2/25/92   Mark Rogalski
;        Add function back in to handle special case of VDM duped network
;        handle. Should do ordinary close for these.
;       3/2/92 Steve Best sfb reduce perfview overhead on close code
; 46691 7/29/92 Steve Best only call pvwkillinstance_fil for files
; 72774 - 8/25/93  Lisa L. Mobley
;         Copy default JFN information back into JFN_pTable after DOS_Abort
;         frees the JFN segment, allowing PDOPEN to suceesfully open the popup
;         supression log file.
; 76699 - 12/15/93 RAS Make Perfview Optional with assembly flag
; 72417 - 11/27/95 - MLM
;                 moved from PTDA to TCB:
;                 PathNameType, Ren_WFP, Sattrib, ThisCDS, WFP_Start
;                 DirStart
; 02/05/96 Chg Team  148006  Called function was moved to a different segment.
;                             It needs to be a callfar.
;
;

.xlist
include fnofs.inc
include dosseg.inc

.xcref
include buffer.inc
include curdir.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include defdbug.inc
include dossymc.inc
include dpb.inc
include devhdr.inc
include dirent.inc
include error.inc
include extattr.inc
include filemode.inc
include filesys.inc
include inbuf.inc
include tkexport.inc
                                            ;72417 moved find.inc after tkexport.inc
include find.inc
include mft.inc
include mi.inc
include mult.inc
include oplock.inc
include seldesc.inc
include virtsym.inc
include sft.inc
include fsh.inc
include pdb.inc
include protmode.inc
include vpb.inc
include proc.inc
include fsc.inc
include shutdown.inc
include filemacs.inc
include bsedosp.inc
include pgexport.inc
include vmexport.inc

.cref
.list
        CpuMode 386
        ModName , FNO_DOSFS_ASM


        ReferGlobal     NumIO,BYTE      ; Number of physical drives     AZ10291
        ReferGlobal     NumIORipl,BYTE  ; # of reserved drives for RIPL AZ10291
        ReferGlobal     DPBHead,DWORD           ; Beginning of DPB chain

        ReferFlat       _sftFCB,WORD
        ReferFlat       _fLWDaemon,DWORD
        ReferFlat       _pPTDACur,dword                                ;b722136

ifdef PERFVIEW
        ReferFlat       _fPerfview,DWORD                               ;sfb
endif
ifdef SECURITY
        ReferFlat       _fSec,BYTE       ; @SEC
endif

        ReferGDT        GDT_FSC,WORD
        ReferGDT        GDT_VPB,WORD
        ReferGDT        GDT_PTDA,WORD   ;llm 72774
                                                ;72417 removed PTDA entries now
                                                ;      in TCB

        ReferTask       ExtErr_Locus,BYTE       ; Extended Error Locus
        ReferTask       CurrentPDB,WORD
        ReferTask       CurrTCB,WORD            ; Current thread control block
        ReferTask       JFN_Flg_Ptr,DWORD       ; pointer to JFN flags
        ReferTask       JFN_Length,WORD
        ReferTask       JFN_pTable,DWORD
        ReferTask       JFN_Table
        ReferTask       JFN_Flags
        ReferTask       ptda_handle,WORD
        ReferTask       Pid,WORD
        ReferTask       ProcType,BYTE
        ReferTask       SrchRmp,DWORD


        EXTRNFAR        ClosePipe
        EXTRNFAR        Date16
        EXTRNFAR        Dev_Open_SFT
        EXTRNFAR        DevReleaseHandle
        EXTRNFAR        DevUnlockPhysDrive
        EXTRNFAR        FlushBuf
        EXTRNFAR        FNotify_Handle_Wake
        EXTRNFAR        CNotify_Path_Wake
        EXTRNFAR        fVolumeWriteable
        EXTRNFAR        GetBuffr
        EXTRNFAR        MemMapAlias
        EXTRNFAR        MemUnmapAlias
        EXTRNFAR        RlseBuffr
        EXTRNFAR        StrLen
        EXTRNFAR        w_Close
        EXTRNFAR        DOS_Delete
        EXTRNFAR        DOS_Rename
        EXTRNFAR        UnlockDrive
        EXTRNFAR        Volume_Access_Lock
        EXTRNFAR        Volume_Access_Unlock
        EXTRNFAR        Volume_Alloc_Lock
ifdef SECURITY
        EXTRNFAR        SecClose                                       ;@SEC
        EXTRNFAR        SecMovePre                                     ;@SEC
        EXTRNFAR        SecMovePost                                    ;@SEC
        EXTRNFAR        SecDeletePre                                   ;@SEC
        EXTRNFAR        SecDeletePost                                  ;@SEC
endif

        EXTRN           NmPipeClose:NEAR
        EXTRN           NmPipeCommit:NEAR
        EXTRN           AddShare:NEAR
        EXTRN           AllocJFNSeg:NEAR
        EXTRN           DecVPBRef:NEAR
        EXTRNFAR        Dev_Close_SFT  ; 148006
        EXTRN           Find3xExit:NEAR
        EXTRN           FreeJFNSeg:NEAR
        EXTRN           fscNext:NEAR
        EXTRN           fValidFATDisk:NEAR
        EXTRN           GetPath:NEAR
        EXTRN           LockMFT:NEAR
        EXTRN           MFTclose:NEAR
        EXTRN           NameTrans:NEAR
        EXTRN           PackName:NEAR
        EXTRN           PathIncVPBRef:NEAR
        EXTRN           PathDecVPBRef:NEAR
        EXTRN           PathPref:NEAR
        EXTRN           pJFNFromHandle:NEAR
        EXTRN           RemoveShare:NEAR
        EXTRN           ScanCDS:NEAR
        EXTRN           ScanJFN:NEAR
        EXTRN           SFFromHandle:NEAR
        EXTRN           ShCloseFile:NEAR
        EXTRN           ShSU:NEAR
        EXTRN           SkipBack:NEAR
        EXTRN           UnlockMFT:NEAR
        EXTRN           UseCache:NEAR

        EXTRN32         _SELFreeKHB
        EXTRN32         CacheFlush
        EXTRNF32        PrefLookupMFT

ifdef PERFVIEW
        EXTRN32         _PVWKillInstance_Fil                           ;b722136
endif

        EXTRN32         _BufFlushFATDir

DATA    segment

        PUBLIC  DOS_Abort_Close

DOS_Abort_Close   db      0

DATA    ends


        FARCODE HIGH2
HIGH2CODE  SEGMENT
        ASSUME  SS:TASKAREA

;**     PhysClose - unlock and release a possibly physical disk handle
;
;       ENTRY   bx = real handle
;
;       EXIT    if carry set - error (e.g. invalid handle)
;               if carry clear and zero set - not physical handle
;               if carry clear and zero clear - physical handle closed
;
;       USES    ax,cx,si,di,ds,es,flags

Procedure PhysClose,near,local
    assume ds:nothing,es:nothing
        call    SFFromHandle            ; es:di point to SFT
        jc      short phclx             ; invalid handle, go error exit

        test    es:[di].sf_fsi.sfi_mode2,open2_phys_disk ; phys disk handle?
        jz      short phclx             ; no, go exit w/ zero set

        CALLFAR DevUnlockPhysDrive      ; unlock possible phys locks
        jc      short phclie1           ; unlock error, go panic

        push    bx                      ; [tos] = handle
        mov     si,sp
        TaskContext <DS>                ; ds:si -> handle
        mov     bx,ds                   ; BX:SI -> handle
        mov     cx,2                    ; pass length of data (2 bytes)

;     inputs: DS:DI ponter to data (handle), CX = data length (2 bytes)

        CALLFAR DevReleaseHandle        ; close physical disk handle
        pop     bx                      ; restore sp
        jc      short phclie2           ; close error, go panic

        or      ax,1                    ; clear zero and exit
phclx:  ret

phclie1:        InternalError <PhysClose: Physical Disk Unlock Failed>
phclie2:        InternalError <PhysClose: Physical Disk Handle Release Failed>

EndProc PhysClose


;***    Byte13_PackName  - call PackName with 13 byte local buff as dest.
;       ENTRY
;               DS:SI   source string
;
;       EXIT    NC      Successful
;                       Packed name put in [WFP_Start]
;               CY      error
;                   AX - error code
;                        ERROR_PATH NOT FOUND
;       USES    ax,bx,cx,di,es,flags
;       Byte13_PackName:
;           calls PackName with dest. buff as the local 13 byte buffer
;           finds remaining byte available on the dest. buff
;           and compares it with the given string name; if not enough
;           space gives error, else copies it to final destination.

Procedure   Byte13_PackName,faronly
            ASSUME DS:NOTHING,ES:NOTHING

LocalVar pn_temp_buffer, 13     ; local temporary buffer

            EnterProc
            push    si
            push    dx

            TaskContext <es>
            lea     di,pn_temp_buffer

            mov     dx,di
            call    PackName
            mov     di,dx       ; restore start offset again
            CALLFAR StrLen      ; get the string length after PackName call
                                ; This is the actual string length
            mov     dx,cx       ; save the count ( this includes NULL )

                                ;72417 moved from PTDA to TCB
            mov     di,CurrTCB
            mov     di,TASKAREA:[di].TCBWFP_Start
                                ;72417 end
            mov     bx,di       ; now get the actual destination buffer
            add     bx,2        ; skip "d:" in the WFP
            CALLFAR StrLen      ; get the string length of the dest
            dec     cx          ; don't count NULL
            add     di,cx       ; go to end of WFP
            call    SkipBack    ; position at the end of path string
            inc     di          ;
;
;  so far, packName is done on a temp. buffer; strlen is found for both the wfp
;  and the string returned by packname; now find out how many free bytes are
;  left in the orignal buffer. (it is assumed to PMPATHLEN long).
;  at this point : CX = count of wfp ;  DX = count of chars in PackName string
;  es = TaskContext, es:di points to the place in openbuf where we should copy
;  the packname string
                                     ;72417 moved from PTDA to TCB
            SetBP_CurrTCB
            mov     ax,[bp].TCBWFP_Start   ;
            UnSetBP_CurrTCB
                                     ;72417 end
            add     ax,PMPATHLEN     ; point to one byte after end of buffer
            sub     ax,di            ; now ax = remaining bytes in buffer
            cmp     ax,dx            ; enough space left ?
            jb      short b13_nospace
; enough space - go ahead and copy to final destination
            mov     cx,dx            ; move entire PackName string
            push    ds
            TaskContext <ds>
            lea     si,pn_temp_buffer  ; source for copying
            rep     movsb              ; copy the string
            clc
            pop     ds

b13_ret:    pop     dx
            pop     si
            LeaveProc
            ret

b13_nospace:
            mov ax,ERROR_PATH_NOT_FOUND
            jmp     short b13_ret

EndProc     Byte13_PackName
;
;**     FSD_Exit - notify fsds to release process related data
;
;       This procedure is called when a process terminates to notify all fsds
;       to free storage allocated for this process.
;
;       ENTRY   none
;
;       EXIT    none
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

Procedure FSD_Exit,hybrid
    assume ds:nothing,es:nothing

        xor     di,di                   ; start from first FSC
fse10:  call    fscNext                 ; es:di -> fsc
        jc      short fsex              ; no more fsc, go exit

        SaveReg <es,di>                 ; save fsc pointer
        mov     si,[CurrTCB]
        push    TASKAREA:[si].TCBUser_ID
        push    TASKAREA:[si].TCBProc_ID
        push    [CurrentPDB]
        call    es:[di].FS_EXIT         ; ignore error
        RestoreReg <di,es>              ; restore fsc pointer
        jmp     short fse10

fsex:   ret
EndProc FSD_Exit


;***    FSD_Close - Close the specified file
;
;       FSD_Close is called to close the file specified in the sft.
;       It routes it to the appropriate file system, DOS or an FSD, to do
;       the low level work (writing the directory entry to disk).
;       It then removes the file from the sharing set. The reference count
;       on the SFT is decremented.
;
;       The SFT is not released.
;
;       ENTRY   [TCBThisSFT] set to the SFT for the file being used
;               [TCBJfnFlag] set to handle flag for handle being used
;               BX = real handle
;                  == -1 in case of FCB closes and those cases where there
;                               is no handle associated with the SFT
;
;       EXIT    (ES:DI) -> SFT
;               if 'NC'
;                  sf_ref_count decremented
;               if 'C'
;                  AX is error code
;                    ERROR_INVALID_HANDLE
;                    ERROR_FILE_NOT_FOUND
;                    ERROR_ACCESS_DENIED
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       EFFECTS  Switches Context
;
;       ALGORITHM
;
;       SetSFTTimes
;       switch (sft.FSC) {
;
;         case FSD:
;            find the close type
;            ( if sf_ref_count == 1 => close of this sft for system; else
;              if another JFN entry for this SFN exists => ordinary close;
;              else this is close of sft for this process)
;            FS_Close
;            if (<end of serving pipe redirection>)
;                 DOS_Close
;            break
;
;         case FAT:
;           if (physdisk) {
;             dec ref count
;             return
;           } else
;             DOS_Close   /* close pipe or file */
;           break
;       }
;       if (!pipe) {
;         Dev_Close_SFT
;         sft.refcount--
;         if (sft.refcount == 0)
;           MFTclose
;       }
;       return

Procedure FSD_Close,<hybrid, far16>
    ASSUME DS:NOTHING, ES:NOTHING
        PMONLY

ifdef   SECURITY
        FlatContext <ds>,di                                               ;@SEC
        cmp     _fSec,0                                                   ;@SEC
        jz      fscl3                                                     ;@SEC
        CALLFAR SecClose                                                  ;@SEC
fscl3:
endif

        TaskContext <ds>
        mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT     ; (ES:DI) -> SFT
        SaveReg <bx>                    ; save the real handle
        call    SetSFTTimes             ; time stamp sft
        RestoreReg <bx>                 ; bx = real handle

; dispatch to  fsd or fat
; es:di -> sft
; bx = real handle (JFN)

        push    bx                      ; ****** save the handle
                                        ; *** and don't forget to restore later
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL  ; what file system?
        jb      short fscl30            ; fsd, go call fsd worker

; must be fat file or physical disk

; check for physical disk handle

        test    es:[di].sf_fsi.sfi_mode2,open2_phys_disk ; physical disk?
        jz      short fscl10            ; not phys disk handle - continue
        dec     es:[di].sf_ref_count    ; decrement reference count
        pop     bx
        RETP

; (ES:DI) -> SFT

fscl10:

ifdef PERFVIEW

        CpuMode 386                                                    ;b722136
        SaveReg <ds,es>                                                ;b722136
        SaveReg <ax>                                                   ;46691
        mov    ax,es                                                   ;46691

        FlatContext <ds,es>                                            ;b722136
        cmp     _fPerfview,0            ; check if perfview is up      ;46691
        je      short fscl15                                           ;46691

        mov     es,ax                                                  ;46691
        test    es:[di].sf_flags,DEVID_DEVICE   ; check if device      ;46691
        jnz     short fscl15                                           ;46691
        FlatContext <es>                                               ;44691
        mov     edx,_pPTDACur           ; (EDX) = pointer to current PTDA ;b722136

        CCALL32 _PVWKillInstance_Fil,<edx>                              ;b722136
                                        ; Clean up Perfview per-process data blk
fscl15:                                                                 ;sfb
                                                                        ;b722136
        CpuMode Reset                                                   ;b722136
        ASSUME  DS:TASKAREA                                             ;b722136
        ASSUME  ES:NOTHING                                              ;b722136
        RestoreReg <ax>                                                 ;46691
        RestoreReg <es,ds>                                              ;b722136

endif ;PERFVIEW

        CALLFAR DOS_Close
    ASSUME DS:NOTHING
;;;;;;;;;;;;;;;;;;;;;;;;;;; Ray Andrade Defect# 77378 Change Team ;;;;;;;;;;;;;
;       clc                             ; 58335 \\ withold carry bit to allow
                                        ; display Error.
;       clc                             ; cp20 ptr j200324
        jmp     fscl40                  ; go do post processing    ;BC001

; setup parameters and call FS_CLOSE
; ES:DI -> SFT
; bx = real handle

fscl30: mov     cx,FS_CL_FORSYS         ; assume fs_close type to be for system
        cmp     es:[di].sf_ref_count,1  ; last close for this sft?
        je      short fscl35            ; if ref_count == 1 => close for system
; to fix defect 108412, replace the je line above with the following code.
; However, LAN Server 4.0 has coded to the bug and expect a FS_CL_FORSYS even
; if it is only a close for process.  With this fix in, they break (D153078).
;;        jne     short fscljw            ; if ref_count != 1 => not last close
;;        cmp     es:[di].sf_chain,0      ; any other opens on this file?
;;        je      short fscl35            ; if sf_chain == 0 => close for system
;;        mov     cx,FS_CL_FORPROC        ; else, close for process
;;        jmp     short fscl35

; sf_rec_count != 1; Now if there is atleast one jfn entry  for the same sft,
; then this is an ordinary close; else it is close for this process.
; (jfn entry for this handle is removed before fsd_close is called).

        SaveReg <es,di>                 ;save sft pointer
;;fscljw: SaveReg <es,di>                 ;save sft pointer

;;BUGBUG philipl - I agree with Nagara nuke this section 09-Sep-1989

;;;         cmp     bx,-1                   ; consider no handle OR fcb close as
;;;         je      fscl33                  ; ordinary close
;;;         cmp     ProcType,PT_REALMODE    ;
;;;         jne     fscl31                  ;
;;;
;;; ; BUGBUG ----
;;; ;    This should not be there for cruiser ???
;;; ;       - Nagara
;;; ;
;;; ; for real mode and sf_refcount >1; Look at the PDB_JFN_Table for a dup of
;;; ; the real handle. If it is a dup, then ordinary close; else close for process
;;;
;;;         mov     ax,bx                   ; ax = real handle
;;;         call    Scan3xJFN               ; CY if no dups -> close for process
;;;         jmp     short fscl32            ;
;;;
;;; ; END BUGBUG
;;;

; for real mode and sf_refcount >1, if network file handle then do
; ordinary close; else close for process

        cmp     ProcType,PT_REALMODE       ;BC001
        jne     short fscl31               ;BC001
        cmp     es:[di].sf_ref_count,1     ;BN001 last close for this sft?
        je      short fscl31               ;BN001 yes? do normal processing
        test    es:[di].sf_flags,SF_ISNET  ;BN001 network handle?                                ;BN001
        jnz     short fscl33               ;BN001 yes? do ordinary close


; Protect mode; scan the JFN table for this process ;

fscl31: mov     ax,es:[di].sf_fsi.sfi_selfsfn   ; SFN to search for
        call    ScanJFN                 ; (CY) -> if no jfn for this sfn

fscl32: mov     cx,FS_CL_FORPROC        ;
        jc      short fscl34            ; close for process
fscl33: mov     cx,FS_CL_ORDINARY       ; type = 0 ; ordinary close
fscl34: RestoreReg  <di,es>             ; restore sft pointer

fscl35: push    es                      ; save es - FSD may trash it
        push    cx                      ; push close type
        get_advise                      ; ax = advisory bits
        push    ax                      ; push IOFlag (advisory bits)
        lea     ax,es:[di].sf_fsi       ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to fsi
        lea     ax,es:[di].sf_fsd       ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to fsd

ifdef PERFVIEW

         SaveReg <ds,es>                                               ;b722136
         SaveReg <ax>                                                  ;46691
         mov     ax,es                                                 ;46691
         FlatContext <ds,es>                                           ;b722136
         cmp     _fPerfview,0            ; check if perfview is up     ;sfb
         je      short fscl36                                          ;sfb
         mov     es,ax                                                 ;46691

         test    es:[di].sf_flags,DEVID_DEVICE   ; check if device     ;46691
         jnz     short fscl36                                          ;46691
         FlatContext <es>                ; only do for file            ;46691
         mov     edx,_pPTDACur           ; (EDX) = pointer to current PTDA ;b722136
         CCALL32 _PVWKillInstance_Fil,<edx>                                ;b722136
                                         ; Clean up Perfview per-process data blk
fscl36:                                                                 ;sfb
                                                                        ;b722136
         RestoreReg <ax>                                                ;46691
         RestoreReg <es,ds>                                             ;b722136
         CpuMode Reset                                                  ;b722136
         ASSUME  DS:NOTHING                                             ;b722136
         ASSUME  ES:NOTHING                                             ;b722136
                                                                        ;b722136
endif ;PERFVIEW


        lds     si,es:[di].sf_FSC       ; ds:si -> FSC
        call    ds:[si].FS_CLOSE        ; ax = 0 or error code
        pop     es                      ; unsave es (sft segment)

; check for undoing the redirection of a local pipe.  In this case,
; also direct the close to the local named pipe code.  if the ref count is > 1,
; we leave the pipe redirected and call NmPipeClose to decrement the ref count.
; if the ref count == 1, we remove the redirection.

        test    es:[di].sf_flags2,SF_SRVRDR     ; only valid for named pipes
        jz      short fscl37
        cmp     es:[di].sf_ref_count,1  ; last close for this sft?
        jne     fscl10                  ; if ref_count == 1 => close for system
        mov     es:[di].sf_FSC.Offst,FSC_LOCAL  ; revert to the local file sys
        jmp     fscl10                  ; call NmPipeClose to really close

fscl37: xor     bx,bx                   ;
        cmp     bx,ax                   ; set CY if ax != 0
;
; Post processing
;
fscl40: pop     bx                      ; bx = real handle *** restored
        pushf                           ; save carry flag
        SaveReg <ax,bp>                 ; save error code and caller's bp
        mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT     ; (ES:DI) -> SFT
        test    es:[di].sf_flags,DEVID_DEVICE   ; device ?
        jz      short fscl40a                   ; no, go check for pipe
        CALLFAR Dev_Close_SFT    ;148006        ; go tell the device that the
                                                ; sft is getting closed
                                                ; and Ignore errors

; decrement ref-count for files and remote named pipes
; but don't dec ref-count for pipes, local named pipes, and server side
; redirected pipe

fscl40a:test    es:[di].sf_flags,SF_PIPE        ; pipe?
        jz      short fscl41                    ; no, go dec ref count
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL  ; local pipe?
        jae     short fscl60                    ; yes, don't dec ref count
        test    es:[di].sf_flags2,SF_SRVRDR     ; server redirected pipe?
        jnz     short fscl60                    ; yes, don't dec ref count
                                                ; (already done by DOS_Close)

fscl41: dec     ES:[DI].sf_ref_count
        jnz     short fscl50            ; not last ref., don't free VPB

        mov     bp,NULLVPB              ; bp = null vpb
        xchg    bp,es:[di].sf_fsi.sfi_hVPB      ; volume is no longer referenced
        cmp     bp,NULLVPB              ; was hVPB null already?
        jz      short fscl50            ; yes, no need to decvpbref
        call    DecVPBRef               ; decrement ref count on vpb

; bx = real handle

fscl50: test    es:[di].sf_flags,SF_NMPIPE ; Is this a named pipe
        jnz     short fscl60            ; Yes. Don't call MFTClose for it.
        test    es:[di].sf_flags2,sff2_UNC ; Is this a UNC object
        jnz     short fscl60            ; Yes. Don't call MFTClose for it.
        TaskContext DS
        call    MFTclose                ; remove SFT from share list
fscl60: RestoreReg <bp,ax>              ; restore caller's bp and error code
        POPF                            ; restore carry status
        ret

EndProc FSD_Close


Break   <FSD_Commit - update directory entries>

;**     FSD_Commit - Commit the file
;
;       Cached directory information is committed to disk.
;
;       ENTRY   [ThisSFT] set to the SFT for the file being used
;               AX = Commit type
;                    FS_COMMIT_ONE  - for one specific handle
;                    FS_COMMIT_ALL  - for all handles (DosBufReset with -1)
;
;       EXIT    if 'NC' - success
;                  sf_ref_count decremented
;                  (ES:DI) -> SFT
;               if 'C' - error
;                  AX is error code
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

Procedure   FSD_Commit,<hybrid,far16>
    ASSUME      DS:nothing, ES:NOTHING
        PMONLY

        mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT ; es:di -> sft
        push    ax                      ; save commit type
        call    SetSFTTimes             ; time stamp SFT
        pop     ax
        lds     si,es:[di].sf_FSC       ; ds:si -> fsc
        cmp     si,FSC_LOCAL            ; what file system?
        jb      short fsc20             ; fsd, go call fsd worker

; call fat worker

        mov     bx,es:[di].sf_flags

        TaskContext <ds>
        SaveReg <bp>
        CALLFAR DOS_Commit
        RestoreReg <bp>

        jmp     short fsc30             ; do post processing

; call FSD

    ASSUME DS:NOTHING, ES:NOTHING

fsc20:  push    ax                      ; commit type
        SaveReg <si>
        get_advise                      ; ax = advisory bits
        RestoreReg <si>
        push    ax                      ; push IOFlag (advisory bits)
        lea     dx,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    dx                      ; push pointer to sf_fsi
        lea     dx,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    dx                      ; push pointer to sf_fsd
        call    ds:[si].FS_COMMIT
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0


; don't reset the time stamp bits. The FSD is supposed to do this.
; Besides, it would break lanman.
;b713457
; if commit was successful, reset the time stamp bits so we don't have to
; time stamp the SFT again (unless another write occurs)
fsc30:  jc      short fsc40             ; error, go exit
;mov    di,[CurrTCB]
;les    di,TASKAREA:[di].TCBThisSFT     ; es:di -> sft
;mov    es:[di].sf_fsi.sfi_tstamp,0     ; reset all time stamp bits

fsc40:  RETP

EndProc FSD_Commit


Break   <SetSFTTimes - signal a change in the times for an SFT>

;***    SetSFTTimes - update the timestamp on an SFT
;
;       Examine the flags for a SFT and set the time appropriately.
;       Reflect these times in other SFT's for the same file.
;       If time stamps need to be propagated, the file is marked dirty so that
;       the commit routine knows to write the dir entry to the disk.
;
;       ENTRY   ES:DI point to SFT
;
;       EXIT    Set SFT times to current time iff File & dirty & !nodate
;
;       USES    ax, bx, cx, dx, flags
;
;       ALGORITHM
;
;       if (sfi_tstamps & (ST_SCREAT|ST_SWRITE|ST_SREAD)) {  /* update time stamps */
;         stamp = Date16
;         if (sfi_tstamp & ST_SCREAT)
;           sf_ctime, sf_cdate = stamp
;         if (sfi_tstamp & ST_SWRITE)
;           sf_mtime, sf_mdate = stamp
;         if (sfi_tstamp & ST_SREAD)
;           sf_atime, sf_adate = stamp
;       }
;       if (sfi_tstamp != 0)
;         ShSU(ax=0)

Procedure SetSFTTimes,NEAR,LOCAL
        Assert  ISSFT,<ES,DI>,"SetSFTTimes"

        test    es:[di].sf_fsi.sfi_tstamp,ST_SCREAT or ST_SWRITE or ST_SREAD
                                        ; need time stamp?
        jz      short sst30             ; no, go propagate time stamps

        CALLFAR Date16                  ; Date/Time to AX/DX
        test    es:[di].sf_fsi.sfi_tstamp,ST_SCREAT ; stamp creation time?
        jz      short sst10             ; no, go check last write time

        mov     es:[di].sf_fsi.sfi_cdate,ax     ; update creation date
        mov     es:[di].sf_fsi.sfi_ctime,dx     ; update creation time

sst10:  test    es:[di].sf_fsi.sfi_tstamp,ST_SWRITE ; stamp last write time?
        jz      short sst20             ; no, go check last read time

        mov     es:[di].sf_fsi.sfi_mdate,ax     ; update last write date
        mov     es:[di].sf_fsi.sfi_mtime,dx     ; update last write time

sst20:  test    es:[di].sf_fsi.sfi_tstamp,ST_SREAD ; stamp last read time?
        jz      short sst30             ; no, go propagate time stamps

        mov     es:[di].sf_fsi.sfi_adate,ax     ; update last read date
        mov     es:[di].sf_fsi.sfi_atime,dx     ; update last read time

sst30:  test    es:[di].sf_fsi.sfi_tstamp,ST_PCREAT or ST_PWRITE or ST_PREAD
                                        ; anything to propagate?
        jz      short sstx              ; no, go home

; bugbug 21-Dec-89 peterwu - we should mark the directory entry dirty,
; not the file. But we don't have a flag to indicate directory entry dirty, so
; we just overload the file dirty bit to indicate dir entry dirty as well.

        and     es:[di].sf_flags,not DEVID_FILE_CLEAN ; mark file dirty

        XOR     AX,AX
        CALL    ShSU                    ; propagate time stamps to other SFTs
sstx:   ret
EndProc SetSFTTimes


;***    FSD_Delete
;
;       ENTRY   [WFP_Start] Points to WFP string ("d:/" must be first 3
;                       chars, NUL terminated) with no metachars
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else Points
;                        to first char after last "/" of current dir part)
;               [ThisCDS] Points to CDS being used
;                       (Low word = -1 if NUL CDS (Net direct request))
;               [Sattrib] Is attribute of search, determines what files can
;                       be found
;               [cMeta] indicates no metas
;
;       EXIT    CARRY CLEAR
;                       OK
;               CARRY SET
;                   AX is error code
;                   if (AX == ERROR_ACCESS_DENIED) && (FAT FS)
;                      if (BX == 0)
;                         error caused by attempted delete of directory
;                      else
;                         error caused by attempted delete of read-only file
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, flags
;
;       PSEUDOCODE
;          PathIncVPBRef
;          ShCloseFile
;          AddShare
;          if (FAT)
;             DOS_Delete
;          else
;             FS_Delete
;          PathDecVPBRef
;
;
Procedure FSD_Delete,<near,faronly>
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        push    bp
ifdef   SECURITY
        FlatContext <DS>                                                ;@SEC
        cmp     _fSec,0                                                 ;@SEC
        jz      fd7                                                     ;@SEC
        CALLFAR SecDeletePre                                            ;@SEC
        jnc     fd7                                                     ;@SEC
        mov     bx,1        ;BUGBUG -- This really isn't necessary      ;@SEC
        jmp     fd55                                                    ;@SEC
fd7:
endif

; Since this is a "path-based" call, there has been no referent put in the
; VPB for this call. We need to increment the ref-count in the VPB in order
; to ensure that the VPB does not go away while we are working on it.
;
        call    PathIncVPBRef           ; Increment reference count
;
; add to sharing.  allocates SFT and calls MFT_Enter

        TaskContext <DS>
        call    ShCloseFile
        mov     ax,sharing_deny_both    ; exclusive access to file
        mov     cx,opbatchbreak         ; break opbatch
        call    AddShare                ; doesn't return ERROR_ACCESS_DENIED
        LJC     fd50                    ; sharing failed, go return

; dispatch to net, fsd, or fat worker

        mov     bx,CurrTCB              ; bx = tcb
        les     di,TASKAREA:[bx].TCBThisFSC ; es:di -> fsc
        cmp     di,FSC_LOCAL            ; what file system?
        jb      short fd20              ; fsd, go call fsd worker

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

        ShutdownGate  DOS_Delete, fd40, ES

; the above jumps to fd40 on completion

fd20:   mov     si,TASKAREA:CurrTCB             ; (SI) = current TCB     ;### PTR B720732
        lds     si,TASKAREA:[si].TCBThisCDS     ; (DS:SI) = CDS pointer  ;### PTR B720732
;### PTR B720732 fd20:  lds     si,TASKAREA:[ThisCDS]   ; ds:si -> cds
  assume ds:nothing
        lea     ax,ds:[si].cd_fsi       ; ds:ax -> cd_fsi
        push    ds
        push    ax                      ; push pointer to cd_fsi
        lea     ax,ds:[si].cd_fsd       ; ds:ax -> cd_fsd
        push    ds
        push    ax                      ; push pointer to cd_fsd
        push    ss
        mov     bp,bx
                                        ;72417 moved from PTDA to TCB
        push    [bp].TCBWFP_Start       ; push pointer to path name
        mov     ax,[bp].TCBCurr_Dir_End
        cmp     ax,-1                   ; is curr_dir_end set?
        je      short fd30              ; no, pass -1 to FS_Delete
        sub     ax,[bp].TCBWFP_Start    ; yes, convert to offset from WFP_Start
fd30:   push    ax                      ; push iCurDirEnd
;       mov     bp,bx                   ; commented out since it now needs to
                                        ; be done before referencing the TCB
                                        ;72417 end
        call    es:[di].FS_DELETE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0

    ASSUME DS:NOTHING, ES:NOTHING

ifdef   SECURITY
;  Common return point for FAT and IFS                                  ;@SEC
;fd40:   mov     di,CurrTCB                                             ;@SEC
fd40:                                                                   ;@SEC
        pushf                        ; save CY flag                     ;@SEC
        FlatContext <DS>                                                ;@SEC
        cmp     _fSec,0                                                 ;@SEC
        jz      fd40c                                                   ;@SEC
;                                                                       ;@SEC
;  for FAT, DOS_Delete can return ax != 0 without the CY flag being     ;@SEC
;  set. When CY is not set, the operation was successful. To let the    ;@SEC
;  callout know this, we will set AX = 0 in this case.                  ;@SEC
        popf                         ; restore CY flag                  ;@SEC
        jc      fd40a                ; CY set. don't zero out ax.       ;@SEC
        mov     ax,0                 ; use mov so CY not changed.       ;@SEC
fd40a:  pushf                        ; save CY flag                     ;@SEC
        CALLFAR SecDeletePost                                           ;@SEC

fd40c:  popf                         ; restore CY flag                  ;@SEC
        TaskContext <DS>                                                ;@SEC
else
fd40:
endif
        mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT ; (ES:DI) -> SFT
        push    bx
        call    RemoveShare             ; preserves AX and flags
        pop     bx
fd50:   call    PathDecVPBRef           ; Remove our referent in the VPB
        jc      fd55                    ;### Notify
        pushf
        push    ax                      ;### Notify
        mov     ax,2                    ;### Delete
        CALLFAR CNotify_Path_Wake       ;### Notify
        pop     ax                      ;### Notify
        popf
fd55:   pop     bp
        ret

EndProc FSD_Delete


;**     FSD_Move -  move/rename files/sub-directories
;
;       ENTRY   dx      = Curr_Dir_End for Ren_WFP
;               [WFP_Start] Points to SOURCE WFP string ("d:/" must be first
;                       3 chars, NUL terminated)
;               [Curr_Dir_End] Points to end of SOURCE Current dir part
;                       ( = -1 if current dir not involved, else Points
;                        to first char after last "/" of current dir part)
;               [Ren_WFP] Points to DEST WFP string ("d:/" must be first 3
;                       chars, NUL terminated)
;               [ThisCDS] Points to CDS being used
;                       (Low word = -1 if NUL CDS (Net direct request))
;               [Sattrib] Is attribute of search, determines what files
;                       can be found
;               [TCBThisFSC] = fsc of source and destination
;               [TCBThishVPB] = hVPB for the volume. This is guaranteed to be
;                               a valid hVPB since the only names that come
;                               through are local and remote file/directory
;                               names and they all have valid hVPBs (including
;                               UNC names).
;               [cMeta] indicates no metas
;
;       EXIT    'NC' if no error
;               'C' if error occurred
;                   AX is error code
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       PSEUDOCODE
;          if (FAT)
;            Volume_Access_Lock(EXCL)
;          ShCloseFile(source)
;          Ren_Check
;          AddShare(source)
;          AddShare(dest)
;          if (FAT)
;            Volume_Alloc_Lock(EXCL)
;            DOS_Rename
;          else
;            FS_Rename
;          RemoveShare(source)
;          RemoveShare(dest)
;          if (FAT)
;            Volume_Access_Unlock(EXCL)
;
;       NOTE:   If renaming directories/files between drives is ever allowed,
;               volume_access_lock will have to be called for the destination.
;               It is not at this point because 1) it is not necessary, and 2)
;               deadlock would occur in the directory case.
;               May context switch.
;

Procedure FSD_Move,<near,faronly>
    assume ds:nothing,es:nothing
LocalVar fm_destcde,WORD                ; cur_dir_end for renbuf
LocalVar fm_srcsft,DWORD                ; ptr to source sft
LocalVar fm_destsft,DWORD               ; ptr to destination sft
LocalVar fm_srceqdest,BYTE              ; 1 if src=dest, else 0
LocalVar fm_fstype,BYTE                 ; 0 if not FAT
LocalVar fm_saveax,WORD                 ; save area for ax reg          ;@SEC

        EnterProc <dx>                  ; init destcde

ifdef  SECURITY
; See if security is interested                                         ;@SEC
        FlatContext <ds>,ax                                             ;@SEC
        cmp     _fSec,0                                                 ;@SEC
        jz      fsm1                                                    ;@SEC
        CALLFAR SecMovePre                                              ;@SEC
        jnc     fsm1                                                    ;@SEC
        jmp     fsm73                                                   ;@SEC
fsm1:
endif

; do rename checks

        TaskContext <ds>
        mov     fm_fstype,0             ; Assume not FAT FS
        mov     di, TASKAREA:[CurrTCB]
        cmp     TASKAREA:[di].TCBThisFSC.Offst,FSC_LOCAL
        jne     short fsm3              ; Assumption was correct
        mov     fm_fstype,1             ; Mark this as FAT FS
;
; For the FAT FS, we need to lock the volume in order to prevent races
; between RENAME, CREATE and MKDIR. To be safe, we take an exclusive lock.
;
; We have to take the lock here since it is possible to block in AddShare or
; Ren_Check and this may cause race conditions later.
;
        mov     cx,VPB_ACC_EXCL
        CALLFAR Volume_Access_Lock

fsm3:   TaskContext <es>                ; Set up DS=ES=TaskArea for Ren_Check
        call    ShCloseFile
        call    Ren_Check               ; any illegal move attempted?
        LJC     fsm60                   ; yes, error exit
        mov     fm_srceqdest,dl

; if NONEWFILES, disallow renaming of file to itself.

IFDEF DISABLENEWFILES
        cmp     fm_srceqdest,1          ; src = dest?
        jne     short fsm05             ; no, continue
        mov     ax,ERROR_ACCESS_DENIED
        stc
        jmp fsm60
fsm05:
ENDIF
;
; add source and dest to sharing.
;
        mov     ax,sharing_deny_both    ; exclusive access to file
        mov     cx,opbatchbreak         ; break opbatch
        call    AddShare
        LJC     fsm60                   ; error, go return
        mov     si,CurrTCB
        mov     ax,TASKAREA:[si].TCBThisSFT.Offst ; save src sft ptr
        mov     fm_srcsft.Offst,ax
        mov     ax,TASKAREA:[si].TCBThisSFT.Segmt
        mov     fm_srcsft.Segmt,ax
        cmp     fm_srceqdest,1          ; src = dest?
        je      short fsm15             ; yes, don't enter dest into sharing

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        push    [bp].TCBWFP_Start       ; save source string pointer
        MOV     SI,[bp].TCBRen_WFP      ; Swap source and destination ptrs
        MOV     [bp].TCBWFP_Start,SI    ;  because AddShare is passed parameters
        mov     ax,sharing_deny_both    ; exclusive access to file
        mov     cx,opbatchbreak         ; break opbatch
        push    bp
        call    AddShare
        pop     bp
        pop     [bp].TCBWFP_Start       ; make this point to source string
        UnSetBP_CurrTCB
                                        ;72417 end
        jnc     short fsm10             ; no error, go continue rename

        cmp     ax,ERROR_SHARING_VIOLATION ; error because source and dest are same?
        stc
        LJNE    fsm55                   ; no, go return error
        mov     ax,ERROR_ACCESS_DENIED  ; yes, go return "compatible" error
        jmp     fsm55                   ; and remove destination from sharing

fsm10:  mov     si,CurrTCB
        mov     ax,TASKAREA:[si].TCBThisSFT.Offst ; save dest sft ptr
        mov     fm_destsft.Offst,ax
        mov     ax,TASKAREA:[si].TCBThisSFT.Segmt
        mov     fm_destsft.Segmt,ax

fsm15:  call    PathIncVPBRef           ; increment vpb ref count

        lds     si,TASKAREA:[si].TCBThisFSC     ; ds:si -> fsc
    ASSUME DS:NOTHING
        cmp     si,FSC_LOCAL            ; what file system
        jb      short fsm20             ; fsd, go call fsd worker

;
; Lock the volume for allocation so as to prevent races with MakeNode.
;
        mov     cx,VPB_ALLOC_EXCL
        CALLFAR Volume_Alloc_Lock       ; DOS_Rename will unlock the volume
;
; call FAT worker using special shutdown gate
; ShutdownGate  function name, exit label, seg with FSC segment (DS or ES)
;
        mov     dh,fm_srceqdest         ; dx = (src = dest?)
        ShutdownGate  DOS_Rename, fsm50, DS
;
; DOS_Rename took care of unlocking the volume alloc lock.
;

; The above jumps to fsm50 on completion

; ds:si -> fsc
; dx = Curr_Dir_End for Ren_WFP

fsm20:  mov     di,TASKAREA:CurrTCB             ; (di) = current TCB     ;### PTR B720732
        les     di,TASKAREA:[di].TCBThisCDS     ; (es:di) = CDS pointer  ;### PTR B720732
;### PTR B720732 fsm20: les     di,ThisCDS              ; es:di -> cds
    ASSUME ES:NOTHING
        lea     ax,[di].cd_fsi          ; es:ax -> cd_fsi
        push    es
        push    ax                      ; push pointer to cd_fsi
        lea     ax,[di].cd_fsd          ; es:ax -> cd_fsd
        push    es
        push    ax                      ; push pointer to cd_fsd
        push    ss
                                        ;72417 moved from PTDA to TCB
        mov     bx,bp
        mov     bp,CurrTCB
        push    [bp].TCBWFP_Start       ; push pointer to source path
        mov     ax,[bp].TCBCurr_Dir_End
        cmp     ax,-1                   ; is curr_dir_end set?
        je      short fsm30             ; no, pass -1 to fsd
        sub     ax,[bp].TCBWFP_Start    ; ax = Curr_Dir_End - WFP_Start
fsm30:  push    ax                      ; push iCurDirEnd of source
        push    ss
        push    [bp].TCBRen_WFP         ; push pointer to destination path
;          mov     dx,fm_destcde           ; dx = dest cur_dir_end  ; 159470
        mov     dx,ss:[bx-2]            ; 159470 (cheesy fix, but bp was saved
                                        ; 159470    to bx, then changed above)
        cmp     dx,-1                   ; is curr_dir_end set?
        je      short fsm40             ; no, pass -1 to fsd
        SetBP_CurrTCB
        sub     dx,[bp].TCBRen_WFP      ; dx = dest Curr_Dir_End - Ren_WFP
        UnSetBP_CurrTCB
fsm40:  push    dx                      ; push iCurDirEnd of destination
        mov     bp,bx
        SetBP_CurrTCB
        mov     al,TASKAREA:[bp].TCBPathNameType
        UnSetBP_CurrTCB
                                        ;72417 end
        and     ax,PNType_NEWFILE       ; ax = 0 or 0x0040
        push    ax                      ; attr (file attributes for CREATE)
        call    ds:[si].FS_MOVE         ; call FSD worker
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0

; remove source and dest from sharing.
; RemoveShare preserves flags and AX.

ifdef SECURITY
;  Common return point for FAT and IFS                                  ;@SEC
;fsm50:  call    PathDecVPBRef           ; decrement vpb ref count      ;@SEC
fsm50:                                                                  ;@SEC
        pushf                        ; save CY flag                     ;@SEC
        FlatContext <DS>                                                ;@SEC
        cmp     _fSec,0                                                 ;@SEC
        jz      fsm50c                                                  ;@SEC
;                                                                       ;@SEC
;  for FAT, DOS_Rename can return ax != 0 without the CY flag being     ;@SEC
;  set. When CY is not set, the operation was successful. To let the    ;@SEC
;  callout know this, we will set AX = 0 in this case.                  ;@SEC
        mov     fm_saveax,ax         ; save ax                          ;@SEC
        popf                         ; restore CY flag                  ;@SEC
        jc      fsm50a               ; CY set. don't zero out ax.       ;@SEC
        mov     ax,0                 ; use mov so CY not changed.       ;@SEC
fsm50a: pushf                        ; save CY flag                     ;@SEC
        CALLFAR SecMovePost                                             ;@SEC
        mov     ax,fm_saveax         ; restore ax                       ;@SEC
fsm50c: popf                         ; restore CY flag                  ;@SEC
        TaskContext <DS>                                                ;@SEC
else
fsm50:
endif
        call    PathDecVPBRef           ; decrement vpb ref count
        pushf
        cmp     fm_srceqdest,1          ; src = dest?
        je      short fsm54             ; yes, go return src from sharing
        les     di,fm_destsft           ; (ES:DI) -> dest sft
        call    RemoveShare             ; remove dest from sharing
fsm54:  popf
fsm55:  les     di,fm_srcsft            ; (ES:DI) -> src sft
        call    RemoveShare             ; remove src from sharing

fsm60:  pushf
        cmp     fm_fstype,1
        jne     short fsm70             ; Not FAT FS. No need to unlock volume.
        mov     cx,VPB_ACC_EXCL
        CALLFAR Volume_Access_Unlock    ; Unlock the volume for access
fsm70:  popf                            ; Restore any error indications
        jc      fsm73                   ;### Notify
        pushf
        push    ax                      ;### Notify
        mov     ax,8                    ;### Rename/Move
        CALLFAR CNotify_Path_Wake       ;### Notify
        pop     ax                      ;### Notify
        popf
fsm73:  LeaveProc
        ret

EndProc FSD_Move


;**     DupJFN - Duplicate file handle data
;
;       Duplicate the parent process's file handles for the child process.
;       Called from Exec
;
;       ENTRY   (DS)    = new PTDA (not necessarily a permanent selector!)
;               (ES:DI) = pointer to file table to fill in
;               (CX)    = selector of new JFN Table (permanent)
;                         NOTE: We use this for the JFN_Flg_Ptr selector
;                               also, since it is set ONLY if we are copying
;                               to a prot-mode task, in which case both the
;                               Handle and Flags tables will be in the PTDA.
;       EXIT    None
;
;       CALLS   SFFromHandle, DOS_Dup, pJFNFromHandle
;
;       USES    AX, BX, CX, DS, SI, DI, DS, ES, flags
;
;       ALGORITHM
;
;          call DupPMJFN

Procedure DupJFN,<FARONLY,FAR16>
    ASSUME      DS:TASKAREA,ES:NOTHING,SS:TASKAREA

        DBPRT 8h,40h,<DupJFN 8:40 - es:bx = $x:$x di = $x>,<ES,BX,DI>;


; ds = selector of new ptda valid in parent's context
; cx = selector of new ptda valid in child's context

; we assume destination is ds:JFN_Table so we don't bother passing es:di
; to DupPMJFN

IFDEF FSSTRICT
; verify above assumption
        cmp     di,offset TASKAREA:JFN_Table
        jne     short djfn12
        mov     ax,es
        mov     bx,ds
        cmp     ax,bx
        je      short djfn15
djfn12: InternalError  <DupJFN: es:di not JFN_Table>
ENDIF

djfn15: call    DupPMJFN
        ret
EndProc DupJFN


;**     DupPMJFN - Duplicate file handle data for PM process
;
;       Duplicate the parent process's file handles for the child
;       process.
;
;       ENTRY   DS = selector to child's PTDA valid in parent's context
;               CX = selector to child's PTDA (permanent) valid in
;                    child's context.
;               SS = selector of parent's PTDA
;
;       EXIT    if carry clear - ok
;               if carry set - error
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       ALGORITHM
;
;       if (parent's JFN table is in PTDA)
;         copy parent's JFN table to child's PTDA
;       else /* parent's JFN table has been grown into segments */
;         allocate JFN and flags segments for child
;         copy parent's JFN and flags segments to child's

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

        LocalVar dpj_selParent,WORD     ; parent's selector to child's ptda
        LocalVar dpj_selChild,WORD      ; child's selector to child's ptda

        EnterProc       <ds,cx>         ; initialize localvar's

        mov     bx,ss:JFN_Length        ; bx = parent's JFN length
        cmp     bx,FILPERPROC           ; Has it been grown ?
        ja      short dpj20             ;   YES, go allocate segments

; bx = parent's JFN_Length
; ds = parent's selector to child's ptda
; cx = child's selector to child's ptda

; do PTDA to PTDA dup
        mov     ds:JFN_Length,bx        ; child's JFN_Length = parent's
        mov     di,offset TASKAREA:JFN_Table
        mov     ds:JFN_pTable.Offst,di
        mov     ds:JFN_pTable.Segmt,cx
        mov     es,dpj_selParent        ; es:di->child's JFN table

; CopyJFNTab(es:di->destination JFN table)
        call    CopyJFNTab

    assume ds:nothing, es:nothing

        mov     es,dpj_selParent        ; es = child's ptda
        mov     di,offset TASKAREA:JFN_Flags
        mov     es:JFN_Flg_Ptr.Offst,di
        mov     cx,dpj_selChild         ; cx = selector to child's ptda
        mov     es:JFN_Flg_Ptr.Segmt,cx
; CopyJFNTab(es:di->child's flag table)
        call    CopyJFNFlag
        jmp     short dpjx              ; done
    assume es:nothing

; Parent's JFN table has been grown into a JFN table segment and a JFN
; flag segment. Allocate similar segments for the child.

; bx = parent's JFN_Length
; ds = parent's selector to child's ptda

dpj20:  mov     dx,ds:ptda_handle       ; dx = child ptda
; bx=selFlag,dx=selJFN = AllocJFNSeg(bx=size,dx=hPTDA)
        call    AllocJFNSeg             ; allocate segments
    assume ds:nothing                   ; ds was destroyed
        jc      short dpjx              ; ERROR, exit with CARRY and
                                        ; error code in ax

; bx = gdt selector to new flag segment
; dx = gdt selector to new JFN segment

; save child's selectors in child's ptda
        mov     ds,dpj_selParent        ; ds = parent's sel to child's ptda
        xor     si,si
        mov     ds:JFN_pTable.Segmt,dx
        mov     ds:JFN_pTable.Offst,si  ; JFN_pTable -> new JFN segment
        mov     ds:JFN_Flg_Ptr.Segmt,bx
        mov     ds:JFN_Flg_Ptr.Offst,si ; JFN_Flg_Ptr -> new flag Segmt
        mov     si,ss:JFN_Length        ; si = parent's JFN_Length
        mov     ds:JFN_Length,si        ; store in child's ptda

; copy JFN flag table from parent to child
    assume      es:nothing
        mov     es,bx
        xor     di,di                   ; es:di -> new flag table

        call    CopyJFNFlag             ; copy flags to es:di-> dest flag table

        mov     es,dx
        xor     di,di                   ; es:di -> new JFN table

        call    CopyJFNTab              ; copy JFNs to es:di-> dest JFN table
        clc                             ; clear carry and exit

dpjx:   LeaveProc
        ret
EndProc DupPMJFN


;**     CopyJFNTab - copy JFN table
;
;       Copy real JFN table and flags from current process to given
;       destination.
;
;       ENTRY   es:di -> destination JFN table
;
;       EXIT    no error indication
;
;       USES    ax,bx,cx,si,di,ds,es,flags
;
;       ALGORITHM
;
;       for (handle = 0; handle < JFN_Length; handle++) {
;         if (handle is duplicatable)
;           copy SFN from source handle slot to destination handle slot
;         else
;           write SFN_NULL into destination handle slot
;       }

Procedure CopyJFNTab,NEAR,LOCAL
    assume ds:nothing,es:nothing,ss:TASKAREA

        SaveReg <di,es>         ; [tos] -> destination JFN table
        RestoreReg <ds,si>      ; ds:si -> destination JFN table
        mov     cx,JFN_Length   ; cx = #jfns to copy
        xor     bx,bx           ; bx = handle currently being duped

; ax=SFN = Dup1Handle(bx=handle); uses ax,di,es
cjt1:   call    Dup1Handle      ; ax = SFN
        mov     ds:[si],ax      ; save SFN in destination handle slot
        inc     si
        inc     si              ; update destination JFN table pointer
        inc     bx              ; next JFN...
        loop    short cjt1

        ret
EndProc CopyJFNTab


;**     Dup1Handle - duplicate one handle
;
;       Perform error checks on the handle. If duplicatable, then
;       call DOS_Dup and return carry clear. If not duplicatable,
;       return with carry set.
;
;       ENTRY   bx = real handle
;
;       EXIT    if carry clear - handle can be duped
;                 ax = SFN
;               if carry set - handle can't be duped
;                 ax = SFN_NULL
;
;       USES    ax,di,es,flags
;
;       ALGORITHM
;
;       if (handle has inherit bit clear)
;         return with carry set, ax=SFN_NULL
;       call DOS_Dup
;       if (carry)
;         return with carry set, ax=SFN_NULL
;       else
;         return with carry clear, ax=SFN

Procedure Dup1Handle,NEAR,LOCAL
    assume ds:nothing,es:nothing,ss:TASKAREA

        les     di,JFN_Flg_Ptr
        test    byte ptr es:[di+bx],JFN_DIH ; do we inherit this file?
        jnz     short d1h20             ; no, set carry and exit

; es:di = SFFromHandle(bx=handle); uses es,di,ax
        call    SFFromHandle            ; get SF pointer
        jc      short d1h20             ; Error, go set carry and exit

; The handle we have found is inheritable.

; DOS_Dup(es:di->sft); uses ax,es,di
        CALLFAR DOS_Dup                 ; signal duplication
        jc      short d1h20             ; dup failed

; get the old SFN for copy
; es:di = pJFNFromHandle(bx=handle); uses ax,es,di
        call    pJFNFromHandle          ; ES:DI is JFN
        mov     ax,es:[di]              ; return ax = SFN
        ret

d1h20:  stc                             ; don't dup handle
        mov     ax,SFN_NULL             ; return invalid SFN
        ret
EndProc Dup1Handle


;**     CopyJFNFlag - copy JFN flag table
;
;       Copy JFN flag table from current process to given destination.
;
;       ENTRY   es:di -> destination JFN flag table
;
;       EXIT    no error indication
;
;       USES    al,cx,si,di,ds,flags

Procedure CopyJFNFlag,NEAR,LOCAL
    assume ds:nothing,es:nothing,ss:TASKAREA
        lds     si,ss:JFN_Flg_Ptr       ; ds:si -> old JFN flag table
        mov     cx,JFN_Length           ; cx = #flags to copy

; es:di -> JFN flag to write into
; ds:si -> parent's JFN flag
; cx = number of flags remainding

cjf10:  lodsb                           ; al = flag
        and     al,NOT JFN_WRTTHR       ; Clear the write through bit
        stosb                           ; Store flag in new table
        loop    short cjf10             ; Keep going until all copied

        ret
EndProc CopyJFNFlag


;***    CheckPathSyntax - Check the path to see if it is syntactically correct
;
;       CheckPathSyntax checks the path to ensure that it is correct for
;       the FAT file system. It assumes that the path has been Canonicalized
;       beforehand, and its length is legal.
;       It is assumed that the only thing to check is the lengths of the
;       names in the path, and the characters in the path.
;
;       ENTRY   [WFP_Start] Points to WFP string, NULL terminated.
;                   For non-UNC names:  "d:\" must be first 3 chars.
;                   FOR UNC names:      "\\"  must be first 2 chars.
;               [ThisCDS] Points to CDS being used
;               [TCBThishVPB] = hVPB
;               DS = TASKAREA
;
;       EXIT    'C' if path is not legal for the FAT file system
;                   AX = error code from NameTrans or ERROR_INVALID_PATH
;               'NC' if path is legal for the FAT file system
;
;       USES    AX, SI, Flags
;
;       ALGORITHM
;
;           if (UNC name)
;               skip over first two chars ("\\")
;           else
;               skip over first three chars ("d:\")
;           While ((! end of path) && (!error) {
;               if (NameTrans())
;                   error = ERROR_INVALID_PATH
;               else
;                   Move to next component in path
;           }
;           Return any error
;
Procedure   CheckPathSyntax,hybrid
    ASSUME DS:TASKAREA, ES:NOTHING, SS:TASKAREA
        SegAssert SS,<DS>,"CheckPathSyntax"

        LocalVar    cps_StrOffst,WORD       ; Current offset into path string

        EnterProc
                                            ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start ; DS:SI -> canonicalized path
        mov     ax,3                        ; Assume not UNC name. Skip over "d:\"
        SetBP_CurrTCB
        test    TASKAREA:[bp].TCBPathNameType,PNType_UNC
        UnSetBP_CurrTCB
                                            ;72417 end
        jz      short cps5                  ; Not UNC name. Our assumption was correct
        mov     ax,2                        ; Skip over leading "\\"
cps5:   add     si,ax                       ; Skip over leading characters
cps10:  mov     [cps_StrOffst],si
        call    NameTrans                   ; Validate 8.3 name
        jc      short cps50                 ; Invalid char in component name. AX = error code
        cmp     si,[cps_StrOffst]
        je      short cps60                 ; First character was invalid. Error.
;
; Get next character (either a path separator or NULL). If it is NULL, then
; we have reached the end of string.
; This also works for DBCSs since a NULL is not a DBCS character.
;
; DS:SI has been advanced by NameTrans past the NULL or path separator.
;
        mov     al,[si-1]                   ; Get NULL or Path Separator
        or      al,al
        jnz     short cps10                 ; Not end of string. DS:SI -> Next component
cps50:  LeaveProc
        ret

cps60:  mov     ax,ERROR_INVALID_PATH
        stc
        jmp     short cps50

EndProc     CheckPathSyntax


Break   <DOS_Abort -- Close all files for process>

;**     DOS_Abort - free file system resources for terminating process
;
;       Close all files and free all SFTs for this PID.  Close all searches
;       in progress.  Release all locked drives.
;
;       This routine is called for OS/2 process termination, VDM process
;       termination and VDM PDB process (DOS application) termination.
;       During PDB process termination only the file handles opened by
;       the PDB process are cleaned up.  During OS2 or VDM (PTDA) process
;       termination (CurrentPDB == 0), all file handles are cleaned up.  Note
;       that CurrentPDB == 0 means the whole VDM process is terminating.
;
;       ENTRY   none
;
;       EXIT    None
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure DOS_Abort,<near,faronly,far16>

;*  Unlock any drives locked by this process

        mov     bx,[Pid]                ; (BX) = our process ID
        DosContext <ds>

        cmp     NumIO,0                 ; How many drives we got?        AZ10291
        jz      short dab20             ; None? Okay, we get out of here AZ10291

        lds     si,DPBHead              ; (DS:SI) = start of DPB list
     ASSUME     DS:NOTHING
dab10:  cmp     si,0ffffh               ; while not end-of-DPB-list {
        je      short dab20             ;   .
        CALLFAR UnlockDrive             ;   unlock drive, if we locked it
        lds     si,[si].dpb_next_dpb    ;   (DS:SI) = next DPB
        jmp     short dab10             ; }

;*  NOTE:   We used to scan the VPB handle table in the PTDA here
;           to remove search references to VPBs.  Since this action is
;           only performed at PTDA process termination, and DOS_Abort is
;           called at PDB process termination, too, this scanning code
;           was moved into ProcessTerminate.

;*  Close all open files

dab20:  mov     cx,[JFN_Length]         ; number of JFNs
dab30:  mov     bx,cx
        push    cx
        dec     bx                      ; get jfn (start with last one)

;*  If an SFT is bad (no VPB attached) (due to a BeginFormat failure), we must
;   still call w_Close, which knows how to handle it.  'NZ' & 'C' indicates
;   that there is an SFT which must be closed.
;
;   'Z' means that the handle is bad, so nothing to do (get next handle)

        push    bx
        call    SFFromHandle            ; (ES:DI) = sft
        pop     bx
        jnc     short dab40
        jz      short dab60             ; 'Z' means bad handle, skip handle

dab40:  mov     ax,[CurrentPDB]         ; (AX) = current PDB
        or      ax,ax                   ; is PDB == 0
        jz      short dab45             ;  yes, match all PDBs
        cmp     es:[di].sf_fsi.sfi_PDB,ax ; is this one of ours?
        jne     short dab60               ; no, skip it
        jmp     short dab48

dab45:  cmp     [ProcType],PT_REALMODE  ; is this a VDM termination
        jne     dab48                   ;

; yes, vdm termination; we have to get rid of this sft
; The easiest way is to do this is to make ref count to 1.
; Killing the SFT for the CON is a bad idea
DEVID_DEVICE_CONS EQU (DEVID_DEVICE_CON_OUT OR DEVID_DEVICE_CON_IN)

        test    es:[di].sf_flags,DEVID_DEVICE   ; device ?
        jz      dab47                           ; no, dec ref to 1
        test    es:[di].sf_flags,DEVID_DEVICE_CONS ; is device the console ?
        jnz     short dab48                     ; Yes, keep ref count the same
dab47:
        mov     es:[di].sf_ref_count,1          ; Get rid of the sft

dab48:  test    es:[di].sf_flags2,SF_BEGINFORMAT_FAILED
        jnz     short dab50             ; if BeginFormat error, skip PhysClose

;*  The device manager maintains its own physical disk handle/locks
;   table. Therefore, we must notify it now to allow it to clean up
;   gracefully.

        call    PhysClose               ; close possibly physical handle
ifdef FSSTRICT
        LJC     dabie                   ; bad SFT (BeginFormat failure)
endif
        jnz     short dab60             ; physical handle closed

dab50:
        DosContext <ds>
        mov     byte ptr DOS_Abort_Close, -1 ; no filehandlelockid check
        CALLFAR w_Close                 ; close handle
dab60:  pop     cx                      ;  skip close if phys disk handle
        loop    dab30                   ; and do 'em all
    ASSUME   DS:NOTHING
        call    FSD_Exit                ; tell fsds this process is dead
        cmp     [JFN_Length],FILPERPROC ; Have segmnets been allocated?
;       je      short dabx              ; no, just go exit, #80335- change to
;                                         long jump
        jne     dab61                   ;                  #80335
        jmp     dabx                    ; no, just go exit #80335
dab61:                                  ;                  #80335


; Free JFN segs only if CURRENT_PDB == 0 i.e VDM/OS2 termination
; if a VDM process aborts, CURRENT_PDB will not be 0.
;
        mov     ax,[CurrentPDB]         ; (AX) = current PDB
        or      ax,ax                   ; is PDB == 0
        jnz     short dabx              ;  no, Not VDM/OS2 termination
                                        ; let the JFN seg be there

        SaveReg <cx,ax,es,ds,si,di>                     ;#80335
        mov     cx,FILPERPROC                           ;#80335
        les     di,[JFN_pTable]                         ;#80335
        mov     si,GSEL GDT_PTDA                        ;#80335
        mov     ds,si                                   ;#80335
        mov     si,offset TASKAREA:JFN_Table            ;#80335
dab70:                                                  ;#80335
        mov     ax,es:[di]                              ;#80335
        mov     ds:[si],ax                              ;#80335
        inc     di                                      ;#80335
        inc     di                                      ;#80335
        inc     si                                      ;#80335
        inc     si                                      ;#80335
        dec     cx                                      ;#80335
        jnz     dab70                                   ;#80335

        mov     cx,FILPERPROC                           ;#80335
        les     di,[JFN_Flg_Ptr]                        ;#80335
        mov     si,GSEL GDT_PTDA                        ;#80335
        mov     ds,si                                   ;#80335
        mov     si,offset TASKAREA:JFN_Flags            ;#80335
dab80:                                                  ;#80335
        mov     al,es:[di]                              ;#80335
        mov     ds:[si],al                              ;#80335
        inc     di                                      ;#80335
        inc     si                                      ;#80335
        dec     cx                                      ;#80335
        jnz     dab80                                   ;#80335
        RestoreReg <di,si,ds,es,ax,cx>                  ;#80335

        mov     ax,[JFN_pTable].Segmt   ; (ax) = selector for JFN table
        mov     [JFN_pTable].Segmt,0    ; clear field for perfview  ;58461
        mov     bx,[JFN_Flg_Ptr].Segmt  ; (bx) = selector for JFN flags
        call    FreeJFNSeg
        SaveReg <di>                               ;llm 72774
        mov     di,offset TASKAREA:JFN_Table       ;llm 72774
        mov     [JFN_pTable].Offst,di              ;llm 72774
        mov     [JFN_pTable].Segmt,GSEL GDT_PTDA   ;llm 72774
        mov     di,offset TASKAREA:JFN_Flags       ;llm 72774
        mov     [JFN_Flg_Ptr].Offst,di             ;llm 72774
        mov     [JFN_Flg_Ptr].Segmt,GSEL GDT_PTDA  ;llm 72774
        mov     [JFN_Length],FILPERPROC            ;llm 72774
        RestoreReg <di>                            ;llm 72774

dabx:   ret
ifdef FSSTRICT
dabie:  InternalError   <DOS_Abort: PhysClose>
endif
EndProc DOS_Abort


;**     DOS_AbortVDM - close all search handles and FCB SFTs for a VDM process
;
;       This routine is called from demDosAbort for PDB process termination
;       and DEMDestroyVDM2 for VDM process termination.  It is cleans up the
;       search handles and FCB SFTs owned by the PDB process in the first
;       case and cleans up everything no matter what PDB owns when the VDM
;       process terminates.
;
;       We do need to explicitly close FCBs.  Reasons are as follows: If we
;       are running in the no-sharing no-network environment, we are simulating
;       the 2.0 world and thus if the user doesn't close the file, that is his
;       problem BUT... the cache remains in a state with garbage that may be
;       reused by the next process.  We scan the set and blast the ref counts
;       of the FCBs we own.
;
;       If sharing is loaded (which is always the case in OS/2), then
;       the following call to close process will correctly close all
;       FCBs.  We will then need to walk the list AFTER here.
;
;       Scan the FCB cache for guys that belong to this process and
;       explicitly close them.  Note that FCBs can't be shared between
;       processes.
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure DOS_AbortVDM,<far16>
        cmp     word ptr [SrchRmp],0    ; if (3xbox searches exists)
        je      short dav10
        call    Find3xExit              ;    Find3xExit

dav10:  FlatContext ds
        .386
        mov     cx,[_sftFCB]            ; get pointer to the table
    ASSUME   DS:NOTHING
        CpuMode reset
        jcxz    short cusx              ; no FCBs allocated
        mov     es,cx
    ASSUME   ES:NOTHING
        mov     cx,es:[sft_count]
        jcxz    short cusx
        mov     di,SIZE sft             ; (es:di) -> sft entry array

dav20:  cmp     es:[di].sf_ref_count,0  ; SFT in use?
        jz      short dav40             ; no, try next
        mov     ax,[CurrentPDB]         ; (AX) = current PDB
        or      ax,ax                   ; if PDB == 0
        jz      short dav30             ;  match all PDBs
        cmp     es:[di].sf_fsi.sfi_PDB,ax ; is this one of ours?
        jne     short dav40               ; no, skip it

dav30:  push    bp
        mov     bp,[CurrTCB]
        mov     ax,[bp].TCBProc_ID
        mov     bx,[bp].TCBUser_ID
        pop     bp

        cmp     es:[di].sf_fsi.sfi_PID,ax ; is this one of ours?
        jne     short dav40               ; no, skip it
        cmp     es:[di].sf_fsi.sfi_UID,bx ; is this one of ours?
        jne     short dav40               ; no, skip it
        mov     es:[di].sf_ref_count,1  ; assure only one close is necessary

        TaskContext <ds>
        push    bp
        mov     bp,[CurrTCB]
        mov     [bp].TCBThisSFT.Offst,di
        mov     [bp].TCBThisSFT.Segmt,es
        pop     bp

        SaveReg <es,di,cx>
        mov     bx,-1                   ; to indicate fcb close
        call    FSD_Close               ; close the SFT
        RestoreReg <cx,di,es>
        mov     es:[di].sf_ref_count,0  ; blast ref count
dav40:  add     di,size sf_entry
        loop    short dav20
cusx:   RETD
EndProc   DOS_AbortVDM


;***    Ren_Check - do necessary checks before renaming a directory
;
;       we do the following checks in this procedure
;
;       1. (if file) whether it is open currently
;       2. (if dir) whether any file in this dir currently open
;
;       3. whether source name is a prefix of destination name (circularity)
;
;       4. whether the source directory is current directory for any process
;
;       This procedure is used by DOSMOVE
;
;       ENTRY   wfp_start -> source name
;               ren_wfp -> dest name
;               DS, ES = TASKAREA
;
;       EXIT    'NC' - no error ( DS = TASKAREA )
;                   dl = 0 -> source and dest are different
;                   dl = 1 -> source and dest are exact match
;               'CY' - error, (AX) = error code
;                       - ERROR_ACCESS_DENIED
;                       - ERROR_CIRCULARITY_REQUESTED
;                       - ERROR_DIRECTORY_IN_CDS
;
;       USES    ax, cx, dx, di, si, es, flags
;
Procedure Ren_Check,near,local
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
    SegAssert SS,<DS,ES>,"Ren_Check"
;
; check whether the source file or a file in the source dir is opened by
; someone ; go through MFTs in the system checking for the source file
;
                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        mov     di,TASKAREA:[di].TCBWFP_Start   ;
                                        ;72417 end
        CALL32  PrefLookupMFT,nocheck   ; CY if not found;
                                        ; NC if found & ZR if exact match
        jnc     rc40                    ; error if found in MFT

;   CIRCULARITY TEST
;   ================
;
;   Before move of directory from its current parent to another parent.
;   Check that the new parent is not one of our decendents, ie not a child
;   or a childs child etc., as this would cause a circularity in the
;   directory system.
;
;   If the source canonical directory name is a prefix of the target canonical
;   directory path, then error - since this would cause a circularity.
;
;;BUGBUG - nagara - we are doing this test now irrespective of file type.
; we want to do this only for dirs. But this test will work even when we use
; file names.

;
;   setup correct register for prefix compare subroutine
;
                                        ;72417 moved from PTDA to TCB
rc10:   mov     si,CurrTCB
        mov     di,TASKAREA:[si].TCBWFP_Start   ; get potential prefix pointer source name
        mov     si,TASKAREA:[si].TCBRen_WFP     ; get target string pointer
                                        ;72417 end
;
; es:di->current dir path (potential prefix)
; ds:si->proposed new dir path
;
; ZR=prefix found=PathPref(es:di->potential prefix, ds:si->path)
;
        call    PathPref                ; Check target for source prefix
                                        ; Z == prefix or exact match
        JZ      rc50                    ; jump if circularity error
        xor     dl,dl                   ; source and dest are different

; Current Directory Structure (CDS) Test
; ======================================
;
; Before renaming a directory, check to see if the source directory is
; a prefix of any CDS path. If so, don't do the rename.

rc20:   TaskContext <ds>,si
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start  ; (ds:di)-> source path
                                        ;72417 end
        mov     dh,ds:[si]              ; (dh) = drive letter
        xor     di,di                   ; Start at beginning of chain
        call    ScanCDS                 ; is source a prefix of some CDS
        TaskContext ds
    assume es:nothing
        cmc                             ; error if cds found and no err if no
                                        ; cds
        jnc     short rc30              ; no prefix error, go continue rename
        mov     ax,ERROR_DIRECTORY_IN_CDS ; preset error code
rc30:   ret

;
; The potential prefix given is a prefix or exact match of given string
; ds:si -> points to the string after matching
;
rc50:   cmp     byte ptr [si],0         ; exact match?
        mov     dx,1                    ; assume exact match
        je      short rc20              ; yes, go do CDS check
        mov     ax,ERROR_CIRCULARITY_REQUESTED  ; Circularity on a directory.
        stc
        jmp short rc30
;
; AX = error code to return
;b713457
rc40:   jz      rc10                    ; file is open or oplocked, later
                                        ; sharing will catch it, continue
        stc                             ;
        mov     ax,ERROR_ACCESS_DENIED  ;
        jnz     short rc30              ; a file is open in this directory
        mov     ax,ERROR_SHARING_VIOLATION
        jmp     short rc30              ; file is open by someone else

EndProc Ren_Check


HIGH2CODE   ENDS

        CpuMode 386
        FARCODE HIGH32
HIGH32CODE SEGMENT

Procedure _FSAbortVDM,near
        SaveReg <ebx, esi, edi>
        CALL16  DOS_AbortVDM
        RestoreReg <edi, esi, ebx>
        FlatContext <ds,es>,eax
        RETC
EndProc   _FSAbortVDM

HIGH32CODE ENDS

        CpuMode 286

        FARCODE HIGH4
HIGH4CODE  SEGMENT


Break <DOS_Close -- Close FILE from SFT>

;***    DOS_Close - Close the specified file
;
;       The SFT passed is closed and the directory information is
;       updated if necessary.
;
;       ENTRY   (ES:DI) -> SFT
;
;       EXIT    if 'NC' - success
;                  sf_ref_count decremented for pipe and named pipe (not for file)
;                  ES:DI - points to SFT
;               if 'C'
;                  AX is error code
;                    ERROR_INVALID_HANDLE
;                    ERROR_FILE_NOT_FOUND
;                    ERROR_ACCESS_DENIED
;
;       USES   ax,bx,cx,dx,si,di,ds,es,flags
;
;       EFFECTS  Switches Context
;
;       PSEUDOCODE
;             if (pipe)
;                ClosePipe
;             else
;                DOS_Commit (update directory entry)

Procedure   DOS_Close,faronly
    ASSUME ds:nothing,ES:NOTHING
        PMONLY

        Assert  ISSFT,<ES,DI>,<"DOS_Close">
        DBPRT   40h,1,<CLOSE SFT: $x:$x\n>,<ES,DI>

        mov     bx,es:[di].sf_flags     ; bx = sf_flags
        test    bx,SF_PIPE              ; pipe?
        jz      short docl10            ; not a pipe
        test    bx,SF_NMPIPE            ; named pipe?
        jz      short docl5             ; no, go close anony pipe
        call    NmPipeClose
        ret

docl5:  CALLFAR ClosePipe               ; close pipe
        ret

; (ES:DI) -> SFT

docl10: TaskContext DS,,noassume
        SaveReg <bp>
        call    DOS_Commit              ; update the directory entry
        RestoreReg <bp>
        ret
EndProc DOS_Close


;**     DirFromSFT - locate a directory entry given an SFT.
;
;       Load the directory entry referred to by some SFT into a
;       disk buffer.
;
;       ENTRY   ES:DI point to SFT
;               DS = TASKAREA
;
;       EXIT    ExtErr_Locus = ERRLOC_DISK
;               CurBuf points to buffer
;               Carry Clear -> operation OK
;                   ES:DI point to entry
;                   ES:BX point to buffer
;                   DS:SI point to SFT
;               Carry SET   -> operation failed
;                   registers trashified
;
;       USES    ALL
;

Procedure   DirFromSFT,NEAR
        SegAssert SS,<DS>,"DirFromSFT"
        ASSUME  DS:TASKAREA, ES:NOTHING

        mov     [ExtErr_Locus],ERRLOC_DISK
        SaveReg <ES,DI>
        mov     DX,word ptr ES:[DI.sf_fsd.sfdFAT_dirsec]
        mov     CX,word ptr ES:[DI.sf_fsd.sfdFAT_dirsec].hiword

        mov     bp,CurrTCB
        mov     [bp].TCBAllowed,ALLOWED_FAIL + ALLOWED_RETRY
        SaveReg <bp,ds>         ; TOS = currtcb
        mov     bp,es:[di].sf_fsi.sfi_hVPB
        VPBContext es,bp
        xor     AL,AL                   ; Pre read
        CALLFAR GetBuffr
        RestoreReg      <es,di> ; di = CurrTCB
        jc      short PopDone
        RestoreReg  <SI,DS>             ; Get back SFT pointer
    ASSUME      DS:NOTHING,ES:TASKAREA
        les     DI,TASKAREA:[DI].TCBCurBuf
        or      ES:[DI.buf_flags],BUF_ISDIR
        mov     BX,DI                   ; ES:BX point to buffer header
        lea     DI,[DI].BUFINSIZ        ; Point to buffer
        mov     AL,SIZE dir_entry
        mul     [SI].sf_fsd.sfdFAT_dirpos
        add     DI,AX                   ; Point at the entry
        ret                             ; carry is clear

PopDone:RestoreReg  <DI,ES>
        ret
EndProc DirFromSFT,NoCheck

;**     DOS_Commit - update the directory entry for a local file
;
;       Perform local commit operation by updating the directory entry
;       on disk with information from the SFT.
;
;       ENTRY   ES:DI   ( = [ThisSFT]) point to SFT
;               BX      sf_flags from SFT
;               DS      TASKAREA
;
;       EXIT    'C' set if problems writing to disk,
;                       AX = standard error code
;
;       USES    ALL
;

Procedure DOS_Commit,hybrid
    ASSUME DS:TASKAREA, ES:NOTHING, SS:TASKAREA
        SegAssert SS,<DS>,"DOS_Commit"
;
; File clean or device does not get stamped nor disk looked at.
;
        TEST    BX,DEVID_FILE_CLEAN + DEVID_DEVICE + SF_PIPE ; Clears carry

; We assume that the only time we need to do I/O is when file is dirty. In
; the case of FileTimes, it sets the dirty bit after it changes the time.

        LJNZ    dcmt30          ; not a dirty file, go check for name pipe

        CALL    LockMFT                 ; lock MFT associated with SFT (Excl.)
        CALL    FileCommit
        jc      short dcmt10
        CALLFAR FNotify_Handle_Wake

;### PTR B721312    CP13 B713303
; if file commit was successful, reset time stamps flags so we don't have to ;### PTR B721312
; time stamp the SFT again (unless another write occurs)                     ;### PTR B721312
                                                                             ;### PTR B721312
        mov     bx,[CurrTCB]                                                 ;### PTR B721312
        lds     si,TASKAREA:[bx].TCBThisSFT     ; ds:si -> SFT               ;### PTR B721312
    assume ds:nothing                                                        ;### PTR B721312
        mov     ds:[si].sf_fsi.sfi_tstamp,0     ; reset time stamps bits     ;### PTR B721312

dcmt10: call    UnlockMFT               ; UnlockMFT preserves flags
dcmt20: ret

dcmt30: test    bx,SF_NMPIPE            ; named pipe?
        jz      short dcmt20            ; no, plain pipe, device, or clean
                                        ; file; do nothing

ifdef FSSTRICT                          ; panic if sf_nmpipe=1, sf_pipe=0
        test    bx,SF_PIPE
        jz      short dcmtie
endif
        call    NmPipeCommit
        ret

ifdef FSSTRICT
dcmtie: InternalError <DOS_Commit: bad sf_flag>
endif

EndProc DOS_Commit


;******  FileCommit
;
;       Perform local commit operation by updating the directory entry
;       on disk with information from the SFT.
;
;       ENTRY   ES:DI   ( = [ThisSFT]) point to SFT
;               DS      TaskArea
;
;       EXIT    'C' set if problems writing to disk,
;                       AX = standard error code
;
;       USES    AX,BX,CX,DX,SI,DI,**BP**,DS,ES
;
Procedure       FileCommit,NEAR

        test    es:[di].sf_fsi.sfi_mode,open_direct ; is it a direct DASD device?
        jz      short flc1              ; no, go do normal file committing

;       We do NOT update the directory entry (there ain't one for DASD
;       devices).  However, we do need to flush the buffers.  We need to
;       retrieve the VPB for the device.

        push    es                      ; (don't use savereg, endproc gets upset)
        push    di                      ; set up stack for later common code
        mov     ax,es:[di].sf_fsi.sfi_hVPB      ; retrieve VPB handle for flushing
        jmp     flc5                    ; go join common flushing code


;*      Commit a local file

;BUGBUG 16-Oct-1987 bens Optimize register setup for SetSFTTimes and DirFromSFT

flc1:   SaveReg <ax>                    ; Set TCBThishVPB for fVolumeWriteable
        mov     bp,CurrTCB
        mov     ax,es:[di].sf_fsi.sfi_hVPB  ; (ax) = hVPB
        mov     TASKAREA:[bp].TCBThishVPB,ax ; Set TCBThishVPB
        RestoreReg <ax>
        CALLFAR fVolumeWriteable        ; Can we write to the volume?
        LJC     flc8                    ;   NO, exit with error
;
; Retrieve the directory entry for the file
;
        CALL    DirFromSFT
    ASSUME      DS:NOTHING
        MOV     AX,ERROR_ACCESS_DENIED
        LJC     flc8                    ; pretend the close worked.

ifdef FSSTRICT
        test    ES:[BX.buf_flags],BUF_ISFAT ;Buffer for FAT
        jz      flcdbg2
        InternalError <FileCommit: bad buffer flag>
flcdbg2:
endif
;
; ES:DI points to entry
; DS:SI points to SFT
; ES:BX points to buffer header
;
        MOV     AX,[SI.sf_fsd.sfdFAT_firFILEclus]
        MOV     ES:[DI.dir_firstfile],AX    ;Set firFILEclus pointer
        MOV     AX,[SI.sf_fsd.sfdFAT_EAHandle]
        MOV     ES:[DI.dir_EAhandle],AX     ;Set firEAclus pointer
        MOV     AX,WORD PTR [SI.sf_fsi.sfi_size]
        MOV     ES:[DI.dir_size_l],AX       ;Set size
        MOV     AX,WORD PTR [SI.sf_fsi.sfi_size.hiword]
        MOV     ES:[DI.dir_size_h],AX
        MOV     AX,[SI.sf_fsi.sfi_mdate]
        MOV     ES:[DI.dir_date],AX         ;Set date
        MOV     AX,[SI.sf_fsi.sfi_mtime]
        MOV     ES:[DI.dir_time],AX         ;Set time
        mov     al,byte ptr es:[di].dir_attr
        and     al,NOT attr_changeable
        or      al,byte ptr ds:[si].sf_fsi.sfi_DOSattr ; al = current attribute
        mov     byte ptr es:[di].dir_attr,al           ; mov al into dir entry
        test    ds:[si].sf_flags2,SFF2_STPTHINFDN      ; Don't set archive if user
        jnz     short flc4                             ; just called DosSetPathInfo
        test    es:[di].dir_attr,attr_directory
        jnz     short flc4                             ; Don't set archive for directories
        or      ES:[DI].dir_attr,attr_archive          ; Set archive
flc4:   and     ds:[si].sf_flags2,NOT SFF2_STPTHINFDN  ; Reset the flag to 0

ifdef FSSTRICT
        test    ES:[BX.buf_flags],BUF_ISFAT ;Buffer for FAT
        jz      flcdbg
        InternalError <FileCommit: bad buffer flag>
flcdbg:
endif
        OR      ES:[BX.buf_flags],BUF_DIRTY ;Buffer dirty
        CALLFAR RlseBuffr                   ; release the directory sector

        SaveReg  <DS,SI>
        mov     ax,[si].sf_fsi.sfi_hVPB
flc5:   TaskContext DS
        CALLFAR FlushBuf                ; flush all relevant buffers
        RestoreReg  <DI,ES>
        MOV     AX,ERROR_ACCESS_DENIED
        jc      short flc8

        SaveReg <ax,bx>
        mov     bx,es:[di].sf_fsi.sfi_hVPB
        call    UseCache                ; Can we use the cache for this vol ?
        RestoreReg <bx,ax>
        jc      short flc7

        SaveReg <bp>
        mov     bp,CurrTCB
        test    TASKAREA:[bp].TCBJfnFlag,JFN_WRTTHR + JFN_NCACHE
        RestoreReg <bp>
        jnz     short flc6

        SaveReg <bp>
        mov     bp,CurrTCB
        test    TASKAREA:[bp].TCBJfnFlag,JFN_FLUSH  ; DosResetBuffer called
        RestoreReg <bp>                             ; flush the cache D132324
        jnz     short flc6

CpuMode 386
        SaveReg <ds>
        FlatContext <ds>
        cmp     _fLWDaemon,0            ; lazy writer present ?
        RestoreReg <ds>
        jne     flc7                    ; yes, don't flush now

flc6:   SaveReg <ds,es,edi,esi,ebx>
        mov     esi,es:[di].sf_fsd.sfdFAT_fldMask
        mov     di,es:[di].sf_fsi.sfi_hVPB
        movzx   edi,di
        FlatContext <ds,es>
        CCALL32  CacheFlush,<esi,edi>   ; flush all buffers for file
        RestoreReg <ebx,esi,edi,es,ds>

CpuMode reset
        ASSUME  DS:TASKAREA,ES:NOTHING

flc7:   or      es:[di].sf_flags,DEVID_FILE_CLEAN ; File is now clean
flc8:   ret

EndProc FileCommit

;***    DOS_ChDir - FAT specific code to set/verify/free a Current Directory.
;
;       FUNCTION
;
;           Validates a path for a new (potential) Current Directory,
;         OR
;           Verifies a Current Directory is still valid,
;         OR
;           Frees a Current Directory (nothing done at this level)
;
;       ENTRY   [WFP_Start]    -> Well Formed Path (must start "d:\"), NULL terminated
;                                 (contains path to validate/verify)
;               [Curr_Dir_End] -> end of Current Dir in path (-1 => ignore CurDir)
;               [ThisCDS]      -> CDS being used (must not be NULL (-1))
;                                 (contains 'current' Current Directory)
;               AX              = OpFlag (0 = SetCDS, 1 = VerifyCDS, 2 = FreeCDS)
;               DS              = TASKAREA
;               [cMeta] indicates no metas
;
;       EXIT    'NC' - successful
;               'C'  - Error
;                   AX = error code
;                       ERROR_PATH_NOT_FOUND
;                               Bad path
;                       ERROR_ACCESS_DENIED
;                               device or file name
;                       ERROR_INVALID_PARAMETER
;                               OpFlag is invalid
;
;       USES    AX, BX, CX, DX, DI, SI, BP, DS, ES, flags
;
;       PSEUDOCODE {
;
;           if (OpFlag is invalid)
;               InternalError
;           if (OpFlag == CHDIR_FREE)
;               return                              /* nothing to do */
;           if (!ValidFATDisk)
;               return (not dos disk)
;           Save Sattrib
;           Sattrib = Directory+System+Hidden
;           GetPath (returns cluster)               /* root cluster = 0 */
;           Restore Sattrib
;           if (path is not a directory)
;               error = AccessDenied
;           RlseBuffr (preserving error)
;
;           if (!error)
;               if (OpFlag == CHDIR_VERIFY)
;                   cddFAT_id = cluster             /* update currency */
;
;           return (error, if any)
;       } /* DOS_ChDir */

Procedure   DOS_ChDir,faronly
        TaskContext ds
        ASSUME  DS:TASKAREA, ES:NOTHING
        PMONLY

.errnz CHDIR_FREE-2
        cmp     ax,CHDIR_FREE
        je      short DCD60             ; nothing to do if CHDIR_FREE
        ja      short DCDIE             ; OpFlag is out of range, InternalError

;   OpFlag = SET or VERIFY

;   If cd_fsd is invalid, we must set cddFAT_id to -1

        mov     si,TASKAREA:CurrTCB             ; (SI) = current TCB     ;### PTR B720732
        lds     si,TASKAREA:[si].TCBThisCDS     ; (DS:SI) = CDS pointer  ;### PTR B720732
;### PTR B720732        lds     si,ThisCDS          ; DS:SI = ThisCDS = ptda_pCDS
        test    ds:[si.cd_fsi].cdi_flags,CDI_ISVALID    ; is cd_fsd valid
        jz      short DCD10                             ; no, id should be -1

;   If cdi_mediaswapped, we must be sure that cdFAT_id = -1, so we don't
;   assume any currency

        test    ds:[si.cd_fsi].cdi_flags,CDI_MEDIASWAPPED   ; media known?
        jz      short DCD20                 ; yes, just go on

DCD10:  mov     ds:[si.cd_fsd].cddFAT_id,-1 ; invalidate cluster/id

DCD20:  call    fValidFATDisk               ; Valid format for FAT FS?
        jz      short DCD90                 ; No. AX = error code. Return error.

                                            ;72417 moved from PTDA to TCB
        push    bp
        mov     bp,CurrTCB
        mov     [bp].TCBNoSetDir,FALSE      ; push whole word (extra byte)
        SaveReg [bp].TCBSattrib
        mov     [bp].TCBSattrib,attr_directory+attr_system+attr_hidden
        push    bp                          ; Dir calls can find these
        call    GetPath
        pop     bp
        RestoreReg  [bp].TCBSattrib          ; restore Sattrib
        pop     bp
                                            ;72417 end

        mov     AX,ERROR_PATH_NOT_FOUND
        jc      short DCD40                ; jump on error
        jnz     short DCD80                ; Path not a directory

DCD40:  pushf                               ; save return code
        CALLFAR RlseBuffr
        popf                                ; restore error state
        jc      short DCD60                 ; if error, go return

;   Update the cluster number in the CDS
;
;   Since there was no error, the cluster must be updated.

        mov     di,TASKAREA:CurrTCB             ; (di) = current TCB     ;### PTR B720732
        les     di,TASKAREA:[di].TCBThisCDS     ; (es:di) = CDS pointer  ;### PTR B720732
;### PTR B720732        les     di,[ThisCDS]                ; (ES:DI) -> CDS
                                                ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     CX,[bp].TCBDirStart             ; Get cluster number, for currency
        UnSetBP_CurrTCB
                                                ;72417 end
        mov     es:[di].cd_fsd.cddFAT_id,cx ; set currency
DCD60:  ret

;   GetPath returned NotADirectory, return AccessDenied

DCD80:  mov     ax,ERROR_ACCESS_DENIED
DCD90:  stc
        jmp     short DCD40                 ; go finish up

;   OpFlag is invalid

DCDIE:  InternalError <DOS_ChDir: OpFlag is invalid>


EndProc DOS_ChDir

BREAK <DOS_Dup -- DUP SFT>

;***    DOS_Dup - duplicate an SFT
;
;       Signal to the devices that a logical open is occurring
;
;       ENTRY   ES:DI point to the SFT for the file being DUPed
;
;       EXIT    ES:DI point to SFT
;               Carry clear if no error
;                   SFT ref_count is incremented
;                   AX preserved
;               else
;                   AX = error code
;
;       USES    AX, DI, ES
;

Procedure   DOS_Dup,<hybrid,far16>
        ASSUME  ES:NOTHING,SS:TASKAREA
        Assert  ISSFT,<ES,DI>,"DOSDup"
        PMONLY

        test    es:[di].sf_flags,SF_PIPE
        jnz     short dup4                      ; just increment for pipe

        CALLFAR Dev_Open_SFT
        jnc     short dup4
        mov     ax,ERROR_ACCESS_DENIED
        ret

dup4:   Assert  ISSFT,<ES,DI>,"DOSDup/DoInc"
        inc     es:[di].sf_ref_count
        clc
        RETP

EndProc   DOS_Dup


HIGH4CODE  ENDS
        END
