title 'FDOS IOCTL - DOS file system input/output control'
;    File              : $FIOCTL.A86$
;
;    Description       :
;
;    Original Author   : DIGITAL RESEARCH
;
;    Last Edited By    : $CALDERA$
;
;-----------------------------------------------------------------------;
;    Copyright Work of Caldera, Inc. All Rights Reserved.
;      
;    THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
;    PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
;    ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
;    WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
;    THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
;    HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
;    AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
;    AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
;    COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
;    CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
;    TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
;    CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
;    AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
;    CIVIL LIABILITY.
;-----------------------------------------------------------------------;
;
;    *** Current Edit History ***
;    *** End of Current Edit History ***
;    $Log$
;    FIOCTL.A86 1.18 93/12/09 23:39:17
;    Move non-inherited bit to correct place in file handle
;    FIOCTL.A86 1.17 93/11/03 13:38:29
;    Int 21 4409 return 0x0080 if drive is joined
;    FIOCTL.A86 1.16 93/11/02 18:04:17
;    Int21/4400 from file returns AH=0
;    FIOCTL.A86 1.15 93/09/14 20:03:15
;    Trust LFLG_PHYSICAL
;    FIOCTL.A86 1.12 93/06/17 22:11:50 
;    support for ioctl 10/11 query ioctl support
;    FIOCTL.A86 1.11 93/06/16 16:21:37
;    Codepage preparation bug fixed
;    ENDLOG

;   Date       Modification
;   ---------  ---------------------------------------
;   29 Jun 89  Initial version splits from FDOS
;   21 Nov 89  iocE/F use relative unit number
;   12 Dec 89  ioc2/3 zero the unused portions of req hdr
;              save a few bytes in other ioc's while I'm here
;    4 Jun 90  default file access permissions and user group support
;    5 Jun 90  ioc0/1 tidied up
;   12 Jun 90  get_doshndl parameter in BX not AX
;    2 Oct 90  net_vec moves to per-VC basis (info held in CCB_)
;   13 feb 91  ioc1 tests FHIO_DEV, not HM_DEV, (123 MSNet printing)
;    7 may 91  ioc2,ioc3,ioc6,ioc7,iocC destabilised for Lanstatic
;              by-product is ioc6 for disks returns read-ahead char
;              in AH
;   28 feb 92  ioctl C checks bit 6 not bit 14 in DH_ATTRIB
;    3 aug 92  ioctl C/D pass thru' SI/DI
;    5 aug 92  substatial ioctl rework saves a few bytes - ioctl
;              request header now built by PCMODE

	eject ! include i:fdos.equ
	eject ! include rh.equ
	eject ! include i:msdos.equ
	eject ! include i:mserror.equ
	eject ! include i:doshndl.def	; DOS Handle Structures
	eject ! include i:driver.equ
	eject ! include i:f52data.def	; DRDOS Structures
	eject


PCMODE_DATA	dseg	BYTE
	extrn	ioctlRH:byte		; request header is build in PCMODE
					;  data area

BDOS_DATA	dseg	word
	extrn	fdos_pb:word
	extrn	fdos_ret:word
	extrn	last_drv:byte
	extrn	req_hdr:byte
if PASSWORD
	extrn	global_password:word
endif

BDOS_CODE	cseg

	extrn	local_disk:near
	extrn	device_driver:near
	extrn	block_device_driver:near
	extrn	fdos_error:near
	extrn	fdos_ED_DRIVE:near
	extrn	fdos_ED_FUNCTION:near
	extrn	fdos_read:near
	extrn	get_ddsc:near
	extrn	vfy_dhndl_ptr:near
	extrn	get_pb2_drive:near
	extrn	ioc6_dev:near		; IOCTL(6): input status for device
	extrn	ioc7_dev:near		; IOCTL(7): output status for device
	extrn	vfy_dhndl_ptr:near
	extrn	verify_handle:near
	extrn	reload_registers:near
if JOIN
	extrn	get_ldt:near
endif
if PASSWORD
	extrn	hash_pwd:near
endif

	public	fdos_ioctl

eject
;	INPUT/OUTPUT CONTROL (IOCTL)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    26   |  param  |  func   |   request header  |
;	+----+----+----+----+----+----+----+----+----+----+


;	entry:
;	------
;	param:	handle/drive on some functions
;	func:	sub function (0-A)
;	request header:	far pointer to IOCTL request header

;	exit:
;	-----
;	AX:	return code or error code ( < 0)
;	param:	return value from some functions



