; 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