;       SCCSID = @(#)fsinfo.asm 6.5 91/09/03
SCCSID  equ     "@(#)fsinfo.asm 6.5 91/09/03"
TITLE   FSINFO - get/set file system information
NAME    FSINFO
        PAGE ,132

;   FSINFO contains the API workers for retrieving and setting file-system
;   dependent information.  The currently defined entries are:
;
;       Global          Local
;
;       w_SetFSInfo
;                       cook_label
;       FSD_SetFSInfo
;       w_QFsInfo
;       DOS_QFsInfo
;       GetVolLabel
;       GSetMediaID
;       IOGetDiskSpaceInfo
;
;   MODIFICATION HISTORY:
;    - 11/27/95 - MLM defect 72417
;                 moved from PTDA to TCB:
;                 cMeta, Dirstart, LCurDrv, Sattrib, WFP_Start, WFP_Path_End
;                 Curr_Dir_End, NoSetDir, PathNameType

.xlist
include dosseg.inc
include basemaca.inc
include osmaca.inc
include argframe.inc ;74049
include fnofs.inc
include oldmaca.inc
include osconst.inc

.xcref
include dossymc.inc
include dirent.inc
include buffer.inc
include curdir.inc
include error.inc
include inbuf.inc
include mult.inc
include tcbdata.inc
include vpb.inc
include dpb.inc
include dbcs.inc
include protmode.inc
include fsc.inc
include mi.inc
include filesys.inc
include shutdown.inc
include dem.inc
.cref
.list
        CpuMode 386
        ModName , FNO_FSINFO_ASM

; BUGBUG danno 23-Mar-1989
; The conditional jumps in this module need to be modified to short
; jumps.


        ReferTask       CurrTCB,WORD    ; Current thread control block

                                        ;72417 removed PTDA entries now
                                        ;      in TCB
        ReferTask       LCurDrv,BYTE

        ReferGDT        GDT_VPB,WORD
        ReferGDT        GDT_FSC,WORD

        ReferFlat       _aseldemTemp,WORD
        ReferFlat       _pTCBCur,DWORD
        ReferGlobal     uchLastDrive,BYTE

        EXTRN16         Find_VPB
        EXTRN16         VPBContext32
        EXTRN16         DISK_INFO_NoCDS

        EXTRN           CharType:BYTE

        EXTRN           DeleteVID:NEAR
        EXTRN           ExmDBCS:NEAR
        EXTRN           FuBuff:NEAR
        EXTRN           FSCFromCDS:NEAR
        EXTRN           fValidFATDisk:NEAR
        EXTRN           GETENT:NEAR
        EXTRN           GetPath:NEAR
        EXTRN           GetThisDrv:NEAR
        EXTRN           DecVPBRef:NEAR
        EXTRN           IncVPBRef:NEAR
        EXTRN           PathDecVPBRef:NEAR
        EXTRN           PathIncVPBRef:NEAR
        EXTRN           SuBuff:NEAR
        EXTRN           DOS_Get_Drive_Freespace:NEAR
        EXTRN           TransPath:NEAR
        EXTRN           UCase:NEAR
        EXTRN           UpdateVID:NEAR
        EXTRN           ioSaveTCB:NEAR
        EXTRN           ioRestoreTCB:NEAR

        EXTRNFAR        Date16
        EXTRNFAR        DOS_SetFSInfo
        EXTRNFAR        FlushBuf
        EXTRNFAR        fValidFATDisk
        EXTRNFAR        fVolumeWriteable
        EXTRNFAR        GetPath
        EXTRNFAR        RlseBuffr
        EXTRNFAR        WaitVPB
        EXTRNFAR        WakeVPB
        EXTRNFAR        w_qFSAttach          ;74049
        EXTRN32         _SELMapVirt


OS2QMedia       struc   ; OS2 struc for DosQFsInfo level 2
OS2VolSN        dd  ?           ; Volume serial number
cbVolLabel      db  ?           ; Length of volume label
OS2VolLabel     db  100h dup(?) ; Volume label
OS2QMedia       ends


PCDOSMedia      struc
reserved        dw  ?           ; Reserved field
VolSN           dd  ?           ; Volume serial number
VolLabel        db  0Bh dup(?)  ; Volume label
FSName          db   8 dup(?)   ; File system name
PCDOSMedia      ends

VLabelNull      equ    FSName-VolLabel+1 ; Length of volume Label + null char


MVDMINSTDATA    SEGMENT

        align   4
FSInfoBuf       OS2QMedia < >   ; Buffer for 3xb Get/Set Media ID

MVDMINSTDATA    ENDS

DATA            SEGMENT         ;74049

FSQAttachBufLen dd    100       ;74049
FSQAttachBuf    db    100 dup(0);74049

DATA            ENDS            ;74049


        FARCODE HIGH2
HIGH2CODE SEGMENT

        ASSUME  SS:TASKAREA

Break   <w_SetFSInfo - set file-system-specific information>

;**     w_SetFSInfo - set file-system-specific information
;
;       w_SetFSInfo is used to address the needs of diverse file systems in
;       terms of information that is fs-dependent rather than info that is
;       easily mapped into the file-system hierarchy.  Currently, the code
;       below is specific to the FAT file system.
;
;       We dispatch to an appropriate subroutine based on the level of
;       function requested.
;
;       The levels we support are:
;
;           0   Illegal
;           1   Illegal
;           2   Setting the volume label
;
;       ENTRY   (ax)    is level number for information to be set
;               (bx)    is the drive number being addressed (0 = default, 1=A)
;               (cx)    is the length of the buffer supplied
;               (ds:dx) points at the user buffer
;
;       EXIT    CARRY SET => an error occurred
;                   (ax) has the error code
;               CARRY CLEAR => operation was successful
;
;       USES    ax,bx,cx,dx,si,di,es,ds,flag

        ASSUME  DS:NOTHING,ES:NOTHING
Procedure   w_SetFSInfo,<hybrid,far16>  ;01/12/94 Marc P.Louis 76963, add far16
LocalVar wsf_pVid,DWORD                 ; pointer to user VID
LocalVar wsf_cbvid,WORD                 ; length of VID
LocalVar wsf_level,WORD                 ; info level
LocalVar wsf_RootName,8                 ; "D:\*.*<nul>" plus a pad byte (cuz
                                        ; EnterProc pushes words)
LocalVar wsf_GoodLabel,MAXLABLEN+1      ; validated, uppercased label
LocalVar wsf_openbuf,PMPATHLEN
LocalVar wsf_dummy,WORD                 ; KLUDGE: see gshare.asm
LocalVar wsf_dummycds,<SIZE curdir>

        PMONLY
        EnterProc <ds,dx, cx, ax, 0,"*.","*\",":A"> ; init pVid, cbvid
                                        ;   level, and RootName
        lea     di,wsf_dummycds
        push    bx                                      ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,di       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],di ; Initialize ptda_pCDS
        lea     di,wsf_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,di            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],di ; Initialize ptda_pOpenBuf
        pop     bx                                      ;### PTR B720732

