SCCSID  equ     "@(#)notify.asm 6.22 92/03/21"
PAGE    ,132
TITLE   Notify - Protected mode API FindNotify
NAME    Notify

;
;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved
;
;
;****   Notify - Protected mode API FindNotify
;
;       Source for w_FindNotifyFirst, w_FindNotifyNext, w_FindNotifyClose,
;       which support DOSFINDNOTIFYFIRST, DOSFINDNOTIFYNEXT,
;       DOSFINDNOTIFYCLOSE.
;
;       We will allocate a GDT segment (Ring 0) for all processs to store a
;       vector of of offsets. We allocate the segment on the first
;       FindNotifyFirst request. The segment is managed by BMP.
;       The user DirHandle will be an offset into this
;       BMP segment. We verify that a process is allowed to issue a
;       FindNotifyNext/Close on the handle he passed us by making sure that
;       offset points to a valid BMP record and by checking for his PID.
;
;       The BMP segment will contain the following info:
;       i. the handle returned by the appropriate subsystem (FAT,FSD)
;       ii. the fsc pointer and flags (Used by FAT only)
;       iii. the process id of the process issuing the request
;       We will perform routing using the FSC pointer.
;       Flags are used
;        i) to mark the handle busy in case of FAT FINDNOTIFY
;               searches so that two FAT notifynext calls for the same handle
;               CANNOT BE issued.
;        ii) to avoid using the handle if another thread is in the process of
;               closing the notify search.
;
;               Global BMP segment
;               *----------*
;               |FSC ptr   |
;               |SybSys Hdl| (for process A)
;         *-----|Flags     |
;         |     |Next      |
;         |     *----------*
;         |         .
;         |         .
;         |         .
;         *---->*----------*
;        0<-----|Next      | (for process C)
;               |FSC ptr   |
;               |SybSys Hdl|
;               |Flags     |
;               *----------*
;
;       FAT File System
;       ---------------
;
;       We will allocate a RMP segment to store information about each
;       FindNotify in progress. The structure of the RMP segment will contain
;       a handle used for FindNotifyNext/Close, WFP, attribute of WFP,
;       length of wfp, change count required. The handle is a word field
;       that gets incremented on each successive FindNotifyFirst. We first
;       check to make sure the handle is not in use before we use it by
;       issuing a Rmp_Find. When the handle wraps over from 0FFFFH we
;       then start from 1. This wiil always work since we will not be able to
;       store 64k structures in the RMP segment.
;       There may exisit multiple records with the same WFP.
;
;       The following APIs' call FNotify_Path_Wake, call TransPath to generate
;       a WFP CREATE, DELETE, MKDIR, RMDIR, RENAME, SETPATHINFO.  We will do
;       the following: Compare the WFP against WFPs in the Rmp segment, check
;       the attributes, if they match we decrement the change count and if its
;       zero we retrieve the handle from the Rmp and wakeup anyone waiting on
;       notification.
;
;       The following handle base APIs call FNotify_Handle_Wake SETFILEMODE,
;       COMMIT We retrieve the WFP from the MFT entry assosciated with the SFT
;       and the attribute from the SFT. We compare the WFP against WFPs in the
;       Rmp segment. We then check the attributes If they match we decrement
;       the change count and if its zero we retrieve the Handle from the Rmp
;       record and wakeup anyone waiting on notification.
;
;       FindNotifyFirst calls GetPath to make sure that the path actually
;       exists. We generate a handle, and add the new record. We then block on
;       the RMP selector and the Handle using the requested timeout value.
;
;       FindNotifyNext finds the record in the RMP segment using the input
;       Dirhandle and changes the change count in the RMP record.  It then
;       blocks on the RMP selector and the Handle using the requested timeout
;       value.
;
;       FindNotifyClose, using the input DirHandle, calls RMP_FIND to find the
;       record. If it exists we call RMP_REM to remove the record from the RMP
;       segment.
;
;       Procedures list:
;
;               public proc           local proc
;               -------------         ------------
;               w_FindNotifyClose
;               w_FindNotifyFirst
;               w_FindNotifyNext
;                                     FSD_FindNotifyFirst
;                                     DOS_FindNotifyClose
;                                     DOS_FindNotifyFirst
;                                     DOS_FindNotifyNext
;                                     FNotify_AllocDirHandle
;                                     FNotify_FreeDirHandle
;                                     FNotify_AllocFatHandle
;                                     FNotify_ChkDirHandle
;                                     Find_RMPFNYRec
;               FNotify_Handle_Wake
;               FNotify_Path_Wake
;                                     FNotify_Wake
;               FNotify_Vol_Wake
;               FNotify_Exit
;
;       Call hierarchy:
;
;       w_FindNotifyClose
;               FNotify_ChkDirHandle
;               DOS_FindNotifyClose
;               FS_FindNotifyClose
;               FNotify_FreeDirHandle
;
;       w_FindNotifyFirst
;               FuWord
;               FNotify_AllocDirHandle
;               TransPath
;               FSD_FindNotifyFirst
;               FNotify_FreeDirHandle
;
;       w_FindNotifyNext
;               FNotify_ChkDirHandle
;               DOS_FindNotifyNext
;               FS_FindNotifyNext
;
;       FSD_FindNotifyFirst
;               DOS_FindNotifyFirst
;               FS_FindNotifyFirst
;
;       DOS_FindNotifyClose
;               Rmp_Find
;               Rmp_Rem
;
;       DOS_FindNotifyFirst
;               GetPath
;               RlseBuffr
;               FNotify_AllocFATHandle
;               DStrLen
;               SegAlloc
;               Rmp_Alloc
;               SegFree
;               Rmp_Add
;               DOS_FindNotifyNext
;               Find_RMPFNYRec
;               Rmp_Rem
;
;       DOS_FindNotifyNext
;               ProbeBuf
;               Rmp_Find
;               ProcBlock
;               ProcRun
;               Find_RMPFNYRec
;
;       FNotify_AllocDirHandle
;               SegAlloc
;               Bmp_Init
;               Bmp_Get
;               Bmp_Free
;
;       FNotify_FreeDirHandle
;               Bmp_Free
;
;       FNotify_AllocFatHandle
;               Rmp_Find
;
;       FNotify_ChkDirHandle
;               NONE
;
;       FNotify_Handle_Wake
;               FNotify_Wake
;
;       FNotify_Path_Wake
;               FNotify_Wake
;
;       FNotify_Vol_Wake
;
;       FNotify_Wake
;               Rmp_FindN
;               MetaMatch
;               MatchAttributes
;               ProcRun
;
;       FNotify_Exit
;               w_FindNotifyClose
; 3/7/92  b734301 SFB Take si,ds off the stack if error condition
;11/27/95 72417   MLM moved from PTDA to TCB: Attrib, Sattrib, Curr_Dir_End
;                     NoSetDir, PathNameType, WFP_Start, WFP_Path_End,
;                     Ren_WFP, cMeta
; 4/01/96 150371  DRB System Anchor Block support
;
include fnofs.inc
include dosseg.inc
.xcref
.xlist
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include osconst.inc
include error.inc
include curdir.inc
include dirent.inc
include filesys.inc
include fsc.inc
include vmowner.inc
include tkexport.inc
include mft.inc
include mi.inc
include mult.inc
include protmode.inc
include sft.inc
include kmode.inc
include sysentry.inc
include vpb.inc
include argframe.inc
include pgexport.inc
include vmexport.inc
include virtsym.inc
include seldesc.inc
include codelock.inc
.list
.cref
include find.inc

        ModName , FNO_NOTIFY_ASM

        EXTRN           GetPath:NEAR
        EXTRN           fValidFATDisk:NEAR
        EXTRN           MatchAttributes:NEAR

        extrn     Bmp_SegSize:ABS


        EXTRNFAR        Bmp_Get
        EXTRNFAR        Bmp_Free
        EXTRNFAR        Bmp_Init
        EXTRNFAR        Clear_Kernel_Sem
        EXTRNFAR        DecVPBRef
        EXTRNFAR        DStrLen
        EXTRNFAR        FreeTempBuffer
        EXTRNFAR        FStrCpy
        EXTRNFAR        FStrCpy32
        EXTRNFAR        FuWord
        EXTRNFAR        GetTempBuffer
        EXTRNFAR        MetaMatch
        EXTRNFAR        PathDecVPBRef
        EXTRNFAR        PathIncVPBRef
        EXTRNFAR        ProbeBuff
        EXTRNFAR        ProcBlock
        EXTRNFAR        ProcRun
        EXTRNFAR        RlseBuffr
        EXTRNFAR        Rmp_Add
        EXTRNFAR        Rmp_Find
        EXTRNFAR        Rmp_FindN
        EXTRNFAR        Rmp_Alloc
        EXTRNFAR        Rmp_Rem
        EXTRNFAR        SegAlloc
        EXTRNFAR        SegFree
        EXTRNFAR        StrCpy
        EXTRNFAR        Take_Kernel_Sem
        EXTRNFAR        TransPath

        EXTRN32         CodeLockProc
        EXTRN32         CodeUnlockProc
        EXTRN32         _KSEMRequestMutex
        EXTRN32         _KSEMReleaseMutex

        ReferGlobal     CNotifySem,DWORD

                                        ;72417 removed PTDA entries now
                                        ;      in TCB

        ReferTask       CurrTCB,WORD    ; Current task control block

; The following is used as a count of ongoing FNY searches for this process.
; At process termination time, this is checked for nonzero to clear out the
; FNY records for that process.

        ReferTask       FNotifyLocal_Count,WORD ; used as a per process count

; This points to the first BMP FNY record for the current process. The FNY
; records are chained (forward link list) on a per-process basis through
; the FNY_Next field in the record.

        ReferTask       FNotifyLocal_First,WORD

        ReferTask       ptda_handle,WORD
        ReferTask       Pid,WORD

        public          _CNotifyGlobal_Count, CNotifyGlobal_Count

DATA segment

; The following two vars are used as a single structure by RMP
; procedures : they have to be in this order and SHOULD BE THERE.

FNotifyFATRmpHdl        dw          0   ; FindNotify rmp handle (flags)
FNotifyFATRMPSel        dw          0   ; FNY rmp selector (used by RMP
                                        ;    procedures)

; The following var is used as a counter  and the RMP index for each FAT
;       FNY search;  On overflow, we start from 0 again.

FNotifyFATHandle    dw      0       ;

; this variable keeps track of the no of active GLOBAL FAT findnotify searches.
; This is incremented whenever a new findnotify search is started successfully
; and decremented when it is closed. The wake routines look at this and
; return quickly if there is no ongoing search

FNotifyGlobal_Count     dw      0       ;

; The global BMP selector is stored here. The bmp records for each process
; are chained thro'the FNY_Next fields; The head of this list for each
; process is pointed to by TASKAREA:FNotifyLocal_First

FNotifyGlobal_Sel       dw      0       ;


; this variable keeps track of the no of active GLOBAL changenotify searches.
; This is incremented whenever a new changenotify search is started successfully
; and decremented when it is closed. The wake routines look at this and
; return quickly if there is no ongoing search

_CNotifyGlobal_Count    label   WORD    ; D150371
CNotifyGlobal_Count     dw      0       ;


; Selector for the Change Notify ring buffer

CNotifyRing_Sel         dw      0       ;

; The Head pointer for the Change Notify Ring Buffer, global.

CNotifyHeadPtr          dw      0       ;
CNotifyHeadLoop         dw      0       ;
CNotifyTailPtr          dw      0       ;
CNotifyTailLoop         dw      0       ;

CNotifyOpenCheck        dw      0       ;


DATA ends


        FARCODE HIGH4
FSSWAPCODE SEGMENT

        PUBLIC  _StartNOTIFYGROUP
_StartNOTIFYGROUP       label   byte

;***    w_FindNotifyClose - Terminate FindNotify Search.
;
;       w_FindNotifyClose releases the resources needed to keep track of
;       a particular FindNotify search.
;
;       ENTRY   BX      = DirHandle to close
;
;       EXIT    Carry Clear
;                       No error
;               Carry Set
;                       AX = ERROR_INVALID_HANDLE
;
;       CALLS   FNotify_ChkDirHandle,DOS_FindNotifyClose,FS_FindNotifyClose,
;               FNotify_FreeDirHandle
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       if (rc = FNotify_ChkDirHandle(DirHandle,&FNY_Global_Ptr) != 0)
;          return (rc);
;
;       handle = FNY_Global_Ptr->FNY_subsys_handle;
;
;       if FNY_Global_PTr->FNY_FSC.Offst = FSC_LOCAL
;          rc = DOS_FindNotifyClose(Handle)
;       else
;             if FNY_Global_Ptr->FNY_FSC.Offst < FSC_LOCAL
;                rc = FS_FINDNOTIFYCLOSE(handle);
;
;       if (rc == 0)
;          FNotify_FreeDirHandle(DirHandle,FNY_Global__Ptr);

Procedure w_FindNotifyClose,HYBRID
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar fnyc_DirHandle,WORD                    ; user DirHandle

        EnterProc <bx>

; Verify that the user passed in a valid DirHandle, and retrieve the subsystem
; handle.

        call    FNotify_ChkDirHandle                     ; Handle Valid ?
        jc      fnyc4                                    ; no, go return error

; es:di ->global BMP record

        mov     bx,es:[di].FNY_SubSys_Handle             ; BX =subsystem handle

; Route FindNotifyClose to appropriate subsystem

        push    es
        cmp     es:[di].FNY_FSC.Offst,FSC_LOCAL  ; FSD?
        jb      fnyc1                                    ; yes

