SCCSID  equ     "@(#)gshare32.asm       6.6 92/03/15"
page ,132

Title   Sharer - MultiProcess File Share and Lock Support --32 bit code
NAME    Sharer

;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;      SCCSID = @(#)gshare32.asm        6.6 92/03/15

;
;       Global          Local
;
;       MFT_KeyAlloc
;       MFT_KeyFree
;       MFT_GetKey
;
;       MFT_Enter
;       FNM
;       PrefLookupMFT
;       RMN
; Change History
; Defect  date        Author              Abstract
; ======================================================================
; d11111  11/9/92      GEB         move swapper's bitmap from the resident
;                                  heap smHeap (that uses KRH functions) to
;                                  it own memory object
; ======================================================================
; 70134   06-10-93     Rix         In RMN for MFT_INACTIVE clear its mft_opflags
;                                  to avoid spurious OpLockCleanUp upon next use.
; ======================================================================
; 140726  19-Oct-95   scott garfinkle     Serialize access to VPBs.
; ======================================================================
;  72417  11/27/95     MLM         moved from PTDA to TCB:
;                                       WFP_Start
; ======================================================================
; 150371  04/01/96     DRB         System Anchor Block support
; ======================================================================


.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
.cref
.list

;
                                                ;72417 removed PTDA entries now
                                                ;      in TCB
        ReferGlobal     MFT_hPTree,DWORD
        ReferGlobal     Serial,WORD
;
        ReferFlat       _pTCBCur,DWORD
;       ReferFlat       _smHeap,DWORD   d111111
;
        EXTRN32         _KSEMInit
        EXTRN32         _KSEMRelease
        EXTRN32         _KSEMRequestExclusive
        EXTRN32         _PTDelete
        EXTRN32         _PTFind
        EXTRN32         _PTInsert
        EXTRN32         _PTFindFromPrefix
        EXTRN32         _VMAllocKHB
        EXTRN32         _VMFreeKHB
        EXTRN32         _VMGetTempBuffer
        EXTRN32         _VMFreeTempBuffer
;
        EXTRN16         CMKill
        EXTRN16         StrLen32
        EXTRN16         OpLockCleanUp
        EXTRN16         ASC
;

CpuMode 386

CONSTANTS       SEGMENT

public  _ksemMFT, ksemMFT
_ksemMFT        label   BYTE            ; D150371
ksemMFT     db  SIZE KSEMSHR DUP (0)    ; semaphore for all MFT adds/deletes
public  ksemVPB                         ;140726
ksemVPB     db  SIZE KSEMMTX DUP (0)    ; semaphore for all VPB ops     ;140726
ifdef  CLUSTERMAP
        public  MFT_InHead
        MFT_InHead  DD  0
        public  MFT_InTail
        MFT_InTail  DD  0
        public  MFT_InCount
        MFT_InCount DD  0
endif

CONSTANTS       ENDS

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

;** 32 bit code

FARCODE HIGH32
HIGH32CODE segment

BREAK <MFT_Enter - Make an MFT entry and check access>

;***    MFT_Enter - make an entry in the MFT
;
;       MFT_Enter is called to make an entry in the MFT.
;       MFT_Enter checks for a file sharing conflict:
;               No conflict:
;                   A new MFT entry is created, or the existing one updated,
;                   as appropriate.
;               Conflicts:
;                   The existing MFT is left alone.  We may have even just
;                   added the MFT entry, but blocked allowing someone else
;                   to set sharing modes on it before us.
;
;       If no conflict has been discovered, the SFT list for the file is
;       checked for one that matches the following conditions:
;
;           If mode == 70 then
;               don't link in SFT
;               increment refcount
;           If flags&SF_FCB and userids match and process ids match then
;               don't link in SFT
;
;       ENTRY   ThisSFT points to an SFT structure.
;                  sf_fsi.sfi_mode = open mode
;                  if character device
;                     sf_flags = DEVID_DEVICE
;                     sf_devptr = device ptr
;               WFP_Start is an offset from TaskArea of the full pathname for
;                   the file
;               User_ID = 16-bit user id of issuer
;               Proc_ID = 16-bit process id of issuer
;               (DS) = TASKAREA
;               TCBThishVPB is set
;               (CX) = whether to break oplock/opbatch
;                  opnobreak == 0
;                  oplockbreak == 1
;                  opbatchbreak == 2
;               If (fSharing and open)   /* server */
;                  TCBOpCookie contains server per-file cookie
;                  TCBOpFlags contains flag indicating whether server wants an oplock/opbatch
;                     opnolockrequested == 0
;                     oplockrequested == 1
;                     opbatchrequested == 2
;       EXIT    'C' clear if no error
;               'C' set if error
;                 (ax) = error code
;       USES    ALL but EBX and DS
;
;       PSEUDOCODE
;               GetTempBuffer
;               FNM  (find or create MFT record for name)
;       foo:    if (MFT.flags = oplock/batch)
;                  if ((MFT.flags == oplock & break oplock) | (MFT.flags == opbatch & break opbatch) {
;                     OpLockCleanup(cookie,procblock key) /* procblock = &SFT */
;                     if (error)
;                        save server's cookie in new SFT
;                        return  /* server */
;                     FNM (find or create MFT record for name)
;                     if MFT.opflags = opbreakfailed
;                        return ERROR_SHARING_VIOLATION
;                     else
;                        goto foo   /* could have gotten oplocked again during between */
;                  }                /* clean-up and returning so go look again */
;                  else
;                     return ERROR_SHARING_VIOLATION
;               if (fSharing && oplock/batch requested)
;                  if (MFT->SFT == NULL)      /* file not open by any other process */
;                     MFT flags = oplock/batch
;                     remember oplock granted in oplock_flags
;                  else
;                     remember oplock not granted in oplock_flags
;               save server's cookie in new SFT
;               ASC
;               FreeTempBuffer
;
Procedure   MFT_Enter,<near,far32>
    ASSUME DS:TASKAREA, ES:NOTHING

        SaveReg <ds,ebx>

LocalVar me_breakop,WORD                ; whether an oplock/batch should be broken
LocalVar me_SFT,DWORD                   ; pointer to new SFT
LocalVar me_TempBuffer,DWORD            ; pointer to temp buffer containing WFP
LocalVar me_TempBufferFlat,DWORD        ; FLAT pointer to temp buffer

        EnterProc <cx>
        FlatContext <ds,es>,cx
        mov     edi,_pTCBCur
        mov     edi,[edi].TCBThisSFT            ; (EDI) -> new SFT (16:16)
        mov     me_SFT,edi                      ; save pointer to new SFT
;
; One more small step in the drive to non-serialization of the FS.  Save full
; path so that other threads in same process can use WFP_Start, OpenBuf, etc.
; Still a problem because GetTempBuffer can block and the WFP can change
; under us - have to not block or use a stack buffer.
;

        lea     edi,me_TempBufferFlat
        SSToDS  edi                     ; Flat:32 version
        lea     ecx,me_TempBuffer
        SSToDS  ecx                     ; 16:16 version
me2:    CCall   _VMGetTempBuffer <edi,ecx>
        TaskContext <ds>,di
        ASSUME DS:TASKAREA
        or      ax,ax                   ; Only error should be ERROR_INTERRUPT
        jnz     me2

                                        ;72417 moved from PTDA to TCB
        mov     esi,es:[_pTCBCur]
        mov     si,es:[esi].TCBWFP_Start ; (DS:SI) -> file name
                                        ;72417 end
        les     di,me_TempBuffer        ; (es:di) -> temp buffer
ifdef DISABLENEWFILES
        CALL16  FStrCpy                 ; copy to buffer and no need to uppercase
else
        CALL16  StrCpy                  ; copy to buffer and uppercase
endif

;       find or make a name record
        FlatContext <ds,es>,ax

        call    GetMFTSem

        mov     esi,me_TempBufferFlat   ; (Flat:ESI) -> file name
;;      DBPRT   1,8,<MFT_Enter 1:8 - @$x:$x Name=$S >,<DS, SI, DS, SI>
        mov     al,1                    ; allow creation of MFT entry
        push    bp

        CALL    FNM                     ; find or create name in MFT

        pop     bp                      ; (FLAT:EBX) -> MFT
    ASSUME      DS:NOTHING

        mov     ax,ERROR_SHARING_BUFFER_EXCEEDED
        jc      me40                    ; not enough space
        DBPRT   1,8,<Survived FNM ($x) >,<bx>

;
; (DS:EBX) -> MFT
;
me5:    test    [ebx].mft_opflags,mft_opanylock ; opbatch or oplock on file?
        jz      SHORT me20              ; no, go see if oplock/batch requested
        mov     cx,me_breakop           ; (CX) = what to break
        test    [ebx].mft_opflags,cx    ; (oplocked & break oplock) | (opbatched & break opbatch)
        mov     ax,ERROR_SHARING_VIOLATION
        stc
        jz      me40            ; no, go return error

        call    FreeMFTSem

        les     di,[ebx].mft_sptr       ; (ES:DI) -> SFT
;       lds     si,me_TempBuffer        ; (DS:SI) -> WFP          ;### AZ09831
        mov     esi,me_TempBufferFlat   ; (Flat:ESI) -> file name ;### AZ09831
        CALL16  OpLockCleanUp
        jc      me50           ; server request, return so server can cleanup

        FlatContext <ds,es>,ax

t2a:
        call    GetMFTSem
t2b:

        mov     esi,me_TempBufferFlat   ; (FLAT:ESI) -> WFP
        mov     al,1                    ; allow creation of MFT entry
        push    bp

        CALL32  FNM,nocheck             ; find or create name in MFT

        pop     bp                      ; (DS:EBX) -> MFT

;ifdef FSSTRICT                         ; do sanity check
;        call    _AssertDSESFlat
;endif

        mov     ax,ERROR_SHARING_BUFFER_EXCEEDED
        jc      me40            ; not enough space
        test    [ebx].mft_opflags,mft_opbreakfailed ; failed cleanup of oplock?
        jz      SHORT me5               ; no, go continue sharing check
        mov     ax,ERROR_SHARING_VIOLATION ; yes, fail sharing check
        stc
        jmp     SHORT me40              ; go return error
;
; When we get here, any opportunistic file locks have been broken.  We now
; see if anyone is requesting one.
;
me20:   mov     edi,_pTCBCur
        cmp     FLAT:[edi].TCBfSharing,-1; server?
        jne     SHORT me30              ; no, go add SFT to chain
        mov     cx,FLAT:[edi].TCBOpFlags; (CX) = whether oplock/batch requested
        test    cx,opanylockrequested   ; any lock requested?
        jz      SHORT me30              ; no, go add SFT to chain
        cmp     [ebx].mft_sptr.Segmt,0  ; any SFT connected?
        jnz     SHORT me25              ; yes, fail oplock request
        and     cx,mft_opanylock        ; (CX) = oplock/batch
        mov     [ebx].mft_opflags,cx
        jmp     SHORT me30

me25:   mov     [edi].TCBOpFlags,mft_opnolock ; no oplock/batch granted

;       add the SFT to the chain
;
;       (FLAT:ebx) -> MFT
;       (edi) = _PTCBCur
;
me30:   lds     si,me_SFT               ; (DS:SI) -> new SFT
        ASSUME  DS:NOTHING
        mov     eax,FLAT:[edi].TCBOpCookie; (EAX) = server's cookie
        mov     [si].sf_cookie,eax      ; save cookie in SFT

ifdef FSSTRICT
        mov     ax,ds
        mov     cx,ss
        cmp     ax,cx
        jne     SHORT ent8              ; if SFT on process stack, error

        Panic <'MFT_Enter: SFT on stack'>
ent8:
endif

        SaveReg <bp>
        CALL16  ASC                     ; try to add to chain
        RestoreReg <bp>                 ; BP -> LocalVar frame

;       As noted above, we don't have to worry about an "empty" MFT being
;       left if ASC refuses to add the SFT as ASC cannot refuse if we had
;       just created the MFT...

;       return.
;
;       'C' and (AX) setup appropriately

me40:
        call    FreeMFTSem

IF  Debug
        pushf
        xor     cx,cx
        popf
        jnc     SHORT ent10
        not     cx
ent10:  DBPRT   1,8,<MFT_Enter rtn: CF=$b AX=$x\n>,<cx,ax>
ENDIF

; when serialization of fs is removed, WFP in temp buffer should be copied
; back to WFP_Start
;

me50:   pushf
        push    ax
        FlatContext <ds,es>,cx
        mov     edi,me_TempBufferFlat   ; address of temporary buffer
        CCall   _VMFreeTempBuffer <edi> ; free temporary buffer
        pop     ax
        popf
        LeaveProc
        RestoreReg <ebx,ds>
        ret




EndProc     MFT_Enter
BREAK <FNM - Find name in MFT>

;***    FNM - Find name in MFT
;
;       FNM searches the MFT for a name record.
;
;       ENTRY   (FLAT:ESI) = pointer to name string (asciz AND upper-cased)
;               (al) = 1 create record if none exists
;                    = 0 just find name, don't create
;               TCBThishVPB set
;       EXIT    'C' clear if found or created
;                 (DS:EBX) -> MFT  (where DS is FLAT)
;               'C' set if error
;                 (DS:SI) unchanged
;                 (ax) = error code
;                 If find,   item not found
;                 If create, out of space
;
;       USES    eax,ebx,ecx,edx,esi,edi,ds,es

Procedure   FNM,<near,far32>
    ASSUME  ds:FLAT, es:FLAT, ss:TASKAREA

        LocalVar        fnm_pname,DWORD ; FLAT:32 pointer to name
        LocalVar        fnm_hKey,DWORD  ; handle to key -> MFT

        EnterProc       <esi>,,alignesp
        FlatContext     <ds,es>,bx

        mov     bl,al                   ; (bl) = create flag

        mov     edi,esi                 ; edi -> name

        call    MFT_KeyLen              ; (ecx) = key length (hvpb + name)
                                        ; (edi) -> hVPB (before name)

        SSToDS  eax,fnm_hKey            ; (FLAT:eax) -> hKey

        or      bl,bl
        jnz     SHORT fnm20             ; go do insert

        CCall   _PTFind <MFT_hPTree,edi,ecx,eax>
        mov     cx,ERROR_PATH_NOT_FOUND          ; (cx) = default error code
        jmp     SHORT fnm30

fnm20:  CCall   _PTInsert <MFT_hPTree,edi,ecx,eax>

        mov     cx,ERROR_SHARING_BUFFER_EXCEEDED ; (cx) = default error code

fnm30:  or      eax,eax                 ; (clears carry)
ifdef CLUSTERMAP
        jz      SHORT fnm101            ; find/insert succeeded
endif
        jz      SHORT fnmx

;       not found or insert failed
fnm90:
;BUGBUG bryand 1-7-90 - leave ERROR_INTERRUPTED alone?
;       cmp     ax,ERROR_INTERRUPTED
;       je      fnm99                   ; leave error_interrupted alone
        mov     ax,cx                   ; (ax) = error code

fnm99:  mov     esi,fnm_pname           ; (esi) -> name
        stc

ifdef CLUSTERMAP
        jmp     fnmx

fnm101: mov     ebx,fnm_hKey            ; (ds:ebx) -> MFT
        test    [ebx].mft_flags,MFT_INACTIVE ; was MFT inactive ?
        jz      fnmx                    ; it was active goto exit

        dec     MFT_InCount             ;dec count of saved mft entries
        xor     eax,eax
        and     [ebx].mft_flags,not MFT_INACTIVE  ; remove inactive flag
        mov     esi,dword ptr [ebx].mft_lptr      ;esi -> previous node
        mov     edi,[ebx].mft_sptr      ;edi -> next node
        test    esi,esi
        jz      fnm102              ;node is first in list adjust MFT_InHead
        test    edi,edi             ;node is last in chain adjust MFT_InTail
        jz      fnm105
        mov     [esi].mft_sptr,edi
        mov     dword ptr [edi].mft_lptr,esi
        jmp     short fnm110

fnm102: test    edi,edi             ;are both esi and edi zero
        jz      short fnm107    ;both are zero go clear both inhead and intail
        mov     MFT_InHead,edi      ;patch MFT_InHead
        mov     dword ptr [edi].mft_lptr,eax
        jmp     short fnm110

fnm105: mov     MFT_InTail,esi      ;patch MFT_InTail
        mov     [esi].mft_sptr,eax
        jmp     short fnm110

fnm107:
        mov     es: MFT_InTail,eax
        mov     es: MFT_InHead,eax


fnm110: mov     [ebx].mft_sptr,eax;
        mov     dword ptr [ebx].mft_lptr,eax
ifdef FSSTRICT                          ; do sanity check
        mov     [ebx].mft_signature,MFT_PATTERN ; mft is valid
endif
        mov     dword ptr [ebx].mft_lptr,eax ; this also clears mft_opflags

fnmx:   LeaveProc
        ret
else   ;  This is not clustermap
fnmx:   mov     ebx,fnm_hKey
        LeaveProc
        ret
endif


EndProc FNM

BREAK <RMN - Remove MFT Name record>

;***    RMN - Remove MFT Name record
;
;       RMN removes a name record from the MFT tree.
;
;       ENTRY   (FLAT:EBX) -> MFT entry to remove
;       EXIT    to INTERR if lock and SFT chains are not empty
;       USES    ALL

Procedure   RMN,<near,far32>
    ASSUME  ds:FLAT, es:FLAT, ss:TASKAREA
        EnterProc ,,alignesp
        mov     ax, seg FLAT:DGROUP
        mov     cx,ds
        cmp     cx,ax
        je      rmn1
        Panic   <'RMN : DS NOT DGROUP'>

rmn1:   mov     es,ax           ; ds,es = FLAT
        mov     ax,[ebx].mft_sptr.Offst
        or      ax,[ebx].mft_lptr
        jnz     rmnier          ; not clean - internal error
        mov    word ptr [ebx].mft_opflags,0   ;clr opflags for next use ;72418;

ifdef CLUSTERMAP
        mov     eax,[ebx].mft_pCMap
        or      eax,eax
        jnz     rmn5
endif
ifdef FSSTRICT
        mov     [ebx].mft_signature,MFT_PAT_DEL ; mft is invalid
endif
        lea     edi,[ebx].mft_name      ; (es:edi) -> name
        CALL16  StrLen32                ; (ecx) = strlen INCLUDING NULL
        add     ecx,(size mft_hvpb) - 1 ; (ecx) = strlen with hVPB prefix
                                        ;         and no null
        sub     edi,size mft_hvpb       ; (es:edi) -> hVPB (before name)

        CCall   _PTDelete <MFT_hPTree,edi,ecx>


ifdef CLUSTERMAP
        jmp     short rmn10

rmn5:
        xor    eax,eax
        mov    word ptr [ebx].mft_opflags,ax  ;clr opflags for next use ;70134;
        or     word ptr [ebx].mft_flags,MFT_INACTIVE                    ;70134;
        ; insert this MFT at head of inactive list
        mov    ecx,es: MFT_InHead       ;ecx -> head of inactive list MRU
        mov    [ebx].mft_sptr,ecx       ;mft_sptr -> first inactive MFT
        or     ecx,ecx
        jz     rmn6
        mov    dword ptr [ecx].mft_lptr,ebx
rmn6:   mov    MFT_InHead,ebx          ;MFT_InHead -> this MFT
        mov    dword ptr [ebx].mft_lptr,eax
        mov    ecx, MFT_InCount
        inc    ecx
        mov    MFT_InCount,ecx
        cmp    ecx, MFT_MAX_INACTIVE
        jb     short rmn8
        call   MFT_Del_Inactive             ;delete inactive MFT's
rmn8:   cmp    MFT_InTail,eax               ;has mft tail been initialized?
        jne    short rmn10                  ;no, don't change tail
        mov    MFT_InTail,ebx
rmn10:
endif   ; CLUSTERMAP
        LeaveProc
        ret

rmnier: Panic   <'RMN: mft_lptr or mft_sptr field not 0'>
EndProc RMN

ifdef CLUSTERMAP
;***    MFT_Del_Inactive - delete the least frequently used inative MFT entry
;
;       ENTRY   MFT_InTail pointes to end of MFT linked list, and that
;               the list is greater than 3 nodes long.
;               eax = 0
;
;       EXIT    LRU MFT is deleted
;
;       USES    ebx,ecx,edx,edi,es,ds

Procedure   MFT_Del_Inactive,NEAR
        assume ds:FLAT, es:FLAT, ss:nothing

        mov     ebx,MFT_InTail
        mov     esi,dword ptr [ebx].mft_lptr    ; esi points to 2nd to last node
        mov     MFT_InTail, esi                 ; set tail pointer
        mov     dword ptr [esi].mft_sptr,eax    ; clear pointer to next

ifdef FSSTRICT                          ; do sanity check
        mov     [ebx].mft_signature,MFT_PAT_DEL ; mft is invalid
endif

        lea     edi,[ebx].mft_name      ; (es:edi) -> name
        CALL16  StrLen32                ; (ecx) = strlen INCLUDING NULL
        add     ecx,(size mft_hvpb) - 1 ; (ecx) = strlen with hVPB prefix
                                        ;         and no null
        sub     edi,size mft_hvpb       ; (es:edi) -> hVPB (before name)

        CCall   _PTDelete <MFT_hPTree,edi,ecx>
        ret
EndProc MFT_Del_Inactive
endif

BREAK <MFT_KeyLen - get MFT string length from string>

;***    MFT_KeyLen - get MFT string length from string
;
;       ENTRY   FLAT:edi -> name string
;               TCBThishVPB set
;       EXIT    ecx = length of string (without null) plus hvpb
;               edi -> hvpb in front of name string
;               edi.hvpb = TCBThishVPB
;
;       USES    ecx,edi

Procedure   MFT_KeyLen,NEAR
    ASSUME  ds:FLAT, es:NOTHING, ss:TASKAREA

        FlatContext <ds>,cx

;       movzx   edi,di                  ; (es:edi) -> name
        CALL16  StrLen32                ; (ecx) = strlen INCLUDING NULL
        add     ecx,(size mft_hvpb) - 1 ; (ecx) = strlen with hVPB prefix
                                        ;         and no null
        sub     edi,size mft_hvpb       ; (es:edi) -> hVPB (before name)

        SaveReg <ebx>
;       mov     bx,CurrTCB
;       mov     bx,TASKAREA:[bx].TCBThishVPB    ; volume in use
;       mov     [edi],bx                                ; save hVPB in front of name
        mov     ebx,_pTCBCur
        mov     bx,[ebx].TCBThishVPB
        mov     [edi],bx
        RestoreReg <ebx>

        ret

EndProc   MFT_KeyLen

;** Heap to allocate non-swapfile MFT Entries from
;
; The MFT for the swap file must allways be resident
; so it gets allocated from _smHeap.
;
; With MFTs allocated from the swappable heap, we don't
; want them causing extra page faults in the middle of the
; data structure, so we make MFTs not cross page boundaries.

MFT_HEAP        EQU     VM_HKH_PUB_RESRW
MFT_HEAP_FLAGS  EQU     0 ; was VMAC_NOXPAGE


BREAK  <MFT_KeyAlloc - allocate and init MFT entry

;***    MFT_KeyAlloc - allocate and init MFT entry
;
;       ENTRY   (TOS+8) = DWORD 32 bit pointer to key allocated
;               (TOS+4) = DWORD 32 bit name length
;               (TOS)   = DWORD 32 bit pointer to name (with hVPB prefix)
;
;       EXIT    EAX = zero
;                       (*phKey) -> allocated MFT entry
;               EAX = non-zero
;                       allocation failed
;
;       USES    eax,ecx,edx,flags
;
; PseudoCode:   alloc MFT from kernel heap
;               fill in MFT entry

Procedure   MFT_KeyAlloc,NEAR
    ASSUME  ds:FLAT, es:FLAT, ss:TASKAREA

ArgVar          mka_name,DWORD  ; 32 bit flat pointer to name (with hVPB prefix)
ArgVar          mka_len,DWORD   ; 32 bit name string length
ArgVar          mka_phKey,DWORD ; 32 bit pointer to handle to key allocated
LocalVar        mka_heap,WORD   ; flags for MFT allocation heap

        EnterProc
        SaveReg <esi,edi>

        mov     ecx,size MFT_Entry
        add     ecx,mka_len             ; (ecx) =  length + 1 for null

        mov     eax,MFT_HEAP            ; (eax) = normal MFT heap
        mov     edx,MFT_HEAP_FLAGS      ; (edx) = normal MFT heap flags
        mov     [mka_heap],MFT_DEFAULTHEAP  ; assume normal MFT heap
        mov     edi,_pTCBCur
         test    [edi].TCBfSwapping,SM_TCB_RESIZING ; resizing swap file?
         jz      SHORT mka10                        ;   NO,  use normal heap

         mov     [mka_heap],mft_pagerheap; assume normal MFT heap
; move from swappers resident heap to resident r/w heap ; d11111
;        mov     eax,_smHeap             ; (eax) = swap MFT heap
         mov     eax, VM_HKH_PUB_RESRW
; end
         xor     edx,edx                 ; (edx) = swap MFT heap flags
mka10:  CCall   _VMAllocKHB,<eax,ecx,FSMFTOWNER,edx,mka_phKey>

        or      eax,eax
        jnz     SHORT mka90                     ; VMAlloc failed, return error

        mov     esi,mka_phKey                   ; (esi) -> hKey
        mov     esi,[esi]                       ; (esi) =  hKey -> MFT

ifdef FSSTRICT
        mov     [esi].mft_signature,MFT_PATTERN ; used for sanity checks
endif

        lea     edx,[esi].mft_ksem      ; (edx) -> mft_ksem
        CCALL32 _KSEMInit,<edx,KRNL_SHARESEM, KSEM_NOINTERRUPT>

        lea     edx,[esi].mft_CMapKSem  ; (edx) -> mft_CMapKSem
        CCALL32 _KSEMInit,<edx,KRNL_SHARESEM, KSEM_NOINTERRUPT>

        xor     eax,eax
        lea     edi,[esi].mft_lptr      ; (es:edi) -> mft_lptr
B1EMac  stos    word ptr es:[edi]       ; zero LCK pointer

    .errnz      mft_sptr-mft_lptr-2
B1EMac  stos    dword ptr es:[edi]      ; zero SFT pointer

    .errnz      mft_pCMap-mft_sptr-4
B1EMac  stos    dword ptr es:[edi]      ; zero cluster map pointer

        lea     edi,[esi].mft_opflags   ; (es:edi) -> mft_opflags
    .errnz      mft_opnolock
B1EMac  stos    word ptr es:[edi]       ; zero mft_opflags

        push    ds
        DosContext ds
        inc     Serial                  ; bump serial number
        mov     ax,Serial
        pop     ds
    ASSUME      DS:NOTHING
    .errnz      mft_serl-mft_opflags-2
B1EMac  stos    word ptr es:[edi]       ; set serial number

    .errnz      mft_flags-mft_serl-2
        mov     ax,[mka_heap]           ; (ax) = heap for allocation
B1EMac  stos    word ptr es:[edi]       ; set heap allocation flags

        lea     edi,[esi].mft_hvpb      ; (es:edi) -> mft_hvpb

;       Now copy the hvpb and name.

    .errnz      mft_name-mft_hvpb-2
        mov     ecx,mka_len             ; (ecx) =  length without null
        inc     ecx                     ; (ecx) =  length with null
        mov     esi,mka_name            ; (ds:esi) -> original name
B1EMac  rep     movs byte ptr es:[edi],byte ptr ds:[esi] ; copy the name

        mov     eax,NO_ERROR            ; return NO_ERROR

mka90:  RestoreReg <edi,esi>
        LeaveProc
        RETC

EndProc MFT_KeyAlloc


;***    MFT_KeyFree - free MFT entry
;
;       ENTRY   (TOS) = DWORD 32 bit handle to key
;
;       EXIT    eax = NO_ERROR
;
;       USES    eax,ecx,edx,flags
;
; PseudoCode:   free MFT from kernel heap

Procedure   MFT_KeyFree,NEAR
    ASSUME  ds:FLAT, es:FLAT, ss:TASKAREA

ArgVar          mkf_hkey,DWORD          ; handle to key = -> MFT
        EnterProc
        SaveReg <edi>

        mov     edi,mkf_hkey            ; (ds:edi) -> MFT
ifdef FSSTRICT
        cmp     ds:[edi].mft_signature,MFT_PAT_DEL      ; ds:edi -> mft?
        jne     SHORT mkfie                             ; no, go panic

        mov     ds:[edi].mft_signature,0                ; clear pattern
endif
        CALL16  CMKill                  ; Free cluster map

        mov     eax,MFT_HEAP            ; (eax) = normal MFT heap
        test    ds:[edi].mft_flags,mft_pagerheap; pager heap allocation?
        jz      SHORT mkf10                     ;   NO,  use normal heap

; move from swappers resident heap to resident r/w heap ; d11111
;        mov     eax,_smHeap             ; (eax) = swap MFT heap
         mov     eax, VM_HKH_PUB_RESRW
; end

ifdef FSSTRICT
        cmp     ds:[edi].mft_sptr,0             ; Is there an SFT linked?
        jne     SHORT mkfie2                    ;   yes, go panic
        cmp     ds:[edi].mft_lptr,0             ; Is there a lock linked?
        jne     SHORT mkfie2                    ;   yes, go panic
endif

mkf10:  CCall   _VMFreeKHB,<eax,mkf_hkey>

        mov     eax,NO_ERROR

        RestoreReg <edi>
        LeaveProc
        RETC

mkfie:  Panic   <'MFT_KeyFree: bad MFT'>
mkfie2: Panic   <'MFT_KeyFree: SFT on sharing list'>

EndProc MFT_KeyFree


;***    MFT_GetKey - return pointer to key string given handle to key
;
;       ENTRY   (TOS) = DWORD 32 bit handle to key
;
;       EXIT    EAX = -> key string
;
;       USES    eax

Procedure   MFT_GetKey,NEAR
    ASSUME  ds:FLAT, es:FLAT, ss:TASKAREA

ArgVar          mgk_hkey,DWORD          ; 32 bit handle to key
        EnterProc

        mov     eax,mgk_hkey            ; (eax) = handle to key -> MFT
ifdef FSSTRICT
        cmp     ds:[eax].mft_signature,MFT_PATTERN      ; ds:eax -> mft?
        je      SHORT mgk10                             ; no, go panic

        cmp     ds:[eax].mft_signature,MFT_PAT_DEL      ; ds:eax -> mft?
        jne     SHORT mgkie                             ; no, go panic
mgk10:
endif
        lea     eax,[eax].mft_key       ; (eax) -> key string in MFT

        LeaveProc
        RETC

mgkie: Panic    <'MFT_GetKey: bad MFT'>

EndProc MFT_GetKey

;***    FreeMFTSem - serialize for possible add/delete of MFT entry
;
;       FreeMFTSem  frees an exclusive kernel semaphore to prevent race
;       conditions that may cause MFT entries marked for deletion from
;       being inadvertently linked to.
;
;       ENTRY   NONE
;
;       EXIT    Kernel MFT semaphore freed
;
;       USES    NONE (including flags)

Procedure   FreeMFTSem,<near,far32>
    ASSUME  ds:nothing, es:nothing

        pushf
        SaveReg <eax,ebx,ecx,edx,ds,es>

        FlatContext <ds,es>, cx

        CCALL32 _KSEMRelease,<OFFSET FLAT:ksemMFT>

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

EndProc     FreeMFTSem

;***    GetMFTSem - serialize for possible add/delete of MFT entry
;
;       GetMFTSem gets an exclusive kernel semaphore to prevent race
;       conditions that may cause MFT entries marked for deletion from
;       being inadvertently linked to.
;
;       ENTRY   NONE
;
;       EXIT    Kernel MFT semaphore taken
;
;       USES    NONE (including flags)

Procedure   GetMFTSem,<near,far32>
    ASSUME  ds:nothing, es:nothing

        EnterProc ,,alignesp
        pushf
        SaveReg <eax,ebx,ecx,edx,ds,es>

        FlatContext <ds,es>, cx

        CCALL32 _KSEMRequestExclusive,<OFFSET FLAT:ksemMFT, -1>

        or      ax,ax
        je      goback
        Panic   <'GetMFTSem: Get MFT semaphore fail'>
goback:

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

EndProc     GetMFTSem

HIGH32CODE ends

BREAK <PrefLookupMFT - Look for a prefix match in MFT>
;;HIGH32SWAP_TEXT SEGMENT  ; this only gets called in rename so try to get it
LOAD_FTUNE1_SWP segment                    ; swapped out.

;***    PrefLookupMFT -Look for a prefix match in MFT
;
;       This routine is case-insensitive as it copies the name to a
;       TempBuffer and upper cases it before searching for it in the MFT.
;       First search the MFT for an exact match.
;       Then  search the MFT for a prefix match with a \ appended.
;
;       ENTRY   (ES:DI) = pointer to name string (.asciz)
;               TCBThishVPB set
;               DS,ES do not need to be FLAT
;       EXIT    NC if found
;                       ZR      if exact match
;                               a prefix otherwise
;               CY set if not found
;
;       USES    eax,flags
;
; PseudoCode:   GetTempBuffer
;               copy string to TempBuffer and uppercase
;               PTFind
;               append path separator for PFindFromPrefix
;               PTFindFromPrefix
;               FreeTempBuffer

Procedure   PrefLookupMFT,<near,far32>
    ASSUME  ds:nothing, es:nothing, ss:TASKAREA

        LocalVar        plm_hKey,DWORD          ; handle to key -> MFT
        LocalVar        plm_FlatBuf,DWORD       ; Flat addr of temp buf
        LocalVar        plm_TiledBuf,DWORD      ; 16:16 addr of temp buf

        EnterProc  ,,alignesp

        SaveReg <ecx,edx,esi,edi,ds,es>


        SaveReg <es,di>                ; es:di ->source name
        FlatContext <ds,es>,ax
        lea     eax, plm_FlatBuf
        SSToDS  eax
        lea     edx, plm_TiledBuf
        SSToDS  edx
        CCall   _VMGetTempBuffer <eax,edx>
        RestoreReg <si,ds>             ; CAVEAT! ds:di->Source Name

        les     di,plm_TiledBuf         ; (es:di) -> destination buf
        mov     ax,di
        CALL16  StrCpy                  ; copy to buffer and uppercase
        dec     di                      ; es:di -> null at end of name
        sub     di,ax
        movsx   eax,di                  ; eax -> len of string (inc NULL)

        ; strcpy is the last routine that needs a 16:16 pointer
        ; use plm_FlatBuf from here on out
        FlatContext <ds,es>,di
        mov     edi,plm_FlatBuf
        mov     esi,edi
        add     esi,eax                 ; FLAT:ESI -> null at end of string

        call    MFT_KeyLen              ; (ecx) = key length (hvpb + name)
                                        ; (es:edi) -> hVPB (before name)

        lea     eax,plm_hKey
        SSToDS  eax                     ; (FLAT:eax) -> hKey

        SaveReg         <ecx,edx>
        CCall   _PTFind <MFT_hPTree,edi,ecx,eax>
        RestoreReg      <edx,ecx>
        or      eax,eax
        jz      SHORT plmx              ; found exact match - NC and ZR

        ; no exact match, look for prefix

        ; append path separator so PTFindFromPrefix will work right
        ; There is always room for the path separator because there is always
        ; room in the buffer for a trailing null and the PTree doesn't need
        ; a trailing null as long as the length is right.
        ; So, the worst case is we overwrite the trailing null in TempBuffer.

        cmp     byte ptr [esi-1],'\'    ; is last char already a path sep?
        je      SHORT plm50             ;  yes, don't append another

        mov     byte ptr [esi],'\'      ; append path separator
        inc     ecx                     ; adjust length

plm50:
        lea     eax,plm_hKey
        SSToDS  eax                     ; (FLAT:eax) -> hKey

        CCall   _PTFindFromPrefix <MFT_hPTree,edi,ecx,eax>
        or      eax,eax
        stc                             ; assume not found - CY
        jnz     SHORT plmx              ; not found - CY

        or      esp,esp                 ; found prefix - NC and NZ

plmx:   mov     ecx,plm_FlatBuf         ; (es:di) -> temp buffer
        pushf                           ; save flags
        CCall   _VMFreeTempBuffer <ecx> ; free the temp buffer
        popf                            ; restore flags
        RestoreReg <es,ds,edi,esi,edx,ecx>
        LeaveProc
        ret

EndProc PrefLookupMFT
LOAD_FTUNE1_SWP ENDS
;;HIGH32SWAP_TEXT ends

        END