; fill in default drive if necessary, and convert to 0-based drive number

        dec     bl                      ; assume bl != 0, convert to 0-based
        jns     wsf10                   ; assumption correct, continue
                                        ;72417
        mov     bl,ss:[LCurDrv]         ; assumption wrong, use default drive#
                                        ;72417 end
wsf10:  DosContext      es
        cmp     bl,es:[uchLastDrive]    ; valid drive number?
        ja      wsf20                   ;   no, go error exit
        add     wsf_RootName,bl         ; RootName = "D:\*.*"

; check info level

        cmp     ax,FSINFOLEV_LABEL      ; valid info level?
        jne     wsf25                   ; no, go error exit

; process volume label
; cx = size of user buf
; ds:dx -> user buf

        TaskContext <es>,,noassume
        lea     di,wsf_GoodLabel        ; es:di -> wsf_GoodLabel
        call    cook_label              ; wsf_GoodLabel -> length+label
        jc      wsf15                   ; error, go error exit

; We form the string D:\*.* and pass it to transpath to set up WFP and other
; PTDA/TCB variables.
; (bx) is the drive of interest (0=A, etc)

        TaskContext <ds>
        lea     si,wsf_RootName         ; (ds:si) point to source name
        lea     di,wsf_openbuf          ; (TASKAREA:di) point to dest. name
        SaveReg <bp>
        call    TransPath               ; convert name
        RestoreReg <bp>
        jc      wsf35                   ; error, go error exit

; dispatch to proper file system

        mov     ax,ss
        lea     dx,wsf_GoodLabel+1      ; ax:dx -> label
        mov     cl,wsf_GoodLabel        ; cl = label length
        xor     ch,ch                   ; cx = label length
        mov     bx,wsf_level            ; bx = info level
        call    FSD_SetFSInfo

;### PTR B720732 wsf15: mov     SS:[ptda_pOpenBuf],0    ; Clear ptda_pOpenBuf
;### PTR B720732        mov     SS:[ptda_pCDS],0        ; Clear ptda_pCDS
wsf15:  LeaveProc
        RETP

wsf20:  mov     ax,ERROR_INVALID_DRIVE
wsf23:  stc
        jmp     short wsf15

wsf25:  mov     ax,ERROR_INVALID_LEVEL  ; the user passed in a bad level number
        jmp     short wsf23

; TransPath found an error and returned an error code.  However,
; (BUGBUG) the error code is likely to be wrong as TransPath needs to
; return the DOS 3 compatible error.  We presume the error to be due to
; be invalid drive.

wsf35:  mov     ax,ERROR_INVALID_DRIVE  ; set error code
        jmp     short wsf15             ; return to user (carry is set)

EndProc w_SetFSInfo


;**     cook_label - process volume label
;
;       Copy label from user space, check label length, uppercase label,
;       check for illegal characters, and pad to MAXLABLEN with blanks.
;
;       ENTRY   ds:dx -> user buffer contain label
;                        ds:dx      = length of label
;                        ds:[dx+1] -> label
;               es:di -> kernel buffer to return processed label
;               cx     = user buffer size
;
;       EXIT    if 'NC' - success
;                 kernel buffer contains processed label:
;                       es:di = length of label
;                       es:[di+1] = blank padded label
;               if 'C' - error code in ax
;                       ERROR_LABEL_TOO_LONG
;                       ERROR_INSUFFICIENT_BUFFER
;                       ERROR_INVALID_NAME
;                       ERROR_PROTECTION_VIOLATION
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure cook_label,near,local
LocalVar clb_pLabel,DWORD               ; pointer to label in kernel buffer
LocalVar clb_cbBuf,WORD                 ; size of user buffer

        EnterProc <es,di, cx>           ; clb_pLabel -> kernel buffer
                                        ; clb_cbBuf = user buffer size

;       jcxz    clb70                   ; buffer is too short to hold label length
        or      cx,cx                           ;
        LJZ     clb70                           ;

        mov     ax,MAXLABLEN+1          ; ax = max. bytes to copy from buffer
        cmp     cx,ax                   ; is buffer size smaller or same?
        jbe     clb10                   ; yes, use copy only buffer size
        mov     cx,ax                   ; copy only enough for a label
clb10:  mov     bx,ds
        mov     si,dx                   ; bx:si -> user buffer
        call    FuBuff                  ; copy user buffer to kernel buffer
        jc      clb50                   ; error, go error exit

; check length

        lds     si,clb_pLabel           ; ds:si -> label
        mov     cl,ds:[si]              ; cl = label length
        xor     ch,ch                   ; cx = label length
        cmp     cx,MAXLABLEN            ; is label too long
        ja      clb60                   ; yes, go error exit
        cmp     cx,clb_cbBuf            ; is buffer size too small?
        jae     clb70                   ; yes, go error exit

; scan for illegal characters
; cx = label length
; ds:si -> label length, label

        DosContext es,,noassume
        lea     bx,DOSGROUP:CharType    ; es:bx -> CharType array
        inc     si                      ; ds:si -> label
        jcxz    clb40                   ; null label, nothing to check
clb20:  lodsb                           ; al = byte from label
        TestDBCS                        ; is it a DBCS lead byte?
        jz      clb80                   ; yes, go skip DBCS character
        call    UCase                   ; no, convert to upper case
        mov     ds:[si-1],al            ; store it back
        cmp     al,'*'                  ; illegal characters?
        je      clb90                   ; yes, go error exit
        cmp     al,'?'                  ; illegal character?
        je      clb90
        cmp     al,'.'
        je      clb90
        xlat    byte ptr es:[bx]        ; look up in table
        test    al,fFCB OR fNewChar     ; valid character?
        jz      clb90                   ; no, go error
clb30:  loop    clb20

; label is valid and is uppercased. now pad it with blanks

clb40:  les     di,clb_pLabel           ; es:di -> length, label
        mov     bl,es:[di]              ; bl = length of label
        xor     bh,bh                   ; bx = length of label
        lea     di,[di+bx+1]            ; es:di -> first byte after label
        mov     cx,MAXLABLEN            ; cx = MAXLABLEN
        sub     cx,bx                   ; cx = number of bytes to pad blank
        mov     al,' '                  ; al = blank
        rep     stosb                   ; pad blanks

clb50:  LeaveProc
        RETP