; Call FAT FS FindNotifyClose routine

        call    DOS_FindNotifyClose
        jmp     short fnyc3

; Call FSD FindNotifyClose routine and set correct error setting

fnyc1:  push    bx
        les     di,es:[di].FNY_FSC               ; ES:[DI]-> FSC
        call    es:[di].FS_FINDNOTIFYCLOSE
        or      ax,ax                                    ; any errors ?
        jz      fnyc3                                    ; no, go free dirhnd
        stc                                              ; yes, set carry flag

fnyc3:  pop     es
        pushf
        push    ax
;
; remove the global references to this notify search, irrespective
; of success or failure of FSD_findnotifyclose
;
        mov     di,fnyc_DirHandle                        ; user DirHandle

        cmp     es:[di].FNY_hVPB,NULLVPB                 ; NULLVPB ?
        je      fnyc35                                   ; Yes, skip decvpbref
        push    bp
        mov     bp,es:[di].FNY_hVPB              ; BP = hVPB
        CALLFAR DecVPBRef                                ; Dec. reference count
        pop     bp

fnyc35: call    FNotify_FreeDirHandle
        CodeUnlock      NOTIFY_GROUP
        pop     ax
        popf

fnyc4:  LeaveProc
        ret

EndProc  w_FindNotifyClose

