;       SCCSID = @(#)fatdirop.asm       6.11 92/06/15
SCCSID  equ     "@(#)fatdirop.asm       6.11 92/06/15"
TITLE   FATDIROP - OS/2 DIR operations interface
NAME    DOSFS

;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved
;
;       FAT specific code particularly for directory/(path based) operations
;
;   Routines in this module:
;
;       Global          Local
;       ======          =====
;       DOS_Delete
;       DOS_RmDir
;       DOS_Search_First
;       DOS_Search_Next
;                       GET_SOURCE
;       DOS_PathInfo
;                       FillSFTForRootDir
;
; MODIFICATION HISTORY:
;
;       Created on 1/22/1990 - Nagara
;               by moving code from DOSFS.ASM 13.27 90/01/22
;      11/17/94  Change Team  Defect 88121 - Set DS to task context before
;               referencing CurrTCB value (Spawned defect #105222).
;      11/27/95 - MLM defect 72417
;                 moved from PTDA to TCB:
;                 Attrib, cMeta, Creating, Curr_Dir_End, DirStart,
;                 Found_dev, LastEnt, Name1, NoSetDir,
;                 PathNameType, Sattrib, WFP_Start

.xlist
include fnofs.inc
include dosseg.inc

.xcref
include buffer.inc
include curdir.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include osconst.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 sft.inc
include pdb.inc
include protmode.inc
include vpb.inc
include proc.inc
include fsc.inc
include shutdown.inc

include pgexport.inc
include vmexport.inc
include codelock.inc
.cref
.list
        CpuMode 286
        ModName , FNO_FATDIROP_ASM

        ReferFlat       _fLWDaemon,DWORD
        ReferGDT        GDT_VPB,WORD
                                                ;72417 removed PTDA entries now
                                                ;      in TCB

        ReferTask       ExtErr_Locus,BYTE       ; Extended Error Locus
        ReferTask       CurrTCB,WORD            ; Current thread control block
        ReferTask       JFN_Flg_Ptr,DWORD       ; pointer to JFN flags

        EXTRNFAR        AddShare
        EXTRNFAR        CheckPathSyntax
        EXTRNFAR        FlushBuf
        EXTRNFAR        FNotify_Path_Wake
        EXTRNFAR        fVolumeWriteable
        EXTRNFAR        GetBuffr
        EXTRNFAR        JFNFindFree
        EXTRNFAR        ProbeBuff
        EXTRNFAR        RemoveShare
        EXTRNFAR        RlseBuffr
        EXTRNFAR        UnpackName
        EXTRNFAR        Volume_Access_Unlock

        EXTRN32         CodeLockProc
        EXTRN32         CodeUnlockProc
        EXTRN32         _BufFlushFATDir


        EXTRN           DeleteVID:NEAR
        EXTRN           DOS_Commit:NEAR
        EXTRN           DOS_SetInfo:NEAR
        EXTRN           DOS_QInfo:NEAR
        EXTRN           FillSFTFromDirEnt:NEAR
        EXTRN           fValidFATDisk:NEAR
        EXTRN           GETENT:NEAR
        EXTRN           GETENTRY:NEAR
        EXTRN           GetPath:NEAR
        EXTRN           GetPathNoSet:NEAR
        EXTRN           NEXTENT:NEAR
        EXTRN           Release:NEAR
        EXTRN           ReleaseEA:NEAR
        EXTRN           SetDirSrch:NEAR
        EXTRN           Srch:NEAR
        EXTRN           StartSrch:NEAR
        EXTRN           UseCache:NEAR


        FARCODE HIGH4
HIGH4CODE       SEGMENT


BREAK <DOS_Search_First - Initiate a directory search>

;**     DOS_Search_First - Initiate a search for a given file spec
;
;       ENTRY   [WFP_Start] Points to WFP string ("d:/" must be first 3
;                       chars, NUL terminated)
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else Points
;                        to first char after last "/" of current dir part)
;               [ThisCDS] Points to CDS being used
;                       (Low word = -1 if NUL CDS (Net direct request))
;               [Sattrib] Is attribute of search, determines what files
;                       can be found
;               [DMAAdd] Points to SRCHBUFSIZE byte buffer
;               [PathNameType] setup
;               [cMeta] set up
;
;       NOTE    The magic number "67" is defined by SRCHBUFSIZE.
;               See find.inc for explanation.
;
;
;       EXIT    'NC' if success
;                   The 67 bytes at DMAAdd are filled in as follows:
;                   NOTE: find_buf is defined in find.inc
;               if NOT a device (i.e. [Found_dev] not set)
;                   [CurBuf+2]:BX = Pointer to directory entry in buffer
;
;               LOCAL   (see find.inc find_buf_search for definition of buffer)
;                   Drive Byte (A=1, B=2, ...) High bit clear
;                       NEVER STORE DRIVE BYTE AFTER  found_it
;                   24 byte search name with Meta chars in it
;                   flag indicating whether name contains metas
;                   Search Attribute Byte, attribute of search
;                   WORD LastEnt value
;                   WORD DirStart
;                   WORD hVPB
;                   2 byte pad
;                   32 bytes of the directory entry found
;
;               'C' if error
;                   AX = error code
;                       ERROR_NO_MORE_FILES
;                               No match for this file
;                       ERROR_PATH_NOT_FOUND
;                               Bad path (not in curr dir part if present)
;                       ERROR_BAD_CURR_DIR
;                               Bad path in current directory part of path
;
;       USES    all but DS
;

Procedure   DOS_Search_First,hybrid
        SegAssert SS,<DS>,"DOS_Search_First"
        ASSUME  DS:TASKAREA, ES:NOTHING, SS:TASKAREA
        PMONLY

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_NMPIPE ; named piped path?
        UnSetBP_CurrTCB
        jnz     short dsf10             ; yes, go error exit

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

        SetBP_CurrTCB
        MOV     [bp].[TCBNoSetDir],1    ; if we find a dir, don't change to it
        UnSetBP_CurrTCB
                                        ;72417 end
        call    GetPath
        JNC     short find_check_dev
;
; Couldn't find a thing.  Presume the path was invalid.
;
dsf10:  MOV     AX,ERROR_PATH_NOT_FOUND
        JNZ     stcret
        OR      CL,CL
        JZ      short stcret
find_no_more:
        MOV     AX,ERROR_NO_MORE_FILES
stcret: STC
        ret

find_check_dev:
        OR      AH,AH
        JNS     short sfmn
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     [bp].TCBLastEnt,-1      ; Cause DOS_Search_Next to fail
        INC     [bp].TCBFound_dev       ; Tell DOS_Rename we found a device
        UnSetBP_CurrTCB
                                        ;72417 end

;
; We set the physical drive byte here Instead of after found_it; Doing
; a search-next may not have wfp_start set correctly
;
sfmn:   mov     DI,CurrTCB
        LES     DI,TASKAREA:[DI].TCBDMAAdd
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        MOV     SI,ss:[si].TCBWFP_Start ; get pointer to beginning
                                        ;72417 end
        LODSB
        SUB     AL,'A'-1                ; logical drive
        STOSB                           ; High bit not set (local)

;
; We have found a directory entry; go fill in data area.  This is the
; entry for DOS_Search_Next.
;

found_it:
                                        ;72417 moved from PTDA to TCB
        mov     DI,CurrTCB
        LES     DI,TASKAREA:[DI].TCBDMAAdd
        assume  es:nothing
        INC     DI

;
; move the name into the user buffer
;
        TaskContext DS
        mov     si,CurrTCB
        lea     SI,ss:[si].TCBName1 ; find_buf 2 = formatted name of search

        MOV     CX,MAXMETANAME SHR 2
        .errnz (MAXMETANAME AND 3)
        .386
        REP MOVSD
        CpuMode reset
        SetBP_CurrTCB
        MOV     AL,byte ptr [bp].TCBAttrib
        STOSB
        PUSH    AX                      ; Save AH device info
        MOV     AX,[bp].TCBLastEnt
        STOSW
        MOV     AX,[bp].TCBDirStart
                                        ;72417 end
        STOSW

        pop     ax
        UnSetBP_CurrTCB
        mov     bp,NULLVPB
        or      ah,ah
        js      short sfgotvpb
        mov     bp,CurrTCB
        mov     bp,TASKAREA:[bp].TCBThishVPB            ; store VPB info
sfgotvpb:
        mov     es:[di],bp
        add     di,4
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     AL,[bp].TCBcMeta
        UnSetBP_CurrTCB
                                        ;72417 end
        STOSB

        OR      AH,AH
        JS      short DOSREL            ; Device entry is TASKAREA relative
        push    bp
        mov     bp,CurrTCB
        cmp     word ptr [bp].TCBCurBuf,-1
        pop     bp
        JNZ     short OKSTORE
; The user has specified the root directory itself, rather than some
; contents of it.  We can't "find" that.

.errnz (find_buf_flags + 1 - find_buf_LastEnt - 9)  ; ds:si -> one past flags
        MOV     WORD PTR ES:[DI-9],-1   ; Cause DOS_Search_Next to fail by
                                        ;   stuffing a -1 at Lastent
        JMP     find_no_more

OKSTORE:
        mov     si,CurrTCB
        mov     ds,word ptr TASKAREA:[si].TCBCurBuf.Segmt
    ASSUME      DS:NOTHING
DOSREL:
        MOV     SI,BX                   ; SI-> start of entry

; NOTE: DOS_Rename depends on BX not being altered after this point

; Special E5 code - translate 05h to E5h, allowing E5 as special
; deleted file indicator.

        lodsb
        CMP     al,5
        JNZ     short NOTKANJB
        MOV     al,0E5H
NOTKANJB:
        stosb

        MOV     CX,((SIZE dir_entry) - 1) SHR 2
        .errnz  SIZE dir_entry AND 3
        .386
        REP MOVSD
        CpuMode reset
        MOVSW
        MOVSB
        TaskContext DS
        CLC
        ret

EndProc DOS_Search_First

BREAK <DOS_Search_Next - scan for subsequent matches>

;**     DOS_Search_Next - continue a directory search
;
;       DOS_Search_Next continues a search started by DOS_Search_First.
;       It will attempt to find subsequent matches.
;
;
;       NOTE    The magic number "67" is defined by SRCHBUFSIZE.
;               See find.inc for explanation.
;
;       ENTRY   [DMAAdd] Points to 67 byte buffer returned by DOS_Search_First
;                   (only first 35 bytes must have valid information)
;                   (see find.inc find_buf_search for definition of buffer)
;       EXIT    'NC' if success
;               [CurBuf+2]:BX = Pointer into directory buffer
;                   The 67 bytes at DMAAdd are updated for next call
;                       (see DOS_Search_First)
;                       (see find.inc find_buf_search for buffer definition)
;               'C' if error
;                   AX = error code
;                       ERROR_NO_MORE_FILES
;                               No more files to find
; DS preserved, others destroyed

Procedure   DOS_Search_Next,hybrid
        SegAssert SS,<DS>,"DOS_Search_Next"
        ASSUME  DS:TASKAREA, ES:NOTHING, SS:TASKAREA
        PMONLY

        mov     DI,CurrTCB
        LES     DI,TASKAREA:[DI].TCBDMAAdd
        MOV     AL,ES:[DI]

;AL is drive A=1
        MOV     [ExtErr_Locus],ERRLOC_DISK
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     BYTE PTR [bp].TCBCreating,0
        MOV     [bp].[TCBNoSetDir],1    ; if we find a dir, don't change to it
        UnSetBP_CurrTCB
                                        ;72417 end
        mov     SI,CurrTCB
        LDS     SI,TASKAREA:[SI].TCBDMAAdd
    ASSUME      DS:NOTHING

        mov     bp,ds:[si].find_buf_hVPB
        SaveReg <si>
        mov     si,CurrTCB
        mov     [si].TCBThishVPB,bp
        RestoreReg  <si>

        INC     SI                      ; Drive Byte - ignore

        TaskContext ES                  ; This BLOWS ES:BP POINTER TO DPB
                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        lea     DI,ss:[di].TCBName1
                                        ;72417 end
        MOV     CX,MAXMETANAME SHR 2
        .errnz (MAXMETANAME AND 3)
        .386
        REP MOVSD                       ; Search name
        CpuMode reset
        xor     ah,ah
        LODSB                           ; attribute
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     [bp].TCBAttrib,ax       ; b724744
        UnSetBP_CurrTCB
                                        ;72417 end
        LODSW                           ; LastEnt
        OR      AX,AX
        JNS     short cont_load
No_files:JMP    find_no_more

cont_load:
        PUSH    AX                      ; Save LastEnt
        LODSW                           ; DirStart
        MOV     BX,AX
        TaskContext DS

.errnz (find_buf_flags - find_buf_hVPB - 4) ; ds:si -> vpb
        mov     al,ds:[si+4]            ; set up cMeta
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     [bp].TCBcMeta,AL
        UnSetBP_CurrTCB
                                        ;72417 end

        mov     bp,CurrTCB
        mov     bp,TASKAREA:[bp].TCBThishVPB
        VPBContext es,bp                ; Recover ES:BP

        push    dx
        call    SetDirSrch
        pop     dx
        JNC     SEARCH_GOON
        POP     AX                      ; Clean stack
        JMP     short No_files

SEARCH_GOON:
        call    StartSrch
        POP     AX

        push    dx                      ; preserve flag
        call    GETENT                  ; start search
        JC      short sg1               ; error, go restore flag
        call    NEXTENT                 ; find file
sg1:    pop     dx                      ; restore flag

        JC      short No_files          ; error, go process VPB and return

        XOR     AH,AH                   ; If Search_Next, can't be a DEV
        call    found_it
        ret

EndProc DOS_Search_Next

HIGH4CODE       ENDS

        FARCODE HIGH4
FSSWAPCODE      SEGMENT
;***    DOS_PathInfo - Get the info for the path for the FAT file system
;
;       DOS_PathInfo gets the information for the file or directory in
;       the FAT file system. It essentially sets up an SFT for the object
;       and then calls either DOS_QInfo or DOS_SetInfo with the correct
;       parameters.
;       It is called by both w_QPathInfo and w_SetPathInfo.
;
;       ENTRY   ES:DI -> InfoBuf
;               DX = InfoLevel
;               CX = Size of InfoBuf
;               BX = Flag to indicate GET or SET
;                       (fGSPI_GETINFO if we are to GET information
;                        fGSPI_SETINFO if we are to SET information)
;                        and
;                        fGSPI_WRTTHRU if write-throu is needed for SET
;                        this bit may or may not be set in case of SETINFO
;                         and will not be there for GETINFO
;               AX = SFN
;               [WFP_Start] Points to WFP string ("d:\" must be first 3 chars.) NULL terminated.
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else
;                        Points to first char after last "\" of current
;                        dir part)
;               [ThisCDS] Points to CDS being used
;               [Sattrib] Is attribute of search, determines what files can
;                       be found
;               [TCBNoSetDir] set
;               [TCBThishVPB] = hVPB
;               [TCBThisSFT] = SFT
;               [cMeta] indicates no metas
;
;       EXIT    'NC' if success
;                   InfoBuf filled with relevant information
;               'C' if error
;                   AX = error code
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
;       ALGORITHM
;
;           if (InfoLevel == QINFOLEV_PATHSYNTAX)
;               CheckPathSyntax()
;           else {
;               JFNFindFree()
;               if (!error) {
;                       if (fValidFATDisk)
;                       {
;                          GetPath()
;                          if (not found)
;                              error = path_not_found
;                          else {
;                              FillSFTFromDirEnt(SFT)
;                              if (GET)
;                                  DOS_QInfo()
;                              else
;                                  DOS_SetInfo()
;                          }
;                       }
;               }
;               free the SFN
;           }
;           return any errors
;
Procedure   DOS_PathInfo,faronly
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar    dpi_InfoBuf,DWORD           ; Pointer to InfoBuf for returned info
LocalVar    dpi_GetSetFlag,WORD         ; Flag for GET or SET
LocalVar    dpi_InfoBufSiz,WORD         ; Size of InfoBuf
LocalVar    dpi_InfoLevel,WORD          ; Level of information requested
LocalVar    dpi_SFN,WORD                ; SFN for the SFT allocated
LocalVar    dpi_pJFN,DWORD              ; Pointer to JFN
LocalVar    dpi_JFN,WORD                ; JFN number (= file/directory handle)


        CodeLock        FSSWAP_GROUP
        TaskContext DS
        EnterProc <ES,DI,BX,CX,DX,AX>   ; Initialize local variables
;
; If this is a Query operation with InfoLevel 6, then we need to verify that
; the path passed in is syntactically correct for the FAT file system. This
; may be different for real and protected mode since non 8.3 names get
; truncated in the 3x box.
;
        cmp     dx,QINFOLEV_PATHSYNTAX  ; Are we checking syntax of a path string?
        jne     short dpi10             ; No. It is for an EA operation.
ifdef FSSTRICT
;
; This code is assuming that QINFOLEV_PATHSYNTAX does not overlap with one
; for a "set" operation. If this changes, then this code should also be changed.
;
        test    bx,fGSPI_SETINFO
        JNZ     dpi90                   ; Infolevel for "set" overlaps. Generate error.
endif
        CALLFAR CheckPathSyntax
        jmp     dpi60                   ; Return any errors to caller


;**** Process Infolevels [1-4]
;
; We need to "fake" an OPEN on the file or directory, i.e. we need to get an
; SFT for it and fill it in if the directory entry exists. We cannot use an
; SFT on the stack since we may eventually call a routine that requires the
; use of a handle (DOS_SetInfo which calls FileTimes).
; So, what we do is to allocate an SFT from the system pool, and fill it in
; if the path is valid.
; We allocate the SFT now so we don't have to worry about losing our buffer
; if we block later on.
;
; NOTE: We do not link it into the JFN until after we have filled it in so as
;       to avoid anyone from using it before it is valid.
;
dpi10:  CALLFAR JFNFindFree             ; ES:DI -> JFN
        LJC     dpi60                   ; Error. Return to caller.
    ASSUME ES:NOTHING
        mov     [dpi_pJFN].Segmt,ES
        mov     [dpi_pJFN].Offst,DI     ; Save the pointer for now
        mov     [dpi_JFN],BX            ; Save handle for SFT
;
; Verify that this is a valid FAT disk
;
        call    fValidFATDisk           ; Valid format for FAT FS?
        LJZ     dpi80                   ; No. AX = error code. Return error.
;
; Now verify that the path is valid.
;
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     [bp].TCBNoSetDir,0      ; Change to dir if this is a dir
        MOV     [bp].TCBSattrib,attr_directory+attr_system+attr_hidden
        UnSetBP_CurrTCB
                                        ;72417 end
                                        ; Dir calls can find these
        SaveReg <BP>                    ; GetPath trashes BP
        call    GetPath                 ; See if path is valid
        RestoreReg <BP>                 ; BP points to local var frame
        jnc     short dpi11
;
; Got an error from GetPath. Set correct error code, clean up and return.
; CL = 0 if path error
;        else file name error
;
        mov     ax,ERROR_PATH_NOT_FOUND
        jnz     dpi85                   ; Path was bad. #57685
;       jnz     short dpi15             ; Path was bad. #57685
        or      cl,cl
        stc                             ; Set the carry again to counter previous OR
        jz      short dpi15             ; Bad name in path.
        mov     ax,ERROR_FILE_NOT_FOUND ; The name was not found.
        jmp     short dpi15             ; Clean up and return error

dpi11:  SegAssert SS,DS,"DOS_PathInfo - Set ThisSFT"

;
; If this is the root directory, then it is considered an error since we
; cannot "get" or "set" information for it (there is no directory entry
; for it on the disk
;
; For a "get" operation, if it is the root directory, we pretend that we
; have the information (all zeros). This is required by FAPI in particular
; for emulating the operation of the protect mode FindFirst/Next operations.
;
; For a "set" operation, we return access_denied.
;
        mov     di,TASKAREA:[CurrTCB]
        lds     si,TASKAREA:[di].TCBThisSFT ; DS:SI -> SFT
    ASSUME DS:NOTHING
;
; Set the sf_flags and sf_ref_count to something reasonable.
; FillSFTFromDirEnt assumes these to have been set up correctly.
;
        mov     [si].sf_flags,SF_NOJFN  ; Say there is no JFN (since handle is "invisible")
        mov     [si].sf_ref_count,1     ; We are the only reference to the SFT
        cmp     word ptr TASKAREA:[di].TCBCurBuf,-1
        jnz     short dpi13             ; Not the root
        test    [dpi_GetSetFlag],-1     ; no writethrough for get operation
        stc
        mov     ax,ERROR_ACCESS_DENIED  ; Set 'C' and error code in case it is root.
        jnz     short dpi15             ; "Set" operation. Return error for root directory.
;
; This is a "get" operation on the root directory. We set up the SFT so that
; all the relevant entries are "zero" since there is no physical data for the
; root directory on the disk.
;
        call    FillSFTForRootDir
        jmp     short dpi14

dpi15:  jmp     dpi30                   ; Clean up and return error

;
; GetPath has set up [CurBuf+2]:BX pointing to the directory entry.
; We "fake" an SFT for the file or directory by filling it in from the
; directory entry obtained.
;
dpi13:  call    FillSFTFromDirEnt
;
; Now it is safe to add the SFN into the JFN.
; Set up JFN and JFN flags as well.
;
dpi14:  les     di,[dpi_pJFN]
    ASSUME ES:NOTHING
        mov     ax,[dpi_SFN]            ; ES:DI -> JFN
        mov     es:[di],ax              ; Store SFN in the JFN
        les     di,JFN_Flg_Ptr          ; get pointer to JFN flags
        mov     bx,[dpi_JFN]            ; use handle to index to JFN entry
        mov     byte ptr es:[di+bx],JFN_DIH ; set no inherit bit
        test    [dpi_GetSetFlag],fGSPI_WRTTHRU
        jz      short dpi16
        or      byte ptr es:[di+bx],JFN_WRTTHR ; set write through bit

dpi16:  CALLFAR RlseBuffr               ; Release the directory buffer

        mov     cx,[dpi_InfoBufSiz]     ; CX = size of InfoBuf
;
; We blocked earlier and so need to verify that the user buffer pointer is
; still valid.
;
        mov     bx,[dpi_InfoBuf].Segmt
        mov     si,[dpi_InfoBuf].Offst  ; BX:SI -> user InfoBuf; CX = length of buffer
        SaveReg <cx>
        mov     dh,ACCTYPE_READ         ; Only need read access for now
        CALLFAR ProbeBuff
        RestoreReg <cx>                 ; CX = length of buffer
        jc      short dpi30             ; GP fault. Return error.
        mov     di,TASKAREA:[CurrTCB]
        les     di,TASKAREA:[di].TCBThisSFT ; ES:DI -> SFT
        mov     dx,[dpi_InfoLevel]      ; DX = infolevel requested
        lds     si,[dpi_InfoBuf]        ; Buffer is valid. DS:SI -> InfoBuf.
    ASSUME DS:NOTHING

        lea     ax,DOSHIGH4CODE:DOS_SetInfo ; AX = routine to call
        mov     bx,SINFOLEV_MAX         ; Assume this is a "set" operation
        test    [dpi_GetSetFlag],-1
        jnz     short dpi18             ; Our assumption was correct

.errnz (fGSPI_GETINFO)

;
; It is a "query" operation
;
        mov     bx,QPATHINFOLEV_MAX     ; Maximum infolevel for a "query"
        lea     ax,DOSHIGH4CODE:DOS_QInfo   ; AX = routine to call
;
; We make sure that the infolevel is valid before calling the worker.
;
; DX = infolevel
; BX = maximum info level
;
dpi18:  cmp     dx,bx
        ja      short dpi70             ; Level is too large. Return error.
        or      dx,dx
        jz      short dpi70             ; Level = 0. Return error.

;
; ES:DI -> SFT (filled in)
; DS:SI -> InfoBuf
; AX = offset of routine in HIGH4CODE to call
; CX = size of InfoBuf
; DX = Info level
;
dpi20:  mov     bx,[dpi_JFN]            ; BX = "handle" for the SFT
        call    ax                      ; Do work of "get" or "set" - NEAR CALL
        jc      short dpi30             ; Got an error. Clean up and return error.
;
; The operation succeeded. As part of our emulation of a "close", we need
; to ensure that the information gets written out to disk. So, we need to
; do a COMMIT if this was a "set" operation.
;
        test    [dpi_GetSetFlag],-1
        jz      short dpi30             ; This was a "get". No need to commit.
        mov     di,TASKAREA:[CurrTCB]
        les     di,TASKAREA:[di].TCBThisSFT ; ES:DI -> SFT
        or      es:[di].sf_flags2,SFF2_STPTHINFDN  ; Don't set archive bit
                                                   ; during commit of the sft
        mov     bx,es:[di].sf_flags     ; BX = flags from SFT
        SaveReg <BP>                    ; DOS_Commit trashes BP!!
        TaskContext DS                  ; Set up DS for DOS_Commit
        call    DOS_Commit              ; Commit the information to disk
        RestoreReg <BP>                 ; Restore stack frame pointer
;
; Now free the JFN
;
dpi30:  lds     si,[dpi_pJFN]
    ASSUME DS:NOTHING
        mov     word ptr [si],SFN_NULL  ; free the SFN...
        mov     di,TASKAREA:[CurrTCB]
        les     di,TASKAREA:[di].TCBThisSFT ; ES:DI -> SFT
        mov     es:[di].sf_ref_count,0  ; release SFT
dpi60:  LeaveProc
        CodeUnlock      FSSWAP_GROUP
        ret                             ; Return any error indications


;
; The user specified an invalid infolevel. Set error, and clean up and return.
;
dpi70:  mov     ax,ERROR_INVALID_LEVEL  ; invalid function
dpi80:  stc
        jmp     short dpi30

dpi85:  pushf                           ; save flags                   #57685
        CALLFAR RlseBuffr               ; Release the directory buffer #57685
        popf                            ; restore flags                #57685
        jmp     short dpi30             ;                              #57685


ifdef FSSTRICT
dpi90:  InternalError <DOS_PathInfo: Infolevel for SET = QINFOLEV_PATHSYNTAX>
endif

EndProc     DOS_PathInfo


;***    FillSFTForRootDir - Fill an SFt for the root directory
;
;       FillSFTForRootDir sets up an SFT to be used for the root directory.
;       This is necessary since we will call DOS_QInfo to get some information
;       to return to the user.
;       We do this in order to allow FAPI to emulate the FindFirst/Next
;       infolevels that return EA information about a directory. This will
;       ensure that a QPathInfo call for the root directory will not fail.
;       The information returned will be "all zeros", since this information
;       does not exist physically on the disk.
;
;       FAPI does a FindNext on a sub-directory from the root. It gets the
;       entry for ".." that exists in every sub-directory. It then does
;       an Int 21 equivalent of QPathInfo on <drive>:<subdir>\.., whose
;       canoincal form is the root directory.
;
;       ENTRY   DS:SI -> SFT
;               The following fields in the SFT have been set up:
;                   sf_fsi.sfi_mode
;                   sf_ref_count = 1
;                   sf_flags = 0
;
;       EXIT    SFT set up for root directory
;
;                   sfi_hVPB set with TCBThishVPB
;                   sfi_ctime/cdate set to zero
;                   sfi_size set to zero
;                   sf_fsd.sfdFAT_EAHandle set to zero
;
;       USES    BX, Flags
;
Procedure   FillSFTForRootDir,NEAR,LOCAL
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        SaveReg <BP>
;
; Fill in sfi_hVPB
;
        mov     bp,CurrTCB
        mov     bp,[bp].TCBThishVPB     ; get volume for which the open is intended
        mov     [si].sf_fsi.sfi_hVPB,bp ; volume of handle is current volume

        xor     bx,bx
        mov     [si].sf_fsi.sfi_ctime,bx         ; time/date are not relevant
        mov     [si].sf_fsi.sfi_cdate,bx         ; 0, however, is a reasonable value
        mov     word ptr [si].sf_fsi.sfi_size.loword,bx
        mov     word ptr [si].sf_fsi.sfi_size.hiword,bx
;
; There are no EAs for the root directory
;
        mov     [si].sf_fsd.sfdFAT_EAHandle,bx
;
; Set the attributes for a directory
;
        mov      [si].sf_fsi.sfi_DOSattr,attr_directory
        RestoreReg <BP>
        ret
EndProc FillSFTForRootDir


Break   <DOS_Delete - Delete files>

;**     DOS_Delete - Delete the specified file(s)
;
;       ENTRY   [WFP_Start] Points to WFP string ("d:/" must be first 3
;                       chars, NUL terminated)
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else Points
;                        to first char after last "/" of current dir part)
;               [ThisCDS] Points to CDS being used
;                       (Low word = -1 if NUL CDS (Net direct request))
;               [Sattrib] Is attribute of search, determines what files can
;                       be found
;               [cMeta] indicates no metas
;
;       EXIT    CARRY CLEAR
;                       OK
;               CARRY SET
;                   AX is error code
;                       ERROR_FILE_NOT_FOUND
;                               Last element of path not found
;                       ERROR_PATH_NOT_FOUND
;                               Bad path (not in curr dir part if present)
;                       ERROR_BAD_CURR_DIR
;                               Bad path in current directory part of path
;                       ERROR_ACCESS_DENIED
;                               Attempt to delete directory or read-only file
;                          BX == 1 if error caused by read-only file
;                          BX == 0 if error caused by directory
;                       ERROR_SHARING_VIOLATION
;                          if (Real mode) {
;                               Deny both access required, generates an INT 24.
;                               This error is NOT returned.  The INT 24H is
;                               generated, and the file is ignored (not
;                               deleted).  Delete will simply continue on
;                               looking for more files.
;                                 Carry will NOT be set in this case.
;                          }  else { /* protected */
;                               ERROR_SHARING_VIOLATION is returned with
;                               carry set.
;                          }
;
;       USES    all
;
;       PSEUDOCODE
;          fVolumeWriteable
;          fValidFATDisk
;          GetPathNoSet
;          if (bad path | not found)
;             return error
;          if (dir)
;             return error
;          if (read only file && not deleting RO files)
;             return
;          mark buffer dirty
;          mark dir entry deleted
;          RlseBuffr
;          if (valid firFILEclus)
;             Release
;          if (valid firEAclus)
;             Release
;          FNotify_Path_Wake
;          FlushBuf
;
    ASSUME DS:nothing, ES:NOTHING,SS:TASKAREA
Procedure   DOS_Delete,hybrid
LocalVar dd_renbuf,PMPATHLEN            ; temp buf for Unpack
LocalVar dd_dummy,WORD                  ; KLUDGE: see gshare.asm

        PMONLY

        CodeLock        FSSWAP_GROUP

        TaskContext <ds>

        ASSUME DS:TASKAREA

        CALLFAR fVolumeWriteable        ; Can we write to the volume?
        jc      short ErrorReturn       ;   NO, exit with error
        call    fValidFATDisk           ; Valid format for FAT FS?
        jz      short ErrorReturn       ; No. AX = error code. Return error.
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     BYTE PTR [bp].TCBCreating,0
        MOV     [bp].TCBNoSetDir,1
        UnSetBP_CurrTCB
                                        ;72417 end
        call    GetPathNoSet
    ASSUME      ds:nothing,ES:NOTHING
        JNC     short Del_found
        JNZ     short bad_path1
        OR      CL,CL
        JZ      short bad_path1
No_file:
        MOV     AX,ERROR_FILE_NOT_FOUND
ErrorReturn:
        STC
        JMP     ddlx

bad_path1:
        MOV     AX,ERROR_PATH_NOT_FOUND
        JMP     short ErrorReturn

Del_access_err:
        MOV     AX,ERROR_ACCESS_DENIED
        JMP     short ErrorReturn

Del_found:
        JZ      short Del_access_err; Directory. Go return error.
;
; CurBuf+2:BX points to a matching directory entry.
;
DELFILE:
    ASSUME      DS:TASKAREA
        PUSH    DS
        mov     DI,CurrTCB
        LDS     DI,TASKAREA:[DI].TCBCurBuf
    ASSUME      DS:NOTHING

;
; In the case of a read only file, we set bx=1 to indicate that the error
; ERROR_ACCESS_DENIED was not because we were trying to delete a directory.
; This is because in the FCB delete with meta-characters case, we should
; continue to delete operation if the error was caused by a read-only file
; and not if the error was caused by deleting a directory.  otherwise, bx=0.
; don't you love compatibility?
;
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        TEST    [bp].TCBAttrib,attr_read_only ; are we deleting RO files too?
        UnSetBP_CurrTCB
                                        ;72417 end
        JNZ     short DoDelete          ; yes
        TEST    DS:[BX.dir_attr],attr_read_only
        JZ      short DoDelete          ; not read only
        pop     ds
        mov     bx,1                    ; error caused by RO file
        mov     ax,ERROR_ACCESS_DENIED
        stc
        jmp     ddlx

; (DS:DI) -> curbuf
; (DS:BX) -> dir entry

DoDelete: or      [di.buf_flags],BUF_DIRTY
        mov     cx,word ptr [bx].dir_EAhandle   ; CX = EA handle

ifdef FSSTRICT
        EnterProc
        pusha
        SaveReg <es,ds>
        TaskContext <es,ds>,di
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        lea     si,ss:[si].TCBName1     ; ds:si -> asciiz exp to rmdir
                                        ;72417 end
        lea     di,dd_renbuf            ; es:di -> destination
        CALLFAR UnpackName              ; convert to dir entry form
        RestoreReg <ds>                 ; ds:bx -> dir entry name
        mov     si,bx                   ; ds:si -> name
        lea     di,dd_renbuf            ; es:di -> exp
        mov     cx,11
        repe    cmpsb                   ; same?
        je      short dd0               ; yes
        InternalError <DOS_Delete - deleting wrong entry>
dd0:    RestoreReg <es>
        popa
        LeaveProc
endif


        MOV     BYTE PTR [BX],DIRENT_DELETED    ; Put in E5H
        mov     al,DS:[BX.dir_attr]     ; AL = deleted entry attribute
        MOV     BX,[SI]                 ; Get firFILEclus pointer
        CALLFAR RlseBuffr
        POP     DS

        SaveReg <ax,bx>
        mov     bx,bp
        call    UseCache                ; Can we use the cache for this vol ?
        RestoreReg <bx,ax>
        jc      dd0_1

        SaveReg <ds,es>
        pusha
        mov     ax,bp
        CALLFAR FlushBuf                ; flush buffers from dosbuffers to
        popa
        RestoreReg <es,ds>              ; cache
        LJC     No_file

CpuMode 386
        SaveReg <ds,es>
        pushad
        movzx   eax,bp
        FlatContext <ds,es>
        CCALL32  _BufFlushFATDir,<eax>  ; flush cache to disk
        popad
        RestoreReg <es,ds>
        ASSUME  DS:TASKAREA,ES:NOTHING
CpuMode reset

dd0_1:
    SegAssert SS,<DS>,"DOS_Delete"
        ASSUME  DS:TASKAREA
        CMP     BX,MINCLUS
        JB      short DelEA             ; File has invalid firFILEclus (too small)
        chkvselhd es,bp
        CMP     BX,ES:[bp].vpb_fsd.vpdFAT_max_cluster
        JA      short DelEA             ; File has invalid firFILEclus (too big)
        push    ax                      ; AL = attribute of deleted entry
        call    Release                 ; Free file data
        pop     ax
        jc      short dd2               ; Error. Close MFT and return error.
;
; We must also release any clusters used for the Extended Attributes for the
; file, since the directory entry has been marked as deleted.
;
; CX = EA Handle.  CX != 0 => EAs need to be deleted
;

DelEA:  jcxz    short dd1               ; No EAs to delete

        mov     bx,cx                   ; (bx) = EA handle
        SaveReg <ax,dx,si,ds>           ; AL = attribute of deleted entry
        mov     si,bp                   ; es:si -> VPB
        call    ReleaseEA               ; remove the EAData
        RestoreReg <ds,si,dx,ax>
        jmp     short dd2               ; save return code

dd1:    clc
dd2:    pushf
        mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT     ; (ES:DI) -> SFT
        MOV     ES:[DI.sf_ref_count],0  ; Pretend closed and free
                                        ; NOT a system SFT, so DON'T call
                                        ; SFLinkFree on it.
        mov     ah,fFNY_DELETE          ; AH = this is for delete
        CALLFAR FNotify_Path_Wake       ; AL = attribute of deleted entry
        popf
        LJC     No_file

        mov     bp,CurrTCB
        mov     ax,TASKAREA:[bp].TCBThishVPB    ; NEXTENT sets ES=TASKAREA
        SaveReg <ax>
        CALLFAR FlushBuf
        RestoreReg <ax>
        LJC     No_file

                                        ;72417 moved from PTDA to TCB
        mov     bp,CurrTCB
        test    [bp].TCBSattrib,attr_volume_id
                                        ;72417 end
        jz      short ddl1              ; jump if not deleting a volume label
;
; Make vpb_text "UNLABELED" since there is no more volume label
;
        mov     bp,TASKAREA:[bp].TCBThishVPB ; (BP) = VPB handle
        call    DeleteVID
ddl1:   clc
ddlx:   CodeUnlock      FSSWAP_GROUP
        ret                             ; all done (carry clear)


EndProc DOS_Delete



BREAK <DOS_RmDir -- Remove a directory>

;**     DOS_RmDir - Remove a directory
;
;       NOTE: Attempt to remove current directory must be detected by caller
;
;       ENTRY   [WFP_Start] Points to WFP string ("d:/" must be first 3
;                       chars, NUL terminated)
;               [Curr_Dir_End] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else Points
;                        to first char after last "/" of current dir part)
;               [ThisCDS] Points to CDS being used
;                       (Low word = -1 if NUL CDS (Net direct request))
;               [cMeta] indicates no metas
;
;       EXIT    NOTE:
;                   [Sattrib] is modified by this call
;               Carry Clear
;                       No error
;               Carry Set
;                   AX is error code
;                       ERROR_PATH_Not_found
;                               Bad path (not in curr dir part if present)
;                       ERROR_BAD_CURR_DIR
;                               Bad path in current directory part of path
;                       ERROR_ACCESS_DENIEd
;                               device or file name, root directory
;                               Bad directory ('.' '..' messed up)
;
;       USES    All
;
;       EFFECTS Switches context?
;
;       PSEUDOCODE
;          AddShare()
;          if (! fVolumeWriteable)
;             return error
;          if (! fValidFATDisk)
;             return error
;          GetPath
;          if (path_not_found)
;             return error
;          if (path not a directory | dir is root) {
;             RlseBuffr
;             return error
;          }
;          RlseBuffr
;          name1 = *0
;          StartSrch
;          GetEntry     ; get first entry in dir
;          if (not .)
;             return error
;          if (second entry not ..)
;             return error
;          GetEntry     ; get next entry
;          Srch         ; look for entries in dir
;          RlseBuffr
;          if (found one)
;             return error
;          Release      ; free directory
;          if (error) {
;             RlseBuffr
;             return error
;          }
;          mark dir deleted
;          mark buffer dirty
;          Volume_Access_Unlock ; Unlock the volume
;          ReleaseEA   ; free extended attributes
;          FlushBuf
;          FNotify_Path_Wake
;          RemoveShare()
;          return

        ASSUME  DS:TASKAREA, ES:NOTHING
