|
|
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
|