;***    w_FindNotifyNext - continue FindNotify search
;
;       w_FindNotifyNext continues searching, using the filename associated
;       with the given search handle.
;
;       ENTRY   DX:AX   =  TimeOut for Search
;               BX      =  DirHandle
;               CX      =  result buffer length
;               SI      =  # of changes required
;               ES:DI   -> result buffer
;
;       EXIT    Carry Clear
;                       AX = result count (# of entries actually found)
;               Carry Set
;                       AX = ERROR_INVALID_HANDLE
;                          = error_buffer_overflow
;
;       CALLS   Dos_FindNotifyNext,FS_FINDNOTIFYNEXT
;               FNotify_ChkDirHandle
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       if (rc = FNY_ChkDirHandle(DirHandle,&FNY_Ptr) != 0)
;          return (rc);
;
;       if FSC.Offst = FSC_LOCAL
;          rc = DOS_FindNotifyNext(FNY_Ptr,other parms)
;       else
;              rc= FS_FINDNOTIFYNEXT(handle, other parms);
;       return(rc);

Procedure w_FindNotifyNext,HYBRID
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar fnyn_DirHandle,WORD                    ; user DirHandle
LocalVar fnyn_ResultBufPtr,DWORD                ; Result Buffer pointer
LocalVar fnyn_ChangeCnt,WORD                    ; Change Count required

        EnterProc <bx,es,di,si>

;       Reasonable search count?

        or      si,si                                    ; 0 search results ?
        jz      fnyn5                                    ; yes, complain

; Verify that the user passed in a valid DirHandle

        call    FNotify_ChkDirHandle                    ; valid handle ?
        jc      fnyn4                                   ; no, return error

; es:di -> local bmp record

        mov     bx,es:[di].FNY_SubSys_Handle            ; BX = subsystem handle

; Route FindNotifyNext to appropriate subsystem

        cmp     es:[di].FNY_FSC.Offst,FSC_LOCAL  ; FSD ?
        jb      fnyn1                                   ; yes

; Call FAT FS FindNotifyNext routine

        push    fnyn_ResultBufPtr.Segmt                 ; Result buffer seg
        push    fnyn_ResultBufPtr.Offst                 ; Result buffer off
        call    DOS_FindNotifyNext
        jmp     short fnyn3

; Call FSd FindNotifyNext routine

; ES:DI   = FNotify Record
; BX      = FSD Generated handle
; CX      = Result Buffer Length
; DX:AX   = Timeout
; SS:SI   = Pointer to change count on stack

fnyn1:  push    bx                                      ; FSD generated handle
        push    fnyn_ResultBufPtr.Segmt                 ; Result buffer seg
        push    fnyn_ResultBufPtr.Offst                 ; Result buffer off
        push    cx                                      ; Result buffer len
        lea     si,fnyn_ChangeCnt
        push    ss                                      ; pcMatch
        push    si
        push    es:[di].FNY_InfoLevel                   ; InfoLevel
        push    dx                                      ; TimeOut
        push    ax
        les     di,es:[di].FNY_FSC                      ; ES:[DI]-> FSC
        call    es:[di].FS_FINDNOTIFYNEXT
        mov     cx,fnyn_ChangeCnt                       ; AX = #changes found
        or      ax,ax                                   ; any errors ?
        jz      fnyn3                                   ; no,
        stc

fnyn3:  LeaveProc
        ret

fnyn4:  mov     cx,0                                    ; no xor cx,cx
        jmp     short fnyn3

fnyn5:  mov     ax,ERROR_INVALID_PARAMETER
        stc
        jmp     short fnyn4

EndProc w_FindNotifyNext

;***    w_FindNotifyFirst - begin FindNotify search
;
;       w_FindNotifyFirst begins a FindNotify search.
;
;       ENTRY   CX      =  result buffer length
;               DX:AX   =  TimeOut for Search
;               SS:BX   -> parameter packet
;                                   (see FindNotifyFirstStruc).
;               DS:SI   -> ASCIIZ filename to search for.
;
;       EXIT    Carry Clear
;                       BX = DirHandle
;                       CX = number of entries found
;               Carry Set
;                       CX = 0 = number of entries found
;                       AX = error_invalid_parameter
;                          = error_path_not_found
;                          = error_buffer_overflow
;                          = error_too_many_findnotifyfirsts
;
;       CALLS   FuWord,FNotify_AllocDirHandle,TransPath,FSD_NotifyFirst
;               FNotify_FreeDirHanlde
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       if (FileInfoLevel != 1)
;          return (error_invalid_parameter);
;
;       if (Attribute specifies volume label)
;          return (error_invalid_parameter);
;
;       if (rc = FNotify_AllocDirHandle(&DirHandle)!=0)
;          return (rc);
;
;       if (rc = TransPath() !=0)
;          return (rc);
;
;       if (rc = FSD_FindNotifyFirst(&Handle)== 0)
;          if (rc = FNotify_AllocDirHandle(&DirHandle,Handle)!=0)
;              return (rc);
;
;       return (rc);
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_FindNotifyFirst,HYBRID
LocalVar        wfnyf_ppacket,WORD              ; parm. packet offset
LocalVar        wfnyf_TimeOut,DWORD             ; timeout
LocalVar        wfnyf_DirHandle,WORD            ; DirHandle
LocalVar        wfnyf_ChangeCnt,WORD            ; change count
LocalVar        wfnyf_openbuf,PMPATHLEN
LocalVar        wfnyf_dummy,WORD                ; KLUDGE: see gshare.asm
LocalVar        wfnyf_dummyCDS,<SIZE curdir>

        CodeLock        NOTIFY_GROUP,wfnyf1a
        EnterProc <bx,dx,ax>

        cmp     TASKAREA:[bx].FNYF_InfoLevel,FNOTIFY_INFOLEVEL1 ;Lev. correct ?
        LJNE    wfnyf5                                   ; no, report error

        push    bx                                      ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        lea     si,wfnyf_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,si            ; Initialize TCBpOpenBuf ;### PTR B720732
        lea     si,wfnyf_dummyCDS
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,si       ; Initialize TCBpTmpCDS ;### PTR B720732
        pop     bx                                      ;### PTR B720732

        SaveReg <bx>
        mov     si,TASKAREA:[bx].FNYF_ChangeCountPtr.Offst ; BX:SI-> chg count
        mov     bx,TASKAREA:[bx].FNYF_ChangeCountPtr.Segmt
        CALLFAR FuWord                                   ; AX = search count
        RestoreReg <bx>
        LJC     wfnyf6                                   ; if error return

;       Reasonable search count?

        or      ax,ax                                    ; 0 search results
        LJZ     wfnyf3                                   ; yes, complain

        mov     wfnyf_ChangeCnt,ax                       ; save change count

        mov     dx,TASKAREA:[bx].FNYF_Attribute          ; get attribute

;       Valid attribute word?


; check for valid attribute byte and reserved byte being zero
        test    dx,NOT (attr_all OR attr_read_only OR attr_archive)
        LJNZ    wfnyf3                                   ; no,

                                                         ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     [bp].TCBSattrib,dx
        UnSetBP_CurrTCB
                                                         ;72417 end

;       Convert user input path

        CALLFAR Take_Kernel_Sem                          ; grab Kernel sem
        SaveReg <es,di,bx,bp>
        lea     di,wfnyf_openbuf
        TaskContext ES                                   ; ES:[DI]-> WFP name
        lds     si,TASKAREA:[bx].FNYF_FileNamePtr        ; DS:[SI]-> user path
        CALLFAR TransPath
        RestoreReg <bp,bx,di,es>
        jc      wfnyf2                                   ; if error go return
;
; If this is a device, we return an error.
;
                                                         ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    TASKAREA:[bp].TCBPathNameType,PNType_LOCDEV+PNType_PSDEV
        UnSetBP_CurrTCB
                                                         ;72417 end
        LJNZ    wfnyf8                                   ; Device. Not allowed.

;       Allocate DirHandle

        call    FNotify_AllocDirHandle                   ; call alloc routine
        jc      wfnyf6j                                  ; if error go return
        mov     wfnyf_DirHandle,bx                       ; store DirHanlde
        mov     bx,wfnyf_ppacket                         ; BX = ParmPkt. Off.

; Store InfoLevel in Global BMP record

        mov     ax,TASKAREA:[bx].FNYF_InfoLevel         ; (AX) = InfoLevel
        mov     es:[di].FNY_InfoLevel,ax         ; save infolevel

        mov     es:[di].FNY_Flags,0              ;

; Store FSC pointer into Bmp record

        mov     si,CurrTCB
        mov     ax,TASKAREA:[si].TCBThisFSC.Offst        ; AX = FSC offset
        mov     es:[di].FNY_FSC.Offst,ax
        mov     ax,TASKAREA:[si].TCBThisFSC.Segmt        ; AX = FSC Segmt
        mov     es:[di].FNY_FSC.Segmt,ax

        Assume ES:NOTHING

        mov     ax,NULLVPB                               ; assume no local file
                                                         ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    TASKAREA:[bp].TCBPathNameType,PNType_LOCFILE + PNType_REMFILE
        UnSetBP_CurrTCB
                                                         ;72417 end
        jz      wfnyf0
        push    bp
        mov     bp,CurrTCB
        mov     ax,TASKAREA:[bp].TCBThishVPB             ; AX = hVPB
        pop     bp
wfnyf0: mov     es:[di].FNY_hVPB,ax
;
; 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.
;
        CALLFAR PathIncVPBRef                            ; Inc. reference count

        mov     ax,wfnyf_ChangeCnt                       ; AX = change count

;       Call FindNotifyFirst router to route request

        SaveReg <es,di>
        call    FSD_FindNotifyFirst                      ; ES:[DI]->Global BMP rec
        RestoreReg <di,es>
        jc      wfnyf4                                   ; if error go return

;       Store subsystem generated handle and return user DirHandle

        mov     es:[di].FNY_SubSys_Handle,bx             ; store subsys. handle
        mov     bx,wfnyf_DirHandle                       ; BX = user DirHandle

wfnyf1: jnc     wfnyx
        CodeUnlock      NOTIFY_GROUP
wfnyx:  LeaveProc
wfnyxa: ret

;       Translate error code

wfnyf1a: mov    cx,0                    ; (cx) = 0, does not clear carry
        jmp     short wfnyxa            ; exit, nothing on stack to clean up

wfnyf2: CALLFAR Clear_Kernel_Sem                ; release Kernel Semaphore
        cmp     ax,ERROR_FILENAME_EXCED_RANGE
        je      wfnyf6
        mov     ax, ERROR_PATH_NOT_FOUND
wfnyf6j: jmp    short wfnyf6

wfnyf3: mov     ax,ERROR_INVALID_PARAMETER
        jmp     short wfnyf6

;       Free DirHandle
; es:di -> global bmp rec

wfnyf4: SaveReg <ax,cx>
        call    FNotify_FreeDirHandle
        RestoreReg <cx,ax>
        CALLFAR PathDecVPBRef                   ; Decrement reference count
        jmp     short wfnyf7
;
; Invalid info level passed in
;
wfnyf5: mov     ax,ERROR_INVALID_LEVEL
wfnyf6: xor     cx,cx                           ; 0 entries found
wfnyf7: stc
        jmp     short wfnyf1
wfnyf8: CALLFAR Clear_Kernel_Sem                ; release Kernel Semaphore
        mov     ax,ERROR_FILE_NOT_FOUND
        jmp     short wfnyf6

EndProc   w_FindNotifyFirst

;***    FSD_FindNotifyFirst routes FindNotifyFirst request to subsystem.
;
;       FSD_FindNotifyFirst routes FindNotifyFirst request to subsystem.
;       FAT FS or FSD.
;
;       ENTRY   ES:DI   -> FNotify record
;               AX      =  Search Count
;               SS:BX   -> parameter packet
;                                   (see FindNotifyFirstStruc).
;       EXIT    Carry Clear
;                       BX = Subsystem Generated Handle
;                       AX = Number changes found
;               Carry Set
;                       AX = error code
;
;       CALLS   Dos_FindNotifyFirst,FS_FINDNOTIFYFIRST
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       if FSC.Offst = FSC_LOCAL
;          rc = DOS_FindNotifyFirst_Next(&Handle, other parms);
;       else
;            rc= FS_FINDNOTIFYFIRST(&Handle, other parms);

Procedure FSD_FindNotifyFirst,NEAR
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar fsdfnf_ChangeCnt,WORD                           ; Change count for FSD
LocalVar fsdfnf_Handle,WORD                              ; Handle space for FSD
LocalVar fsdfnf_curdirend,WORD                           ; CurrDirEnd

        EnterProc <ax>

                                                         ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     ax,[bp].TCBCurr_Dir_End
        cmp     ax,-1                                    ; is Curr_Dir_End set?
        je      fsdfnf1                                  ; no, use -1
        sub     ax,[bp].TCBWFP_Start                     ; CurrDirEnd-WFP_Start
fsdfnf1:TaskContext DS
        mov     si,[bp].TCBWFP_Start
        UnSetBP_CurrTCB
        mov     fsdfnf_curdirend,ax                      ; iCurDirEnd (160113 - from above
                                                         ;  need to set after BP restored)
                                                         ;72417 end
; BUGBUG - nagara
; We should use a SuBuffz here ? (we are copying into user buffer )
;
;       CALLFAR DStrLen                                  ; cx = size of WFP +1

        SaveReg <es,di>
        les     di,TASKAREA:[bx].FNYF_FileNamePtr        ; ES:DI-> temp.buffer
        CALLFAR FStrCpy                                 ; copy and don't uppercase
        RestoreReg <di,es>
; END BUGBUG

        mov     ax,fsdfnf_ChangeCnt
; Route FindNotifyFirst to appropriate subsystem

        cmp     es:[di].FNY_FSC.Offst,FSC_LOCAL   ; FSD?
        jb      fsdfnf2                                  ; yes go process

; Call FAT FS FindNotifyFirst routine

        mov     dx,es:[di].FNY_hVPB              ;
        call    DOS_FindNotifyFirst
        jmp     short fsdfnf4

; Call FSD FindNotifyFirst routine

fsdfnf2:CALLFAR Clear_Kernel_Sem                         ; release Kernel sem
        mov     si,TASKAREA:CurrTCB             ; (si) = current TCB     ;### PTR B720732
        lds     si,TASKAREA:[si].TCBThisCDS     ; (ds:si) = CDS pointer  ;### PTR B720732
        lea     ax,ds:[si].cd_fsd                        ; DS:AX->/DS depend.
        lea     si,ds:[si].cd_fsi                        ; DS:SI->CDS independ
        push    ds
        push    si
        push    ds
        push    ax
        push    TASKAREA:[bx].FNYF_FileNamePtr.Segmt     ; segment of WFP
        push    TASKAREA:[bx].FNYF_FileNamePtr.Offst     ; offset of WFP

; push index to end of current directory

        push    fsdfnf_curdirend                         ; iCurDirEnd
        mov     ax,TASKAREA:[bx].FNYF_Attribute          ; attribute
        push    bx
        mov     bx,CurrTCB
        test    TASKAREA:[bx].TCBNewFlags,TCBNEWFILES ; newfiles?
        pop     bx
        jz      fsdfnf3                   ; no, leave attribute alone
        or      al,attr_newfiles
fsdfnf3: push  ax
        lea     ax,fsdfnf_Handle                         ;
        push    ss                                       ;
        push    ax                                       ; SS:AX->handle ptr
        push    TASKAREA:[bx].FNYF_RsltBufPtr.Segmt
        push    TASKAREA:[bx].FNYF_RsltBufPtr.Offst
        push    TASKAREA:[bx].FNYF_RsltBufLen
        lea     ax,fsdfnf_ChangeCnt
        push    ss
        push    ax                                       ; SS:AX->ChangeCnt
        push    es:[di].FNY_InfoLevel             ; InfoLevel
        push    TASKAREA:[bx].FNYF_TimeOut.HighWord      ; TimeOut Highword
        push    TASKAREA:[bx].FNYF_TimeOut.LowWord       ; TImeOut LowWord
        les     di,es:[di].FNY_FSC                ; ES:DI->FSC
        call    es:[di].FS_FINDNOTIFYFIRST               ; Call FSD
        mov     bx,fsdfnf_Handle                         ; BX = FSD handle
        mov     cx,fsdfnf_ChangeCnt                      ; CX = Change Count
        or      ax,ax                                    ; any errors ?
        jnz     fsdfnf5                                  ; yes, go return

fsdfnf4:LeaveProc
        ret
fsdfnf5:stc
        jmp     short fsdfnf4

EndProc   FSD_FindNotifyFirst

;***    DOS_FindNotifyClose terminate FindNotify search.
;
;       DOS_FindNotifyClose terminates FindNotify search by removing the
;       Rmp record from the segment.
;
;       ENTRY   BX      =  Handle
;               ES:DI   = ptr to local BMP record
;
;       EXIT    Carry Clear
;                       no error
;               Carry Set
;                       AX = error code
;
;       CALLS   Rmp_Find,Rmp_Rem
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;label_a: if (a FNYNext is in progress)
;               ProcBlock (BMP Sel:Handle)
;       if (rc = Rmp_Find(Handle,&FNY_Ptr) != 0)
;          return (ERROR_INVALID_HANDLE);
;       if (a FNYNext is blocked on this handle)
;         {     ProcRun(RMP Sel: Handle)
;               goto lable_a;
;         }
;       rc = Rmp_Rem(FNY_Ptr);
;       return (rc);
;
Procedure DOS_FindNotifyClose,NEAR
Assume DS:Nothing,ES:Nothing,SS:TASKAREA

LocalVar        dfnyc_BMP_Ptr,DWORD  ;
LocalVar        dfnyc_runthread,WORD    ;
LocalVar        dfnyc_Handle,WORD
        EnterProc       <es,di,0,bx>

        test    es:[di].FNY_Flags,fFNY_CLOSED
                                        ; has somebody overtaken us ?
        jnz     dfnyc20                 ; sanity check; return success
        or      es:[di].FNY_Flags,fFNY_CLOSED; mark this handle as closed
dfnyc10: les    di,dfnyc_BMP_Ptr             ;
        mov     cl, es:[di].FNY_Flags
        test    cl,fFNY_IN_PROG                 ; is any fnynext thread in prog
        jnz     dfnyc60                         ; if so block
        xor     si,si                           ;
        test    cl,fFNY_INUSE                   ; is any FNYNext thread sleeping
        jz      dfnyc15                         ; if so, run it
        inc     si                              ; we have to do rmpfind and then
                                                ; go for a procrun
dfnyc15: mov    dfnyc_runthread,si              ;
        lea     si,dfnyc_Handle                 ;
        TaskContext DS                          ; DS:SI-> search element
        call    Find_RMPFNYRec                  ; call rmp_find to get rmprec
        Assume  ES:NOTHING
        jc      dfnyc30                         ; if any errors go return
        cmp     dfnyc_runthread,0               ; is any thread blocked on
        jnz     dfnyc50                         ; this handle
        DosContext ds
        dec     FNotifyGlobal_Count             ;
        mov     bx,offset DOSGROUP:FNotifyFATRmpHdl ; DS:BX-> = addr of RMP handle
        CALLFAR Rmp_Rem
        Assume  DS:NOTHING
        clc
dfnyc20: LeaveProc
        ret
dfnyc30: mov    ax,ERROR_INVALID_HANDLE
        jmp     short dfnyc20

; a FINDNOTIFYNext thread is blocked waiting for changes to happen.
; let us wake him up

dfnyc50: mov    ax,es
        mov     bx,es:[di].FNY_FAT_Handle          ; AX:BX = Block ID.
        CALLFAR ProcRun

        mov     bx,dfnyc_Handle

; a FindnotifyNext thread is blocked - let this thread also block till
; that thread realises that this handle is closed and runs us.
; bx = dir handle

dfnyc60: mov    ax,dfnyc_BMP_Ptr.Segmt          ; BX:AX = Block ID
        mov     cx,-1                           ;
        mov     di,cx                           ; DI:CX = -1
        xor     dh,dh                           ; block is interruptible
        cli

; ProcBlock on GLOBAL BMP sel and the Handle

        CALLFAR ProcBlock
        jnc     dfnyc10
        jz      dfnyc10
        mov     ax,ERROR_INTERRUPT              ;
        jmp     dfnyc20

EndProc DOS_FindNotifyClose

;***    DOS_FindNotifyFirst Verify Parameters and Wait for Notification
;
;       DOS_FindNotifyFirst verfies that the path exists and allocates a
;       FAT FS specific handle.
;
;       ENTRY   SS:BX   -> FindNotifyFirst parameter packet
;               AX      =  Change Count
;               ES:DI   = ptr to local BMP record
;               DX      = hVPB
;               [WFP_Start] Points to WFP string (For a device, "\DEV\" must
;                       be first 5 chars; for a file or directory "d:\" must
;                       be first 3 chars.) NULL terminated.
;               [CURR_DIR_END] Points to end of Current dir part of string
;                       ( = -1 if current dir not involved, else
;                        Points to first char after last "\" of current
;                        dir part)
;               [WFP_Path_End] Points to end of path part of string
;                        Points to first char after last "\" of entire string.
;               [ThisCDS] Points to CDS being used
;               [Sattrib] Is attribute of search, determines what files can
;                         be found.
;       EXIT    Carry Clear
;                       BX = Handle
;                       CX = Change Count
;               Carry Set
;                       AX = error code
;                       CX = Change Count
;
;       CALLS   GetPath,RlseBuffr,FNotify_AllocFATHandle,DStrLen,Rmp_Alloc,
;               Rmp_Add,DOS_FindNotifyNext,Rmp_Rem,SegAlloc,fValidFATDisk
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       if (!fValidFATDisk)
;           return (not_dos_disk);
;       if (rc = GetPath() !=0)
;          return (rc);
;
;       if (rc = FNotify_AllocFATHandle(&Handle)!=0)
;          return (rc);
;
;       TASKAREA:DummyFNY->FNY_FAT_WFP = TASKAREA:WFP_Start->name;
;       TASKAREA:DummyFNY->FNY_FAT_Handle = Handle;
;       TASKAREA:DummyFNY->FNY_FAT_Attribute = Attribute;
;
;       if (rc = Rmp_Add(TASKAREA:DummyFNY,&FNY_FAT_Ptr) != 0)
;          return (rc);
;
;       if (rc = DOS_FindNotifyNext() !=0)
;          rc = Rmp_Rem(FNY_FAT_Ptr)
;
;       return (rc);
;
Procedure DOS_FindNotifyFirst,NEAR
Assume DS:Nothing,ES:Nothing,SS:TASKAREA

LocalVar dfnyf_ParmPktOff,WORD                  ; parameter packet offset
LocalVar dfnyf_ChangeCnt,WORD                   ; change count required
LocalVar dfnyf_BMP_Ptr,DWORD                    ; ptr to local bmp record
LocalVar dfnyf_hVPB,WORD                        ; hVPB
LocalVar dfnyf_Handle,WORD                      ; FAT FS Handle
LocalVar dfnyf_StringLen,WORD                   ; Length of string
LocalVar dfnyf_TempSegAlloc,WORD                ; Temporary seg alloc
LocalVar dfnyf_FATRMPPtr,DWORD                  ; ptr to rmp record

        EnterProc <bx,ax,es,di,dx>

; Determine if volume is valid for FAT FS.

        call fValidFATDisk                      ; Valid for FAT?
        LJZ     dfnyf6                          ; No. AX = error code. Return error.

; Determine if path exists by calling GetPath

                                                ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     [bp].TCBNoSetDir,1              ; don't change to it
        UnSetBP_CurrTCB
                                                ;72417 end
        SaveReg <bp>                            ; Getpath destroys BP
        call GetPath                    ; see if path exists
        RestoreReg <bp>
        LJC     dfnyf3                          ; if error get correct error

        CALLFAR RlseBuffr                       ; release buffer

; Allocate FAT FS Handle

        call    FNotify_AllocFatHandle          ; Allocate FAT FS Handle
        LJC     dfnyf5                          ; if error go return
        mov     dfnyf_Handle,bx                 ; save FAT FS Handle

; Get length of name and save it

        TaskContext DS
                                                ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start   ; DS:SI->WFP
                                                ;72417 end
        CALLFAR DStrLen
        Assume DS:NOTHING
        mov     dfnyf_StringLen,cx              ; save length of name
        add     cx,FNY_FAT_WFP                  ; CX = total size of record
        ASSUME  DS:NOTHING

; Allocate Temporary Selector


        xor     ax,ax
        mov     bx, cx                          ; AX:BX = size to allocate
        mov     cx, (D_DATA0 SHL 8) + SA_GDT    ; CX = Access & Allocation flg
        .ERRNZ  HF_MOVABLE
        xor     si, si
        mov     dx,[ptda_handle]                ; DX = pdta handle
        mov     di,FSFNOTIFYGOWNER              ; DI = ptda handle = owner

; DX    = handle to PTDA
; DI    = owner of segment
; AX:BX = Size of segment
; CX    = Allocation and Access Flags
; SI    = Handle Memory Allocation Flags

        CALLFAR SegAlloc                        ; Allocate GDT ring 0

        LJC     dfnyf5                          ; If error go return


        mov     dfnyf_TempSegAlloc,bx           ; Set Temp.seg alloc
        mov     es,bx

        xor     di,di                           ; ES:DI -> Temporary space

; Initialize FAT Rmp structure

        mov     ax,dfnyf_Handle                 ; AX = FAT FS Handle
        mov     es:[di].FNY_FAT_Handle,ax       ; store FAT Handle
        mov     bx,dfnyf_ParmPktOff             ; SS:BX-> User parameters
        mov     ax,TASKAREA:[bx].FNYF_Attribute ; AX = attribute
        mov     es:[di].FNY_FAT_Attribute,ax    ; store store attribute
        mov     ax,dfnyf_hVPB            ;
        mov     es:[di].FNY_FAT_hVPB,ax         ; store hvpb
        xor     ax,ax
        mov     es:[di].FNY_FAT_WrkChangeCnt,ax ;
        mov     es:[di].FNY_FAT_ChangeCnt,ax    ;
; find out the ptr to last component that does not have a wild char
        xor     ax,ax                           ;
                                                ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1                      ; no wild chars in path ?
        je      dfny0a                          ;
        mov     ax,[bp].TCBWFP_Path_End         ;
        sub     ax,[bp].TCBWFP_Start            ;
        dec     ax
dfny0a: mov     es:[di].FNY_FAT_WILDPTR,ax      ;
        UnSetBP_CurrTCB
                                                ;72417 end
        mov     cx,dfnyf_StringLen              ; CX = length of name
        mov     ax,cx
        dec     ax                              ; don't count null
        mov     es:[di].FNY_FAT_WFPLEN,ax       ;

; Copy Name
        mov     si,dfnyf_ParmPktOff             ;
        lds     si,TASKAREA:[si].FNYF_FileNamePtr   ;ds:si -> given search name

        SaveReg <di>
        lea     di,es:[di].FNY_FAT_WFP          ; ES:DI->WFP
        CALLFAR StrCpy                  ; copy and uppercase WFP
        RestoreReg <si>

        push    es
        pop     ds                              ; DS:SI->Private Rmp record

; If FAT RMP segment does not exist call Rmp_Alloc to allocate and initialize
; it and then call Rmp_Add to add the record.

        DosContext es
        mov     di,offset FNotifyFATRmpHdl      ; ES:DI->FAT FS Rmp Handle
        cmp     es:[di].hiword,0                ; RMP segment allocated ?
        jne     dfnyf1                          ; yes, no need to allocate one

        SaveReg <ds,si>
        mov     ax,FSFNOTIFYPOWNER              ; (ax) = hobowner
        xor     bx,bx                           ; (bx) = hobmte
        mov     esi,PG_RESIDENT                 ; (esi) = alloc flags
        CALLFAR Rmp_Alloc
        RestoreReg <si,ds>
        jnc     dfnyf1                          ; if error go return
        assume ds:nothing,es:nothing

        push    ax
        mov     ax,dfnyf_TempSegAlloc           ; AX =  Temp. GDT selector
        CALLFAR SegFree                         ; Free GDT ring 0
        pop     ax
        stc
        jmp     dfnyf5

; Add the Record
; DS:SI -> Temp Buffer that contains record
; CX    =  Size of record to add
; ES:DI -> FAT FS Rmp Handle

dfnyf1: mov     cx,dfnyf_StringLen              ; CX = length of name
        add     cx,FNY_FAT_WFP                  ; CX = total size of record
        CALLFAR Rmp_Add

        SaveReg <es,di,ax>
        pushf
        mov     ax,dfnyf_TempSegAlloc           ; AX =  Temp. GDT selector
        CALLFAR SegFree                         ; Free GDT ring 0
        popf
        RestoreReg <ax,di,es>

; Note we are checking the error code from Rmp_Add not SegFree

        jc      dfnyf5                          ; if error go return

        CALLFAR Clear_Kernel_Sem                ; release Kernel Semaphore

        mov     dfnyf_FATRMPPtr.Segmt,es        ; store rmp record ptr
        mov     dfnyf_FATRMPPtr.Offst,di        ;  for removing later
; Get parameters into registers and onto stack for DOS_FindNotifyNext

        mov     bx,dfnyf_Handle                 ; bx = FAT FS Handle
        mov     di,dfnyf_ParmPktOff             ; SS:DI-> user parameters
        mov     si,dfnyf_ChangeCnt              ; si = # of changes required
        mov     cx,TASKAREA:[di].FNYF_RsltBufLen   ; cx = result buffer length
        mov     dx,TASKAREA:[di].FNYF_TimeOut.HighWord
        mov     ax,TASKAREA:[di].FNYF_TimeOut.LowWord ; DX:AX = Timeout
        push    TASKAREA:[di].FNYF_RsltBufPtr.Segmt   ; segment of result buf.
        push    TASKAREA:[di].FNYF_RsltBufPtr.Offst   ; offset of result buffer

        les     di,dfnyf_BMP_Ptr             ;

; BX      = FAT Generated handle
; CX      = Result Buffer Length
; SI      = Change Count required
; DX:AX   = Timeout
; SS:SP   = Result Buffer pointer
; ES:DI   = ptr to bmp record

        DosContext ds
        inc FNotifyGlobal_Count                 ; one more active FAT FNY.

        call    DOS_FindNotifyNext              ; valdate buffer and wait.
        jc      dfnyf4                          ; if error remove record
dfnyf2: LeaveProc
        ret

dfnyf3: mov     ax,ERROR_PATH_NOT_FOUND         ; assume path not found
        jnz     dfnyf5                          ; assume correct go return
        or      cl,cl
        stc                                     ; or may clear carry
        jz      dfnyf5
        mov     ax,ERROR_FILE_NOT_FOUND         ; set file not found error
        jmp     short dfnyf5                    ; go return error

dfnyf4: SaveReg <ax,cx>                         ; save error code, change cnt
        lea     si,dfnyf_Handle                 ;
        TaskContext     ds
        call    Find_RMPFNYRec                  ; get rmp fat rec ptr
        DosContext ds
        dec FNotifyGlobal_Count                 ; retract, since we failed
        jc      dfny4a                          ; no record; skip remove op
        mov     bx,offset DOSGROUP:FNotifyFATRmpHdl ; DS:BX->FAT FS Rmp Handle
        CALLFAR Rmp_Rem                         ; go remove record
dfny4a: RestoreReg <cx,ax>                      ; rest. errorcode, change cnt
        stc
        jmp     short dfnyf2

dfnyf5: xor     cx,cx                           ; 0 entries found
        CALLFAR Clear_Kernel_Sem                ; release Kernel Semaphore
dfnyf6: stc
        jmp     short dfnyf2

EndProc DOS_FindNotifyFirst

;***    DOS_FindNotifyNext Verify Parameters and Wait for Notification
;
;       DOS_FindNotifyNext verfies that the change count is not zero
;       and that ResultBuffer is writable and is as large as ResultBufLen. We
;       the ProcBlock using the requested TimeOut waiting on notification.
;
;       ENTRY   SS:SP+2 -> ResultBufPtr
;               CX      =  ResultBufLen
;               DX:AX   =  TimeOut for Search
;               SI      =  ChangeCount
;               BX      =  Handle
;               ES:DI   =  ptr to BMP record
;
;       EXIT    Carry Clear
;                       BX = FAT FS Handle
;                       CX = Number of entries found
;               Carry Set
;                       AX = error code
;                       CX = Number of entries found
;
;       CALLS   ProbeBuff,ProcBlock,Rmp_Find,ProcRun
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       If (Vol changed or handle in use) return proper error code;
;
;       FNY_Ptr->FNY_FAT_ChangeCnt = ChangeCnt;
;
;       if (rc = ProbeBuff(ResultBufPtr,ResultBufLen,writeacess) !=0)
;         return (rc);
;
;WakeClose:
;       if (handle is closed)
;       {   wake up close thread;
;           return (error_handle_closed)
;       }
;       if (requested changes already happened)
;               return with no of changes               (SUCCESS)
;
;       rc = ProcBlock(TimeOut);
;       if (Vol changed) return (error_vol_changed);
;       if (handle closed) go to WakeClose
;       return (rc);
;
Procedure DOS_FindNotifyNext,NEAR
Assume DS:Nothing,ES:Nothing,SS:TASKAREA

ArgVar    dfnyn_ResultBufPtr,DWORD              ; result buffer pointer

LocalVar  dfnyn_BMP_Ptr,DWORD                   ; ptr to local bmp rec
LocalVar  dfnyn_Handle,WORD                     ; FAT FS Rmp Handle
LocalVar  dfnyn_TimeOut,DWORD                   ; Timeout from user

        EnterProc <es,di,bx,dx,ax>

        test    es:[di].FNY_Flags,fFNY_CLOSED    ; is this handle already
                                                        ; closed ?
        LJNZ    dfny150
        test    es:[di.FNY_Flags],fFNY_INUSE OR fFNY_IN_PROG
                                        ; another thread sleeping
        LJNZ    dfny100                 ; on this handle? if so quit immediate.
        mov     es:[di.FNY_Flags],fFNY_IN_PROG
                                ; set the in-progress flag so
                                ; that no other findnotify thread can get in
; Validate result buffer by calling Probebuf. In this release of OS/2 we return
; no information in Result Buffer but we still have to verify the buffer.

        mov     bx,dfnyn_ResultBufPtr.Segmt
        mov     di,dfnyn_ResultBufPtr.Offst     ; BX:DI-> Result Buffer

        SaveReg <es,si>

        or      cx,cx
        jz      dfny10                          ; 0 Length buffer is OK

        mov     si,di                           ; BX:SI-> Result Buffer
        mov     dh,ACCTYPE_RW                   ; READ/WRITE ACCESS
        CALLFAR ProbeBuff
        xor     cx,cx                           ; no changes happened
                                                ;in this case
dfny10: RestoreReg <si,es>

        LJC     dfny125                         ; if error go return
;
; Find record in Rmp segment
;
dfny30: SaveReg <si>
        TaskContext DS                          ;
        lea     si,dfnyn_Handle                 ; DS:SI->handle on stack
        call    Find_RMPFNYRec                  ; call rmp_find to get rmprec
        RestoreReg <si>
        LJC     dfnyn_ie                        ; if error go complain
        SaveReg <es,di>                         ;
        les     di,dfnyn_BMP_Ptr             ;
        test    es:[di].FNY_Flags,fFNY_CLOSED; handle closed already?
        jnz     dfny40                          ;
        mov     es:[di].FNY_Flags,fFNY_INUSE     ;
dfny40: RestoreReg      <di,es>                 ; get back FAT RMP ptr
        mov     cx,si                           ; requested count in cx
        LJNZ    dfny137                         ; go report handle closed
        cmp     es:[di].FNY_FAT_WILDPTR,fFNY_OBJ_REMOVED
                                                ; FNY Object removed ?
        LJE     dfny145
        cmp     es:[di].FNY_FAT_hVPB,fFNY_INVALID_hVPB  ;
        je      dfny90                  ; vol changed - go report so
; Save Change Change count in Rmp Record

        cmp     es:[di].FNY_FAT_WrkChangeCnt,cx ; requested changes already over
        jb      dfny50
        pushf                                   ; to offset the popf later
        jmp     short dfny60

dfny50: mov     es:[di].FNY_FAT_ChangeCnt,cx    ; cx = # of changes required

; ProcBlock on FAT RMP sel and the Handle

        mov     ax,es
        mov     bx,es:[di].FNY_FAT_Handle       ; BX:AX = Block ID
        mov     di,dfnyn_TimeOut.hiword
        mov     CX,dfnyn_TimeOut.loword         ; DI:CX = Timeout
        xor     dh,dh                           ; block is interruptible
        cli
        CALLFAR ProcBlock
        pushf

; Find record in Rmp segment

dfny55: SaveReg <si,bx>
        TaskContext DS                          ;
        lea     si,dfnyn_Handle                 ; DS:SI->handle on stack
        call    Find_RMPFNYRec                  ; call rmp_find to get rmprec
        RestoreReg <bx,cx>                      ;
        jc      dfnyn_ie                        ; if error go complain

; Determine Number of entries found
; cx = requested no of changes

dfny60: cmp     cx,es:[di].FNY_FAT_WrkChangeCnt ;
        jbe     dfny70
        mov     cx,es:[di].FNY_FAT_WrkChangeCnt ; CX = number of entries fnd.
dfny70: sub     es:[di].FNY_FAT_WrkChangeCnt,cx ;
        cmp     es:[di].FNY_FAT_hVPB,fFNY_INVALID_hVPB  ;
        je      dfny80                  ; vol changed - go report so
        cmp     es:[di].FNY_FAT_WILDPTR,fFNY_OBJ_REMOVED
                                        ; object removed ?
        je      dfny140                         ;
        les     di,dfnyn_BMP_Ptr             ;
        test    es:[di].FNY_Flags,fFNY_CLOSED    ; is this handle closed
        jnz     dfny130                         ; go wake up Close thread
        popf
        jnc     dfny120                         ; if no error return
        mov     ax,ERROR_INTERRUPT              ; error assume interrupted
        jnz     dfny120                         ; assume correct go return
        mov     ax,ERROR_FINDNOTIFY_TIMEOUT     ; FindNotify Time Out
        jmp     short dfny120

dfny80: pop     ax                              ; forget flags
dfny90: mov     ax,ERROR_VOLUME_CHANGED         ;
        jmp     short dfny110

dfny100: mov    ax,ERROR_FINDNOTIFY_HANDLE_IN_USE
dfny110: stc
dfny120: les    di,dfnyn_BMP_Ptr             ;
        pushf                   ; save and restore flags
        and     es:[di].FNY_Flags,NOT(fFNY_INUSE + fFNY_IN_PROG);
                                ; reset in use and in progress flags
        popf                    ;
        LeaveProc
        ret     ?aframe         ; clean args off stack

dfny125: mov    cx,0            ; nothing found! We might have
        jmp     dfny120         ; seen some changes but it does not matter
                                ; anymore since we have an error here.

dfny130: pop    ax              ; forget flags
        mov     ax,dfnyn_BMP_Ptr.Segmt       ;
        mov     bx,dfnyn_Handle                 ;AX:BX = block ID
        CALLFAR ProcRun                         ; run the FNY close thread
dfny135: mov    ax,ERROR_FINDNOTIFY_HANDLE_CLOSED       ;
        jmp     dfny110                         ;

dfny137: pushf                  ; to offset popf later; ALSO NO CY HERE
        jmp     dfny60

dfny140: pop    ax              ; forget flags
dfny145: mov    ax,ERROR_NOTIFY_OBJECT_REMOVED
        jmp     dfny110

dfny150: xor    cx,cx
        jmp     dfny135

dfnyn_ie: les   di,dfnyn_BMP_Ptr             ;
        test    es:[di].FNY_Flags,fFNY_CLOSED ; is it closed ?
        jz      dfnyn_ie0
        pop     ax              ; forget flags
        xor     cx,cx           ; no changes happened
        jmp     dfny135
dfnyn_ie0: InternalError         <DOS_FindNotifyNext:Rmp_Find failed>

EndProc DOS_FindNotifyNext

;***    FNotify_AllocDirHandle - get a new DirHandle
;
;       FNotify_AllocDirHandle get a new Handle
;
;       ENTRY None
;       EXIT
;          Carry Clear
;             BX = DirHandle
;             ES:[DI] = Global BMP record
;          Carry Set
;             error
;       CALLS   SegAlloc,Bmp_Init,SegAlloc,Bmp_Get,Bmp_Free
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       if (DosContext:FNotifyGlobalSel == 0)
;          if (rc = GDTAlloc(&DosContext:FNotifyGlobal_Sel) != 0)
;             return (rc);
;
;       if (rc =Bmp_Get(DosContext:FNotifyGlobal_Sel,&FNotifyGlobal_Ptr)!=0)
;          return (rc);
;
;
;       /* Put global BMP record on the Chain          */
;
;       Temp = DosContext:FNotifyGlobalFirst;
;       TASKAREA:FNotifyLocalFirst = FNotifyLocal_Ptr.Offst;
;       FNotifyLocal_Ptr.Offst = temp;
;
;       DirHandle = FNotifyLocal_Ptr.Offst;
;
;

Procedure FNotify_AllocDirHandle,NEAR
Assume DS:Nothing,ES:Nothing,SS:TASKAREA

; If the system does not have a FindNotify segment allocated, call
; SegAlloc to allocate one and save its selector in DOSHIGHCONTEXT

        DosContext ds

        mov     ax,FNotifyGlobal_Sel            ; AX = Gloabl FindNotify Sel.
        or      ax,ax                           ; Global Selector allocated ?
        jnz     fnya0                           ; yes, no need to allocate one

        mov     bx, Bmp_SegSize                 ; AX:BX = size of BMP segment
        mov     cx, (D_DATA0 SHL 8) + SA_GDT    ; CX = Access & Allocation flg
        xor     si, si
        .errnz  HF_MOVABLE
        xor     dx,dx                           ; cuurent PTDA not needed thou
        mov     di,FSFNOTIFYGOWNER              ; FindNotify owner of segment

; DI    = owner of segment
; AX:BX = Size of segment
; CX    = Allocation and Access Flags
; SI    = Handle Memory Allocation Flags

        push    ds
        CALLFAR SegAlloc                        ; allocate GDT ring 0
        pop     ds

        jc      fnya2                           ; if error go return
        mov     word ptr [FNotifyGlobal_Sel],bx ; save global selector

;
; Initialize the segment to be a BMP managed segment.
;
;       ax = segment handle
;       bx = segment selector
;       cx = block size
;       dx = grow delta (in blocks)
;

        mov     cx, SIZE FNY_GLOBAL             ; CX = size of structure
        mov     dx, FNOTIFY_GROWDELT            ; DX = grow delta
        CALLFAR Bmp_Init                        ; Initialize Bmp segment
        LJC     fnya2                           ; if error go return
        mov     ax,es                           ;

; Get a Global Bmp record into ES:DI by calling Bmp_Get
;; AX = Global FindNotify Sel

fnya0:  mov     es,ax                           ;
        CALLFAR Bmp_Get                         ; ES:DI-> Global Bmp record
        jc      fnya2                           ; if error go return

; Put Global BMP record on local Chain

        mov     ax,di                                   ; ax = new local bmp head
        xchg    ax,FNotifyLocal_First; ax = old local bmp head
        mov     es:[di].FNY_Next,ax             ;put old first after current

        inc     TASKAREA:FNotifyLocal_Count     ; use this as counter
        mov     ax,Pid
        mov     es:[di].FNY_PID,ax              ;
        mov     bx,di                           ; BX = user DirHandle

fnya2:  ret

EndProc FNotify_AllocDirHandle

;***    FNotify_FreeDirHandle - Free a Global DirHandle
;
;       FNotify_FreeDirHandle Free a Global Handle
;
;       ENTRY ES:DI -> (bmp rec)  Ptr
;                       (note that the FNY handle is the same as the offset to BMP rec)
;       EXIT
;          Carry Clear
;             no error
;          Carry Set
;             error
;       CALLS   Bmp_Free
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       Bmp_Free(DosContext:FNotifyGlobal_Sel,DirHandle)
;
;       Take Global BMP record off the Chain
;
Procedure FNotify_FreeDirHandle,NEAR
Assume DS:Nothing,ES:Nothing,SS:TASKAREA


        push    di

; Take Global BMP record off the Chain, Start by finding previous record

        DosContext ds
        mov     es,FNotifyGlobal_Sel

        mov     ax,ss
        lea     si,TASKAREA:FNotifyLocal_First
        mov     bx,TASKAREA:[si]        ; SS:SI->First local record
        ASSUME DS:NOTHING

fnyf1:  or      bx,bx             ; end of chain ?
        jz      fnyf2                     ; yes, exit loop
        cmp     bx,di                     ; found record ?
        je      fnyf2                     ; yes, exit loop
        mov     si,bx                     ; ax:si-> previous local record
        mov     ax,es                     ; only 2 clocks inside of loop
        mov     bx,es:[bx].FNY_Next ; next local Bmp record offset
        jmp     short fnyf1

fnyf2:  mov     ds,ax                     ; ds:si-> previous local record
        mov     ax,es:[di].FNY_Next ; ax = next local record
        mov     ds:[si],ax                ; unlink current local record


        dec     TASKAREA:FNotifyLocal_Count     ;
        pop     di          ; ES:DI->Global Bmp record
        mov     es:[di].FNY_PID,0               ; invalid process id

; Free Global Bmp record

        CALLFAR Bmp_Free
        jc      fnyf_ie

        ret

fnyf_ie:InternalError   <FNotify_FreeDirHandle:Bmp_free failed>

EndProc   FNotify_FreeDirHandle

;***    FNotify_AllocFATHandle - get a new Handle for Fat FS
;
;       FNotify_AllocFATHandle get a new Handle for Fat FS
;
;       ENTRY none
;       EXIT
;          Carry Clear
;             BX = Handle
;          Carry Set
;             error
;
;       CALLS Rmp_Find
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       continue = true;
;       while (continue) {
;
;           NewHandle = HandleCounter++;
;
;          if (!HandleCounter)   HandleCounter++;
;
;          if (rc = Rmp_Find(NewHandle) != 0) {
;             DirHandle = NewHandle;
;             return (no error);
;          }
;       }
;
Procedure FNotify_AllocFatHandle,NEAR
Assume DS:Nothing,ES:Nothing,SS:TASKAREA

        DosContext ds
fnyaf1: inc     FNotifyFATHandle        ; handle to try
        jnz     fnyaf2
        inc     FNotifyFATHandle        ; make handle wrap to 1
fnyaf2: mov     bx,FNotifyFATHandle
        Assume DS:NOTHING
        push    bx                      ; push handle on stack for Rmp_Find
        mov     si,sp
        push    ds
        TaskContext DS                  ; DS:SI-> handle on stack
        call    Find_RMPFNYRec                  ; call rmp_find to get rmprec
        Assume ES:NOTHING,DS:NOTHING
        pop     ds
        pop     bx                      ; restore FAT FS Handle
        jnc     fnyaf1
        clc
        ret
EndProc FNotify_AllocFatHandle

;***    FNotify_ChkDirHandle - Verify DirHandle
;
;       FNotify_ChkDirHandle verifies that the associated entry for that
;       Dirhandle is valid (i.e. A FindNotifyFirst has been issued).
;
;       We go through the per process FNotify bmp record chain looking
;       for the given handle.
;
;       ENTRY  BX = DirHandle
;
;       EXIT
;              Carry Clear
;                 ES:DI -> Global BMP Record
;              Carry Set
;                 AX = ERROR_INVALID_HANDLE
;
;       CALLS  NONE
;
;       USES    DI,DS,ES,FLAGS
;                 (AX is used only in case of error)
;
;       PSEUDOCODE:
;
;       if (TASKAREA:FNotifyLocal_First = 0)
;          return (ERROR_INVALID_HANDLE);
;
;       TempHdlPtr = FNotifyGlobal_Sel:FNotifyLocal_First]
;
;       compare the process id with the current process id for the given
;               handle  (double checking)
;       if (no match)
;               return (ERROR_INVALID_HANDLE);
;       else return the bmp record ptr

