;       SCCSID = @(#)file.asm   6.14 92/03/11
TITLE   FILE - Pathname related system calls
NAME    FILE
PAGE    ,132

;***    file.asm
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       Pathname related system calls.  These will be passed direct text of
;       the pathname from the user.  They will need to be passed through
;       the macro expander prior to being sent through the low-level stuff.
;       The procedures in this file:
;
;       Global          Local
;
;       api_OpenCreate
;       w_OpenCreate
;       TransactSpooler
;                       OpenSpooled
;                       SetPhysDiskSFT
;       w_Creat
;       w_CreateNew
;       AccessSet
;                       w_3xCreateNew
;                       w_CreateTempFile
;       DoOpenCreate
;       w_ChMod
;       w_Unlink
;       w_Rename
;                       SetDeviceFont
;                       SetAttrib
;                       Check_Access_AX
; 3/10/92 SFB b734380 take perfview pipe counters
;
;***********************************************************************;BN040;
;  29Jul93 - J.M.Hare    defect 71358                                   ;BN040;
; Add propogating of DOSFLAG through files system for exec_only test.   ;BN040;
;***********************************************************************;BN040;
;  80893   rdw           3/22/94 DosOpen rc=12 from w_opencreate ... worked in 1.3
;
;  72417   MLM          11/27/95 moved from PTDA to TCB:
;                                 Attrib, cMeta, Curr_Dir_End,
;                                 ptda_pSrchBuf, PathNameType, Ren_WFP, Sattrib,
;                                 WFP_Start, WFP_Path_End
;
.xlist
include dosseg.inc      ; get the standard segment definitions
.xcref
include defdbug.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include dossymc.inc
include argframe.inc
include dbcs.inc
include devsym.inc      ; Needed by ioctl.inc
include dirent.inc
include error.inc
include filemode.inc
include filesys.inc
include ioctl.inc       ; IOC_PC=category printer, IOLW_AF=function activate font
include mi.inc
include opncreat.inc
include protmode.inc
include proc.inc
include sft.inc
include syscalls.inc
include kmode.inc
include sysentry.inc
;include tcbdata.inc
include curdir.inc
include fsc.inc
include mult.inc
include extattr.inc
include chardd.inc
include vmowner.inc
include virtsym.inc
include seldesc.inc
include splmsgp.inc
;b734380include perfvw.inc
include tkexport.inc
                                      ;72417 moved find.inc after tkexport.inc
include find.inc
.cref
.list
        ModName         file

        EXTRN           FSD_OpenCreate:NEAR
        EXTRN           FSD_Delete:NEAR
        EXTRN           JFNFindFree:NEAR
        EXTRN           SFNAlloc:NEAR
        EXTRN           PathChrCmp:NEAR
        EXTRN           SFNAlloc:NEAR
        EXTRN           TransPath:NEAR
        EXTRN           TransPathNoErrNMP:NEAR
        EXTRN           TransPathNoJoinIfSame:NEAR

        EXTRN           SFLinkFree:NEAR
        EXTRN           FSD_QFileMode:NEAR
        EXTRN           FSD_SetFileMode:NEAR
        EXTRN           FSD_Move:NEAR
        EXTRN           PathDecVPBRef:NEAR
        EXTRN           PathIncVPBRef:NEAR

        EXTRN           PIPEDIRLEN:ABS

        EXTRN           W_DOSDELETE: far

        EXTRNFAR        PathIncVPBRef
        EXTRNFAR        TransPath
        EXTRNFAR        PathDecVPBRef
        EXTRNFAR        CheckPipeDir
        EXTRNFAR        GDTFree
        EXTRNFAR        NmPipeOpen
        EXTRNFAR        HexToAscii
        EXTRNFAR        ReadTime
        EXTRNFAR        SegMapVirt
        EXTRNFAR        w_16DevIOCtl        ; For Activate Font (@CP)
        EXTRNFAR        GetTempBuffer
        EXTRNFAR        FreeTempBuffer
        EXTRNFAR        FindCharDevEntry
        EXTRNFAR        FindSpoolDevEntry
        EXTRNFAR        CNotify_Path_Wake
        EXTRNFAR        CheckSpoolDevice
        EXTRNFAR        w_TransactNmPipe
        EXTRNFAR        SegAlloc
        EXTRNFAR        SegFree
        EXTRNFAR        w_GetEnv

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

                                            ;72417 removed PTDA entries now
                                            ;      in TCB

        ReferTask       codepage_tag,WORD   ; current code page (@CP)
        ReferTask       CurrTCB,WORD        ; current thread control block
        ReferTask       ExtErr_Locus,BYTE   ; Extended Error Locus
        ReferTask       JFN_Flg_Ptr,DWORD   ; ptr to array of per-fd flags
        ReferTask       ProcType,BYTE

        ReferGDT        GDT_FSC,WORD

;b734380  ReferFlat       _fPerfview,dword

        ReferGlobal  SFNAllocCtr,DWORD

        FARCODE HIGH2
HIGH2CODE SEGMENT
        ASSUME  SS:TASKAREA


;***    api_OpenCreate - Open or Create a file to get a handle
;
;       api_OpenCreate gets handle to file (which may be created)
;       or device.
;       api_OpenCreate will check the reserved bit fields in
;       the open mode word and return an error any is not
;       not zero. The  monitor open (bit 3) bit is ignored.
;       It will call w_OpenCreate to do the open work.
;       api_OpenCreate will pass any errors returned from
;       w_OpenCreate back to the caller.
;
;       This is called by both DosOpen and DosOpen2. If called from DosOpen,
;       it makes the stack frame looks like that of DosOpen2 before calling
;       w_opencreate.
;
;
;       ENTRY POINT:        api_OpenCreate
;       LINKAGE:            FAR CALL
;
;       ENTRY   AL = Interface level
;                   (IFaceLevel1 => DOSOPEN
;                    IFaceLevel2 => DOSOPEN2
;                    IFaceLevel3 => DOSOPEN2COMPT)
;                    IFaceLevel4 => DOSPROTECTOPEN (i.e. DosOpen3 in comments)
;               SS:BX -> Parameter packet (see OpenStruc, Open2Struc, and Open3Struc3)
;               DS:DX -> copy of asciiz name in kernel buffer
;
;       EXIT    'NC' if OK
;                        (AX) = handle
;                        (BX) = creation action (1 - file existed,
;                                                2 - file created,
;                                                3 - file replaced)
;
;               'C' if error
;                        (AX) = ERROR_DISK_FULL
;                               ERROR_OPEN_FAILED
;                               ERROR_FILE_NOT_FOUND
;                               ERROR_INVALID_PARAMETER
;                               ERROR_PATH_NOT_FOUND
;                               ERROR_ACCESS_DENIED
;                               ERROR_DRIVE_LOCKED
;                               ERROR_NOT_DOS_DISK
;                               ERROR_SHARING_BUFFER_EXCEEDED
;                               ERROR_SHARING_VIOLATION
;                               ERROR_INVALID_ACCESS
;                               ERROR_CANNOT_MAKE
;                               ERROR_TOO_MANY_OPEN_FILES
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
;       INTERNAL REFERENCES:
;       ROUTINES:
;              w_OpenCreate - open file
;
;       EXTERNAL REFERENCES:
;       ROUTINES:
;
;*********************** START OF PSEUDO CODE *****************
; entry api_OpenCreate;
;
; if open mode reserved bits ON then
;    return ERROR_INVALID_PARAMETER;
;
; if called from DosOpen
;    make stack frame look like DosOpen2's
;
; if (exist_replace and read_only_mode)
;               return ACCESS_DENIED error;
;
; call w_OpenCreate;
; return;
;
;********************** END OF PSEUDO CODE *********************

        ASSUME DS:NOTHING,ES:NOTHING
Procedure api_OpenCreate,hybrid
LocalVar        aoc_IFaceLevel,WORD        ; Interface Level
LocalVar        aoc_NewFlags,BYTE           ; used to reset TCBKnowEAs flag
LocalVar        aoc_para,<size Open3Struc> ; used only if called from DosOpen
                                           ; or DosOpen2

        PMONLY
        EnterProc <ax>

        mov     [aoc_NewFlags],-1       ; default, preserve all flags

;       MMIOPH Performance Hook Removed
;       Maj/Min = 306AH, Data = Filename (Asciiz string at ds:dx)

        cmp     al,IFaceLevel1        ; called from DosOpen?
        je short aoc01                ; yes, clone a DosOpen3 parameter packet

        cmp     al,IFaceLevel2        ; called from DosOpen2?
        je short aoc02                ; yes, clone a DosOpen3 parameter packet
        jmp short aoc05               ; called from DosOpen3, check parameters

; called from DosOpen. Clone a parameter packet that looks like DosProtectOpen's
aoc01:
        SaveReg <ds>                    ; save path pointer.Segmt
        TaskContext <es,ds>
        mov     si,bx                   ; ds:si -> DosOpen parameter packet
        lea     di,aoc_para             ; es:di -> empty DosOpen3 param packet
        cld                             ; march forward

        .386
        .errnz  opns_Reserved
        .errnz  opn3s_Reserved
        movsd                           ; copy dword of reserved

        .errnz  opn3s_FileHandleLockIDPtr - 4
        xor     eax,eax
        stosd                           ; FileHandleLockIDPtr = 0

        .errnz  opn3s_EABufPtr - 8
        xor     eax,eax
        stosd                           ; EABufPtr = 0
        .errnz  opns_Mode - 4
        .errnz  opn3s_Mode - 12
        movsw                           ; copy openmode (low word)

        .errnz  opn3s_Mode2 - 14
        stosw                           ; high word of openmode = 0

; the remaining fields are the same, so do rep movsw
; since we don't need to use the original NamePtr, we don't copy it either

rsize = opns_NamePtr - opns_Flag        ; remaining bytes to copy
        .errnz  rsize - (opn3s_NamePtr - opn3s_Flag)
        .errnz  rsize and 3             ; assume size is even number

        mov     cx,rsize shr 2          ; cx = remaining words to copy
        rep     movsd                   ; copy mode, flag, attrs, size, ...
        CpuMode reset

        lea     bx,aoc_para             ; ss:bx -> cloned parameter packet
        RestoreReg <ds>                 ; ds:dx -> path
        jmp     short aoc10

    assume ds:nothing,es:nothing

; called from DosOpen2. Clone a parameter packet that looks like DosOpen3's
aoc02:
        SaveReg <ds>                    ; save path pointer.Segmt
        TaskContext <es,ds>
        mov     si,bx                   ; ds:si -> DosOpen2 parameter packet
        lea     di,aoc_para             ; es:di -> empty DosOpen3 param packet
        cld                             ; march forward

        .386
        .errnz  opns_Reserved
        .errnz  opn3s_Reserved
        movsd                           ; copy dword of reserved

        .errnz  opn3s_FileHandleLockIDPtr - 4
        xor     eax,eax
        stosd                           ; FileHandleLockIDPtr = 0

; the remaining fields are the same, so do rep movsw
; since we don't need to use the original NamePtr, we don't copy it either

        .errnz  opn3s_EABufPtr - 8
rsize = opn2s_NamePtr - opn2s_EABufPtr        ; remaining bytes to copy
        .errnz  rsize - (opn3s_NamePtr - opn3s_EABufPtr)
        .errnz  rsize and 3             ; assume size is even number

        mov     cx,rsize shr 2          ; cx = remaining words to copy
        rep     movsd                   ; copy mode, flag, attrs, size, ...
        CpuMode reset

        lea     bx,aoc_para             ; ss:bx -> cloned parameter packet
        RestoreReg <ds>                 ; ds:dx -> path

    assume ds:nothing,es:nothing

;       Set the KnowEA's bit for all DosOpen2 calls
aoc05:  mov     di,[CurrTCB]            ; ds:si -> CurrTCB
        mov     al,TASKAREA:[di].TCBNewFlags; (al) = TCBNewFlags
        or      al,NOT TCBKNOWEAS       ; preserve KnowEAs, set all other flags
        mov     [aoc_NewFlags],al       ; save the flag mask
        or      TASKAREA:[di].TCBNewFlags,TCBKNOWEAS; set the know EAs flag

aoc10:  mov     ah,attr_hidden+attr_system+attr_directory   ; AH = search attributes
        mov     al,OPCR_SATTR_INDIRECT
;
; Check the open mode parameter. Since w_Opencreate will check for reserved
; bits as well, we are mainly only making sure open2_phys_disk is not set.
;
        test    TASKAREA:[bx].opn3s_Mode2,o_mode2_reserved
                                        ; are reserved bits zero?
        jnz     short aoc90             ; no, go fail the operation

; save path pointer in NamePtr. This pointer is pointing to a copy of the
; path in the kernel buffer, i.e. this pointer is validated.

        mov     TASKAREA:[bx].opn3s_NamePtr.Offst,dx
        mov     TASKAREA:[bx].opn3s_NamePtr.Segmt,ds

; AH = attribute to set
; AL = 0/1 - flag to say whether to call SAttrib or do direct setting of attrib
; SS:[BX] = arg. frame (opn3Struc - see argframe.inc)
;
; don't allow file-replace if opened for read only

        mov     cx,TASKAREA:[bx].opn3s_Mode     ; cx = open mode LSW
        and     cx,open_access          ; mask other than access bits
        cmp     cx,open_for_read        ; open for read only access mode
        jne     short aoc20             ; no , skip truncate check

        mov     cx,TASKAREA:[bx].opn3s_Flag     ;
        and     cx,exist_mask           ; have only exist options
        cmp     cx,exist_replace        ; replacing a file with read-only access
        je      short aoc100            ; yes, go return error

aoc20:  mov     dx,aoc_IFaceLevel       ; dx = Interface Level
;       Exec only flags set false       ; exec_only BN040
        xor     dh,dh                   ; exec_only BN040
        and     dh, not EXECOPEN        ; exec_only BN040

        call    w_OpenCreate

;       MMIOPH Performance Hook Removed
;       Maj/Min = 30EAH, Data = ax (handle)

aoc80:  pushf
        mov     si,[CurrTCB]            ; si -> CurrTCB
        mov     cl,[aoc_NewFlags]       ; (cl) = TCBNewFlags mask
        and     TASKAREA:[si].TCBNewFlags,cl; mask to the old flags
        popf
        LeaveProc
        ret

aoc90:  mov     ax,ERROR_INVALID_PARAMETER
        stc
        jmp     short aoc80

aoc100: mov     ax,ERROR_ACCESS_DENIED
        stc
        jmp     short aoc80

EndProc   api_OpenCreate



;***    w_OpenCreate - Get handle to file, device or physical disk
;
;       w_OpenCreate will check for physical disk and cache bypass or
;       direct open conflicts. It will also check for cache bypass - direct
;       open conflict. It then sets up a JFN entry and an SFT and calls
;       TransPath to check the path, canonicalize it and to set up the
;       information for roouting the call to the correct file system.
;       It then calls FSD_OpenCreate that will route the call to the
;       correct file system routine, where the low-level work will be done.
;
;
;       ENTRY   AL - Flag for Sattrib (0 => Set SAttrib directly
;                                      1 => Call SetAttrib to set SAttrib)
;               AH - Value for SAttrib
;               DH - exec_only value                                       exec_only BN040
;               DL - Interface Level
;                       (IFaceLevel1 => DOSOPEN
;                        IFaceLevel2 => DOSOPEN2
;                        IFaceLevel3 => DOSOPEN2COMPT
;                        IFaceLevel4 => DOSPROTECTOPEN (i.e. DosOpen3 in comments)
;               SS:[BX] -> argument frame on stack (see Open3Struc in
;                          ARGFRAME.INC). The NamePtr is already validated.
;
;       EXIT:   'NC' if OK
;                        (AX) = handle
;                        (BX) = creation action  (1 - file existed,
;                                                 2 - file created,
;                                                 3 - file replaced)
;               'C' if error
;                        (AX) = ERROR_DISK_FULL
;                               ERROR_OPEN_FAILED
;                               ERROR_FILE_NOT_FOUND
;                               ERROR_INVALID_PARAMETER
;                               ERROR_PATH_NOT_FOUND
;                               ERROR_ACCESS_DENIED
;                               ERROR_DRIVE_LOCKED
;                               ERROR_NOT_DOS_DISK
;                               ERROR_SHARING_BUFFER_EXCEEDED
;                               ERROR_SHARING_VIOLATION
;                               ERROR_INVALID_ACCESS
;                               ERROR_CANNOT_MAKE
;                               ERROR_TOO_MANY_OPEN_FILES
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
;       ANCESTORS
;                             $Open,         w_Create,
;       DosOpen DosOpen2  $OpenExtended     w_CreateNew  DevReturnHandle  ???
;          \       /            \________         |   _________/__________/
;           \     /        (6C)          \        |  /
;        api_opencreate  $Extended_Open  |   DoOpenCreate
;               \___________    |    ___/         /
;                           \   |   /  __________/
;                            \  |  /  /
;                          w_opencreate
;
;       ALGORITHM
;
;           Check parameters passed by user
;           Set SAttrib depending on input flag
;           CheckAccessAX()
;           Allocate JFN and SFN
;           if (open_direct)
;               set up path name to "d:\<NULL>"
;           if (phys_disk)
;               SetPhysDiskSFT()
;           else {
;               TransPath(OpenBuf)
;               if (!error) {
;                   if (device)
;                       Take actions if spooled device
;                   if (open_direct) {
;                       if bad path
;                           jump to errorpath
;                   }
;                   if (meta chars) {
;                       error = file not found
;                       jump to errorpath
;                   }
;                   set sfi_mode
;                   FSD_OpenCreate()
;                   if (error)
;                       jump to errorpath
;                   else
;                       Set printer fonts if printer
;               }
;               else {
;               errorpath:  map error
;                           Unlink the SFT (set refcnt = 0)
;               }
;           }
;           if (!error)
;               Set up JFN entry            /* OK to use SFT now */
;           return any errors
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure w_OpenCreate,<near,faronly,far16>
LocalVar  wOpn_AX,DBYTE,wOpn_SAttrFlg,wOpn_SAttrVal
                                        ; Flag and Value for SAttrib
LocalVar  wOpn_ArgFrame,WORD            ; Offset on stack of argument frame
LocalVar  wOpn_IFaceLevel,WORD          ; Interface Level
LocalVar  wOpn_mode,WORD                ; Inherit, Write-through, etc.
LocalVar  wOpn_Action,WORD              ; Action taken for create
LocalVar  wOpn_TmpName,4                ; Temp name buffer for DASD open
LocalVar  wOpn_SFN,WORD                 ; SFN allocated for this handle
LocalVar  wOpn_JFN,WORD                 ; JFN allocated for this handle
LocalVar  wOpn_pJFN,DWORD               ; Pointer to the JFN for this handle
LocalVar  wOpn_openbuf,PMPATHLEN        ; Temp Buffer
LocalVar  wOpn_dummy,WORD               ; KLUDGE: see gshare.asm
LocalVar  wOpn_dummycds,<SIZE curdir>   ; temporary CDS
LocalVar  wOpn_flags2,WORD              ; Save flags2 if nonspooled set
LocalVar  wOpn_FileHandleLockID,DWORD   ; FileHandleLockID allocated for this handle
LocalVar  wOpn_exec_only,WORD           ; exec_only flag    BN040


        PMONLY
        EnterProc <ax,bx,dx>

        mov     byte ptr wOpn_exec_only,dh              ; exec_only flag BN040
        xor     dh,dh                                   ; exec_only flag BN040
        mov     byte ptr wOpn_exec_only+1,dh            ; exec_only flag BN040

        push    bx
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        lea     cx,wOpn_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,cx            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],cx ; Initialize ptda_pOpenBuf
        lea     cx,wOpn_dummycds
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,cx       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],cx ; Initialize ptda_pCDS
        xor     bx,bx
        mov     wOpn_flags2, bx                         ; Save flags2 if nonspooled set ;### B729771
        pop     bx                                      ;### PTR B720732