Procedure   DOS_RmDir,faronly
LocalVar rmd_renbuf,PMPATHLEN           ; temp buffer for Unpacking
LocalVar rmd_dummy,WORD                 ; KLUDGE: see gshare.asm
        TaskContext <ds>,,noassume
        PMONLY

        CodeLock        FSSWAP_GROUP

;
; Add the directory name to sharing to avoid races between RmDir and
; Q/SetPathInfo calls.
;
        mov     ax,sharing_deny_both    ; Sharing mode
        mov     cx,opnobreak            ; Don't break opbatch or oplock
        CALLFAR AddShare                ; Allocate SFT and enter in sharing
        jc      short drmdxx            ; Error. Return to caller.

        CALLFAR fVolumeWriteable        ; Can we write to the volume?
        jc      short drmdx             ;   NO, return error

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

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     [bp].TCBNoSetDir,0
        MOV     [bp].TCBSattrib,attr_directory+attr_system+attr_hidden
        UnSetBP_CurrTCB
                                        ;72417 end
                                ; Dir calls can find these
        SaveReg <bp>                    ; GetPath trashes BP
        call    GetPath
        RestoreReg <bp>
        JC      short NOPATH    ; Path not found
        JNZ     short NOTDIRPATH; Path not a DIR
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     DI,[bp].TCBDirStart
        UnSetBP_CurrTCB
                                        ;72417 end
        OR      DI,DI           ; Root ?
        JNZ     short rmdir_get_buf; No
        JMP     SHORT NOTDIRPATH