Procedure FNotify_ChkDirHandle,NEAR
Assume DS:Nothing,ES:Nothing,SS:TASKAREA

; if there is no global fny segment, then there is no ongoing fny search and
; so the handle is invalid

        push    ax
        cmp     TASKAREA:FNotifyLocal_First,0   ; are there any fny search handles
                                                        ; for this process ?
        je      fnyck2

        DosContext ds
        mov     es,FNotifyGlobal_Sel
        mov     di,FNotifyLocal_First      ;
fnyckdl: or     di,di                      ; END OF ALL RECORDS, INVALID HANDLE
        jz      fnyck2                     ;
        cmp     di,bx                      ;
        je      fnyck1
        mov     di,es:[di].FNY_Next        ;
        jmp     short fnyckdl              ;

; We found a record for the given handle.
; check to make sure that the found handle belongs to the current process

fnyck1: mov     ax,Pid                          ;
        cmp     es:[di].FNY_PID,ax         ;
        jne     fnyck2

fnyck3: pop     ax
fnyck4: ret

fnyck2: pop     ax
        mov     ax,ERROR_INVALID_HANDLE
        stc
        jmp     short fnyck4

EndProc  FNotify_ChkDirHandle

;***    Find_RMPFNYRec  - find rmp fat record
;
;       searches through the RMP segment for the given handle
;
;       ENTRY DS:SI = ptr to handle
;       EXIT
;          Carry Clear
;             no error
;             ES:DI  = FAT RMP record
;          Carry Set
;             error ( no record for this )
;       CALLS   rmp_find
;
;       USES    BX,CX,DS,ES,FLAGS
;
Procedure       Find_RMPFNYRec,NEAR

        mov     bx,FNY_FAT_Handle       ; BX = offset within record
        mov     cx,2                    ; CX = size of search string
        DosContext es
        mov     di,offset DOSGROUP:FNotifyFATRmpHdl
        CALLFAR Rmp_Find                ; ES:DI->FNotify Rmp Handle
        ret

