windows-nt/Source/XPSP1/NT/ds/nw/nw16/tsr/resident.asm
2020-09-26 16:20:57 +08:00

1040 lines
26 KiB
NASM

page ,132
if 0
/*++
Copyright (c) 1993-4 Microsoft Corporation
Module Name:
resident.asm
Abstract:
This module contains the resident code part of the stub redir TSR for NT
VDM NetWare support.
Author:
Colin Watson (colinw) 08-Jul-1993
Environment:
Dos mode only
Revision History:
08-Jul-1993 colinw
Created
--*/
endif
.xlist ; don't list these include files
.xcref ; turn off cross-reference listing
include ..\..\..\..\public\sdk\inc\isvbop.inc ; NTVDM BOP mechanism
include dosmac.inc ; Break macro etc (for following include files only)
include dossym.inc ; User_<Reg> defines
include segorder.inc ; segments
include mult.inc ; MultNET
include sf.inc ; SFT definitions/structure
include pdb.inc ; program header/process data block structure
include debugmac.inc ; DbgPrint macro
include asmmacro.inc ; language extensions
include nwdos.inc ; NetWare structures and nwapi32 interface
.cref ; switch cross-reference back on
.list ; switch listing back on
subttl ; kill subtitling started in include file
.286 ; all code in this module 286 compatible
far_segment segment
far_label label far
far_segment ends
ResidentCodeStart
assume cs:ResidentCode
assume ds:nothing
assume es:nothing
assume ss:nothing
public Old21Handler
Old21Handler dd ?
;
; IMPORTANT: the following up to the comment <END NWDOSTABLE> must
; be kept in the same order as for the NWDOSTABLE structure in NWDOS.H/.INC.
; Align on 32 bits to make it convenient for nwapi32.dll
;
align 4
public ConnectionIdTable
ConnectionIdTable CID MC dup (<>)
public ServerNameTable
ServerNameTable db MC * SERVERNAME_LENGTH dup (0)
public DriveIdTable
DriveIdTable db MD dup (0)
public DriveFlagTable
DriveFlagTable db MD dup (0)
public DriveHandleTable
DriveHandleTable db MD dup (0)
public PreferredServer
PreferredServer db 0
public PrimaryServer
PrimaryServer db 0
public TaskModeByte
TaskModeByte db 0
CurrentDrive db 0
public SavedAx;
SavedAx dw 0
public NtHandleHi;
NtHandleHi dw 0
public NtHandleLow;
NtHandleLow dw 0
public NtHandleSrcHi; // Used in FileServerCopy
NtHandleSrcHi dw 0
public NtHandleSrcLow;
NtHandleSrcLow dw 0
public hVDD
hVDD dw -1
public PmSelector
PmSelector dw 0
public CreatedJob
CreatedJob db 0
public JobHandle
JobHandle db 0
NOV_BUFFER_LENGTH equ 256
public DenovellBuffer
DenovellBuffer db NOV_BUFFER_LENGTH dup (?)
public DenovellBuffer2
DenovellBuffer2 db NOV_BUFFER_LENGTH dup (?)
.errnz (size DeNovellBuffer2 - size DenovellBuffer)
Comspec db "COMSPEC="
COMSPEC_LENGTH equ ($ - Comspec)
;
; this is the <END NWDOSTABLE> structure.
;
;
; data passed from nw16.asm
;
public not_exclusive
not_exclusive db 0
page
public NwInt21
NwInt21 proc far
assume cs:ResidentCode
assume ds:nothing
assume es:nothing
assume ss:nothing
sti ; make sure ints are still enabled
;
; check whether we filter this vector; if not, pass it through to previous INT 21
; handler (DOS or some other TSR)
;
; If this is a name based operation, and the caller is passing through a novell
; format name - SYS:FOO or SERVER\SYS:FOO - then munge the name to be a UNC name
;
cmp ah,0eh
jne @f
jmp select_default_drive
@@: cmp ah,39h ; create directory
je check_name
ja @f
;
; ah less than 39h (mkdir) is definitely for DOS
;
public quick_jump_to_dos
quick_jump_to_dos:
jmp far_label
;
; run any of the following name-based calls through the name check:
;
; 3ah remove directory
; 3bh change directory
; 3ch create file
; 3dh open file
; 41h delete file
; 43h get/set attributes
; 4bh exec program
; 4eh find first file
; 56h rename
;
@@: cmp ah,3dh
jbe check_name
cmp ah,41h ; delete file
je check_name
cmp ah,43h ; get/set attributes
je check_name
cmp ah,4bh ; exec program
je check_name
cmp ah,4eh ; find first file
je check_name
cmp ah,56h ; rename
je rename
jmp dispatch_check
;
; Rename function. This has 2 path names: source in ds:dx and
; destination in es:di. Check the destination first then fall through
; and check the source.
;
rename:
push ds
push dx
push es
push di ; user registers saved for after Int21
push ds ; save ds:dx 'cause we will corrupt them
push dx
mov dx,es
mov ds,dx
mov dx,di ; ds:dx = destination buffer
call IsDosPath
je @f ; DOS path, no modification
cld
push di
call DenovellizeName
pop di
cmp dx,offset DenovellBuffer
je swap_buffers
@@:
pop dx ; ds:dx points at source again
pop ds
pop di
pop es
pop dx
pop ds
jmp check_name
;
; Destination name was normalized and stored in DeNovellBuffer. put the data
; in Denovellbuffer2 in-case we need to put the Source name in Denovellbuffer
;
swap_buffers:
push cx
push si
push ds ; will become es during Dos call
mov si,dx
mov di,cs
mov es,di
mov di,offset DenovellBuffer2
mov cx,NOV_BUFFER_LENGTH / 2
.errnz (NOV_BUFFER_LENGTH and 1)
rep movsw
mov di,offset DenovellBuffer2
pop es ; es:di is now Denovellbuffer2
pop si
pop cx
pop dx ; make ds:dx source again
pop ds
; stack has users di,es,dx,ds pushed
; parameters are same as callers except for es:di
jmp check_src
check_name: ; ds:dx points at name to examine
push ds
push dx
push es
push di
; fall through
check_src: ; only jumped to in rename
cld
call IsDosPath
je for_dos_properR ; x: or UNC filename. No more processing
cmp ah,3dh
jne notNETQ ; special NETQ open only applies for create
cmp CreatedJob,0
jz notNETQ ; don't look at name if no job handle available
push ax
push si
mov si,dx
cld
lodsw
cmp ax,"EN"
jne @f
lodsw
cmp ax,"QT"
jne @f
lodsb
or al,al
jnz @f
pop si ; Opening NETQ. Return Dos handle from CreateJob and File
pop ax
mov CreatedJob,0 ; Only return handle once
mov al, JobHandle
xor ah, ah
pop di
pop es
pop dx
pop ds
clc
retf 2
@@: pop si
pop ax
jmp for_dos_properR
notNETQ:push di
call DenovellizeName ; munge the name if required
pop di ; restore caller DI
;
; Look for compatibility mode opens that need to change to exlusive mode
; opens so that they get properly cached. Criteria for opening exclusive
; is that the application did not specify any sharing modes and the drive
; being opened is on a netware drive.
;
cmp ah, 3ch
je @f
cmp ah, 3dh
jne not_compat
@@: test al,OF_SHARE_MASK
jne not_compat
cmp not_exclusive, 1 ; open shared mode anyway
je not_compat
mov SavedAx,ax
mov ax,hVdd
DispatchCall ; 32 bit code decides if compat mode
not_compat:
pushf
call Old21Handler ; fake int 21 to get to DOS
pop di
pop es
pop dx
pop ds
retf 2 ; return to app (with flags from DOS)
for_dos_properR: ; restore regs and call dos
pop di
pop es
pop dx
pop ds
cmp ah, 3ch
je @f
cmp ah, 3dh
jne for_dos_proper
@@: test al,OF_SHARE_MASK
jne for_dos_proper
cmp not_exclusive, 1 ; open shared mode anyway
je for_dos_proper
mov SavedAx,ax
mov ax,hVdd
@@: DispatchCall ; 32 bit code decides if compat mode
public for_dos_proper
for_dos_proper:
jmp far_label
dispatch_check:
cmp ah,04ch
jne check_9f
jmp process_exit
;
; 'special' entry point to return the data segment info to the protect-mode code
; so it can generate an LDT descriptor which refers to this memory.
;
check_9f:
cmp ah,9fh
jne check_nw_ep ; is it a Netware call?
or al,al
jnz check_handle_mapper
mov bx,seg ConnectionIdTable; 9f00: return segment info
mov dx,offset ConnectionIdTable
clc ; if we loaded then it can't fail
retf 2
;
; if the call is 9f01 then we call MapNtHandle for the value in BX. This will
; update NtHandleHi and NtHandleLow, which we assume will be accessed from the
; code segment register
;
check_handle_mapper:
cmp al,1
jne check_nw_ep ; still not one of ours?
call MapNtHandle ; 9f01: call MapNtHandle
retf 2
check_nw_ep:
cmp ah,0b4h
jb for_dos_proper
cmp ah,0f3h
ja for_dos_proper
jne @f
jmp file_server_copy
@@: cmp ah,0BAh
jne check_f0
push bx ; get environment. used by map.exe
push ax
mov ah,051h ; load current apps PDB into ax
int 021h
@@: mov es, bx
cmp bx, es:PDB_Parent_PID
je @f
mov bx, es:PDB_Parent_PID
jmp @b
@@:
mov dx, es:PDB_environ ; set DX to environment segment
mov es, dx ; set es:di to value of COMSPEC
push si
push ds
mov ds, dx
xor si, si
; future code to save space
; es <- env seg
; di <- env off
; ds <- cs
; si <- offset Comspec
; cx <- .size Comspec / 2
; cld
; repz cmpsw
; jnz no match
; al <- 0
; cx <- remaining size of env seg
; rep scasb
cld
next_var:
lodsb
cmp al, "C"
jne @f
lodsb
cmp al, "O"
jne @f
lodsb
cmp al, "M"
jne @f
lodsb
cmp al, "S"
lodsb
jne @f
cmp al, "P"
jne @f
lodsb
cmp al, "E"
jne @f
lodsb
cmp al, "C"
jne @f
lodsb
cmp al, "="
je got_comspec
@@: ; Search for null terminating environment
or al,al
je next_var
lodsb
jmp @b
got_comspec:
pop ds
mov di,si
pop si
pop ax
pop bx
iret
check_f0:
cmp ah,0f0h
jne for_me
;
; if we're here then we're doing simple stuff that we don't need to bop fer
; currently stuff here is ah=f0, al = 00, 01, 04, 05
;
; caveat emptor dept #312: However, it came to pass that we needed to bop when
; the f00x calls were made without any preceding calls that would cause nwapi32
; to be loaded
;
dispatch_f0:
.errnz ((offset PrimaryServer - offset PreferredServer) - 1)
or al,al ; f000 = set preferred server
jnz try_01
cmp dl,8
ja zap_preferred
mov PreferredServer,dl
iret
zap_preferred:
mov PreferredServer,al ; al contains 0 remember
iret
try_01: cmp al,1 ; f001 = get preferred server
jnz try_02
mov al,PreferredServer
iret
try_02: cmp al,2 ; f002 = get default server
jnz try_04
mov al,PreferredServer
or al,al
jnz @f
mov al,PrimaryServer
@@: iret
try_04: cmp al,4 ; f004 = set primary server
jne try_05
cmp dl,8
ja zap_primary
mov PrimaryServer,dl
iret
zap_primary:
mov PrimaryServer,0
iret
try_05: cmp al,5 ; f005 = get primary server
jne for_me
mov al,PrimaryServer
iret
file_server_copy:
call FileServerCopy ; f3 - Used by ncopy.exe
;jmp for_me
;
; if the process exits and the dll is loaded then call the 32 bit code to
; close any cached handles.
;
process_exit:
;jmp for_me
;
; if we're here then the dispatch code is for a NetWare client API. First we
; check if we have already loaded the 32-bit code. If not, then load it. If we
; get an error, we will fall through to DOS
;
for_me:
cmp ah,0BCh ; bc,bd,be need handle mapping
jb no_mapping
cmp ah,0BEh
ja no_mapping
;do_mapping_call:
call MapNtHandle ; take bx and find the Nt handle
no_mapping:
mov SavedAx,ax
cmp ah,0e3h ; Look for CreateJob NCP
jne @f ; try f2 alternative
mov al,[si+2] ; si is NCP subfunction
jmp lookupcode
@@: cmp ax,0f217h
jne do_dispatch ; Not CreateJob
mov al,[si+2] ; si is NCP subfunction
lookupcode:
cmp al,68h
je createjob
cmp al,79h
jne do_dispatch
createjob: ; It is a CreateJob and File
; Always return the errorcode from the NCP exchange
; regardless of any earlier failures in the NT plumbing.
mov ax, SavedAx
push ax ; Open \\Server\queue for NCP
push ds
push dx
mov ax, 9f02h
mov SavedAx,ax
mov ax,hVdd
DispatchCall ; Set DeNovellBuffer to \\Server\queue
; and registers ready for DOS OpenFile
pushf
call Old21Handler ; Open \\server\queue
jc @f
mov JobHandle, al
mov CreatedJob, 1 ; Flag JobHandle is valid
push bx
xor ah, ah
mov bx, ax ; JobHandle
call MapNtHandle ; take bx and find the Nt handle
pop bx
@@:
pop dx
pop ds ; Proceed and send the NCP
pop ax
mov SavedAx, ax
do_dispatch:
mov ax,hVdd
DispatchCall
retf 2 ; return to the application
public chain_previous_int21
chain_previous_int21:
jmp far_label
;
; Save new drive so we can conveniently handle compatibility mode opens.
; also need to return 32 as the number of available drives.
;
select_default_drive:
pushf
call Old21Handler ; fake int 21 to get to DOS
mov ah,19h ; get current drive
pushf
call Old21Handler ; fake int 21 to get to DOS
mov CurrentDrive,al ; current drive
mov al,32 ; # of drives supported by NetWare
retf 2 ; return to app (with flags from DOS)
NwInt21 endp
;*******************************************************************************
;*
;* FileServerCopy
;*
;* Implement preperation for calling
;* \\...)
;*
;* ENTRY applications registers
;*
;* EXIT nothing
;*
;* RETURNS nothing
;*
;* ASSUMES no registers (except flags) can be destroyed
;*
;******************************************************************************/
FileServerCopy proc near
push ax
push bx
mov bx,word ptr es:[di] ; Map Source Handle
call MapNtHandle
mov bx,NtHandleHi
mov NtHandleSrcHi,bx
mov bx,NtHandleLow
mov NtHandleSrcLow,bx
mov bx,word ptr es:[di+2] ; Map Destination Handle
call MapNtHandle
@@: pop bx
pop ax
ret
FileServerCopy endp
;*******************************************************************************
;*
;* IsDosPath
;*
;* Checks to see if a path name looks like a Microsoft path (<drive>:... or
;* \\...)
;*
;* ENTRY ds:dx = path name
;*
;* EXIT nothing
;*
;* RETURNS ZF = 1: path is for MS-DOS
;*
;* ASSUMES no registers (except flags) can be destroyed
;*
;******************************************************************************/
IsDosPath proc near
push ax
xchg si,dx ; si = offset of filename; dx = ????
mov al,[si+1] ; al = second character of filename
cmp al,':'
je @f ; looks like a DOS filename
cmp al,'\' ; (X\... or \\...)
jne tryFirstbyte
cmp al,'/' ; (X/... or //...)
jne @f ; second char is not "\" or "/"
tryFirstbyte:
mov al,[si] ; al = first character of filename
cmp al,'\' ; (\\... or \/...)
je @f
cmp al,'/' ; (\/... or //...)
@@: xchg si,dx ; dx = offset of filename; si = ????
pop ax
ret
IsDosPath endp
;*******************************************************************************
;*
;* DenovellizeName
;*
;* Converts a name from Novell format (SERVER\SHARE:filename or
;* SHARE:filename) to DOS UNC name. Server name is found by:
;*
;* if PreferredServer != 0 then Index = PreferredServer
;* else if PrimaryServer != 0 then Index = PrimaryServer
;* else Index = 0
;* servername = ServerNameTable[Index * sizeof(SERVER_NAME)]
;*
;* ENTRY ds:dx = name
;*
;* EXIT ds:dx = offset of DenovellBuffer
;*
;* RETURNS if success, DI points to last byte+1 in DenovellBuffer, else
;* DI is garbage
;*
;* ASSUMES 1. filename does not wrap in buffer segment
;* 2. DI register can be trashed
;* 3. DF = 0
;*
;******************************************************************************/
DenovellizeName proc near
assume ds:nothing
assume es:nothing
push ax
push bx
push cx
push bp
push si
push es
mov bp,ds
;
; get the length of the input filename
;
mov cx,ds
mov es,cx
mov di,dx ; es:di = filename
xor cx,cx
dec cx ; cx = ffff
xor al,al
repnz scasb
not cx
dec cx ; cx = strlen(filename)
cmp cx,length DenovellBuffer
jb @f
jmp dnn_ret ; filename too long: give it to DOS
;
; find the offset of ':' in the filename
;
@@: mov bx,cx ; remember length
mov di,dx ; es:di = filename
mov al,':'
repnz scasb ; di = strchr(filename, ':')+1
jz @f
go_home:jmp dnn_ret ; no ':' - not novell format name?
@@: cmp byte ptr [di],0
je go_home ; device name? (eg "LPT1:") - to DOS
mov si,di ; si = offset of ':' in name, +1
;
; find the offset of the first '/' or '\'
;
mov cx,bx ; cx = length of filename
mov di,dx ; di = offset of filename
mov al,'\'
repnz scasb
sub bx,cx
mov cx,bx
mov bx,di
mov di,dx
mov al,'/'
repnz scasb
jnz @f
mov bx,di
;
; if ':' before '\' or '/' then name is SYS:FOO... else SERVER\SYS:FOO...
;
@@: mov di,cs
mov es,di
mov di,offset DenovellBuffer
mov ax,('\' shl 8) + '\'
stosw
cmp bx,si
jb copy_share_name
xor bx,bx
mov cl,PreferredServer
or cl,cl
jnz got_index
mov cl,PrimaryServer
jcxz get_server_name
got_index:
dec cl
jz get_server_name
mov bx,cx
.errnz SERVERNAME_LENGTH - 48
shl cx,5
shl bx,4
get_server_name:
add bx,cx
mov cx,ds
mov si,es
mov ds,si
lea si,ServerNameTable[bx]
cmp byte ptr [si],0
je dnn_ret
mov ah,SERVERNAME_LENGTH
copy_server_name:
lodsb
or al,al
jz done_server_name
stosb
dec ah
jnz copy_server_name
done_server_name:
mov al,'\'
stosb
mov ds,cx
copy_share_name:
mov si,dx
next_char:
lodsb
cmp al,':'
je @f
stosb
jmp short next_char
@@: mov al,'\'
stosb
copy_rest:
lodsb
stosb
or al,al
jnz copy_rest
cmp byte ptr [si-2],':'
jne @f
mov byte ptr [si-2],0
@@: mov dx,offset DenovellBuffer
mov bp,es
dnn_ret:mov ds,bp
pop es
pop si
pop bp
pop cx
pop bx
pop ax
ret
DenovellizeName endp
;*** DosCallBack
;*
;* Call back into DOS via the int 2f/ah=12 back door. If CALL_DOS defined,
;* use a call, else s/w interrupt. Using a call means no other TSRs etc.
;* which load AFTER the redir can hook it, but we DON'T HAVE TO MAKE A
;* PRIVILEGE TRANSITION ON x86 which speeds things up. This should be safe,
;* because no other s/w should really be hooking INT 2F/AH=12
;*
;* ENTRY FunctionNumber - dispatch code goes in al
;* DosAddr - if present, variable containing address of
;* DOS int 2f entry point
;* OldMultHandler - this variable contains the address of DOSs
;* int 2f back door. Specific to redir code
;*
;* EXIT nothing
;*
;* USES ax, OldMultHandler
;*
;* ASSUMES nothing
;*
;***
DosCallBack macro FunctionNumber, DosAddr
mov ax,(MultDOS shl 8) + FunctionNumber
ifdef CALL_DOS
pushf
ifb <DosAddr>
if (((.type OldMultHandler) and 32) eq 0) ;; OldMultHandler not defined
extrn OldMultHandler:dword
endif
call OldMultHandler
else
call DosAddr
endif
else
int 2fh
endif
endm
;
; defines for DosCallBack FunctionNumbers
;
SF_FROM_SFN = 22
PJFN_FROM_HANDLE= 32
; *** MapNtHandle
; *
; * Given a handle in BX, map it to a 32-bit Nt handle store result
; * in NtHandle[Hi|Low]
; *
; *
; * ENTRY bx = handle to map
; *
; * EXIT Success - NtHandle set to 32-bit Nt handle from SFT
; *
; * RETURNS Success - CF = 0
; * Failure - CF = 1, ax = ERROR_INVALID_HANDLE
; *
; * USES ax, bx, flags
; *
; * ASSUMES nothing
; *
; ***
MapNtHandle proc near
pusha ; save regs used by Dos call back
push ds
push es
;
; call back to Dos to get the pointer to the JFN in our caller's JFT. Remember
; the handle (BX) is an index into the JFT. The byte at this offset in the JFT
; contains the index of the SFT structure we want in the system file table
;
DosCallBack PJFN_FROM_HANDLE ; pJfnFromHamdle
jc @f ; bad handle
;
; we retrieved a pointer to the required byte in the JFT. The byte at this
; pointer is the SFT index which describes our 'file' (file to (un)lock in
; this case). We use this as an argument to the next call back function -
; get Sft from System File Number.
;
mov bl,es:[di]
xor bh,bh
DosCallBack SF_FROM_SFN ; SfFromSfn
jc @f ; oops - bad handle
;
; Ok. We have a pointer to the SFT which describes this named pipe. Get the
; 32-bit Nt handle and store it in the shared datastructure.
;
mov bx,word ptr es:[di].sf_NtHandle[2]
mov NtHandleHi,bx
mov bx,word ptr es:[di].sf_NtHandle
mov NtHandleLow,bx
;
; restore all registers used by Dos call back.
; Carry flag is set appropriately
;
@@: pop es
pop ds
popa
jnc @f
;
; finally, if there was an error then return a bad handle indication in ax
;
mov ax,ERROR_INVALID_HANDLE
@@: ret
MapNtHandle endp
ResidentCodeEnd
end