mirror of
https://github.com/SEPPDROID/DR-DOS-OpenDOS.git
synced 2025-10-22 16:04:20 +00:00
2395 lines
63 KiB
Plaintext
2395 lines
63 KiB
Plaintext
; File : $FUNCS.FDO$
|
|
;
|
|
; 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$;
|
|
; ENDLOG
|
|
; General function include module for FDOS.A86
|
|
|
|
BDOS_CODE cseg
|
|
|
|
public fdos_entry
|
|
|
|
;==========
|
|
fdos_entry: ; FDOS module entry point
|
|
;==========
|
|
; On Entry:
|
|
; DS:DX -> parameter block
|
|
; On exit:
|
|
; AX = BX = return code
|
|
; (DS/ES corrupted)
|
|
; entry: DS:DX = argument
|
|
;
|
|
; exit: AX,BX = return code
|
|
|
|
mov si,dx
|
|
lodsw ; AX = FDOS number
|
|
sub ax,39h ; base it at zero
|
|
jc fd_error ; stop if too low
|
|
cmp ax,FDOS_MAX ; check if function in range
|
|
jae fd_error ; yes, continue
|
|
|
|
push ds ; save parameter segment
|
|
push dx ; save parameter offset
|
|
push ax ; save sub-function
|
|
mov bp,sp ; SS:BP -> working variables
|
|
mov bx,ax
|
|
add bx,ax
|
|
add bx,ax
|
|
call fdos_tbl[bx]
|
|
add sp,3*WORD ; clean up stack
|
|
mov ax,bx
|
|
ret
|
|
|
|
fd_error:
|
|
mov ax,ED_FUNCTION ; return "invalid function"
|
|
mov bx,ax
|
|
ret
|
|
|
|
|
|
fdos_tbl dw fdos_mkdir ; 39-make directory
|
|
db 2
|
|
dw fdos_rmdir ; 3A-remove directory
|
|
db 2
|
|
dw fdos_chdir ; 3B-change directory
|
|
db 2
|
|
dw fdos_creat ; 3C-create file
|
|
db 4
|
|
dw fdos_open ; 3D-open file
|
|
db 4
|
|
dw fdos_close ; 3E-close file
|
|
db 1
|
|
dw fdos_read ; 3F-read from file
|
|
db 4
|
|
dw fdos_write ; 40-write to file
|
|
db 4
|
|
dw fdos_unlink ; 41-delete file
|
|
db 4
|
|
dw fdos_lseek ; 42-set file pointer
|
|
db 4
|
|
dw fdos_chmod ; 43-get/set file attributes
|
|
db 6
|
|
dw fdos_ioctl ; 44-IOCTL emulation
|
|
db 3
|
|
dw fdos_dup ; 45-duplicate handle
|
|
db 2
|
|
dw fdos_fdup ; 46-force duplicate handle
|
|
db 2
|
|
dw fdos_curdir ; 47-get current directory
|
|
db 3
|
|
dw fdos_getdpb ;*48*disk information
|
|
db 4
|
|
dw fdos_flush ;*49*flush buffers
|
|
db 0
|
|
dw fdos_select ;*4A*drive select
|
|
db 1
|
|
dw fdos_exec ;*4B*create child PSP
|
|
db 1
|
|
dw fdos_exit ;*4C*close child PSP
|
|
db 0
|
|
dw fdos_fcb ;*4D*generic FCB call
|
|
db 5
|
|
dw fdos_first ; 4E-find first matching file
|
|
db 4
|
|
dw fdos_next ; 4F-find next matching file
|
|
db 0
|
|
dw fdos_commit ;*50*commit file
|
|
db 1
|
|
dw fdos_mknew ;*51*make new file
|
|
db 4
|
|
dw fdos_lock ;*52*lock/unlock block
|
|
db 6
|
|
dw fdos_mkddsc ; 53 build DDSC from BPB
|
|
db 4
|
|
dw fdos_ddio ;*54*Int 25/26 emulation
|
|
db 6
|
|
dw fdos_expand ;*55*expand file name
|
|
db 6
|
|
dw fdos_move ; 56-rename file
|
|
db 6
|
|
dw fdos_dattim ; 57-get/set file name
|
|
db 4
|
|
dw fdos_reopen_fcb ; 58-FCB reopen support function
|
|
db 5
|
|
|
|
FDOS_MAX equ (offset $ - offset fdos_tbl) / 3
|
|
|
|
Public local_disk
|
|
|
|
local_disk: ; continue execution within MXdisk semaphore
|
|
;----------
|
|
; This function will make local copies of the parameters
|
|
; and continue
|
|
|
|
callf ss:lock_tables
|
|
pop fdos_addr ; return address
|
|
add sp,2*WORD ; discard return from call fdos_tbl[bx]
|
|
; and function #
|
|
pop si ; get parameter offset
|
|
pop ax ; get parameter segment
|
|
mov sp_save,sp ; save for re-entry/errors
|
|
mov ds,ax ; DS:SI -> paremeter block
|
|
fdos_reenter:
|
|
push ss ! pop es ; ES = local data segment
|
|
mov di,offset fdos_pb ; ES:DI -> local parameter block copy
|
|
lodsw ! stosw ; load subfunction number
|
|
mov es:fdos_info,si ; save it for BDOS return
|
|
mov es:fdos_info+WORD,ds ; save parameter segment for return
|
|
mov bx,ax
|
|
add bx,ax
|
|
add bx,ax ; BX = function # * 3
|
|
mov cl,byte ptr fdos_tbl-(39h*(WORD+BYTE))+WORD[bx]
|
|
mov ch,0
|
|
; get table of # of parameters
|
|
mov es:fdos_info+2*WORD,cx ; save the parameter count
|
|
rep movsw ; copy parameter block
|
|
push es ! pop ds ; DS = SYSDAT
|
|
mov ioexerr,0FFh ; assume default error code
|
|
mov rwmode,0
|
|
|
|
mov finddfcb_mask,DA_VOLUME*256
|
|
|
|
xor ax,ax
|
|
mov fdos_ret,ax ; assume success unless proven otherwise
|
|
call fdos_addr ; call code following "call local_disk"
|
|
mov ax,fdos_ret ; get return code, fall thru to exit
|
|
|
|
Public fdos_error
|
|
|
|
fdos_error:
|
|
;----------
|
|
; AX = return code
|
|
mov sp,sp_save ; stack to entry value (for errors)
|
|
mov si,offset fdos_pb+2 ; get local copy
|
|
les di,dword ptr fdos_info ; get parameter block address
|
|
mov cx,fdos_info+2*WORD ; get parameter block size
|
|
rep movsw ; copy return values
|
|
mov bx,ax
|
|
callf ss:unlock_tables
|
|
ret ; return to BDOS, which copies BX to AX
|
|
|
|
Public fdos_restart
|
|
|
|
fdos_restart: ; we want to restart the function after an error
|
|
;------------ ; we already have MXdisk
|
|
;
|
|
mov sp,sp_save ; break out from low levels
|
|
lds si,dword ptr fdos_info
|
|
dec si ! dec si ; DS:SI -> parameter block
|
|
jmps fdos_reenter ; try from scratch
|
|
|
|
fdos_ED_ROOM:
|
|
;-----------
|
|
mov ax,ED_ROOM ; no more files
|
|
jmps fdos_error
|
|
|
|
fdos_ED_DRIVE:
|
|
;-----------
|
|
mov ax,ED_DRIVE ; bad drive
|
|
jmps fdos_error
|
|
|
|
fdos_ED_PATH:
|
|
mov ax,ED_PATH ; invalid path or drive
|
|
jmps fdos_error
|
|
|
|
fdos_ED_PROTECT:
|
|
mov ax,ED_PROTECT ; write protect error
|
|
jmps fdos_error
|
|
|
|
Public fdos_ED_ACCESS
|
|
|
|
fdos_ED_ACCESS:
|
|
mov ax,ED_ACCESS ; access denied error
|
|
jmps fdos_error
|
|
|
|
eject
|
|
; MAKE DIRECTORY (MKDIR)
|
|
|
|
; +----+----+----+----+----+----+
|
|
; | 39 | name |
|
|
; +----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; name: segmented address of ASCIIZ name
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
|
|
fdos_mkdir:
|
|
;----------
|
|
call redir_asciiz_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call path_prep_chk ; parse path, walk down the tree
|
|
call finddfcbf ; find matching FCB
|
|
jnz mkdir_access ; file/dir exists, "access denied"
|
|
call allocdir ; allocate a directory entry
|
|
|
|
push ax ; save pointer to directory entry
|
|
lea si,info_fcb+1 ; SI -> file name
|
|
xchg ax,di ; DI -> DNAME
|
|
mov cx,11
|
|
rep movsb ; copy file name into directory buffer
|
|
; lea di,DATTS[si]
|
|
mov al,DA_DIR
|
|
stosb ; mark it as a directory
|
|
xor ax,ax
|
|
mov cx,(32-12)
|
|
rep stosb ; zero remainder of new FCB
|
|
pop si ; SI -> directory
|
|
|
|
call stamp_dir_entry ; set time and date in FCB
|
|
xor ax,ax
|
|
call alloc_cluster ; Allocate a block
|
|
jc mkdir_err ; Report Error(no room on disk)
|
|
mov si,dirp
|
|
mov DBLOCK1[si],ax ; Initialize 1st block of fcb
|
|
push ax ; save block
|
|
call update_fat ; Write the fat to disk
|
|
call update_dir ; Write the directory to disk
|
|
pop ax ; now initialise the sub-directory
|
|
call mkdir_init ; '.' and '..' entries
|
|
call update_dir ; update directory entry
|
|
mov ax,5 ; return a magic value for some
|
|
mov fdos_ret,ax ; PD expansion program
|
|
ret
|
|
|
|
mkdir_err:
|
|
;---------
|
|
mov bx,dirp ; can't make dir, no data space
|
|
mov DNAME[bx],0E5h ; so release our directory entry
|
|
call update_dir ; release this entry
|
|
mkdir_access:
|
|
jmp fdos_ED_ACCESS ; return "access denied" error
|
|
|
|
chk_no_dot: ; reject "." and ".." names
|
|
cmp info_fcb+1,'.'
|
|
je mkdir_access
|
|
ret
|
|
|
|
eject
|
|
; REMOVE DIRECTORY (RMDIR)
|
|
|
|
; +----+----+----+----+----+----+
|
|
; | 3A | name |
|
|
; +----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; name: segmented address of ASCIIZ name
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
|
|
fdos_rmdir:
|
|
;----------
|
|
call redir_asciiz_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call path_prep_chk ; parse path to bottom level
|
|
call finddfcbf ; try to locate directory entry
|
|
jz fdos_rmdir20 ; skip if we can't find it
|
|
test DATTS[bx],DA_DIR ; check directory attribute
|
|
jz fdos_rmdir30 ; O.K. if a directory
|
|
call chkcds ; make sure no-where current
|
|
jnc fdos_rmdir40
|
|
call path_prep ; redo as rmdir_ok may destroy info
|
|
call finddfcbf ; find the entry again
|
|
call rmdir_ok ; make sure not in use
|
|
call finddfcbf ; find the entry again
|
|
if PASSWORD
|
|
call check_pwd_any
|
|
endif
|
|
kill_entry:
|
|
call kill_file ; actually delete entry
|
|
update_dir_fat:
|
|
call update_dir ; update directory entry
|
|
jmp update_fat ; update file allocation table
|
|
|
|
fdos_rmdir20:
|
|
jmp fdos_ED_PATH ; "invalid path"
|
|
|
|
fdos_rmdir30:
|
|
jmp fdos_ED_ACCESS ; "access denied"
|
|
|
|
fdos_rmdir40:
|
|
mov ax,ED_DIR
|
|
jmp fdos_error
|
|
|
|
eject
|
|
; CHANGE DIRECTORY (CHDIR)
|
|
|
|
; +----+----+----+----+----+----+
|
|
; | 3B | name |
|
|
; +----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; name: segmented address of ASCIIZ name
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
|
|
|
|
chdir_ED_PATH:
|
|
jmp fdos_ED_PATH ; all logical errors give "no path"
|
|
|
|
chdir_ED_DRIVE:
|
|
mov ax,ED_DRIVE ; bad drive error message
|
|
chdir_err:
|
|
jmp fdos_error
|
|
|
|
fdos_chdir:
|
|
;----------
|
|
call redir_asciiz_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
fdos_move_chdir:
|
|
;---------------
|
|
mov orig_drive,0FFFFh ; set drive to invalid
|
|
les di,dword ptr fdos_pb+2 ; get string address
|
|
call get_path_drive ; from asciiz or default
|
|
jc chdir_ED_DRIVE ; continue if drive A: - Z:
|
|
call islocal ; reject networked drives
|
|
jc chdir_ED_DRIVE ; as bad drives
|
|
call path_prep_cont ; continue if drive legal
|
|
jc chdir_err ; return error to application
|
|
call chk_no_dev ; no devices allowed
|
|
call chk_no_wild ; no wild cards allowed here
|
|
cmp orig_drive,0FFFFh ; check if assign to specified drive
|
|
jne chdir10 ; skip drive specified
|
|
mov ax,path_drive ; else same as path drive
|
|
mov orig_drive,ax
|
|
chdir10:
|
|
if JOIN
|
|
call offer_join ; are we opening a JOIN'd drive ?
|
|
jnc chdir30 ; if so move to it and skip open_dir
|
|
endif
|
|
call chk_for_root ; check if we are in the root
|
|
je chdir30 ; if so, just skip the open_dir
|
|
call finddfcbf ; try to locate directory entry
|
|
jz chdir_ED_PATH ; skip if no valid directory
|
|
test DATTS[bx],DA_DIR ; check directory attribute
|
|
jz chdir_ED_PATH ; return if not a directory
|
|
if PASSWORD
|
|
call check_pwd_any ; check if PW prot'd & wrong PW
|
|
endif
|
|
call open_dir ; else open last directory
|
|
jc chdir_ED_PATH ; return an error if we can't
|
|
chdir30:
|
|
mov ax,orig_drive ; get logical drive
|
|
call get_ldt ; ES:BX -> LDT_
|
|
jc chdir10 ; no LDT_, must be init time...
|
|
mov si,es:LDT_FLAGS[bx] ; normally we inherit parents
|
|
mov cx,es:LDT_ROOTLEN[bx] ; root block and drive
|
|
mov dx,es:word ptr LDT_NAME[bx]
|
|
mov bx,es:LDT_ROOT[bx] ; if it's a "SUBST I: C:\PATH" form
|
|
cmp ax,path_drive ; we pick up new root and drive
|
|
je chdir50 ; like this
|
|
mov ax,path_drive ; ASCII drive from path drive
|
|
mov dx,'A'+256*':' ; make it into ASCII "C:"
|
|
add dx,ax ; in case LDT does not exist
|
|
call get_ldt ; get LDT_ in case it's subst'd
|
|
jc chdir40
|
|
mov dx,es:word ptr LDT_NAME[bx]
|
|
chdir40:
|
|
mov bx,fdos_hds_blk ; root block from fdos_hds
|
|
chdir50:
|
|
mov di,offset pri_pathname ; build new LDT_ here
|
|
or si,LFLG_PHYSICAL ; drive is always physical
|
|
mov LDT_FLAGS[di],si
|
|
mov LDT_ROOTLEN[di],cx ; inherit rootlen from parent
|
|
mov LDT_ROOT[di],bx ; root as above
|
|
mov ax,fdos_hds_blk ; current block from HDS
|
|
mov LDT_BLK[di],ax
|
|
mov al,fdos_hds_drv ; current drive we from HDS
|
|
mov LDT_DRV[di],al
|
|
mov ax,word ptr current_ddsc
|
|
mov word ptr LDT_PDT[di],ax
|
|
mov ax,word ptr current_ddsc+WORD
|
|
mov word ptr LDT_PDT+WORD[di],ax
|
|
push ss ! pop es ; now build ASCII path
|
|
; lea di,LDT_NAME[di] ; ES:DI -> name
|
|
xchg ax,dx
|
|
stosw ; plant 'A:'
|
|
mov ax,'\'
|
|
stosw ; make that 'A:\',0
|
|
dec di ; point at NUL
|
|
mov fdos_hds_root,0 ; we want to go back to root
|
|
mov fdos_pb+6,di ; rebuild into LDT_ at the address
|
|
mov fdos_pb+8,es
|
|
push ds ! pop es
|
|
chdir60:
|
|
call hdsblk ; get current block
|
|
if JOIN
|
|
jnz chdir65 ; if not at root unparse
|
|
call check_join ; check if drive is joined to another
|
|
jz chdir80 ; at root, unJOIN'd, so exit
|
|
; we are at the root of this drive
|
|
xchg al,fdos_hds_drv ; update HDS drive so we can force
|
|
push es ; an exit next time round
|
|
call get_ldt ; now copy the JOIN'd name
|
|
push ds ! push es ! pop ds ! pop es
|
|
lea si,LDT_NAME+3[bx] ; point to my JOIN data
|
|
mov di,offset save_area
|
|
call copy_asciiz
|
|
push es ! pop ds
|
|
pop es
|
|
jmps chdir70 ; copy this into place
|
|
chdir65:
|
|
else
|
|
jz chdir80 ; if at root, we're done
|
|
endif
|
|
call parent2save_area ; parental name written in save_area
|
|
chdir70:
|
|
dec di ; DI -> '\0' at end
|
|
mov al,'\' ! stosb ; add a trailing backslash
|
|
sub di,offset save_area+1
|
|
mov dx,di ; DX = length of name
|
|
|
|
push es ; insert name into user buffer
|
|
les di,dword ptr fdos_pb+6
|
|
mov al,0 ! scasb ; check if first directory
|
|
je chdir75 ; if not 1st in path
|
|
inc dx ; we need space for '\'
|
|
chdir75:
|
|
dec di ; adjust for scan
|
|
call mkspace_parent ; move ES:DI up by DX bytes
|
|
mov di,fdos_pb+6 ; ES:DI -> user buffer
|
|
rep movsb ; copy directory into user buffer
|
|
pop es ; ES = local segment again
|
|
jmp chdir60 ; try again til root
|
|
|
|
chdir80: ; arrive here when root reached
|
|
mov ax,orig_drive ; get logical drive
|
|
call get_ldt ; ES:BX -> LDT_
|
|
jc chdir85 ; no LDT_, what happended ?
|
|
push ds
|
|
mov di,bx ; ES:DI -> destination LDT_
|
|
push ss ! pop ds
|
|
mov si,offset pri_pathname ; DS:SI -> new LDT_ we just built
|
|
mov cx,LDT_LEN
|
|
rep movsb ; copy the new LDT_ into place
|
|
pop ds
|
|
cmp ax,path_drive ; if the drive's are same
|
|
je chdir85 ; "CD" or "SUBST d:=d:path"
|
|
or es:LDT_FLAGS[bx],LFLG_SUBST
|
|
lea di,LDT_NAME[bx] ; ES:DI -> name
|
|
xor ax,ax
|
|
mov cx,LDT_FLAGS-LDT_NAME ; name can be this long
|
|
repne scasb ; look for terminating NUL
|
|
sub di,bx ; get # chars skipped
|
|
dec di ; forget the NUL
|
|
cmp di,3 ; root is special case again !
|
|
jbe chdir85
|
|
mov es:LDT_ROOTLEN[bx],di ; set new root position
|
|
chdir85:
|
|
ret
|
|
|
|
eject
|
|
; CREATE FILE (CREAT)
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 3C | name | mode | attrib |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; name: segmented address of ASCIIZ name
|
|
; mode: open mode for handle
|
|
; attrib: attribute for file
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: file handle or error code ( < 0)
|
|
|
|
fdos_creat:
|
|
;----------
|
|
call redir_asciiz_dev_offer ; offer it as a network device
|
|
call asciiz_dev_offer ; offer it as a local device
|
|
call redir_asciiz_file_offer ; offer it as a network file
|
|
call local_disk ; get MXdisk, switch stack
|
|
call mustbe_free_handle ; make sure we have spare handle
|
|
call path_prep ; parse path, walk down the tree
|
|
call check_device ; is it a device ?
|
|
jc creat_disk
|
|
jmp open_dev ; open as a device handle
|
|
creat_disk:
|
|
call chk_no_dot_or_wild
|
|
test byte ptr fdos_pb+8,DA_VOLUME
|
|
jz creat_nolbl ; skip if not volume label
|
|
call mustbe_nolbl ; do we have an existing label ?
|
|
jmps creat_new_file ; no, create one
|
|
creat_nolbl:
|
|
call finddfcbf ; find entry, ignore labels
|
|
jz creat_new_file ; skip if it doesn't exist yet
|
|
|
|
call check_no_dir ; make sure not a directory
|
|
call check_ro ; check if file is read/only
|
|
call close_if_open ; make sure not open by any process
|
|
if PASSWORD
|
|
call check_pwd_d ; check if PW req'd & supplied
|
|
endif
|
|
mov bx,dirp
|
|
mov ax,DBLOCK1[bx] ; release all blocks of the file
|
|
call delfat ; so it is truncated to zero size
|
|
call update_fat ; update file allocation table
|
|
jmps creat2 ; reinitialize directory entry
|
|
|
|
creat_new_file: ; create new file - shared by MKNEW
|
|
call allocdir ; allocate a directory entry
|
|
creat2:
|
|
mov bx,dirp ; BX -> directory entry
|
|
lea di,DNAME[bx] ; DI -> name offset in dir buffer
|
|
mov si,offset info_fcb+1 ; SI -> name offset in FCB
|
|
mov cx,11
|
|
rep movsb ; copy name into directory
|
|
mov ax,fdos_pb+8 ; get creation file attribute
|
|
mov dx,fdos_pb+6 ; open in r/w, compatibilty mode
|
|
test al,not (DA_RO+DA_SYSTEM+DA_HIDDEN+DA_ARCHIVE)
|
|
jz creat5 ; allow r/o, hidden, system, archive
|
|
test al,DA_VOLUME ; failure as file, creating a label?
|
|
jz creat_access_err ; no, then it's really a problem
|
|
mov dl,DHM_RO+DHM_LOCAL ; open in compatibility mode
|
|
creat5:
|
|
or al,DA_ARCHIVE ; always create as new file
|
|
stosb ; update directory attributes
|
|
sub ax,ax ; zero out remainder of entry
|
|
mov cx,32-12
|
|
rep stosb ; zero remainder of entry
|
|
|
|
push bx ; save dirp
|
|
push dx ; save the open mode
|
|
call stamp_dir_entry ; set time and date in FCB
|
|
call update_dir ; update directory
|
|
pop ax ; restore open mode
|
|
pop bx ; restore dirp
|
|
jmp creat_handle ; now allocate file handle
|
|
|
|
creat_access_err:
|
|
jmp fdos_ED_ACCESS ; return "access denied"
|
|
|
|
|
|
eject
|
|
; OPEN FILE (OPEN)
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 3D | name | mode | attrib |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; name: segmented address of ASCIIZ name
|
|
; mode: open mode
|
|
; attrib: file attrib for search (default = 16h)
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: file handle or error code ( < 0)
|
|
|
|
fdos_open:
|
|
;---------
|
|
call redir_asciiz_dev_offer ; offer it as a network device
|
|
call asciiz_dev_offer ; offer it as a local device
|
|
call redir_asciiz_file_offer ; offer it as a network file
|
|
call local_disk ; get MXdisk, switch stack
|
|
call mustbe_free_handle ; make sure we have spare handle
|
|
call path_prep ; parse the path, go to bottom level
|
|
call check_device
|
|
jc open_disk
|
|
jmp open_dev ; open as a device
|
|
open_disk:
|
|
call chk_no_wild ; we don't allow no wild cards here
|
|
call finddfcbf ; search by name
|
|
jz open8 ; bail out if not found
|
|
mov al,byte ptr fdos_pb+8 ; check requested attributes
|
|
if PASSWORD
|
|
cmp word ptr DPWD[bx],0
|
|
je open1
|
|
or al,DA_HIDDEN
|
|
open1:
|
|
endif
|
|
not al
|
|
and al,DA_HIDDEN+DA_SYSTEM
|
|
or al,DA_DIR+DA_VOLUME
|
|
test DATTS[bx],al
|
|
jnz open9 ; error if attrib not as requested
|
|
mov ax,fdos_pb+6 ; determine open mode
|
|
if PASSWORD
|
|
sub dx,dx ; assume no PW conflicts
|
|
endif
|
|
test al,DHM_RW+DHM_WO ; test if read/only access
|
|
jz open2
|
|
if PASSWORD ; if write or r/w check writes
|
|
or dx,PWM_W ; check for write password
|
|
endif
|
|
; mov bx,dirp ; get directory pointer
|
|
test DATTS[bx],DA_RO ; read/only file?
|
|
jz open2 ; no, skip FCB check
|
|
test ax,DHM_FCB ; if FCB open of read-only file
|
|
jz open9 ; allow it, but adjust to read-only
|
|
and al,not DHM_RWMSK ; open mode and proceed
|
|
open2:
|
|
if PASSWORD
|
|
test al,DHM_WO ; will we try to read?
|
|
jnz open3 ; skip if not open for reading
|
|
or dx,PWM_R + PWM_E ; else check for read/exec password
|
|
open3:
|
|
push ax ; save the open mode
|
|
xchg ax,dx ; AX = password modes to check for
|
|
call check_pwd ; check for r/w passwords
|
|
pop ax
|
|
endif
|
|
jmp open_handle ; now allocate file handle
|
|
|
|
open8:
|
|
jmp fdos_ED_FILE ; "file not found"
|
|
open9:
|
|
jmp fdos_ED_ACCESS ; return "access denied" error
|
|
|
|
|
|
eject
|
|
; CLOSE FILE (CLOSE)
|
|
|
|
; +----+----+----+----+
|
|
; | 3E | handle |
|
|
; +----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; handle: open file handle to be closed
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 3Exx (where xx = # time it was open) or error code ( < 0)
|
|
|
|
fdos_close:
|
|
;----------
|
|
call vfy_dhndl_ptr ; check file handle #
|
|
call redir_dhndl_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call check_handle ; check if legal file handle
|
|
mov ax,es:DHNDL_COUNT[bx] ; return # times file WAS open
|
|
mov ah,3Eh ; AH = MS_X_CLOSE
|
|
mov fdos_ret,ax ; return 3Exx if successful
|
|
jc close20 ; skip if character device close
|
|
close_dhndl:
|
|
call release_handle ; release file handle into pool
|
|
callf ss:share_stub+S_CLOSE ; call the fdos stub routine
|
|
dec es:DHNDL_COUNT[bx] ; one less XFN refers to this IFN
|
|
test es:DHNDL_WATTR[bx],DHAT_REMOTE+DHAT_CLEAN
|
|
jnz close10 ; skip update if not dirty local file
|
|
call select_handle ; select the disk
|
|
call file_update ; update directory & FAT if written
|
|
close10:
|
|
ret
|
|
|
|
close20: ; handle refers to character device
|
|
jmp close_dev ; close the device handle
|
|
|
|
eject
|
|
; READ FROM FILE (READ)
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 3F | handle | buffer | count |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; handle: open file handle
|
|
; buffer: buffer to read into
|
|
; count: max. number of bytes to read
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: byte count or error code ( < 0)
|
|
|
|
fdos_read:
|
|
;---------
|
|
call vfy_dhndl_ptr ; check file handle #
|
|
test es:DHNDL_MODE[bx],DHM_WO
|
|
jnz rw_ED_ACCESS ; fail if open write only
|
|
call redir_dhndl_offer
|
|
test es:DHNDL_WATTR[bx],DHAT_DEV
|
|
jnz fdos_read_dev ; skip if character device
|
|
call local_disk ; get MXdisk, switch stack
|
|
call verify_handle ; check if legal file handle
|
|
mov cx,fdos_pb+8 ; get requested transfer length
|
|
mov ax,es:DHNDL_POSLO[bx] ; If we get an error we want to have
|
|
mov dx,es:DHNDL_POSHI[bx] ; xfer len adjusted for EOF (CopyIIpc)
|
|
sub ax,es:DHNDL_SIZELO[bx] ; are we beyond EOF ?
|
|
sbb dx,es:DHNDL_SIZEHI[bx]
|
|
; jb read2 ; beyond EOF already, no action required
|
|
; ja read2 ; more the 64K to go, no problems
|
|
jnz read2
|
|
cmp ax,cx ; do we want more than there is?
|
|
jae read2 ; yes, shorten the fdos_pb count
|
|
sub fdos_pb+8,ax ; in case of a critical error
|
|
read2:
|
|
mov fdrwflg,1 ; we're reading
|
|
les di,dword ptr fdos_pb+4 ; get disk transfer address
|
|
call fdosrw ; read from file
|
|
xor ax,ax ; return OK
|
|
xchg ax,fdos_ret ; get returned byte count
|
|
mov fdos_pb+8,ax ; return in requested count
|
|
ret
|
|
|
|
fdos_read_dev:
|
|
jmp read_dev ; read from character device
|
|
|
|
rw_ED_ACCESS:
|
|
mov bx,ED_ACCESS ; return access denied
|
|
ret
|
|
|
|
eject
|
|
; WRITE TO FILE (WRITE)
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 40 | handle | buffer | count |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; handle: open file handle
|
|
; buffer: buffer to be wriiten
|
|
; count: max. number of bytes to write
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: byte count or error code ( < 0)
|
|
|
|
fdos_write:
|
|
;----------
|
|
call vfy_dhndl_ptr ; check file handle #
|
|
test es:DHNDL_MODE[bx],DHM_WO+DHM_RW
|
|
jz rw_ED_ACCESS ; fail if not open in r/w or w mode?
|
|
call redir_dhndl_offer
|
|
test es:DHNDL_WATTR[bx],DHAT_DEV
|
|
jnz fdos_write_dev ; skip if not character device
|
|
call local_disk ; get MXdisk, switch stack
|
|
call verify_handle ; check if legal file handle
|
|
mov fdrwflg,0 ; we're writing
|
|
les di,dword ptr fdos_pb+4 ; get disk transfer address
|
|
mov cx,fdos_pb+8 ; get requested transfer length
|
|
call fdosrw ; write to file file
|
|
|
|
xor ax,ax ; return OK
|
|
xchg ax,fdos_ret ; get returned byte count
|
|
mov fdos_pb+8,ax ; return in requested count
|
|
ret
|
|
|
|
fdos_write_dev:
|
|
jmp write_dev ; write to character device
|
|
|
|
eject
|
|
; DELETE FILE (UNLINK)
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 41 | name | ***** | attrib |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; name: segmented address of ASCIIZ name
|
|
; attrib: delete mask (FCB only..)
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
|
|
|
|
fdos_unlink:
|
|
;-----------
|
|
call redir_asciiz_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call path_prep ; parse the path, go to bottom level
|
|
call chk_no_dev ; can't be a device
|
|
; look to see is the call is a remote
|
|
cmp ss:remote_call,0 ; a 21/5D00 one or a local FCB one
|
|
jz unlink1 ; neither, no wild cards allowed
|
|
jns unlink2 ; if it's an FCB call we need to check
|
|
test fdos_pb+8,DA_VOLUME ; for deleting a VOLUME label
|
|
jz unlink2
|
|
call find_labelf ; lets find the volume label
|
|
jz unlink4 ; reporting error if we don't match
|
|
jmp kill_entry ; found it - delete it
|
|
|
|
unlink4:
|
|
jmp fdos_ED_FILE ; return "file not found"
|
|
|
|
unlink1:
|
|
call chk_no_wild ; make sure not a wild card
|
|
unlink2:
|
|
call setenddir ; search from the start
|
|
|
|
; We now skip any labels/dirs, returning ED_FILE if no other matches
|
|
unlink3:
|
|
call finddfcb ; try to locate directory entry
|
|
jz unlink4 ; returning an error on failure
|
|
test DATTS[bx],DA_DIR+DA_VOLUME
|
|
jnz unlink3 ; make sure it isn't a label/directory
|
|
|
|
unlink5:
|
|
; we have a match, so return ED_ACCESS if we can't find a file we can delete
|
|
call unlink_attribs ; AL = attribs we can't delete
|
|
jz unlink7 ; yes, go for it
|
|
call finddfcb ; look for another entry
|
|
jnz unlink5 ; try this one instead
|
|
jmp fdos_ED_ACCESS ; "access denied" if nothing deleted
|
|
; since we found at least 1 file
|
|
|
|
; we will delete at least one file, so now we succeed
|
|
unlink6:
|
|
call unlink_attribs ; AL = attribs we can't delete
|
|
jnz unlink8 ; yes, go for it
|
|
unlink7:
|
|
call close_if_same_psp ; make sure not open by any process
|
|
if PASSWORD
|
|
call check_pwd_d ; check for password
|
|
endif
|
|
call kill_file ; delete file
|
|
unlink8:
|
|
call finddfcb ; look for another entry
|
|
jnz unlink6 ; try and delete it if we find one
|
|
jmp update_dir_fat ; write out dirty directories/FAT
|
|
|
|
|
|
unlink_attribs:
|
|
; On Entry:
|
|
; BX -> directory entry
|
|
; On Exit:
|
|
; AL = attribute mask which prevents deletion
|
|
; ZF = 1 if delete OK, ZF = 0 if file should be skipped
|
|
;
|
|
mov al,byte ptr fdos_pb+8 ; check attributes
|
|
if PASSWORD
|
|
cmp word ptr DPWD[bx],0
|
|
je unlink_attribs10
|
|
or al,DA_HIDDEN
|
|
unlink_attribs10:
|
|
endif
|
|
not al
|
|
and al,DA_HIDDEN+DA_SYSTEM+DA_RO
|
|
or al,DA_DIR+DA_VOLUME
|
|
test DATTS[bx],al ; should we delete this entry ?
|
|
ret
|
|
|
|
|
|
eject
|
|
; GET/SET FILE POSITION (LSEEK)
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 42 | handle | offset ! method |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; handle: open file handle
|
|
; offset: long integer offset
|
|
; method: 0 = begin, 1 = current, 2 = end of file
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
; offset: new offset
|
|
|
|
fdos_lseek:
|
|
;----------
|
|
call vfy_dhndl_ptr ; check file handle #
|
|
call redir_dhndl_offer
|
|
test es:DHNDL_ATTR[bx],DHAT_DEV
|
|
jnz lseek_dev ; skip if character device
|
|
call local_disk ; get MXdisk, switch stack
|
|
call check_handle ; check if legal file handle
|
|
mov ax,fdos_pb+4 ; get 32-bit file offset
|
|
mov dx,fdos_pb+6 ; into AX,DX
|
|
mov cx,fdos_pb+8 ; get seek mode
|
|
jcxz lseek4 ; seek from beginning
|
|
dec cx
|
|
jz lseek2 ; seek from current position
|
|
dec cx
|
|
jz lseek3 ; seek from end
|
|
mov ax,ED_DATA ; else invalid seek mode
|
|
jmp fdos_error ; return error code
|
|
lseek1:
|
|
ret ; return error code
|
|
|
|
lseek2: ; seek mode 1: relative to position
|
|
add ax,es:DHNDL_POSLO[bx]
|
|
adc dx,es:DHNDL_POSHI[bx]
|
|
jmps lseek4 ; update new position
|
|
|
|
lseek3: ; seek mode 2: relative to end
|
|
add ax,es:DHNDL_SIZELO[bx]
|
|
adc dx,es:DHNDL_SIZEHI[bx] ; add file size + offset
|
|
lseek4: ; seek mode 0: set absolute position
|
|
mov es:DHNDL_POSLO[bx],ax
|
|
mov es:DHNDL_POSHI[bx],dx
|
|
mov fdos_pb+4,ax ; set return values
|
|
mov fdos_pb+6,dx
|
|
ret
|
|
|
|
lseek_dev: ; jump here if character device
|
|
mov si,2[bp] ; SI -> parameter block
|
|
xor bx,bx
|
|
mov 4[si],bx ; always return result of 0000h
|
|
mov 6[si],bx
|
|
ret
|
|
|
|
eject
|
|
; GET/SET FILE ATTRIBUTES (CHMOD)
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 43 | name | flag | attrib |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | size |
|
|
; +----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; name: pointer to ASCIIZ file name
|
|
; flag: 00 = get attrib/size
|
|
; 01 = set attrib
|
|
; attrib: file attribute if flag=1
|
|
;
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
; attrib: file attribute if flag=0
|
|
; size: file size if flag=0
|
|
;
|
|
if PASSWORD
|
|
; entry:
|
|
; ------
|
|
; name: pointer to ASCIIZ file name
|
|
; flag: 02 = get password mode
|
|
; 03 = set pw mode/password
|
|
; 04 = get encrypted password
|
|
; 05 = set encrypted password
|
|
; attrib: password mode if flag = 3,5
|
|
; dma: ascii password if flag = 3
|
|
; encrypted password if flag = 5
|
|
;
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
; attrib: file's attribute if flag = 0
|
|
; password mode if flag = 2
|
|
; encrypted password if flag = 4
|
|
endif
|
|
if UNDELETE
|
|
; entry:
|
|
; ------
|
|
; name: pointer to ASCIIZ file name
|
|
; flag: 80 = undelete file
|
|
; 81 = purge file
|
|
; dma: result of sucessful search
|
|
;
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
;
|
|
endif
|
|
|
|
fdos_chmod:
|
|
;----------
|
|
if UNDELETE
|
|
mov si,2[bp] ; SI -> parameter block
|
|
mov al,6[si] ; AX = flag
|
|
sub al,80h ; is it undelete or purge
|
|
jb fdos_chmod_path ; if so set
|
|
cmp al,1
|
|
ja fdos_chmod_path
|
|
call local_disk ; get MXdisk, switch stack
|
|
call select_from_DTA ; prepare for the search
|
|
mov chdblk,0 ; don't assume sequential access
|
|
dec dcnt ; retract so we find the same entry
|
|
call find_pending_delete ; did we find it ?
|
|
jz chmod_notfound ; No, then skip
|
|
call hdsblk ; AX = directory root cluster
|
|
xchg ax,dx ; DX = dir cluster
|
|
mov cx,dcnt ; CX = directory index for entry
|
|
mov ax,fdos_pb+6 ; get operation type
|
|
mov ah,DELW_UNDEL ; assume we are about to undelete
|
|
cmp al,80h ; is it undelete ?
|
|
je fdos_undelete_purge
|
|
mov ah,DELW_PURGE ; no, it must be purge entry
|
|
fdos_undelete_purge:
|
|
mov al,physical_drv ; give delwatch a chance to do it
|
|
callf ss:fdos_stub
|
|
jc fdos_ED_FUNCTION ; return error if DELWATCH not there
|
|
mov fdos_ret,ax ; else return result
|
|
ret
|
|
fdos_chmod_path:
|
|
endif
|
|
call redir_asciiz_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
|
|
call path_prep ; parse the path, go to bottom level
|
|
call chk_no_wild ; can't have wildcards
|
|
call finddfcbf ; find first matching FCB
|
|
jnz chmod10 ; if we can't find a file/dir
|
|
call chk_for_root ; check if we are in the root
|
|
jnz chmod_notfound ; if so return directory attribute
|
|
mov cx,fdos_pb+6 ; but only for get attributes
|
|
jcxz chmod_root ; other fall through to not found
|
|
chmod_notfound:
|
|
jmp fdos_ED_FILE
|
|
chmod_root:
|
|
mov fdos_pb+8,DA_DIR ; return directory attribute
|
|
ret
|
|
|
|
fdos_ED_FUNCTION:
|
|
mov ax,ED_FUNCTION ; invalid subfunction
|
|
jmp fdos_error
|
|
|
|
chmod10:
|
|
mov bx,dirp ; BX -> matching directory entry
|
|
mov ax,DSIZE[bx]
|
|
mov fdos_pb+10,ax
|
|
mov ax,DSIZE+WORD[bx]
|
|
mov fdos_pb+12,ax
|
|
xor ax,ax
|
|
mov al,DATTS[bx] ; get directory attributes
|
|
mov cx,fdos_pb+6 ; get function #
|
|
jcxz chmod30 ; always allow get attribs
|
|
if PASSWORD
|
|
cmp cl,5 ; validate sub-function number
|
|
ja chmod15
|
|
mov ax,DPWM[bx] ; assume return password mode
|
|
and ax,PWM_ANY ; isolate password mode bits
|
|
cmp cl,2 ; is it get mode ?
|
|
je chmod30 ; yes, just return it
|
|
push ax
|
|
push cx
|
|
call check_pwd_any ; check the password
|
|
pop cx
|
|
pop ax
|
|
mov dx,fdos_pb+8 ; DX = new attributes
|
|
cmp cl,2 ; is it get mode ?
|
|
ja chmod20 ; how about other password functions?
|
|
else
|
|
cmp cl,1 ; validate sub-function number
|
|
ja chmod15
|
|
mov dx,fdos_pb+8 ; DX = new attributes
|
|
endif
|
|
; Set new file attrib
|
|
; BX = DIRP
|
|
; DX = attrib
|
|
;
|
|
test dl,DA_DIR ; directory bit not allowed
|
|
jnz chmod12
|
|
xor dl,DATTS[bx] ; check which bits are changed
|
|
and dl,not DA_DIR ; don't check or flip directory bit
|
|
test dl,DA_FIXED ; trying to change the unchangeable?
|
|
jz chmod13
|
|
chmod12:
|
|
jmp fdos_ED_ACCESS ; return "access denied"
|
|
chmod13:
|
|
xor DATTS[bx],dl ; set new attributes
|
|
if PASSWORD
|
|
jmps chmod90
|
|
else
|
|
jmp update_dir ; update directory
|
|
endif
|
|
|
|
chmod15:
|
|
mov ah,PASSWD_CHMOD ; call out to SECURITY TSR
|
|
callf ss:fdos_stub
|
|
jc fdos_ED_FUNCTION
|
|
jmps chmod90
|
|
|
|
if PASSWORD
|
|
chmod20:
|
|
; Password support for functions 3-5
|
|
; BX = DIRP
|
|
; CX = flag
|
|
; DX = attrib
|
|
;
|
|
push bx
|
|
push cx
|
|
push dx
|
|
mov di,S_DENY_IF_OPEN ; check if file already open
|
|
call check_with_share ; and stop if it is
|
|
pop dx
|
|
pop cx
|
|
pop bx
|
|
mov ax,DPWD[bx] ; assume get encrypted password
|
|
cmp cl,4 ; was it ?
|
|
jne chmod50
|
|
endif ; yes, return encrypted password
|
|
chmod30:
|
|
mov fdos_pb+8,ax
|
|
ret
|
|
|
|
|
|
if PASSWORD
|
|
; Password support for functions 3/5
|
|
; BX = DIRP
|
|
; CX = flag
|
|
; DX = attrib
|
|
;
|
|
chmod50: ; set password/password mode
|
|
test dh,80h ; assign new password?
|
|
jz chmod70 ; skip if mode change only
|
|
mov di,offset save_area ; ES:DI -> local structure
|
|
push ds
|
|
call lds_si_dmaptr ; DS:SI -> users DMA address
|
|
lodsw ; AX = possible encrypted password
|
|
cmp cl,5 ; was it set encrypted password ?
|
|
je chmod60
|
|
dec si ; no, the DMA buffer contains
|
|
dec si ; an 8 character password so we
|
|
call hash_pwd ; compute password hash code
|
|
chmod60:
|
|
pop ds
|
|
or DATTS[bx],DA_HIDDEN ; file will be hidden
|
|
mov DPWD[bx],ax ; set new file password
|
|
test ax,ax ; null password?
|
|
jnz chmod70
|
|
xor dx,dx ; can't be protected....
|
|
chmod70:
|
|
mov ax,not PWM_ANY ; clear existing file
|
|
and DPWM[bx],ax ; password mode bits
|
|
not ax ; isolate password mode bits
|
|
and dx,ax ; in new password mode
|
|
jz chmod80 ; if no protection, leave them off
|
|
test DATTS[bx],DA_DIR ; directories protected in all modes
|
|
jz chmod80 ; skip if not a directory
|
|
xchg ax,dx ; force all modes on
|
|
chmod80:
|
|
or DPWM[bx],dx ; set password mode bits
|
|
test dx,dx ; test if protection enabled
|
|
jnz chmod90 ; skip if any protection still active
|
|
mov DPWD[bx],dx ; remove the password and hidden bit
|
|
and DATTS[bx],not DA_HIDDEN ; as file is no longer protected
|
|
endif
|
|
chmod90:
|
|
jmp update_dir ; now update the directory
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eject
|
|
; DUPLICATE FILE HANDLE (DUP)
|
|
|
|
; +----+----+----+----+----+----+
|
|
; | 45 | handle | newhnd |
|
|
; +----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; handle: open file handle
|
|
|
|
; exit:
|
|
; -----
|
|
; newhnd: new file handle
|
|
; AX: duplicate file handle or error code ( < 0)
|
|
|
|
fdos_dup:
|
|
;--------
|
|
call find_xfn ; find new external file #
|
|
mov si,2[bp] ; SI -> parameter block
|
|
mov ds:4[si],di ; save new handle #
|
|
jnc fdos_fdup ; share the code with DUP2
|
|
mov bx,ED_HANDLE ; can't find a handle
|
|
ret
|
|
eject
|
|
; FORCE DUPLICATE FILE HANDLE (DUP2)
|
|
|
|
; +----+----+----+----+----+----+
|
|
; | 46 | handle | newhnd |
|
|
; +----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; handle: open file handle
|
|
; newhnd: new file handle
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: duplicate file handle or error code ( < 0)
|
|
|
|
fdos_fdup:
|
|
;---------
|
|
call vfy_dhndl_ptr ; check file handle #
|
|
call local_disk ; get critical section locks
|
|
call get_xftptr ; ES:DI -> XFN table
|
|
jc dup_err ; we need one..
|
|
mov bx,fdos_pb+4 ; get user file number (0-19)
|
|
cmp bx,cx ; is it sensible ?
|
|
jae dup_err
|
|
cmp es:byte ptr [di+bx],0FFh
|
|
jne dup_err ; handle should be closed by PCMODE..
|
|
call check_handle ; check if legal file handle
|
|
inc es:DHNDL_COUNT[bx] ; another user
|
|
call dup_dev ; inform device driver it's happened
|
|
call get_xftptr ; ES:DI -> XFT's
|
|
mov bx,fdos_pb+2 ; get XFN to dup from
|
|
mov al,es:[di+bx] ; get it's IFN
|
|
mov bx,fdos_pb+4 ; BX = XFN to dup to
|
|
mov es:[di+bx],al ; it gets same IFN
|
|
mov fdos_ret,bx ; return XFN to caller
|
|
ret
|
|
|
|
dup_err: ; complain someone stole my handle
|
|
jmp fdos_ED_H_MATCH
|
|
|
|
eject
|
|
; GET CURRENT DIRECTORY
|
|
;
|
|
; +----+----+----+----+----+----+----+----+
|
|
; | 47 | drive | path |
|
|
; +----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; drive: drive to get path for
|
|
; path: address of 64 byte path buffer to be
|
|
; filled in with current path
|
|
|
|
; exit:
|
|
; -----
|
|
; BX: 0100 or error code
|
|
|
|
fdos_curdir:
|
|
;-----------
|
|
call local_disk ; it's a disk function
|
|
call get_pb2_drive ; get specified drive in AL
|
|
call get_ldt ; ES:BX -> LDT_ for drive
|
|
jc fdos_curdir30
|
|
mov dx,es:LDT_FLAGS[bx]
|
|
test dx,LFLG_NETWRKD
|
|
jnz fdos_curdir10
|
|
test dx,LFLG_PHYSICAL
|
|
jz fdos_curdir30
|
|
call select_unique ; select the drive for media changes
|
|
les bx,ss:current_ldt ; ES:BX -> LDT_ for this drive
|
|
cmp es:LDT_BLK[bx],0FFFFh ; is LDT valid
|
|
jne fdos_curdir10
|
|
call rebuild_ldt_curdir ; no, better rebuild it
|
|
fdos_curdir10:
|
|
push ds
|
|
push es ! push bx ; save LDT
|
|
les di,dword ptr fdos_pb+4 ; ES:DI -> destination buffer
|
|
pop si ! pop ds ; DS:SI -> LDT
|
|
add si,ds:LDT_ROOTLEN[si] ; skip the '\\server\dir'
|
|
lodsb ; eat the slash
|
|
call check_slash ; if it is one..
|
|
je fdos_curdir20
|
|
dec si ; I didn't mean it!
|
|
fdos_curdir20:
|
|
call copy_asciiz ; copy the string
|
|
pop ds
|
|
mov fdos_ret,100h ; return 100h for success
|
|
ret
|
|
|
|
fdos_curdir30:
|
|
jmp fdos_ED_DRIVE ; naughty - it's a bad drive
|
|
|
|
|
|
eject
|
|
; GET DISK PARAMETER BLOCK
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 48 | drive | dpb | adjust |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; drive: drive to get information about
|
|
; (top bit of word set if free space not required)
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
; dpb: address of DOS DPB (offset/segment)
|
|
; adjust: delwatch adjustment of free space
|
|
|
|
fdos_getdpb:
|
|
;-----------
|
|
call redir_drv_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call get_pb2_drive ; get drive from parameter block
|
|
call logical2physical ; AX = physical drive
|
|
call select_physical_drv
|
|
test fdos_pb+2,8000h ; free space required ?
|
|
jnz fdos_getdpb10
|
|
call update_ddsc_free ; then make sure it is up to date
|
|
if DELWATCH
|
|
xor cx,cx ; assume no adjustment
|
|
mov ah,DELW_SPACE
|
|
mov al,physical_drv ; now we call DELWATCH to
|
|
callf fdos_stub ; add pending deletes
|
|
mov fdos_pb+8,cx ; return free space adjust value
|
|
endif
|
|
fdos_getdpb10:
|
|
les ax,ss:current_ddsc
|
|
mov fdos_pb+4,ax
|
|
mov fdos_pb+6,es
|
|
ret
|
|
|
|
eject
|
|
; FLUSH BUFFERS
|
|
|
|
; +----+----+
|
|
; | 49 |
|
|
; +----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; none
|
|
|
|
; exit:
|
|
; -----
|
|
; none
|
|
|
|
fdos_flush:
|
|
;----------
|
|
call local_disk ; be alone...
|
|
fdos_flush_local:
|
|
; Entry point for people who already have the MX disk
|
|
xor dx,dx ; starting with drive A
|
|
fdos_flush10:
|
|
push dx
|
|
call mark_ldt_unsure ; ask for LDT's to be relogged
|
|
xchg ax,dx ; AL = drive to check
|
|
mov ah,BF_DIRTY
|
|
call buffers_check ; any buffers on this drive?
|
|
jz fdos_flush20 ; skip flush if none
|
|
call select_physical_drv ; select drive in AL
|
|
call update_fat ; flush all FAT buffers
|
|
; call update_dir ; dir always up to date
|
|
call update_dat ; flush all data buffers
|
|
fdos_flush20:
|
|
pop dx
|
|
xchg adrive,dl
|
|
call discard_all ; discard all buffers
|
|
xchg adrive,dl
|
|
inc dx ; onto next drive
|
|
cmp dl,phys_drv
|
|
jb fdos_flush10
|
|
mov al,0FFh ; AL = all drives
|
|
jmp hshdscrd ; forget about our hashing
|
|
; ret
|
|
|
|
|
|
eject
|
|
; SELECT DRIVE
|
|
|
|
; +----+----+----+----+
|
|
; | 4A | drive |
|
|
; +----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; drive: drive (zero based) to select as default drive
|
|
|
|
; exit:
|
|
; -----
|
|
; current_dsk: drive (if legal)
|
|
|
|
fdos_select:
|
|
;-----------
|
|
call local_disk ; cheap way to get MX
|
|
mov ax,fdos_pb+WORD ; AL = drive
|
|
call get_ldt ; ES:BX -> LDT for drive A
|
|
mov dx,ax ; DL = logical drive
|
|
jc fdos_select10 ; no LDT, treat as physical=logical
|
|
mov ax,es:LDT_FLAGS[bx] ; get the LDT_FLAGS
|
|
test ax,LFLG_NETWRKD ; NETWORK drives are OK, but we must
|
|
jnz fdos_select20 ; skip physical selection bit
|
|
test ax,LFLG_PHYSICAL
|
|
jz fdos_select30 ; skip physical selection bit
|
|
if JOIN
|
|
test ax,LFLG_JOINED ; JOINed drives are bad news
|
|
jnz fdos_select30 ; so don't select one
|
|
endif
|
|
mov al,es:LDT_NAME[bx] ; get ASCII drive letter
|
|
call toupper ; it was ascii
|
|
sub al,'A' ; make it zero based
|
|
fdos_select10:
|
|
call get_ddsc ; ES:BX -> DDSC_ for drive
|
|
jc fdos_select30 ; no, don't select
|
|
fdos_select20:
|
|
mov ss:current_dsk,dl ; new logical disk selected
|
|
fdos_select30:
|
|
ret
|
|
|
|
eject
|
|
; EXECUTE CHILD (EXEC)
|
|
|
|
; +----+----+----+----+
|
|
; | 4B | pspseg |
|
|
; +----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; pspseg: segment of child PSP
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000h
|
|
|
|
fdos_exec:
|
|
;---------
|
|
call local_disk ; get MXdisk, switch stack
|
|
mov cx,fdos_pb+2
|
|
jcxz exec30 ; no duplicate handles if no new PSP
|
|
mov di,offset PSP_XFT
|
|
mov es,cx ; get child PSP
|
|
mov cx,XFNMAX ; get # of handles in child PSP
|
|
mov PSP_XFNMAX,cx
|
|
mov PSP_XFTOFF,di
|
|
mov PSP_XFTSEG,es
|
|
mov al,0FFh ; assume all handles closed
|
|
rep stosb
|
|
xor si,si ; start with XFN 0
|
|
exec10:
|
|
call get_xftptr ; ES:DI -> XFN table
|
|
jc exec30 ; stop if none
|
|
add di,si
|
|
mov al,es:[di] ; get IFN of old handle
|
|
call ifn2dhndl ; ES:BX -> DHNDL_
|
|
jc exec20
|
|
mov cx,es:DHNDL_COUNT[bx] ; skip files that aren't open
|
|
jcxz exec20
|
|
test es:byte ptr DHNDL_WATTR+1[bx],DHAT_LOCAL/100h
|
|
jnz exec20 ; don't inherit private files
|
|
test es:byte ptr DHNDL_MODE+1[bx],DHM_FCB/100h
|
|
jnz exec20 ; don't inherit FCB's
|
|
inc cx
|
|
mov es:DHNDL_COUNT[bx],cx ; increment in-use count
|
|
push ax
|
|
call dup_dev ; inform device driver it's happened
|
|
pop ax
|
|
mov es,fdos_pb+2 ; get child PSP
|
|
mov es:PSP_XFT[si],al ; inherit this IFN
|
|
exec20:
|
|
inc si ; next file handle
|
|
cmp si,XFNMAX
|
|
jb exec10 ; inherit all file handles
|
|
exec30:
|
|
ret
|
|
|
|
|
|
eject
|
|
; FIND FIRST FILE
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 4E | name | ***** | attrib |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; name: pointer to ASCIIZ file name
|
|
; attrib: attribute to be used in search
|
|
; nb. API addition - attrib bit 7 set returns starting cluster
|
|
;
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
|
|
; Note: This call returns matching files in
|
|
; the current DMA address and also saves
|
|
; the BDOS state in the there.
|
|
;
|
|
|
|
fdos_first:
|
|
;----------
|
|
call redir_asciiz_dev_offer ; offer it as a network device
|
|
call asciiz_dev_offer ; offer it as a local device
|
|
call redir_asciiz_file_offer ; offer it as a network file
|
|
call local_disk ; get MXdisk, switch stack
|
|
call path_prep ; parse path, walk down the tree
|
|
call check_device ; if it a device
|
|
jnc fdos_first10
|
|
mov ax,fdos_pb+8 ; get search attribute
|
|
mov attributes,al ; set for attribute match
|
|
mov dcnt,0FFFFh ; search from beginning
|
|
jmps search_next ; find next matching file
|
|
|
|
fdos_first10:
|
|
jmp first_dev ; return device name
|
|
|
|
next_deverr: ; NEXT after FIRST on device:
|
|
mov bx,ED_FILE ; "no more files"
|
|
ret
|
|
|
|
eject
|
|
; FIND NEXT FILE
|
|
|
|
; +----+----+
|
|
; | 4F |
|
|
; +----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
;
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
|
|
; Note: This call returns matching files in
|
|
; the current DMA address and also saves
|
|
; the BDOS state in the there.
|
|
;
|
|
|
|
fdos_next:
|
|
;---------
|
|
push ds
|
|
call lds_si_dmaptr ; DS:SI -> users DMA address
|
|
lodsw
|
|
xchg ax,dx ; DH = local drive
|
|
pop ds
|
|
cmp dh,0FFh ; check if FIRST was character device
|
|
je next_deverr ; "no more files" if device
|
|
call redir_snext_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call select_from_DTA ; prepare for the search
|
|
; jmp search_next ; now go and look for next file
|
|
|
|
search_next:
|
|
;-----------
|
|
mov chdblk,0 ; don't assume sequential access
|
|
mov al,attributes ; are we looking for a VOL label?
|
|
test al,DA_DIR+DA_SYSTEM+DA_HIDDEN
|
|
jnz search_n10 ; these bits take precedence
|
|
test al,DA_VOLUME ; searching for label?
|
|
jz search_n10 ; search for directory label only
|
|
if UNDELETE
|
|
cmp al,DA_DELWATCH+DA_VOLUME
|
|
jne search_vol ; searching for pending delete entries?
|
|
call find_pending_delete ; did we find it ?
|
|
jz search_next_err ; No, then skip
|
|
jmps search_n30 ; save search state for user
|
|
search_vol:
|
|
endif
|
|
call find_label ; did we find it ?
|
|
jnz search_n30 ; save search state for user
|
|
search_next_err:
|
|
jmp fdos_ED_ROOM ; else end of directory
|
|
|
|
search_n10:
|
|
call finddfcb ; find next matching entry
|
|
jz search_next_err ; if not found
|
|
mov al,attributes ; get attributes that we support
|
|
if PASSWORD
|
|
cmp word ptr DPWD[bx],0 ; does it have a password?
|
|
jz search_n20 ; skip if not
|
|
or al,DA_HIDDEN ; else include hidden files
|
|
search_n20:
|
|
endif
|
|
not al ; attributes we don't support
|
|
and al,DA_DIR+DA_HIDDEN+DA_SYSTEM+DA_VOLUME
|
|
test DATTS[bx],al ; any attributes we don't support
|
|
jnz search_n10 ; then don't count this one
|
|
search_n30:
|
|
push ss ! pop es
|
|
mov di,offset srch_buf ; ES:DI -> DMA search address
|
|
push di ; save for later
|
|
if JOIN
|
|
mov al,fdos_hds_drv ; save the PHYSICAL drive
|
|
else
|
|
mov al,byte ptr path_drive ; save the specified drive
|
|
endif
|
|
inc al
|
|
stosb
|
|
mov dx,di ; remember start of name field
|
|
mov si,offset info_fcb+1 ; point at search FCB
|
|
mov cx,11
|
|
rep movsb ; save name for search
|
|
mov al,attributes ; get search attribute
|
|
stosb
|
|
|
|
mov ax,dcnt
|
|
stosw ; save directory count
|
|
mov ax,fdos_hds_blk ; get the directory block
|
|
stosw ; save the current block
|
|
|
|
add di,4 ; skip 4 reserved bytes
|
|
|
|
mov si,dirp ; point to directory name
|
|
test attributes,DA_CLUSTER ; is the caller requesting
|
|
jz search_n35 ; the starting cluster ?
|
|
mov ax,DBLOCK1[si] ; pick it up from dir entry
|
|
dec di ; and return at offset 13h
|
|
dec di ; in DTA (WARNING - Lantastic
|
|
stosw ; server uses these bytes too..)
|
|
search_n35:
|
|
mov cx,32/WORD
|
|
rep movsw ; location (also used by FCB search)
|
|
|
|
pop si ; SI = offset srch_buf
|
|
call les_di_dmaptr ; ES:DI -> DMA address
|
|
push ss ! pop ds ; DS:SI -> srch_buf
|
|
mov cx,21
|
|
rep movsb ; copy from buffer to user DMA
|
|
mov bx,dirp ; BX -> matching entry
|
|
mov al,DATTS[bx]
|
|
stosb ; return directory attribute
|
|
lea si,DTIME[bx]
|
|
movsw ! movsw ; return time, date
|
|
inc si
|
|
inc si ; skip starting cluster
|
|
movsw ! movsw ; return file size
|
|
jmp unparse ; return file name
|
|
|
|
eject
|
|
; COMMIT FILE (COMMIT)
|
|
|
|
; +----+----+----+----+
|
|
; | 50 | handle |
|
|
; +----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; handle: open file handle to be flushed
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
|
|
file_updt20: ; NO-OP for clean files
|
|
commit_dev: ; NO-OP on character devices
|
|
ret
|
|
|
|
fdos_commit:
|
|
;-----------
|
|
call vfy_dhndl_ptr ; check file handle
|
|
call redir_dhndl_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call verify_handle ; check if legal file handle
|
|
jc commit_dev
|
|
; jmp file_update ; update directory & FAT if written
|
|
|
|
; Update directory & File Allocation Table (partial close)
|
|
; entry: ES:BX -> DHNDL_
|
|
; exit: ES:BX preserved
|
|
|
|
Public file_update
|
|
|
|
file_update:
|
|
;-----------
|
|
test es:DHNDL_ATTR[bx],DHAT_CLEAN
|
|
jnz file_updt20 ; skip if file is clean
|
|
mov al,es:DHNDL_DCNTLO[bx] ; get directory count
|
|
mov ah,es:DHNDL_DCNTHI[bx]
|
|
dec ax
|
|
mov dcnt,ax ; set search position
|
|
xor cx,cx ; find any entry
|
|
mov chdblk,cx ; non-sequential access
|
|
push es ! push bx
|
|
call getdir ; read the directory entry
|
|
pop bx ! pop es
|
|
test ax,ax ; did we find something?
|
|
jz file_updt10 ; skip if directory entry lost
|
|
xchg ax,di ; DI -> directory entry in buffer
|
|
or DATTS[di],DA_ARCHIVE ; mark file as modified
|
|
;;; call timestamp_dhndl ; record the current time
|
|
mov ax,es:DHNDL_BLK1[bx] ; AX = 1st block in file
|
|
lea di,DTIME[di] ; DI -> [time,date,block1,size]
|
|
push es ! push bx
|
|
push ds
|
|
lea si,DHNDL_TIME[bx]
|
|
push ds ! push es
|
|
pop ds ! pop es ; swap ES and DS
|
|
movsw ! movsw ; copy time then date
|
|
stosw ; now 1st cluster
|
|
movsw ! movsw ; finally file size
|
|
pop ds
|
|
call update_dir ; update directory
|
|
call update_fat ; flush dirty FAT
|
|
call update_dat ; flush all dirty data buffers
|
|
pop bx ! pop es
|
|
or es:DHNDL_ATTR[bx],DHAT_CLEAN
|
|
ret ; only mark as clean AFTER it's written
|
|
|
|
file_updt10:
|
|
mov ax,ED_H_MATCH ; can't find open file
|
|
mov fdos_ret,ax ; save error code
|
|
ret
|
|
|
|
|
|
|
|
eject
|
|
; CREATE NEW FILE
|
|
|
|
; +----+----+----+----+----+----+----+----+
|
|
; | 51 | name | mode |
|
|
; +----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; name: segmented address of ASCIIZ name
|
|
; mode: attribute for file
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: file handle or error code ( < 0)
|
|
|
|
; Note: The function is identical to CREATE FILE
|
|
; with the exception that an error is returned
|
|
; if the specified file already exists.
|
|
|
|
fdos_mknew:
|
|
;----------
|
|
call redir_asciiz_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call mustbe_free_handle ; make sure we have spare handle
|
|
call path_prep_chk ; parse path, walk down the tree
|
|
call finddfcbf ; check if we can find this one
|
|
jnz mknew10
|
|
test byte ptr fdos_pb+8,DA_VOLUME
|
|
jz mknew_nolbl ; skip if not volume label
|
|
call mustbe_nolbl ; do we have an existing label ?
|
|
mknew_nolbl:
|
|
jmp creat_new_file ; go ahead and create the file
|
|
mknew10:
|
|
mov ax,ED_EXISTS
|
|
jmp fdos_error
|
|
|
|
eject
|
|
; LOCK/UNLOCK FILE DATA (LOCK/UNLOCK)
|
|
|
|
; +----+----+----+----+----+----+----+----+
|
|
; | 52 | handle | offset |
|
|
; +----+----+----+----+----+----+----+----+
|
|
; | length | lock |
|
|
; +----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; handle: open file handle
|
|
; offset: long integer offset
|
|
; length: long integer byte count
|
|
; lock: 0 = lock, 1 = unlock
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: byte count or error code ( < 0)
|
|
|
|
fdos_lock:
|
|
;---------
|
|
call vfy_dhndl_ptr ; check file handle #
|
|
test es:DHNDL_WATTR[bx],DHAT_DEV
|
|
jnz lock_dev ; skip if character device
|
|
call redir_dhndl_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call check_handle ; check if legal file handle
|
|
mov ax,ED_ACCESS ; assume a problem
|
|
jc lock_error ; can't lock/unlock device handles
|
|
mov di,offset fdos_pb
|
|
callf ss:share_stub+S_LOCKS ; call the stub routine
|
|
jnc lock_ret ; return error if we got one
|
|
lock_error:
|
|
jmp fdos_error ; can't do locking
|
|
|
|
lock_dev:
|
|
mov bx,ED_ACCESS
|
|
lock_ret:
|
|
ret
|
|
|
|
eject
|
|
; BUILD DDSC FROM BPB
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 53 | bpbptr ! ddscptr |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; bpbptr: address of BPB
|
|
; ddscptr: address of DDSC to be built
|
|
|
|
; exit:
|
|
; -----
|
|
; 0000 or error code ( < 0)
|
|
|
|
fdos_mkddsc:
|
|
;-----------
|
|
push ds
|
|
mov si,2[bp] ; SI -> parameter block
|
|
les di,6[si] ; ES:DI -> DDSC
|
|
lds si,2[si] ; DS:SI -> BPB
|
|
|
|
call bpb2ddsc ; convert BPB to DDSC
|
|
xor bx,bx ; no error
|
|
pop ds ; restore segment register
|
|
ret
|
|
|
|
|
|
bpb2ddsc:
|
|
;--------
|
|
; build a DDSC from a BPB, leavinf UNIT, RUNIT, DEVICE, FIRST, and LINK fields
|
|
; unchanged
|
|
; On Entry:
|
|
; DS:SI -> source BPB
|
|
; ES:DI -> destination DDSC
|
|
; On Exit:
|
|
; None
|
|
lodsw ; get sector size in bytes
|
|
mov es:DDSC_SECSIZE[di],ax
|
|
|
|
lodsb ; get sectors/allocation unit
|
|
dec ax ; get cluster mask
|
|
mov es:DDSC_CLMSK[di],al ; store cluster mask
|
|
lodsw ; get FAT address
|
|
mov es:DDSC_FATADDR[di],ax
|
|
|
|
lodsb ; get # of fats
|
|
mov es:DDSC_NFATS[di],al
|
|
cbw ; make it a word value
|
|
xchg ax,dx ; and keep in DX
|
|
|
|
lodsw ; get # of directory entries
|
|
mov es:DDSC_DIRENT[di],ax
|
|
|
|
lodsw ; get # of sectors total in image
|
|
push ax ; save disk size for later
|
|
|
|
lodsb ; get FAT id byte
|
|
mov es:DDSC_MEDIA[di],al ; set media byte
|
|
|
|
lodsw ; get # of sectors in a fat
|
|
if DOS5
|
|
mov es:DDSC_NFATRECS[di],ax ; set FAT size
|
|
else
|
|
mov es:DDSC_NFATRECS[di],al ; set FAT size
|
|
endif
|
|
|
|
mul dx ; AX = FAT size
|
|
add ax,es:DDSC_FATADDR[di] ; AX = 1st directory sector
|
|
mov es:DDSC_DIRADDR[di],ax ; set root directory address
|
|
|
|
add si,word+word+dword ; skip SPT, NHEADS, BIGHIDDEN
|
|
|
|
mov cx,es:DDSC_SECSIZE[di]
|
|
mov ax,32 ; size of single directory entry
|
|
mul es:DDSC_DIRENT[di] ; AX/DX = root directory bytes
|
|
add ax,cx ; round up sector size
|
|
dec ax ; in case of odd number
|
|
div cx ; convert to whole sectors
|
|
add ax,es:DDSC_DIRADDR[di] ; add in base of root directory
|
|
mov es:DDSC_DATADDR[di],ax ; set this as first cluster address
|
|
|
|
pop ax ; recover total disk size
|
|
xor dx,dx ; assume 16 bit number
|
|
test ax,ax ; test for big drive
|
|
jnz bpb2ddsc10 ; skip if not large drive
|
|
lodsw ; get low word of size
|
|
mov dx,[si] ; get high word of size
|
|
bpb2ddsc10: ; AX/DX = disk size in sectors
|
|
sub ax,es:DDSC_DATADDR[di] ; subtract non-data portion
|
|
sbb dx,0
|
|
|
|
xor cx,cx ; CL = cluster shift
|
|
mov ch,es:DDSC_CLMSK[di] ; CH = cluster mask
|
|
bpb2ddsc20: ; count # of 1 bits
|
|
shr ch,1 ; shift right mask
|
|
jnc bpb2ddsc30 ; skip if all 1 bits shifted out
|
|
inc cx ; count another 1 bit
|
|
shr dx,1
|
|
rcr ax,1 ; div by two
|
|
jmps bpb2ddsc20 ; repeat until all 1's counted
|
|
bpb2ddsc30: ; CL = log2 (CH)
|
|
mov es:DDSC_CLSHF[di],cl ; set cluster shift
|
|
inc ax ; clusters 0/1 reserved (+2), and we
|
|
mov es:DDSC_NCLSTRS[di],ax ; want max (-1), so +1
|
|
xor ax,ax
|
|
mov es:DDSC_BLOCK[di],ax ; next block = 0
|
|
dec ax
|
|
mov es:DDSC_FREE[di],ax ; free space = -1 (unknown)
|
|
ret
|
|
|
|
eject
|
|
; DIRECT DISK IO
|
|
|
|
; +----+----+----+----+----+----+
|
|
; | 54 |drv | op |nsectors |
|
|
; +----+----+----+----+----+----+----+----+
|
|
; | startsec | dma address |
|
|
; +----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; param block set up for direct disk IO
|
|
|
|
; exit:
|
|
; -----
|
|
; AX = 0, or error code
|
|
;
|
|
|
|
DIO_25_READ_OP equ 1
|
|
DIO_26_WRITE_OP equ 2
|
|
|
|
fdos_ddio:
|
|
;---------
|
|
; Used by emulator for Int 25H/26H disk calls to BIOS
|
|
;
|
|
; entry: dio = offset of parameter block in user data segment
|
|
;
|
|
; exit: AX = return code from BIOS
|
|
|
|
call local_disk ; get the MX
|
|
mov ax,fdos_pb+WORD ; AX = logical drive
|
|
call logical2physical ; AX = physical drive
|
|
call get_ddsc ; make sure this drive is valid
|
|
jc fdos_ddio30 ; bail if not
|
|
mov adrive,al ; remember this unit
|
|
mov ax,CMD_INPUT+0*256 ; disk read operation of system area
|
|
cmp byte ptr fdos_pb+3,DIO_26_WRITE_OP
|
|
jne fdos_ddio10
|
|
mov ax,0FFFFh
|
|
mov es:DDSC_FREE[bx],ax ; free space = -1 (unknown)
|
|
call hshdscrd ; discard hashing info for all drives (AL=FF)
|
|
mov ax,CMD_OUTPUT+1*256 ; disk write operation of system area
|
|
fdos_ddio10:
|
|
mov rwmode,ah ; save read/write of system area
|
|
mov req_hdr+5,ah ; (so driver can get the hint)
|
|
mov bx,offset req_hdr ; we will build request here
|
|
mov RH_CMD[bx],al ; save the command
|
|
mov ax,fdos_pb+4 ; AX = # sectors
|
|
mov RH4_COUNT[bx],ax ; set requested sector count
|
|
les ax,dword ptr fdos_pb+6 ; pick up 32-bit record address
|
|
mov word ptr pblock,ax
|
|
mov word ptr pblock+WORD,es
|
|
les ax,dword ptr fdos_pb+10 ; ES:AX -> DMA seg
|
|
mov RH4_BUFOFF[bx],ax
|
|
mov RH4_BUFSEG[bx],es
|
|
call ddioif ; go do it
|
|
jns fdos_ddio20 ; did we have a problem ?
|
|
cbw ; AX = base error
|
|
add ax,ED_PROTECT ; "add" in BIOS error code
|
|
mov fdos_ret,ax ; save return code
|
|
fdos_ddio20:
|
|
ret
|
|
|
|
fdos_ddio30:
|
|
jmp fdos_ED_DRIVE ; return bad drive error
|
|
|
|
eject
|
|
; EXPAND FILE NAME
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|
|
; | 55 | relative name | | absolute name |
|
|
; +----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; relative name: segmented address of ASCIIZ name
|
|
; absolute name: segmented address of ASCIIZ name
|
|
|
|
; exit:
|
|
; -----
|
|
; AX = BX: 0000 or error code ( < 0)
|
|
|
|
fdos_expand:
|
|
;-----------
|
|
call redir_asciiz_dev_offer ; is it a networked device ?
|
|
call asciiz_dev_offer ; see it we are expanding a device name
|
|
call local_disk ; get MXdisk, switch stack
|
|
push ds
|
|
les si,dword ptr fdos_pb+2 ; get relative name buffer
|
|
mov ax,es:[si] ; get 1st two chars of path
|
|
call check_dslash ; is it a '\\' form ?
|
|
je fdos_expand10
|
|
mov bx,ED_FILE
|
|
test al,al
|
|
jz fdos_expand60
|
|
call toupper ; work on premise we have drive
|
|
sub al,'A' ; specified in relative name
|
|
sub ah,':' ; did we have ?
|
|
je fdos_expand20 ; if so use it
|
|
fdos_expand10:
|
|
mov al,current_dsk ; use current drive
|
|
fdos_expand20:
|
|
cmp al,last_drv ; is it a legal drive
|
|
jae fdos_expand50
|
|
|
|
; we should check for a media change so select_logical_drv seems to be
|
|
; a better solution
|
|
; call get_ldt ; ES:BX -> LDT_ for drive
|
|
; jc fdos_expand40 ; no LDT at init - copy relative path
|
|
; mov word ptr current_ldt,bx
|
|
; mov word ptr current_ldt+WORD,es
|
|
call select_logical_drv
|
|
les bx,current_ldt
|
|
|
|
test es:LDT_FLAGS[bx],LFLG_PHYSICAL
|
|
jz fdos_expand50 ; make sure it's a valid drive
|
|
push ss ! pop es
|
|
mov di,offset pri_pathname ; build name in pathname buffer
|
|
lds si,dword ptr fdos_pb+2
|
|
|
|
; we must trick redir_build_path to find the correct function number -
|
|
; otherwise we could not get correct (critical) error reporting to work
|
|
push bp
|
|
mov bp,offset fdos_pb+6
|
|
mov 2[bp],bp
|
|
mov byte ptr [bp],FD_EXPAND
|
|
|
|
call redir_build_path ; build name from LDT
|
|
pop bp
|
|
jc fdos_expand60 ; bail out if bad name
|
|
lds di,ss:current_ldt ; we need to append a '\' if
|
|
mov bx,ds:LDT_ROOTLEN[di] ; we are at the root
|
|
test ds:LDT_FLAGS[di],LFLG_SUBST
|
|
jz fdos_expand30 ; if drive is SUBST'd then
|
|
mov bx,2 ; only append if real root
|
|
fdos_expand30:
|
|
mov si,offset pri_pathname ; SS:SI -> full path
|
|
push ss ! pop ds ; leave current ldt -> buffer as LAN
|
|
mov word ptr current_ldt,si ; MAN 2.1 extended edition expects it
|
|
mov word ptr current_ldt+WORD,ds
|
|
mov ax,'\' ; get pathchar in AX for checks
|
|
cmp ds:byte ptr [si+bx],ah ; are we at the root ?
|
|
jne fdos_expand40 ; if so append a '\'
|
|
mov ds:word ptr [si+bx],ax
|
|
fdos_expand40:
|
|
les di,ss:dword ptr fdos_pb+10; ES:DI -> destination buffer
|
|
call copy_asciiz ; copy the full pathname
|
|
pop ds
|
|
ret
|
|
|
|
fdos_expand50:
|
|
mov bx,ED_DRIVE ; return bad drive error
|
|
fdos_expand60:
|
|
mov ax,bx
|
|
pop ds
|
|
jmp fdos_error
|
|
|
|
expand_dev:
|
|
;----------
|
|
les si,dword ptr fdos_pb+2 ; this is the original source
|
|
lods es:ax ; get the source
|
|
les di,dword ptr fdos_pb+10 ; data will end up here
|
|
cmp ah,':' ; is a drive specified ?
|
|
je expand_dev10
|
|
mov al,'A'
|
|
add al,ss:current_dsk
|
|
expand_dev10:
|
|
call toupper ; make sure drive letter is upper case
|
|
stosb ; plant an 'd'
|
|
mov ax,':'+256*'/'
|
|
stosw ; make that 'd:/'
|
|
mov bx,offset name_buf ; DS:BX -> name buffer
|
|
jmp unparse ; unparse the device name
|
|
|
|
eject
|
|
; RENAME FILE
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|
|
; | 56 | old name | | new name |
|
|
; +----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; old name: segmented address of ASCIIZ name
|
|
; new name: segmented address of ASCIIZ name
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
|
|
; Note: R/O files can be renamed.
|
|
; ---
|
|
|
|
fdos_move:
|
|
;---------
|
|
call redir_move_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call path_prep ; parse the path, go to bottom level
|
|
call chk_no_dev
|
|
cmp ss:remote_call,0 ; wildcards allowed if server/FCB
|
|
jnz move10
|
|
call chk_no_wild ; make sure not a wild card
|
|
move10:
|
|
call finddfcbf ; else try to locate directory entry
|
|
jz fdos_ED_FILE ; error if no match occurred
|
|
move20:
|
|
call close_if_same_psp ; make sure not open by other PDs
|
|
if PASSWORD
|
|
call check_pwd_any ; check if password protected
|
|
endif
|
|
mov si,dirp ; get matching directory entry
|
|
mov di,offset save_area ; get a temporary save area
|
|
mov cx,32/WORD ; save it all
|
|
rep movsw ; so we can move it to new entry
|
|
|
|
mov si,offset fdos_hds
|
|
mov di,offset saved_hds
|
|
mov cl,HDS_LEN
|
|
rep movsb ; copy current HDS to safe place
|
|
|
|
mov ax,dcnt ; also save directory index
|
|
mov saved_dcnt,ax ; so we can release entry later
|
|
|
|
les di,dword ptr fdos_pb+10 ; es:di -> path name
|
|
call path_prep_ptr ; find the destination path
|
|
call chk_no_dev ; no devices allowed here
|
|
cmp ss:remote_call,0 ; wildcards allowed if server/FCB
|
|
jnz move25
|
|
call chk_no_wild ; make sure not a wild card
|
|
move25:
|
|
mov al,fdos_hds_drv ; get the work drive
|
|
cmp al,saved_hds_drv ; make sure same drive for
|
|
je move30 ; source and destination
|
|
|
|
mov ax,ED_DEVICE ; "not same device" error
|
|
jmps fdos_moverr ; return the error
|
|
fdos_ED_FILE:
|
|
mov ax,ED_FILE ; get error code
|
|
fdos_moverr:
|
|
jmp fdos_error ; return the error
|
|
|
|
move30: ; same drive for source & destination
|
|
mov al,byte ptr fdos_pb+8 ; check attributes
|
|
if PASSWORD
|
|
cmp save_area+DPWD,0
|
|
je move31
|
|
or al,DA_HIDDEN
|
|
move31:
|
|
endif
|
|
not al
|
|
and al,DA_HIDDEN+DA_SYSTEM+DA_DIR+DA_VOLUME
|
|
test save_area+DATTS,al
|
|
jnz move80
|
|
mov si,offset info_fcb+1 ; SI->new name (possible wildcards)
|
|
mov di,si ; DI->new name
|
|
mov bx,offset save_area ; current name on disk
|
|
cmp byte ptr DNAME[bx],'.'
|
|
je move80
|
|
mov cx,11
|
|
move40: ; fill in the wild card characters
|
|
lodsb ; get next character from new name
|
|
if KANJI
|
|
call dbcs_lead ; is it the 1st of a kanji pair
|
|
jne move45
|
|
dec cx ; copied this one
|
|
jcxz move55 ; discard if no room for kanji char
|
|
stosb ; copy 1st byte of Kanji pair
|
|
inc bx
|
|
lodsb ; copy 2nd byte
|
|
jmps move50
|
|
move45:
|
|
endif
|
|
call toupper ; make it upper case
|
|
cmp al,'?' ; is it just a wild card?
|
|
jne move50 ; no, put in destination as is
|
|
mov al,[bx] ; else leave original character
|
|
move50: ; AL = next char for destination
|
|
stosb ; store next char in destination
|
|
inc bx ; increment all pointers
|
|
loop move40 ; repeat for all 11 characters
|
|
move55:
|
|
|
|
call finddfcbf ; find first non-volume entry
|
|
jnz move_access ; file already exists, return error
|
|
|
|
call move_comp ; source & destination in same dir?
|
|
jne move60 ; skip if moving to different directory
|
|
call move_seek ; else find old entry again
|
|
jmps move70 ; replace name in old entry
|
|
|
|
move60: ; moving to different directory
|
|
test save_area+DATTS,DA_DIR ; can't move directory to new path
|
|
jnz move_access ; so return error if new path
|
|
call allocdir ; allocate a directory entry
|
|
move70:
|
|
mov si,offset info_fcb+1 ; get pointer to new name
|
|
mov di,dirp ; get pointer to directory entry
|
|
mov cx,11 ; copy new name into buffer
|
|
rep movsb
|
|
|
|
|
|
mov si,offset save_area+11 ; copy remaining info
|
|
mov cx,32-11
|
|
rep movsb
|
|
call update_dir ; update the disk
|
|
|
|
call move_comp ; did we rename across directories?
|
|
je move80 ; skip if in same directory
|
|
mov si,offset saved_hds
|
|
mov di,offset fdos_hds ; else need to copy HDS back
|
|
mov cx,HDS_LEN ; so we can get old entry again
|
|
rep movsb ; (time to get rid of it)
|
|
call move_seek ; seek the original entry
|
|
mov bx,dirp
|
|
mov DNAME[bx],0E5h ; and bye, bye! it goes...
|
|
call update_dir ; update the old directory entry
|
|
move80:
|
|
cmp ss:remote_call,0 ; wildcards allowed if server/FCB
|
|
jz move90 ; so check for multiple files
|
|
call path_prep ; parse the path, go to bottom level
|
|
|
|
mov ax,saved_dcnt ; restore dcnt for remote call
|
|
mov dcnt,ax ; to function properly
|
|
and chdblk,0
|
|
|
|
call finddfcb ; try to locate another directory entry
|
|
jz move90 ; no, return now
|
|
jmp move20 ; round again if we do
|
|
|
|
move_access:
|
|
jmp fdos_ED_ACCESS ; "access denied" if file
|
|
; already exists
|
|
move90:
|
|
; in order to update the current directory in case part of it has been
|
|
; renamed we call fdos_chdir which does all the work for rebuilding
|
|
; the LDT_ for the given drive
|
|
push ss
|
|
pop ds
|
|
mov bx,offset sec_pathname
|
|
mov ax,path_drive
|
|
add ax,'A'+(':'*100h)
|
|
mov [bx],ax
|
|
mov word ptr 2[bx],'.'
|
|
mov fdos_pb+4,ds
|
|
mov fdos_pb+2,bx
|
|
mov byte ptr remote_call,0
|
|
jmp fdos_move_chdir
|
|
|
|
move_seek: ; re-seek the old directory entry
|
|
mov ax,saved_dcnt ; get saved directory count
|
|
dec ax ; move back one for GETDIR
|
|
mov dcnt,ax ; set search offset
|
|
and chdblk,0 ; follow the chains, not sequential
|
|
mov cl,0 ; return next entry
|
|
jmp getdir ; in current directory
|
|
|
|
move_comp:
|
|
mov ax,fdos_hds_blk
|
|
cmp ax,saved_hds_blk
|
|
ret
|
|
|
|
eject
|
|
; GET/SET FILE DATE/TIME
|
|
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
; | 57 | handle | mode | date | time |
|
|
; +----+----+----+----+----+----+----+----+----+----+
|
|
|
|
; entry:
|
|
; ------
|
|
; handle: open file handle
|
|
; mode: 0 = get date/time, 1 = set date/time
|
|
; date: date as in directory FCB
|
|
; time: time as in directory FCB
|
|
|
|
; exit:
|
|
; -----
|
|
; AX: 0000 or error code ( < 0)
|
|
; date: date of last modification if mode = 0
|
|
; time: date of last modification if mode = 0
|
|
|
|
fdos_dattim:
|
|
;-----------
|
|
call vfy_dhndl_ptr ; check file handle #
|
|
call redir_dhndl_offer
|
|
call local_disk ; get MXdisk, switch stack
|
|
call check_handle ; check if legal file handle
|
|
cmp fdos_pb+4,0 ; get/set date/time?
|
|
jne dattim1
|
|
mov ax,es:DHNDL_DATE[bx] ; get date
|
|
mov fdos_pb+6,ax
|
|
mov ax,es:DHNDL_TIME[bx] ; get time
|
|
mov fdos_pb+8,ax
|
|
ret
|
|
dattim1: ; else set date/time stamp
|
|
mov dx,fdos_pb+6 ; set date
|
|
mov ax,fdos_pb+8 ; and time
|
|
jmps set_timestamp
|
|
|
|
Public timestamp_dhndl
|
|
|
|
timestamp_dhndl:
|
|
;---------------
|
|
; On Entry:
|
|
; ES:BX -> DHNDL_
|
|
; On Exit:
|
|
; ES:BX preserved
|
|
;
|
|
; Mark this DHNDL_ with the current time and date
|
|
push es
|
|
push bx
|
|
call ReadTOD ; get TOD in DX/AX
|
|
pop bx
|
|
pop es
|
|
; jmp set_timestamp
|
|
|
|
set_timestamp:
|
|
;-------------
|
|
; On Entry:
|
|
; ES:BX -> DHNDL_
|
|
; AX = time
|
|
; DX = date
|
|
; On Exit:
|
|
; None
|
|
;
|
|
and es:DHNDL_ATTR[bx],not DHAT_CLEAN
|
|
mov es:DHNDL_DATE[bx],dx ; remember to update directory
|
|
mov es:DHNDL_TIME[bx],ax ; ask SHARE to record the changes
|
|
callf share_stub+S_RECORD ; for the benefit of others
|
|
ret
|
|
|
|
|
|
fdos_reopen_fcb:
|
|
;---------------
|
|
; On entry
|
|
; On exit on success pb offset 6 set to 0 if device, 1 if disk file
|
|
test byte ptr remote_call+1,DHM_FCB/100h
|
|
jnz fcb_reopen0
|
|
mov ax,ED_FUNCTION
|
|
jmp fdos_error
|
|
fcb_reopen0:
|
|
call local_disk
|
|
call path_prep
|
|
jc fcb_reopen_error ; any error means reopen is invalid
|
|
call check_device
|
|
jnc fcb_reopen_error ; we should not find devices here
|
|
mov ax,fdos_pb+6
|
|
mov fdos_hds_blk,ax
|
|
mov ax,fdos_pb+8
|
|
dec ax
|
|
mov dcnt,ax
|
|
xor cx,cx
|
|
mov chdblk,cx
|
|
call getdir
|
|
jz fcb_reopen_error
|
|
cmp DNAME[bx],0e5h ; has the entry been deleted ?
|
|
je fcb_reopen_error
|
|
mov fdos_pb+2,ax
|
|
ret
|
|
fcb_reopen_error:
|
|
mov ax,ED_NOFCBS
|
|
jmp fdos_error
|