EndProc Find_RMPFNYRec

;***    FNotify_HWake - FindNotify Handle Wakeup.
;
;       FNotify_HWake retrieves the attribute from the SFT and gets
;       addressibility to the MFT name and calls the common wake routine.
;
;       ENTRY  TCBThisSFT -> SFT
;
;       EXIT   NONE
;
;       CALLS  FNotify_Wake
;
;       USES   NONE
;
;       Attribute = TASKAREA:TCBThisSFT->sf_fsi.sfi_DOSattr;
;       MFT_Ptr = TASKAREA:TCBThisSFT->sf_MFT;
;       WFP_Ptr = &MFT_Ptr->mft_name;
;       Fnotify_Wake(Attribute,WFP_Ptr);
;       return (rc);
;
Procedure FNotify_HWake,faronly
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA
; Save all registers, get the attribute from the SFT, get addressibility to
; the WFP in the MFT segment,  and call FNotify_Wake

        CpuMode 386

        pushad
        push    es
        push    ds
        mov     bp,CurrTCB
        les     di,[bp].TCBThisSFT      ; ES:DI->SFT
        xor     dh,dh
        mov     dl,es:[di].sf_fsi.sfi_DOSattr ; DX = Attribute
        mov     esi,es:[di].sf_MFT      ; ESI -> MFT entry
        cmp     esi,SF_MFT_NULL         ; Not entered into sharing ??
        jz      SHORT fnyhwk_ie         ; Yes, Internal Error
        FlatContext ds,ax,noassume      ; DS:ESI->MFT entry
        lea     esi,ds:[esi].mft_name   ; DS:ESI->MFT name

        CALLFAR GetTempBuffer           ; (es:di) -> temp buffer
        SaveReg <di>

        movzx   edi,di                  ; (es:edi) -> temp buffer

        CALLFAR FStrCpy32               ; copy the name onto the buffer

        RestoreReg <di>                 ; (es:di) -> temp buffer with name
        SaveReg <di>

        cld                             ; make sure we're going the right way

        xor     al,al                   ;
        mov     cx,-1                   ;
        repne   scas byte ptr es:[di]   ;
        not     cx                      ;
        dec     cx                      ; cx = strlen(es:di)

        RestoreReg <di>                 ;
        xor     bx,bx                   ; no delete/rename in handle_wake

        SaveReg <es,di>
        call    FNotify_Wake
        RestoreReg <di,es>              ; (es:di) -> temp buffer

        CALLFAR FreeTempBuffer          ; free the temp buffer

        pop     ds
        pop     es
        popad
        ret