;
; Check reserved bits in openmode. Note that open2_phys_disk is not a
; reserved bit in w_OpenCreate.
;
        mov     cx,TASKAREA:[bx].opn3s_Mode     ; cx = openmode (LSW)
        mov     wOpn_mode,cx            ; save it for later use
        test    cx,o_mode_reserved
        jnz     short opc40             ; reserved bits set, go return error
        test    TASKAREA:[bx].opn3s_Mode2,o_mode2_reserved and (not open2_phys_disk)
        jnz     short opc40             ; reserved bits set, go return error

; check for invalid locality of reference value

        mov     dx,cx                   ; dx = copy of openmode
        and     dx,open_locality        ; isolate loc. of ref. bits
        cmp     dx,locality_semirandom  ; valid value?
        ja      short opc40             ; no, error exit
;
; Check the open mode for physical disk - dasd conflict
;
        test    cx,open_direct          ; dasd open?
        jz      short opn20             ; no, no conflict
        test    TASKAREA:[bx].opn3s_Mode2,open2_phys_disk ; phys disk set?
        jnz     short opc40             ; yes, error
;
;       We get here if the open mode parameter has:
;       1. All its reserved bits set to zero,
;   and 2. phys disk ON and DASD OFF
;    or 3. phys disk OFF
;
opn20:  cmp     TASKAREA:[ProcType],PT_REALMODE ; Are we real mode box?
        je      short opc30             ; Yes, compatibility mode allowed.
        mov     di,CurrTCB
        test    SS:[di].TCBfSharing,-1
        jnz     short opc30             ; fSharing set, skip compat test
;
; Test for compatibility open, which is disallowed for all but fSharing.
;
        test    cx,open_sharing_mode    ; compatiblity mode open?
        jnz     short opc30             ; no -- check open flag parameter
;
; We're try to do a compatibility open in protected mode.
; Check wOpn_IFaceLevel and allow the open ONLY if we were
; called from DOSOPEN2COMPT.
;
        mov     cx,wOpn_IFaceLevel      ; CL = Interface Level
        cmp     cl,IFaceLevel3          ; called from DOSOPEN2COMPT?
        jne     short opc45             ; no -- return error_invalid_access
;
; Check the open flag parameter
;
opc30:  mov     cx,TASKAREA:[bx].opn3s_Flag  ; CX = Open flag value
        test    cx,o_flag_reserved      ; are all the reserved bits zero?
        jnz     short opc40             ; no -- return error_invalid_parameter

        and     cl,exist_mask           ; check the exist part of the parameter
        cmp     cl,exist_replace
        ja      short opc40             ; bad parameter return error
;
; Valid attribute word?
;
        test    TASKAREA:[bx].opn3s_Attrs, NOT attr_changeable
        jnz     short opc50             ; bad parameter -- ERROR_ACCESS_DENIED
        or      TASKAREA:[bx].opn3s_Attrs,attr_archive  ; Always set this bit
;
; Valid initial size?
;
        test    TASKAREA:[bx].opn3s_Size.HighWord,8000H ; High order word of size signed?
        jns     short opc60             ; Size is unsigned, OK

;   Bad value in reserved byte of attribute or control, bad control value
;   for exist or create, negative initial size, or bogus attribute.

opc40:  mov     ax,ERROR_INVALID_PARAMETER
        jmp     short opc58

opc45:  mov     ax,ERROR_INVALID_ACCESS
        jmp     short opc58

opc50:  mov     ax,ERROR_ACCESS_DENIED
        jmp     short opc58

opc55:  mov     ax,ERROR_OPEN_FAILED
opc58:  jmp     opc108

;
; Disallow opening a file read-only with a create size > 0 (DSR MS2032)
;
opc60:  mov     cx,TASKAREA:[bx].opn3s_Mode     ; CX = open mode
        and     cx,open_access                  ; isolate access bits
        cmp     cx,open_for_read+open_sharing_mode ;rdw 80893 open read-only?
        jne     short opc60a                    ; no -- go check DASD

        mov     cx,TASKAREA:[bx].opn3s_Flag     ; CX = open flags
        and     cx,nexist_mask                  ; isolate not-exist bits
        cmp     cx,nexist_create                ; create-if-not-exist?
        jne     short opc60a                    ; no -- go check DASD

        mov     cx,TASKAREA:[bx].opn3s_Size.HighWord
        or      cx,TASKAREA:[bx].opn3s_Size.LowWord     ; size == 0?
        jnz     short opc45             ; trying to open read-only,
                                        ; create-if-not-exist, and
                                        ; size > 0, ERROR_INVALID_ACCESS