NOPATH: MOV     AX,ERROR_PATH_NOT_FOUND
        jmp     short drmd20

NotDirPathPop2:
        POP     AX
        POP     AX
NotDirPathPop:
        POP     AX
NOTDIRPATH:
        TaskContext <ds>,,noassume      ; (ds) = TaskArea, error paths trash DS @cmd009i
        CALLFAR RlseBuffr
        mov     ax,ERROR_ACCESS_DENIED
drmd20: stc
drmdx:  mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT     ; (ES:DI) -> SFT
        CALLFAR RemoveShare             ; remove from sharing and free sft
drmdxx: mov     cx,VPB_ACC_EXCL         ; Free exclusive lock on the volume
        CALLFAR Volume_Access_Unlock
        jmp     drmd99

rmdir_get_buf:
        mov     DI,CurrTCB
        LDS     DI,TASKAREA:[DI].TCBCurBuf
    ASSUME      DS:NOTHING
ifdef FSSTRICT
        EnterProc
        pusha
        SaveReg <es>
        SaveReg <ds>
        TaskContext <es,ds>,di
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        lea     si,ss:[si].TCBName1     ; ds:si -> asciiz exp to rmdir
                                        ;72417 end
        lea     di,rmd_renbuf           ; es:di -> destination
        CALLFAR UnpackName              ; convert to dir entry form
        RestoreReg <ds>                 ; ds:bx -> dir entry name
        mov     si,bx                   ; ds:si -> name
        lea     di,rmd_renbuf           ; es:di -> exp
        mov     cx,11
        repe    cmpsb                   ; same?
        je      short drmd50            ; yes
        InternalError <DOS_RmDir - removing wrong entry>