fnyhwk_ie: InternalError <FNotify_HWake sf_mft null>

        CpuMode reset

EndProc FNotify_HWake

;***    FNotify_PWake - FindNotify Path Wakeup
;
;       FNotify_PWake - retrieves the attribute from Sattrib and gets
;       addressibility to the WFP and calls the common wake routine.
;
;       ENTRY  TASKAREA:WFP_Start-> WFP
;              AL = Found Attribute
;              AH = del/ren flag -> 0 - this wake up is for create/FSINFO change
;                                -> 1 - this wake up is for DOSDEL/DOSRMDIR/
;                                         DOSMOVE
;
;       EXIT   NONE
;
;       CALLS  FNotify_Wake
;
;       USES   NONE
;
;       Attribute = TASKAREA:Sattrib;
;       WFP_Ptr = TASKAREA:WFP_Start;
;       Fnotify_Wake(Attribute,WFP_Ptr);
;       return;
;
Procedure FNotify_PWake,faronly
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

        Assume  ES:Nothing

; Save all registers, get the attribute from the SAttrib, get addressibility to
; the WFP at WFP_Start, and call FNotify_Wake

        Pusha
        push    es
        push    ds
        TaskContext ES
        xor     dh,dh
        mov     dl,al                   ; DX = Attribute
        xor     bh,bh                   ;
        mov     bl,ah                   ; bx = del/ren flag
;       mov     di,[WFP_Path_End]       ; ES:DI -> last component in path
;       xor     al,al
;       mov     cx,-1                   ; Assume string is NULL terminated
;       cld
;       repne   scasb                   ; Find the NULL at the end of that path
;       mov     cx,di

                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        mov     di,[bp].TCBWFP_Start    ; ES:DI->WFP
                                        ;72417 end

; get a temp buffer and copy the given path after uppercasing;
; this is done , since we use this wfp for MetaMatch and metamatch does
; case sensitive compares only

        SaveReg <es,di> ;               ES:DI->WFP
        CALLFAR GetTempBuffer   ; ES:DI-> tempbuffer
        RestoreReg      <si,ds>         ; DS:si ->WFP
        SaveReg <es,di>
        mov             cx,di                   ;
        CALLFAR StrCpy                  ; copy and uppercase WFP   (di -> after null in dest
        xchg    cx,di                   ; es:di -> uppercased WFP ; cx -> after null in dest
        dec             cx                              ;
        sub     cx,di                           ; cx = count of chars in WFP

        call    FNotify_Wake
        RestoreReg      <di,es>         ;
        CALLFAR FreeTempBuffer  ; release temp buffer
        pop     ds
        pop     es
        Popa
        ret

EndProc FNotify_PWake

;***    FNotify_Wake - Wakeup FindNotify waiters.
;
;       FNotify_Wake compares the WFP for the current process against WFPs
;       in Rmp segment. If a match is found we decrement the count
;
;       ENTRY  ES:DI -> WFP pointer
;              BX    = del/ren flag
;                       == 1 -> this wake call is for del/rename of a
;                               directory/file
;                       == 0 -> not for del/rename
;              DX    =  Attribute
;              CX    = count of wfp string
;
;       EXIT   NONE
;
;       CALLS  Rmp_FindN,ProcRun,MetaMatch
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       RecordOff = 0;
;       length = 0;                     ; match on any record
;       continue = true;
;       while (continue) {
;          if ((rc = Rmp_FindN (RecordOff,length,&FNY_Ptr) == 0) {
;            RecordOff = FNY_Ptr.Offst
;              if (rc = MetaMatch(WFP_Ptr,FNY_Ptr->FNY_Name) == 0){
;
;                if attributes match
;
;                  if (FNY_Ptr->ChangeCnt > 0)
;                     --FNY_Ptr-ChangeCnt;
;                  if (FNY_Ptr->ChangeCnt == 0)
;                     ProcRun (FNY_RmpSel:FNY_Ptr->DirHandle);
;                }
;              }
;            }
;          }
;          else
;            continue = false;
;       }
;
Procedure FNotify_Wake,NEAR,local
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar fnywk_ConsiderFlag,WORD                ; Consider record flag
LocalVar fnywk_RecordOff,WORD                   ; Previous record offset
LocalVar fnywk_WFP_Attribute,WORD               ; Attribute of WFP
LocalVar fnywk_WFP_Ptr,DWORD                            ; Pointer to WFP
LocalVar fnywk_wfplen,WORD                      ; length of wfp
LocalVar fnywk_delren_flag,WORD                 ; delete/rename flag
LocalVar fnywk_wake_anyway,WORD                 ; force wake up if del/ren
                                                ; of fny object happened

        EnterProc <0,0,dx,es,di,cx,bx>

fnywk10: xor    bx,bx                              ; match any record
        mov     fnywk_wake_anyway,bx               ; no force wakeup
        DosContext es
        mov     di,offset DOSGROUP:FNotifyFATRmpHdl; ES:DI->FAT Rmp Handle
        xor     cx,cx                              ; match any record
        mov     dx,fnywk_RecordOff                 ; record offset to start
        mov     ax,fnywk_ConsiderFlag              ; consider flag
        CALLFAR Rmp_FindN
        mov     fnywk_RecordOff,di                 ; save found record offset
        mov     fnywk_ConsiderFlag,1               ; do not consider rec.
        Assume ES:NOTHING
        jc      fnywk30

; See if the names match. The source string must not contain any WildCard
; characters.

        mov     cx,es:[di].FNY_FAT_WFPLEN       ; expression length
        lea     di,es:[di].FNY_FAT_WFP          ; ES:DI->Expression
        lds     si,fnywk_WFP_Ptr                        ; DS:SI->String
        mov     bx,fnywk_wfplen                 ; string length
        CALLFAR MetaMatch
        jc      fnywk40                 ; If no Match, check for
                                        ; directory removal and do force
                                        ; wake up if necessary
; Names Match now check attributes
        mov     di,fnywk_RecordOff                 ; get back ptr to record

; names matched ; now
;    if (wake up is for del/ren and no wild chars in fny wfp), then
;       this is an exact match and the fny object is being removed. so
;       wake up the thread anyway.
;    else continue to match for attribute and then increment count, etc...

        cmp     fnywk_delren_flag,fFNY_DELETE      ; is the wake up for del/ren
        jne     fnywk20                            ; operation
        cmp     es:[di].FNY_FAT_WILDPTR,fFNY_NO_WILDCHAR
                                                ; no wild chars in source
        jne     fnywk20                            ;
        mov     fnywk_wake_anyway,1                ; set force wake up flag
        mov     es:[di].FNY_FAT_WILDPTR,fFNY_OBJ_REMOVED
fnywk20: mov    cx,fnywk_WFP_Attribute             ; get string attribute

                                                   ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        push    [bp].TCBAttrib                     ; save old attrib
        mov     ax,es:[di].FNY_FAT_Attribute       ; get Search Attribute
        mov     ch,cl                              ; ch = found attribute
        mov     [bp].TCBAttrib,ax                  ; Attrib = search attribute
        call    MatchAttributes
        pop     [bp].TCBAttrib                     ; restore old attrib
        UnSetBP_CurrTCB
                                                   ;72417 end

        jnz     fnywk10                            ; if no Match get next

; Attributes match ; increment the wrk change count and see if wake up needed
fnywk21: inc    es:[di].FNY_FAT_WrkChangeCnt       ;
        mov     bx,es:[di].FNY_FAT_WrkChangeCnt    ;
        cmp     bx,es:[di].FNY_FAT_ChangeCnt       ;
        jae     fnywk25                            ; time to wake up
        cmp     fnywk_wake_anyway,0                ;
        LJE     fnywk10                            ; no force flag and count
                                                   ; not reached; go find others
; time to wake up sleeping findnotifynext threads
fnywk25: mov    ax,es
        mov     bx,es:[di].FNY_FAT_Handle          ; AX:BX = Block ID.
        CALLFAR ProcRun
fnywk28:jmp     fnywk10

fnywk30: LeaveProc
        ret
; match failed; now
;       if (wake up is for del/ren AND
;           if the match failed exactly on the FNY_FAT_WILDPTR
;           and if unmatched char on the current WFP is null)
;          then the path is getting removed. we have to do a force wakeup
;          for the affected threads.
;       else continue search
fnywk40: cmp    fnywk_delren_flag,fFNY_DELETE      ; is the wake up for del/ren
        jne     fnywk28                            ; operation
        mov     ax,di                              ; save ptr to unmatched char
        mov     di,fnywk_RecordOff                 ; get back ptr to record
        cmp     es:[di].FNY_FAT_WILDPTR,fFNY_NO_WILDCHAR
                                                   ; no wild chars in source
        je      fnywk28                            ;
        lea     bx,es:[di].FNY_FAT_WFP             ;
        add     bx,es:[di].FNY_FAT_WILDPTR         ; ptr to end of path comp.
        cmp     ax,bx                              ;
        jne     fnywk28                            ;
        mov     cx,fnywk_WFP_Ptr.Offst             ;
        add     cx,fnywk_wfplen                    ; we matched upto end of
        cmp     si,cx                              ; current WFP?
        jb      fnywk28                            ; No, go look for next rec
        mov     fnywk_wake_anyway,1                ; set force wake up flag
        mov     es:[di].FNY_FAT_WILDPTR,fFNY_OBJ_REMOVED
        jmp     fnywk21                           ; no need to match attribute
                                                  ; go force wake up

EndProc FNotify_Wake

;***    FNotify_VWake - FindNotify Volume change Wakeup
;
;       FNotify_VWake - looks at the FAT RMP records  for the old
;       volume and wakes anyone waiting on the corresponding FAT records
;
;       ENTRY  BX = Old hVPB
;
;       EXIT   NONE
;
;       CALLS  FNotify_Wake
;
;       USES   NONE
;
Procedure FNotify_VWake,faronly
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar fnyvwk_RecordOff,WORD                  ; Previous record offset
LocalVar fnyvwk_hVPB,WORD                       ; hVPB that was changed

        EnterProc       <0,bx>
        SaveReg <es,di,ds,si,ax,bx>
        xor     ax,ax                           ;first time get the first
                                                ; record also
        TaskContext     DS                      ;
