SCCSID  equ     "@(#)volmount.asm       6.17 91/09/13"
;       IBM/Microsoft Confidential
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved

page    ,132
TITLE   VOLMOUNT - volume ID Mounting
NAME    VOLMOUNT

;***    volume ID Mounting for MSDOS
;
;
; Volume Management, i.e, detecting when the wrong volume is mounted and
; notifying the operator to take corrective action is handled by this module,
; VOLIO.ASM, VOLSUBR.ASM and the device driver. At Format time, the FORMAT/
; DISKCOPY utilities will generate a 32-bit Volume Serial Number, place it and
; an 8-byte FSD name and a volume label in the boot record.
;
; When the FSD passes an I/O request to FSH_DOVOLIO the driver passes the
; 32-bit volume serial number and the user's volume label (via the VPB). When
; I/O is performed the Volume Manager compares the requested volume serial
; number with the current volume serial number it maintains for the device.
; This is an in-storage test (no I/O required) performed by checking the
; Drive Parameter Block's (DPB) VPB of the volume currently mounted in the
; drive. If unequal, the Volume Manager signals the HardError Daemon to
; prompt the user to insert the volume having the label specified.
;
; When the Volume Manager detects a media change in a drive, first access after
; an Unlock operation, or first access after boot it will determine the FSD
; that will be responsible for managing I/O to that volume. This process is
; known as MOUNT.
;
; The Mount process begins by reading logical sector zero on the Media. Any
; errors encountered are ignored because it is possible that different types
; of media (i.e. Optical Disk and CD-ROM) may have track zero unreadable or
; other file systems may have another location where they store boot sector
; info. Before reading logical sector zero, the buffer is initialize to zero to
; prevent a FSD from accepting a volume which was present in the drive some
; time in the past. The volume label text field is initialize to "UNLABELED".
; We check the boot sector (logical sector zero) to see if we recognize the
; the format by comparing the signature byte for a special value (41). If so.
; we copy information pertaining to the VPB from the boot sector to a newly
; created VPB.
;
; A BuildBPB call is then issued to the device driver specified in the DPB.
; The device driver is passed a pointer to the boot sector. If the devices'
; media is expected to have an unreadable sector zero or the contents of
; sector zero does noy contain the data that the device driver needs,
; then the device driver is required to have the DEV_NON_IBM bit set in the
; Attribute word of the device drivers header. When set, this allows the
; device driver the capability of reading known areas of the media that
; contains the information needed to process this call. There will a published
; set of values to use for BPB fields that are currently be used by the FAT
; file system.
;
; The Volume Manager then polls each FSD by calling the FS_MOUNT entry point
; with the handle of the newly created VPB, a pointer to the boot sector,
; and pointers to the VPB file system independent and dependent areas. Once
; a FSD has recognized the volume, it should fill in relevant fields in the
; VPB file system independent and dependent areas. If no FSD accepts the
; volume then it is assigned to the default FAT file system.
;
; Once a volume has been recognized, the relationship between drive, FSD,
; volume serial number, and volume label is remembered. Subsequent requests for
; a removed volume require one I/O for the volume serial number, and the
; volume label contained in the boot record. The connection between media and
; VPB is remembered until all open files on the volume are closed and search
; references are removed.
;
;
; VPBInit
; WhatVolume,WhatVolmeForce,WhatVolumeCheck,WhatVolumePerc
; GenhVPB
; FSMountVolume
; FSDMount
; FindVID
; Check_if_FAT
; Check_if_FAT_Init
; Check_if_FAT_Check
; ForceMount
; RedetermineMedia
;
;
;***********************************************************************;BN040;
;  29Jul93 - J.M.Hare    defect 71358                                   ;BN040;
; Add propogating of DOSFLAG through files system for exec_only test.   ;BN040;
;***********************************************************************;BN040;
;  10May94 - Change Team - defect 81429                                 ;81429
; Check for BAD_UNIT return code from DiskIO call so that DosOpen will  ;81429
; return an error if the IBM2FLPY.ADD driver is not loaded.             ;81429
;***********************************************************************;81429
;  30Jun98 - M.Monahan - defect 88189                                   ;88189
; Mark the SFT as bad if we are not able to finish the redetermine media;88189
; due to the reference count being too high after the flush.  Since     ;88189
; FSD_CLOSE had been called, the information in the SFT is not valid    ;88189
;***********************************************************************;88189
; 11/27/95 - MLM defect 72417                                           ;72417
;                 moved from PTDA to TCB: PathNameType, WFP_Start       ;72417
;***********************************************************************;72417

.xlist
.xcref
include dosseg.inc
include defdbug.inc
include basemaca.inc
include osmaca.inc
include fnofs.inc
include oldmaca.inc
include dossymc.inc
include argframe.inc
include devsym.inc
include dirent.inc
include bpb.inc
include boot.inc
include dpb.inc
include error.inc
include filesys.inc
include ioctl.inc
include vmowner.inc
include opncreat.inc
include protmode.inc
include sft.inc
include tkexport.inc
include mi.inc
include mult.inc
include osconst.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include strat2.inc
.cref
.list

CpuMode 386

include vpb.inc
include fsc.inc

        ReferGDT        GDT_VPB,WORD    ; VPB BMP Selector

        ReferGlobal     initfatchkdsk,BYTE  ; init chkdsk of fat boot partition ;### AC DCR 1520
        ReferGlobal     pVPBBuf,DWORD   ; sector buffer
        ReferGlobal     fatvolume,BYTE

                                        ;72417 removed PTDA entries now
                                        ;      in TCB

        ReferTask       CurrTCB,WORD    ; Current task control block
        ReferTask       Pid,WORD        ; process ID

        extrn           Bmp_SegSize:ABS

        extrn           BuildBPB:NEAR
        extrn           CRC:NEAR
        extrn           DecVPBRef:NEAR
        extrn           DecVPBRefInv:NEAR
        extrn           DiskIO:NEAR
        extrn           FNotify_Vol_Wake:NEAR
        extrn           fscNext:NEAR
        EXTRN           FSD_ChgFilePtr:NEAR
        EXTRN           FSD_Close:NEAR
        EXTRN           FSD_FlushBuf:NEAR
        EXTRN           FSD_OpenCreate:NEAR
        extrn           IncVPBRef:NEAR
        EXTRN           InitCDS:NEAR
        extrn           LockVBuf:NEAR
        extrn           ReadBoot:NEAR
        extrn           ProbeChange:NEAR
        extrn           ResetCurrency:NEAR
        extrn           ResetMedia:NEAR
        extrn           sw_DevIOCtl:NEAR
        extrn           UnlockVBuf:NEAR
        extrn           VPBLink:NEAR
        extrn           VPBFind:NEAR
        extrn           VPBFree:NEAR
        extrn           VPBAllocCopy:NEAR
        extrn           WaitDPB:NEAR
        extrn           WaitVPB:NEAR
        extrn           WakeDPB:NEAR
        extrn           WakeVPB:NEAR

        EXTRNFAR        DosIOAllowed
        EXTRNFAR        Bmp_Init
        EXTRNFAR        Clear_Kernel_Sem
        EXTRNFAR        HogEAFile
        EXTRNFAR        UnHogEAFile
        EXTRNFAR        Bmp_SetGrowFunction
        EXTRNFAR        SetVPB
        EXTRNFAR        Take_Kernel_Sem
        EXTRNFAR        XCHGP
        EXTRNFAR        AllocReqPacketWait
        EXTRNFAR        FreeReqPacket
        EXTRNFAR        DevIOCall2

        EXTRN           _SELAllocKHB:near
        EXTRN           _SELReallocKHB:near

_DATA segment
        public          _pVPB_BMP
        _pVPB_BMP       DD  0           ; linear address of VPB segment
_DATA ends

        ModName , FNO_VOLMOUNT_ASM

        FARCODE INIT
INITCODE SEGMENT

;***    VPBInit - initialize the VPB selector/segment.
;
;       VPBInit allocates a GDT segment for storing the VPBs.
;       The segment is managed by BMP.  The segment will be mapped
;       to the selector GDT_VPB.
;
;       ENTRY   NONE
;
;       EXIT    GDT_VPB mapped to newly allocated segment.
;
;       USES    ax, bx, cx, dx, si, di, ds, es
;

Procedure VPBInit,NEAR
        ASSUME ds:NOTHING, es:NOTHING, ss:NOTHING
        LocalVar vmacHeapBlockInfo,<size vmac_s>

        EnterProc
        PMONLY

        ; initialize selector in vmacHeapBlockInfo
        mov     vmacHeapBlockInfo.ac_sel,GSEL GDT_VPB
        ; _SELAllocKHB(VM_HKRH_PUBRW,Bmp_SegSize,0,SELAL_ALLOCATED,
        ;              FSVPBOWNER,&vmacHeapBlockInfo)
        mov     ebx,Bmp_SegSize
        FlatContext <ds,es>,ax
        SSToDS  eax,vmacHeapBlockInfo
        CCALL32 _SELAllocKHB,<VM_HKRH_PUBRW,ebx,0,SELAL_ALLOCATED,FSVPBOWNER,eax>
        or      eax,eax
        jz      ivpbs1
        InternalError   <VPBInit: cannot allocate segment>
ivpbs1:

        mov     eax, vmacHeapBlockInfo.ac_va    ; save linear address
        mov     _pVPB_BMP, eax                  ; move into global

;
; Initialize the segment to be a BMP managed segment.
;       ax = segment handle
;       bx = segment selector
;       cx = block size
;       dx = grow delta (in blocks)
;
        mov     bx,GSEL GDT_VPB
        xor     ax,ax
        mov     cx, SIZE vpb
        mov     dx, VPB_GRODELT
        CALLFAR Bmp_Init
;
; Change the callback function used to grow the segment
;
        mov     ax, GSEL GDT_VPB      ; (ES) = segment selector
        mov     es,ax
        mov     ax,seg VPBGrow        ; (DS:DX) = address of function
        mov     ds,ax
        mov     dx,offset VPBGrow
        CALLFAR Bmp_SetGrowFunction

        LeaveProc
        ret
EndProc VPBInit

INITCODE ENDS

          FARCODE HIGH2
HIGH2CODE SEGMENT

;***    VPBGrow - grow the Bmp segment used for VPB's
;
;       ENTRY:  (ES) = selector of Bmp segment
;               (AX) = desired new size.
;       EXIT:   C clear if successful
;               C set if unsuccessful
;       USES    AX, FLAGS
;
        ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure VPBGrow,far
;       PMONLY  TASKTIME
        LocalVar ulHeapBlockAddr,DWORD

        EnterProc
        TaskTime
        SaveReg <BX,CX,DX,SI,DI,DS,ES>

        movzx   ebx,ax               ; (ebx) = desired new size
        mov     cx,es                ; (ecx) = selector
        movzx   ecx,cx
        ; _SELReallocKHB(VM_HKRH_PUBRW,selector,newsize,0,&ulHeapBlockAddr)
        FlatContext <ds,es>,ax
        SSToDS  eax,ulHeapBlockAddr
        CCALL32 _SELReallocKHB,<VM_HKRH_PUBRW,ecx,ebx,0,eax>
        xor     ebx,ebx
        cmp     ebx,eax                     ; sets carry iff (eax)!=0

        RestoreReg <ES,DS,DI,SI,DX,CX,BX>
        LeaveProc
        ret
EndProc   VPBGrow


;***    WhatVolume, WhatVolumeForce, WhatVolumeCheck - determine volume in drv.
;
;       WhatVolume is the workhorse of the whole volume ID detection mechanism.
;       It's whole purpose is to generate (quickly!) the volume ID info for
;       the specified drive.
;       The BufHE_Percolate bit in TCBBufHE is cleared.
;
;       WhatVolumeForce ALWAYS recomputes the volume id.  This is called when
;       a media uncertain error is detected.
;       TCBBufHE is respected.
;
;       WhatVolumeCheck is WhatVolumeForce without the drive lock check.  It
;       is called for int25/int26 processing, which involves special locking.
;       The BufHE_Percolate bit in TCBBufHE is cleared.
;
;       WhatVolumePerc is WhatVolume without setting TCBBufHE.  This entry
;       point is used by callers that set TCBBufHE to BufHE_Percolate.
;
;       ALGORITHM (WhatVolume):
;
;       o       If the hVPB field is not 0 then quickly poke the device to see
;               if the media has been changed. We can do this with the
;               media-check call; old drivers support it and so will new
;               drivers.  If no change is indicated, then we return
;               immediately.
;       o       If the hVPB field was not 0, get that VPB and see if it was
;               locked down. If so, we wait for it to become free.
;       o       If the hVPB is not 0 Lock down the old volume.
;       o       Tell the device driver that we are examining the volume and to
;               stop sending Uncertain Media calls
;       o       Find (generate) the hVPB for this VPB.
;       o       make dpb point to volume currently in drive
;       o       make new vpb point to dpb
;       o       release old vpb and wake up everyone waiting on that vpb.
;
;       ENTRY   (DS:SI) point to dpb
;
;       EXIT    Carry clear =>
;                   (DS:SI).hVPB is filled in with the "correct" volume
;               Carry Set =>
;                   (AX) = I/O packet status; operation was failed
;
;       USES    AX, BX, DX, DI, ES, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure  WhatVolume,HYBRID