;*  Special cases?
;
;   Direct access block device OPEN?
;
opc60a: test    wOpn_mode,open_direct ; Caller ask for direct access open?
    .errnz      open_direct LT 100h
        jz      short opc70             ; NOT direct open, go do normal open

;
; Caller wants direct open, is control word reasonable?
;
        cmp     TASKAREA:[bx].opn3s_Flag,(nexist_fail shl 4) or exist_open
        jne     short opc40             ; control word conflicts with direct
                                        ; access open, go complain
;
; If we fall through to here, we have a direct open request, and other
; parameters appear ok.  Lower level routines will take care of
; direct versus fat fs open issues.
;
opc70:  mov     cx,TASKAREA:[bx].opn3s_Flag
        jcxz    short opc55             ; always fail, go directly to exit
    .errnz      exist_fail
    .errnz      nexist_fail
        xor     cx,cx                   ;
        mov     cl,wOpn_SAttrVal
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     TASKAREA:[bp].TCBSattrib,cx   ; Assume direct setting of SAttrib
        UnSetBP_CurrTCB
                                        ;72417 end
        mov     ch,cl
        mov     cl,wOpn_SAttrFlg
        or      cl,cl
    .errnz OPCR_SATTR_DIRECT
        jz      short opc75             ; Our assumption was correct
        call    SetAttrib
opc75:  mov     ax,wOpn_mode
        call    Check_Access_AX         ; check sharing/access mode
        LJC     opc108                  ; error - give up

;
;   WARNING ** WE MUST NOT BLOCK AFTER SFN IS SAVED AWAY IN ITS JFN CELL,
;           ** UNLESS SFT IS FILLED AND VALID, OTHERWISE AN ERRANT WRITE
;           ** FROM ANOTHER THREAD MAY WRITE THROUGH THE INVALID SFT, WITH
;           ** VERY DESTRUCTIVE EFFECT.
;

;
; Find a free area in the user's JFN table.
;
        call    JFNFindFree             ; get a free JFN
        LJC     opc108                  ; No JFNs, go return error

        mov     wOpn_pJFN.Offst,di      ; save the JFN offset
        mov     wOpn_pJFN.Segmt,es      ; save the JFN segment
        mov     wOpn_JFN,bx             ; save the JFN itself
;
; Grab a free SFT and mark it as busy.
;
        call    SFNAlloc                ; get a free SFN
        LJC     opc105                  ; oops, no free SFT's

        mov     wOpn_SFN,bx             ; save the SFN for later
        mov     si,CurrTCB
        mov     TASKAREA:[si].TCBThisSFT.Offst,di
        mov     TASKAREA:[si].TCBThisSFT.Segmt,es

        mov     bx,wOpn_ArgFrame

        cmp     byte ptr wOpn_IFaceLevel,IFaceLevel4  ; Interface Level = ProtectOpen?
        jne     opc77                    ; no, skip filehandleLockID ptr check

; verify non-null FileHandleLockIDPtr
        mov     ax, TASKAREA:[bx].opn3s_FileHandleLockIDPtr.HighWord
        or      ax, TASKAREA:[bx].opn3s_FileHandleLockIDPtr.LowWord
        jnz     opc75a

; user requested a protected file-handle but specified a null return
; buffer address

        mov     ax,ERROR_INVALID_PARAMETER
        jmp     opc101          ; jump to EXIT

opc75a:
;  protected file-handles get a SFN LockID
        test    TASKAREA:[bx].opn3s_Mode2, open2_protected_filehandle
        jz      opc76             ; user wants normal unprotected file handle


; Set the sf_LockID in case this SFN is for protected filehandle access
        push    ds
        DosContext <ds>
        mov     ax, [SFNAllocCtr].LowWord
        mov     es:[di].sf_LockID.LowWord, ax
        mov     ax, [SFNAllocCtr].HighWord
        mov     es:[di].sf_LockID.HighWord, ax
        pop     ds
        ASSUME   DS:NOTHING

opc76:
; move FileHandleLockID to localvar

        mov     ax, es:[di].sf_LockID.LowWord
        mov     wOpn_FileHandleLockID.LowWord, ax
        mov     ax, es:[di].sf_LockID.HighWord
        mov     wOpn_FileHandleLockID.HighWord, ax

opc77:
;
; Some of the openmode bits are stored in the JFN flags (a byte). Here we
; extract those bitFods and pack them into a byte.
;
        mov     ax,wOpn_mode            ; get open mode

    .errnz      open_no_inherit    - (JFN_DIH    SHL 7)
    .errnz      open_autofail      - (JFN_FAIL   SHL 7)
    .errnz      open_write_through - (JFN_WRTTHR SHL 7)
    .errnz      open_no_cache      - (JFN_NCACHE SHL 7)

        shl     ax,1                    ; put relevant bits in ah
        and     ah,JFN_USED             ; mask relevant bits
        mov     si,CurrTCB
        mov     TASKAREA:[si].TCBJfnFlag,ah ; set local autofail/writethru flags

        les     di,JFN_Flg_Ptr          ; get pointer to JFN flags
        mov     bx,wOpn_JFN             ; use handle to index to JFN entry
        mov     es:[di+bx],ah           ; set no inherit/autofail bits

        mov     bx,wOpn_ArgFrame        ; ss:bx -> open3struc
        lds     si,TASKAREA:[bx].opn3s_NamePtr ; ds:si -> name
;
; If we are trying to open a drive for direct access, we need to sanity
; check the path and put it into a form that TransPath will accept.
;
        test    wOpn_mode,open_direct ; is this a normal file/device open?
        jz      short opc80             ; yes, go normalize the path
;
; We test to make sure the path is nothing more than DRIVE:.
;
        cmp     word ptr [si+1], 0 shl 8 + ':'  ; does it end with ":<NUL>"?
        mov     ax,ERROR_PATH_NOT_FOUND ; presume error
        LJNZ    opc101                  ; not recognized, go signal error
;
; TransPath will barf if we give it a path of DRIVE:. Copy onto local
; buffer, tack on a \ to make it acceptable to TransPath.
;
        mov     ax,[si]                 ; get drive:
        lea     si,wOpn_TmpName         ; point at temp area
        mov     TASKAREA:[si],ax        ; set drive:
        mov     word ptr ss:[si+2],0 shl 8 + '\'  ; concatenate \, NUL terminate
        mov     TASKAREA:[bx].opn3s_NamePtr.Offst,si
        mov     TASKAREA:[bx].opn3s_NamePtr.Segmt,ss    ; new source pointer
;
; Set the fOPENDASD flag bit in the TCB to handle any unformatted
; media in the drive. We don't want to fail a direct OPEN on a
; drive that has an unformatted diskette in it. WhatVolume will
; ensure that a VPB is set up for the volume.
;
opc80:  mov     di,CurrTCB
        mov     TASKAREA:[di].TCBModeFlags,0    ; this is a "normal" OPEN/CREATE
;
; Check if open mode has physical disk bit ON.
;
        test    TASKAREA:[bx].opn3s_Mode2,open2_phys_disk ; physical disk open?
        jz      short opc85             ; no, continue with normal open
        lds     si,TASKAREA:[di].TCBThisSFT     ; ds:si -> SFT
        jmp     opc91                   ; go directly fill in SFT

;
; This is not the physical disk open call
;
opc85:  push    cx                      ; save routine to call
        push    bp                      ; save frame pointer

        test    wOpn_mode,open_direct
        jz      short opc87             ; This is not a DASD open
        mov     TASKAREA:[di].TCBModeFlags,fOPENDASD  ; Set flags for DASD open

opc87:  lea     di,wOpn_openbuf         ; appropriate buffer
        lds     si,TASKAREA:[bx].opn3s_NamePtr  ; input buffer
        call    TransPath               ; convert the path
    ASSUME   DS:TASKAREA
        pop     bp                      ; restore frame pointer
        pop     di                      ; (DI) = address of routine to call
    ASSUME   DS:NOTHING
        mov     si,CurrTCB
        lds     si,TASKAREA:[si].TCBThisSFT
    ASSUME   DS:NOTHING
        LJC     opc93                   ; jump if error

        mov     ds:[si].sf_flags2, 0    ; We initialize flags here.
        mov     bx, [wOpn_ArgFrame]
        test    TASKAREA:[bx].opn3s_Mode2, open2_nonspooled
        jnz     short opc88a             ; nonspooled access   ;### B729771

        ; wOpn_openbuf -> Formatted name
        ; DS:SI  SFT

    ASSUME   DS:NOTHING

        SaveReg <ds, si>
        mov     bx, [wOpn_ArgFrame]
        call    OpenSpooled                     ; Get spooler data  if spooled
        RestoreReg <si, ds>
        jc      short opc93                     ; error, get out
        jz      short opc88                     ; not spooled, continue
        mov     cx,TASKAREA:[bx].opn3s_Mode
        mov     wOpn_mode,cx
        jmp     short opc88                                                     ;### B729771

opc88a: mov     wOpn_flags2, SFF2_NONSPOOLED           ; mark SFT as NONspooled ;### B729771
;
; If we're opening a DASD device, check for a SUBSTED or NETWORK drive
;
opc88:  test    wOpn_mode,open_direct   ; is this a direct DASD open?
        jz      short opc89             ; no, go check for other errors
;
; The output drive format MUST be DRIVE:\<NUL>  Anything else is illegal.
;
        mov     ax,ERROR_PATH_NOT_FOUND ; presume error
        cmp     word ptr wOpn_openbuf+1,'\' SHL 8 + ':'
        jne     short opc95             ; not valid name, go signal error
        cmp     byte ptr wOpn_openbuf+3,0; is it NUL at the end?
        jne     short opc95             ; no, go return error

opc89:  mov     ax,ERROR_FILE_NOT_FOUND
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; meta chars present?
        UnSetBP_CurrTCB
                                        ;72417 end
        jne     short opc95             ; yes, go return error

opc91:  mov     ax,wOpn_mode            ; (ax) = mode
;
; We need to get the new inheritance bits.
;
        mov     [si.sf_fsi.sfi_mode],ax         ; initialize mode field
        mov     bx,wOpn_ArgFrame
        mov     ax,TASKAREA:[bx].opn3s_Mode2    ; ax = openmode2
        mov     [si].sf_fsi.sfi_mode2,ax        ; save in SFT
;
; If sf_fsi.sfi_mode2 == physical disk then zero out SFT entry
; and return, else continue with normal open
;
        test    ax,open2_phys_disk      ; physical disk open?
        jz      short opc97             ; no, continue with normal open

        call    SetPhysDiskSFT          ; Set up SFT
;
; Make JFN_Table[wOpn_JFN] -> wOpn_SFN, since SFT is now valid, and thus safe.
;
        mov     ax,wOpn_SFN
        les     di,wOpn_pJFN
        mov     es:[di],ax
                                        ;108976 - vmboot defect
        mov     [wOpn_Action],01        ; Set Open Action to File Existed
                                        ;  if this is uninitialized, it
                                        ;  may end up being 2 and triggering
                                        ;  a call to CNotify_Path_Wake.  It
                                        ;  should be set anyway for completeness.
        jmp     opc111                  ; we're done

;
; This code translates ERROR_META_EXPANSION_TOO_LONG into
; ERROR_FILE_NOT_FOUND. This is because any meta characters
; in the pathname are considered invalid even if the resulting
; expansion is too long.
;
opc93:  cmp     ax,ERROR_META_EXPANSION_TOO_LONG
        jne     short opc95             ; jump if filename not too long
        mov     ax,ERROR_FILE_NOT_FOUND
opc95:  jmp     opc101          ; jump to EXIT

;
; The following fields in the SFT have been set up at this point:
;
;       sf_fsi.sfi_mode
;       sf_ref_count = 0
;       sf_flags
;       sf_flags2
;
opc97:  TaskContext DS
        mov     bx,[wOpn_ArgFrame]      ; SS:BX -> Argument frame to pass
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_NMPIPE ; is a \pipe\ path specified?
        UnSetBP_CurrTCB
                                        ;72417 end
        jz      short opc98             ; no, go handle file/device

