SCCSID  equ     "@(#)gshare.asm 6.10 92/02/20"
page ,132

Title   Sharer - MultiProcess File Share and Lock Support
NAME    Sharer

;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       Global          Local
;
;
;       MFTclose
;       AddShare
;       RemoveShare
;       LockMFTShar
;       LockMFT
;       UnlockMFT
;       ASC
;       CUC
;       CSI
;       RSC
;       CPS
;       cpj
;                       SFM
;       ShChk
;       ShSave
;       ShCloseFile
;       ShSU
;
;   HISTORY:
;
;   B001  - 2/13/92  CP20 B732823  Mark Rogalski
;           Need to look for identifyer flag in DOS PDBs to determine if
;           this is a VM Boot PDB. If so, do not scan JFT.
;
;   59570 - 1/18/93  Carlos A. Romero
;           Add code to handle case of DOS Compatability mode
;
;   68259 - 4/26/93  G.L.Fulk
;           Backing out the changes made above in 59570.
;
;   72417 - 11/27/95 - MLM
;                 moved from PTDA to TCB:
;                 PathNameType, WFP_Start
;

.xlist
.xcref
include fnofs.inc
include dosseg.inc
include defdbug.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include devhdr.inc
include dossymc.inc
include cpmfcb.inc
include curdir.inc
include dirent.inc
include dpl.inc
include error.inc
include filemode.inc
include filesys.inc
include mi.inc
include tkexport.inc
include mft.inc
include mult.inc
include oplock.inc
include pdb.inc
include protmode.inc
include selxport.inc
include sft.inc
include proc.inc
include osconst.inc
include pgexport.inc
include vmexport.inc
include vmowner.inc
include vpb.inc                                 ; 74088
ifdef SECURITY
include secure2.inc
endif
.cref
.list

        ReferGDT        GDT_VPB,WORD            ; 74088 VPB BMP Selector
        ReferGDT        GDT_RLR,WORD            ; #73832  Lock Record

        ReferTask       CurrTCB,WORD            ; current thread control block
        ReferTask       CurrentPDB,WORD         ; current PDB segment
        ReferTask       JFN_Length,WORD
        ReferTask       JFN_pTable,DWORD
                                                ;72417 removed PTDA entries now
                                                ;      in TCB
        ReferTask       Pid,WORD
        ReferTask       ProcType,BYTE

        ReferGlobal     MFT_hPTree,DWORD
        ReferGlobal     Serial,WORD

        ReferFlat       _pTCBCur,DWORD

        EXTRN32         _KSEMInit
        EXTRN32         _KSEMRelease
        EXTRN32         _KSEMRequestExclusive
        EXTRN32         _KSEMRequestShared
        EXTRNF32         GetMFTSem
        EXTRNF32         RMN
        EXTRNF32         MFT_Enter
        EXTRNF32         FreeMFTSem
        EXTRNF32         FNM
        EXTRN32         _PTDelete
        EXTRN32         _PTFind
        EXTRN32         _PTInsert
        EXTRN32         _PTFindFromPrefix
        EXTRN32         _VMAllocKHB
        EXTRN32         _VMFreeKHB
        EXTRN32         _VMGetTempBuffer
        EXTRN32         _VMFreeTempBuffer

        EXTRN16         CMKill
        EXTRN16         StrLen32
        EXTRN16         StrCpy
        EXTRN16         OpLockCleanUp

        EXTRNFAR        FreeTempBuffer
        EXTRNFAR        GDTFree
        EXTRNFAR        GetTempBuffer
        EXTRNFAR        ProcBlock
        EXTRNFAR        ProcRun
        EXTRNFAR        ScanJFN
        EXTRNFAR        SegMapVirt
        EXTRNFAR        Bmp_Free

        EXTRN           CheckSFTRef:NEAR
        EXTRN           ClrSFLock:NEAR
        EXTRN           FSD_Close:NEAR
        EXTRN           GetSFT:NEAR
        EXTRN           PathPref:NEAR
        EXTRN           PointComp:NEAR
        EXTRN           SFFromHandle:NEAR
        EXTRN           SFLinkFree:NEAR
        EXTRN           w_Close:NEAR

CpuMode 386


CpuMode reset

;
; If newfiles code is disabled, then TransPath already uppercased the string.
; So, we can optimize the code a little by not uppercasing the string when we
; copy it over into the temp buffer.
; When newfiles code is enabled, we need to call StrCpy since the string that
; we get will not be all uppercased.
;
ifdef DISABLENEWFILES
        EXTRN16         FStrCpy
        EXTRN           FStrCpy:NEAR
else
        EXTRN16         StrCpy
        EXTRN           StrCpy:NEAR
endif

.sall

        ModName gshare,FNO_GSHARE_ASM

        CpuMode 386


        FARCODE HIGH2
HIGH2CODE SEGMENT
    ASSUME  SS:TASKAREA


BREAK <Sharer - MultiProcess File Sharer>

;**     MSDOS MFT Functions
;
;       The Master File Table (MFT) associates the cannonicalized pathnames,
;       lock records and SFTs for all files open on this machine.
;
;       These functions are supplied to maintain the MFT and extract
;       information from it.  All MFT access should be via these routines so
;       that the MFT structure can remain flexible.
;



BREAK <MFTclose - Close out an MFT for given SFT>

;***    MFTclose
;
;       MFTclose(SFT)
;
;       MFTclose removes the SFT entry from the MFT structure.  If this was
;       the last SFT for the particular file the file's entry is also removed
;       from the MFT structure.
;
;       If the SFT indicates FCB, we do nothing special.  The SFT behaves
;           EXACTLY like a normal handle.
;
;       If the SFT indicates mode 70 then we do nothing special.  These are
;       normal HANDLES.
;
;       Note that we always care about the SFT refcount.  A refcount of 1
;       means that the SFT is going idle and that we need to remove the SFT
;       from the chain.
;
;       ENTRY   (ES:DI) points to an SFT structure
;               (DS) = TASKAREA
;               BX = real handle (JFN)
;                       -1 indicates no handle (used to pass on to CheckSFTRef)
;       EXIT    NONE
;       USES    ALL but DS, ES:DI
;
;    Change history:
;    ---------------
;    74088 Nizinski 09/28/93  If "EA DATA. SF" file is being closed,
;                             zero out the EA-related fields in VPB.
;                             It could be the case where the user has just
;                             erased the EA file, so we want to clean up the
;                             VPB to no longer reference the SFT.
;     73832  Virginia Romero  10/4/93
;                             If for whatever reason any record was not deleted
;                             (failed one of the tests in ClrSFLock) since were
;                             are ready to remove the name record go ahead and
;                             remove any lock records left.
;

Procedure   MFTclose,<near,faronly,far16>
    ASSUME DS:TASKAREA, ES:NOTHING
        SegAssert SS,<DS>

        DBPRT   1,8,<MFTClose by $x of $x:$x\n>,<Pid,ES,DI>

        cmp     es:[di].sf_MFT,SF_MFT_NULL
        jz      mcl10                   ; No entry for it, ignore (carry clear)
        call    CheckSFTRef             ; process has other handles to SFT?
        jnc     mcl10                   ; yes, go return