LocalVar   wv_pDPB,DWORD

        PMONLY
        DBPRT   80h,40h,<WhatVolume($x:$x>,<ds,si>

;*  Do retry/ignore processing in whatvolume

        mov     bx,CurrTCB
        and     TASKAREA:[bx].TCBBufHE,not BufHE_Percolate

;*  Respect caller's setting of TCBBufHE

Entry   WhatVolumePerc
        xor     ax,ax                   ; Don't force volume check
        jmp     short wv1               ; Make a new VPB if necessary
                                        ; Skip other entry points

Entry   WhatVolumeCheck,HYBRID          ; Avoids drive locking check
        PMONLY
        mov     ax,1                    ; Force volume check
;*  Do retry/ignore processing in whatvolume
        mov     bx,CurrTCB
        and     TASKAREA:[bx].TCBBufHE,not BufHE_Percolate
        jmp     short wv2               ; Make a new VPB if necessary
                                        ; Enter code AFTER drive lock check
Entry   WhatVolumeForce
        PMONLY
        mov     ax,1                    ; Force volume check

wv1:    CALLFAR DosIOAllowed            ; See if DOS disk I/O is allowed
        jnc     wv2                     ; DOS I/O not allowed, drive is locked
        ret
;
; AX = 0 don't force a volume check and make a new VPB if necessary
;    = 1 force a volume check and make a new VPB if necessary
;
; if (VPB exists && vpb_flags == VPB_FM_UNKNOWN)
;    force volume check
;

wv2:    SaveReg <cx>
        EnterProc <ds,si>               ; Save (DS:SI) -> DPB

        cmp     [si].dpb_hVPB,NULLVPB   ; Check old VPB
        je      wv3                     ; No VPB there, generate one!

        SaveReg <di,es>
        mov     di,[si].dpb_hVPB
        VPBContext es,di                ; ES:DI -> VPB
        test    es:[di].vpb_fMisc,VPB_FM_UNKNOWN  ; fat by default?
        RestoreReg <es,di>
        jnz     wv3                     ; yes, go force volume check

        or      ax,ax                   ; recompute volume always only if
        jnz     wv3                     ; WhatVolumeForce

        call    ProbeChange             ; Is the volume reliable?
;b715176       ljnc    wv99                    ; Yes, all done!
;b715176  Hold on here. ProbeChange calls AllocReqPacketWait which can
;b715176  block.  Better make sure DPB->hVPB is not NULL. There could
;b715176  be other threads out there.
        lds     si,wv_pDPB                      ; (DS:SI) = address of DPB
        jc      wv3                                                     ;b715176
        cmp     [si].dpb_hVPB,NULLVPB                                   ;b715176
        je      wv3                                                     ;b715176
        jmp     wv99a                                                   ;b715176

;
; We spin waiting for the DPB to become free AND for the VPB in that DPB
; to become free
;

wv3:    call    WaitDPB                 ; Wait for the DPB to be free
        mov     ax,[si].dpb_hVPB        ; Grab the VPB that was last
                                        ; in the drive
        or      ax,ax                   ; Was there a recognized volume?
        .errnz  NULLVPB
        jz      wv4                     ; No known volume, just lock DPB
        mov     bl,VPBCHECK
        push    bp                      ; Save frame pointer
        call    WaitVPB                 ; Wait for VPB to be free
        mov     di,bp                   ; es:di -> VPB
        pop     bp                      ; Restore frame pointer
        jc      wv3                     ; We blocked, the DPB may be locked
                                        ; again, go wait again.
        or      es:[di].vpb_flags,VPBCHECK ; Lock that VPB down

wv4:    or      [si].dpb_flags,DPBCHECK ; Lock that DPB down

;
;  Everything is locked down. We must be sure to unlock it even if we fail!
;
        lds     si,wv_pDPB                      ; (DS:SI) = address of DPB
        call    ResetMedia              ; Stop telling me about the
                                        ; uncertain media
;
; We should only build a new VPB (by calling GenhVPB) if the VPBFORMATMOUNT
; bit is set in vpb_flags. Otherwise, we want to keep the VPB as is until
; a RedetermineMedia call is made.
;
        mov     bx,[si].dpb_hVPB
        or      bx,bx
        jz      wv4_5                   ; No existing VPB. Generate one.
        .errnz  NULLVPB
        VPBContext es,bx                ; ES:BX -> VPB
        test    es:[bx].vpb_flags,VPBFORMATMOUNT    ; Was format not cleared?
        mov     ax,bx                   ; Put hVPB into AX for possible return.
        jz      wv4_5                   ; No. Then generate a new VPB.
;
; Push flags and AX for later popping. This should match what was pushed on the
; stack above after the call to GenhVPB.
; Carry cleared by above TEST instruction - OK since there is no error.
;
        pushf
        push    ax
        jmp     short wv6               ; Don't generate new VPB

;
; Generate a new VPB.
;
wv4_5:  call    GenhVPB                 ; Generate the VPB in the DPB
        lds     si,wv_pDPB              ; (DS:SI) = address of DPB

        pushf                           ; Save flags from GenhVPB call
        push    ax                      ; Save (AX), has error code if we failed
        jnc     wv5                     ; No error. Continue.
        xor     ax,ax                   ; We failed, (AX) = NULLVPB
        .errnz  NULLVPB
        jmp     short wv7               ; Don't link VPB


; @FATEA@

wv5:    CALLFAR HogEAFile               ; hog fat ea file
        lds     si,wv_pDPB              ; (DS:SI) = address of DPB
        pop     ax                      ; ax = hVPB
        push    ax
;
; We have the new VPB.  We need to link the VPB to the DPB.
; Note: GenhVPB linked the DPB to the VPB already. (ie vpb_fsi.vpi_pDPB -> DPB)
;
wv6:    push    bp
        mov     bp,ax                   ; (BP) = handle of new VPB
        call    IncVPBRef               ; Count DPB -> VPB reference
        pop     bp
;
; If GenhVPB failed, then (AX) = NULLVPB (0).  Hence, we will skip stuff
; below that handles a new VPB.  However, we still want to break the
; assocation between the DPB and the old VPB! If the old VPB was not NULL
; and the VPB belongs to a FSD we issue a MOUNT (VOLREMOVED) call on the old
; VPB to the FSD if the new VPB != old VPB.
;
wv7:    mov     bx,[si].dpb_hVPB        ; bx = old VPB
        or      bx,bx                   ; bx = old VPB  = NULLVPB  ?
        je      wv9                     ; Yes, skip vol removed MOUNT
        cmp     ax,bx                   ; new VPB = old VPB ?
        je      wv91                    ; Yes, skip old volume stuff
        VPBContext ES,BX
        cmp     es:[bx].vpb_FSC.Offst,FSC_LOCAL ; valid fsc pointer ?
        je      wv8                     ; FAT ; wake up FNy threads
        ja      wv9                     ; NET ;no unmount needed
        push    bp                      ; stack frame pointer
        mov     bp,bx                   ; bx = old VPB handle
        mov     bx,FSM_VOLREMOVED
        call    FSDMount
        pop     bp                      ; stack frame pointer
        jmp     short wv9

; The old VPB was a FAT volume  and is going away.
; So, we need to do:
;       i. wake up any pending Findnotify threads on this Volume
; ds:si -> dpb
; es:bx -> old VPB
; ax -> new VPB

wv8:    call    FNotify_Vol_Wake        ; wake up findnotifynext threads
                                        ; sleeping on this volume with error

wv9:    xchg    ax,[si].dpb_hVPB        ; Assign new VPB to DPB, ax = old VPB

;*      Clean Up
;       We must unlock the old VPB, if there was one.
;       We must unlock the DPB.
;
;       if (TOS+2.CF == TRUE) AND (TOS+2.ZR == TRUE)
;           GenhVPB hit a nested uncertain media error, so we try again
;
;       (AX) = old dpb_hVPB
;       (DS:SI) -> DPB
;       (TOS)   = error status (ax) to return to caller
;       (TOS+2) = flags to return to caller

wv90:   or      ax,ax                   ; Was there an old VPB?
        .errnz  NULLVPB
        jz      wv97                    ; Old VPB was null, skip old VPB stuff
;
; We are done with an old volume or we detected a potential change in the
; volume. In either case, blast the currency info. for the old VPB.
;
wv91:   call    ResetCurrency           ; Blast the currency info for old VPB

;
; Decrement the ref count in the VPB since we either removed the old VPB from
; the drive, or we had incremented the reference earlier but the VPB is the
; same as what we had in the drive originally.
;
        push    bp
        mov     bp,ax                   ; (BP) = handle of old VPB
        call    DecVPBRef               ; Since DPB is changed, remove referent
        jz      wv95                    ; No more referents; skip wake up
;
; Unlock the VPB and wake up anyone waiting on the VPB.
;
        mov     bl,VPBCHECK
        call    WakeVPB                 ; Wake up everyone waiting for old VPB
wv95:   pop     bp
;
; Wake up anyone waiting on the DPB
;
wv97:   and     [si].dpb_flags,NOT DPBCHECK
        call    WakeDPB                 ; Wake up all those DPB sleepers

        pop     ax                      ; (AX) = error return code, perhaps
        popf                            ; Restore flags from GenhVPB call

        jnc     wv99                    ; Volume generation was successful
        LJZ     wv3                     ; Nested uncertain media err, go over

wv99:   lds     si,wv_pDPB              ; (DS:SI) = address of DPB
wv99a:                                                                  ;b715176
        LeaveProc
        RestoreReg <cx>
        DBPRT   80h,40h,<)>
        ret

EndProc   WhatVolume

Break   <GenhVPB - determine the VPB in a particular drive>

;***    GenhVPB - determine the VPB in a particular drive>
;
;   GenhVPB is used to determine the internal VPB.  Any errors we get are
;   sent to the user for him/her to see.  Since we may read in 2 sectors here,
;   we go to the top to restart the read.  We reset the volume-writeable bit
;   in the VPB, since the user may have change the writability of the volume
;   while it was uncertain (i.e., applied or removed a write-protect tab).
;
;   Inputs:     ds:si   point to DPB of interest.  It and whatever volume was
;                       in it last are locked.
;
;   Outputs:    Carry clear => ax is handle to VPB for drive
;               Carry set => operation generated an error
;                   zero clear => operation was failed
;                   zero set => nested uncertain media occurred
;
;   Registers modified: everything
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

TABLE   SEGMENT

Public lbl,lblLen
lbl     db  "UNLABELED",0
lblLen  equ $-lbl

public  lbl2
lbl2    db  "UNREADABLE",0
public  lbl2Len
lbl2Len equ $-lbl2

; A minimum BPB for unformatted media when doing a
; direct OPEN on the drive.
;
; This is also the BPB that we use for media that are not recognised as
; FAT, when we have to mount onto FAT by default. NOTE that the number of
; FATs is set to zero. This is used in Crackit in dir.asm to see if it
; should allow FAT FS operations on this volume.
;
minimumBPB  a_BPB   <200h,1,1,0,10H,0,0F0H,1,9,1,0,0,0FFFFh,03Fh>
public  minimumBPB

TABLE   ENDS

;#### DCR 1176 #####
ghv_GIO_DataPack STRUC
ghv_waste       db 34 dup (?)
ghv_attributes  dw ?
ghv_GIO_DataPack ENDS
;#### DCR 1176 #####

Procedure GenhVPB,NEAR,LOCAL

LocalVar  DiskIOFlags,WORD
LocalVar  pDPB,DWORD
LocalVar  DevDrvErr,WORD
LocalVar  LocalVPB,<size vpb>
LocalVar  NumberOfFats,BYTE

LocalVar  IOParms,<size iop>    ; parameter packet for ioctl.

LocalVar  ghv_DCS,<size DriverCaps>     ; driver capabilities structure
LocalVar  ghv_VCS,<size VolChars>       ; volume characteristics
LocalVar  ghv_GIO_Parm,BYTE                       ;#### DCR 1176 ####
LocalVar  ghv_GIO_Data,<size ghv_GIO_DataPack >   ;#### DCR 1176 ####
LocalVar  ghv_good_boot,BYTE                       ; b724625

        PMONLY

        DBPRT   80h,40h,<GenhVPB($x:$x) ax:$x\n>,<ds,si,ax>

;       Initialize the VPB as much as possible

        EnterProc <0,ds,si>             ; initialize DiskIOFlags and pDPB

        mov     LocalVPB.vpb_fsi.vpi_pDPB.Offst,si
        mov     LocalVPB.vpb_fsi.vpi_pDPB.Segmt,ds

        CALLFAR AllocReqPacketWait      ; es:bx -> req packet

        mov     es:[bx].PktCmd,CMDGetDevSupport           ; set up command code
        mov     es:[bx].PktLen,LENGetDevSpprt+PktHeadSize ; set up packet len

        mov     al,ds:[si].dpb_unit             ; get device unit
        mov     LocalVPB.vpb_fsi.vpi_unit,al    ; set up unit in VPB
        mov     es:[bx].PktUnit,al              ; Set up unit # for this driver

        mov     al,byte ptr ds:[si].dpb_drive   ; get drive
        mov     LocalVPB.vpb_fsi.vpi_drive,al   ; set drive
        mov     LocalVPB.vpb_fsi.vpi_flags,0    ; needs contig memory
                                                ; and cannot handle > 16M

        mov     es:[bx].PktStatus,0             ; initialize status
        lds     si,ds:[si].dpb_driver_addr      ; ds:si -> device header
        push    ds                              ;#### DCR 1176 ####
        push    si                              ;#### DCR 1176 ####

;       mov     ax,ds:[si].SDevAtt              ; device attributes    ;#### DCR 1176 ####
;       Dev_FuncLev     DEVLEV_3,ax             ; pre os2 2.0 driver ? ;#### DCR 1176 ####
;       jc      ghva00                          ;  yes                 ;#### DCR 1176 ####