drmd50: RestoreReg <es>
        popa
        LeaveProc
endif
        SUB     BX,DI                   ; BX = offset of dir entry in sector
        PUSH    BX                      ; Save entry pointer
        PUSH    word ptr [DI.buf_sector]
        PUSH    word ptr [DI.buf_sector].hiword ; Save sector number
        CALLFAR RlseBuffr
        TaskContext DS
        TaskContext ES
                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        lea     di,ss:[di].TCBName1
                                        ;72417 end
        MOV     AL,'*'
        stosb
        XOR     AL,AL
        STOSB                   ; Nul terminate it
        call    StartSrch       ; Set search
        call    GETENTRY        ; Get start of directory
        JC      NotDirPathPop2          ; jump if error
        mov     si,CurrTCB
        mov     ds,word ptr TASKAREA:[si].TCBCurBuf.Segmt
    ASSUME      DS:NOTHING
        MOV     SI,BX                   ; DS:SI -> sector with files for this dir
        LODSW
        CMP     AX,(' ' SHL 8) OR '.'   ; First entry '.'?
        JNZ     NotDirPathPop2          ; Nope
        ADD     SI,(SIZE dir_entry) - 2 ; Next entry
        LODSW
        CMP     AX,('.' SHL 8) OR '.'   ; Second entry '..'?
        JNZ     NotDirPathPop2          ; Nope
        CALLFAR RlseBuffr
        TaskContext DS
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     [bp].TCBLastEnt,2       ; Skip . and ..
        UnSetBP_CurrTCB
                                        ;72417 end
        call    GETENTRY                ; Get next entry