; we can't clear the file locks for this sft if this handle is dupped by
; this process. We can only clear the file locks if this handle is not referred
; to by this process.

        SaveReg <ebx,ds,es,di>

        SaveReg <es,di>
        call    ClrSFLock               ; clear SFT locks
        RestoreReg <di,es>
    ASSUME  DS:NOTHING
        mov     ax,es:[di].sf_ref_count ; (ax) = ref count
;
; We need to release information in one of two spots.  First, when the SFT has
; a ref count of 0.  Here, there are no more referents and, thus, no sharing
; record need be kept.  Second, the ref count may be -1 indicating that the
; SFT is being kept but that the sharing information is no longer needed.
; This occurs in creates of existing files, where we verify the allowed
; access, truncate the file and regain the access.  If the truncation
; generates an error, we do NOT want to have the access locked down.
;
        or      ax,ax
        jz      SHORT mcl85             ; ref count is 0 - don't dechain
        inc     ax                      ; -1 + 1 = 0.  Busy SFT.
        jnz     mcl9
;
; if SFT is going away, we need to see if the close was issued as cleanup on
; oplocked/batched file.  if so, mft_opbreak is set and the breaking thread
; plus anyone who requested the file during cleanup is sleeping on the SFT.
; we need to wake those threads up now because the cleanup has succeeded and
; no DosOpLockRelease will be issued.
;
mcl85:  mov     ebx,es:[di].sf_MFT
        FlatContext <ds>,ax             ; (ds:ebx) -> MFT
        test    [ebx].mft_opflags,mft_opbreak ; anyone waiting on SFT?
        jz      SHORT mcl87             ; no go remove SFT
        mov     ax,es
        mov     bx,di                   ; (AX:BX) = procblock key (SFT ptr)
        CALLFAR ProcRun                 ; yes, wake up processes waiting on SFT

mcl87:  ;Flat:ebx -> mft    ES:DI -> sft
        CALL32    GetMFTSem,nocheck

;  If the hVPB field in the SFT is not null, and the ptr to the EA file SFT
;  in the VPB points to this SFT, then clear the EA fields in the VPB.
;  This fields that get cleared are the sameas in UnHogEAFile.        74088
;  Begin block of code for 74088:
        cmp     es:[di].sf_fsi.sfi_hVPB,NULLVPB
        jz      short not_ea2
        SaveReg <ds,bx>
        mov     bx,es:[di].sf_fsi.sfi_hVPB
        push    GSEL GDT_VPB            ;ds:bx -> vpb    99569
        pop     ds                                      ;99569
        cmp     ds:[bx].vpb_signature,VPB_VALID         ;99569
        jne     short not_ea                            ;99569
;99569  VPBContext ds,bx                ; ds:bx -> vpb
        cmp     di,ds:[bx].vpb_fsd.vpdFAT_pEASFT.Offst
        jnz     short not_ea
        mov     ax,es
        cmp     ax,ds:[bx].vpb_fsd.vpdFAT_pEASFT.Segmt
        jnz     short not_ea
        xor     ax,ax
        mov     ds:[bx].vpb_fsd.vpdFAT_pEASFT.Offst,ax
        mov     ds:[bx].vpb_fsd.vpdFAT_pEASFT.Segmt,ax
        mov     ds:[bx].vpb_fsd.vpdFAT_eaflags,ax
        mov     ds:[bx].vpb_fsd.vpdFAT_eareaders,ax
not_ea:
        RestoreReg <bx,ds>
not_ea2:
;  end block of code for 74088

        call    RSC                     ; remove SFT from chain
        jnz     SHORT mcl88             ; not the last SFT for this name

; #73832
        mov     di,[ebx].mft_lptr
        or      di,di
        jz      rmn1a

        RLRCONTEXT <es>
        mov     si,[ebx].mft_lptr

recycle:
        mov     di, si
        mov     si,es:[di].rlr_next        ; next
        CALLFAR Bmp_Free                   ; recycle it
        or      si,si
        jnz     recycle
        mov     [ebx].mft_lptr,si
; #73832

rmn1a:
        FlatContext  <es>,ax
        CALL32  RMN                     ; remove name record

mcl88:  CALL32  FreeMFTSem,nocheck

mcl9:   RestoreReg <di,es,ds,ebx>

mcl10:  RETP

EndProc     MFTclose


;***    AddShare - allocate an SFT and add to sharing set
;
;       ENTER   WFP_START -> full path of name to add
;               TCBThishVPB set up
;               PathNameType set up
;               TCBUser_ID = 16-bit user id of issuer
;               TCBProc_ID = 16-bit process id of issuer
;               (AX) = sharing mode
;               (CX) = whether to break oplock/opbatch
;                  opnobreak == 0
;                  oplockbreak == 1
;                  opbatchbreak == 2
;
;       EXIT    'NC' - no error
;                  TCBThisSFT -> sft
;                  BX = SFN
;               'CY' - error
;                  (AX) = error code
;
;       USES    AX, BX, CX, DI, SI, DS, ES, flags
;
Procedure AddShare,hybrid
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
;
; get a free SFT and fill it in.  set up TCBThisSFT
;
        call    GetSFT                  ; (ES:DI) -> free sft, (BX) = SFN
        jc      SHORT as10              ; error, go return
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_NMPIPE OR PNType_UNC    ;
        UnSetBP_CurrTCB
                                        ;72417 end
        jnz     SHORT as30              ; no sharing for named pipes
        push    bx
        CALL32  MFT_Enter,nocheck       ; mft_enter loads ds,es itself
        pop     bx                      ; (BX) = SFN
        jc      SHORT as20              ; error, go free sft
as10:   ret

as20:   mov     di,CurrTCB
        les     di,TASKAREA:[di].TCBThisSFT ; (ES:DI) -> SFT
        mov     es:[di].sf_ref_count,0
        call    SFLinkFree              ; put SFT on free list, uses nothing
        stc
        jmp     SHORT as10

                                        ;72417 moved from PTDA to TCB
as30:   SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_UNC    ;
        UnSetBP_CurrTCB
                                        ;72417 end
        jnz     SHORT as40
        or      es:[di].sf_flags,SF_PIPE OR SF_NMPIPE ; mark this as a named pipe
        ret                                           ; and don't enter into sharing

as40:   or      es:[di].sf_flags2,sff2_UNC ; mark this as unc
        ret                             ; and don't enter into sharing

EndProc AddShare


;***    RemoveShare - remove sft from sharing and free it
;
;       ENTER   (ES:DI) -> SFT
;
;       EXIT    NONE
;
;       USES    BX, CX, DX, SI, DS
;
Procedure RemoveShare,hybrid
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        pushf
        push    ax
        TaskContext <ds>
        mov     es:[di].sf_ref_count,0  ; make sure that the sft gets released
        test    es:[di].sf_flags,SF_PIPE    ; should we remove from sharing
        jnz     SHORT rs10
        test    es:[di].sf_flags2,sff2_UNC  ; should we remove from sharing
        jnz     SHORT rs10
        push    bx
        mov     bx,-1           ; no handle for this call
        call    MFTclose        ; remove from sft from sharing
        pop     bx
rs10:   call    SFLinkFree      ; free sft
        pop     ax
        popf
        ret

EndProc RemoveShare



BREAK <ASC - Add SFT to Chain>