ioctl_tbl	dw	ioctl0		; 0-get handle status
		dw	ioctl1		; 1-set handle status
		dw	ioctl2		; 2-receive control string (handle)
		dw	ioctl3		; 3-send control string (handle)
		dw	ioctl4		; 4-receive control string (drive)
		dw	ioctl5		; 5-send control string (drive)
		dw	ioctl6		; 6-input status
		dw	ioctl7		; 7-output status
		dw	ioctl8		; 8-removable media check
		dw	ioctl9		; 9-networked drive check
		dw	ioctlA		; A-networked handle check
if PASSWORD
		dw	ioctl54		; B-set global password
else
		dw	device_ED_FUNCTION
endif
		dw	ioctlC		; C-code page support
		dw	ioctlD		; D-generic IOCTL disk i/o
		dw	ioctlE		; E-get logical drive
		dw	ioctlF		; F-set logical drive
		dw	ioctl10		; 10-query IOCTL for char devs
		dw	ioctl11		; 11-query IOCTL for disks

NUM_IOCTL	equ	(offset $ - offset ioctl_tbl)/WORD



fdos_ioctl:
;----------
	mov	bx,2[bp]		; BX -> parameter block
	mov	bx,4[bx]		; get I/O control subfunction
	cmp	bx,NUM_IOCTL		; is it in the supported range?
	 jae	device_ED_FUNCTION	; skip if value too large
	shl	bx,1			; else make it word index
	jmp	ioctl_tbl[bx]		; call the right function
device_ED_FUNCTION:
	mov	bx,ED_FUNCTION		; "invalid function"
	ret



ioctl0:		; get device status
;------
;	Note:	We store the binary flag for the standard console
;		handles in the console mode, not in the file handle,
;		as the handles are shared across all consoles...

	call	vfy_dhndl_ptr		; check if good handle
	mov	ax,es:DHNDL_WATTR[bx]	; get attrib from doshndl
	test	ah,DHAT_REMOTE/256
	mov	ah,0			; files/networks return 0 in AH/DH
	 jnz	device_OK		; return attrib if network device
	test	al,DHAT_DEV		; or a file
	 jz	device_OK
	les	bx,es:DHNDL_DEVPTR[bx]	; get real device driver address
	mov	ah,es:byte ptr 5[bx]	; get device driver attribute
;	jmp	device_OK

device_OK:
;---------
	mov	bx,2[bp]		; get parameter block address
	mov	6[bx],ax		; save returned status
	xor	bx,bx			; successful return code
	ret

ioctl1:		; set device status
;------
	call	vfy_dhndl_ptr		; make sure this is an open handle
	test	es:DHNDL_WATTR[bx],DHAT_DEV
	 jz	device_ED_FUNCTION	; can't set status of disk files
	mov	ax,word ptr ioctlRH+14
					; pick up new device status
	test	ah,ah			; test if high byte is zero
	 jnz	device_ED_FUNCTION	; skip if O.K.
	or	al,DHAT_DEV		; make sure it stays a device
	mov	es:DHNDL_ATTR[bx],al	; store ioctl state in doshndl
	jmps	device_OK		; success


ioctl2:		; receive control string (devicehandle)
;------
ioctl3:		; send control string (device handle)
;------
ioctlC:		; generic ioctl (device handle)
;------
ioctl10:	; query ioctl support (device handle)
;-------
	call	vfy_dhndl_ptr		; check file handle #
	call	local_disk		; get MXdisk
	call	verify_handle		; make sure the handle is good
	 jnc	short_fdos_ED_FUNCTION	;  and is for a DEVICE
	xor	cx,cx			; device relative unit # always zero
	les	si,es:DHNDL_DEVPTR[bx]	; ES:SI -> device driver
	jmps	ioc2345CDcommon		;  now use common code

ioctl4:		; receive control string (drive)
;------
ioctl5:		; send control string (drive)
;------
ioctlD:		; generic ioctl (drive)
;------
ioctl11:	; query ioctl support (drive)
;-------
	call	local_disk		; get MXdisk, switch stack
	call	get_pb2_ddsc		; get drives DDSC_
	mov	cl,es:DDSC_RUNIT[bx]	; get relative unit #
	les	si,es:DDSC_DEVHEAD[bx]	; ES:SI -> device header
;	jmps	ioctl2345Common		;  now use common code