;       test    ds:[si].SDevCaps,-1             ;                      ;#### DCR 1176 ####
;       jz      ghva00                          ; cannot handle >16MB memory ;#### DCR 1176 ####
;
;       or      LocalVPB.vpb_fsi.vpi_flags,VPB_ABOVE16M_ALLOWED        ;#### DCR 1176 ####

ghva00: CALLFAR DevIOCall2                      ; all registers are preserved
;
; The device has returned.  We must check to see whether the device
; recognized the GetDeviceSupport query and thus supports extended
; capabilities (ie, scatter/gather).
;
        test    es:[bx].ReqStat,STERR           ; error from driver?
        jnz     ghv00                           ; yes.
        lds     si,es:[bx].VCS_Addr             ; ds:si -> driver's VCS
        mov     ax,[si].VC_MaxSGList            ; ax = max s/g list size
        cmp     ax,FAT_MIN_MAX_SGLIST           ; do they meet our minimum?
        jb      ghv00                           ; no, they get strat1
;
; The driver recognized the extended query.  We must copy the DCS and VCS
; from the driver's address space to local address space, and store the address
; of the Strategy2 entry point in the dpb.  We put the addresses of the local
; DCS/VCS in the vpb so the FSD uses the stable local copies.
;
        lds     si,es:[bx].DCS_Addr             ; ds:si -> driver's DCS
        mov     edx,ds:[si].GDC_DD_Strategy2    ; edx -> strategy2 entry point
        mov     ax,ss
        SaveReg <es>                            ; save selector of req packet
        mov     es,ax
        lea     di,ghv_DCS                      ; es:di -> local DCS
        mov     LocalVPB.vpb_fsi.vpi_pDCS.Segmt,es
        mov     LocalVPB.vpb_fsi.vpi_pDCS.Offst,di ;set address of DCS in vpb
        .ERRNZ  (size DriverCaps) MOD 2
        mov     cx,(size DriverCaps)/2
        rep     movsw                           ; copy DCS to local DCS

        RestoreReg <es>                         ; es -> request packet
        lds     si,es:[bx].VCS_Addr             ; ds:si -> driver's VCS
        SaveReg <es>                            ; save selector of req packet
        test    ds:[si].VC_VolDescriptor,VC_RAM_DISK
        jz      ghv000
        les     di,pDPB
        or      es:[di].dpb_flags,DPBVCRAMDISK
ghv000: mov     es,ax
        lea     di,ghv_VCS                      ; es:di -> local VCS
        mov     LocalVPB.vpb_fsi.vpi_pVCS.Segmt,es
        mov     LocalVPB.vpb_fsi.vpi_pVCS.Offst,di ;set address of VCS in vpb
        .ERRNZ  (size VolChars) MOD 2
        mov     cx,(size VolChars)/2
        rep     movsw                           ; copy VCS to local VCS
        RestoreReg <es>                         ; es:bx -> req packet
        jmp     short ghv01                     ; continue.
;
; The driver did not recognize the extended query.  Store 0 as the dcs and vcs
; addresses in the vpb, and 0 as the strategy2 entry point in the dpb.
;
ghv00:  xor     edx,edx
        mov     LocalVPB.vpb_fsi.vpi_pDCS,edx
        mov     LocalVPB.vpb_fsi.vpi_pVCS,edx
;
; edx = strategy2 address to store in dpb.
;
ghv01:  lds     si,pDPB
        mov     ds:[si].dpb_strategy2,edx


; Use category 8, function 63h IOCTL to determine whether or not the
; device can address above 16Mb. "es:bx" still addresses the request packet.
        pop     si                                                          ;#### DCR 1176 ####
        pop     ds                              ; ds:si -> device header    ;#### DCR 1176 ####
        mov     ghv_GIO_Parm,0                                              ;#### DCR 1176 ####
        mov     es:[bx].PktCmd,CMDGenIOCTL                                  ;#### DCR 1176 ####
        mov     es:[bx].PktLen,PktHeadSize+LENGenIOCTL2                     ;#### DCR 1176 ####
        mov     al,LocalVPB.vpb_fsi.vpi_unit                                ;#### DCR 1176 ####
        mov     es:[bx].PktUnit,al                                          ;#### DCR 1176 ####
        mov     es:[bx].PktStatus,0                                         ;#### DCR 1176 ####
        mov     es:[bx].GIOCategory,8                                       ;#### DCR 1176 ####
        mov     es:[bx].GIOFunction,63h                                     ;#### DCR 1176 ####
        lea     ax,ghv_GIO_Parm                                             ;#### DCR 1176 ####
        mov     word ptr es:[bx].GIOParaPack,ax                                      ;#### DCR 1176 ####
        mov     es:[bx].GIOParaPack+2,ss                                    ;#### DCR 1176 ####
        lea     ax,ghv_GIO_Data                                             ;#### DCR 1176 ####
        mov     word ptr es:[bx].GIODataPack,ax                                      ;#### DCR 1176 ####
        mov     es:[bx].GIODataPack+2,ss                                    ;#### DCR 1176 ####
        mov     es:[bx].GIOSFN,0                                            ;#### DCR 1176 ####
        CALLFAR DevIOCall2                      ; all registers are preserved ;#### DCR 1176 ####
        test    ghv_GIO_Data.ghv_attributes,4                               ;#### DCR 1176 ####
        jz      ghv_next                                                    ;#### DCR 1176 ####
        or      LocalVPB.vpb_fsi.vpi_flags,VPB_ABOVE16M_ALLOWED             ;#### DCR 1176 ####
ghv_next:                                                                   ;#### DCR 1176 ####
        lds     si,pDPB                                                     ;#### DCR 1176 ####
;
; Even if the function 63h IOCTL did not reuse the request packet,          ;#### DCR 1176 ####
; this must be done all the way down here because it may block.  If the VCS
; had changed while it blocked we'd be in trouble.
;
        CALLFAR FreeReqPacket
;
; Now continue initializing vpb.
;
        xor     ax,ax
        mov     LocalVPB.vpb_flags,al
        mov     LocalVPB.vpb_fMisc,al
        mov     LocalVPB.vpb_ref_count,ax
        mov     LocalVPB.vpb_search_count,ax
        mov     LocalVPB.vpb_flink,ax   ; forward link pointer
        mov     LocalVPB.vpb_blink,ax   ; backward link pointer
        mov     LocalVPB.vpb_fsd.vpdFAT_access,ax   ; no one has access to volume
        mov     LocalVPB.vpb_fsd.vpdFAT_accwait,ax  ; no one waiting for access
        mov     LocalVPB.vpb_fsd.vpdFAT_alloc,ax  ; no one waiting for alloc/rel

        mov     LocalVPB.vpb_fsd.vpdFAT_pEASFT.Segmt,ax ; EA file not opened
        mov     LocalVPB.vpb_fsd.vpdFAT_pEASFT.Offst,ax
        mov     LocalVPB.vpb_fsd.vpdFAT_eareaders,ax; # threads doing reads
        mov     LocalVPB.vpb_fsd.vpdFAT_eawaiters,ax; # threads waiting to run
        mov     LocalVPB.vpb_fsd.vpdFAT_eahandles,ax; # handles in EAOffTable
        mov     LocalVPB.vpb_fsd.vpdFAT_eaflags,ax
        mov     LocalVPB.vpb_fsd.vpdFAT_pBadSector.LowWord,ax
        mov     LocalVPB.vpb_fsd.vpdFAT_pBadSector.HighWord,ax
        mov     LocalVPB.vpb_fsd.vpdFAT_pClusBitMap.LowWord,ax
        mov     LocalVPB.vpb_fsd.vpdFAT_pClusBitMap.HighWord,ax
        mov     LocalVPB.vpb_FSC.Segmt,FSC_BAD
        mov     LocalVPB.vpb_FSC.Offst,FSC_BAD

;       Since we are reading into a static buffer, we need to
;       exclude other threads from accessing it while we are using it.

        call    LockVBuf                ; gain access to internal buffer

        xor     ax,ax                   ;
        mov     cx,[si].dpb_cbSector    ; sector size
        DosContext  es
        les     di,pVPBBuf              ; es:di => VPB buf
        assume  es:nothing
        rep     stosb                   ; zero sector 0 buffer

;       Read in the boot sector.

        call    ReadBoot
        jnc     ghv0_4

;       Read failed zero out buffer

ghv0_3: mov     DevDrvErr,ax            ; AX = Device Driver error code (PTR B712810)

        pushf
        xor     ax,ax                   ;
        lds     si,pDPB                 ; (DS:SI) -> DPB
        mov     cx,[si].dpb_cbSector    ; sector size
        DosContext  es
        les     di,pVPBBuf              ; es:di => VPB buf
        assume  es:nothing
        rep     stosb                   ; zero sector 0 buffer
        mov     ax,DevDrvErr            ; AX = Device Driver error code
        popf
        jmp     ghv3

;       We presume it to be UNLABELLED.

ghv0_4: DosContext ds
        mov     si,offset DOSGROUP:lbl
        TaskContext es
        lea     di,LocalVPB.vpb_fsi.vpi_text
        mov     cx,lblLen
        rep movsb

;       Do I recognize the boot format?

                                                ; b724625
        les     di,pVPBBuf
        assume  es:nothing
; check to see if boot record is blown away (multiboot puts in all F6s)
        mov     al,0F6H
        mov     cx,(SIZE boot)
        repe    scasb
        cmp     cx,0
        jne     goodrec
        mov     [ghv_good_boot],0               ; assume bad boot record
        jmp     ghv1

goodrec:
        mov     [ghv_good_boot],1               ; good boot record
        lds     bx,pVPBBuf
        assume  ds:nothing
        cmp     [bx].boot_sig,BTIDKNOWN
        je      ghv0_5
        cmp     [bx].boot_sig,BTIDKNOWN2
        jne     ghv1

;       Known format.  Fill in relevant fields

        DBPRT   80h,40h,<new boot sector/volume ID\n>,<>
ghv0_5: mov     ax,[bx].boot_ID.LowWord
        mov     LocalVPB.vpb_fsi.vpi_ID.LowWord,ax
        mov     ax,[bx].boot_ID.HighWord
        mov     LocalVPB.vpb_fsi.vpi_ID.HighWord,ax
        xor     ax,ax
        mov     LocalVPB.vpb_fsd.vpdFAT_IDsector.LowWord,ax
        mov     LocalVPB.vpb_fsd.vpdFAT_IDsector.HighWord,ax
        or      LocalVPB.vpb_flags,VPBNEWBOOT

;       Now that we know what type of disk we have, we need to fill
;       in the media-specific parts of the VPB.  These were the old
;       DPB fields.

ghv1:   mov     LocalVPB.vpb_FSC.Offst,FSC_LOCAL
        mov     LocalVPB.vpb_FSC.Segmt,0
        lds     si,pDPB
        call    BuildBPB                ; old-style BPB construction
        jc      ghv0_3                  ; error b712810
;LJC    ghv3                    ; Error, check if DASD open

        mov     bl,[si].BPB_NumberOfFATs; (bl) = fats on volume
        mov     [NumberOfFats],bl       ; save the number of fats

        lea     di,LocalVPB             ; offset of local vpb on stack

        SaveReg <ds,si,bp>              ; ds:si-> BPB  BP = stack frame ptr
        call    FSMountVolume           ; Attempt to Mount the requested Volume
        RestoreReg <bp,si,ds>           ; ds:si-> BPB  BP = stack frame ptr
        mov     LocalVPB.vpb_fsi.vpi_pDCS,0 ; DCS/VCS no longer valid
        mov     LocalVPB.vpb_fsi.vpi_pVCS,0
        LJC      ghv3                   ; Error, check if DASD open

        cmp     LocalVPB.vpb_FSC.Offst,FSC_LOCAL  ; Did IFS Accept volume ?
        cmc
        LJNC    ghv10                   ; yes, then exit loop with carry clear

;
; The volume was not accepted by any of the FSDs. We default to the FAT FS.
; We need to be careful here since we don't want to allow "random" operations
; on the partition using FAT as the file system, especially if we have an IFS
; partition that is valid, but the IFS was not installed. This would cause a
; problem with data loss.
; On the other hand, we want to allow direct access OPEN calls so that the
; user can at least format the partition to something else if necessary.
;
; the master boot record is examined. if it's system index shows that it is
; a FAT partition, an attempt is made, via "SetVPB", to build a VPB using the
; driver provided BPB pointer. if this succeeds, the volume is made fully
; available to the FAT file system.
;
; if the master boot record shows that this is a non-FAT partition, or an
; error occurs trying to read the master boot record, or SetVPB returns
; an error, then another attempt is made to build the VPB, this time using
; the minimum BPB. this results in the field "vpdFAT_FAT_count" being set to
; zero. in turn, this causes the routine "FValidFatDisk" to inform its caller
; that this is a non-FAT disk, thus inhibiting access to the volume except via
; a direct access open.
;
    ASSUME   DS:NOTHING,ES:NOTHING
        cmp     [ghv_good_boot],0       ; is it a bad boot record
        je      ghv3_5                  ; b724625

        call    Check_if_FAT            ; is this a FAT volume?
        LJC     ghv3_5                  ; no, go install minimum BPB.

        TaskContext es
        lea     di,LocalVPB

; FAT VOLUME ; initialise vpi_flags
; FAT can do IO to non-contig. memory and also can take care of more than 16MB
; memory if DD can handle it.

        or      es:[di].vpb_fsi.vpi_flags,VPB_NONCONTIG_ALLOWED

        CALLFAR SetVPB                  ; set up all VPB fields from BPB in boot
        LJC     ghv3_5                  ; invalid BPB. Install minimum BPB

;       Find the user displayable VID.  If one is present, we use
;       it as the displayable name for the user.

        lds     si,pDPB
        lea     di,LocalVPB