; deal with named pipe open

        mov     cx,TASKAREA:[bx].opn3s_Flag ; cx = open flags

;b734380 pushf
;b734380        CpuMode 386
;b734380        SaveReg <es>
;b734380        FlatContext <es>
;b734380        cmp     es:_fPerfview,0         ; check if perfview is up
;b734380        je      short opc97a
;b734380        PvwStartTm thd,PipeCWait,0
;b734380opc97a:
;b734380        CpuMode Reset
;b734380        ASSUME  ES:NOTHING
;b734380        RestoreReg <es>
;b734380        popf

        CALLFAR NmPipeOpen              ; open pipe

;b734380        pushf
;b734380        CpuMode 386
;b734380        SaveReg <es>
;b734380        FlatContext <es>
;b734380        cmp     es:_fPerfview,0         ; check if perfview is up
;b734380        je      short opc97b
;b734380        PvwEndTm thd,PipeCWait,0
;b734380opc97b:
;b734380        CpuMode Reset
;b734380        ASSUME  ES:NOTHING
;b734380        RestoreReg <es>
;b734380        popf

        jmp     short opc98b

opc98:  SaveReg <bp>                    ; save frame pointer
        mov     ch, byte ptr wOpn_exec_only  ; exec_only flag  BN040
        call    FSD_OpenCreate          ; CX = Action taken for create
        RestoreReg <bp>                 ; restore frame pointer

opc98b: mov     si,CurrTCB
        lds     si,[si].TCBThisSFT
        pushf
        push    bx
        mov     bx, wOpn_flags2         ; Get flags value
        or      ds:[si].sf_flags2, bx   ; mark SFT as NONspooled ;### B729771
        pop     bx
        popf
    ASSUME      DS:NOTHING
        jc      short opc95             ; jump to EXIT
;b734380jc      opc95             ; jump to EXIT

;
;* The SFT was successfully opened.  Remove busy mark.  Fill in JFN.
;  Make JFN_Table[wOpnJFN] -> wOpn_SFN, since SFT is now valid, and thus safe.
;
        mov     [wOpn_Action],cx        ; Save it for now
        mov     ax,wOpn_SFN
        les     di,wOpn_pJFN
        mov     es:[di],ax              ; set JFN, it's safe now

        mov     ax,wOpn_JFN
        mov     [si].sf_ref_count,1
        mov     bx,ax                   ; put handle in approriate place

; if( spooled file )
;   set codepage font(DEVICE bit will not be set!)    CP20 @B731489
;
        test    [si].sf_flags2,SFF2_SPOOLED   ; is it a spooled file?
        jnz     short opc98b2                 ; yes -> set codepage

; if( ! DEVICE )
;   not a device - don't set font
;
        test    [si].sf_flags,DEVID_DEVICE
        jz      short opc99

opc98b2:                                ; label if spooled file CP20 @B731489
;
; if( CONsole Input || Output )
;   device is the console - don't set font
;
        test    [si].sf_flags,DEVID_ISCOT or DEVID_ISCIN
        jnz     short opc98c
        test    wOpn_mode,open_monitor  ; DosMonOpen?
        jnz     short opc99             ;  YES, don't send code page IOCTL
;
; (BX) = handle
; (DS:SI) = SFT pointer
;
        CALLFAR SetDeviceFont           ; initialize fonts
        jmp     short opc99

; this open is for a console device ;
; set the jfn bits for console device and raw/cooked mode bits
; ax = jfn

opc98c: les     di,JFN_Flg_Ptr          ; get pointer to JFN flags
        add     di, ax                  ; es:di -> flags for this handle
        or      byte ptr es:[di],JFN_ISCON      ; mark this as a console device
        mov     bx,wOpn_ArgFrame        ; ss:bx -> open3struc
        test    ss:[bx].opn3s_Mode2,open2_io_mode
        jz      opc99
        or      byte ptr es:[di],JFN_RAWIO      ;

opc99:  DBPRT   1,40h,<Open/CreateXX: return $x\n>,<AX>
        jmp     short opc111
;
; AX has error code.  Stack has mode and attr arguments
; to FSD_OpenCreate.
;
opc101: mov     si,CurrTCB
        les     di,TASKAREA:[si].TCBThisSFT
        mov     es:[di].sf_ref_count,0    ; release SFT
;
; The above section gets the index into the current thread control
; block, gets the offset and segment for the current sft, storing
; them such that es:di->sft.  Then the value of sf_ref_count in the
; current sft is set to zero.
;
        call    SFLinkFree              ; put SFT on free list
opc105: lds     si,wOpn_pJFN
        mov     word ptr [si],SFN_NULL  ; free the SFN...

opc108: DBPRT   1,40h,<Open/CreateXX: error $x\n>,<AX>
        stc                             ; no free, return with error set
        jmp     short opc120

opc111: mov     si,CurrTCB
;
; Zero out mode flags. No special case in WhatVolume.
;
        mov     TASKAREA:[si].TCBModeFlags,0
        mov     ax,[wOpn_JFN]           ; AX = handle
        mov     bx,[wOpn_Action]        ; BX = Action taken for create
        cmp     bx, 2
        jne     opc120a                 ;### Notify
        push    ax                      ;### Notify
        mov     ax,1                    ;### File added
        CALLFAR CNotify_Path_Wake       ;### Notify
        pop     ax                      ;### Notify
opc120a:
        clc
opc120:
        pushf
        CpuMode 386
        mov     edx, dword ptr wOpn_FileHandleLockID
        jnc short opc121
        xor     edx, edx
opc121:
        CpuMode Reset
        popf
        LeaveProc
        RETP

EndProc   w_OpenCreate


;***    OpenSpooled -  Get spool file info
;
;
;       ENTRY   TCBThisSFT  -> SFT
;               TASKAREA:[ptda_pOpenBuf] -> name
;               TASKAREA:[BX] -> Argument frame on stack (see w_OpenCreate)
;
;       EXIT    TASKAREA:[BX] -> Argument frame on stack
;               NC:
;                   NZ: Device is spooled. The appropriate fields of the
;                       SFT have been filled-in and mode, mode2 and flags in
;                       Argframe have been set.
;                   ZR: Not a spooled device
;
;
;               CY:
;                   AX = Error code
;
;       USES    AX, BX, CX, DI, ES, DS, SI, Flags
;
;       PSEUDOCODE
;
;           Get name of Spool object;
;           TransactNmPipe(HDR_FILEOPEN, usCurrentCodePage, szSpoolObj,
;                          &JobId, &Openflags, pszSpoolFile);
;           TransPath(pszSpoolFile);
;           if (!error) {
;               Mark SFT as spooled,
;                    save SFN of named pipe and JobId in SFT;
;                    Set codepage to default.
;               Save open mode and flags in ArgFrame;
;           }
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure   OpenSpooled, near, local
LocalVar  Ops_ArgFrame, WORD            ; Offset in stack of argument frame
LocalVar  Ops_pTempBuf, DWORD           ; Temporary buffer
LocalVar  Ops_pTempSeg, DWORD           ; Temporary segment
LocalVar  Ops_SFT, DWORD                ; SFT of file
LocalVar  Ops_NmPipeSFN, WORD           ; SFN of named pipe
LocalVar  Ops_NmPipeStruc, <size DosTransactNmPipeStruc>

        EnterProc <bx>

        TaskContext <ds>
        mov     si,CurrTCB                                                    ;### PTR B720732
        mov     si,DS:[si].TCBpOpenBuf              ; DS:SI -> Formatted name ;### PTR B720732
;### PTR B720732        mov     si,DS:[ptda_pOpenBuf]               ; DS:SI -> Formatted name
        CALLFAR CheckSpoolDevice                    ; Is it a spool device?
        jnc     short Ops10                         ; yes, DS:SI -> Spool record
        xor     ax, ax
        jmp     Ops85                               ; return ZR, NC


Ops10:  CALLFAR GetTempBuffer                       ; ES:DI -> Temp Buffer
        mov     Ops_pTempBuf.Segmt,es
        mov     Ops_pTempBuf.Offst,di               ; Save the pointer

;   We have to get the name of the spool file, the open
;   flags and mode from the spooler.

        TaskContext <es>
        mov     di, es:[CurrTCB]
        les     di, es:[di].TCBThisSFT              ; ES:DI -> SFT (file)
        mov     Ops_SFT.Segmt,es
        mov     Ops_SFT.Offst,di                    ; Remember SFT (file)

;   Remember the SFN of the named pipe (will need it later)

        mov     ax, ds:[si].SDE_Sfn                 ; AX == SFN (named pipe)
        mov     [Ops_NmPipeSFN], ax                 ; Remember SFN

;   We now have to build the message that we will send to the spooler.
;
;   DS:SI -> Spool Record
;   ES:DI -> SFT (file)
;
        mov     ax, es:[di].sf_fsi.sfi_selfsfn      ; AX == SFN of file
        les     di, Ops_pTempBuf                    ; ES:DI -> Buffer
        mov     es:[di].MSGOUT_Header.MHO_Type, HDR_FILEOPEN
        mov     es:[di].MSGOUT_Header.MHO_uHandle, ax
        push    ds
        TaskContext <ds>
        mov     ax, ds:[codepage_tag]               ; AX: current codepage
        ASSUME DS:NOTHING
        pop     ds
        mov     es:[di].MSGOUT_Msg.MOO_Codepage, ax            ; Put codepage
        CpuMode 386
        mov     eax, ds:[si].SDE_ulKey
        mov     es:[di].MSGOUT_Msg.MOO_ulKey, eax              ; Put attached key
        CpuMode Reset
        mov     cx, (size MSG_OPEN_OUT + size MSG_HDR_OUT)     ; CX: Lenght of message

;   Now fill the fields of the NmPipeStruc for InBuffer

        mov     [Ops_NmPipeStruc].tnmp_InBufferLen, cx
        mov     [Ops_NmPipeStruc].tnmp_InBuffer.Segmt,  es
        mov     [Ops_NmPipeStruc].tnmp_InBuffer.Offst,  di

;   We now have the attached key and the SFN of the named
;   pipe, We need a buffer to read information from the pipe.
;   The size of the buffer will be MAXPATHLEN (for file name) +
;   2 (error code) + 2 (mode) + 2 (mode2) + 2 (flags)

        xor     ax, ax
        mov     bx, (MAXPATHLEN + size MSG_OPEN_OUT + size MSG_HDR_OUT) ; AX:BX -> size
        mov     ch,D_DATA0
        mov     cl,SA_GDT                           ; SFlags and AFlags
        mov     si,HF_MOVABLE
        mov     di,FSCDEVTMPOWNER
        CALLFAR SegAlloc                            ; BX:00 -> Segment
        LJC     Ops95                               ; error, return Open_failed
        mov     es,bx
        xor     di,di                               ; ES:DI -> segment
        mov     Ops_pTempSeg.Segmt,es
        mov     Ops_pTempSeg.Offst,di               ; Save the pointer

;   Now fill the fields of the NmPipeStruc for OutBuffer

        mov     [Ops_NmPipeStruc].tnmp_OutBufferLen, (MAXPATHLEN + 4)
        mov     [Ops_NmPipeStruc].tnmp_OutBuffer.Segmt, es
        mov     [Ops_NmPipeStruc].tnmp_OutBuffer.Offst, di

;   Communicate with the spooler via TransactSpooler

        mov     dx,  [Ops_NmPipeSFN]                ; DX: SFN (pipe)
        lea     si,  [Ops_NmPipeStruc]              ; SS:SI -> struct.
        call    TransactSpooler
        LJC     Ops97                               ; error, get out

        lds     si, Ops_pTempSeg                    ; DS:SI -> Read Buffer

;   If the spooler wants us to return error, we do so

        mov     ax, ds:[si].MSGIN_Header.MHI_Error
        test    ax, ax
        jz      short Ops50
        stc
        jmp short Ops60

;   Call TransPath to make sure that the name passed by the spooler is
;   valid (and to set variables in ptda).

Ops50:  SaveReg <ds, si>
        lea     si, ds:[si].MSGIN_Msg.MOI_szSpoolFileName
        mov     di,CurrTCB                                                    ;### PTR B720732
        mov     di,TASKAREA:[di].TCBpOpenBuf                ; DS:SI -> Formatted name ;### PTR B720732