ioc2345CDcommon:
;---------------
; On Entry:
;	ES:SI -> device driver header
;	CL = media byte (0 if character device)
;	CH = relative unit  (0 if character device)
;	MXDisk obtained
; On Exit:
;	IOCTL performed
;
	mov	ax,fdos_pb+6		; device driver support required
	test	es:DH_ATTRIB[si],ax	; does device driver support function ?
	 jz	short_fdos_ED_FUNCTION
	push	ds
	push es ! pop ds		; DS:SI -> device driver
	push ss ! pop es
	mov	bx,offset ioctlRH	; ES:BX -> request header
	mov	es:RH_UNIT[bx],cl	; set relative unit for block devices
	call	device_driver		; call the device driver
	pop	ds			; check for errors on return
;	jmp	fdos_error_check

fdos_error_check:
;-------------
; On Entry:
;	AX = Req Status
; On Exit:
;	SIGN set if an error, AX&BX = Internal DOS error code
;
	test	ax,ax			; top bit == 1 if error
	 jns	fdos_ioc_ec10		; skip if no errors
	xor	ah,ah
    add ax,-ED_PROTECT      
    neg ax          
	jmp	fdos_error		; return critical error
fdos_ioc_ec10:
	ret

;	IOCTL subfunctions:
	

get_pb2_ddsc:
;------------
; On Entry:
;	local_disk called, pick us drive from pb2
; On Exit:
;	ES:BX -> DDSC_ for the drive
;
	call	get_pb2_drive		; get specified drive
	cmp	al,last_drv		; is it a valid drive
	 ja	bad_drive
	call	get_ldt			; ES:BX -> LDT for this drive
	 jc	get_pb2_ddsc10		;  no LDT, physical=logical
	mov	ax,es:LDT_FLAGS[bx]
	test	ah,LFLG_NETWRKD/256
	 jnz	short_fdos_ED_FUNCTION	; reject network drives
	test	ah,LFLG_PHYSICAL/256
	 jz	bad_drive
	test	ah,LFLG_JOINED/256
	 jnz	bad_drive		; reject JOIN'd drives
	mov	al,es:LDT_NAME[bx]	; get physical drive from LDT
	and	al,1Fh			; convert to 1 based drive
	dec	ax			; make that zero based
get_pb2_ddsc10:
	call	get_ddsc		; ES:BX -> DDSC_
	 jc	bad_drive		;  or does it?
	ret

bad_drive:
	jmp	fdos_ED_DRIVE		; invalid drive specified

short_fdos_ED_FUNCTION:
	jmp	fdos_ED_FUNCTION


ioctl8:		; removable media check
;------
	call	local_disk		; get MXdisk, switch stack
	call	get_pb2_ddsc		; get drives DDSC_
	push	ds
	lds	si,es:DDSC_DEVHEAD[bx]	; DS:SI -> device driver
	test	ds:DH_ATTRIB[si],DA_REMOVE
	pop	ds			; do we support the check ?
	 jz	short_fdos_ED_FUNCTION	; if we don't then don't ask
	mov	req_hdr,RH15_LEN
	mov	req_hdr+2,CMD_FIXED_MEDIA
	call	block_device_driver	; call the device driver
	 js	short_fdos_ED_FUNCTION
	and	ax,RHS_BUSY		; BUSY bit set if permanent - we just
	xchg	ah,al			;  need to get bit in the right place
	shr	ax,1			; now 1 if permanent media
	mov	fdos_pb+6,ax		; return status removable (=0)
ioctl8_10:
	ret



ioctl6:		; file input status
;------
	call	vfy_dhndl_ptr		; make sure this is an open handle
	mov	ax,es:DHNDL_WATTR[bx]
	test	ax,DHAT_REMOTE
	 jnz	ioctl6_10		; always ask networked handles
	test	ax,DHAT_DEV
	 jz	ioctl6_10		; files are always askable
	jmp	ioc6_dev

ioctl6_10:				; disk files/network devices
	push	es:DHNDL_POSLO[bx]	; save current position in file
	push	es:DHNDL_POSHI[bx]	;  so we can read ahead
	push	es
	push	bx			; save DHNDL_ too..
	push	bp			; save stack frame
	mov	si,2[bp]		; SI -> parameter block
	mov	ax,1
	push	ax			; read 1 byte ahead
	push	ds			; use fdos_pb as read-ahead
	lea	ax,6[si]		;  buffer
	push	ax			
	push	ds:word ptr 2[si]	; user file number 
	mov	ax,MS_X_READ		; READ function #, so we create
	push	ax			;  a dummy fdos_pb
	mov	bx,sp			; SS:BX -> dummy fdos_pb
	mov	cx,offset ioctl6_20	; CX -> return address
	push	cx			; Return to here
	push	ss			; save parameter segment
	push	bx			; save parameter offset
	push	ax			; save sub-function
	mov	bp,sp			; SS:BP -> working variables
	call	fdos_read		; make FDOS_READ do the hard word
	add	sp,4*WORD		; discard param's on stack