clb70:  mov     ax,ERROR_INSUFFICIENT_BUFFER
        jmp     short clb100

clb60:  mov     ax,ERROR_LABEL_TOO_LONG
        jmp     short clb100

clb80:  cmp     byte ptr [si],0         ; is trail byte zero?
        jz      clb90                   ; yes, go return error
        inc     si                      ; skip DBCS trail byte
        loop    clb30                   ; dec cx, continue
                                        ; half a DBCS character, error
clb90:  mov     ax,ERROR_INVALID_NAME

clb100: stc
        jmp     short clb50
EndProc cook_label


;**     FSD_SetFSInfo - dispatch DosSetFSInfo to worker
;
;       ENTRY   bx = infolevel (assumed to be 2 currently)
;                 for infolevel 2:
;                   ax:dx          -> label (uppercased, blank padded, etc)
;                   cx             =  label length (not counting blanks)
;                   [WFP_Start]    -> "<drv>:\*.*"
;                   [Curr_Dir_End] -> end of current directory in WFP_Start or -1
;                   [WFP_Path_End] -> char after last \ in WFP_Start
;                   [ThisCDS]      -> CDS
;                   [TCBThishVPB]  =  hVPB
;                   [TCBThisFSC]   =  FSC
;
;       EXIT    if 'NC' - success
;               if 'C' - error
;                 ax = error code
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       NOTE    may block

        ASSUME DS:NOTHING,ES:NOTHING
Procedure FSD_SetFSInfo,<NEAR,FARONLY>

svl             struc                   ; this structure defines the usage
                                        ; of "svl_temp_label" when used with
                                        ; "FSF_SETINFO".

svl_lab_len     db  ?                   ; length of volume label.
svl_label       db  12 dup (?)          ; volume label in asciiz format.

svl             ends

LocalVar    svl_temp_label <size svl>   ; temporary label buffer.


; Since this is a "path-based" call, there has been no referent put in the
; VPB for this call. We need to increment the ref-count in the VPB in order
; to ensure that the VPB does not go away while we are working on it.

        call    PathIncVPBRef           ; Increment reference count
        mov     si,CurrTCB
        les     di,TASKAREA:[si].TCBThisFSC ; es:di -> fsc
        cmp     di,FSC_LOCAL            ; what file system?
        jb      fsf10                   ; fsd, go call fsd worker

; call FAT worker using special shutdown gate
; ShutdownGate  function name, exit label, seg with FSC segment (DS or ES)

        ShutdownGate  DOS_SetFSInfo,fsf20,es

; the above jumps to fsf20 on completion

; call FS_FSINFO
; ss:si -> TCB
; es:di -> fsc

;
; this is a create of a volume label on an ifs owned volume - start by
; extracting the new volume id from the fcb and putting it into a
; parameter packet
;
fsf10:  EnterProc                       ; **** NOTE EnterProc

        push    bx                      ; save the info level.
        push    si                      ; save the pointer to the tcb.

        mov     cx,8+3                  ; set up the loop control.
        lea     bx,svl_temp_label       ; point to the temporary label buffer.

        mov     si,dx                   ; point to caller provided
        mov     ds,ax                   ; volume label.

fsf15:  lodsb                           ; pick up a character for the label.
        mov     TASKAREA:[bx.svl_label],al ; store this character away.

        inc     bx                      ; move down to the next character.

        loop    fsf15                   ; more to do? yes, go round again.

        mov     TASKAREA:[bx.svl_label],0 ; no, set up the string delimiter.

        mov     TASKAREA:[svl_temp_label.svl_lab_len],8+3 ; set up the label's length.

        pop     si                      ; recover the pointer to the tcb.
        pop     ax                      ; recover the info level.
;
; put the new label onto the volume
;
        push    FSF_SETINFO             ; pass a flag showing this is a 'set'.
        push    TASKAREA:[si.TCBThishVPB] ; pass the volume's handle.
        lea     bx,svl_temp_label       ; point to the temporary label buffer.

        push    ss                      ; pass the address of the
        push    bx                      ; parameter packet.
        push    size svl                ; pass the parameter packet's length.

        push    ax                      ; pass indicator showing this is a label.

        call    es:[di].FS_FSINFO       ; call the ifs to create the label.

        LeaveProc                       ; **** NOTE LeaveProc

        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0

fsf20:  call    PathDecVPBRef           ; Remove our referent in the VPB
        RETP

EndProc FSD_SetFSInfo


;***    w_QFsInfo - Get a file's information
;
;       w_QFsInfo preforms the DOSQFSINFO function call.
;
;       ENTRY   AX - FSInfoLevel
;               CX - size of FSInfoBuf
;               DX - Drive number (1=A)
;               ES:DI - pointer to FSInfoBuf
;
;       EXIT    if NC - success
;                 FSInfoBuf is filled with information about
;                 the file system.
;               if CY - error
;                 ax = error
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       ALGORITHM
;
;       GetThisDrv
;       FSCFromCDS
;       if (Fat Drive)
;         return DOS_QFsInfo
;       else
;         return FS_FSINFO

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

LocalVar qfs_level,WORD         ; info level
LocalVar qfs_cbData,WORD        ; size of user buffer
LocalVar qfs_lpData,DWORD       ; points to user buffer

if (size fsibuf) gt (size fsibuf2)
        LocalVar qfs_Buf,<size fsibuf>  ; local buffer
else
        LocalVar qfs_Buf,<size fsibuf2> ; local buffer
endif
LocalVar qfs_dummycds,<SIZE curdir>

        EnterProc <ax,cx,es,di>         ; qfs_level = ax
                                        ; qfs_cbData = cx
                                        ; qfs_lpData = es:di
        lea     si,qfs_dummycds
        push    bx                                      ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,si       ; Initialize TCBpTmpCDS ;### PTR B720732
;       mov     TASKAREA:[bx].TCBpTmpCDS.Segmt,ss       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],si ; Initialize ptda_pCDS
        pop     bx                                      ;### PTR B720732
        dec     ax
        cmp     ax,FSINFOLEV_MAX -1     ; level 0 is also not valid
        LJA     qfs45                   ; Level is too big. Return error.

        mov     ax,dx                   ; ax = drive number
        dec     dl                      ; assume dl != 0, convert to 0-based
        jns     qfs10                   ; assumption correct, continue
                                        ;72417
        mov     dl,ss:[LCurDrv]         ; assumption wrong, use default drive#
                                        ;72417 end
qfs10:  DosContext      ds
        cmp     dl,[uchLastDrive]       ; valid drive number?
        ja      qfs30                   ;   no, go error exit

        call    GetThisDrv              ; ax = physical drive#; ThisCDS setup
        LJC     qfs30                   ; invalid drive, go report error