ifdef FSSTRICT
        LJC     NotDirPathPop2          ; jump if error
else
        JC      NotDirPathPop2          ; jump if error
endif
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     [bp].TCBAttrib,attr_directory+attr_hidden+attr_system
        xor     cx,cx                   ;
        xchg    [bp].TCBcMeta,cl        ; save val of cMeta
        UnSetBP_CurrTCB
        push    cx                      ;
        call    Srch                    ; Do a search
        pop     cx                      ;
        SetBP_CurrTCB
        xchg    [bp].TCBcMeta,cl        ; restore val of cMeta
        UnSetBP_CurrTCB
                                        ;72417 end
        LJNC    NotDirPathPop2          ; Found another entry!
        push    bp
        mov     bp,CurrTCB
        cmp     [bp].TCBFailErr,0
        pop     bp
        LJNZ    NotDirPathPop2          ; Failure of search due to Ignore to
        CALLFAR RlseBuffr               ; hard error.

        POP     CX
        POP     DX                      ; CX:DX = Sector # of entry
        push    bp
        mov     bp,CurrTCB
        mov     [bp].TCBAllowed,ALLOWED_FAIL + ALLOWED_RETRY
        pop     bp
        VPBContext es,bp
        XOR     AL,AL                   ; Pre read
        SaveReg <cx>
        CALLFAR GetBuffr                ; Get sector back
        RestoreReg <cx>                 ; CX:DX = sector # of directory entry
        LJC     NotDirPathPop           ; jump if error
        mov     DI,CurrTCB
        LDS     DI,TASKAREA:[DI].TCBCurBuf
    ASSUME      DS:NOTHING
        OR      [DI.buf_flags],BUF_ISDIR    ; Mark this as a directory buffer
        pop     bx                      ; BX = Offset of dir entry within buffer