;### PTR B720732        mov     di,TASKAREA:[ptda_pOpenBuf]         ; appropriate buffer
        call    TransPath                           ; convert the path
        RestoreReg <si, ds>                         ; DS:SI -> Read Buffer
        jc      short Ops60                         ; if error, get out

;   Initialize the fields of the SFT

        les     di, Ops_SFT                         ; ES:DI -> SFT (file)
        or      es:[di].sf_flags2, SFF2_SPOOLED     ; mark SFT as spooled
        or      es:[di].sf_flags, DEVID_DEVICE_RAW  ; Open in Raw mode
        mov     ax, [Ops_NmPipeSFN]
        mov     es:[di].sf_NmPipeSfn, ax            ; Save NmPipe SFN
        push    ds
        TaskContext <ds>
        mov     ax, ds:[codepage_tag];              ; get default codepage
        pop     ds
        mov     es:[di].sf_codepage, ax             ; save it in SFT

;   Set the fields in the Open ArgFrame
;   DS:SI -> Read Buffer
;
        TaskContext <es>
        mov     di, [Ops_ArgFrame]                  ; ES:DI -> ArgFrame
        mov     ax, word ptr ds:[si].MSGIN_Msg.MOI_OpenMode
        mov     es:[di].opn3s_Mode, ax              ; set mode
        mov     ax, word ptr ds:[si + 2].MSGIN_Msg.MOI_OpenMode
        mov     es:[di].opn3s_Mode2, ax             ; set mode2
        mov     ax, ds:[si].MSGIN_Msg.MOI_OpenFlags
        mov     es:[di].opn3s_Flag, ax              ; set flags

;   Restore TCBThisSFT, because now it points to the named pipe SFT
;   and it should point to the file's SFT.

Ops60:  lds     si, Ops_SFT                         ; DS:SI -> SFT (file)
        TaskContext <es>
        mov     di, es:[CurrTCB]
        mov     es:[di].TCBThisSFT.Segmt, ds
        mov     es:[di].TCBThisSFT.Offst, si        ; Restore file's SFT

Ops70:  pushf
        push    ax                                  ; Save possible error code
        mov     ax, [Ops_pTempSeg].Segmt
        CALLFAR SegFree                             ; Free temp. segment
        pop     ax                                  ; Restore possible error code
        popf

Ops80:  pushf
        les     di,Ops_pTempBuf                     ; ES:DI -> temp buffer
        CALLFAR FreeTempBuffer                      ; Free the temp buffer
        popf
        mov     bx, [Ops_ArgFrame]                  ; SS:[BX] -> Argframe on stack
        jc      short Ops85
        xor     ax, ax
        inc     ax                                  ; return NZ
Ops85:  LeaveProc
        Ret

Ops90:  mov     ax, ERROR_PATH_NOT_FOUND
        jmp short Ops80

Ops95:  mov     ax, ERROR_OPEN_FAILED
        jmp short Ops80

Ops97:  mov     ax, ERROR_OPEN_FAILED
        jmp short Ops60

EndProc OpenSpooled


;***    TransactSpooler - Communicate with spooler via TransactNmPipe
;
;
;       ENTRY   SS:SI ->  DosTransactNmPipeStruc
;               DX:       SFN of named pipe
;
;       EXIT
;               CY if error
;               NC if ok.
;
;       USES    AX, BX, CX, DX, DI, ES, DS, SI, Flags
;
;       PSEUDOCODE
;               {
;                   Allocate temporary handle
;                   TransactNmPipe
;                   Release temporary handle
;               }
;
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure   TransactSpooler, hybrid

        EnterProc

;   We have the SFN of the named pipe, but w_TransactNmPipe is handle-based.
;   So we need to create a temporary handle.

        call    JFNFindFree                             ; BX = handle
                                                        ; ES:DI -> JFN entry
        jc      short Trs90

        mov     TASKAREA:[si].tnmp_Handle, bx           ; Put handle in NmPipeStruc
        mov     es:[di], dx                             ; put  SFN in JFN

        SaveReg <es, di>                                ; Remember JFN entry
        mov     dx, bx                                  ; DX = handle
        mov     cx, TASKAREA:[si].tnmp_InBufferLen      ; CX = InbufferLen
        mov     ax, TASKAREA:[si].tnmp_OutBufferLen     ; AX = OutBufferLen
        mov     bx, si                                  ; SS:BX -> struct.

        CALLFAR w_TransactNmPipe
        RestoreReg <di, es>

;   Release the temporary handle

        mov     WORD ptr es:[di], SFN_NULL      ; Free JFN

Trs90:  LeaveProc
        ret

EndProc TransactSpooler


;***    SetPhysDiskSFT - Set up the SFT for a physical disk OPEN
;
;       SetPhysDiskSFT sets up an SFT for a physical disk OPEN operation.
;
;       ENTRY   DS:SI -> SFT
;
;       EXIT    Fields of SFT filled in
;
;       USES    AX, CX, DI, ES, Flags
;
        ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure   SetPhysDiskSFT,NEAR,LOCAL

        mov     [si].sf_FSC.Segmt,GSEL GDT_FSC
        mov     [si].sf_FSC.Offst,FSC_LOCAL ; Set to local FSC

; This SFT is for a physical disk. Zero out all entries

        xor     eax,eax                 ; EAX = value to store (=0)
        push    ds
        pop     es
        lea     di,[si].reserved        ; ES:DI point to reserved field
        mov     cx,(sf_FSC - reserved) SHR 2 ; number of bytes to zero
        .errnz  (sf_FSC - reserved) AND 2
        .erre   (sf_FSC - reserved) AND 1
        rep     stosd
        stosb                           ; zero out reserved, sf_flags,
                                        ;          sf_flags2, sf_devptr
;
; Make sure that the SFT fields are correct
;
    .errnz      sf_flags   -reserved       -1
    .errnz      sf_flags2  -sf_flags       -2
    .errnz      sf_devptr  -sf_flags2      -2
    .errnz      sf_FSC     -sf_devptr      -4
;
; Zero out the relevant fields in the file system independent area of the SFT
;
        lea     di,[si].sf_fsi.sfi_hVPB ; es:di point to sfi_hVPB field
        mov     cx,(sfi_UID - sfi_hVPB) SHR 2 ; number of bytes to zero
        .errnz  (sfi_UID - sfi_hVPB) AND 1
        .erre   (sfi_UID - sfi_hVPB) AND 2
        rep     stosd
        stosw                           ; zero out hVPB,ctime,cdate,atime,
                                        ; adate,mtime,mdate,size,position
;
; Make sure that the SFT file system independent fields are correct
;
    .errnz      sfi_ctime   - sfi_hVPB  - 2
    .errnz      sfi_cdate   - sfi_ctime - 2
    .errnz      sfi_atime   - sfi_cdate - 2
    .errnz      sfi_adate   - sfi_atime - 2
    .errnz      sfi_mtime   - sfi_adate - 2
    .errnz      sfi_mdate   - sfi_mtime - 2
    .errnz      sfi_size    - sfi_mdate - 2
    .errnz      sfi_position - sfi_size - 4
    .errnz      sfi_UID     - sfi_position - 4
;
; Zero out the relevant fields in the file system dependent area of the SFT
;
        lea     di,[si].sf_fsd
        mov     cx,size sftfsd SHR 2    ; Zero out the whole area
        .errnz  size sftfsd AND 1
        .erre   size sftfsd AND 2
        rep     stosd
        stosw
;
;  sf_fsi.sfi_UID already set in SFNAlloc
;  sf_fsi.sfi_PID already set in SFNAlloc
;
        mov     [si].sf_fsi.sfi_PDB,ax  ; no PDB
        mov     [si].sf_ref_count,1     ; remove busy mark
        mov     [si].sf_fsi.sfi_DOSattr,ah ; zero out DOSattr field
        ret
EndProc     SetPhysDiskSFT



;***    w_Creat - Create a new file
;***    w_CreateNew - Create a new file only if it does not exist.
;
;       w_Creat - create the directory entry specified in DS:DX and give
;       it the initial attributes contained in CX
;
;       w_CreateNew - just like w_Create except that the file to
;       be created must not exist.  If file exists return
;       ERROR_FILE_EXISTS.
;
;       ENTRY   DI    = Open mode.
;               BX    = initial attributes
;               DS:DX = ASCIIZ path name
;
;       EXIT    'C' if error
;                   AX has error code
;               'NC' if success
;                   AX has handle
;                   BX is action taken (Open/Create/Replace)
;
;       CALLS   DoOpenCreate
;
;       USES    ALL
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_Creat,HYBRID
        DBPRT   1,60h,<w_Create 1:60 >
        DBPRT   1,40h,<Att = $b file = $S\n>,<CX,DS,DX>
        mov     cx,exist_replace+nexist_create     ; Create file, truncate if exists
        jmp     short AccessSet

Entry   w_CreateNew
        mov     cx,exist_fail+nexist_create        ; Create file, fail if exists

Entry   AccessSet
        PMONLY
        mov     ah,attr_hidden+attr_system  ; AH = search attributes
        xor     al,al                       ; Set SAttrib directly
    .errnz OPCR_SATTR_DIRECT
        xor     si,si                       ; openmode2 = 0
        call    DoOpenCreate                ; Set up parms and call w_OpenCreate
        ret                                 ; Return any error indications
EndProc   w_Creat



;***    w_3xCreateNew - Create a new directory entry
;
;       w_3xCreateNew - 3xBox prelude to the w_CreateNew
;       procedure.  The procedure sets the proper mode (in AX),
;       moves the parameters to the proper registers and calls
;       w_CreteNew. The overall effect is to create a new
;       directory entry.  Return a file handle if there was no
;       previous directory entry, and fail if a directory entry
;       with the same name existed previously.
;
;       ENTRY   DS:DX point to an ASCIIZ file name
;               CX contains default file attributes
;
;       EXIT    Carry Clear:
;                   AX has file handle opened for read/write
;               Carry Set:
;                   AX has error code
;
;       CALLS   w_CreateNew
;
;       USES    ALL
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_3xCreateNew,<near,far16>
        mov     di,sharing_compat+open_for_both
        mov     bx,cx                                   ; (bx) = new file attrs.
        call    w_CreateNew
        RETP
EndProc   w_3xCreateNew



;***    w_CreateTempFile - create a uniquely named file
;
;       Given a directory, create a unique name in that directory.
;       Method used is to get the current time, convert to a name and
;       attempt a CreateNewFilePM.  Repeat until CreateNewFilePM succeeds.
;
;       ENTRY   DS:DX point to a null terminated directory name.
;               CX  contains default attributes
;
;       EXIT    Unique name is appended to DS:DX directory.
;               AX has handle
;
;       USES    all
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_CreateTempFile,far16
LocalVar  EndPtr,DWORD
LocalVar  FilPtr,DWORD
LocalVar  Attr,WORD

        PMONLY
        EnterProc
        test    cx,NOT attr_changeable
        jz      ctf1                    ; Ok if no non-changeable bits set

;       We need this "hook" here to detect these cases (like user
;       sets one both of vol_id and dir bits) because of the structure
;       of the or $CreateNewFile loop below.  The code loops on
;       ERROR_ACCESS_DENIED, but if one of the non changeable attributes
;       is specified, the loop COULD be infinite or WILL be infinite
;       because CreateNewFile will fail with access_denied always.  Thus we
;       need to detect these cases before getting to the loop.

        mov     ax,ERROR_ACCESS_DENIED
        jmp     ctf8

ctf1:   mov     Attr,cx                 ; save attribute
        mov     FilPtrl,dx              ; pointer to file
        mov     FilPtrh,ds
        mov     EndPtrh,ds              ; seg pointer to end of dir
        mov     si,dx
        mov     cx,dx
        neg     cx                      ; number of bytes remaining in segment

;*      look for null at end of string - watch for double byte characters

ctf2:   xor     ax,ax
ctf3:   jcxz    short ctf5              ; end of segment - done
        lodsb                           ; get byte
        dec     cx
        SaveReg <ax>
        TestDBCS                        ; double byte character?
        RestoreReg <ax>
        jnz     short ctf4              ; no - continue below
        mov     ah,1                    ; remember last char was DBCS
        inc     si                      ; skip trailing byte of DBCS char
        dec     cx
        jmp     short ctf3              ; loop again