ioctl6_20:
	add	sp,4*WORD		; discard most of dummy fdos_pb
	pop	cx			; return # read
	cmp	bx,ED_LASTERROR		; did we succeed ?
	 jb	ioctl6_30		; if so we can trust # read
	xor	cx,cx			; else in error assume nothing
ioctl6_30:
	pop	bp			; recover stack frame
	pop	bx			; rewind DHNDL_POS to where
	pop	es
	pop	es:DHNDL_POSHI[bx]	;  it was before we started
	pop	es:DHNDL_POSLO[bx]
	mov	ax,1a00h		; assume not ready
	 jcxz	ioctl6_40
	dec	ax			; AL = FF, ie. ready
	mov	si,2[bp]		; SI -> parameter block
	mov	ah,ds:byte ptr 6[si]	; get character we read
ioctl6_40:
	jmp	device_OK


ioctl7:		; file output status
;------
	call	vfy_dhndl_ptr		; make sure this is an open handle
	mov	ax,0FFh			; assume it's networked/disk
	mov	dx,es:DHNDL_WATTR[bx]
	test	dx,DHAT_REMOTE
	 jnz	ioctl6_40		; networked handles are always ready
	test	dx,DHAT_DEV
	 jz	ioctl6_40		; files are always ready
	jmp	ioc7_dev		; devices we ask...



ioctl9:		; networked drive check
;------
	call	local_disk		; get disk semaphore
	call	get_pb2_drive		; get specified drive
	call	get_ldt			; ES:BX -> LDT for this drive
	 jc	ioctl940
	mov	ax,es:LDT_FLAGS[bx]
	test	ah,LFLG_NETWRKD/256
	 jz	ioctl910
if 1
	mov	ax,1000h		; return drive as remote
else
	les	di,es:LDT_PDT[bx]	; pick up network internal pointer
	mov	ax,es:4[di]		; pick up garbage
	or	ah,10h			; return drive as remote
endif
	jmps	ioctl930

ioctl910:
	test	ah,LFLG_PHYSICAL/256
	 jz	ioctl940
	test	ah,LFLG_SUBST/256
	xchg	ax,dx			; save flags
	mov	ax,8000h		; assume it's SUBST'd
	 jnz	ioctl920
	test	dh,LFLG_JOINED/256
	xchg	al,ah			; assume it's JOIN'd
	 jnz	ioctl930
	xor	ax,ax			; clear if not
ioctl920:
	push	ax
	call	get_pb2_ddsc		; get drives DDSC_
	pop	ax
	les	si,es:DDSC_DEVHEAD[bx]	; ES:SI -> device driver
	or	ax,es:4[si]		; get device attributes
ioctl930:
	mov	fdos_pb+6,ax		; return updated status
	ret

ioctl940:
	jmp	fdos_ED_DRIVE		; return ED_DRIVE error


ioctlA:		; networked handle check
;------
	call	vfy_dhndl_ptr
	mov	ax,es:DHNDL_WATTR[bx]
	jmp	device_OK		; return attributes


ioctlE:
;------
	call	local_disk
	mov	al,CMD_GET_DEVICE	; get logical device
	jmps	iocEFcommon		; common code for IOCTL(E)/IOCTL(F)

ioctlF:
;------
	call	local_disk
	mov	al,CMD_SET_DEVICE	; set logical device
iocEFcommon:
    mov req_hdr,RH24_LEN    
	mov	req_hdr+2,al
	call	get_pb2_ddsc		; get drives DDSC_
	inc	ax			; make drive one-relative
	mov	req_hdr+13,al		; set this as new drive
	xor	ax,ax			; assume not supported
	push	ds
	lds	si,es:DDSC_DEVHEAD[bx]	; does device driver support function ?
	test	ds:DH_ATTRIB[si],DA_GETSET
	pop	ds
	 jz	iocF_single		; skip if not supported
	call	block_device_driver	; call the device driver
	call	fdos_error_check	; return any errors
	mov	al,req_hdr+1		; get returned drive
iocF_single:				; AX = return value
    mov ah,7            
	mov	fdos_pb+6,ax		; return the drive
	ret

if PASSWORD

ioctl54:	; set global password
;-------
	call	local_disk		; get the MX disk
	push	ds
	lds	si,dword ptr ioctlRH+14
	call	hash_pwd		; encrypt new default password
	pop	ds
	mov	global_password,ax
	ret

endif
	
end