; dispatch to fsc or fat

        mov     di,TASKAREA:CurrTCB             ; (DI) = current TCB     ;### PTR B720732
        les     di,TASKAREA:[di].TCBThisCDS     ; (ES:DI) = CDS pointer  ;### PTR B720732
;### PTR B720732 les    di,ThisCDS              ; es:di -> cds
        SaveReg <ax,bp>                 ; preserve drive# and bp
        call    FSCFromCDS              ; es:di = fsc; BL = PathNameType
        RestoreReg <bp,ax>              ; restore bp and drive#
        LJC     qfs30                   ; can't get cds, error exit
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     TASKAREA:[bp].TCBPathNameType,bl
        UnSetBP_CurrTCB
                                        ;72417 end
;
; Since this is a "path-based" call, there has been no referent put in the
; VPB for this call. We need to increment the ref-count in the VPB in order
; to ensure that the VPB does not go away while we are working on it.
;
        call    PathIncVPBRef           ; Increment reference count

        cmp     di,FSC_LOCAL            ; what type of file system?
        jb      qfs15                   ;   FSD, go call FSD
                                        ;   FAT, fall thru

; dispatch DOS_QFsInfo
; ax = phys drive# (0=A)

        mov     bx,[CurrTCB]
        mov     si,TASKAREA:[bx].TCBThishVPB
        VPBContext ds,si                ; ds:si -> vpb
        mov     dx,ax                   ; dx = drive
        mov     ax,qfs_level            ; ax = info level
        TaskContext es,,noassume
        lea     di,qfs_Buf              ; es:di -> qfs_Buf
        CALLFAR DOS_QFsInfo             ; cx = length of return info
        call    PathDecVPBRef           ; Remove our referent in the VPB
        jnc     qfs20                   ; go copy qfs_Buf into use buffer
        jmp     short qfsx              ; error, go error exit

; dispatch FS_FSINFO
; es:di -> fsc

qfs15:  push    FSF_QINFO               ; flag = retrieve info
        mov     si,TASKAREA:CurrTCB             ; (si) = current TCB     ;### PTR B720732
        lds     si,TASKAREA:[si].TCBThisCDS     ; (ds:si) = CDS pointer  ;### PTR B720732
;### PTR B720732 lds    si,ThisCDS              ; ds:si -> CDS
        push    ds:[si].cd_fsi.cdi_hVPB ; hVPB
        push    qfs_lpData.Segmt
        push    qfs_lpData.Offst        ; pointer to user buffer
        push    qfs_cbData              ; size of user buffer
        push    qfs_level               ; info level
        call    es:[di].FS_FSINFO       ; call FSC
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        call    PathDecVPBRef           ; Remove our referent in the VPB
        jmp     short qfsx              ; return