fnyvw10: mov    bx,FNY_FAT_hVPB                 ; try to match the hVPB
        DosContext es
        mov     di,offset DOSGROUP:FNotifyFATRmpHdl ; ES:DI->FAT Rmp Handle
        mov     cx,2                            ; a Word to match (hVPB
        mov     dx,fnyvwk_RecordOff                ; record offset to start
        lea     si,fnyvwk_hVPB                  ; DS:SI -> hVPB
        CALLFAR Rmp_FindN
        mov     fnyvwk_RecordOff,di                ; save found record offset
        Assume ES:NOTHING
        jc      fnyvw50

; record matched - go mark vpb as invalid and wake up

        mov     es:[di].FNY_FAT_hVPB,fFNY_INVALID_hVPB

        mov     ax,es
        mov     bx,es:[di].FNY_FAT_Handle ; AX:BX = Block ID.
        CALLFAR ProcRun
        mov     ax,1                    ; skip the current record
                                        ; and start looking from next record
        jmp     short fnyvw10

fnyvw50: RestoreReg     <bx,ax,si,ds,di,es>
fnyvw60: LeaveProc
        ret

EndProc FNotify_VWake

;***    FNotifyExit - remove terminated process findnotfiy structures
;
;       Step through the BMP search segment and free each record's structures.
;
;       ENTRY   ES point to valid BMP segment    (not necessary)
;
;       EXIT    search structures removed
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, FLAGS
;
;       FNotifyGlobal_Ptr = DosContext:FNotifyGlobal_Sel:FNotifyGlobalFirst;
;
;       While (FNotifyGlobal_Ptr.Offst != 0) {
;
;           if (FNotifyRecord.PID == Current process PID)
;               w_FindNotifyClose(FNotifyGlobal_Ptr.Offst);
;           FNotifyGlobal_Ptr.Offst = FNotifyGlobal_Ptr->FNY_Next;
;       }
; USES: ax,bx,di,ds,flags

Procedure   FNotifyExit,FARONLY
    ASSUME  ds:nothing, es:nothing,ss:TASKAREA

        SaveReg <es>
        mov     di,FNotifyLocal_First
        DosContext es
        mov     es,FNotifyGlobal_Sel

fnye1:  or      di,di
        jz      fnye2
        mov     bx,di
        mov     di,es:[di].FNY_Next     ;
        mov     ax,Pid                          ;
        cmp     es:[bx].FNY_PID,ax      ;
        jne     fnye1                   ;

; extra check; if this handle does not belong to this process , then how in
; the world, did it get into this process's fny chain in the first place ??

        SaveReg <es,di,bx>
        call    w_FindNotifyClose
        RestoreReg <bx,di,es>
        mov     di,bx                   ;
        jmp     short fnye1

fnye2:  RestoreReg      <es>
        ret

EndProc     FNotifyExit

;***    w_OpenChangeNotify - open ChangeNotify handle
;
;       w_OpenChangeNotify opens a ChangeNotify handle.
;
;       ENTRY
;               DX,AX   =  Size of Buffer
;                                   (see CNPATHINFO).
;
;       EXIT    Carry Clear
;                       BX = DirHandle
;               Carry Set
;                       CX = 0 = number of entries found
;                       AX = error_invalid_parameter
;                          = error_path_not_found
;                          = error_buffer_overflow
;                          = error_access_denied
;
;       CALLS   CNotify_AllocDirHandle
;               CNotify_FreeDirHanlde
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_OpenChangeNotify,HYBRID
LocalVar        wocn_LogSize,DWORD              ; LogSize
LocalVar        wocn_DirHandle,WORD             ; DirHandle
LocalVar        wocn_BMP_seg,WORD
LocalVar        wocn_BMP_off,WORD

        CodeLock        NOTIFY_GROUP
        EnterProc <dx,ax>

        call    CNotifySemGet                            ; Grab Sem
        DosContext ds
        cmp     CNotifyOpenCheck, 0                      ; Make sure no one has an open
        JNE     wocnerr3
;       inc     CNotifyOpenCheck                         ; Grab open  ;###
        mov     ax,Pid                                                ;###
        mov     CNotifyOpenCheck, ax                     ; Grab PID   ;###
        mov     bx, 9                                    ; Made up handle
        mov     wocn_DirHandle,bx                        ; store DirHanlde
        call    CNotify_AllocStruc
        jc      wocnerr2
        call    CNotifySemRel                            ; Release Sem
        mov     bx,wocn_DirHandle                        ; Return DirHanlde
        xor ax,ax
wocn_end:
        LeaveProc
        ret
wocnerr2:
        DosContext ds
        mov     CNotifyOpenCheck, 0                      ; Free open
        SaveReg <ax>
        call    CNotifySemRel                            ; Release Sem
        RestoreReg <ax>
        CodeUnlock      NOTIFY_GROUP
        jmp     short wocn_end
wocnerr3:
        call    CNotifySemRel                            ; Release Sem
        stc
        mov     ax, ERROR_ACCESS_DENIED
        jmp     short wocn_end

EndProc w_OpenChangeNotify

;***    w_ResetChangeNotify -  ChangeNotify handle
;
;       w_ResetChangeNotify starts or restarts a ChangeNotify handle.
;
;       ENTRY
;               CX      =  Handle
;               AX      =  Size of Buffer
;               bx:si   -> InfoBuf
;                                   (see CNINFO).
;
;       EXIT    Carry Clear
;               Carry Set
;                       CX = 0 = number of entries found
;                       AX = error_invalid_parameter
;                          = error_buffer_overflow
;
;       CALLS   CNotify_ChkDirHandle
;               ProcBlock
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_ResetChangeNotify,HYBRID
LocalVar        wrcn_DirHandle,WORD             ; user DirHandle
LocalVar        wrcn_LogSize,WORD               ; LogSize
LocalVar        wrcn_Count,DWORD                ; Count
LocalVar        wrcn_InfoBuf,DWORD              ; InfoBuf

        EnterProc <cx,ax,dx,di,bx,si>
        cmp     cx, 9
        JNE     wrcerr
        clc

; Check to see if the  head is greater than the tail when the loop
; count is not equal.
; Return buffer overflow if true.


        call    CNotifySemGet                  ; Grab Sem
        DosContext ds
        push    word ptr [CNotifyRing_Sel]     ; save global selector
        pop     es                             ; in es
        mov     di, word ptr [CNotifyTailPtr]  ; Tail in di
        mov     bx, word ptr [CNotifyHeadLoop] ; HeadLoop in bx
        mov     ax, word ptr [CNotifyHeadPtr]  ; HeadPtr in ax
        SaveReg <ax>
        call    CNotifySemRel                  ; Release Sem
        RestoreReg <ax>

        cmp     bx, CNotifyTailLoop      ; Cmp head & tail loops
        JE      wrc01                    ; Jmp if equal

        cmp     ax, CNotifyTailPtr       ; Cmp head & tail ptr (loops !=)
        push    es
        push    di
        JLE     wrcfill                  ; Jmp if tail is greater than or equal
                                         ; to get next buffer
        jmp     wrcberr                  ; Head has overtaken tail, ERROR

wrc01:  cmp     ax, CNotifyTailPtr       ; Loops are equal, check if head & tail
        SaveReg <es,di>
                                         ; ptrs.
        JNE     wrcfill                  ; If not equal, get next buffer
                                         ; else wait for next buffer.


; If head equal to tail, block until notify comes in.
;SaveReg <es,di>
        mov     bx, word ptr [CNotifyTailPtr]   ; TailPtr in bx
        mov     ax, es                          ; BX:AX = Block ID
        mov     cx,-1                           ;
        mov     di,cx                           ; DI:CX = -1
        xor     dh,dh                           ; block is interruptible
        cli

; ProcBlock on GLOBAL BMP sel and the Handle

        CALLFAR ProcBlock
;RestoreReg <si,ds>
        jnc     wrcfill
;b734301jz      wrcend                          ; ZF set if timeout
        jz      wrcend1                         ; ZF set if timeout
        mov     ax,ERROR_INTERRUPT              ;
;b734301jmp     wrcend
        jmp     wrcend1
wrcfill:
        RestoreReg <si,ds>
        call    CNotifySemGet                   ; Grab Sem
        SaveReg <ds>
        DosContext ds
        add     word ptr [CNotifyTailPtr], 0400h   ; Each buf is 1024
;        cmp     word ptr [CNotifyTailPtr], 07FF0h ; for 32k alloc
        cmp     word ptr [CNotifyTailPtr], 02FF0h  ; for 12k alloc
        JL      wrc07
        mov     word ptr [CNotifyTailPtr], 0       ; Start at 0
        inc     word ptr [CNotifyTailLoop]         ; Inc loop counter
wrc07:
        RestoreReg <ds>
        les     di, wrcn_InfoBuf
        mov     bx,si                              ; Save in case of rename/move
        mov     dx,di                              ; Save in case of rename/move
        mov     eax, ds:[si].CNINFO_oNextEntryOffset  ; Check for next entry in PathBuf
        mov     es:[di].CNINFO_oNextEntryOffset,eax   ; Check for next entry in PathBuf
        mov     al,ds:[si].CNINFO_bAction          ; Action
        mov     es:[di].CNINFO_bAction,al          ; Action
        mov     ax,ds:[si].CNINFO_cbName           ; Length
        mov     es:[di].CNINFO_cbName,ax           ; Length
;       add     si, 6                              ; Get Name
;       add     di, 6                              ; Get Name
        add     si, 7                              ; Get Name
        add     di, 7                              ; Get Name
        mov     cx,di                              ; length
        CALLFAR StrCpy                             ; copy and uppercase WFP   (di -> after null in dest
        cmp     ds:[bx].CNINFO_oNextEntryOffset,0  ; Check for next entry in PathBuf
        je      wrc14
        mov     eax, ds:[si].CNINFO_oNextEntryOffset  ; Check for next entry in PathBuf
        mov     es:[di].CNINFO_oNextEntryOffset,eax   ; Check for next entry in PathBuf
        mov     al,ds:[si].CNINFO_bAction          ; Action
        mov     es:[di].CNINFO_bAction,al          ; Action
        mov     ax,ds:[si].CNINFO_cbName           ; Length
        mov     es:[di].CNINFO_cbName,ax           ; Length
;       add     si, 6                              ; Get Name
;       add     di, 6                              ; Get Name
        add     si, 7                              ; Get Name
        add     di, 7                              ; Get Name
        mov             cx,di                   ;
        CALLFAR StrCpy                  ; copy and uppercase WFP   (di -> after null in dest
wrc14:  call    CNotifySemRel                   ; Release Sem
        xor     ax,ax

wrcend: LeaveProc
        ret
wrcerr: mov     ax, ERROR_INVALID_HANDLE
        stc
        jmp     wrcend
wrcberr:
        pop    si
        pop    ds
        mov     ax, ERROR_BUFFER_OVERFLOW
        stc
        jmp     wrcend
wrcend1:                                                               ;b734301
        pop     si                                                     ;b734301
        pop     ds                                                     ;b734301
        jmp     wrcend                                                 ;b734301

EndProc w_ResetChangeNotify

;***    w_CloseChangeNotify - Terminate FindNotify Search.
;
;       w_CloseChangeNotify releases the resources needed to keep track of
;       a particular ChangeNotify search.
;
;       ENTRY   BX      = DirHandle to close
;
;       EXIT    Carry Clear
;                       No error
;               Carry Set
;                       AX = ERROR_INVALID_HANDLE
;
;       CALLS   FNotify_ChkDirHandle,DOS_FindNotifyClose,FS_FindNotifyClose,
;               FNotify_FreeDirHandle
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure w_CloseChangeNotify,HYBRID
LocalVar ccn_DirHandle,WORD                     ; user DirHandle

        EnterProc <bx>
        cmp     bx, 9
        JNE     wccerr
        clc
        call    CNotify_FreeStruct            ; Free structures for Notify
        jc      wccend
        clc
        xor     ax,ax
        CodeUnlock      NOTIFY_GROUP
wccend: LeaveProc
        ret
wccerr: mov     ax, ERROR_INVALID_HANDLE
        stc
        jmp     wccend
EndProc w_CloseChangeNotify


Procedure CNotifySemGet, NEAR

          EnterProc
          SaveReg <bx,cx,si,di,es,ds>

          .386P
          CpuMode 386
          FlatContext <DS, ES>,BX
          CCALL32 _KSEMRequestMutex,<<offset FLAT:CNotifySem>,-1>
          CpuMode Reset

          RestoreReg <ds,es,di,si,cx,bx>
          LeaveProc
          ret
EndProc   CNotifySemGet


Procedure CNotifySemRel, NEAR

          EnterProc
          SaveReg <bx,cx,si,di,es,ds>

          .386P
          CpuMode 386
          FlatContext <DS, ES>,BX
          CCALL32 _KSEMReleaseMutex,<offset FLAT:CNotifySem>
          CpuMode Reset

          RestoreReg <ds,es,di,si,cx,bx>
          LeaveProc
          ret
EndProc   CNotifySemRel


;***    CNotify_AllocStruct - get New Structures
;
;       CNotify_AllocStruct
;
;       ENTRY None
;       EXIT
;          Carry Clear
;          Carry Set
;             error
;       CALLS   SegAlloc
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       if (DosContext:CNotifyRing_Sel == 0)
;          if (rc = GDTAlloc(&DosContext:CNotifyRing_Sel) != 0)
;             return (rc);
;
;
;

Procedure CNotify_AllocStruc,NEAR
Assume DS:Nothing,ES:Nothing,SS:TASKAREA

; If the system does not have a ChangeNotify segment allocated, call
; SegAlloc to allocate one and save its selector in DOSHIGHCONTEXT

        DosContext ds

        mov     ax,CNotifyRing_Sel              ; AX = Gloabl ChangeNotify Sel.
        or      ax,ax                           ; Global Selector allocated ?
        jnz     cna0                            ; yes, no need to allocate one

;       mov     bx, 08000h                      ; AX:BX = size of RING segment
        mov     bx, 03000h                      ; AX:BX = size of RING segment
                                                ; 12k, 12 * 1024 buffers
        mov     cx, (D_DATA0 SHL 8) + SA_GDT    ; CX = Access & Allocation flg
        xor     si, si
        .errnz  HF_MOVABLE
        xor     dx,dx                           ; cuurent PTDA not needed thou
        mov     di,FSFNOTIFYGOWNER              ; FindNotify owner of segment

; DI    = owner of segment
; AX:BX = Size of segment
; CX    = Allocation and Access Flags
; SI    = Handle Memory Allocation Flags

        push    ds
        CALLFAR SegAlloc                        ; allocate GDT ring 0
        pop     ds

        jc      cna2                            ; if error go return
        mov     word ptr [CNotifyRing_Sel],bx   ; save global selector
        ;Sanity
cna0:   mov     CNotifyHeadPtr, 0       ;
        mov     CNotifyHeadLoop,0       ;
        mov     CNotifyTailPtr, 0       ;
        mov     CNotifyTailLoop,0       ;


        inc     CNotifyGlobal_Count             ; use this as counter

cna2:   ret

EndProc CNotify_AllocStruc

;***    CNotify_FreeStruct - Free Struct
;
;       CNotify_FreeDirHandle Free a Global Handle
;
;       ENTRY : None
;       EXIT
;          Carry Clear
;             no error
;          Carry Set
;             error
;       CALLS
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;
Procedure CNotify_FreeStruct,NEAR
Assume DS:Nothing,ES:Nothing,SS:TASKAREA



        DosContext ds

        dec     CNotifyGlobal_Count     ;
        mov     CNotifyHeadPtr, 0       ;
        mov     CNotifyHeadLoop,0       ;
        mov     CNotifyTailPtr, 0       ;
        mov     CNotifyTailLoop,0       ;
        mov     CNotifyOpenCheck, 0     ; Free open
        clc
        ret

EndProc   CNotify_FreeStruct


;***    CNotify_PWake - ChangeNotify Path Wakeup
;
;       CNotify_PWake - retrieves the attribute from Sattrib and gets
;       addressibility to the WFP and calls the common wake routine.
;
;       ENTRY  TASKAREA:WFP_Start-> WFP
;              AL = Found Attribute
;              AH = del/ren flag -> 0 - this wake up is for create/FSINFO change
;                                -> 1 - this wake up is for DOSDEL/DOSRMDIR/
;                                         DOSMOVE
;
;       EXIT   NONE
;
;       CALLS  CNotify_Wake
;
;       USES   NONE
;
;       Attribute = TASKAREA:Sattrib;
;       WFP_Ptr = TASKAREA:WFP_Start;
;       Cnotify_Wake(Attribute,WFP_Ptr);
;       return;
;
Procedure CNotify_PWake,faronly
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

        Assume  ES:Nothing

; Save all registers, get the attribute from the SAttrib, get addressibility to
; the WFP at WFP_Start, and call FNotify_Wake

        Pusha
        push    es
        push    ds
        TaskContext ES

                                               ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        mov     di,TASKAREA:[di].TCBWFP_Start  ; ES:DI->WFP
                                               ;72417 end

; get a temp buffer and copy the given path after uppercasing;
; this is done , since we use this wfp for MetaMatch and metamatch does
; case sensitive compares only

        SaveReg <es,di> ;               ES:DI->WFP
        CALLFAR GetTempBuffer   ; ES:DI-> tempbuffer
        RestoreReg      <si,ds>         ; DS:si ->WFP
        SaveReg <es,di>
        mov             cx,di                   ;
        CALLFAR StrCpy                  ; copy and uppercase WFP   (di -> after null in dest
        xchg    cx,di                   ; es:di -> uppercased WFP ; cx -> after null in dest
        dec             cx                              ;
        sub     cx,di                           ; cx = count of chars in WFP

        call    CNotify_Wake
        RestoreReg      <di,es>         ;
        CALLFAR FreeTempBuffer  ; release temp buffer
        pop     ds
        pop     es
        Popa
        ret

EndProc CNotify_PWake

;***    CNotify_ADWake -
;
;
;       CNotify_ADWake - retrieves the attribute from Sattrib and gets
;       addressibility to the WFP and calls the common wake routine.
;
;       ENTRY  TASKAREA:WFP_Start-> WFP
;              AL = Found Attribute
;              AH = Drive letter for attach/detach
;
;       EXIT   NONE
;
;       USES   NONE
;
;

Procedure CNotify_ADWake,faronly
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA


        push    bx
        push    ax                             ; Entry Value
        call    CNotifySemGet                  ; Grab Sem
        DosContext ds
        mov     es,CNotifyRing_Sel
        mov     di,CNotifyHeadPtr                  ;
        add     word ptr [CNotifyHeadPtr], 0400h   ; Each buf is 1024
        cmp     word ptr [CNotifyHeadPtr], 02FF0h  ; For 12k alloc
;        cmp     word ptr [CNotifyHeadPtr], 07FF0h ; For 32k alloc
        JL      cnadw07
        mov     word ptr [CNotifyHeadPtr], 0       ; Start at 0
        inc     word ptr [CNotifyHeadLoop]         ; Inc loop counter
cnadw07:
        pop     ax                                 ; Entry Value
        SaveReg <di>
        mov     es:[di].CNINFO_oNextEntryOffset,0  ; Check for next entry in PathBuf
        mov     es:[di].CNINFO_bAction,al          ; Action
        mov     bx,1
        mov     es:[di].CNINFO_cbName,bx           ; Action
        mov     bx, di
;       add     bx, 6
        add     bx, 7
        mov     es:[bx], ah
        xor     dx,dx
        mov     es:[bx+1], dl
        RestoreReg <di>
        call    CNotifySemRel                      ; Release Sem
        mov     ax,es                              ; AX = Gloabl ChangeNotify Sel.
        mov     bx,di                              ; AX:BX = Block ID.
        CALLFAR ProcRun
        pop     bx
        ret

EndProc  CNotify_ADWake

;***    CNotify_Wake - Wakeup FindNotify waiters.
;
;       CNotify_Wake compares the WFP for the current process against WFPs
;       in Rmp segment. If a match is found we decrement the count
;
;       ENTRY  ES:DI -> WFP pointer
;              BX    = del/ren flag
;                       == 1 -> this wake call is for del/rename of a
;                               directory/file
;                       == 0 -> not for del/rename
;              DX    =  Attribute
;              CX    = count of wfp string
;
;       EXIT   NONE
;
;       CALLS  Rmp_FindN,ProcRun,MetaMatch
;
;       USES    AX,BX,CX,DX,DI,SI,DS,ES,FLAGS
;
;       RecordOff = 0;
;       length = 0;                     ; match on any record
;       continue = true;
;       while (continue) {
;          if ((rc = Rmp_FindN (RecordOff,length,&FNY_Ptr) == 0) {
;            RecordOff = CNY_Ptr.Offst
;              if (rc = MetaMatch(WFP_Ptr,FNY_Ptr->FNY_Name) == 0){
;
;                if attributes match
;
;                  if (CNY_Ptr->ChangeCnt > 0)
;                     --CNY_Ptr-ChangeCnt;
;                  if (CNY_Ptr->ChangeCnt == 0)
;                     ProcRun (FNY_RmpSel:FNY_Ptr->DirHandle);
;                }
;              }
;            }
;          }
;          else
;            continue = false;
;       }
;
Procedure CNotify_Wake,NEAR,local
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

LocalVar cnywk_ConsiderFlag,WORD                ; Consider record flag
LocalVar cnywk_RecordOff,WORD                   ; Previous record offset
LocalVar cnywk_WFP_Attribute,WORD               ; Attribute of WFP
LocalVar cnywk_WFP_Ptr,DWORD                            ; Pointer to WFP
LocalVar cnywk_wfplen,WORD                      ; length of wfp
LocalVar cnywk_delren_flag,WORD                 ; delete/rename flag
LocalVar cnywk_wake_anyway,WORD                 ; force wake up if del/ren
                                                ; of fny object happened

        EnterProc <0,0,dx,es,di,cx,bx>

        SaveReg <es,di,ds,si>

; get address of next ring buffer slot
; es = ring buffer selector; di = Head ptr

        call    CNotifySemGet                  ; Get Sem
        DosContext ds
        push    word ptr [CNotifyRing_Sel]     ; save global selector
        pop     es                             ; in es
        mov     di, word ptr [CNotifyHeadPtr]  ; Head in di

        add     word ptr [CNotifyHeadPtr], 0400h   ; Each buf is 1024
        cmp     word ptr [CNotifyHeadPtr], 02FF0h  ; For 12k alloc
;        cmp     word ptr [CNotifyHeadPtr], 07FF0h
        JL      cnywk07
        mov     word ptr [CNotifyHeadPtr], 0       ; Start at 0
        inc     word ptr [CNotifyHeadLoop]         ; Inc loop counter
cnywk07:
        lds     si,cnywk_WFP_Ptr                   ; DS:SI->String

        mov     bx,di                              ; Save in case of rename/move
        xor     eax,eax
        mov     es:[di].CNINFO_oNextEntryOffset,eax  ; Check for next entry in PathBuf
        mov     ax, cnywk_delren_flag              ; is the wake up for del/ren
        mov     es:[di].CNINFO_bAction,al          ; Action
        mov     ax,cnywk_wfplen                    ; string length
        mov     es:[di].CNINFO_cbName,ax          ; Action
;       add     di, 6
        add     di, 7
        add     ax, 8                             ; eax has nextentryoffset if necessary
;       add     ax, 7
        mov     cx,di                   ;
        CALLFAR StrCpy                  ; copy and uppercase WFP   (di -> after null in dest

        cmp     cnywk_delren_flag, 8               ; is the wake up for del/ren
        jne     cnywk20                            ; operation
        mov     es:[bx].CNINFO_oNextEntryOffset,eax ; Reset first nextentryoffsett
        xor     eax,eax
        mov     es:[di].CNINFO_oNextEntryOffset,eax  ; Check for next entry in PathBuf
        mov     ax, 9
        mov     es:[di].CNINFO_bAction, al        ; Action
        mov     bx,di                             ; Save index
;        add     di, 6
        add     di, 7
        TaskContext DS
                                                  ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBRen_WFP       ; ds:si->WFP
        CALLFAR StrCpy                            ; copy and uppercase WFP   (di -> after null in dest
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBRen_WFP       ; ds:si->WFP
                                                  ;72417 end
        CALLFAR DStrLen
        dec     cx
        mov     es:[bx].CNINFO_cbName,cx          ; Action
;        add     bx, 5
;        mov     byte ptr es:[bx], cl

cnywk20:
                                                   ; not reached; go find others
        RestoreReg <si,ds,di,es>
; time to wake up sleeping findnotifynext threads
        DosContext ds
        mov     bx, word ptr [CNotifyTailPtr]      ; AX:BX = Block ID.
        call    CNotifySemRel                      ; Release Sem
        mov     ax,CNotifyRing_Sel                 ; AX = Gloabl ChangeNotify Sel.
        CALLFAR ProcRun

        LeaveProc
        ret

EndProc CNotify_Wake

;***    CNotifyExit - remove terminated process Changenotfiy structures
;
;       ENTRY   None
;
;       EXIT    search structures removed
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, FLAGS
;
; USES: ax,bx,di,ds,flags

Procedure   CNotifyExit,FARONLY
    ASSUME  ds:nothing, es:nothing,ss:TASKAREA

        mov     bx,Pid                                                ;###
        push    ds
        DosContext ds
        cmp     CNotifyOpenCheck, bx                     ; Grab PID   ;###
        pop     ds
        jne     cne6
        mov     bx, 9                ; Handle
        call    w_CloseChangeNotify
cne4:   ret
cne6:   xor     ax,ax
        clc
        jmp     cne4

EndProc     CNotifyExit

        PUBLIC  _EndNOTIFYGROUP
_EndNOTIFYGROUP label   byte

FSSWAPCODE ENDS

        FARCODE HIGH2
HIGH2CODE       SEGMENT

;***    FNotify_Handle_Wake - FindNotify Handle Wakeup.
;
;       FNotify_Handle_Wake - checks if this process has any findnotify search
;               on, and if so calls the real Handle_Wake procedure
;
;       ENTRY  TCBThisSFT -> SFT
;
;       EXIT   NONE
;
;       CALLS  FNotify_Wake
;
;       USES   NONE
;
;       Attribute = TASKAREA:TCBThisSFT->sf_fsi.sfi_DOSattr;
;       MFT_Ptr = GDT_MFT:TASKAREA:TCBThisSFT->sf_MFT;
;       WFP_Ptr = &MFT_Ptr->mft_name;
;       Fnotify_Wake(Attribute,WFP_Ptr);
;       return (rc);
;
Procedure FNotify_Handle_Wake,HYBRID
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

; If no Global Segment allocated then return quickly.

        pushf
        push    es
        DosContext es
        cmp     FNotifyGlobal_Count,0
        pop     es
        je      fnyhwk1

        Assume  ES:Nothing
        CALLFAR FNotify_HWake

fnyhwk1: popf
        ret

EndProc FNotify_Handle_Wake



;***    FNotify_Vol_Wake - FindNotify Volume change Wakeup
;
;       FNotify_Vol_Wake - checks if this process has any findnotify search
;               on, and if so calls the real Vol_Wake procedure
;
;       ENTRY  BX = Old hVPB
;
;       EXIT   NONE
;
;       CALLS  FNotify_Wake
;
;       USES   NONE
;
Procedure FNotify_Vol_Wake,NEAR
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

; If no Global Segment allocated then return quickly.

        pushf
        push    es
        DosContext es
        cmp     FNotifyGlobal_Count,0
        pop     es
        je      fnyvwk1

        Assume  ES:Nothing
        CALLFAR FNotify_VWake

fnyvwk1: popf
        ret

EndProc FNotify_Vol_Wake

;***    FNotify_Path_Wake - FindNotify Path Wakeup
;
;       FNotify_Path_Wake - checks if this process has any findnotify search
;               on, and if so calls the real path_wake procedure
;
;       ENTRY  TASKAREA:WFP_Start-> WFP
;              AL = Found Attribute
;              AH = del/ren flag -> 0 - this wake up is for create/FSINFO change
;                                -> 1 - this wake up is for DOSDEL/DOSRMDIR/
;                                         DOSMOVE
;
;       EXIT   NONE
;
;       CALLS  FNotify_PWake
;
;       USES   NONE
;
;       if (FNOTIFY in EFFECT)
;               call FNotify_PWake
;
Procedure FNotify_Path_Wake,HYBRID
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

; If no Global Segment allocated then return quickly.

        pushf
        push    es
        DosContext es
        cmp     FNotifyGlobal_Count,0
        pop     es
        je      fnypwk1
        Assume  ES:Nothing
        CALLFAR FNotify_PWake
fnypwk1: popf
        ret

EndProc FNotify_Path_Wake

;***    CNotify_Path_Wake - ChangeNotify Path Wakeup
;
;       CNotify_Path_Wake - checks if this process has any findnotify search
;               on, and if so calls the real path_wake procedure
;
;       ENTRY  TASKAREA:WFP_Start-> WFP
;              AL = Found Attribute
;              AH = del/ren flag -> 0 - this wake up is for create/FSINFO change
;                                -> 1 - this wake up is for DOSDEL/DOSRMDIR/
;                                         DOSMOVE
;
;       EXIT   NONE
;
;       CALLS  CNotify_PWake
;
;       USES   NONE
;
;       if (CNOTIFY in EFFECT)
;               call CNotify_PWake
;
Procedure CNotify_Path_Wake,HYBRID
Assume DS:NOTHING,ES:NOTHING,SS:TASKAREA

; If no Global Segment allocated then return quickly.

        pushf
        push    es
        DosContext es
        cmp     CNotifyGlobal_Count,0
        pop     es
        je      cnypwk1
        Assume  ES:Nothing
        cmp     al, 9
        ja      cnypwk4
        push    bx
        mov     bx,ax
        CALLFAR CNotify_PWake
        pop     bx
        jmp     short cnypwk1
cnypwk4:
        CALLFAR CNotify_ADWake
cnypwk1: popf
        ret

EndProc CNotify_Path_Wake

HIGH2CODE       ENDS

        END