;       We stash some info into the DPB for later use during hard error stuff

        mov     bx,es:[di.vpb_fsi.vpi_cbSector]
        mov     [si].dpb_cbSector,bx    ; optimization for hard error stuff
        mov     bl,es:[di.vpb_fsd.vpdFAT_media]
        mov     [si].dpb_media,bl
        and     es:[di].vpb_fMisc,NOT VPB_FM_UNKNOWN; mark the disk as FAT type
        cmp     [NumberOfFats],0        ; Does the volume have FATs?
        ja      ghv1_5                  ;   yes, it is a fat disk

        or      es:[di].vpb_fMisc,VPB_FM_UNKNOWN; mark the disk as UNKNOWN type

ghv1_5: call    FindVID                 ; es:di points to dir entry
    ASSUME   ES:NOTHING

;       If FindVID returned an error, we go and test to see if this is part of
;       a DASD open or a LOCK operation. If it is, we set up the VPB as for
;       an "unreadable" volume and assume that it is unreadable. This is
;       necessary since FORMAT needs to be able to perform these operations on
;       bad volumes.  We must, however, be sensitive to nested uncertain media
;       errors.

        jc      ghv3                    ; Error, check if DASD open
        jz      ghv2                    ; No VID found, use name procedure

        CALLFAR XCHGP                   ; ds:si points to dir entry
        TaskContext es
        lea     di,LocalVPB.vpb_fsi.vpi_text    ; es:di point to vpb_fsi.vpi_text area
        mov     cx,VPBTEXTLEN-1
    rep movsb                           ; move the VID in
        xor     al,al                   ; nul terminate it
        stosb

;       Old disks have the first sector of the FAT as the
;       checksum sector.  Read in the FAT sector.
;
;       NOTE: the first sector of the root directory is now used as the ID
;             sector because it is more likely to be unique

    ASSUME  ES:NOTHING

ghv2:   test    LocalVPB.vpb_flags,VPBNEWBOOT
        LJNZ    ghv5
        mov     dx,LocalVPB.vpb_fsd.vpdFAT_dir_sector.LowWord
        mov     LocalVPB.vpb_fsd.vpdFAT_IDsector.LowWord,dx
        mov     di,LocalVPB.vpb_fsd.vpdFAT_dir_sector.HighWord
        mov     LocalVPB.vpb_fsd.vpdFAT_IDsector.HighWord,di    ; DI:DX = ID sector
        mov     al,CMDINPUT
        DosContext ds
        les     bx,pVPBBuf              ; es:bx => VPB buf
        lds     si,pDPB                 ; ds:si => DPB
    ASSUME  DS:NOTHING
        mov     cx,1                    ; read in the first sector of the dir
        call    DiskIO                  ; try to get the sector
        jnc     ghv4                    ; no error, go CRC the buffer

;       We had an error reading the sector. We check to see if
;       this is part of an attempt to OPEN directly a drive. If
;       it is, we assume that the error was caused by an unformatted
;       disk in the drive. We set up the VPB appropriately and return
;       as though no error occurred. If the error was an "not ready"
;       error, we return since there is no disk in the drive.

ghv3:   LJZ     ghv10                   ; We got an "uncertain media" error

        and     ax,STECODE              ; Extract error code
        cmp     ax,ERROR_I24_NOT_READY
        LJE     ghv9                    ; Return error if drive not ready
        cmp     ax,ERROR_I24_DISK_CHANGE
        LJE     ghv9                    ; Return error if wrong disk
        cmp     ax,ERROR_I24_NOT_DOS_DISK
        je      ghv3_5                  ; Not dos disk, set up with minimum BPB
        cmp     ax,ERROR_I24_BAD_UNIT   ;81429  error_i24_bad_unit => 0x01
        jne     ghv3_4                  ;81429
        mov     ax,ERROR_BAD_UNIT       ;81429  error_bad_unit => 0x14
        jmp     ghv9                    ;81429
ghv3_4:                                 ;81429
        mov     di,CurrTCB
        test    ss:[di].TCBModeFlags,fOPENDASD+fLOCKDRV
        LJZ     ghv9                    ; This is not part of a DASD open oper

ghv3_5: TaskContext es
        DosContext ds
        lea     si,minimumBPB
        lea     di,LocalVPB
        CALLFAR SetVPB                  ; Set up info from "minimum BPB" in VPB
        LJC     ghv20                   ; minimum BPB was unacceptable

        DosContext ds
        mov     si,offset DOSGROUP:lbl2
        lea     di,LocalVPB.vpb_fsi.vpi_text
        mov     cx,lbl2Len
        rep     movsb                   ; copy "UNREADABLE" into vpb
        mov     LocalVPB.vpb_FSC.Offst,FSC_LOCAL    ; assume FAT
        mov     LocalVPB.vpb_FSC.Segmt,0
        or      LocalVPB.vpb_flags,VPBNEWBOOT       ; Assume new disk
        mov     LocalVPB.vpb_fsi.vpi_ID.LowWord,UNREAD_ID1
        mov     LocalVPB.vpb_fsi.vpi_ID.HighWord,UNREAD_ID2 ; Set up a unique ID
        or      LocalVPB.vpb_fMisc,VPB_FM_UNKNOWN ; mark disk as unknown type
        jmp     short ghv5

;       CRC the buffer

ghv4:   mov     cx,LocalVPB.vpb_fsi.vpi_cbSector
        DosContext ds
        lds     si,pVPBBuf              ; point to sector
    ASSUME  DS:NOTHING
        call    CRC                     ; get the checksum into DX,AX
        mov     LocalVPB.vpb_fsi.vpi_ID.LowWord,ax
        mov     LocalVPB.vpb_fsi.vpi_ID.HighWord,dx

;       We now have the entire VPB:
;
;       The BPB/media-specific info is filled in.
;
;       For new disks:
;           the ID sector is not relevant
;           the ID is the CRC from the boot sector
;           the VPB is marked as a new disk
;       For old disks:
;           the ID sector is the first sector of the first FAT
;           the ID is the CRC of that FAT sector
;           the VPB is marked as an old disk
;
;       For labelled disks:
;           the text is locked and not improvable
;           the text is taken from the VID found
;       For unlabelled disks:
;           the text is improvable
;           the text is "UNLABELLED"
;       For unreadable disks:
;           the text is "UNREADABLE"
;           the ID is a "unique" 32-bit ID
;
;       Find the VPB (examining ID only) in the internal
;       list, or, allocate a handle for this NEW vpb.

ghv5:   TaskContext ds
        lea     si,LocalVPB.vpb_fsi.vpi_ID
        call    VPBFind
ghv6:   or      ax,ax                   ; Did we find an old VPB ?
        jnz     ghv10                   ; Yes, ax = old vpb hVPB

;       We have exhausted the set of VPB's.  We need to allocate
;       the space, copy in the new VPB and link it into the list

ghv7:   lea     si,LocalVPB
        call    VPBAllocCopy
        jc      ghv9                    ; fail on no memory
        call    VPBLink
        clc
        jmp     short ghv10

;       Carry is set here iff:
;          we have run out of memory
;          the user failed a disk request
;          nested uncertain media
;
;       Carry is clear otherwise
;

ghv9:   or      sp,sp                   ; Reset the zero flag to indicate no
        stc                             ;  uncertain media error

;       Release the buffer as we are now done with it.

ghv10:  pushf
        jc      ghv11                   ; Error, VPB is not valid

        mov     di,ax                   ; (di) = hVPB
        VPBContext <es>,di              ; (es:di) -> VPB
        and     es:[di].vpb_fMisc,NOT VPB_FM_WRITEABLE  ; reset writeable flag

; A user can remove a floppy, copy/delete a file on another machine. So we
; need to invalidate the free count. Currently floppy can only be FAT, so
; this only needs to be done on FAT. But if other file system is supported
; on floppy, this code needs to be added to FSDs.

        cmp     es:[di].vpb_FSC.Offst,FSC_LOCAL ; FAT?
        jne     ghv11

        mov     es:[di].vpb_fsd.vpdFAT_free_cnt,-1      ; reset free count

ghv11:  call    UnlockVBuf
        popf
        LeaveProc
        ret

ghv15:  test    ax,ax                   ; ax != 0, zero flag cleared
        stc
        pushf
        jmp     short ghv11

ghv20:  InternalError <Minimum BPB invalid>,<ghv13>

EndProc   GenhVPB

;***    Check_if_FAT - checks if volume may be used by FAT file system
;
;
;       ENTER   Assumes that BP points to the local variable space acquired
;               by GenhVPB
;
;               Assumes that the caller has done a LockVBuf and uses that
;               buffer
;
;       EXIT    NC - volume may be used by FAT file system
;
;               CY - volume may not be used by FAT file system. this may
;                    be because of an error on an ioctl operation
;
;       PSEUDOCODE
;
;       USES    Flags, all register are preserved
;
;
;
Procedure Check_if_FAT,NEAR

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA

        SaveReg <ds,si,bp>              ; save various registers.
;
; check if the volume is removable
;
        push    ss                      ; pass a pointer
        lea     bx,IOParms              ; to the ioctl
        push    bx                      ; parameter packet.

        push    pDPB.HighWord           ; pass the pointer to the
        push    pDPB.LowWord            ; dpb of interest.

        call    Check_if_FAT_Init       ; initialize some of the ioctl stuff.

        mov     bx,dx                   ; point to the i/o buffer.
        mov     byte ptr ds:[bx],9      ; pre-set the returned value location
                                        ; to a recognizably invalid value.

        mov     ax,IODC_BR*256+IOC_DC   ; set up category (al=disk_category)
                                        ; and function (ah=block removable).

        mov     bx,0100h                ; set up the flags (bl=not a device
                                        ; and bh=protect mode pointers).

        call    sw_DevIOCtl             ; do the i/o.

        LJC     cif_80                  ; did it work? no, go return showing
                                        ; that the volume is not usable by FAT.

        DosContext ds                   ; set up the
        mov     bx,pVPBBuf.Offst        ; pointer to
        mov     ds,pVPBBuf.Segmt        ; the i/o
        Assume  ds:nothing              ; buffer.

        cmp     byte ptr ds:[bx],1      ; is the volume removable?
        LJNE    cif_60                  ; yes, go return to our caller showing
                                        ; that the volume may be used by FAT.
;
; the volume is non-removable - read its master boot record
;
        mov     IOParms.iop_command,1   ; initialize the command.
        mov     IOParms.iop_head,0      ; initialize the head number.
        mov     IOParms.iop_cyl,0       ; initialize the cylinder number.
        mov     IOParms.iop_sect,0      ; initialize the sector number.
        mov     IOParms.iop_sect_ct,1   ; initialize the number of sectors.
        mov     IOParms.iop_sec_num,1   ; build track
        mov     IOParms.iop_sec_siz,512 ; format map.

        push    ss                      ; pass a pointer
        lea     bx,IOParms              ; to the ioctl
        push    bx                      ; parameter packet.

        push    pDPB.HighWord           ; pass the pointer to the
        push    pDPB.LowWord            ; dpb of interest.

        call    Check_if_FAT_Init       ; initialize some of the ioctl stuff.

        mov     ax,IODC_RT*256+IOC_DC   ; set up category (al=disk_category)
                                        ; and function (ah=read_track).

        mov     bx,0100h                ; set up the flags (bl=not a device
                                        ; and bh=protect mode pointers).

        call    sw_DevIOCtl             ; do the i/o.

        jnc     cif_40                  ; did it work? yes, go take a look at
                                        ; the parition tables.

        cmp     ax,ERROR_BAD_COMMAND    ; no, did the driver reject our command?

        je      cif_80                  ; yes, go show that fat may use this
                                        ; volume (this is the path that we
                                        ; go thru with the vdisk driver).

        cmp     ax,ERROR_NOT_SUPPORTED  ; no, is it an unsupported command?

        je      cif_80                  ; yes, go show that fat may use this
                                        ; volume (this path is provided in
                                        ; case some old driver does this).

        jmp     short cif_55            ; no, go return showing that the
                                        ; volume is not usable by FAT.

cif_40: DosContext ds                   ; set up the
        mov     bx,pVPBBuf.Offst        ; pointer to
        mov     ds,pVPBBuf.Segmt        ; the i/o
        Assume  ds:nothing              ; buffer.

        add     bx,offset parttbl       ; move down to first partition table.

        mov     cx,MAXPART              ; set up the loop control.

cif_50: call    Check_if_FAT_Check      ; does the system index show that
                                        ; fat may use this volume?
     ;  jnc     cif_80                  ; yes, go return showing that.
        jnc     cif_58
        add     bx,size RawPartRec      ; move down to the next parition table.
        loop    cif_50                  ; another one? yes, go round again.

cif_55: stc                             ; no, go return showing that the
        jmp     short cif_80            ; volume may not be used by FAT.
cif_58:
        push    ds
        DosContext ds
        mov     fatvolume,1             ; set flag that we have 1 or more FAT
        pop     ds                      ; partitions
cif_60: clc                             ; show that FAT may use this volume.

cif_80: RestoreReg <bp,si,ds>           ; restore various registers.

        ret                             ; return to our caller.

;***    Check_if_FAT_Check - checks if the system index reflects a FAT
;                            usable partition
;
;       ENTER   ds:bx points to a raw partition record
;
;       EXIT    NC - system index does reflect a FAT usable partition
;
;               CY - system index does not reflect a FAT usable partition
;
;       PSEUDOCODE
;
;       USES    Flags, all register are preserved
;
Procedure Check_if_FAT_Check

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA

        cmp     ds:[bx.system_index],PART_SMALL_DOS ; is it a small dos partn?
        je      cifc_9                              ; yes, go return showing ok.
        cmp     ds:[bx.system_index],PART_MED_DOS   ; no, is it a medium dos partn?
        je      cifc_9                              ; yes, go return showing ok.
        cmp     ds:[bx.system_index],PART_HUGE_DOS  ; no, is it a huge dos partn?
        je      cifc_9                              ; yes, go return showing ok.

        stc                                         ; no, show that the volume
                                                    ; cannot be used by FAT.