ctf4:   or      al,al                   ; null?
        jnz     short ctf2              ; no -- loop again

;*      found a null - see if a previous character was path separator

        dec     si                      ; [SI] = null byte
        or      ah,ah                   ; previous character DBCS?
        jnz     short ctf5              ; yes -- cannot be path separator
        mov     al,[si-1]               ; get single byte character
        call    PathChrCmp              ; is path separator?
        jz      short ctf6              ; yes - jump down
ctf5:   mov     byte ptr [si], '\'      ; add a path separator
        inc     si
ctf6:   mov     EndPtrl,si              ; pointer to the tail

;       Start of create loop

ctf7:   DosContext ds                   ; let ReadTime see variables
        SaveReg <bp>
        CALLFAR ReadTime                ; go get time
        RestoreReg  <bp>

;       Time is in CX:DX.  Go drop it into the string.

        les     di,EndPtr               ; point to the string
        mov     ax,cx
        CALLFAR HexToAscii              ; store upper word
        mov     ax,dx
        CALLFAR HexToAscii              ; store lower word
        xor     al,al
        stosb                           ; nul terminate
        lds     dx,FilPtr               ; get name
    ASSUME   DS:NOTHING

        mov     cx,Attr                 ; get attr
        SaveReg <bp>
        call    w_3xCreateNew           ; try to create a new file
        RestoreReg  <bp>
        jnc     short ctf9              ; failed, go try again

;       The operation failed and the error is in AX.

        cmp     ax,ERROR_FILE_EXISTS
        je      short ctf7              ; file existed => try with new name
        cmp     ax,ERROR_ACCESS_DENIED
        je      short ctf7              ; access denied (attr mismatch)
; some other error; if there is a critical error, and retry is done, the same
; procedure is called; So zap the temp. file name added to the given directory
; string within this procedure
        lds     si,EndPtr               ; zap the new file name
        mov     byte ptr [si],0
ctf8:   stc
ctf9:   LeaveProc
        RETP
EndProc   w_CreateTempFile


;***    DoOpenCreate - Set up parameters for call to w_OpenCreate
;
;       DoOpenCreate sets up the argument frame on the stack for the
;       call to w_OpenCreate. It also sets up the registers and PTDA
;       variables necessary for the call.
;
;       It is assumed that if a "replace" operation is OK, then the file
;       will be truncated if it exists. This routine cannot be used as
;       it is to re-size a file to something other than a size of  zero.
;
;       ENTRY   AL    = Flag for Sattrib (0 => Set SAttrib directly
;                                         1 => Call SetAttrib to set SAttrib)
;               AH    = Value for SAttrib (search attributes)
;               DI    = Open mode
;               SI    = Open mode2
;               BX    = Attributes for file if CREATEd (=0 for OPEN only)
;               CX    = Flags for operation (OPEN/CREATE/REPLACE/FAIL)
;               DS:DX = ASCIIZ path name
;
;       EXIT    'NC' if no error
;                   AX = handle
;                   BX is action taken (Open/Create/Replace)
;               'C' if error
;                   AX = Error code
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure   DoOpenCreate,<near,faronly,far16>
LocalVar    dopc_ArgFrame,<size Open3Struc>  ; Argument frame to pass to w_OpenCreate

        EnterProc
        SaveReg <BX>
        lea     bx,[dopc_ArgFrame]      ; SS:BX -> Argument frame
        mov     TASKAREA:[bx].opn3s_Mode2,si
        xor     si,si                   ; si = 0
        mov     TASKAREA:[bx].opn3s_FileHandleLockIDPtr.HighWord,si
        mov     TASKAREA:[bx].opn3s_FileHandleLockIDPtr.LowWord,si
        mov     TASKAREA:[bx].opn3s_EABufPtr.HighWord,si
        mov     TASKAREA:[bx].opn3s_EABufPtr.LowWord,si
        mov     TASKAREA:[bx].opn3s_Mode,di
        mov     TASKAREA:[bx].opn3s_Flag,cx
        RestoreReg <CX>                     ; CX = file attributes
        mov     TASKAREA:[bx].opn3s_Attrs,cx
        mov     TASKAREA:[bx].opn3s_Size.HighWord,si
        mov     TASKAREA:[bx].opn3s_Size.LowWord,si   ; New size will be 0
        mov     TASKAREA:[bx].opn3s_NamePtr.Segmt,ds
        mov     TASKAREA:[bx].opn3s_NamePtr.Offst,dx
        xchg    cx,di                       ; CX = open mode
        mov     dl,IFaceLevel3              ; DL = Interface Level 3 (DosOpen2)
                                            ; compatibility mode open
        xor     dh,dh                       ; exec_only BN040
        and     dh, not EXECOPEN            ; set as not from exec_only BN040
        call    w_OpenCreate
        LeaveProc
        RETP
EndProc DoOpenCreate


;***    w_ChMod - change file attributes - worker routine
;
;       ENTRY   AL 0 = Get attributes
;                  1 = Set attributes
;               CX (if AL = 1)
;                       Attribute to be set
;               DS:DX = pointer to ASCIIZ pathname
;
;       EXIT    Carry Clear
;                       CX = attribute byte (if AL = 0)
;
;               Carry Set (error)
;                       AX = ERROR_INVALID_FUNCTION
;                       AX = ERROR_PATH_NOT_FOUND
;                       AX = ERROR_ACCESS_DENIED
;
;       USES    AX, BX, CX, DX, DI, SI, ES, DS, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_ChMod,<near,faronly,far16>
LocalVar wchm_openbuf,PMPATHLEN         ; temp buffer
LocalVar wchm_dummy,WORD                ; KLUDGE: see gshare.asm
LocalVar wchm_dummycds,<SIZE curdir>
LocalVar wchm_attribs,WORD              ; new attributes                ;@SEC
LocalVar wchm_pattribs,DWORD            ; 16:16 ptr to new attributes   ;@SEC

        PMONLY
        EnterProc
        lea     di,wchm_dummycds
        mov     si,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[si].TCBpTmpCDS.Offst,di       ; Initialize TCBpTmpCDS ;### PTR B720732
;       mov     TASKAREA:[si].TCBpTmpCDS.Segmt,ss       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],di ; Initialize ptda_pCDS
        lea     di,wchm_openbuf         ; appropriate buffer
        mov     TASKAREA:[si].TCBpOpenBuf,di            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],di ; Initialize ptda_pOpenBuf
        push    ax                      ; save function and attributes
        push    cx
        mov     si,dx                   ; get things in appropriate places
        call    TransPathNoJoinIfSame   ; get correct path
        pop     cx                      ; (CX) = attributes
        pop     bx                      ; (BL) = function code
                                        ;72417 moved from PTDA to TCB
        LJC     ChModMapErr             ; jump if error
        TaskContext ds                  ; set up for later possible calls
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_NMPIPE ; named pipe path?
        UnSetBP_CurrTCB
        jnz     ChModErr                ; yes, go error exit
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1
        UnSetBP_CurrTCB
        jne     ChModErr
        SetBP_CurrTCB
        mov     [bp].TCBSattrib,attr_hidden+attr_system+attr_directory
        UnSetBP_CurrTCB
                                        ;72417 end
        sub     bl,1                    ; fast way to discriminate
        jb      short ChModGet          ; 0 -> go get value
        jz      short ChModSet          ; 1 -> go set value
        mov     ExtErr_Locus,ERRLOC_UNK ; Extended Error Locus
        mov     ax,ERROR_INVALID_FUNCTION ; we were given a bad function code
        jmp     short ChModE

ChModGet:
;
; 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
        call    FSD_QFileMode           ; suck out the ol' info
        call    PathDecVPBRef           ; Decrement reference count
        jc      short ChModE            ; error codes are already set for ret
        mov     cx,ax                   ; put attribute in specified register
        jmp     short ChModx

ChModSet:
        mov     ax,cx                   ; get attrs in position
;
; 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
                                                                        ;@SEC
; See if security is interested                                         ;@SEC
        FlatContext <DS>                                                ;@SEC
        cmp     _fSec,0                 ; Is Security enabled           ;@SEC
        jz      ChModSkp                ; No.. business as usual        ;@SEC
                                                                        ;@SEC
; Call SecSetFileMode with registers as follows:                        ;@SEC
;       eax       = 16:16 address of attributes (changeable by callout) ;@SEC
;       WFP_START = ASCIIZ pathname                                     ;@SEC
                                                                        ;@SEC
        mov     wchm_attribs,ax         ; new file attributes           ;@SEC
        lea     ax,wchm_attribs         ; callout may change attribs    ;@SEC
        mov     wchm_pattribs.Segmt,ss                                  ;@SEC
        mov     wchm_pattribs.Offst,ax                                  ;@SEC
        mov     eax,wchm_pattribs                                       ;@SEC
                                                                        ;@SEC
        CALLFAR SecSetFileMode                                          ;@SEC
        TaskContext <ds>                                                ;@SEC
        jc      short ChModE            ; callout rejects. return error.;@SEC
                                                                        ;@SEC
        mov     ax,wchm_attribs         ; restore attribs               ;@SEC
        jmp     short ChModSk2          ; skip over TaskContext         ;@SEC
ChModSkp:                                                               ;@SEC
        TaskContext <ds>                                                ;@SEC
ChModSk2:                                                               ;@SEC
        call    FSD_SetFileMode         ; go set
        call    PathDecVPBRef           ; Decrement reference count
        jmp     short ChModx

ChModMapErr:
        cmp     ax,ERROR_FILENAME_EXCED_RANGE
        je      ChModE
        cmp     ax,ERROR_INVALID_NAME    ;#### PTR B719957
        je      ChModE                   ;#### PTR B719957
ChModErr:
        mov     ax,ERROR_PATH_NOT_FOUND
ChModE: stc
;### PTR B720732 ChModx:        mov     SS:[ptda_pOpenBuf],0    ; Clear ptr to openbuf
;### PTR B720732        mov     SS:[ptda_pCDS],0        ; Clear ptda_pCDS
ChModx: LeaveProc
        ret
EndProc   w_ChMod


;***    w_Unlink - delete a file
;
;       ENTRY DS:DX   points to ASCIIZ path name
;
;       EXIT    Carry set (error):
;                       AX = ERROR_FILE_NOT_FOUND
;                          = ERROR_ACCESS_DENIED
;               Carry not set:
;                       no error
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_Unlink,<faronly,far16>
LocalVar wul_openbuf,PMPATHLEN          ; temp buffer
LocalVar wul_dummy,WORD                 ; KLUDGE: see gshare.asm
LocalVar wul_dummycds,<SIZE curdir>
        PMONLY
        EnterProc
        mov     si,dx                   ; DS:SI -> path
        lea     di,wul_dummycds
        push    bx
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,di       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],di ; Initialize ptda_pCDS
        lea     di,wul_openbuf          ; temp spot for path
        mov     TASKAREA:[bx].TCBpOpenBuf,di            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],di ; Initialize ptda_pOpenBuf
        pop     bx
        SaveReg <bp>
        call    TransPathNoJoinIfSame   ; go get normalized path
        RestoreReg <bp>
        jc      short wun40             ; badly formed path

        TaskContext ds
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; meta chars?
        UnSetBP_CurrTCB
        jne     short wun50
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_NMPIPE OR PNType_LOCDEV OR PNType_PSDEV ; named pipe or device path?
        UnSetBP_CurrTCB
                                        ;72417 end
        jnz     short wun50             ; yes, go error exit

; adding attr_directory below allows FAT FS to return the correct error
; message (error_access_denied) when the target object is a directory and
; not a file.
; Felix Miro' 6/28/90

        mov     ch,attr_hidden+attr_system+attr_directory ; unlink appropriate files
        call    SetAttrib

        call    FSD_Delete                  ; route to FAT, FSD
        jmp     short wunx

wun40:  cmp     ax,ERROR_FILENAME_EXCED_RANGE
        je      short wun60
        cmp     ax,ERROR_INVALID_NAME
        je      short wun60

