minix.fst/get_dir_entry.aii

1009 lines
12 KiB
Plaintext

;
; This file contains the implementation of get_dir_entry.
; It also contains other functions for finding an entry within
; a directory.
;
string asis
include 'gsos.equ'
include 'minix.equ'
include 'records.equ'
include 'fst.equ'
include 'fst.macros'
include 'M16.Debug'
include 'p.equ'
import disk_inode:v1_inode
import disk_super:v1_super
;
; dirent_entry 0 is a special case.
; (assuming base = 1, displacement = 1)
; first call:
; before
; dirent_entry = 0, dirent_zone = 0, dirent_offset = 0
; after:
; dirent_entry = 1, dirent_zone = 0, dirent_offset = 0
;
; subsequent calls:
; before
; dirent_entry = 1, dirent_zone = ..., dirent_offset = ...
; after
; dirent_entry = 2, dirent_zone = ..., dirent_offset = ...
;
; don't think this needs to be dp... move to a normal record.
data record
; size of the directory, divided by dirent_size.
size ds.l 1
displacement ds.w 1
dirent_size ds.w 1
dirent_zone ds.w 1
dirent_offset ds.w 1
dirent_entry ds.w 1
dirent ds v1L_dirent
endr
import init_fcr
import init_vcr
import load_inode
import read_data_block
import do_ignore
import do_eof
import do_blocks
import do_r_eof
import do_r_blocks
import do_access
import do_file_sys_id
import do_option_list
import do_create_date_time
import do_mod_date_time
import do_file_type
import do_aux_type
entry init
entry base_displace
entry find_next_dirent
entry find_absolute_dirent
entry find_dirent_block
entry count_entries
entry count_dirent_block
entry do_flags
entry do_name_buffer_0
entry do_name_buffer_1
entry do_entry_num
entry sparse_dirent_block
get_dir_entry procname export
with fst_parms
with dp
with data
;~DebugSetTrace #1
jsr init_vcr
jsr init_fcr
;brk $ea
; check for read-access
ldy #fcr.access
lda [<my_fcr],y
and #read_access
bne access_ok
lda #invalid_access
sec
rtl
access_ok
; is it a dir?
ldy #fcr.disk_inode.mode
lda [my_fcr],y
and #S_IFMT
cmp #S_IFDIR
beq dir
lda #bad_store_type
sec
rtl
dir
jsr init
jsr base_displace
bcc @ok
rtl
@ok
; if data.dirent.inode is valid, load the inode
; (if not valid, this was a base/displace of 0 to return
; the dirent count. field info will be returned from the
; directory inode)
lda data.dirent.inode
beq dispatch
; if inode == the directory inode (.) , no need to re-load it.
cmp inode
beq dispatch
sta inode
jsr load_inode
bcc dispatch
exit
rtl
dispatch
lda <call_class
beq class0
class1
lda [param_blk_ptr] ; pcount
dec a
asl a ; x 2
asl a ; x 4
tax
dispatch get_dir_entry_dcb_1
;~DebugHexDump param_blk_ptr, #DirEntryRecGS.__sizeof
lda tool_error
cmp #1
rtl
class0
ldx #get_dir_entry_dcb_0_size-4
dispatch get_dir_entry_dcb_0
lda tool_error
cmp #1
rtl
get_dir_entry_dcb_0
dc.w $00, do_ignore ; refnum
dc.w $02, do_flags
dc.w $04, do_ignore ; base
dc.w $06, do_ignore ; displacement
dc.w $08, do_name_buffer_1 ; uses gs/os output string!
dc.w $0c, do_entry_num
dc.w $0e, do_file_type
dc.w $10, do_eof
dc.w $14, do_blocks
dc.w $18, do_create_date_time
dc.w $20, do_mod_date_time
dc.w $28, do_access
dc.w $2a, do_aux_type
dc.w $2e, do_file_sys_id
get_dir_entry_dcb_0_size equ *-get_dir_entry_dcb_0
get_dir_entry_dcb_1
;dc.w $00, do_ignore ; pCount
dc.w $02, do_ignore ; refnum
dc.w $04, do_flags
dc.w $06, do_ignore ; base
dc.w $08, do_ignore ; displacement
dc.w $0a, do_name_buffer_1 ;
dc.w $0e, do_entry_num
dc.w $10, do_file_type
dc.w $12, do_eof
dc.w $16, do_blocks
dc.w $1a, do_create_date_time
dc.w $22, do_mod_date_time
dc.w $2a, do_access
dc.w $2c, do_aux_type
dc.w $30, do_file_sys_id
dc.w $32, do_option_list
dc.w $36, do_r_eof
dc.w $3a, do_r_blocks
endp
init procname
with fst_parms,dp,data
; must be set up before hand.
;lda device
;sta dev_parms.dev_num
; directory entry (and inode lookup) should be cached.
lda #in_cache
sta dev_parms.dev_cache_pr
; should probably read half-blocks...
lda #1024
;sta dev_parms.dev_blk_size
sta dev_parms.dev_req_cnt
;stz dirent_offset
;stz dirent_entry
;stz dirent_zone
ldy #fcr.dirent_zone
lda [my_fcr],y
sta dirent_zone
ldy #fcr.dirent_offset
lda [my_fcr],y
sta dirent_offset
ldy #fcr.dirent_entry
lda [my_fcr],y
sta dirent_entry
stz data.dirent.inode
stz data.dirent.name
lda #v1_dirent.__sizeof
sta dirent_size
lda disk_inode.size
sta size
lda disk_inode.size+2
sta size+2
; / 2
lsr size+2
ror size
; / 4
lsr size+2
ror size
; / 8
lsr size+2
ror size
; / 16
lsr size+2
ror size
;~DebugHexDump <my_vcr, #vcr.__sizeof
ldy #vcr.super.magic
lda [my_vcr],y
cmp #v1L.MAGIC
bne minix
; this is
linux
; linux dirents are twice as big.
asl dirent_size
; / 32
lsr size+2
ror size
minix
rts
endp
base_displace procname
with fst_parms, dp
with data
; offset is $04 for class 0, $06 for class 1
lda #$04
clc
adc <call_class
tay
lda [param_blk_ptr],y ; base
cmp #3
bcs perr
asl a
tax
iny
iny
lda [param_blk_ptr],y ; displacement
sta displacement
jmp (base_table,x)
perr
lda #parm_range_err
sec
rts
base_table
dc.w absolute, forward, backward
absolute
; absolute with a displacement of 0 -> return total count.
;
cmp #0
beq @count
jmp find_absolute_dirent
@count
jmp count_entries
forward
jmp find_next_dirent
;
backward
; backward 0 ? == forward 0
cmp #0
beq forward
eor #$ffff
sec ; inc / clc
adc dirent_entry
sta displacement
jmp find_absolute_dirent
endp
count_entries procname
; count the number of entries.
with fst_parms
with dp, data
; read each block and count the entries.
; only handles direct blocks. so there.
; disk inode has been copied to the dp.
stz dirent_entry
ldx #0
zone_loop
stx dirent_zone
lda disk_inode.zone,x
bne @ok ; directory should not be sparse!
; sparse!
jsr sparse_dirent_block
bcs done
bra next
@ok
jsr read_data_block
bcs exit
; ~DebugHexDump <io_buffer, #1024
jsr count_dirent_block
lda size
beq done
next
ldx dirent_zone
inx
inx
cpx #v1.NR_DZONES*2
bcs done
bra zone_loop
;
; minix has 16-byte dirents.
; linux has 32-byte dirents.
done
; - 2 for . and ..
dec dirent_entry
dec dirent_entry
; also reset the displacement to 0.
lda #0
ldy #fcr.dirent_entry
sta [my_fcr],y
ldy #fcr.dirent_zone
sta [my_fcr],y
ldy #fcr.dirent_offset
sta [my_fcr],y
clc
exit
rts
endp
count_dirent_block procname
; 16-byte dirent entries -- 64 per block.
with dp, data
ldy #0
loop
lda [io_buffer],y
beq next
inc dirent_entry
next
; not 32-bit safe.
dec size
beq done
tya
clc
adc dirent_size
tay
cmp #1024
bcc loop
done
rts
endp
strlen procname
; strlen the dirent.
; will be 0-terminated unless if < dirent size.
lda data.dirent.inode
beq exit
ldx #2
short m
loop
lda data.dirent,x
beq zero
inx
cpx data.dirent_size
bcc loop
zero
long m
txa
dec a ; -2 for dirent.inode
dec a
exit
rts
endp
do_flags procname
with fst_parms
; $8000 for extended file, 0 for everything else.
lda #0
sta [param_blk_ptr],y
rts
endp
do_entry_num procname
with fst_parms
with data
lda dirent_entry
sta [param_blk_ptr],y
rts
endp
do_name_buffer_0 procname
with fst_parms
with dp
lda [param_blk_ptr],y
sta ptr
iny
iny
lda [param_blk_ptr],y
sta ptr+2
ora ptr
beq exit
copy_it
phx
jsr strlen
; strlen handles inode = 0
tax
tay
short m
sta [ptr]
; no need to dey since there is a length byte.
dex
bmi @done
@loop
lda data.dirent.name,x
sta [ptr],y
dey
dex
bpl @loop
@done
long m
plx
exit
rts
endp
do_name_buffer_1 procname
with fst_parms
with dp
lda [param_blk_ptr],y
sta ptr
iny
iny
lda [param_blk_ptr],y
sta ptr+2
ora ptr
beq exit
phx
jsr strlen
pha ; save
lda [ptr] ; total output buffer size
sec
sbc #4
bmi error
cmp 1,s
bcs ok
error_store_size
ldy #2
sta [ptr],y
error
pla
lda #buff_too_small
sta tool_error
plx
rts
ok
pla ; strlen
ldy #2
sta [ptr],y
tax
tay
iny ; skip gs/os string overhead
iny
iny
short m
dex
bmi @done
@loop
lda data.dirent.name,x
sta [ptr],y
dey
dex
bpl @loop
@done
long m
plx
exit
rts
endp
;
;
; new code....
; here's our big insight
; 1. base 1, displacement xxx is equivalent to stepping forward xxx entries.
; 2. base 2, displacement xxx is equivalent to base 0, displacement fcr.dirent_entry - displacement
; 2a. if displacement is 0, is equivalent to stepping forward 0 entries.
; 3. base 0, displacement xxx is equivalent to base 1 displacement xxx when fcr.dirent_entry = 0
;
; therefore the only special case is dirent_entry == 0 (since displacement 1 -> read entry 0)
;
;
; 9/4/2015 -- The first two entries (. and ..) should be skipped.
;
;
find_absolute_dirent procname
with data
; if displacement > dirent_entry, we can re-use that info.
lda displacement
cmp dirent_entry
bcc no
sec
sbc dirent_entry
sta displacement
bra find_next_dirent
no
stz dirent_entry
stz dirent_zone
stz dirent_offset
bra find_next_dirent
endp
find_next_dirent proc
with data, dp
lda dirent_entry
beq @first
dec dirent_entry
bra @ok
@first
dec displacement
bmi eod
; set dirent_offset = dirent_size * 2 to skip over . and ..
lda dirent_size
asl a ; x 2
sta dirent_offset
@ok
; make sure dir not empty (should never happen...)
lda size
beq eod
ldx dirent_zone
loop
lda disk_inode.zone,x
beq sparse ; should never happen...
phx ; save
jsr read_data_block
plx
bcs exit
; phx
; phy
; pha
; ~DebugHexDump <io_buffer, #1024
; pla
; ply
; plx
jsr find_dirent_block
bcc next
; a = 0 if it was found!
cmp #1
rts
next
inx
inx
stz dirent_offset
cpx #v1.NR_DZONES*2
bcs exit
bra loop
sparse
jsr sparse_dirent_block
bcs eod
bra next
found_it
;stx dirent_zone
;sty dirent_offset
clc
exit
rts
eod
lda #end_of_dir
sec
rts
endp
;
; returns:
; carry set on error or if found.
; a = 0 if found, or error.
;
; inputs y = dirent offset
find_dirent_block procname
with dp, data
ldy dirent_offset
loop
lda [io_buffer],y
beq next
inc dirent_entry
dec displacement
bmi found_it
next
dec size
beq eod
tya
clc
adc dirent_size
tay
cmp #1024
bcc loop
; not found ...
lda #0
clc
rts
eod
lda #end_of_dir
sec
rts
found_it
; we found our entry!!!
; copy it over.
stx dirent_zone
sty dirent_offset
; dirent_entry updated above.
ldx #0
@loop
lda [io_buffer],y
sta data.dirent,x
iny
iny
inx
inx
cpx dirent_size
bcc @loop
; update the fcr.
lda dirent_entry
ldy #fcr.dirent_entry
sta [my_fcr],y
lda dirent_offset
ldy #fcr.dirent_offset
sta [my_fcr],y
lda dirent_zone
ldy #fcr.dirent_zone
sta [my_fcr],y
lda #0
sec ; found!
rts
endp
strcmp procname
; clobbers ptr
; check if the name is even possible...
with dp, data
import target:GSString32
;~DebugSetTrace #1
iny ; skip inode
iny
tya
clc
adc io_buffer
sta ptr
lda #0
adc io_buffer+2
sta ptr+2
ldy #0
ldx target.length
short m
; y = offset
; x = length of target string.
loop
lda [ptr],y
beq eos
cmp target.text,y
bne no8
iny
dex
bpl loop
eos
long m
; if y == target.length, this is a match.
cpy target.length
bne no
yes
;~DebugSetTrace #0
clc
rts
no8
long m
no
;~DebugSetTrace #0
sec
rts
endp
sparse_dirent_block procname
; (not 32-bit safe)
; decrease size by the appropriate amount
; for a sparse block.
; (should probably never happen!)
; sets c if size <= 0
with data
lda dirent_size
cmp #32 ; linux? 32 dirents per block
beq @ok
lda #64 ; minix? 64 dirents per block
@ok
eor #$ffff
sec
adc size
sta size
bmi eod
beq eod
clc
rts
eod
sec
rts
endp
find_entry_by_name procname export
; scan the directory and search by name...
with dp
with data
import target:GSString32
jsr init
lda size
beq fnf
bmi fnf
; make sure the name is valid.
ldx target.length
beq fnf
; if dirent_size < target.length, it can't be found.
; should do this sooner.
lda data.dirent_size
dec a
dec a
cmp target.length
bcc fnf
ldx #0
zone_loop
; x = dirent_zone
stx dirent_zone
lda disk_inode.zone,x
beq sparse
jsr read_data_block
bcc @ok
rts
@ok
ldy #0
dirent_loop
; y = dirent offset
sty dirent_offset
lda [io_buffer],y
beq next_dirent
sta inode ; optomistic...
jsr strcmp
bcs next_dirent
; found it!
lda #0
clc
rts
next_dirent
dec size
beq fnf
lda dirent_offset
clc
adc dirent_size
tay
cmp #1024
bcs next_zone
bra dirent_loop
next_zone
ldx dirent_zone
inx
inx
cpx #v1.NR_DZONES*2
bcc zone_loop
; all out of zones -> not found!
fnf
stz inode
lda #file_not_found
sec
rts
sparse
; sparse block. jump to the next zone, decrement size appropriately.
jsr sparse_dirent_block
bcs fnf
bra next_zone
endp
end