;
; We must also release any clusters used for the Extended Attributes for the
; directory that is being removed. What we do is to get the EA handle and
; store it away for later release.
;
; DS:DI -> directory sector
; BX = offset of directory entry within buffer
;
        add     bx,di                   ; DS:BX -> directory entry
        SaveReg <[bx].dir_EAhandle>     ; Save EA handle

;
; We now mark the directory entry as deleted. We do this before we release the
; EAs because we can potentially deadlock on the volume while OPENing the EA
; file. Once we have marked the directory entry as deleted, we can release the
; volume lock and then free up the EAs.
;
; Mark the buffer as dirty so that we flush it out later.
;
ifdef FSSTRICT
;       EnterProc
;       pusha
;       SaveReg <es>
;       TaskContext <es>,di
;       lea     di,rmd_renbuf           ; es:di -> destination
;       mov     si,bx                   ; ds:si -> name
;       mov     cx,11
;       repe    cmpsb                   ; same?
;       je      short drmd60            ; yes
;       InternalError <DOS_RmDir - removing wrong entry 2nd time>
;drmd60: RestoreReg <es>
;       popa
;       LeaveProc
endif
        MOV     BYTE PTR [BX],DIRENT_DELETED    ; Free the entry
        mov     al,DS:[BX.dir_attr]             ; AL = deleted entry attribute
        or      [di.buf_flags],BUF_DIRTY        ; Dirty buffer
        CALLFAR RlseBuffr                       ; Release the buffer to avoid deadlocks

        mov     bp,CurrTCB
        mov     bp,TASKAREA:[bp].TCBThishVPB
        SaveReg <ax,bx>
        mov     bx,bp
        call    UseCache                ; Can we use the cache for this vol ?
        RestoreReg <bx,ax>
        jc      drmd55

        TaskContext DS
        mov     ax,bp
        SaveReg <bp>
        CALLFAR FlushBuf
        RestoreReg <bp>