wun50:  mov     ax,ERROR_PATH_NOT_FOUND
wun60:  stc
;### PTR B720732wunx:   mov     SS:[ptda_pOpenBuf],0    ; Clear ptr to openbuf
;### PTR B720732        mov     SS:[ptda_pCDS],0
wunx:   LeaveProc
        RETP
EndProc   w_Unlink


;***    w_StoreFile - store file in delete directory
;
;       ENTRY DS:DX   points to ASCIIZ path name
;
;       EXIT    Carry set (error):
;                       AX = ERROR_FILE_NOT_FOUND
;                          = ERROR_ACCESS_DENIED
;               Carry not set:
;                       no error
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_StoreFile,<faronly,far16>
          PMONLY
          EnterProc
          SaveReg <bx,cx,dx,si,di,es,ds>

          push ds
          push dx
          CALLFAR w_GetEnv
          pop  dx
          pop  ds

          push ds
          push dx

          push ax
          push 0

          call W_DOSDELETE

          clc

          cmp  ax, 0
          je   wsfx

          stc

wsfx:
          RestoreReg <ds,es,di,si,dx,cx,bx>
          LeaveProc
          RETP
EndProc   w_StoreFile


;***    w_StoreFile3x - store file in delete directory (from dos box)
;
;       ENTRY DS:DX   points to ASCIIZ path name
;             ES:0    points to environment
;
;       EXIT    Carry set (error):
;                       AX = ERROR_FILE_NOT_FOUND
;                          = ERROR_ACCESS_DENIED
;               Carry not set:
;                       no error
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_StoreFile3x,<faronly,far16>
          PMONLY
          EnterProc
          SaveReg <bx,cx,dx,si,di,es,ds>


          push ds
          push dx

          push es
          push 0

          call W_DOSDELETE

          clc

          cmp  ax, 0
          je   wsf3xx

          stc

wsf3xx:
          RestoreReg <ds,es,di,si,dx,cx,bx>
          LeaveProc
          RETP
EndProc   w_StoreFile3x


;***    w_Rename - rename/move directories or files
;
;       ENTRY   DS:DX = points to ASCIIZ string with dir/file name
;               ES:DI = points to ASCIIZ string with new dir/file name
;
;       EXIT    Carry Set (error):
;                       AX = ERROR_FILE_NOT_FOUND
;                          = ERROR_NOT_SAME_DEVICE
;                          = ERROR_ACCESS_DENIED
;               Carry Clear:
;                       No error
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_Rename,<faronly,far16>
LocalVar wre_lpSrc,DWORD                ; -> source path
LocalVar wre_dcde,WORD                  ; Curr_Dir_End of Ren_WFP
LocalVar wre_dhVPB,WORD                 ; hVPB of destination path
LocalVar wre_openbuf,PMPATHLEN          ; openbuf
LocalVar wre_dummy1,WORD                ; KLUDGE: see gshare.asm
LocalVar wre_renbuf,PMPATHLEN           ; renbuf
LocalVar wre_dummy2,WORD                ; KLUDGE: see gshare.asm
LocalVar wre_dummycds,<SIZE curdir>
LocalVar wre_searchbuf,SRCHBUFSIZE+1

        PMONLY

        EnterProc <ds,dx>               ; lpSrc -> source path
        lea     si,wre_searchbuf
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     TASKAREA:[bp].TCB_pSrchBuf,si ; Initialize ptda_pSrchBuf
        UnSetBP_CurrTCB
                                        ;72417 end
        lea     si,wre_dummycds
        push    bx
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,si       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],si ; Initialize ptda_pCDS
        lea     si,wre_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,si            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],si ; Initialize ptda_pOpenBuf
        pop     bx
        push    es
        pop     ds
        mov     si,di                   ; DS:SI -> destination name
        lea     di,wre_renbuf           ; ss:di -> wre_renbuf

;   (DS:SI)       points to destination path
;   (SS:DI) points to buffer in SS:wre_renbuf

        SaveReg <bp>
        call    TransPathNoJoinIfSame   ; setup full path name
        RestoreReg <bp>
        LJC     wre10                   ; error, go error exit

; DOS_Rename will handle meta characters, but only FCB_Rename is allowed
; to use that feature.

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; meta char in destination?
        UnSetBP_CurrTCB
        LJNE    wre20                   ; Yes -> go return error
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_NMPIPE OR PNType_LOCDEV OR PNType_PSDEV ; named pipe or device path?
        UnSetBP_CurrTCB
        LJNZ    wre50                   ; yes, go error exit

; save destination path related variables set by TransPath
; PNType_NEWFILE has to be set up correctly for destination for call
; to FS_MOVE, so save its value and restore after calling TransPath for
; source path.

        mov     si,CurrTCB              ; ss:si -> TCB
        mov     al,TASKAREA:[si].TCBPathNameType
        and     al,PNType_NEWFILE       ; al = 0 or 0x40
        push    TASKAREA:[si].TCBWFP_Start
        pop     TASKAREA:[si].TCBRen_WFP ; Ren_WFP -> well formed dest. path
        push    TASKAREA:[si].TCBCurr_Dir_End
                                        ;72417 end

        pop     wre_dcde                ; save destination Curr_Dir_End
        push    TASKAREA:[si].TCBThishVPB
        pop     wre_dhVPB               ; wre_dhVPB = hVPB of destination

; canonicalize source path

        lds     si,wre_lpSrc            ; ds:si -> source path
        lea     di,wre_openbuf          ; ss:di -> wre_openbuf
        SaveReg <bp,ax>
        call    TransPathNoErrNMP
        RestoreReg <cx,bp>              ; CX = 0 of 0x40
        jc      short wre10

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; meta characters in source path?
        UnSetBP_CurrTCB
        jne     short wre20             ; Yes -> go return error
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_LOCDEV OR PNType_PSDEV ; device path?
        UnSetBP_CurrTCB
                                        ;72417 end
        jnz     short wre50             ; yes, go error exit

        mov     si,CurrTCB              ; ss:si -> TCB
        mov     ax,TASKAREA:[si].TCBThishVPB ; ax = hVPB of source
        cmp     ax,wre_dhVPB            ; source hVPB = destination hVPB?
        jne     short wre40             ; no, go error exit
                                        ;72417 moved from PTDA to TCB
        and     TASKAREA:[si].TCBPathNameType,not PNType_NEWFILE ; turn off PNType_NEWFILE
        or      TASKAREA:[si].TCBPathNameType,cl ; reset PNType_NEWFILE value for dest
                                        ;72417 end

;  Ren_WFP      -> well-formed destination path
;  WFP_Start    -> well-formed source path
;  Curr_Dir_End  = curr_dir_end of WFP_Start (source)
;  wre_dcde      = curr_dir_end of Ren_WFP (destination)

; set the search attribute
;       SetAttrib will set the search attribute (SAttrib) either to the normal
;       value (CH) or to a special value if the current system call is through
;       ServerDosCall.

        mov     ch,attr_all             ; look for all entries
        call    SetAttrib               ; set search attribute for FSD_Move
        mov     dx,wre_dcde             ; dx = Curr_Dir_End of Ren_WFP
;
; 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
        call    FSD_Move                ; dispatch to worker
        call    PathDecVPBRef           ; Remove our reference in the VPB

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

wre10:  cmp     ax,ERROR_FILENAME_EXCED_RANGE
        je      short wre30

wre20:  mov     ax,ERROR_PATH_NOT_FOUND
wre30:  stc
        jmp     short wrex

wre40:  mov     ax,ERROR_NOT_SAME_DEVICE
        jmp     short wre30

wre50:  mov     ax,ERROR_ACCESS_DENIED
        jmp     short wre30

EndProc   w_Rename


;***    SetAttrib - set the search attribute
;
;       SetAttrib will set the search attribute (SAttrib) either to the normal
;       value (CH) or to a special value if the current system call is through
;       ServerDosCall.
;
;       ENTRY   CH           = nominal search attribute
;               TCBfSharing  = indicates whether to use special server attribute
;               TCBSrvAttrib = server attribute to use
;
;       EXIT    CL = SAttrib = Search attribute we really want to use
;
;       USES    CL, FLAGS
;
;       ALGORITHM
;           {   if (TCBfSharing = -1)
;                   CL = TCBSrvAttrib
;               SAttrib = CL
;           }
;

        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure SetAttrib,NEAR,local

        mov     cl,ch
        push    bp
        mov     bp,CurrTCB
        cmp     [bp].TCBfSharing,-1             ; server call?
        jne     short sa1                       ; no
        mov     cl,[bp].TCBSrvAttrib
                                        ;72417 moved from PTDA to TCB
sa1:    mov     [bp].TCBSattrib,0
                                        ;72417 end
        mov     byte ptr [bp].TCBSattrib,cl
        pop     bp
        ret
EndProc   SetAttrib


;**     Check_Access_AX - Check access mode for correctness
;
;       ENTRY   AX is mode
;                 High NIBBLE of AL (Sharing Mode)
;                       sharing_compat     file is opened in compatibility mode
;                       sharing_deny_none  file is opened Multi reader,
;                               Multi writer
;                       sharing_deny_read  file is opened Only reader,
;                               Multi writer
;                       sharing_deny_write file is opened Multi reader,
;                               Only writer
;                       sharing_deny_both  file is opened Only reader,
;                               Only writer
;                 Low NIBBLE of AL (Access Mode)
;                       open_for_read   file is opened for reading
;                       open_for_write  file is opened for writing
;                       open_for_both   file is opened for both read and write
;
;       EXIT    Carry Clear
;                       Mode is correct
;                       AX unchanged
;               Carry Set
;                       Mode is bad
;                       AX = ERROR_INVALID_ACCESS
;
;       USES    AX
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure Check_Access_AX,near,local
        test    ax,o_mode_reserved      ; are all the reserved bits zero?
        jnz     short make_bad_param    ; no -- return error
        push    bx
        mov     bl,al
        and     bl,open_sharing_mode
        cmp     bl,sharing_max          ; is this a good sharing mode?
        ja      short make_bad_access
        mov     bl,al
        and     bl,open_access
        cmp     bl,open_max
        ja      short make_bad_access
        pop     bx
        clc
        ret

make_bad_param:
        mov     ax,ERROR_INVALID_PARAMETER
        stc
        ret

make_bad_access:
        mov     ax,ERROR_INVALID_ACCESS
        pop     bx
        stc
        ret

EndProc Check_Access_AX
;***    CheckNotPipeDir - Check that a path name does not refer to a pipe
;
;       A check is made that a path name after translation does not
;       start with the pipe pseudo-directory, \PIPE\.
;       We also check for the case where a pipe name was specified
;       with a drive letter, as in d:PIPE\foo where the current
;       directory is at the root on drive d.
;
;       ENTRY   [WFP_Start] contains pointer to start of path name
;
;       EXIT    'C' set if pipe name
;                       (AX) = ERROR_PATH_NOT_FOUND
;
;       USES    AX, Flags

        ASSUME  DS:NOTHING,ES:NOTHING
Procedure       CheckNotPipeDir NEAR

        SaveReg <si,ds>

;       test    [PathNameType],PNType_NMPIPE ; is it a named pipe?
;       jnz     short cnpde

        TaskContext DS
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start
                                        ;72417 end
        add     si,2
        cmp     byte ptr ds:[si+PIPEDIRLEN-1],0 ; possibly end with \pipe?
        jne     short cnpd1
        mov     al,'\'
        xchg    al,ds:[si+PIPEDIRLEN-1] ; replace NUL with slash
        jmp     short cnpd2

cnpd1:  mov     al,ds:[si+PIPEDIRLEN-1] ; save character
cnpd2:  SaveReg <ax>
        CALLFAR CheckPipeDir            ; start with d:\pipe\?
        RestoreReg <ax>
        mov     ds:[si+PIPEDIRLEN-1],al ; restore character
        je      short cnpde             ; yes, it's an error
        clc
cnpdx:  RestoreReg <ds,si>
        ret

cnpde:  mov     ax,ERROR_PATH_NOT_FOUND
        stc
        jmp     short cnpdx

EndProc CheckNotPipeDir

HIGH2CODE ENDS

        FARCODE HIGH4
FSSWAPCODE      SEGMENT

ERROR_CPS_CODE_PAGE_INVALID equ 02h     ; code page is not available