;***    ASC - Add SFT to Chain
;
;       ASC is called to add an SFT to the front of the chain.
;
;       ASC checks the file share mode bits on the other SFTs in the chain and
;       reports a conflict.  The new SFT is NOT ADDED in the case of
;       conflicts. The sfi_mode field for a file opened for EXEC is changed
;       to be open for READ.
;
;       ENTRY   (FLAT:EBX) -> MFT
;               (DS:SI)    -> SFT
;
;       EXIT    'C' clear if added
;                   (FLAT:EBX) -> MFT
;                   (DS:SI)    -> SFT
;               'C' set if conflict
;                 (AX) = Error code
;
;       USES    AX, BX, CX, DX, DI, SI, DS, ES, Flags
;
Procedure ASC,<NEAR,far16>
    ASSUME  ds:nothing, es:nothing

        cmp     [si].sf_MFT,SF_MFT_NULL
                                        ;72417 changed to LJNE
        LJNE    asc5                    ; already on chain - internal error

;       The SFT looks good... lets see if there are any use conflicts

        push    bp
        mov     bp,CurrTCB
        mov     ax,[bp].TCBUser_ID
        mov     [si].sf_fsi.sfi_UID,ax          ; do it before CUC (he checks UID)
        mov     ax,[bp].TCBProc_ID
        mov     [si].sf_fsi.sfi_PID,ax
        pop     bp
        mov     ax,CurrentPDB
        mov     [si].sf_fsi.sfi_PDB,ax
;
; If the file was opened for EXEC, we convert the open mode to open_for_read.
;
        mov     ax,[si].sf_fsi.sfi_mode ; (AX) = open mode of new sft
        and     al,open_access          ; Isolate access bits
        cmp     al,open_for_exec        ; Open_for_exec?
        jne     SHORT asc0              ; No, leave open mode alone
        and     [si].sf_fsi.sfi_mode,NOT open_access    ; Yes, map access to open_for_read

asc0:   test    ds:[si].sf_flags,DEVID_DEVICE
        jz      SHORT asc1              ; not a device, go check sharing
        test    ds:[si].sf_flags,SF_ISNET
                                        ; remote device ?
        jnz     SHORT asc1              ; don't look at device header and
                                        ; do share checking
        les     di,ds:[si].sf_devptr    ; (ES:DI) -> device header
        test    es:[di].SDevAtt,DEV_SHARE
        jz      SHORT asc2              ; device does not want share checking

asc1:
ifdef SECURITY
        mov     di,CurrTCB              ; @SEC add check for sharing skip ;@SEC
        movzx   di,ss:[di].TCBSecFlags                                    ;@SEC
        test    di,TCBSEC_NOCHECK       ; does security want filesharing disabled ;@SEC
        jnz     asc2                    ; skip sharing check              ;@SEC
endif


        call    CUC                     ; check use conflicts
        jc      SHORT asc4              ; use conflicts - forget it

;       (DS:SI)    -> SFT
;       (FLAT:EBX) -> MFT

asc2:   mov     [si].sf_MFT,ebx         ; make SFT point to MFT
        mov     cx,[si].sf_fsi.sfi_mode ; (CX) = open mode
        mov     dx,ds                   ; (DX:SI)  -> SFT
        FlatContext <ds>                ; (DS:EBX) -> MFT
ifdef FSSTRICT                          ; do sanity check
        cmp     ds:[ebx].mft_signature,MFT_PATTERN ; is ds:bx -> mft?
        jne     SHORT ascie1            ; no, go panic
endif

;
;   Not special file and no previous SFT found OR normal SFT.  We link it in
;   at the head of the list.
;
;   (DX:SI)  -> SFT
;   (DS:EBX) -> MFT
;
; no race condition filling in MFT first because SFT is always resident

        les     di,[ebx].mft_sptr       ; (ES:DI) -> first SFT in MFT chain
        mov     [ebx].mft_sptr.Offst,si ; link in this SFT
        mov     [ebx].mft_sptr.Segmt,dx ; link in this SFT
        mov     ds,dx
    ASSUME  ds:nothing
        mov     [si].sf_chain.Offst,di
        mov     [si].sf_chain.Segmt,es
asc3:   mov     ds,dx                   ; point back to SFT
        clc

asc4:   ret

;       the SFT is already in use... internal error

asc5:   InternalError   <ASC: SFT already in use>

ifdef FSSTRICT
ascie1: InternalError <ASC: bad MFT>
endif

EndProc   ASC


BREAK <CUC - check usage conflicts>