CpuMode 386
        SaveReg <ds,es,edi,esi,ebx,eax>
        movzx   eax,bp
        FlatContext <ds,es>
        CCALL32  _BufFlushFATDir,<eax>  ; flush cache to disk
        RestoreReg <eax,ebx,esi,edi,es,ds>
        ASSUME  DS:TASKAREA,ES:NOTHING
CpuMode reset
drmd55:
        TaskContext <ds>                ; B736962
        mov     bp,CurrTCB
        mov     bp,TASKAREA:[bp].TCBThishVPB
        VPBContext es,bp

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        MOV     BX,[bp].TCBDirStart
        UnSetBP_CurrTCB
                                        ;72417 end
        call    Release                 ; Release data in sub dir
        LJC     NotDirPathPop2          ; jump if error
;
; Now unlock the volume since the race condition that we were trying to avoid
; between rmdir, mkdir and create cannot occur since we have already marked
; the directory enrty as deleted.
;
        mov     cx,VPB_ACC_EXCL         ; Free exclusive lock on the volume
        CALLFAR Volume_Access_Unlock
;
; Now it is OK to free the EAs since we will avoid any deadlocks with the
; volume being locked.
;
        RestoreReg <bx>                 ; BX = EA handle
        SaveReg <ax>                    ; AL = deleted entry attribute
        or      bx,bx                   ; Are ther any EAs to delete?
        jz      short drmd70            ; No EAs to delete
        mov     si,bp                   ; es:bp -> VPB
        call    ReleaseEA

drmd70: TaskContext <ds>                ; 88121 - set correct DS
        mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT     ; (ES:DI) -> SFT
        CALLFAR RemoveShare             ; remove from sharing and free sft
        TaskContext DS
        mov     ax,bp
        SaveReg <bp>
        CALLFAR FlushBuf
        RestoreReg <bp>
        RestoreReg <AX>                 ; AL = deleted entry attribute
        mov     ah,fFNY_DELETE          ; AH = this is for removing an entry
        CALLFAR FNotify_Path_Wake
        mov     ax,ERROR_ACCESS_DENIED
drmd99: CodeUnlock      FSSWAP_GROUP
        ret

EndProc DOS_RmDir

FSSWAPCODE      ENDS

END