; SUBROUTINE NAME: SetDeviceFont
;
; DESCRIPTIVE NAME:  Activate Font for code page for printer device.
;
; FUNCTION:   This routine will get the current code page from the
;           PDTA. w_16DevIOCtl is called to activate font for code
;           page for the printer device driver. If this fails, then
;           w_16DevIOCtl is called again with the hardware default
;           code page. If this fails, the routine simply returns.
;           (no error is reported).
;
; NOTES:  @@
;
; ENTRY POINT:              SetDeviceFont
;   LINKAGE:        Hybrid Call
;
; INPUT: (PARAMETERS)
;           (BX) -  HANDLE
;           (DS:SI) - pointer to filled in SFT
;
; EXIT-NORMAL:
;           none
; EXIT-ERROR:
;           none
;
; EFFECTS:
;
; INTERNAL REFERENCES:
;  ROUTINES: none
;
; EXTERNAL REFERENCES:
;  ROUTINES:
;                   w_16DevIOCtl - activate font for code page
;
; NOTES: This procedure reads a PTDA var, but since this is called by
;     DOSOPEN, it is protected by  Kernel sem.(enterfscrit).
;
;*********************** END OF SPECIFICATIONS ***********************
;*********************** START OF PSEUDO CODE *****************
;
; entry SetDeviceFont;
;
; get codepage_tag from PDTA;
; call w_16DevIOCtl - activate font for current code page;
; if carry set and return code == ERROR_CPS_CODE_PAGE_INVALID then
;    call w_16DevIOCtl - activate hardware default font/code page;
; endif;
; return;
;
;********************** END OF PSEUDO CODE *********************

      ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure SetDeviceFont,faronly

LocalVar RetryCount,BYTE
LocalVar ParmPacket,BYTE              ; Zero  for activate font w_16DevIOCtl
LocalVar DataPacket,DWORD             ; wordL=code page, wordH=font Id

      EnterProc
      pusha
      push    ds
      push    es

;     If activate font w_16DevIOCtl failed because the code page
;     requested was invalid, then try to activate font once more,
;     but use the default hardware code page (0). RetryCount
;     is used as a counter for this purpose.

      mov     RetryCount,1

;     Setup for Parm & Data Packet for DevIOCtl call

      mov     ParmPacket,0            ; reserved must be set to zero
      mov     ax,TASKAREA:codepage_tag; get codepage_tag defined in pdata.asm
      mov     DataPacketl,ax          ; value of code page to make active

sdf1: mov     DataPacketh,0           ; High word has font Id value (0)

;     (BX) already contains the JFN

      mov     al,IOC_PC               ; category for printer control
      mov     ah,IOLW_AF              ; function number for Activate Font
      xor     cx,cx                   ; parm.pkt len;0 is ok (unknown len)
      mov     si,cx                   ; data pkt.len; 0 is ok

      TaskContext ds
      lea     dx,DataPacket           ; DS:DX ->Data Packet

      TaskContext es                  ;
      lea     di,ParmPacket           ; ES:DI ->Parameter Packet

;     Now activate hardware default font/ code page

      push    bp                      ; w_16DevIOCtl clobbers it
      push    bx                      ; save handle, w_16DevIOCtl clobbers it
      CALLFAR w_16DevIOCtl
      pop     bx                      ; restore handle
      pop     bp                      ; restore bp for RetryCount
      jnc     short sdf2

      dec     RetryCount              ; if( RetryCount != zero)
      jnz     short sdf2                      ;  exit routine, no more retries

      and     ax,STECODE              ; mask off Device Driver Error

;     if code page requested is invalid, then exit

      cmp     ax,ERROR_CPS_CODE_PAGE_INVALID
      jne     short sdf2              ; no, something else went wrong, exit

      mov     DataPacketl,0           ; use hardware default (0) for retry
      jmp     short sdf1              ; retry Activate Font with defaults

sdf2: pop     es
      pop     ds
      popa
      LeaveProc
      ret

EndProc         SetDeviceFont


;***    w_copy - copy file or subdirectory
;
;       ENTRY   ds:dx -> source name
;               es:di -> target name
;               cx     = OpMode
;
;               OpMode  5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
;               bits    R R R R R R R R R R R R R F A E
;
;               E       Existing Targe File Disposition
;
;                       If E = 0; Do not copy the source file to
;                                 the target if the file name
;                                 already exists within the
;                                 target directory.
;
;                       If E = 1; Copy the source file to the
;                                 target even if the file name
;                                 already exists within the
;                                 target directory.
;
;               A       Append the source file to the target
;                       file's end of data.  This bit is ignored
;                       when copying a subdirectory, since the
;                       source subdirectory is added to the target
;                       subdirectory and the target is therefore
;                       not replaced.
;
;                       If A = 0; Replace the target file
;                                 with the source file.
;
;                       If A = 1; Append the source file to the
;                                 target file's end of data.
;
;               F       Fail the copy if the destination filesystem
;                       does not support EAs and the source has EAs.
;
;                       If F = 0; Discard the source's EAs and proceed
;                                 with the copy, unless the source has
;                                 NEED EAs.
;
;                       If F = 1; Fail the copy operation on the
;                                 file or dir that has EAs
;
;               R       Reserved and must be set to zero.
;
;       EXIT    if 'NC' - success
;               if 'C' - error
;                 ax = error code
;                       ERROR_FILENAME_EXCED_RANGE
;                       ERROR_PATH_NOT_FOUND
;                       ERROR_SHARING_VIOLATION
;                       ERROR_INSUFFICIENT_DISK_SPACE
;                       ERROR_ACCESS_DENIED
;                       ERROR_DIRECTORY
;                       ERROR_FILE_NOT_FOUND
;                       ERROR_NOT_DOS_DISK
;                       ERROR_DRIVE_LOCKED
;                       ERROR_SHARING_BUFFER_EXCEEDED
;                       ERROR_INVALID_PARAMETER
;                       ERROR_EAS_NOT_SUPPORTED
;                       ERROR_NEED_EAS_FOUND
;                       ERROR_NOT_SUPPORTED
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       NOTES:  This code is fully preemptible; the ds,es registers are
;       pointing to a kernel buffer (got through by GetTempBuffer by
;       SCI threadop getusrbufz3 at API entry point).
;
;       ALGORITHM
;
;       if (reserved bits in OpMode not zero)
;         return ERROR_INVALID_PARAMETER
;       TransPath(source name)
;       if (cMeta)
;         return ERROR_PATH_NOT_FOUND
;       TransPath(destination name)
;       if (cMeta)
;         return ERROR_PATH_NOT_FOUND
;       if (source FSC != destination FSC)
;         return ERROR_NOT_SAME_DEVICE

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
wco_OpReserved  = not 7                 ; reserved bits in OpMode

Procedure w_Copy,faronly
LocalVar wco_lpSrc,DWORD                ; points to source name
LocalVar wco_lpDest,DWORD               ; points to destination name
LocalVar wco_OpMode,WORD                ; mode
LocalVar wco_dcde,WORD                  ; curr_dir_end of destination name
LocalVar wco_dFSC,DWORD                 ; FSC of destination
LocalVar wco_typeDest,BYTE              ; destination path name type
LocalVar wco_renbuf,PMPATHLEN           ; renbuf
LocalVar wco_dummy1,WORD                ; KLUDGE: see gshare.asm
LocalVar wco_openbuf,PMPATHLEN          ; openbuf
LocalVar wco_dummy2,WORD                ; KLUDGE: see gshare.asm
LocalVar wco_dummycds,<SIZE curdir>

        EnterProc <ds,dx, es,di, cx>    ; save all parameters
        lea     si,wco_dummycds
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,si       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],si ; Initialize ptda_pCDS
        lea     si,wco_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,si            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],si ; Initialize ptda_pOpenBuf
        test    cx,wco_OpReserved       ; are reserved bits zero?
        LJNZ    wco50                   ; no, go error exit

; analyze destination name

        push    es
        pop     ds
        mov     si,di                   ; ds:si -> destination
        lea     di,wco_renbuf
        SaveReg <bp>
        CALLFAR TransPath
        RestoreReg <bp>
        LJC     wco40                   ; error, go error exit

                                        ;72417 moved from PTDA to TCB
        mov     bx,CurrTCB
        cmp     TASKAREA:[bx].TCBcMeta,-1  ; meta char in destination?
        LJNE    wco60                   ; yes, go error exit

; save destination related variables

        mov     ax,TASKAREA:[bx].TCBCurr_Dir_End
        cmp     ax,-1                   ; is Curr_Dir_End set?
        je      short wco5              ; no
        sub     ax,TASKAREA:[bx].TCBWFP_Start ; ax = iCurrDirEnd
wco5:   mov     wco_dcde,ax             ; save destination iCurrDirEnd or -1

        mov     ax,TASKAREA:[bx].TCBThisFSC.Offst       ; save FSC
        mov     wco_dFSC.Offst,ax
        mov     ax,TASKAREA:[bx].TCBThisFSC.Segmt
        mov     wco_dFSC.Segmt,ax
        mov     al,TASKAREA:[bx].TCBPathNameType
                                        ;72417 end
        mov     wco_typeDest,al

; analyze source name

        lds     si,wco_lpSrc            ; ds:si -> source
        lea     di,wco_openbuf          ; ss:di -> wco_openbuf
        SaveReg <bp>
        CALLFAR TransPath
        RestoreReg <bp>
        jc      short wco40             ; error, go error exit

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; source contains meta?
        UnSetBP_CurrTCB
                                        ;72417 end
        jne     short wco60             ; yes, go error exit

; if source and destination not managed by same file system, give up

        mov     bx,CurrTCB
        lds     si,TASKAREA:[bx].TCBThisFSC ; ds:si -> fsc
    assume ds:nothing
        cmp     si,wco_dFSC.Offst
        jne     short wco80
        mov     ax,ds
        cmp     ax,wco_dFSC.Segmt
        jne     short wco80

        CALLFAR PathIncVPBRef

; dispatch to proper file system workers

        cmp     si,FSC_LOCAL            ; what file system?
        jae     short wco100            ; FAT not supported yet

; call fsd worker
; bx = CurrTCB
; ds:si -> fsc

        push    wco_OpMode              ; push copy mode
        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]
        lea     ax,[di].cd_fsi          ; es:ax -> cd_fsi
        push    es                      ; push pointer to cd_fsi
        push    ax
        lea     ax,[di].cd_fsd          ; es:ax -> cd_fsd
        push    es                      ; push pointer to cd_fsd
        push    ax
        push    ss                      ; push pointer to WFP source name
                                        ;72417 moved from PTDA to TCB
        push    TASKAREA:[bx].TCBWFP_Start
        mov     ax,TASKAREA:[bx].TCBCurr_Dir_End ; ax = curr_dir_end
        cmp     ax,-1                   ; is curr_dir_end set?
        je      short wco10             ; no, pass in -1
        sub     ax,TASKAREA:[bx].TCBWFP_Start ; ax = iCurrDirEnd
                                        ;72417 end
wco10:  push    ax                      ; push iCurrDirEnd or -1
        lea     ax,wco_renbuf           ; ss:ax -> wco_renbuf
        push    ss                      ; push pointer to WFP destination name
        push    ax
        push    wco_dcde                ; push iCurrDirEnd for destination
        mov     al,wco_typeDest         ; al = destination path name type
        and     ax,PNType_NEWFILE       ; ax = 0 or 0x0040
        push    ax                      ; attr (file attributes for CREATE)
        call    ds:[si].FS_COPY
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0

wco30:  CALLFAR PathDecVPBRef

;### PTR B720732wco40:  mov     SS:[ptda_pOpenBuf],0    ; Clear ptr to openbuf
;### PTR B720732        mov     SS:[ptda_pCDS],0
wco40:  LeaveProc
        ret

wco50:  mov     ax,ERROR_INVALID_PARAMETER
        jmp     short wco90

wco60:  mov     ax,ERROR_PATH_NOT_FOUND
        jmp     short wco90


wco80:  mov     ax,ERROR_CANNOT_COPY
        jmp     short wco90

wco90:  stc
        jmp     short wco40

wco100: mov     ax,ERROR_CANNOT_COPY
        stc
        jmp     short wco30

EndProc w_Copy

FSSWAPCODE      ENDS

        END