;***    Use conflict table
;
;       Algorithm:
;
;               if ((newmode == COMPAT) or (oldmode == COMPAT))
;                       and (user ID's match)
;                  then accept
;               else
;               for new and old mode, compute index of (SH*3)+ACC
;               shift right table[new_index] by old_index+2;
;                       'C' set if FAIL
;
;       The bit in the old_index position indicates the success or failure.  0
;       => allow access, 1 => fail access

CUCA:   DW      0ffffh                  ; Compat    Read
        DW      0ffffh                  ; Compat    Write
        DW      0ffffh                  ; Compat    Read/Write
        DW      0ffffh                  ; Deny R/W  Read
        DW      0ffffh                  ; Deny R/W  Write
        DW      0ffffh                  ; Deny R/W  Read/Write
        DW      0df7fh                  ; Deny W    Read
        DW      0dbffh                  ; Deny W    Write
        DW      0dfffh                  ; Deny W    Read/Write
        DW      0beffh                  ; Deny R    Read
        DW      0b7ffh                  ; Deny R    Write
        DW      0bfffh                  ; Deny R    Read/Write
        DW      01c7fh                  ; Deny None Read
        DW      003ffh                  ; Deny None Write
        DW      01fffh                  ; Deny None Read/Write

;                                            4443 3322 2111 000
;   Deny/Compat                         /    DDDD DDDD DDDD CCCx
;   DenyRead                           /        R RR    RRR
;   DenyWrite             1st Access =<             WW WWWW
;   AccessRead                         \     R RR  RR  RR R R R
;   AccessWrite                         \    WW W W WW  WW  WW
;   x                                        1111 1111 1111 1111
;   C  R    00                               1111 1111 1111 1111  ffff
;   C   W   01                               1111 1111 1111 1111  ffff
;   C  RW   02                               1111 1111 1111 1111  ffff

;   DRWR    10                               1111 1111 1111 1111  ffff
;   DRW W   11                               1111 1111 1111 1111  ffff
;   DRWRW   12                               1111 1111 1111 1111  ffff
;   D WR    20                               1101 1111 0111 1111  df7f

;   D W W   21                               1101 1011 1111 1111  dbff
;   D WRW   22                               1101 1111 1111 1111  dfff
;   DR R    30                               1011 1110 1111 1111  beff
;   DR  W   31                               1011 0111 1111 1111  b7ff

;   DR RW   32                               1011 1111 1111 1111  bfff
;   D  R    40                               0001 1100 0111 1111  1c7f
;   D   W   41                               0000 0011 1111 1111  03ff
;   D  RW   42                               0001 1111 1111 1111  1fff

;   In order to allow the greatest number of accesses, compatability read mode
;   is treated as deny-write read.  The other compatability modes are treated
;   as deny-both.

;***    CUC - check usage conflicts
;
;       CUC is called to see if a would-be open would generate a share
;       conflict with an existing open.  See CUCA for the algorithm and table
;       format.
;
;       ENTRY   (FLAT:EBX) -> MFT
;               (DS:SI)    -> SFT
;       EXIT    'C' clear if OK
;               'C' set if conflict
;                 (ax) = error code
;       USES    ALL but arguments (EBX, DS:SI)
;
;       PSEUDOCODE:
;           if (file not open by anyone)
;              return
;           if (new SFT is sharing_compat && read only file)
;              mode = sharing_deny_write | open_for_read
;           if (new SFT is open_for_exec)
;              access mode = open_for_read
;           CSI     ; compute share index of new SFT
;           repeat {
;              if (next SFT is sharing_compat) {
;                 if (new SFT is sharing_compat)
;                    if (both SFT's opened by same process) {
;                       next SFT = next SFT ->next
;                       goto repeat
;                    }
;                    if (read only file)
;                       mode = sharing_deny_write | open_for_read
;              }
;              CSI   ; compute share index of new SFT
;              test for conflict
;              if (conflict)
;                 return(sharing_error)
;              next SFT = next SFT ->next
;           until } (end of SFT chain)
;

Procedure   CUC,NEAR
        ASSUME  ds:nothing, es:nothing
        FlatContext es                  ; (ES:EBX) -> MFT
ifdef FSSTRICT                          ; do sanity check
        cmp     es:[ebx].mft_signature,MFT_PATTERN ; es:ebx -> mft?
        jne     SHORT cucie             ; no, go panic
endif
        LES     DI,es:[ebx].mft_sptr    ; (ES:DI) -> first SFT guy
        ASSUME  es:nothing
        mov     ax,es
        or      ax,ax                   ; file open by anyone?
        jz      SHORT cucx              ; no, go return

        mov     ax,ds:[si].sf_fsi.sfi_mode    ; (AX) = open mode of new sft
        mov     ch,open_sharing_mode    ; assume not sharing_compat
        mov     cl,es:[di].sf_fsi.sfi_DOSattr ; (CL) = file attributes
.errnz  sharing_compat
        test    ax,open_sharing_mode    ; sharing compat open?
        jnz     SHORT cuc0              ; no, leave open mode alone

        xor     ch,ch                   ; sharing_compat
        test    cl,attr_read_only       ; read only file?
        jz      SHORT cuc0              ; no, leave open mode alone

        mov     al,sharing_deny_write + open_for_read
cuc0:
        push    cx
        call    csi                     ; (AX) = share index
        pop     cx
        add     ax,ax                   ; *2 for word index
        xchg    ax,si                   ; (si) = share table index
        mov     dx,WORD PTR CUCA[si]    ; (dx) = share mask
        mov     si,ax                   ; (DS:SI) -> new sft
        ASSUME  ds:nothing

;       ready to do access compares.
;
;       (ES:DI) = address of next SFT
;       (DS:SI) = address of new  SFT
;       (DX) = share word from CUCA
;       (FLAT:EBX) -> MFT
;       (ch) = 0 if new SFT is compatibility mode, else open_sharing_mode
;       (cl) = file attributes

cuc1:
        mov     ax,es:[di].sf_fsi.sfi_mode ; (AX) = open mode
.errnz  sharing_compat
        test    ax,open_sharing_mode ; sharing compat open?
        jnz     SHORT cuc3              ; no, leave open mode alone
        or      ch,ch                   ; new SFT sharing compat?
        jnz     SHORT cuc2              ; no, do sharing check

;       Both the old and the new guy are sharing_compat mode. If the UIDs match,
;       step onward.  If they don't match do normal share check.

        mov     bp,es:[di].sf_fsi.sfi_UID
        cmp     bp,[si].sf_fsi.sfi_UID
        jz      SHORT cuc20             ; equal => next SFT to check

cuc2:
        test    cl,attr_read_only       ; read only file?
        jz      SHORT cuc3                      ; no, leave open mode alone
        mov     al,sharing_deny_write + open_for_read

cuc3:
        push    cx
        call    csi                     ; compute the share index
        inc     ax
        inc     ax
        xchg    al,cl                   ; (cl) = shift count
        mov     ax,dx
        sar     ax,cl                   ; select the bit
        pop     cx
        jc      SHORT cuc8              ; a conflict!

cuc20:
        LES     DI,es:[di].sf_chain        ; (ES:DI) -> address of next SFT
        mov     ax,es
        or      ax,di                   ; file open by anyone?
        jz      SHORT cucx              ; at end of chain, no problems
        jmp     SHORT cuc1              ; chain to next SFT and try again

;       Have a share conflict

cuc8:   mov     ax,ERROR_SHARING_VIOLATION  ; assume share conflict
        stc

;       done with compare.  Restore regs and return
;
;       'C' set as appropriate
;       (DS:SI) = new SFT address
;       (AX) set as appropriate
;       (FLAT:EBX) -> MFT

cucx:   ret

ifdef FSSTRICT
cucie:  InternalError <CUC: bad mft>
endif
EndProc CUC


BREAK <csi - compute share index>

;***    csi - compute share index
;
;
;       If the mode byte has a leading 7 then it is interpreted as a 0
;       csi turns a mode byte into an index from 0 to 14:
;
;               (share index)*3 + (access index)
;
;       ENTRY   (al) = mode byte
;       EXIT    (ax) = index
;       USES    AX, CL

Procedure   CSI,NEAR
        ASSUME  ds:nothing, es:nothing
        mov     ah,al
        and     ah,open_access          ; (ah) = access bits
        and     al,open_sharing_mode    ; (al) = share bites

        shr     al,3
        mov     cl,al                   ; (cl) = SHVAL*2
        shr     al,1
        add     al,cl                   ; (al) = SHVAL*3
        add     al,ah                   ; (al) = SH*3 + ACC
        sub     ah,ah
        ret

EndProc CSI








BREAK <RSC - Remove SFT from SFT chain>

;***    RSC - Remove SFT from chain of SFT
;
;       RSC removes a given SFT from its MFT chain.  The caller must insure
;       that any locks have been cleared and that the SFT is indeed free.
;       The sf_MFT field is set to SF_MFT_NULL to indicate that this SFT is
;       no longer chained.
;
;       NOTE - RSC does NOT remove the MFT entry if this was the last SFT on
;               it.  The caller must check for this and remove it, if
;               necessary.
;
;       ENTRY   (ES:DI) = SFT address
;       EXIT    (DS:EBX) -> MFT entry for this SFT
;               'Z' set if this is the last SFT
;       USES    ALL

Procedure   RSC,HYBRID
    ASSUME  ds:nothing, es:nothing, ss:TASKAREA

        mov     ax,es                   ; easy spot for compare
        shl     eax,16
        mov     ax,di                   ; (eax) = es:di -> SFT

        mov     ebx,es:[di].sf_MFT      ; (FLAT:ebx) -> MFT
        cmp     ebx,SF_MFT_NULL         ; shared?
        jz      SHORT rscx              ; no, don't do anything (from loader)

        FlatContext ds                  ; (ds:ebx) -> MFT
        cmp     [ebx].mft_sptr,eax      ; is sft first in chain?
        jne     SHORT rsc0              ;  no, keep going
        ;                               ;  yes, remove sft from head of chain
        mov     eax,es:[di].sf_chain    ; (eax) -> next sft in chain
        mov     [ebx].mft_sptr,eax      ; mft_sptr -> next sft in chain
        jmp     SHORT rsc90

rsc0:   lds     si,[ebx].mft_sptr       ; (ds:si) -> first sft in chain
    ASSUME  ds:nothing

rsc1:   cmp     [si].sf_chain,eax       ; is this the right sft?
        jz      SHORT rsc2              ;  yes, go remove it

        lds     si,[si].sf_chain        ; (ds:si) -> next sft in chain
    ASSUME  ds:nothing
        mov     bx,ds
        or      bx,si
        jnz     SHORT rsc1              ; not at end of chain, continue
        jmp     SHORT rscier            ; end of chain,  internal error

;   (ES:DI) is SFT
;   (DS:SI) is prev SFT link

rsc2:   mov     eax,es:[di].sf_chain
        mov     ds:[si].sf_chain,eax

rsc90:  FlatContext ds
        mov     ebx,SF_MFT_NULL
        xchg    ebx,es:[di].sf_MFT      ; (ds:ebx) -> MFT entry
                                        ;    and NULL sf_mft pointer (show free)
        cmp     [ebx].mft_sptr.Segmt,0  ; set z flag if no more SFT

rscx:   ret

rscier: InternalError   <RSC: SFT not in SFT list>
EndProc RSC


Break   <CPS - close process SFT>

;***    CPS - close process SFT.
;
;       During some requests, it is necessary to close a file
;       given ONLY the SFT.  This necessitates walking all JFN
;       tables looking for the SFN.
;
;       NOTE    With the addition of the LAN Manager support (redirector),
;               Compatibility-Mode OPENs are now allowed for PROT-mode
;               processes (i.e., the server).  Hence, we must also handle
;               that case.
;
;       ENTRY   ThisSFT points to SFT of interest
;               CLD
;       EXIT    Handle is closed on all references
;       USES    flags
;
;       ALGORITHM
;
;       sfn = selfsfn
;       if (prot mode process)
;         zap sfn in JFN_pTable
;       else if (fcb sft)
;         return
;       else {
;         fake sfn = ScanJFN(sfn)
;         pdb = currentPDB
;         do {
;           call cpj(pdb)  /* zap fake sfn in current pdb */
;           pdb = parent pdb of pdb
;         } until (pdb = parent pdb)
;       }

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

        LocalVar        cps_gdtPDB,WORD ; GDT selector to PDB
        LocalVar        cps_sfnFake,byte
        LocalVar        cps_segPDB,WORD ; segment of PDB in use

        PMONLY

        EnterProc       <0>             ; gdtPDB = 0
        pusha
        SaveReg <DS,ES>

        mov     si,CurrTCB
        lds     si,TASKAREA:[si].TCBThisSFT ; (DS:SI) -> SFT entry
        mov     ax,ds:[si].sf_fsi.sfi_selfsfn ; (ax) = sfn

        cmp     ProcType,PT_REALMODE    ; 3xbox?
        je      SHORT cps2              ;   YES, zap JFNs in all PDBs

; Zap SFN in JFN table of PROT-mode processes

        les     di,JFN_pTable           ; (es:di) = JFN table
        mov     cx,JFN_Length           ; (cx) = size of JFN table

cps1:   repne   scasw                   ; SFN in JFN table?
        jne     SHORT cpsx              ;   NO, go exit
        mov     word ptr es:[di-2],SFN_NULL ;   YES, zap it
        jcxz    SHORT cpsx              ; Found SFN in last JFN entry, go exit
        jmp     SHORT cps1              ; Continue scanning JFN table

; Zap SFN in JFN tables of all PDBs in PDB chain.
; convert SFN into fake SFN
; ax = sfn
; ds:si -> sft

cps2:   test    ds:[si].sf_flags,SF_FCB ; FCB SFT?
        jnz     SHORT cpsx              ;   yes, go exit

        CALLFAR ScanJFN                 ; bx = fake SFN, es:di->handle slot
        jc      SHORT cpsx              ; SFN not found, go exit

        mov     word ptr es:[di],SFN_NULL ; zap real handle
        mov     cps_sfnFake,bl          ; save away fake SFN

;*  Scan the chain of PDBs from currentPDB up to command.com, and zap the
;   fake handles that contain the fake SFN.
;
;   cps_sfnFake = fake SFN

        mov     ax,CurrentPDB           ; ax = current pdb paragraph

; ax = paragraph of PDB

cps3:   mov     cps_segPDB,ax           ; save paragraph of PDB
        mov     bx,cps_gdtPDB           ; reuse GDT selector from last time
        mov     cx,size Process_data_block ; cx = size
        CALLFAR SegMapVirt              ; ax = selector to PDB
        mov     cps_gdtPDB,ax           ; save selector to PDB

        mov     ds,ax                   ; ds:0 -> PDB
        xor     si,si                   ; ds:si -> PDB

        cmp     dword ptr ds:[si+4],045434341h     ;BN001 look for VM Boot flag
        je      cps4                               ;BN001 jump around if found

        mov     dl,cps_sfnFake          ; dl = fake SFN
        call    cpj                     ; zap handles in this PDB

; get parent's PDB

        mov     bx,cps_segPDB           ; (BX) = old PDB
        mov     ds,cps_gdtPDB           ; (DS) = PDB alias selector
        mov     ax,DS:[PDB_Parent_PID]  ; (AX) = parent PDB
        cmp     ax,bx                   ; Is this COMMAND.COM?
        je      SHORT cps4              ;   yes, go exit

        or      ax,ax                   ; Is there a parent?
        jnz     SHORT cps3              ;   yes, go zap parent's handle

cps4:   mov     ax,cps_gdtPDB           ; ax = GDT allocated by SegMapVirt
        CALLFAR GDTFree                 ; free it

cpsx:   RestoreReg  <ES,DS>
        popa
        LeaveProc
        ret

EndProc CPS


;***    cpj - close PDB process' JFN
;
;       Scan fake JFN table for SFN and put in -1 if found
;
;       ENTRY   (DS:SI) -> PDB
;               (DL)    = fake SFN
;               CLD
;
;       EXIT    None
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;

    ASSUME  ds:nothing,es:nothing

Procedure   cpj,NEAR
    ASSUME  ds:nothing, es:nothing, ss:TASKAREA
        PMONLY

; get protect mode mapping of PDB_JFN_Pointer
        mov     cx,ds:[SI].PDB_JFN_Length ; (CX) = number of handles
        mov     di,ds:[SI].PDB_JFN_Pointer.Offst ; (ES:DI) = JFN table ptr
        SaveReg <CX,DX,DI>
        add     cx,di                   ; (CX) = limit of JFN table segment
        mov     ax,ds:[SI].PDB_JFN_Pointer.Segmt ; (AX) = JFN table seg
        xor     bx,bx                   ; bx = allocate GDT selector
        CALLFAR SegMapVirt              ; ax = selector of JFN table seg
        RestoreReg <DI,DX,CX>
        mov     es,ax                   ; (ES:DI) = JFN table
                                        ; (CX) = size of JFN table
                                        ; (DL) = SFN to search for

        mov     al,dl                   ; (AL) = SFN to search for

cpj1:   repne   scasb                   ; Search for matching SFN
        jnz     SHORT cpjx              ; none found

        mov     byte ptr es:[di-1],SFN_NULL ; free this handle
        jcxz    SHORT cpjx              ; Found one in last JFN entry
        jmp     SHORT cpj1              ; keep looking

cpjx:   mov     ax,es                   ; ax = GDT selector
        CALLFAR GDTFree
        ret

EndProc cpj


Break   <SFM - convert an SFT pointer into a serial number>

;   SFM - convert a pointer to a SFT entry into the serial number for the
;   associated MFT entry.
;   We keep these around to see if a FCB really points to the correct SFT.
;
;   Inputs:     es:di -> SFT
;   Outputs:    bx = MFT serial number
;   Registers modified: none

Procedure   SFM,NEAR
    ASSUME  DS:NOTHING,ES:NOTHING
        SaveReg <eax,ds>
        FlatContext ds
        mov     eax,es:[di].sf_MFT      ; (ds:eax) -> MFT entry
        mov     bx,[eax].mft_serl
        RestoreReg <ds,eax>
    ASSUME      DS:NOTHING
        ret
EndProc SFM


Break   <ShChk - check a fcb for share related information>

;   ShChk - checks the reserved field contents of an FCB with a SFT to see
;   if they represent the same file.  The open ref count must be > 0.
;
;   Inputs:     DS:SI point to FCB
;               ES:DI point to SFT
;   Outputs:    Carry Set if contents do not match
;               Carry clear if contents match
;                   BX has first cluster
;   Registers modified: none

Procedure ShChk,FARONLY
    ASSUME  DS:NOTHING,ES:NOTHING
        CMP     ES:[DI].sf_ref_count,0
        JZ      SHORT BadSFT

        call    SFM                     ; (bx) = MFT serial number

        CMP     BX,[SI].fcb_l_mfs
        JNZ     SHORT BadSFT
        MOV     BX,[SI].fcb_l_firclus
        ret
BadSFT: stc
        ret
EndProc ShChk


Break   <ShSave - save information from SFT into an FCB>

;   ShSave - copy information into the reserved area of an FCB from a SFT.
;   This is so that we can later match the SFT with the FCB.
;
;   Inputs:     DS:SI point to FCB
;               ES:DI point to SFT
;   Outputs:    FCB reserved field is filled in
;               BL = FCBSHARE
;   Registers modified: AX,BX

Procedure ShSave,FARONLY
    ASSUME  DS:NOTHING,ES:NOTHING
        mov     AL,ES:[DI].sf_fsi.sfi_DOSattr     ; move attribute (for reopen)
        mov     [SI].fcb_l_attr,AL
        mov     AX,ES:[DI].sf_fsd.sfdFAT_firFILEclus    ; get first cluster
        mov     [SI].fcb_l_firclus,AX

        call    SFM                     ; (bx) = MFT serial number

        mov     [SI].fcb_l_mfs,BX
        mov     BL,FCBSHARE
        ret
EndProc ShSave


Break   <ShCloseFile - close a particular file for a particular UID/PID>

;***    ShCloseFile - Close a particular file.
;
;       Compatability mode programs will often delete files that they had
;       open.  This was perfectly valid in the 2.0 days, but this presents
;       a reliability problem in the network based operating environment.
;       As a result, both RENAME and DELETE will call us to see if the file is
;       open by us only.  If it is not open or is open by us only, we close it.
;       Note that we will ONLY close compatability SFTs.
;       Otherwise, we signal and error.
;
;   Inputs:     TASKAREA:WFP_Start -> file name
;               DS is TASKAREA
;               TCBThishVPB set up
;   Outputs:    nothing relevant.
;   Registers modified: None.
;

Procedure   ShCloseFile
    ASSUME DS:TASKAREA, ES:NOTHING

    LocalVar    shcl_TempBufPtr,DWORD
    LocalVar    shcl_TempBufFlat,DWORD  ; 32 bit flat pointer to temp buffer

        SegAssert SS,<DS>               ; Ensure DS = TaskArea
        SaveReg <EAX,EBX,CX,DX,ESI,EDI,BP,DS,ES>

        EnterProc               ; Save pointer to buffer

        SaveReg <ds,es>
        FlatContext <ds,es>,di
        lea     edi,shcl_TempBufFlat
        SSToDS  edi                     ; Flat:32 flavor
        lea     esi,shcl_TempBufPtr
        SSToDS  esi                     ; 16:16 flavor
        CCALL32 _VMGetTempBuffer <edi,esi>

        RestoreReg <es,ds>
        ASSUME DS:TASKAREA, ES:NOTHING

        les     di,shcl_TempBufPtr      ; ES:DI -> Temp Buffer
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start ; DS:SI -> Source string
                                        ;72417 end
        call    StrCpy                  ; copy to buffer and uppercase

ShCl:

        SaveReg  <es>
        FlatContext <ds,es>,ax

        mov     esi,shcl_TempBufFlat
        XOR     AL,AL
        CALL32  FNM,,nofar              ; Attempt to find name in list
        RestoreReg <ES>                 ; DS is FLAT after call to FNM
        ASSUME DS:NOTHING,  ES:NOTHING
        JC      SHORT ShCloseDone       ; can't find, signal success
;
; We have found a file in the MFT.  Walk the open SFT list to find the SFTs
; for the current UID/PID.
;
; DS:EBX -> MFT entry that matched  DS is FLAT
;
    ASSUME DS:NOTHING
        MOV     CX,DS
        LDS     SI,[EBX].mft_sptr       ; (ds:si) -> first sft in chain
        MOV     AX,DS                   ; Check to see if there is an sft
        OR      AX,SI
        JZ      ShCloseDone             ; no, go signal sucess

ShClCheck:
        MOV     AX,CurrentPDB
        CMP     [SI].sf_fsi.sfi_PDB,AX
        JNZ     SHORT ShCloseDone

        push    bp
        mov     bp,CurrTCB
        mov     ax,[bp].TCBProc_ID
        pop     bp
        CMP     [SI].sf_fsi.sfi_PID,AX
        JNZ     SHORT ShCloseDone

        push    bp
        mov     bp,CurrTCB
        mov     ax,[bp].TCBUser_ID
        pop     bp
        CMP     [SI].sf_fsi.sfi_UID,AX
        JNZ     SHORT ShCloseDone

        MOV     AX,[SI].sf_fsi.sfi_mode
        AND     AX,open_sharing_mode
        CMP     AX,sharing_compat
        jnz     SHORT ShCloseDone
ShClNext:
        LDS     SI,[SI].sf_chain        ; (ds:si) -> next sft in chain
        mov     ax,ds
        OR      ax,SI
        JNZ     SHORT ShClCheck
        MOV     DS,CX                   ; (ds:ebx) -> MFT entry
        LDS     SI,[EBX].mft_sptr       ; (ds:si)  -> first sft in chain
;
; Everything matches.  Set up ThisSFT and walk the chain from the beginning.
;
        push    bp
        mov     bp,CurrTCB
        mov     TASKAREA:[bp].TCBThisSFT.Offst,si
        mov     TASKAREA:[bp].TCBThisSFT.Segmt,ds
        pop     bp
;
; Close all handles for this SFT
;
        call    CPS
;
; Close the SFT itself.
;
        TaskContext DS
        mov     bx,-1                   ; we don't bother about the real
                                        ; handle
        call    FSD_Close
        call    SFLinkFree                      ; put SFT on free list
;
; The SFT may be free and we have no idea where the next is.  Go and loop all
; over.
;
        JMP     ShCl
;
; There are no more SFTs to close.  Say bye bye
;
ShCloseDone:

        FlatContext <es,ds>,di
        mov     esi,shcl_TempBufFlat        ; Flat:ESI -> temp buffer
        CCALL32 _VMFreeTempBuffer <esi>     ; Free the temp buffer
        LeaveProc
        RestoreReg  <ES,DS,BP,EDI,ESI,DX,CX,EBX,EAX>
        ret

EndProc ShCloseFile


Break   <ShSU - update all SFTs for a specified change>

;***    ShSU - Update the SFTs for a file
;
;   ShSU - SFT update for a file.  In a shared environment, we want to
;   propogate the SFT changes for a particular file to all other SFTs for that
;   file.  The types of things we propogate are:
;
;       Time of last write - we only do this on CLOSE and on FILETIMES.
;
;       Size and allocation information - we do this ONLY when we change
;           sf_fsi.sfi_size.
;
;   We achieve this by walking the linked list of SFTs for the file and do
;   the following:
;
;       If we are updating the time then
;           move the new time from the old into the new SFT
;       else
;       if we are updating the size/allocation info then
;           move in new size
;           move in new firclus
;           if we are shrinking the file then
;               set cluspos to 0
;               set lstclus to firclus
;       else
;       if we are entering a file for the first time then
;           if there is another instance of the file in the MFT then
;               if this is for the FAT file system then
;                   copy file size and allocation info from that SFT into
;                   the current SFT (copy sfdFAT fields too)
;               else
;                   copy fields from sf_fsi into the current SFT
;                   (do not copy sf_fsd fields)
;           else
;               nothing
;       else
;       if we are only updating the size of the file
;           copy the new size
;       else
;           nothing
;
;   ENTRY       ES:DI  has SFT that was just modified.
;               AX = 0 for updating of time/attr from ES:DI into old SFTs
;       FAT FS  AX = 1 for updating of size/allocation for growth from ES:DI
;       FAT FS  AX = 2 for updating of size/allocation for shrink from ES:DI
;       FAT FS  AX = 3 for new instance copy into ES:DI
;               AX = 4 update sf_fsi fields for new instance copy into ES:DI
;               AX = 5 update only the sfi_size field in the SFTs
;
;   EXIT        All relevant SFTs are updated.
;
;   NOTES       Sub-functions 1, 2 and 3 are specific to the FAT file system
;               since they modify sfdFAT fields in the SFT. They * MUST *
;               not be used for anything but the FAT file system, otherwise
;               the data that an FSD may have stored in the SFT may get
;               destroyed.
;               Sub-function 4 should be used for an FSD since we copy
;               only the file system independent fields from an existing
;               SFT into the new one. This is because we do not know what
;               an FSD may have stored in the sf_fsd area (equivalent to 3
;               but for an FSD)
;               Sub-function 5 should be used to just update the sfi_size
;               field in all the SFTs (equivalent to 2, but for an FSD)
;
;               We SKIP SFTs with RefCount 0 for operations 3 and 4, since
;               such sfts are assumed to be not yet set up fully, and so
;               they cannot be used as the source.
;
;   USES        EAX, BX, CX, DX, Flags
;
Procedure   ShSU,hybrid
    ASSUME  DS:NOTHING,ES:NOTHING
;
; Do nothing for device or network
;
        test    es:[di].sf_flags,SF_ISNET + DEVID_DEVICE
        jnz     shsux
        mov     ebx,es:[di].sf_MFT      ; (FLAT:ebx) -> mft
        cmp     ebx,SF_MFT_NULL
        LJZ     shsux
;
; Walk the SFT chain for this file and skip the current SFT (ES:DI)
;
        mov     cx,ax
        SaveReg <DS,SI>
        FlatContext ds,ax               ; (ds:ebx) -> mft
        lds     si,[ebx].mft_sptr       ; (ds:si)  -> first sft in chain
    ASSUME      DS:NOTHING

shsu10: mov     bx,ds
        or      bx,si
        LJZ     shsu90
        call    PointComp               ; pointers different?
        LJZ     shsu80                  ; no, skip this SFT (ourself)
        cmp     [si].sf_ref_count,0     ; is this sft not yet updated ?
        jnz     SHORT shsu15
        cmp     cx,5
        LJZ     shsu75                  ;
        cmp     cx,3                    ;
        LJAE    shsu80                  ; try previous sfts in chain
;
; CX = 0 for updating of time
; CX = 1 for updating of size/allocation for growth
; CX = 2 for updating of size/allocation for shrink
; CX = 3 for new instance copy for FAT file system
; CX = 4 for new instance copy for FSD
; CX = 5 for updating sfi_size only
;
shsu15: or      cx,cx
        jnz     SHORT shsu40
;
; Copy time from ES:DI into DS:SI
;
        xor     bx,bx
        mov     bl,es:[di].sf_fsi.sfi_DOSattr
        mov     [si].sf_fsi.sfi_DOSattr,bl

        test    es:[di].sf_fsi.sfi_tstamp,ST_PCREAT ; propagate ctime?
        jz      SHORT shsu20                    ; no, skip ctime

        mov     bx,es:[di].sf_fsi.sfi_ctime
        mov     [si].sf_fsi.sfi_ctime,bx
        mov     bx,es:[di].sf_fsi.sfi_cdate
        mov     [si].sf_fsi.sfi_cdate,bx

shsu20: test    es:[di].sf_fsi.sfi_tstamp,ST_PWRITE ; propagate mtime?
        jz      SHORT shsu30                    ; no, skip mtime

        mov     bx,es:[di].sf_fsi.sfi_mtime
        mov     [si].sf_fsi.sfi_mtime,bx
        mov     bx,es:[di].sf_fsi.sfi_mdate
        mov     [si].sf_fsi.sfi_mdate,bx

shsu30: test    es:[di].sf_fsi.sfi_tstamp,ST_PREAD ; propagate atime?
        LJZ     shsu80                  ; no, skip atime

        mov     bx,es:[di].sf_fsi.sfi_atime
        mov     [si].sf_fsi.sfi_atime,bx
        mov     bx,es:[di].sf_fsi.sfi_adate
        mov     [si].sf_fsi.sfi_adate,bx
        jmp     SHORT shsu80
;
; CX = 1 for updating of size/allocation for growth (FAT file system)
; CX = 2 for updating of size/allocation for shrink (FAT file system)
; CX = 3 for new instance copy for FAT file system
; CX = 4 for new instance copy for FSD
; CX = 5 for updating sfi_size only
;

shsu40: cmp     cx,2
        ja      SHORT shsu70
;
; We always copy size,firFILEclus and EAHandle
;
        mov     eax,es:[di].sf_fsi.sfi_size
        mov     [si].sf_fsi.sfi_size,eax
        mov     bx,es:[di].sf_fsd.sfdFAT_EAHandle
        mov     [si].sf_fsd.sfdFAT_EAHandle,bx
        mov     bx,es:[di].sf_fsd.sfdFAT_firFILEclus
        mov     [si].sf_fsd.sfdFAT_firFILEclus,bx
        jnz     SHORT shsu60    ; updating for growth, check for more to do
;
; Shrink the file, move in new cluspos and lstclus
;

shsu50: mov     [si].sf_fsd.sfdFAT_cluspos,0       ; retrace from start
        mov     [si].sf_fsd.sfdFAT_lstclus,bx      ; ditto
        jmp     SHORT shsu80

shsu60: cmp     [si].sf_fsd.sfdFAT_lstclus,0  ; Is lstclus UN-set from a create?
        jz      SHORT shsu50        ; Yes it is! set lstclus and cluspos too
        jmp     SHORT shsu80        ; Leave lstclus and cluspos where they are

;
; CX = 3 for new instance copy for FAT file system
; CX = 4 for new instance copy for FSD
; CX = 5 for updating sfi_size only
;
shsu70: cmp     cx,5
        je      SHORT shsu75                ; Just update sfi_size field
;
; Copy all relevant info from sf_fsi at DS:SI into ES:DI as ES:DI is a
; new SFT.
;
        mov     bx,[si].sf_fsi.sfi_ctime
        mov     es:[di].sf_fsi.sfi_ctime,bx
        mov     bx,[si].sf_fsi.sfi_cdate
        mov     es:[di].sf_fsi.sfi_cdate,bx
        mov     eax,[si].sf_fsi.sfi_size
        mov     es:[di].sf_fsi.sfi_size,eax
        cmp     cx,4
        je      SHORT shsu90        ; This is not for the FAT file system
;
; Copy some sfdFAT fields over as well
;
        mov     bx,[si].sf_fsd.sfdFAT_EAHandle
        mov     es:[di].sf_fsd.sfdFAT_EAHandle,bx
        mov     bx,[si].sf_fsd.sfdFAT_firFILEclus
        mov     es:[di].sf_fsd.sfdFAT_firFILEclus,bx
        mov     es:[di].sf_fsd.sfdFAT_cluspos,0         ; retrace from start
        mov     es:[di].sf_fsd.sfdFAT_lstclus,bx        ; ditto
        jmp     SHORT shsu90                            ; All done

;
; Just copy the sfi_size field
;
shsu75: mov     eax,es:[di].sf_fsi.sfi_size
        mov     [si].sf_fsi.sfi_size,eax

;
; Link to next SFT
;
shsu80: lds     si,[si].sf_chain
        jmp     shsu10
;
; All Done
;
shsu90: RestoreReg  <SI,DS>
shsux:  ret
EndProc ShSU




HIGH2CODE ENDS


        FARCODE HIGH4
HIGH4CODE SEGMENT
    ASSUME  SS:TASKAREA

BREAK   <LockMFT/LockMFTShar/UnlockMFT - control access to an MFT>

;***    LockMFT, LockMFTShar, and UnlockMFT
;
;       We must allow only one edit (write, truncate, etc) OR n reads
;       to access a given file at any one time.  We have two kinds of
;       locks, exclusive and shared.  Edits (eg writes) use exclusive
;       locks, while reads use shared locks.  This allows multiple reads
;       to the same file to execute in parallel.
;
;       We use multiple reader/single write kernel semaphores.

;***    LockMFT/LockMFTShar - control access to an MFT, and thus a file.
;
;       LockMFT locks an MFT, for either an edit (exclusive) or read (shared)
;       operation.  This is done to get consistent results when several
;       processes are modifying the file.  It also enforces one of the
;       basic assumptions that the buffer cache code relies on.
;
;       Pseudocode:
;
;               if (share)
;                   KSEMRequestShared(HKSEM,timeout);
;               else            /* exclusive */
;                   KSEMRequestExclusive(HKSEM,timeout);
;               return
;
;       ENTRY   ES:DI = [ThisSFT] contains a pointer to the SFT.
;               It is ASSUMEd to be linked to the MFT.
;
;       EXIT    If exclusive lock - All other threads are locked out
;                                       of access to the file.
;               If shared lock - All other non-reading threads are locked
;                                       out of access to the file.
;
;       USES    EAX, flags
;
;       EFFECTS Switches context (ProcBlock)

Procedure       LockMFTShar

    ASSUME  DS:Nothing, ES:Nothing
        Assert  ISSFT,<ES,DI>,"LockMFTShar"

        mov     ax,1                    ; flag share
        jmp     SHORT lckm0


Entry           LockMFT

        Assert  ISSFT,<ES,DI>,"LockMFT"
    ASSUME  DS:Nothing, ES:Nothing

        xor     ax,ax                   ; flag exclusive

;*      Body of LockMFTShar and LockMFT

lckm0:  SaveReg <ebx,ecx,edx,ds,es>

        mov     ebx,es:[di.sf_MFT]      ; (FLAT:ebx) -> MFT
        cmp     ebx,SF_MFT_NULL         ; if sf_MFT is null, its a pipe
        jz      SHORT lckm99            ; pipe, go return at once

        FlatContext <ds,es>, cx         ; (ds:ebx) -> MFT

ifdef FSSTRICT                          ; do sanity check
        cmp     ds:[ebx].mft_signature,MFT_PATTERN ; ds:ebx -> mft, right?
        jne     SHORT lckmie            ; no, go panic
endif

        mov     edx,ebx                 ; (edx) = handle = addr of mft
        xor     ecx,ecx
        dec     ecx                     ; (ecx) = timeout = -1 = infinite
        or      ax,ax
        jz      SHORT lckm90            ; go do exclusive

        ; ds=es=flat ; edx = handle; ecx = timeout
        CCALL32 _KSEMRequestShared,<edx, -1>; EDX = KSEM Handle
        jmp     SHORT lckm99

lckm90: CCALL32  _KSEMRequestExclusive,<edx, -1>

lckm99: RestoreReg <es,ds,edx,ecx,ebx>
        RET

ifdef FSSTRICT
lckmie: InternalError <LockMFTShar: bad MFT>
endif
EndProc         LockMFTShar


;***    UnlockMFT - release MFT
;
;       UnlockMFT unlocks the MFT.
;
;       ENTRY   [ThisSFT] contains a pointer to the SFT.  It is
;                   ASSUMEd to be linked to the MFT.
;
;       USES    NONE (including flags)

Procedure       UnlockMFT
    ASSUME  ds:nothing, es:nothing

        pushf
        SaveReg <eax,ebx,ecx,edx,ds,es>
        mov     bx,CurrTCB                      ; (TASKAREA:bx) -> CurrTCB
        lds     bx,TASKAREA:[bx].TCBThisSFT     ; (ds:bx)       -> SFT
        mov     ebx,[bx.sf_MFT]                 ; (FLAT:ebx)    -> MFT
        cmp     ebx,SF_MFT_NULL                 ; if null, it's a pipe
        je      SHORT ulkm3

        FlatContext <ds,es>, cx         ; (ds:ebx) -> MFT

ifdef FSSTRICT                          ; do sanity check
        cmp     ds:[ebx].mft_signature,MFT_PATTERN ; ds:ebx -> mft, right?
        jne     SHORT ulkie4            ; no, go panic
endif

        CCALL32 _KSEMRelease,<ebx>      ; EBX = KSEM Handle

ulkm3:  RestoreReg <es,ds,edx,ecx,ebx,eax>
        popf
        ret

ifdef FSSTRICT
ulkie4: InternalError   <UnlockMFT: bad MFT>
endif

EndProc         UnlockMFT

HIGH4CODE ENDS


        END