cifc_9: ret                                         ; return to our caller.

EndProc Check_if_FAT_Check

;***    Check_if_FAT_Init - initialize some of the ioctl interface
;
;        this is a private subroutine for use only by Check_if_FAT
;
Procedure Check_if_FAT_Init

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA

        mov     di,0                    ; set up a dummy SFN.

        mov     cx,size iop             ; set up the parameter packet size.
        mov     si,LocalVPB.vpb_fsi.vpi_pDPB.Offst ; set up the
        mov     ds,LocalVPB.vpb_fsi.vpi_pDPB.Segmt ; data packet
        mov     si,[si].dpb_cbSector               ; size.

        DosContext ds                   ; set up the
        mov     dx,pVPBBuf.Offst        ; pointer to
        mov     ds,pVPBBuf.Segmt        ; the i/o
        Assume  ds:nothing              ; buffer.

        ret                             ; return to our caller.

EndProc Check_if_FAT_Init

EndProc Check_if_FAT

;***    GenRemotehVPB - generate a vpb for a remote drive
;
;   Remote VPBs are static.  There is one per remote drive and they
;   are created when the drive is attached and destroyed when the drive
;   is detached.  When updating TCBThishVPB in FSCfromCDS, we take the hVPB
;   from the CDS, where it was stored during attach.  Since the sft and
;   search structure hVPBs are initialized from TCBThishVPB, setting it
;   correctly here takes care of those structures.
;
;   It is very important to set the ref-count to 1 so that when we go
;   through the Inc/DecVPBRef sequence, the VPB does not get freed.
;   It should only get freed when we do a DETACH operation.
;
;
;       ENTER   AL = drive letter
;               TASKAREA:DI -> temp vpb with FSC pointer and vpbfsd filled in
;                              (must be in TASKAREA - VPBAllocCopy expects it)
;
;       EXIT    NC - no error
;                   VPB is linked into chain
;                      VPB is zeroed out except:
;                      vpb_fsi.vpi_ID = drive name (repeated 4 times)
;                      vpb_FSC -> FSC
;                      vpbfsd copied in
;                      vpb_fMisc = VPB_REMOTE_DRIVE
;                   AX = hVPB
;               CY - error, AX contains error code
;
;       PSEUDOCODE
;          initialize VPB
;          if (found = VPBFind(volid))
;             internal error
;          if (error = VPBAllocCopy())
;             return
;          VPBLink()
;
;       USES    AX, DI, SI, DS, ES, Flags
;
Procedure GenRemotehVPB,FARONLY
    ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
LocalVar  grv_pVPB_Off,WORD             ; Offset to VPB in TASKAREA
LocalVar  grv_drive,WORD                ; drive

        PMONLY

        EnterProc <di,ax>
        TaskContext DS
        TaskContext ES
        mov     si,di                   ; ES:SI -> VPB

;       check for al=0, this means that FSC_Add is calling this routine
;       to create a VPB for an UNC redirector.  Its ID has already
;       been set by FSC_Add, so we need to set di past vpb_fsi.vpi_id

        or      al,al
        jnz     grv05
        lea     di,[si].vpb_fsi.vpi_pDPB
        jmp short grv10

;       store drive letter as ID and zero out the rest of the VPB
;       set vpb_ref_count to 1 so that VPB never goes away

grv05:  lea     di,[si].vpb_fsi.vpi_ID  ; (ES:DI) -> vol id
        mov     cx,vpi_pDPB-vpi_ID
        rep stosb                       ; volume id = drive letter repeated

grv10:  mov     cx,size vpbfsi - vpi_pDPB
        xor     ax,ax                   ; (ES:DI) -> vpbfsi.vpi_pDPB
        rep stosb                       ; zero out rest of vpbfsi
        mov     cx,vpb_FSC - vpb_flink
        lea     di,[si].vpb_flink       ; (ES:DI) -> vpb_flink
        rep stosb                       ; zero out rest of vpb
        mov     [si].vpb_ref_count,0    ; Set count to zero initially
        mov     [si].vpb_fMisc,VPB_REMOTE_DRIVE

        lea     si,[si].vpb_fsi.vpi_ID  ; (DS:SI) -> vol id
        call    VPBFind
        or      ax,ax                   ; Did we find an old VPB ?
        stc
        jnz     grv20                   ; yes, go return error

; We need to allocate the space, copy in the new VPB and
; link it into the list

        mov     si,grv_pVPB_Off         ; SI = offset of VPB on stack
        call    VPBAllocCopy            ; ES:DI -> New VPB
        jc      grv20                   ; fail on no memory
        SaveReg <ax>
        mov     ax,grv_drive
        sub     al,'A'
        mov     es:[di].vpb_fsi.vpi_drive,al   ; set drive
        call    VPBLink
        SaveReg <BP>
        mov     bp,di                   ; BP = hVPB
        call    IncVPBRef               ; Make sure VPB does not go away until we DETACH the drive
        RestoreReg <BP>
        RestoreReg <ax>
        clc

grv20:  LeaveProc
        ret

EndProc   GenRemotehVPB



Break   <FSMountVolume - Check to see if an IFS Drivers recognizes this Volume

;***  FSMountVolume - Check to see if an IFS Drivers recognizes this Volume
;
;   FSMountVolume Loops through the FSC chain calling each FS Driver FS_MOUNT
;   entry point to determine if that IFS recognizes this Volume. The loop will
;   will terminate when the first IFS recognizes the volume or when we reach
;   the end of FSC chain.
;
;   Inputs:     pVPBBuf boot sector
;               di      offset of LocalVPB on Stack
;               DS:SI-  BPB constructed by driver
;
;   Outputs:    carry clear
;                 if an FSD accepted the volume
;                    ax = hVPB
;                    LocalVPB.vpb_FSC.Offst = offset of fsc
;                 else
;                    LocalVPB.vpb_FSC.Offst is unchanged
;               carry set
;                 zero set
;                     ax = ERROR_I24_UNCERTAIN_MEDIA
;   Registers modified: ax,bp,bx,di,es,si,ds

