Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1005 lines
21 KiB

page ,132
title COMMAND2 - resident code for COMMAND.COM part II
name COMMAND2
;/*
; * Microsoft Confidential
; * Copyright (C) Microsoft Corporation 1991
; * All Rights Reserved.
; */
;
; Revision History
; ================
;
; M038 SR 11/5/90 Changed stuff for Novell RPL. These guys cannot
; reserve memory by changing int 12h and then give it
; back to DOS by changing arenas in autoexec.bat.
; This makes command.com reload transient and this
; cannot be done at this stage.
;
;
.xcref
.xlist
include dossym.inc
include pdb.inc
include syscall.inc
include comsw.asm
include comequ.asm
include resmsg.equ
include comseg.asm
.list
.cref
DATARES segment public byte
extrn Append_State:word
extrn Append_Flag:byte
extrn BMemMes:byte
extrn ComBad:byte
extrn ComDrv:byte
extrn ComSpec:byte
extrn EnvirSeg:word
extrn ExtCom:byte
extrn FRetMes:byte
extrn HaltMes:byte
extrn Handle01:word
extrn InitFlag:BYTE
extrn Int_2e_Ret:dword
extrn Io_Save:word
extrn Io_Stderr:byte
extrn Loading:byte
extrn LTpa:word
extrn MemSiz:word
extrn NoHandMes:byte
extrn OldTerm:dword
extrn Parent:word
extrn PermCom:byte
extrn Prompt:byte
extrn PutBackDrv:byte
extrn PutBackMsg:byte
extrn PutBackSubst:byte
extrn Res_Tpa:word
extrn RetCode:word
extrn Save_Pdb:word
extrn SingleCom:word
extrn Sum:word
extrn Trans:dword
extrn TranVarEnd:byte
extrn TranVars:byte
extrn TrnSeg:word
extrn VerVal:word
extrn ResSize:word
extrn OldDS:word
extrn RStack:word
extrn Ctrlc_Trap:near
extrn CritErr_Trap:near
extrn LodCom_Trap:near
DATARES ends
;;ENVARENA segment public para
;;ENVARENA ends
;;ENVIRONMENT segment public para ; default COMMAND environment
;;ENVIRONMENT ends
INIT segment public para
extrn EnvSiz:word
extrn OldEnv:word
extrn ResetEnv:byte
extrn UsedEnv:word
extrn Chuckenv:byte
INIT ends
TRANDATA segment public byte
extrn trandataend:byte
TRANDATA ends
TRANSPACE segment public byte
extrn transpaceend:byte
extrn headcall:dword
TRANSPACE ends
CODERES segment public byte
public BadMemErr
public ChkSum
;; public EndInit
public GetComDsk2
public Int_2e
public LoadCom
public LodCom
public LodCom1
public RestHand
public SavHand
public SetVect
public THeadFix
public TRemCheck
public TJmp
assume cs:CODERES,ds:NOTHING,es:NOTHING,ss:NOTHING
extrn ContC:near
extrn DskErr:near
extrn Alloc_error:near
;* If we cannot allocate enough memory for the transient or there
; was some other allocation error, we display a message and
; then die.
;SR;
; We will have to make sure that at this entry point and at FatalC,
;ds = DATARES. All jumps to these points are made from only within this file
;and so we should be able to do this
assume ds:DATARES
BadMemErr:
mov dx,offset DATARES:BMemMes ; DX = ptr to msg
FatalC:
;; push cs
;; pop ds
;; assume ds:ResGroup
invoke RPrint
; If this is NOT a permanent (top-level) COMMAND, then we exit;
; we can't do anything else!
cmp PermCom,0
je FatalRet
; We are a permanent command. If we are in the process of the
; magic interrupt (Singlecom) then exit too.
cmp SingleCom,0 ; if permcom and singlecom
jne FatalRet ; must take int_2e exit
; Permanent command. We can't do ANYthing except halt.
mov dx,offset DATARES:HaltMes ; DX = ptr to msg
invoke RPrint
sti
Stall:
jmp Stall ; crash the system nicely
FatalRet:
mov dx,offset DATARES:FRetMes ; DX = ptr to msg
invoke RPrint
FatalRet2:
cmp PermCom,0 ; if we get here and permcom,
jne Ret_2e ; must be int_2e
; Bugbug: this is where we'd want to unhook int 2F, *if* we
; were a non-permanent COMMAND that had hooked it! (Just in
; case we decide to do that.)
mov ax,Parent
mov word ptr ds:Pdb_Parent_Pid,ax
mov ax,word ptr OldTerm
mov word ptr ds:Pdb_Exit,ax
mov ax,word ptr OldTerm+2
mov word ptr ds:Pdb_Exit+2,ax
mov ax,(EXIT shl 8) ; return to lower level
int 21h
Ret_2e:
;SR;
; We will ensure that ds = DATARES for all entries to this place
;
;; push cs
;; pop ds
;; assume ds:resgroup,es:nothing,ss:nothing
assume ds:DATARES
mov SingleCom,0 ; turn off singlecom
mov es,Res_Tpa
mov ah,DEALLOC
int 21h ; free up space used by transient
mov bx,Save_Pdb
mov ah,SET_CURRENT_PDB
int 21h ; current process is user
mov ax,RetCode
cmp ExtCom,0
jne GotECode
xor ax,ax ; internals always return 0
GotECode:
mov ExtCom,1 ; force external
;SR; This is actually returning to the caller. However, the old code had
;ds = RESGROUP so I guess we can keep ds = DATARES for us.
;Yes, int 2eh can corrupt all registers so we are ok.
;
jmp Int_2e_Ret ;"iret"
;*** Int_2e, magic command executer
Int_2e:
assume ds:NOTHING,es:NOTHING,ss:NOTHING
;SR;
; We are going to come here from the stub with the old ds and DATARES value
;pushed on the stack in that order. Pick up this stuff off the stack
;
pop ds ;ds = DATARES
assume ds:DATARES
pop ax
; pop ds:OldDS ;Save old value of ds
pop word ptr Int_2e_Ret
pop word ptr [Int_2e_Ret+2] ; store return address
;pop ax ; chuck flags
add sp,2
;; push cs
;; pop es
push ds
pop es ;es = DATARES
; mov ds,OldDS
mov ds,ax
assume ds:nothing ;ds = old value
mov di,80h
mov cx,64
; Bugbug: cld
rep movsw
mov ah,GET_CURRENT_PDB
int 21h ; get user's header
mov es:Save_Pdb,bx
mov ah,SET_CURRENT_PDB
;; mov bx,cs
;SR;
; Set ds = DATARES because BadMemErr expects this
;
push es
pop ds
assume ds:DATARES
mov bx,ds ;es = our PSP now
int 21h ; current process is me
mov SingleCom,81h
mov ExtCom,1 ; make sure this case forced
;SR;
; We can enter LodCom directly after a command shell is terminated or we
;can fall thru from above. When we enter directly from the stub, the stack
;has the old ds value and the data seg value on the stack, so that ds can
;be properly set. To fake this, we push dummy values here.
;
push ds ;old value of ds
push ds ;data seg value, ds = DATARES
LodCom: ; termination handler
pop ds ;ds = DATARES
assume ds:DATARES
add sp,2
; pop OldDS ;store old ds
cmp ExtCom,0
jne @f ; internal cmd - memory allocated
jmp LodCom1
@@:
mov bx,0FFFFh
mov ah,ALLOC
int 21h
call SetSize
add ax,20h
cmp bx,ax
jnc MemOk ; > 512 byte buffer - good enough
BadMemErrJ:
jmp BadMemErr ; not enough memory
;*** SetSize - get transient size in paragraphs
SetSize proc
assume ds:NOTHING,es:NOTHING
mov ax,offset TRANGROUP:TranSpaceEnd + 15
mov cl,4
shr ax,cl
ret
SetSize endp
MemOk:
assume ds:DATARES ;we have set ds = DATARES
mov ah,ALLOC
int 21h
jc BadMemErrJ ; memory arenas probably trashed
mov ExtCom,0 ; flag not to alloc again
mov Res_Tpa,ax ; save current tpa segment
and ax, 0F000h
add ax, 01000h ; round up to next 64k boundary
jc Bad_Tpa ; memory wrap if carry set
; Make sure that new boundary is within allocated range
mov dx,Res_Tpa
add dx,bx ; compute maximum address
cmp dx,ax ; is 64k address out of range?
jbe Bad_Tpa
; Must have 64K of usable space.
sub dx,ax ; compute the usable space
cmp dx,01000h ; is space >= 64k ?
jae LTpaSet
Bad_Tpa:
mov ax,Res_Tpa
LTpaSet:
mov LTpa,ax ; usable tpa is 64k buffer aligned
mov ax,Res_Tpa ; actual tpa is buffer allocated
add bx,ax
mov MemSiz,bx
call SetSize
sub bx,ax
;
;M038; Start of changes
; Changes for Novell RPL. These guys reserve memory for themselves by
;reducing int 12h size and add this memory to the system at autoexec time by
;running a program that changes arenas. This changes the largest block that
;command.com gets and so changes the transient segment. So, command.com does
;a checksum at the wrong address and thinks that the transient is destroyed
;and tries to reload it. At this point, no Comspec is defined and so the
;reload fails, hanging the system. To get around this we just copy the
;transient from the previous address to the new address(if changed) and
;then let command.com do the checksum. So, if the transient area is not
;corrupted, there will not be any reload. In Novell's case, the transient
;is not really corrupted and so this should work.
;
cmp bx,TrnSeg ;Segment still the same?
je LodCom1 ;yes, dont copy
;
;Check if the new segment is above or below the current move. If the new
;segment is above(i.e new block is larger than previous block), then we
;have to move in the reverse direction
;
mov cx,offset TRANGROUP:TranSpaceEnd ;cx = length to move
ja mov_down ;new seg > old seg, reverse move
xor si,si ;normal move
mov di,si
cld
jmp short copy_trans
mov_down:
mov si,cx ;reverse move, start from end
dec si
mov di,si
std
copy_trans:
push ds
push es
mov es,bx ;dest segment
mov ds,TrnSeg ;source segment
assume ds:nothing
rep movsb ;copy transient
cld
pop es
pop ds
assume ds:DATARES
;
;M038; End of changes
;
mov TrnSeg,bx ;new location of transient
LodCom1:
;; mov ax,cs
;; mov ss,ax
;SR; At this point ds = DATARES which is where the stack is located
;
mov ax,ds
mov ss,ax
assume ss:DATARES
mov sp,offset DATARES:RStack
;; mov ds,ax
assume ds:DATARES
call HeadFix ; close files, restore stdin, stdout
xor bp,bp ; flag command ok
mov ax,-1
xchg ax,VerVal
cmp ax,-1
je NoSetVer
mov ah,SET_VERIFY_ON_WRITE ; AL has correct value
int 21h
NoSetVer:
cmp SingleCom,-1
jne NoSng
jmp FatalRet2 ; we have finished the single command
NoSng:
call ChkSum ; check the transient
cmp dx,Sum
je HavCom ; transient ok
Bogus_Com:
mov Loading,1 ; flag DskErr routine
call LoadCom
ChkSame:
call ChkSum
cmp dx,Sum
jz HavCom ; same command
Also_Bogus:
call WrongCom
jmp short ChkSame
HavCom:
mov Loading,0 ; flag to DskErr
mov si,offset DATARES:TranVars
mov di,offset TRANGROUP:HeadCall
mov es,TrnSeg
cld
mov cx,offset DATARES:TranVarEnd
sub cx,si
rep movsb ; transfer info to transient
mov ax,MemSiz
mov word ptr ds:Pdb_Block_Len,ax ; adjust my own header
;*** TJmp - jump-off to transient
;
; Public label so debugger can find this spot.
TJmp:
jmp Trans
;*** TRemCheck - far version of RemCheck for transient
TRemCheck proc far
pop ds ;ds = DATARES
add sp,2 ;discard old value of ds
call RemCheck
ret
TRemCheck endp
;*** RemCheck
;
; ENTRY AL = drive (0=default, 1=A, ...)
;
; EXIT ZR set if removeable media
; ZR clear if fixed media
;
; USED none
RemCheck:
savereg <ax,bx>
mov bx,ax
mov ax,(IOCTL shl 8) + 8
int 21h
jnc rcCont
; If an error occurred, assume the media is non-removable.
; AX contains the non-zero error code from the int 21, so
; 'or ax,ax; sets non-zero. This behavior makes network drives
; appear to be non-removable.
or ax,ax
jmp short ResRegs
rcCont:
and ax,1
not ax
ResRegs:
restorereg <bx,ax>
ret
;*** THeadFix
;
; Far version of HeadFix, called from transient.
THeadFix proc far
pop ds ;ds = DATARES
add sp,2 ;discard old ds value on stack
call HeadFix
ret
THeadFix endp
;*** HeadFix
HeadFix:
call SetVect ; set vectors to our values
; Clean up header
; Bugbug: optimize:
; mov word ptr ds:Pdb_Jfn_Table,cx instead of separate bytes
xor bx,bx ; BX = handle = 0
mov cx,Io_Save ; CX = original stdin, stdout
mov dx,word ptr ds:Pdb_Jfn_Table ; DX = current stdin, stdout
cmp cl,dl
je Chk1 ; stdin matches
mov ah,CLOSE
int 21h ; close stdin
mov ds:Pdb_Jfn_Table,cl ; restore stdin
Chk1:
inc bx ; BX = handle = 1
cmp ch,dh
je ChkStderr ; stdout matches
mov ah,CLOSE
int 21h ; close stdout
mov ds:Pdb_Jfn_Table+1,ch ; restore stdout
ChkStderr:
inc bx ; BX = handle = 2
mov dl,byte ptr ds:[Pdb_Jfn_Table+2] ; Dl = current stderr
mov cl,Io_Stderr ; Cl = original stderr
cmp dl,cl
je ChkOtherHand ; stderr matches
mov ah,CLOSE
int 21h ; close stderr
mov ds:Pdb_Jfn_Table+2,cl ; restore stderr
ChkOtherHand:
add bx,3 ; skip handles 3,4
ifdef NEC_98
add bx,4 ; skip handles 2,3,4
endif ;NEC_98
mov cx,FILPERPROC - 5 ; CX = # handles to close
; (handles 0-4 already done)
;; williamh: March 30, 1993, don't close invalid handle , save some time
push si
mov si, pdb_jfn_table ;go to the handle table
CloseLoop:
cmp byte ptr [bx][si], 0ffh
je Skip_this_handle
mov ah,CLOSE
int 21h ; close each handle
Skip_this_handle:
inc bx
loop CloseLoop
pop si
; Bugbug: since this is for transient code, move it there
; M012: remove this CS -> DS. Must've been missed during
; purification.
;; push ds ; save data segment
;; push cs ; get local segment into DS
;; pop ds ;
cmp Append_Flag,-1 ; do we need to reset APPEND?
jne Append_Fix_End ; no - just exit
mov ax,AppendSetState ; set the state of Append
mov bx,Append_State ; back to the original state
int 2Fh ;
mov Append_Flag,0 ; set append flag to invalid
Append_Fix_End: ;
;; pop ds ; get data segment back
ret
;*** SavHand - save current program's stdin/out & set to our stderr
;
; ENTRY nothing
;
; EXIT nothing
;
; USED flags
;
; EFFECTS
; Handle01 = current program's stdin,stdout JFN entries
; current program's stdin,stdout set to our stderr
;
;SR;
; Changed ds = DATARES. We need it to access our JFN_Table
; Called from ContC ( ds = DATARES ) and DskErr ( ds = DATARES ).
;
SavHand proc
assume ds:DATARES,es:NOTHING,ss:NOTHING
push bx ;preserve registers
push ax
push es
push ds ; save DATARES value
mov ah,GET_CURRENT_PDB
int 21h ; BX = user's header seg addr
mov ds,bx ; DS = user's header seg addr
lds bx,ds:PDB_JFN_POINTER ; DS:BX = ptr to JFN table
mov ax,word ptr ds:[bx] ; AX = stdin,stdout JFN's
pop es ;es = DATARES
push es ;save it back on stack
mov es:Handle01,ax ; save user's stdin, stdout
;SR;
; Use es to address Handle01 & our JFN_Table
;
mov al,es:[PDB_JFN_TABLE+2] ; AL = COMMAND stderr
mov ah,al ; AH = COMMAND stderr
mov word ptr ds:[bx],ax ; set user's stdin/out to our stderr
pop ds ; restore registers
pop es
pop ax
pop bx
ret
SavHand endp
assume ds:DATARES
GetComDsk2:
call GetComDsk
jmp LodCom1 ; memory already allocated
RestHand:
push ds
push bx ; restore stdin, stdout to user
push ax
mov ah,GET_CURRENT_PDB
int 21h ; point to user's header
mov ax,Handle01
mov ds,bx
assume ds:NOTHING
lds bx,ds:Pdb_Jfn_Pointer ; DS:BX = ptr to jfn table
mov word ptr ds:[bx],ax ; stuff his old 0 and 1
pop ax
pop bx
pop ds
ret
assume ds:DATARES,ss:DATARES
Hopeless:
mov dx,offset DATARES:ComBad
jmp FatalC
GetComDsk:
mov al,ComDrv
call RemCheck
jnz Hopeless ; non-removable media
GetComDsk3:
cmp dx,offset DATARES:ComBad
jnz GetComDsk4
mov dx,offset DATARES:ComBad ; DX = ptr to msg
invoke RPrint ; say COMMAND is invalid
GetComDsk4:
; Bugbug: there's always a drive here? No need to check?
cmp PutBackDrv,0 ; is there a drive in the comspec?
jnz Users_Drive ; yes - use it
mov ah,GET_DEFAULT_DRIVE ; use default drive
int 21h
add al,"A" ; convert to ascii
mov PutBackDrv,al ; put in message to print out
Users_Drive:
mov dx,offset DATARES:PutBackMsg ; prompt for diskette
mov si,offset DATARES:PutBackSubst ; containing COMMAND
invoke RPrint
mov dx,offset DATARES:Prompt ; "Press any key"
invoke RPrint
call GetRawFlushedByte
ret
;*** GetRawFlushedByte - flush world and get raw input
GetRawFlushedByte:
mov ax,(STD_CON_INPUT_FLUSH shl 8) or RAW_CON_INPUT
int 21h ; get char without testing or echo
mov ax,(STD_CON_INPUT_FLUSH shl 8) + 0
int 21h
; Bugbug: get rid of this return and the following retz.
return
;*** LoadCom - load in transient
LoadCom:
inc bp ; flag command read
mov dx,offset DATARES:ComSpec
mov ax,OPEN shl 8
int 21h ; open command.com
jnc ReadCom
cmp ax,ERROR_TOO_MANY_OPEN_FILES
jnz TryDoOpen
mov dx,offset DATARES:NoHandMes
jmp FatalC ; will never find a handle
TryDoOpen:
call GetComDsk
jmp LoadCom
ReadCom:
mov bx,ax ; BX = handle
mov dx,offset RESGROUP:TranStart
xor cx,cx ; CX:DX = seek loc
mov ax,LSEEK shl 8
int 21h
jc WrongCom1
mov cx,offset TRANGROUP:TranSpaceEnd - 100h
push ds
mov ds,TrnSeg
assume ds:NOTHING
mov dx,100h
mov ah,READ
int 21h
pop ds
assume ds:DATARES
WrongCom1:
pushf
push ax
mov ah,CLOSE
int 21h ; close command.com
pop ax
popf
jc WrongCom ; error on read
cmp ax,cx
retz ; size matched
WrongCom:
mov dx,offset DATARES:ComBad
call GetComDsk
jmp LoadCom ; try again
;*** ChkSum - compute transient checksum
ChkSum:
push ds
mov ds,TrnSeg
mov si,100h
mov cx,offset TRANGROUP:TranDataEnd - 100H
Check_Sum:
cld
shr cx,1
xor dx,dx
Chk:
lodsw
add dx,ax
adc dx,0
loop Chk
pop ds
ret
;*** SetVect - set interrupt vectors
SetVect:
mov dx,offset DATARES:LodCom_Trap
mov ax,(SET_INTERRUPT_VECTOR shl 8) or 22h
mov word ptr ds:Pdb_Exit,dx
mov word ptr ds:Pdb_Exit+2,ds
int 21h
mov dx,offset DATARES:Ctrlc_Trap
inc al
int 21h
mov dx,offset DATARES:CritErr_Trap
inc al
int 21h
ret
;SR;
; We have this to take care of the extra values pushed on the stack by
;the stub before jumping to LodCom1. We set up ds here and then jump to
;Lodcom1
;
public TrnLodCom1
TrnLodCom1:
pop ds ;ds = DATARES
add sp,2
; pop ds:OldDS
jmp LodCom1
;*** EndInit - end up initialization sequence
;
; Move the environment to a newly allocated segment.
;;EndInit:
;; push ds ; save segments
;; push es ;
;; push cs ; get resident segment to DS
;; pop ds ;
;; assume ds:RESGROUP
;; mov cx,UsedEnv ; get number of bytes to move
;; mov es,EnvirSeg ; get target environment segment
;; assume es:NOTHING
;;
;; mov ds:Pdb_Environ,es ; put new environment in my header
;; mov ds,OldEnv ; source environment segment
;; assume ds:NOTHING
;; xor si,si ; set up offsets to start of segments
;; xor di,di
;; cld
;; rep movsb ; move it
;; xor ax,ax
;; stosb ; make sure it ends with double-null
;;
;; cmp ResetEnv,1 ; do we need to setblock to env end?
;; jne NoReset ; no - we already did it
;; mov bx,EnvSiz ; BX = size of environ in paragraphs
;; push es ; save environment - just to be sure
;; mov ah,SETBLOCK ;
;; int 21h
;; pop es
;;
;;NoReset:
;; mov InitFlag,FALSE ; turn off init flag
;; pop es
;; pop ds
;; jmp LodCom ; allocate transient
;
;The init code has been changed to take care of the new way in which the
;environment segment is allocated.
;NB: We can use all the init variables at this point because they are all in
;RESGROUP
;Bugbug: The above approach will not work for ROMDOS
;
IF 0
EndInit:
push ds
push es ;save segments
push cs
pop ds
assume ds:RESGROUP
;
;Chuckenv flag signals whether it is a passed environment or not
;
mov bx,ds
mov es,bx ;es = RESGROUP
;
;ResSize is the actual size to be retained -- only data for HIMEM COMMAND,
; code + data for low COMMAND
;
mov bx,ResSize ;Total size of resident
mov ah,SETBLOCK
int 21h ;Set block to resident size
;
;Allocate the correct size for the environment
;
mov bx,EnvSiz ;bx = env size in paras
mov ah,ALLOC
int 21h ;get memory
jc nomem_err ;out of memory,signal error
mov EnvirSeg,ax ;Store new environment segment
mov ds:PDB_Environ,ax ;Put new env seg in PSP
mov es,ax ;es = address of allocated memory
assume es:nothing
;
;Copy the environment to the newly allocated segment
;
mov cx,UsedEnv ;number of bytes to move
push ds
mov ds,OldEnv ;ds = Old environment segment
assume ds:nothing
xor si,si
mov di,si ;Start transfer from 0
cld
rep movsb ;Do the copy
xor ax,ax
stosb ;Make it end with double-null
pop ds ;ds = RESGROUP
assume ds:RESGROUP
;
;We have to free the old environment block if it was allocated by INIT
;
cmp Chuckenv,0 ;has env been allocated by INIT?
jne no_free ;no, do not free it
mov ax,OldEnv ;Get old environment
mov es,ax
mov ah,DEALLOC
int 21h ;Free it
no_free:
mov InitFlag,FALSE ;indicate INIT is done
pop es
pop ds
assume ds:nothing
jmp LodCom ;allocate transient
nomem_err:
;
;We call the error routine which will never return. It will either exit
;with an error ( if not the first COMMAND ) or just hang after an error
;message ( if first COMMAND )
;
call Alloc_error
ENDIF
CODERES ends
; This TAIL segment is used to produce a PARA aligned label in
; the resident group which is the location where the transient
; segments will be loaded initial.
TAIL segment public para
org 0
TranStart label word
public TranStart
TAIL ends
; This TAIL segment is used to produce a PARA aligned label in
; the transient group which is the location where the exec
; segments will be loaded initial.
;
; Bugbug: Is TRANTAIL used anymore?
TRANTAIL segment public para
org 0
ExecStart label word
TRANTAIL ends
end