; copy Buf into user buffer for FAT
; cx = length to copy
; with the code added for 75591, DOS_QFsinfo (for info level 2) will pass
; will pass back the size of fsibuf2
; (which was increased from x'11' bytes to x'19'. There are
; still callers of DOSQFSINFO that will pass a buffer length of x'11'.
; To remain compatible, accept either size.                            75591

qfs20:  cmp     cx,qfs_cbData           ; is user buffer large enough?
        jbe     qfs25                   ; yes, go copy contents        75591
        mov     ax,qfs_level            ; get info level               75591
        cmp     ax,FSINFOLEV_LABEL      ; level 2?                     75591
        jne     qfs40                   ; no. level 1 data won't fit.  75591
        sub     cx,fsibuf2_size_h - fsibuf2_time + 2 ; old fsibuf2 len 75591
        cmp     cx,qfs_cbData           ; is user buffer large enough? 75591
        ja      qfs40                   ;   no, go error exit
qfs25:                                  ;                              75591

        TaskContext ds,,noassume        ; ds = ss
        lea     si,qfs_Buf              ; ds:si -> qfs_Buf
        mov     bx,qfs_lpData.Segmt
        mov     di,qfs_lpData.Offst     ; bx:di -> user buffer
        call    SuBuff                  ; copy qfs_Buf to user buffer

;### PTR B720732 qfsx:  mov     SS:[ptda_pCDS],0        ; Clear ptda_pCDS
qfsx:   LeaveProc
        ret

qfs30:  mov     ax,ERROR_INVALID_DRIVE
qfs35:  stc
        jmp     short qfsx

qfs40:  mov     ax,ERROR_BUFFER_OVERFLOW
        jmp     short qfs35

qfs45:  mov     ax,ERROR_INVALID_LEVEL      ; the user passed in a bad level number
        jmp     short qfs35

EndProc w_QFsInfo


;;; WARNING !!!
;;;
;;; Keep the following routine in HIGH2CODE since it does DBCS stuff.
;;;

;**     GetVolLabel - get volume label
;
;       Returns a nul-terminated volume label in given buffer
;       Also returns other information from dirent.                   75591
;
;       ENTRY   es:di          -> buffer to accept label (assume long enough)
;                                 and date, time, and size info.      75591
;               dl              = physical drive number (0=A)
;               si              = hVPB
;               [ThisCDS]      -> CDS being used
;
;       EXIT    cx = length of volume label (could be zero)
;
;       USES    ax,bx,cx,dx,si,di,ds,flags
;               WFP_START, WFP_Path_End, CURDIREND, Sattrib,
;               TCBThishVPB
;
;    Change activity:
;    ----------------
;    75591 11/18/93 Skip Nizinski  Also pass back size, time, and date
;                                  information for compatibility
;                                  with native dos.

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure GetVolLabel,faronly
LocalVar gvl_lpVolBuf,DWORD     ; -> buffer to hold volume label
                                ;    and size, time, and date info    75591
LocalVar gvl_szPath,8           ; "d:\*.*" plus null and a pad byte
LocalVar gvl_pDirent,WORD       ; offset to dirent (from getpath)     75591

        EnterProc <es,di,0,"*.","*\",":A"> ; gvl_lpVolBuf = es:di
                                           ; gvl_szPath = "A:\*.*"

; setup parameters for GetPath

        mov     bx,CurrTCB                      ; ss:di -> TCB
        mov     TASKAREA:[bx].TCBThishVPB,si    ; TCBThishVPB = hVPB
;
; Ensure that disk is formatted for FAT FS.
;
        CALLFAR fValidFATDisk
        jz      gvl40                   ; Not valid for FAT. Return NULL label.

        TaskContext ds                  ; ds = TASKAREA

                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     [bp].TCBSattrib,attr_volume_id ; search for volume label
        mov     [bp].TCBNoSetDir,1      ; don't change directory
        mov     [bp].TCBcMeta,2         ; 2 wildcard characters in path
        UnSetBP_CurrTCB
        add     gvl_szPath,dl           ; szPath = "d:\*.*"
        lea     ax,gvl_szPath           ; ss:ax -> "d:\*.*"
        SetBP_CurrTCB
        mov     [bp].TCBWFP_Start,ax    ; WFP_Start -> "d:\*.*"
        add     ax,3                    ; ss:ax -> "*.*"
        mov     [bp].TCBWFP_Path_End,ax       ; WFP_Path_End -> "*.*"
        mov     [bp].[TCBCurr_Dir_End],-1
        CALLFAR GetPath                 ; get dir. buffer with volume label
        UnSetBP_CurrTCB
                                        ;72417 end
        jc      gvl40                   ; not found, return null label

        mov     si,CurrTCB
        mov     ds,TASKAREA:[si].TCBCurBuf.Segmt ; ds = segment w/ dir entry
        ASSUME DS:NOTHING

        les     di,gvl_lpVolBuf         ; es:di -> buffer to hold label
        mov     gvl_pDirent,bx          ; save offset to dirent      75591
        lea     si,[bx].dir_name        ; ds:si -> volume label
        mov     cx,MAXLABLEN            ; number of bytes to copy
        mov     bx,di                   ; es:bx -> buffer to hold label
        rep     movsb                   ; copy label to buffer

; Now that volume label is taken care of, copy addition fields from
; the dirent returned by callpath into the user's buffer.            75591
; Start of block of 75591 changes.                                   75591
        SaveReg <di,bx>                 ;                            75591
        les     di,gvl_lpVolBuf         ; es:di -> buf to hold data  75591
        lea     di,[di+fsibuf2_time-fsibuf2_szVolLabel]            ; 75591
                                        ; es:di-> user time field    75591
        mov     bx,gvl_pDirent          ; get saved offset to dirent 75591
        lea     si,[bx].dir_time        ; ds:si -> dirent time       75591
        movsw                           ; copy time                  75591
.errnz (dir_date - dir_time - 2)               ;                     75591
.errnz (fsibuf2_date - fsibuf2_time - 2)       ;                     75591
        movsw                           ; copy date                  75591
        add si,2                        ; skip over dir_firstfile    75591
.errnz (dir_size_l - dir_date - 4)             ;                     75591
.errnz (fsibuf2_size_l - fsibuf2_date - 2)     ;                     75591
        movsw                           ; copy size low order word   75591
.errnz (dir_size_h - dir_size_l - 2)           ;                     75591
.errnz (fsibuf2_size_h - fsibuf2_size_l - 2)   ;                     75591
        movsw                           ; copy size high order word  75591
        RestoreReg <bx,di>              ;                            75591
; End of block of 75591 changes                                      75591

; 0e5h in the first character was chosen as an indication of an empty
; directory entry long time ago. But later we want to allow users to use
; 0e5h as the first character of volume label (and file names). So we
; convert 0e5h to 5 (first character only) when we store the
; name, and convert 5 to 0e5h (first character only) when we retrieve the
; name.

        cmp     byte ptr es:[bx],05h    ; first byte 05?
        jnz     gvl10                   ;   no, do nothing
        mov     byte ptr es:[bx],0E5h   ;   yes, convert to 0E5h

gvl10:  CALLFAR RlseBuffr               ; release buffer from GetPath

; es:bx -> volume label
; es:di -> one byte passed the last byte of volume label
; Count length by scanning the label backwards and replacing blanks with
; nulls. Look out for DBCS.

gvl20:  mov     byte ptr es:[di],0      ; null terminate the label
        dec     di                      ; es:di -> one byte of volume label
        cmp     di,bx                   ; reached start of label?
        jb      gvl30                   ;   yes, we're done, length = 0

        mov     dx,di                   ; es:dx -> one byte of volume label
        call    ExmDBCS                 ; is this 2nd byte of a DBCS?
        jc      gvl30                   ;   yes, we're done

        cmp     byte ptr es:[di],' '    ; is character a blank?
        je      gvl20                   ;   yes, go null it

gvl30:  sub     di,bx                   ; di = length of label - 1
        inc     di                      ; di = length of label
        mov     cx,di                   ; cx = length of label
gvlx:   LeaveProc
        RETP

; can't find volume label, return null string and length=0

gvl40:  xor     cx,cx                   ; cx = label length = 0
        les     di,gvl_lpVolBuf         ; es:di -> buffer to return label
        mov     es:[di],cl              ; es:di -> null string

        xor     ax,ax                   ; 75591
        lea     di,[di+fsibuf2_time-fsibuf2_szVolLabel]            ; 75591
                                        ; es:di-> user time field    75591
        stosw                           ; 75591 time = zero
.errnz (fsibuf2_date - fsibuf2_time - 2)
        stosw                           ; 75591 date = zero
.errnz (fsibuf2_size_l - fsibuf2_date - 2)
        stosw                           ; 75591 size = zero
.errnz (fsibuf2_size_h - fsibuf2_size_l - 2)
        stosw                           ; 75591 size = zero

        jmp     gvlx
EndProc GetVolLabel

HIGH2CODE ENDS

        FARCODE HIGH4
HIGH4CODE       SEGMENT

        ASSUME  SS:TASKAREA

;***    DOS_QFsInfo - w_QFsInfo worker for FAT drive
;
;       The buffer es:di-> is assumed to be large enough to hold the return
;       information.
;
;       IMPORTANT NOTE: The swapper uses w_qFSInfo.  Do not make this
;                       routine swappable!!!
;
;       ENTRY   es:di     -> valid buffer to return data in
;               ax        =  info level
;                         =  FSINFOLEV_ALLOC -- return volume allocation info
;                         =  FSINFOLEV_LABEL -- return VSN and volume label
;               dl        =  phys drive number (0=A)
;               ds:si     -> VPB
;               [ThisCDS] -> CDS being used
;
;       EXIT    if NC
;                 cx = length of info stored in buffer
;               if CY
;                 ax = error
;
;       USES    ax,bx,cx,dx,si,di,ds,es,flags
;
;       ALGORITHM
;
;       switch (infolevel) {
;         case FSINFOLEVEL_ALLOC:
;           call get_drive_free_space
;           return
;
;         case FSINFOLEVEL_LABEL:
;           if (new disk format)
;               get VSN from vpb
;           else
;               return VSN == 0
;           call GetVolLabel
;           return
;       }

        ASSUME DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure DOS_QFsInfo,faronly
LocalVar ffi_lpBuf,DWORD

        EnterProc <es,di>               ; ffi_lpBuf = ds:si

    .errnz  FSINFOLEV_ALLOC - 1
        dec     ax                      ; get volume allocation info?
        jz      ffi10                   ;   yes, go get allocation info

    .errnz  FSINFOLEV_LABEL - 2
;
; get VSN

; if new (OS/2) disk, return VSN from VPB

        test    [si].vpb_flags,VPBNEWBOOT       ; is this a new disk format
        jz      ffi05                           ; no, return 0

        mov     ax,[si].vpb_fsi.vpi_ID.loword   ; ax = loword of VSN
        mov     es:[di].fsibuf2_ulVSN.loword,ax ; return loword of VSN
        mov     ax,[si].vpb_fsi.vpi_ID.hiword   ; ax = hiword of VSN
        mov     es:[di].fsibuf2_ulVSN.hiword,ax ; return hiword of VSN
        jmp     short ffi07                     ; go on

; return VSN = 0 (older disk format, don't return real VSN)

ffi05:  xor     ax,ax
        mov     es:[di].fsibuf2_ulVSN.loword,ax ; return VSN == 0
        mov     es:[di].fsibuf2_ulVSN.hiword,ax

; get volume label and length

ffi07:  lea     di,[di].fsibuf2_szVolLabel      ; es:di -> buffer for label
        CALLFAR GetVolLabel
        lds     si,ffi_lpBuf                    ; ds:si -> info buffer
        mov     ds:[si].fsibuf2_cbVolLabel,cl   ; set length of volume label
        mov     cx,size fsibuf2                 ; cx = length of infobuf 75591
        clc
        jmp     short ffix                      ; return

; get volume allocation info
; First zero out the entire buffer because the return structure
; contains DWORDs but we only store words in it.

ffi10:  mov     cx,(size fsibuf) / 2    ; size of return structure in words
        .errnz  (size fsibuf) and 1     ; assume size fsibuf is even
        cld                             ; go forward
        rep     stosw                   ; zero the buffer

        inc     dl                      ; dl = drive# (1=A)
        SaveReg <bp>
        call    DOS_Get_Drive_Freespace   ; ax=SecPerClus, bx=free clus
                                        ; cx=BytePerSec, dx=total clus
        RestoreReg <bp>
        jc      ffix                    ; error, go error exit

        lds     si,ffi_lpBuf            ; ds:si = ffi_lpBuf

; For os/2 the file system ID number is zero. This is set when we
; zero the buffer. Also all the hiword portions are already zeroed.

        mov     [si].fsibuf_csClust.loword,ax   ; return sector per cluster
        mov     [si].fsibuf_ctAlloc.loword,dx   ; return total clusters
        mov     [si].fsibuf_caAlloc.loword,bx   ; return free clusters
        mov     [si].fsibuf_cbSector,cx         ; return bytes per sector
        mov     cx,size fsibuf                  ; cx = size of return info

ffix:   LeaveProc
        RETP

EndProc DOS_QFsInfo


;***    GetpFSInfo - Get 16:16 pointer to FSInfoBuf
;
;       This routine creates a 16:16 pointer to the FSInfoBuf
;       structure in MVDMINSTDATA.
;
;       EXIT
;           ES:DI = point to FSInfoBuf (EDI(16:31)==0)
;
;       NOTES
;           BUGBUG danno 23-Mar-1989.  This is a hack to get the
;           filesystem code to work.  Eventually (by 1995?), the
;           filesystem will be converted to 32-bit code and this
;           routine will become unnecessary.
;
;       USES
;           Flags,EDI,ES
;

Procedure GetpFSInfo,near,local
        EnterProc
        SaveReg     <eax,ecx,edx,ds>
        FlatContext <ds,es>,eax
        mov     ax,_aseldemTemp[ISELDEMFS*2]    ; Get our scratch selector
        SaveReg <ax>                            ; Save it
        CCALL32 _SELMapVirt,<<(OFFSET FLAT:FSInfoBuf)>,<size OS2QMedia>,eax>
        RestoreReg  <es>
        ASSUME  ES:NOTHING
        xor     edi,edi
        RestoreReg  <ds,edx,ecx,eax>
        ASSUME  DS:NOTHING
        LeaveProc
        ret
EndProc   GetpFSInfo


;***    GSetMediaID - Get/Set Media ID worker                   @3xb12
;
;       This routine allows the caller to query or modify the
;       media ID information.  To do this the routine uses
;       the protect mode workers w_QFSInfo and w_SetFSInfo.
;       Also allows for info levels.
;
;       ENTRY      AL = 00h - Get Media ID
;                       01h - Set Media ID
;                  BH = Reserved (must be 00h)
;                       or infolevel if nonzero
;                  BL = Drive Number (0=Default,1=A,2=B, ...)
;                  CX = Buffer size (only if BH<>0)
;                  DS:DX -> Buffer
;
;       EXIT       'C' clear if ok
;                  'C' set if error, AX has error code
;
;       USES       AX,CX,DX,DS,ES,SI,DI
;
;       CALLS       w_QFsInfo
;                   w_SetFSInfo
;
        ASSUME  DS:NOTHING, ES:NOTHING, SS:TASKAREA
Procedure   GSetMediaID,far16
        PMONLY

        cmp     al,1            ; Valid function request?
        ja      gsBad1          ; Yes
        mov     di,dx           ; Save callers buffer offset in DI
                                ; Now check for segment overrun
        cmp     bh,0            ; PCDOS Media ID call?
        jne     gs1             ; No
        mov     cx, SIZE PCDOSMedia
gs1:    add     dx,cx           ; Segment overrun?
        jnc     gsRegs          ; No, jmp
        mov     ax,ERROR_BAD_LENGTH        ; Set return code
        jmp     short gsBad2
gsBad1: mov     ax,ERROR_INVALID_FUNCTION  ; Set return code
gsBad2: stc
        RETP

; Move parms to correct registers for call to protect mode worker
;
;               AX - FSInfoLevel
;               CX - size of FSInfoBuf
;               DX - Drive number
;               ES:DI - pointer to FSInfoBuf
;
gsRegs: xor     ah,ah           ; Clear top half of AX
        push    ds              ; Switch buffer pointer from
        pop     es              ;  DS:DX to ES:DI = callers buffer
        mov     dl,bl           ; DX = Drive number
        xor     dh,dh           ; Clear top half of DX

        or      al,al           ; Is it a GET request?
        mov     al,bh           ; AX = Infolevel
        jnz     gsSet           ; No, must be a SET, jmp

;----------------------------------
; GET Media ID (or FAPI DOSQFsInfo)
;----------------------------------
        cmp     bh,0            ; PCDOS media ID call?
        jne     gsQFS           ; No, jmp

; PCDOS media ID call setup

        mov     ax,2            ; Infolevel is get media ID
        mov     si,di           ; DS:SI = Callers info buffer
        add     si,VolSN        ; Move past reserved word

        call    GetpFSInfo      ; ES:DI = Kernel info ID buffer

gsQFS:  SaveReg <ds,si,bx>      ; Save regs       (DS:SI = callers buff)
        CALLFAR w_QFsInfo       ; Get the information  (fsinfo.asm)
        RestoreReg <bx,di,es>   ; Restore regs    (ES:DI = callers buff)
        assume  es:nothing
        jc      gsBye           ; Go handle error
gsGet:  cmp     bh,0            ; PCDOS media ID call?
        jne     gsOK            ; No, jmp


; Copy the information to the callers PCDOS buffer
; PCDOS  +--+----+-----------+---------+
;        |  | sn | vol name  | fs name |
;        +--+----+-----------+---------+
; OS/2   +--------------------------------------------------------/
;        | sn |l| vol name                                        \
;        +----+-+-------------------------------------------------/
;
                                ; ES:DI = Callers buffer
gsGet1: cld                     ;
        SaveReg     <di,es>
        call    GetpFSInfo
        SaveReg     <di,es>
        RestoreReg  <ds,si,es,di>
        mov     cx,VolLabel-VolSN ; Length of serial number
    rep movsb                   ; Copy serial number

;       inc     si              ; Don't copy length byte             ;74049;
;       mov     cx,size VolLabel ; Length of PCDOS volume label      ;74049;
;   rep movsb                   ; Copy volume label                  ;74049;
;       mov     cx,size FSName  ; Move 8 bytes of                    ;74049;
;       mov     al,0            ;   zeros into                       ;74049;
;   rep stosb                   ;   the file system name.            ;74049;


;------ Copy the volume label.  If there is no volume label,         ;74049;
;       copy the constant "NO NAME    "                              ;74049;

        SaveReg     <ds>        ; Save Data Segment register         ;74049;

        inc     si              ; Don't copy length byte             ;74049;
        mov     cx,size VolLabel ; Length of PCDOS volume label      ;74049;

        cmp     byte ptr ds:[si], 00 ; Is a volume label present     ;74049;
        jne     short gsCopyVol      ; Yes, go copy volume label     ;74049;

        push    cs              ; Set DS                             ;74049;
        pop     ds              ;         = Code Segment             ;74049;

        mov     si, offset NoName   ; Set up to copy NoName constant ;74049;

gsCopyVol:                                                           ;74049;

    rep movsb                       ; Copy the volume label          ;74049;


;------ Because the OS/2 File System never returns a value for the   ;74049;
;       file system, the constant "FAT     " is returned for         ;74049;
;       compatibility with real DOS                                  ;74049;

        ;BEGIN 74049

        ; ASSUMES bl = drive No.


        or       bl,bl                    ; if bl == 0 , use current drive
        jnz      @f

                                        ;72417
        mov      bl, ss:[LCurDrv]
                                        ;72417 end
        inc      bl

 @@:

        ;
        ; Translate the drive number into a drive letter. Copy
        ; to the user's buffer, and append a ':'.
        ;

        push     bx
        add      bx,0040h
        mov      byte ptr es:[di],bl      ; copy drive letter to buffer
        mov      byte ptr es:[di+1], 03ah ; append ':'
        mov      byte ptr es:[di+2], 0    ; Null character
        pop      bx

        push     es                       ; segment of device name
        push     di                       ; offset of device name

        push     0                        ; ordinal
        push     1                        ; FSAInfoLevel

        DosContext ds
        push     ds                       ; segment of buffer
        push     OFFSET FSQAttachBuf      ; offset of buffer

        push     ds                       ; segment of length of buffer
        push     OFFSET FSQAttachBufLen   ; Offset os length of buffer

        xor      eax,eax
        push     eax                      ; ulUnused

        ;
        ; A direct call to w_qFSAttach will have to be made here;
        ; setup the data structures to make this call (refer to the
        ; code in P_DOSQFSATTACH).
        ;

        mov      ax,1                     ; FSAInfoLevel

        push     es
        pop      ds                       ; selector of device name
        mov      si, di                   ; offset of device name

        mov      bx,sp                    ; parameter packet on the stack

        push     es
        push     di
        push     bp
        CALLFAR  w_qFSAttach
        pop      bp
        pop      di
        pop      es
        add      esp,20                   ; restore stack
        jc       @f                       ; Jump if function failed

                                          ; function successful
        push     si                       ; save si

        DosContext ds,si
        mov      si, OFFSET FSQAttachBuf  ; start of buffer

        add      si,4                     ; offset to name
        add      si, WORD PTR [si-2]      ; Add length of name
        inc      si                       ; Add null terminator

        mov      cx, WORD PTR [si]        ; length of FSname
        add      si,2                     ; increment past length

        inc      cx                       ; include null character

        rep      movsb                    ; copy FSname

        pop      si                       ; restore si

@@:
        ;END 74049

        RestoreReg <ds>                   ; Restore Data Segment register  ;74049;

        jmp      gsOK                     ; Return to caller

;--------------------------------------                              ;74049;
; Constants for PCDOS Get Media ID call                              ;74049;
;--------------------------------------                              ;74049;

NoName   db 'NO NAME    '       ; Default volume label if there is   ;74049;
                                ; not a label present on the volume  ;74049;
                                ; **** Must be eleven bytes ****     ;74049;

;------------------------------------
; SET Media ID (or FAPI DOSSetFsInfo)
;------------------------------------
gsSet:  cmp     bh,0            ; PCDOS media ID call?
        jne     gsSetFS         ; No, jmp

; PCDOS media ID call setup
;   Copy the information FROM the callers PCDOS buffer
;        +--+----+-----------+---------+
; PCDOS  |  | sn | vol name  | fs name |
;        +--+--+-+-----------+---------+---------------------/
; OS/2         |l| vol name                                  \
;              +-+-------------------------------------------/
; Note: OS/2 buffer overlaps PCDOS buffer

gsSet1: add     di,VolLabel-1   ; Move to OS/2 length byte
        mov     dh,[di]         ; Save 3rd byte of serial number
        mov     dl,[di+VLabelNull] ; Save 1st byte of fs name
        xor     al,al           ; AL=0
        mov     [di+VLabelNull],al ; Tag string end with terminating zero
        inc     di              ; Point at volume label
        push    di              ; Save offset of volume label
        mov     cx,VLabelNull   ; Search for terminating zero (limit 12)
    repne scasb
        dec     di              ; Don't count the zero
        mov     ax,di           ; AX = offset of volume label end
        pop     di              ; Recall offset of volume label start
        sub     ax,di           ; Calculate length of label
        dec     di              ; Point to length byte
        mov     [di],al         ; Store length of volume label

        mov     cx,FSName+VolSN ; CX = size of infobuf
        mov     AX,2            ; Infolevel is get media ID

gsSetFS: SaveReg <ds,di,bx,dx>
        xor     bh,bh           ; BX=Drive number
        mov     dx,di           ; DS:DX=callers buffer
        CALLFAR w_SetFSInfo     ; Set the information (fsinfo.asm)
        RestoreReg <dx,bx,di,ds>
        jc      gsBye           ; Error, jmp
        cmp     bh,0            ; PCDOS media ID call?
        jne     gsOK            ; No, jmp
        mov     [di],dh         ; Restore 3rd byte of serial number
        mov     [di+VLabelNull],dl ; Restore 1st byte of fs name

gsOK:   clc
gsBye:  RETP

EndProc     GSetMediaID

HIGH4CODE ENDS

CpuMode 386

FARCODE HIGH32
HIGH32CODE segment

;***EP  _IOGetDiskSpaceInfo - Execute a non path-based QFsInfo call to
;                               determine disk free space.
;
;       This C callable routine is for use by the swapper to execute
;       the equivalent of a level-1 QFsInfo call.  We do NOT use path-
;       based PTDA conventions (ie, we don't modify PathNameType and
;       ThisCDS).
;
;       NOTE:   The size of the info buffer is not checked.  The caller
;               had better assure that it's big enough!
;
;       ENTRY:  [ESP+14]        size of FS info buf
;               [ESP+12]        near 32-bit pointer to FS info buf
;               [ESP+4]         drive number
;               [ESP]           return address
;
;       EXIT:   EAX = error code
;
        ASSUME  DS:FLAT,ES:FLAT,SS:NOTHING,FS:NOTHING,GS:NOTHING
Procedure _IOGetDiskSpaceInfo
ArgVar  gdsi_DriveNum,DWORD     ; logical drive number
ArgVar  gdsi_pInfoBuf,DWORD     ; 32-bit near pointer to info buf
ArgVar  gdsi_cbInfoBuf,DWORD    ; size of info buf in bytes
LocalVar gdsi_tcbsavbuf,TCBIOCopySize
LocalVar fTK_FF_BUF,BYTE

        EnterProc
        SaveReg <ebx,esi,edi>
        mov     fTK_FF_BUF,0
                                        ; b724126
        mov     esi,[_pTCBCur]
        test    [esi].TCBForce_Flg,FF_BUF     ; buffer assigned?
        jz      iogetsave
        and     [esi].TCBForce_Flg,not FF_BUF ; make like one isn't assigned
        inc     fTK_FF_BUF

; save the TCB variables
iogetsave:
        SSToDS  edi,gdsi_tcbsavbuf
        CCALL32 ioSaveTCB

; translate near 32-bit pointer to 16:16
        mov     ecx,gdsi_pInfoBuf       ; convert DS relative pointer back
        DSToSS  ecx                     ; into an SS relative one.
        cmp     ecx,64*1024             ; die if not addressable with 16 bit
        jae     gdsie1                  ; offset from SS


        mov     eax,gdsi_DriveNum       ; al = 1-based drive num
        dec     al                      ; al = 0-based drive num
        SaveReg <cx>
        CALL16  Find_VPB                ; es:di -> vpb
        RestoreReg <cx>
        jc      gdsie2                  ;

        cmp     es:[di].vpb_FSC.Offst,FSC_LOCAL ; FAT file?
        jb      gdsi50

        lds     si,es:[di].vpb_fsi.vpi_pDPB     ; ds:si -> DPB ptr

;
; FAT file.  Call directly in to the get-free-space worker.
;
        SaveReg <ebp>
        CALL16  DISK_INFO_NoCDS
        RestoreReg <ebp>
        jc      short gdsix
;
; Now we have to set up the buffer to look like a level 1 qFsInfo call.
;
        movzx   eax,al                          ; sectors per cluster
        movzx   ebx,bx                          ; total number of clusters
        movzx   edx,dx                          ; number of free clusters
        FlatContext <ds>,esi,noassume
        mov     esi,[gdsi_pInfoBuf]             ; ds:esi -> InfoBuf
        mov     [esi].fsibuf_csClust,eax        ; sectors per cluster
        mov     [esi].fsibuf_ctAlloc,ebx        ; total number of clusters
        mov     [esi].fsibuf_caAlloc,edx        ; number of free clusters
        mov     [esi].fsibuf_cbSector,cx        ; sector size in bytes
        xor     eax,eax                         ; no error
        jmp     short gdsix1
;
; not FAT file.  Call FSD.
;
gdsi50: CALL16  Call32FSD

gdsix:  movzx   eax,ax
gdsix1: FlatContext     <ds,es>,ebx
        SSToDS  esi,gdsi_tcbsavbuf              ; restore TCB variables
        CCALL32 ioRestoreTCB

        cmp     fTK_FF_BUF,0                    ; b724126
        je      noiogetres
        mov     esi,[_pTCBCur]
        or      [esi].TCBForce_Flg,FF_BUF ; restore the flag

noiogetres:
        RestoreReg <edi,esi,ebx>
        LeaveProc
        RETC

gdsie1: Panic<"_IOGetDiskSpaceInfo: addr conversion failed">
gdsie2: Panic<"_IOGetDiskSpaceInfo: no VPB">

EndProc _IOGetDiskSpaceInfo

HIGH32CODE      ends

        FARCODE HIGH4
HIGH4CODE SEGMENT

;***    Call32FSD - worker for _IOGetDiskSpaceInfo to call the FSD
;
;       This routine is to be used only by _IOGetDiskSpaceInfo, to allow
;       us to call the FS_FSINFO FSD entry point.  This call must be
;       done from 16-bit code.
;
;       ENTRY:  ss:cx -> QFSInfo info buffer
;               es:di -> vpb
;               [gdsi_cbInfoBuf] contains size of info buffer.  This is
;               in the stack frame of _IOGetDiskSpaceInfo.
;
;       EXIT:   ax = error code from fsd
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure Call32FSD,<far16>

        push    FSF_QINFO               ; flag = query info
        push    di                      ; volume handle
        push    ss
        push    cx                      ; pointer to info buffer
        push    [gdsi_cbInfoBuf].loword ; length of buffer
        push    FSINFOLEV_ALLOC         ; get drive free space query
        les     di,es:[di].vpb_FSC      ; es:di -> FSC
        call    es:[di].FS_FSINFO
        RETP
EndProc Call32FSD

HIGH4CODE ENDS
        END