;struct bpb *pVPBBBuf;
;struct vpb *LocalVPB;
;{
;        int i;
;        struct vpb *vpb_ptr,*oldvpb_ptr;
;        struct fsc *fsc_ptr;
;        unsigned long far *pattribute;
;
;LocalVPB -> vpb_fsi.vpi_cbsector = pVPBBuf -> boot_bpb.BPB_BytesPerSector;
;
;if (pVPBBuf -> boot_bpb.BPB_TotalSectors == 0) {
;
; LocalVPB->vpb_fsi.vpi_totsec.LowWord = pVPBBuf->boot_bpb.BPB_BigTotalSectors.LowWord;
; LocalVPB->vpb_fsi.vpi_totsec.hiword = pVPBBuf->boot_bpb.BPB_BigTotalSectors.hiword;
;}
;
;else {
;
;  LocalVPB->vpb_fsi.vpi_totsec.LowWord = pVPBBuf->boot_bpb.BPB_TotalSectors;
;  LocalVPB->vpb_fsi.vpi_totsec.hiword = 0;
;}
;
;LocalVPB -> vpb_fsi.vpi.trksec = pVPBBuf -> boot_bpb.BPB_SectorsPerTrack;
;LocalVPB -> vpb_fsi.vpi.nhead = pVPBBuf -> boot_bpb.BPB_Heads;
;
;if ((rc = VPBAllocCopy(LocalVPBPtr)) != 0
;   Return (rc);
;
;VPBContext (&vpb_ptr,hVPB);
;
;MemCopy (vpb_ptr,LocalVPB);
;vpb_ptr -> vpb_flags = vpb_ptr -> vpb_flags | VPBMOUNT;
;
;
;while (fsc_ptr = fscNext, NC) {
;     pattribute = fsc_ptr.FS_ATTRIBUTE
;     if (*pattribute & FS_ATTR_REMOTE)
;       continue;
;
;     rc = fsc_ptr -> FS_MOUNT(FSMOUNT,&vpb_ptr->vpb_fsi,&vpb_ptr->vpb_fsd,
;                                      hVPB,pVPBBuf);
;     if (rc == ERROR_I24_UNCERTAIN_MEDIA || rc == 0)
;       break;
;}
;
;if (rc == 0) {
;   vpb_ptr -> vpb_FSC = fsc_ptr;
;   vpb_ptr -> vpb_flags = vpb_ptr -> vpb_flags & ~VPBMOUNT
;   LocalVPB-> vpb_FSC.Offst = fsc_ptr.Offst;
;
;   if ((oldhVPB = VPBFind(hVPB)) == 0 {
;     VPBLink(hVPB);
;     Return(hVPB);
;   }
;   else {
;     FSDMount(hVPB,UNMOUNT);
;     VPBFree(hVPB);
;     Return(oldhVPB);
;   }
;}
;else {
;    VPBFree (hVPB);
;    Return ();
;}
;

        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure FSMountVolume,NEAR,LOCAL

; Get information from the BPB and store it in the Local VPB on the stack

        mov     bx,si                              ; ds:bx ->bpb
        mov     ax,[bx].BPB_BytesPerSector         ; get sector size
        mov     ss:[di].vpb_fsi.vpi_cbSector,ax    ; store sector size in vpb
        mov     ax,[bx].BPB_TotalSectors           ; get total sectors
        xor     si,si                              ; init hiword total sectors
        or      ax,ax                              ; loword total sectors=0 ?
        jnz     fsm0                               ; no, go store total sector
        mov     ax,[bx].BPB_BigTotalSectors.LowWord ; loword ext_total sectors
        mov     si,[bx].BPB_BigTotalSectors.HighWord ; hiword ext_total sectors
fsm0:   mov     ss:[di].vpb_fsi.vpi_totsec.LowWord,ax  ; loword total sectors
        mov     ss:[di].vpb_fsi.vpi_totsec.HighWord,si ; hiword total sectors
        mov     ax,[bx].BPB_SectorsPerTrack        ; get sectors/track
        mov     ss:[di].vpb_fsi.vpi_trksec,ax      ; store sectors/track
        mov     ax,[bx].BPB_Heads                  ; get number of heads
        mov     ss:[di].vpb_fsi.vpi_nhead,ax       ; store number heads
        mov     si,di                              ; local VPB on stack

; Allocate new VPB and copy VPB on the stack into that area

        call    VPBAllocCopy
        LJC     fsm5                               ; fail on no memory
        or      es:[di].vpb_flags,VPBMOUNT         ; indicate mount in progress

; Initialize loop and pointer variables

        lea     cx,es:[di].vpb_fsd                 ; GDT_VPB:CX -> vpb_fsd area
        lea     bx,es:[di].vpb_fsi                 ; GDT_VPB:BX -> vpb_fsi area
        mov     bp,di                              ; bp = vpb handle
        DosContext  ds
        lds     dx,pVPBBuf                         ; ds:dx -> sector 0 buffer
        Assume  DS:Nothing

        xor     di,di                              ; start with first FSC
fsm1:   call    fscNext                            ; es:di -> fsc
        LJC     fsm3_1                             ; no more, go return

; If we are not at the end of the list, Call FSD to Mount the requested volume


; If this is a Remote FSD then we should not invoke it for a local Mount.
; We test the attribute word in FSC to determine if this FSD is Remote.

        SaveReg <es,si>
        les     si,es:[di].FS_ATTRIBUTE
        test    word ptr es:[si],FS_ATTR_REMOTE    ; Remote FSD ?
        RestoreReg <si,es>
        jnz     fsm1                               ; Yes, Skip Mount, go get
                                                   ;     next FSC.
        SaveReg <es,cx,dx,bx>                      ; Save Regs destroyed by IFS

        push    FSM_MOUNT                          ; MOUNT=0 Mount This Volume
        push    GSEL GDT_VPB                       ;
        push    bx                                 ; GDT_VPB:BX -> vpb_fsi
        push    GSEL GDT_VPB                       ;
        push    cx                                 ; GDT_VPB:CX -> vpb_fsd
        push    bp                                 ; VPB used for FSHDOVolIO
        push    ds                                 ;
        push    dx                                 ; ds:dx -> sector 0 buffer
        call    es:[di].FS_MOUNT                   ; Call FSD MOUNT entry point
        RestoreReg <bx,dx,cx,es>                   ; RestoreReg destroyed by IFS

; Evaluate ret.code exit loop if uncertain media error or no error.

        or      ax,ax                              ; error ?
        jz      fsm2                               ; No, return es:[di]= @FSC
        cmp     ax,ERROR_UNCERTAIN_MEDIA           ; Uncertain media ?
        stc                                        ; set carry in case error
        jz      fsm4                               ; yes,ret. error indicators
        jmp     short fsm1                         ; go get next FSC

fsm1_5: add     di,size FS_ENTRY                   ; offset to next FSC
        jmp     short fsm1

; An FSD accepted the volume, see if there are duplicate VPBs' ES:DI-> FSC

fsm2:   mov     ax,es                              ; FSC segment
        VPBContext ES,BP                           ; es:bp -> VPB
        mov     es:[bp].vpb_FSC.Offst,di           ;
        mov     es:[bp].vpb_FSC.Segmt,ax           ; ax:di -> fsc
        and     es:[bp].vpb_flags,not VPBMOUNT     ; indicate mount finished
        mov     ss:[si].vpb_FSC.Offst,di           ; store fsc offset in local
        VPBContext DS,BP
        lea     si,ds:[bp].vpb_fsi.vpi_ID          ; ds:si -> mru volume id
        call    VPBFind

        or      ax,ax                              ; Found match ?
        jz      fsm3                               ; no,skip vpb link

; UNMOUNT the Volume just Mounted because we found a duplicate and we will
; continue to use the old VPB.

        mov     bx,FSM_UNMOUNT                     ; Unmount new volume
        call    FSDMount                           ; bp = new hVPB bx = UNMOUNT
        clc                                        ; no error, ax = old VPB
        jmp     short fsm4                         ; free new vpb

; Link new VPB into list
; ds:bp -> new vpb  just created and accepted by some FSD
;
; BUGBUG Nagara 3/3/90
; This VPB stil does not have the VPI_TEXT filled in; This does not affect
; our functioning since we don't seem to trust this vpi_text anywhere
; END BUGBUG

fsm3:   mov     di,bp                              ; VPB offset
        mov     ds:[di].vpb_fsi.vpi_pDCS,0      ; DCS/VCS no longer valid
        mov     ds:[di].vpb_fsi.vpi_pVCS,0      ; DCS/VCS no longer valid
        push    GSEL GDT_VPB                    ;### Make sure es points
        pop     es                              ;### to the VPB selector
        call    VPBLink                            ; link new VPB into list
        clc                                        ; indicate no error
        mov     ax,bp                              ; return handle
        jmp     short fsm5

; We simply ran off the end of the FSC, so return 'NC', freeing VPB first

fsm3_1: xor     ax,ax                              ; Clear 'C', zero VPB as
                                                   ; prophylactic measure.

; Free New VPB because we found a duplicate VPB or there was an error

fsm4:   pushf                                      ; save flags carry and zero
        mov     bl,1                               ; Only Free VPB, no unlink
        call    VPBFree                            ; free VPB
        popf                                       ; restore flags
fsm5:   ret
EndProc FSMountVolume

Break   <FSDMount - Invokes an FSD to perform a Mount function.

;***  FSDMount - Invokes an FSD to perform a Mount function.
;
;   FSDMount Invokes an FSD to perform a Mount function.
;
;   Inputs:     bp      handle of volume
;               bx      Mount flag 0 = MOUNT 1 = VOLREMOVED 2 = UNMOUNT
;
;   Outputs:    NONE
;
;   Registers modified: NONE
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure FSDMount,NEAR

        pusha
        VPBContext ES,BP                        ; es:bp -> VPB
        push    bx                              ; MOUNT flag
        lea     si,[bp].vpb_fsi                 ; es:si -> vpb_fsi
        push    es                              ;
        push    si                              ; es:si -> vpb_fsi
        lea     si,[bp].vpb_fsd                 ; es:si -> vpb_fsd
        push    es                              ;
        push    si                              ; es:si -> vpb_fsd
        les     di,es:[bp].vpb_FSC              ; es:di -> fsc
        push    bp                              ; handle of vpb to unmount
        push    0
        push    0                               ; null boot sector pointer
        call    es:[di].FS_MOUNT                ; call FSD mount entry point
        popa

        ret
EndProc FSDMount

BREAK   <FindVID - scan root directory looking for volid>

;
;   FindVID - during the detection of a volume switch, we need to ascertain a
;   displayable name for the user.  If a volume ID is present, we will use it.
;   We read in the root directory looking for a dir entry with the VID attribute
;   on.  Any errors are reported.
;
;   Inputs:     ds:si points to DPB
;               es:di points to a VPB not yet installed in VPB database
;               pVPBBuf is locked
;   Outputs:    carry set => hard error detected
;                   (AX) = status word from device
;                   zero set => nested uncertain media error
;                   zero reset => operation was failed
;               carry clear =>
;                   zero set => no matching ID was found
;                   zero reset => es:di point to dir entry found
;   Registers modified: all
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure FindVID,NEAR,LOCAL

LocalVar  dirsec,DWORD
LocalVar  cDir,WORD

        PMONLY
        DBPRT   80h,40h,<FindVid($x:$x,$x:$x>,<ds,si,es,di>
        EnterProc

        mov     ax,word ptr es:[di].vpb_fsd.vpdFAT_dir_sector
        mov     dirsecl,ax
        mov     ax,word ptr es:[di].vpb_fsd.vpdFAT_dir_sector+2
        mov     dirsech,ax

        mov     ax,es:[di].vpb_fsd.vpdFAT_root_entries
        mov     cDir,ax

fvtop:  cmp     cDir,0                  ; any more directory entries to scan?
        jz      fvdone                  ; nope, can't find VID
                                        ; (zero SET, carry RESET)
        mov     al,CMDINPUT
        DosContext es
        les     bx,pVPBBuf
    ASSUME  ES:NOTHING
        mov     cx,1
        mov     dx,dirsecl
        mov     di,dirsech
        call    DiskIO                  ; read in the directory sector
        jc      fvdone                  ; the read failed.  So do we (CARRY SET)

        DosContext es
        les     di,pVPBBuf              ; point to buffer
    ASSUME  ES:NOTHING
        xor     bx,bx                   ; dir entry pointer set to beginning

fvscn:  cmp     bx,[si].dpb_cbSector
        jae     fvbrk                   ; pointing beyond end of buffer
        cmp     cDir,0                  ; any more directory entries?
        jz      fvbrk                   ; no, all done

        cmp     es:[di+bx].dir_name,0   ; end of directory?
        jz      fvdone                  ; yes, no VID found
                                        ; (zero SET, carry RESET)

        cmp     es:[di+bx].dir_name,0E5h    ; deleted entry?
        jz      fvnxt                   ; yes, skip it

        test    es:[di+bx].dir_attr,attr_volume_id
        jnz     fvfnd                   ; we've found a VID
                                        ; (zero RESET, carry RESET)
fvnxt:  add     bx,size dir_entry       ; point to next dir entry
        dec     cDir                    ; one less entry scanned
        jmp     fvscn                   ; back for more in this sector

fvbrk:  add     dirsecl,1               ; INC does not affect CARRY
        adc     dirsech,0               ; advance to next directory sector
        jmp     fvtop

fvfnd:  lea     di,[di+bx]              ; point es:di to dir entry

fvdone: LeaveProc
        DBPRT   80h,40h,<)>
        ret
EndProc   FindVID


;***    ForceMount - Mount the volume forcefully onto the FSD specified
;
;       Used by BeginFormat to do the CLOSE-UNMOUNT-MOUNT-OPEN sequence.
;       This is needed for BeginFormat for mounting a volume that has
;       already been mounted onto another FSD.
;
;       ENTRY       DS:SI -> SFT for handle (identifies the FSD on which
;                            the volume is currently mounted)
;                   ES:DI -> FSC for FSD on which we are going to mount
;                            the volume
;                   AX       Type of MOUNT for the "UNMOUNT" call (always
;                            FSM_UNMOUNT)
;                   BX       Type of MOUNT for the "MOUNT" call
;                            (either FSM_MOUNT or FSM_FORMAT)
;
;       EXIT        'NC'    The operation was a success
;                   'C'     There was an error
;                               AX = error code
;
;       USES        AX, BX, CX, DX, ES, DS, DI, SI, Flags
;
;       PSUEDOCODE
;
;       Take_Kernel_Sem                     /* Enter FS crit section */
;       FSD_Close (SFT)                     /* clean up old FSD */
;
;       if (SFT.FSC.Offset < FSC_Reserved)  /* FSD is attached */
;           FS_MOUNT (volremoved)           /* detach current FSD */
;
;       if (new FSC.Offst < FSC_Reserved)   /* not FAT */
;           FS_MOUNT (format_mount)         /* attach new FSD */
;       else
;           FAT_Mount                       /* set up VPB for FAT */
;       Set SF_Format_Mount                 /* remember we did this */
;       Set VPBFORMATMOUNT in VPB           /* dont gen. newVPB till */
;                                           /* we do redetermineMedia */
;       Set VPBINVALID in VPB               /* invalidate searches */
;                                           /*  on volume */
;       FSD_Open (SFT)                      /* open this instance */
;       Clear_Kernel_Sem                    /* Leave FS crit section */
;       if (error)
;           MarkSFTBad()
;       }
;
;  Change history:
;  ---------------
;  73961 09/15/93 Nizinski Changed to hybrid procedure since some former
;                          callers moved from HIGH2CODE segment to HIGH3CODE.
Procedure   ForceMount,HYBRID               ; 73961
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar    fm_pSFT,DWORD                   ; pointer to SFT
LocalVar    fm_pFSC,DWORD                   ; pointer to specified FSC
LocalVar    fm_hVPB,WORD                    ; hVPB for this SFT
LocalVar    fm_OpenMode,WORD                ; Save mode between close & open
LocalVar    fm_OpenMode2,WORD               ; Save ext.mode between close & open
LocalVar    fm_OpenParms,<size Open3Struc>  ; argument frame for FSD_OpenCreate
LocalVar    fm_OpenPath,4                   ; for "d:\" pathname

        EnterProc <ds,si, es,di>           ; Save away pSFT and pFSC.

;*  We must now do a Close & an Open; first we must 'Lock FS Critical Section'
;   (because it uses PTDA variables)
;
        CALLFAR Take_Kernel_Sem             ; get kernel semaphore
        LJC     fm95                        ; 'C' = abnormal wakeup

;*  Must now call FSD_Close; to detach the FSD/FAT from this SFT

;*  First we must increment the VPB reference count so that FSD_Close will not
;   cause it to be freed.  We also save the hVPB to put back in the SFT after
;   the close.

        SaveReg <si>
        SaveReg <bp>
        mov     bp,ds:[si.sf_fsi].sfi_hVPB
        mov     si,bp                       ; SI = hVPB
        call    IncVPBRef                   ; so VPB won't go away
        RestoreReg  <bp>

        mov     fm_hVPB,si                  ; save hVPB
        RestoreReg  <si>                    ; DS:SI -> SFT

        mov     bx,ds:[si].sf_fsi.sfi_mode
        mov     fm_OpenMode,bx              ; save Mode (for later open)
        mov     bx,ds:[si].sf_fsi.sfi_mode2
        mov     fm_OpenMode2,bx             ; save ext. Mode (for later open)

        mov     bx,CurrTCB

        mov     TASKAREA:[bx].TCBThisSFT.Segmt,ds
        mov     TASKAREA:[bx].TCBThisSFT.Offst,si   ; TCBThisSFT -> current SFT

;*  we must preserve the sf_flags, and set sf_noJFN, so FSD_Close (really
;   MFTClose) will remove SFT from the sharing set.  Then we must restore the
;   original sf_flags.

        push    ds:[si].sf_flags            ; preserve SFT flags
        or      ds:[si].sf_flags,SF_NOJFN   ; pretend no JFN associated
        mov     bx,-1                       ; if no JFN, no handle also

        call    FSD_Close                   ; ES:DI -> SFT
    ASSUME DS:NOTHING
        pop     bx                          ; BX = original SFT_flags
        pushf                               ; save flags from FSD_Close

;*  If original SF_noJFN was off, then we must reset it here

        test    bx,SF_NOJFN                 ; was original sf_noJFN on?
        jnz     fm25                        ; yes, just go on
        and     es:[di].sf_flags,NOT SF_NOJFN   ; no, turn it off

;*  We must increment the SFT reference count.  FSD_Close decremented it,
;   FSD_Open does not touch it (it assumes it was set to 1).

fm25:   inc     es:[di].sf_ref_count

        popf                                ; restore flags from FSD_Close
        LJC     fm97                        ; special error cleanup

;*  If old FSD exists (ie not FAT), then tell FSD that volume removed

        SaveReg <es,di>
        RestoreReg  <si,ds>                 ; DS:SI -> SFT

        les     di,ds:[si].sf_FSC           ; ES:DI -> FSC
        mov     ds:[si].sf_FSC.Offst,FSC_BAD
        mov     ds:[si].sf_FSC.Segmt,FSC_BAD    ; mark FSC 'bad' since SFT was closed
        cmp     di,FSC_RESERVED             ; FSC >= reserved value?
        je      fm28                        ; FAT, skip unmount

        mov     si,fm_hVPB                  ; SI     = SFT's hVPB
        VPBContext  ds,si                   ; DS:SI => VPB

        push    FSM_UNMOUNT                 ; clean up old FSD

        push    ds                          ; push pointer to fsi portion
        lea     bx,[si].vpb_fsi
        push    bx

        push    ds                          ; push pointer to fsd portion
        lea     bx,[si].vpb_fsd
        push    bx

        push    si                          ; push hVPB
        push    0                           ; pBoot is NULL
        push    0

        call    es:[di].FS_MOUNT            ; do the VolumeRemoved
    ASSUME DS:NOTHING
        xor     bx,bx
        cmp     bx,ax                       ; sets 'C' if AX != 0
        LJC     fm97                        ; special error cleanup

;
; The UNMOUNT succeeded. We now clear sfi_hVPB since there is no volume
; associated with this SFT at this time.
;
        les     di,fm_pSFT                  ; ES:DI -> SFT
        mov     es:[di].sf_fsi.sfi_hVPB,NULLVPB
        jmp     short fm30                      ;

; FAT volume unmount. We just do an UnHogEAFile
fm28:   pusha
        mov     bx,fm_hVPB                  ; BX     = SFT's hVPB
        SaveReg <ds>
        CALLFAR UnHogEAFile             ; Remove EA file from sharing
        RestoreReg      <ds>

; the VPB is going to be re used; so clear the VPDFAT_EASFT field

        mov     di,fm_hVPB
        VPBContext      es,di
        xor     ax,ax
        mov     es:[di].vpb_fsd.vpdFAT_pEASFT.Segmt,ax  ; ea file sft <= NULL
        mov     es:[di].vpb_fsd.vpdFAT_pEASFT.Offst,ax
        mov     es:[di].vpb_fsd.vpdFAT_eaflags,0        ; reset flag

        popa

;*  If real FSD was specified, format mount this volume

fm30:   les     di,fm_pFSC                  ; ES:DI -> FSC

        mov     si,fm_hVPB                  ; SI     = SFT's hVPB
        VPBContext  ds,si                   ; DS:SI => VPB
        mov     ds:[si].vpb_FSC.Offst,di
        mov     ds:[si].vpb_FSC.Segmt,es    ; update VPB.FSC

        cmp     di,FSC_RESERVED             ; was FAT specified
        jae     fm40                        ; yes, do special FAT mount

;*  Call FS_MOUNT (FormatMount or Regular mount)
;
;   DS:SI -> VPB
;   ES:DI -> FSC
;
        push    FSM_FORMAT                  ; Do the MOUNT

        push    ds                          ; push pointer to fsi portion
        lea     bx,ds:[si].vpb_fsi
        push    bx

        push    ds                          ; push pointer to fsd portion
        lea     bx,ds:[si].vpb_fsd
        push    bx

        push    si                          ; push hVPB
        push    0                           ; pBoot is NULL
        push    0

        call    es:[di].FS_MOUNT            ; do the format mount
        xor     bx,bx
        cmp     bx,ax                       ; sets 'C' if AX != 0
        jnc     fm50                        ; continue if no error
;
; Got an error doing the MOUNT. Zero out sfi_hVPB.
;
        les     di,fm_pSFT
        mov     es:[di].sf_fsi.sfi_hVPB,NULLVPB
        jmp     fm97

;*  For the FAT case, we must initialize the VPB
;
fm40:   DosContext  ds
        lea     si,minimumBPB               ; DS:SI -> minumum BPB

        mov     di,fm_hVPB                  ; DI     = SFT's hVPB
        VPBContext  es,di                   ; ES:DI => VPB

        CALLFAR SetVPB                      ; Set up info from "minimum BPB" in VPB
        LJC     fm97                        ; Error. Do special cleanup.
;
; The MOUNT onto the new FSD was successful. We now mark all the CDSs that
; were on this VPB to be at the root.
; We then call FSD_OpenCreate to attach this SFT to the new FSD
;
fm50:   mov     di,fm_hVPB                  ; DI     = SFT's hVPB
        VPBContext  es,di                   ; ES:DI => VPB
        lds     si,es:[di].vpb_fsi.vpi_pDPB ; DS:SI -> DPB
        mov     ax,di                       ; AX = hVPB
        call    ResetCurrency               ; Clear currency info in CDSs
        lds     si,fm_pSFT                  ; DS:DI -> SFT
        or      ds:[si].sf_flags2,SF_FORMAT_MOUNT   ; set format_mount flag

;  DS:SI -> SFT
;  ES:DI -> VPB

    ASSUME  ds:nothing

        or      es:[di].vpb_flags,VPBFORMATMOUNT + VPBINVALID
                                        ; to indicate begin format
                                        ; will be cleared by redetermine media
                                        ; till then, vpb will not be changed
                                        ; invalid bit is used to indicate
                                        ; that searches on the drive are now
                                        ; invalid

        mov     bx,CurrTCB

        mov     TASKAREA:[bx].TCBThisSFT.Offst,si   ; already ds:si -> SFT
        mov     TASKAREA:[bx].TCBThisSFT.Segmt,ds   ; TCBThisSFT -> current SFT

        mov     si,fm_pFSC.Offst
        mov     TASKAREA:[bx].TCBThisFSC.Offst,si
        mov     si,fm_pFSC.Segmt
        mov     TASKAREA:[bx].TCBThisFSC.Segmt,si   ; TCBThisFSC -> new FSC

        mov     TASKAREA:[bx].TCBThishVPB,di

        TaskContext DS                      ; for FSD_OpenCreate

        les     di,es:[di].vpb_fsi.vpi_pDPB ; ES:DI -> DPB
        mov     al,es:[di].dpb_drive        ; Al = Logical drive # (A=0,B=1,...)
        add     al,"A"                      ; AL = 1 based drive letter
        mov     ah,":"                      ; AX = "d:"
        lea     di,fm_OpenPath              ; ss:di -> pathname area
        mov     WORD PTR TASKAREA:[di],ax
        mov     WORD PTR TASKAREA:[di]+2,"\"    ; pathname = "d:\"
                                            ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     [bp].TCBWFP_Start,di        ; WFP_Start -> pathname

        mov     [bp].TCBPathNameType,PNType_LOCFILE
        UnSetBP_CurrTCB
                                            ;72417 end
;
; Set up ptda_pCDS appropriately so that the pointers can be passed to
; FS_OpenCreate.
;
; AL = upper-cased drive letter
;
        push    di                                                   ;### PTR B720732
        mov     di,TASKAREA:CurrTCB             ; (DI) = current TCB ;### PTR B720732
        mov     bx,TASKAREA:[di].TCBpTmpCDS.Offst                    ;### PTR B720732
;### PTR B720732        mov     bx,TASKAREA:[ptda_pCDS]
        mov     TASKAREA:[di].TCBThisCDS.Offst,bx     ; CDS pointer  ;### PTR B720732
        mov     TASKAREA:[di].TCBThisCDS.Segmt,ss     ; CDS pointer  ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ThisCDS].Offst,bx
;### PTR B720732        mov     TASKAREA:[ThisCDS].Segmt,ss
        pop     di                                                   ;### PTR B720732
        call    InitCDS                     ; Set up ptda_pCDS

        lea     bx,[fm_OpenParms]           ; SS:BX -> Argument frame
        xor     ax,ax                       ; For use in stuffing zeroes
        mov     TASKAREA:[bx].opn3s_EABufPtr.Segmt,ax
        mov     TASKAREA:[bx].opn3s_EABufPtr.Offst,ax   ; No EAs for this guy
        mov     TASKAREA:[bx].opn3s_Reserved.hiword,ax
        mov     TASKAREA:[bx].opn3s_Reserved.hiword,ax
        mov     TASKAREA:[bx].opn3s_Size.hiword,ax
        mov     TASKAREA:[bx].opn3s_Size.loword,ax
        mov     TASKAREA:[bx].opn3s_Flag,exist_open     ; Open if it exists
        mov     ax,fm_OpenMode
        mov     TASKAREA:[bx].opn3s_Mode,ax  ; set mode
        mov     ax,fm_OpenMode2
        mov     TASKAREA:[bx].opn3s_Mode2,ax

        xor     ch,ch                       ; clear reqister exec_only BN040
        and     ch, not EXECOPEN            ; set exec_only bit off exec_only BN040

        call    FSD_OpenCreate              ; open SFT (to FSD)
        jc      fm97                        ; special error cleanup

    ASSUME DS:NOTHING

fm60:   SaveReg <bp>
        mov     bp,fm_hVPB
        call    DecVPBRef                   ; remove our extra reference
        RestoreReg  <bp>

;*  All done with PTDA variables; clear the 'FS critical section' semaphore

fm70:   pushf
        CALLFAR Clear_Kernel_Sem
        popf

fm80:   LeaveProc
        ret

fm95:   mov     ax,ERROR_INTERRUPT          ; return error (from Take_Kernel_Sem)
        stc
        jmp     short fm80

;*  Some part of the operation failed (either FSD_Close, FS_MOUNT (VolRemoved),
;   FS_MOUNT (FormatMount), SetVPB (== FAT FormatMount), or FSD_OpenCreate.
;
;   The SFT is still open, but it may or may not be hooked to a valid FSD, and
;   that FSD may not know how to handle any handle based calls.
;
;   We set the bit in sf_flags2 to indicate the SFT is bad. SFFromSFN will
;   return 'C' & 'Z' (as well as the SFT pointer), so that all handle based
;   calls will fail (invalid handle).  w_Close detects this special case and
;   will close the SFT (without calling FSD_Close).
;
;   When we come here, the following fields in the SFT have already
;   been set up based on the operations that were performed in
;   ForceMount or RedetermineMedia:
;
;      sfi_hVPB - set to NULLVPB if we had closed the SFT and not
;                 reopened it
;      sf_FSC   - set to FSC_BAD of we had done an UNMOUNT but not
;                 done the MOUNT
;
fm97:   les     di,fm_pSFT                  ; ES:DI -> SFT
        or      es:[di].sf_flags2,SF_BEGINFORMAT_FAILED ; remember we failed
        stc                                 ; Indicate that there was an error
        jmp     short fm70                  ; release semaphore before exit

EndProc     ForceMount


;***    RedetermineMedia - Unmount and Remount the volume
;
;       Used to do the CLOSE-UNMOUNT-MOUNT-OPEN sequence. This is needed
;       after changing the layout of a disk by cleaning up a disk or
;       by formatting it. It gives the application a way to inform the
;       system and the FSD that something may have changed.
;
;       RedetermineMedia creates a new VPB for the volume in the drive.  If
;       that VPB is "proper", the DPB and SFT will be updated to use it.
;       The BufHE_Percolate bit in TCBBufHE is cleared.
;
;       ENTRY       ES:DI -> SFT for handle (identifies the FSD on which
;                            the volume is currently mounted)
;                   DS:SI -> DPB
;
;       EXIT        'NC'    The operation was a success
;                   'C'     There was an error
;                               AX = error code
;
;       USES        AX, BX, CX, DX, DS, ES, DI, SI, Flags
;
;       NOTE the following special circumstance.
;               Suppose the following sequence occurs:
;               Do a physical open on drive a:
;               swap disks in drive a:
;               lock a:
;               redeterminemedia for drive a: with handle created on physical open
;
;       The old volume had 2 refs: one for the disk and one for the handle
;       we've opened onto it.  The lock causes a new VPB to be created.
;       The disk ref for the old VPB is gone, and we need to get rid of the
;       handle ref.  But once we close the handle, the old VPB has 0 refs and
;       goes away.  (Normally, there would still be the one ref--the disk ref.)
;       We would then attempt to flush buffers associated with the old
;       volume, whose VPB is gone.  The same problem would occur when we force
;       unmount by decrementing the VPB refs.  To get around this, we use the
;       hVPB found in the DPB instead of the one in the SFT.  This will be for
;       the new volume, and so we end up flushing buffers associated with the
;       new volume.
;       This has the effect of taking the handle that was for the volume
;       originally in drive a: and making it refer to the new volume in a:,
;       transparent to the caller.  So the physical disk handle will still
;       be valid, for the new volume.
;
;       PSUEDOCODE
;
;           if (We did not LOCK the DPB)
;               return (ERROR_LOCK_VIOLATION)
;           Take_Kernel_Sem                     /* Enter FS crit section */
;           FSD_Close (SFT)                     /* clean up old FSD */
;           DecVPBRef()                         /* Get rid of VPB  */
;           if (VPB is not zero)
;               return (ERROR_BUSY_DRIVE)
;
;           WhatVolumeForce()                   /* Set up a new VPB   */
;           FSD_OpenCreate(SFT)                 /* open this instance */
;           FSD_ChgFilePtr()                    /* Seek back to current position */
;           Clear_Kernel_Sem                    /* Leave FS crit section */
;           if (error)
;               MarkSFTBad()
;
;   Change history:
;   ---------------
;   74088  Nizinski  93/9/23 Increment VPB ref count before calling FSD_Close
;                            or can get IPE on call to FSD_Flushbuf due to
;                            invalid signature in VPB.
;
Procedure   RedetermineMedia,NEAR
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar    rm_pSFT,DWORD                   ; pointer to SFT
LocalVar    rm_pDPB,DWORD                   ; pointer to specified FSC
LocalVar    rm_SeekPosition,DWORD           ; Position in SFT when we are called
LocalVar    rm_hVPB,WORD                    ; Original hVPB for this SFT
LocalVar    rm_OpenMode,WORD                ; Save mode between close & open
LocalVar    rm_OpenMode2,WORD               ; Save ext.mode between close & open
LocalVar    rm_OpenParms,<size Open3Struc>  ; argument frame for FSD_OpenCreate
LocalVar    rm_OpenPath,4                   ; for "d:\" pathname
LocalVar    rm_dpb_hVPB,WORD                ; hVPB in DPB (as opposed to SFT)

        mov     ax,es:[di].sf_fsi.sfi_position.loword
        mov     bx,es:[di].sf_fsi.sfi_position.hiword   ; BX:AX = current file position

        EnterProc <es,di, ds,si, bx,ax>     ; Save away pSFT and pDPB, and current file position.

        mov     bx,CurrTCB
        and     TASKAREA:[bx].TCBBufHE,not BufHE_Percolate

        mov     cx,DS:[SI].dpb_hVPB         ; VPB from dpb instead of SFT
        mov     rm_dpb_hVPB,cx

; Make sure that the DPB is locked and it is locked by us

        mov     cx,DS:[SI].dpb_drive_lock  ; (cx) = drive lock word
        cmp     cx,TASKAREA:Pid         ; Is the drive locked by us?
        LJNE    rm90                    ; Not locked or not locked by us. Fail it.

;*  We must now do a Close & an Open; first we must 'Lock FS Critical Section'
;   (because it uses PTDA variables)
;
        CALLFAR Take_Kernel_Sem             ; get kernel semaphore
        LJC     rm95                        ; 'C' = abnormal wakeup

;*  Must now call FSD_Close; to detach the FSD/FAT from this SFT

;*  First we must increment the VPB reference count so that FSD_Close will not
;   cause it to be freed.  We also save the hVPB to put back in the SFT after
;   the close.

        mov     ax,es:[di.sf_fsi].sfi_hVPB
        mov     rm_hVPB,ax                  ; save hVPB

        mov     bx,es:[di].sf_fsi.sfi_mode
        mov     rm_OpenMode,bx              ; save Mode (for later open)
        mov     bx,es:[di].sf_fsi.sfi_mode2
        mov     rm_OpenMode2,bx             ; save ext. Mode (for later open)

;*  Don't know why, but there was a comment above which indicates to   74088
;   increment the VPB ref count, but no code was ever here to do it.   74088
;   FSD_Close will decrement the SFT ref count...if count goes to zero 74088
;   then FSD_Close will also decrement the VPB ref count.              74088
        SaveReg <bp>                    ;                              74088
        mov     bp,rm_dpb_hVPB          ; (BP) = VPB handle from dpb   74088
        call    IncVPBRef               ; increment ref count          74088
        RestoreReg <bp>                 ;                              74088

        mov     bx,CurrTCB

        mov     TASKAREA:[bx].TCBThisSFT.Segmt,es
        mov     TASKAREA:[bx].TCBThisSFT.Offst,di   ; TCBThisSFT -> current SFT

;*  we must preserve the sf_flags, and set sf_noJFN, so FSD_Close (really
;   MFTClose) will remove SFT from the sharing set.  Then we must restore the
;   original sf_flags.

        push    es:[di].sf_flags            ; preserve SFT flags
        or      es:[di].sf_flags,SF_NOJFN   ; pretend no JFN associated
        mov     bx,-1                       ; if no JFN, no handle also

        call    FSD_Close                   ; ES:DI -> SFT
    ASSUME DS:NOTHING
        pop     bx                          ; BX = original SFT_flags
        pushf                               ; save flags from FSD_Close

;*  If original SF_noJFN was off, then we must reset it here

        test    bx,SF_NOJFN                 ; was original sf_noJFN on?
        jnz     rm25                        ; yes, just go on
        and     es:[di].sf_flags,NOT SF_NOJFN   ; no, turn it off

;*  We must increment the SFT reference count.  FSD_Close decremented it,
;   FSD_Open does not touch it (it assumes it was set to 1).

rm25:   inc     es:[di].sf_ref_count

        popf                                ; restore flags from FSD_Close
        LJC     rm97                        ; special error cleanup

; Close succeeded. We now clear sf_FSC since the FSD is no longer associated
; with this SFT.
;
        les     di,rm_pSFT
        mov     es:[di].sf_FSC.Offst,FSC_BAD
        mov     es:[di].sf_FSC.Segmt,FSC_BAD    ; mark FSC 'bad'

;* Now flush all the buffers for this VOLUME and discard them.
;
        mov     ax,rm_dpb_hVPB          ; AX = VPB to flush
        mov     dx,FLU_DISCARD
        call    FSD_FlushBuf            ; flush and discard buffers

;* Now we need to cause an UNMOUNT. The way we do this is to call DecVPBRef.
;  This will cause the UNMOUNT and will cause the VPB to be freed.
;  This is desirable since then when we call WhatVolumeForce later on, it will
;  cause a new VPB to be built, and will cause a MOUNT on the FSD.
;
;  We are assured that this will happen because we checked to make sure that
;  the DPB was locked by us. This means that there is only a reference for
;  the DPB and the SFT on this VPB.
;
;  Since we incremented the VPB ref count earlier, we must now         74088
;  decrement it. If the decremented count is zero, this effectively    74088
;  frees the VPB and we can skip over some stuff.                      74088

        SaveReg <bp>                        ; 74088
        mov     bp,rm_dpb_hVPB              ; 74088 BP = hVPB
        call    DecVPBRef                   ; 74088
        jz      rm30                        ; 74088 jump if ref count zero
        RestoreReg <bp>                     ; 74088

        SaveReg <bp>
        mov     bp,rm_dpb_hVPB              ; BP = hVPB
        or      bp,bp                       ;#53533 If vpb is null
        jz      rm30                        ;#53533 skip check

        SaveReg <es,ax>
        VPBContext es,bp                    ; make sure that the vpb
        mov     ax,es:[bp].vpb_ref_count    ; will go away
        sub     ax,es:[bp].vpb_search_count
        dec     ax

        RestoreReg <ax,es>
;### AC DCR
;### This is the spot for jumping past if flag set for init fat chkdsk             ;### AC DCR 1520
;  LJNZ    rmie1                       ; No. This is an error. ### PTR B720621
        jz      rm28                   ; If not error condition continue           ;### AC DCR 1520
;       LJNZ    rm93                        ; No. Drive busy.  ### PTR B720621
        push    ds                                                                 ;### AC DCR 1520
        DosContext ds                                                              ;### AC DCR 1520
        cmp     initfatchkdsk,1         ; If trying to chkdsk a fat boot partition ;### AC DCR 1520
        pop     ds                                                                 ;### AC DCR 1520
        jne     rm93                    ; Go to error condition to pick up drive is busy.                   ;### AC DCR 1520
        jmp     short rm30              ; Skip past decvpb for init fat chkdsk   ;### AC DCR 1520;### AC DCR 1520

rm28:   call    DecVPBRefInv                   ; This will free the VPB
rm30:   RestoreReg <bp>

; The VPB has gone away. Remove the reference in the SFT.
;
        les     di,rm_pSFT
        mov     es:[di].sf_fsi.sfi_hVPB,NULLVPB

;* The VPB has gone away. Mark the DPB as not having a corresponding VPB
;  and issue a WhatVolumeForce to cause a new VPB to be built up.
;
        lds     si,rm_pDPB                  ; DS:SI -> DPB
        mov     ds:[si].dpb_hVPB,NULLVPB    ; Zero out dpb_hVPB since the VPB went away
        call    WhatVolumeForce             ; Generate a new VPB
        LJC     rm97                        ; Special error cleanup

;* Now that the MOUNT has succeeded, we need to reopen the file onto the
;  FSD.
;
        mov     bx,CurrTCB
        lds     si,rm_pDPB                  ; DS:SI -> DPB
        mov     di,ds:[si].dpb_hVPB         ; DI = hVPB

        TaskContext DS                      ; for FSD_OpenCreate

        VPBContext ES,DI                    ; ES:DI -> VPB
        mov     TASKAREA:[bx].TCBThishVPB,di

        mov     si,es:[di].vpb_FSC.Offst
        mov     TASKAREA:[bx].TCBThisFSC.Offst,si
        mov     si,es:[di].vpb_FSC.Segmt
        mov     TASKAREA:[bx].TCBThisFSC.Segmt,si   ; TCBThisFSC -> new FSC

        les     di,es:[di].vpb_fsi.vpi_pDPB ; ES:DI -> DPB
        mov     al,es:[di].dpb_drive        ; Al = Logical drive # (A=0,B=1,...)
        add     al,"A"                      ; AL = 1 based drive letter
        mov     ah,":"                      ; AX = "d:"
        lea     di,rm_OpenPath              ; ss:di -> pathname area
        mov     WORD PTR TASKAREA:[di],ax
        mov     WORD PTR TASKAREA:[di]+2,"\"    ; pathname = "d:\"
                                            ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     [bp].TCBWFP_Start,di        ; WFP_Start -> pathname

        mov     [bp].TCBPathNameType,PNType_LOCFILE
        UnSetBP_CurrTCB
                                            ;72417 end
;
; Set up ptda_pCDS appropriately so that the pointers can be passed to
; FS_OpenCreate.
;
; TCBThisSFT already set up when we called FSD_Close.
;
; AL = upper-cased drive letter
;
        push    di                                                   ;### PTR B720732
        mov     di,TASKAREA:CurrTCB             ; (DI) = current TCB ;### PTR B720732
        mov     bx,TASKAREA:[di].TCBpTmpCDS.Offst                    ;### PTR B720732
;### PTR B720732        mov     bx,TASKAREA:[ptda_pCDS]
        mov     TASKAREA:[di].TCBThisCDS.Offst,bx     ; CDS pointer  ;### PTR B720732
        mov     TASKAREA:[di].TCBThisCDS.Segmt,ss     ; CDS pointer  ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ThisCDS].Offst,bx
;### PTR B720732        mov     TASKAREA:[ThisCDS].Segmt,ss
        pop     di                          ;### PTR B720732
        call    InitCDS                     ; Set up ptda_pCDS

        mov     bx,CurrTCB
        mov     ax,rm_pSFT.Segmt
        mov     TASKAREA:[bx].TCBThisSFT.Segmt,ax
        mov     ax,rm_pSFT.Offst
        mov     TASKAREA:[bx].TCBThisSFT.Offst,ax   ; TCBThisSFT -> current SFT

        lea     bx,[rm_OpenParms]           ; SS:BX -> Argument frame
        xor     ax,ax
        mov     TASKAREA:[bx].opn3s_FileHandleLockIDPtr.Segmt,ax
        mov     TASKAREA:[bx].opn3s_FileHandleLockIDPtr.Offst,ax ; Null ptr
        mov     TASKAREA:[bx].opn3s_EABufPtr.Segmt,ax
        mov     TASKAREA:[bx].opn3s_EABufPtr.Offst,ax   ; No EAs for this guy
        mov     TASKAREA:[bx].opn3s_Reserved.hiword,ax
        mov     TASKAREA:[bx].opn3s_Reserved.hiword,ax
        mov     TASKAREA:[bx].opn3s_Size.hiword,ax
        mov     TASKAREA:[bx].opn3s_Size.loword,ax
        mov     TASKAREA:[bx].opn3s_Flag,exist_open     ; Open if it exists
        mov     ax,rm_OpenMode
        mov     TASKAREA:[bx].opn3s_Mode,ax  ; set mode
        mov     ax,rm_OpenMode2
        mov     TASKAREA:[bx].opn3s_Mode2,ax

        xor     ch,ch                      ; clear reqister exec_only BN040
        and     ch, not EXECOPEN           ; set exec_only bit off exec_only BN040

        call    FSD_OpenCreate              ; open SFT (to FSD)
        jc      rm97                        ; special error cleanup

    ASSUME DS:NOTHING

; FSD_OpenCreate has incremented the VPB ref count for this so that
; the volume will not go away from under us.
;
; Now that the file is open, we seek to the position that we had originally
; when this routine was called. This is necessary since the app may have
; done some I/O prior to calling RedetermineMedia.
;
; NOTE that we call FSD_ChgFilePtr since we have already locked the SFT in
; w_DevIOCtl. We have to set up both CX:SI and DX:AX for the call because of
; the way FSD_OpenCreate is coded up and it is easier than special-casing
; for FAT vs FSD.
;
        les     di,rm_pSFT                  ; ES:DI -> SFT
        mov     cx,rm_SeekPosition.hiword
        mov     si,rm_SeekPosition.loword   ; CX:SI = position to seek to
        mov     dx,cx
        mov     ax,si                       ; DX:AX = position to seek to
        xor     bx,bx                       ; BX => Seek absolute
        call    FSD_ChgFilePtr
        jc      rm97                        ; Seek failed. Mark SFT as bad.

;*  All done with PTDA variables; clear the 'FS critical section' semaphore

rm70:   pushf
        CALLFAR Clear_Kernel_Sem
        popf

rm80:   LeaveProc
        ret

rm90:   mov     ax,ERROR_LOCK_VIOLATION     ; drive not locked, or not owned by us
        stc
        jmp     short rm80

rm93:   mov     ax,ERROR_BUSY_DRIVE         ; Drive Busy         ### PTR B720621
        pop     bp                          ; Even out stack after savereg ### PTR B720621

                                            ; ### Defect 88189, we now mark the SFT
                                            ;  as bad because we have already called
                                            ;  FSD_CLOSE and the information in it
                                            ;  is unreliable.
        les     di,rm_pSFT                  ; ES:DI -> SFT
        or      es:[di].sf_flags2,SF_BEGINFORMAT_FAILED ; remember we failed
                                            ; End 88189

        stc                                 ;                    ### PTR B720621
        jmp     short rm80                  ;                    ### PTR B720621

rm95:   mov     ax,ERROR_INTERRUPT          ; return error (from Take_Kernel_Sem)
        stc
        jmp     short rm80

;
; Some part of the operation failed (either FSD_Close, WhatVolumeForce,
; or FSD_OpenCreate. Mark the SFT as being in an unusable state.
;
; The SFT is still open, but it may or may not be hooked to a valid FSD, and
; that FSD may not know how to handle any handle based calls.
;
; We set the bit in sf_flags2 to indicate the SFT is bad. SFFromSFN will
; return 'C' & 'Z' (as well as the SFT pointer), so that all handle based
; calls will fail (invalid handle).  w_Close detects this special case and
; will close the SFT (without calling FSD_Close).
;
; When we come here, the following fields in the SFT have already
; been set up based on the operations that were performed in
; ForceMount or RedetermineMedia:
;
;    sfi_hVPB - set to NULLVPB if we had closed the SFT and not
;               reopened it
;    sf_FSC   - set to FSC_BAD of we had done an UNMOUNT but not
;               done the MOUNT
;
rm97:   les     di,rm_pSFT                  ; ES:DI -> SFT
        or      es:[di].sf_flags2,SF_BEGINFORMAT_FAILED ; remember we failed
        stc                                 ; Indicate that there was an error
        jmp     short rm70                  ; release semaphore before exit

; ifdef FSSTRICT                                            ### PTR B720621
; rmie1:  InternalError <RedetermineMedia: VPB not freed>   ### PTR B720621
; endif                                                     ### PTR B720621

EndProc     RedetermineMedia

HIGH2CODE ENDS
       END
