kolibrios/kernel/trunk/fs/xfs.asm
Ivan Baravy 0ad6f9623f basic xfs read support
git-svn-id: svn://kolibrios.org@3913 a494cfbc-eb01-0410-851d-a64ba20cac60
2013-09-17 21:28:18 +00:00

2770 lines
90 KiB
NASM

include 'xfs.inc'
;
; This file contains XFS related code.
; For more information on XFS check sources below.
;
; 1. XFS Filesystem Structure, 2nd Edition, Revision 1. Silicon Graphics Inc. 2006
; 2. Linux source http://kernel.org
;
; test partition type (valid XFS one?)
; alloc and fill XFS (see xfs.inc) structure
; this function is called for each partition
; returns 0 (not XFS or invalid) / pointer to partition structure
xfs_create_partition:
push ebx ecx edx esi edi
cmp dword[ebx + xfs_sb.sb_magicnum], XFS_SB_MAGIC ; signature
jne .error
; TODO: check XFS.versionnum and XFS.features2
; print superblock params for debugging (waiting for bug reports)
movi eax, sizeof.XFS
call malloc
test eax, eax
jz .error
; standard partition initialization, common for all file systems
mov edi, eax
mov eax, dword[ebp + PARTITION.FirstSector]
mov dword[edi + XFS.FirstSector], eax
mov eax, dword[ebp + PARTITION.FirstSector + 4]
mov dword[edi + XFS.FirstSector + 4], eax
mov eax, dword[ebp + PARTITION.Length]
mov dword[edi + XFS.Length], eax
mov eax, dword[ebp + PARTITION.Length + 4]
mov dword[edi + XFS.Length + 4], eax
mov eax, [ebp + PARTITION.Disk]
mov [edi + XFS.Disk], eax
mov [edi + XFS.FSUserFunctions], xfs_user_functions
; here we initialize only one mutex so far (for the entire partition)
; XFS potentially allows parallel r/w access to several AGs, keep it in mind for SMP times
lea ecx, [edi + XFS.Lock]
call mutex_init
; read superblock and fill just allocated XFS partition structure
mov eax, [ebx + xfs_sb.sb_blocksize]
bswap eax ; XFS is big endian
mov [edi + XFS.blocksize], eax
movzx eax, word[ebx + xfs_sb.sb_sectsize]
xchg al, ah
mov [edi + XFS.sectsize], eax
movzx eax, word[ebx + xfs_sb.sb_versionnum]
xchg al, ah
mov [edi + XFS.versionnum], eax
mov eax, [ebx + xfs_sb.sb_features2]
bswap eax
mov [edi + XFS.features2], eax
movzx eax, word[ebx + xfs_sb.sb_inodesize]
xchg al, ah
mov [edi + XFS.inodesize], eax
movzx eax, word[ebx + xfs_sb.sb_inopblock] ; inodes per block
xchg al, ah
mov [edi + XFS.inopblock], eax
movzx eax, byte[ebx + xfs_sb.sb_blocklog] ; log2 of block size, in bytes
mov [edi + XFS.blocklog], eax
movzx eax, byte[ebx + xfs_sb.sb_sectlog]
mov [edi + XFS.sectlog], eax
movzx eax, byte[ebx + xfs_sb.sb_inodelog]
mov [edi + XFS.inodelog], eax
movzx eax, byte[ebx + xfs_sb.sb_inopblog]
mov [edi + XFS.inopblog], eax
movzx eax, byte[ebx + xfs_sb.sb_dirblklog]
mov [edi + XFS.dirblklog], eax
mov eax, dword[ebx + xfs_sb.sb_rootino + 4] ;
bswap eax ; big
mov dword[edi + XFS.rootino + 0], eax ; endian
mov eax, dword[ebx + xfs_sb.sb_rootino + 0] ; 64bit
bswap eax ; number
mov dword[edi + XFS.rootino + 4], eax ;
mov eax, [edi + XFS.blocksize]
mov ecx, [edi + XFS.dirblklog]
shl eax, cl
mov [edi + XFS.dirblocksize], eax ; blocks for files, dirblocks for directories
; sector is always smaller than block
; so precalculate shift order to allow faster sector_num->block_num conversion
mov ecx, [edi + XFS.blocklog]
sub ecx, [edi + XFS.sectlog]
mov [edi + XFS.blockmsectlog], ecx
mov eax, 1
shl eax, cl
mov [edi + XFS.sectpblock], eax
; shift order for inode_num->block_num conversion
mov eax, [edi + XFS.blocklog]
sub eax, [edi + XFS.inodelog]
mov [edi + XFS.inodetoblocklog], eax
mov eax, [ebx + xfs_sb.sb_agblocks]
bswap eax
mov [edi + XFS.agblocks], eax
movzx ecx, byte[ebx + xfs_sb.sb_agblklog]
mov [edi + XFS.agblklog], ecx
; get the mask for block numbers
; block numbers are AG relative!
; bitfield length may vary between partitions
mov eax, 1
shl eax, cl
dec eax
mov dword[edi + XFS.agblockmask + 0], eax
mov eax, 1
sub ecx, 32
jc @f
shl eax, cl
@@:
dec eax
mov dword[edi + XFS.agblockmask + 4], eax
; calculate magic offsets for directories
mov ecx, [edi + XFS.blocklog]
mov eax, XFS_DIR2_LEAF_OFFSET AND 0xffffffff ; lo
mov edx, XFS_DIR2_LEAF_OFFSET SHR 32 ; hi
shrd eax, edx, cl
mov [edi + XFS.dir2_leaf_offset_blocks], eax
mov ecx, [edi + XFS.blocklog]
mov eax, XFS_DIR2_FREE_OFFSET AND 0xffffffff ; lo
mov edx, XFS_DIR2_FREE_OFFSET SHR 32 ; hi
shrd eax, edx, cl
mov [edi + XFS.dir2_free_offset_blocks], eax
; mov ecx, [edi + XFS.dirblklog]
; mov eax, [edi + XFS.blocksize]
; shl eax, cl
; mov [edi + XFS.dirblocksize], eax
mov eax, [edi + XFS.blocksize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.cur_block], eax
; we do need XFS.blocksize bytes for single inode
; minimal file system structure is block, inodes are packed in blocks
mov eax, [edi + XFS.blocksize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.cur_inode], eax
; temporary inode
; used for browsing directories
mov eax, [edi + XFS.blocksize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.tmp_inode], eax
; current sector
; only for sector size structures like AGI
; inodes has usually the same size, but never store them here
mov eax, [edi + XFS.sectsize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.cur_sect], eax
; current directory block
mov eax, [edi + XFS.dirblocksize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.cur_dirblock], eax
.quit:
mov eax, edi ; return pointer to allocated XFS partition structure
pop edi esi edx ecx ebx
ret
.error:
xor eax, eax
pop edi esi edx ecx ebx
ret
iglobal
align 4
xfs_user_functions:
dd xfs_free
dd (xfs_user_functions_end - xfs_user_functions - 4) / 4
dd xfs_Read
dd xfs_ReadFolder
dd 0;xfs_Rewrite
dd 0;xfs_Write
dd 0;xfs_SetFileEnd
dd xfs_GetFileInfo
dd 0;xfs_SetFileInfo
dd 0
dd 0;xfs_Delete
dd 0;xfs_CreateFolder
xfs_user_functions_end:
endg
; lock partition access mutex
proc xfs_lock
;DEBUGF 1,"xfs_lock\n"
lea ecx, [ebp + XFS.Lock]
jmp mutex_lock
endp
; unlock partition access mutex
proc xfs_unlock
;DEBUGF 1,"xfs_unlock\n"
lea ecx, [ebp + XFS.Lock]
jmp mutex_unlock
endp
; free all the allocated memory
; called on partition destroy
proc xfs_free
push ebp
xchg ebp, eax
stdcall kernel_free, [ebp + XFS.cur_block]
stdcall kernel_free, [ebp + XFS.cur_inode]
stdcall kernel_free, [ebp + XFS.cur_sect]
stdcall kernel_free, [ebp + XFS.cur_dirblock]
stdcall kernel_free, [ebp + XFS.tmp_inode]
xchg ebp, eax
call free
pop ebp
ret
endp
;---------------------------------------------------------------
; block number (AG relative)
; eax -- inode_lo
; edx -- inode_hi
; ebx -- buffer
;---------------------------------------------------------------
xfs_read_block:
push ebx esi
push edx
push eax
; XFS block numbers are AG relative
; they come in bitfield form of concatenated AG and block numbers
; to get absolute block number for fs_read32_sys we should
; 1. extract AG number (using precalculated mask)
; 2. multiply it by the AG size in blocks
; 3. add AG relative block number
; 1.
mov ecx, [ebp + XFS.agblklog]
shrd eax, edx, cl
shr edx, cl
; 2.
mul dword[ebp + XFS.agblocks]
pop ecx
pop esi
and ecx, dword[ebp + XFS.agblockmask + 0]
and esi, dword[ebp + XFS.agblockmask + 4]
; 3.
add eax, ecx
adc edx, esi
;DEBUGF 1,"read block: 0x%x%x\n",edx,eax
; there is no way to read file system block at once, therefore we
; 1. calculate the number of sectors first
; 2. and then read them in series
; 1.
mov ecx, [ebp + XFS.blockmsectlog]
shld edx, eax, cl
shl eax, cl
mov esi, [ebp + XFS.sectpblock]
; 2.
.next_sector:
push eax edx
call fs_read32_sys
mov ecx, eax
pop edx eax
test ecx, ecx
jnz .error
add eax, 1 ; be ready to fs_read64_sys
adc edx, 0
add ebx, [ebp + XFS.sectsize] ; update buffer offset
dec esi
jnz .next_sector
.quit:
xor eax, eax
pop esi ebx
ret
.error:
mov eax, ecx
pop esi ebx
ret
;---------------------------------------------------------------
; push buffer
; push startblock_hi
; push startblock_lo
; call xfs_read_dirblock
; test eax, eax
;---------------------------------------------------------------
xfs_read_dirblock:
;mov eax, [esp + 4]
;mov edx, [esp + 8]
;DEBUGF 1,"read dirblock at: %d %d\n",edx,eax
;DEBUGF 1,"dirblklog: %d\n",[ebp + XFS.dirblklog]
push ebx esi
mov eax, [esp + 12] ; startblock_lo
mov edx, [esp + 16] ; startblock_hi
mov ebx, [esp + 20] ; buffer
; dirblock >= block
; read dirblocks by blocks
mov ecx, [ebp + XFS.dirblklog]
mov esi, 1
shl esi, cl
.next_block:
push eax edx
call xfs_read_block
mov ecx, eax
pop edx eax
test ecx, ecx
jnz .error
add eax, 1 ; be ready to fs_read64_sys
adc edx, 0
add ebx, [ebp + XFS.blocksize]
dec esi
jnz .next_block
.quit:
xor eax, eax
pop esi ebx
ret 12
.error:
mov eax, ecx
pop esi ebx
ret 12
;---------------------------------------------------------------
; push buffer
; push inode_hi
; push inode_lo
; call xfs_read_inode
; test eax, eax
;---------------------------------------------------------------
xfs_read_inode:
;DEBUGF 1,"reading inode: 0x%x%x\n",[esp+8],[esp+4]
push ebx
mov eax, [esp + 8] ; inode_lo
mov edx, [esp + 12] ; inode_hi
mov ebx, [esp + 16] ; buffer
; inodes are packed into blocks
; 1. calculate block number
; 2. read the block
; 3. add inode offset to block base address
; 1.
mov ecx, [ebp + XFS.inodetoblocklog]
shrd eax, edx, cl
shr edx, cl
; 2.
call xfs_read_block
test eax, eax
jnz .error
; note that inode numbers should be first extracted from bitfields using mask
mov eax, [esp + 8]
mov edx, 1
mov ecx, [ebp + XFS.inopblog]
shl edx, cl
dec edx ; get inode number mask
and eax, edx ; apply mask
mov ecx, [ebp + XFS.inodelog]
shl eax, cl
add ebx, eax
cmp word[ebx], XFS_DINODE_MAGIC ; test signature
jne .error
.quit:
xor eax, eax
mov edx, ebx
pop ebx
ret 12
.error:
movi eax, ERROR_FS_FAIL
mov edx, ebx
pop ebx
ret 12
;----------------------------------------------------------------
; push encoding ; ASCII / UNICODE
; push src ; inode
; push dst ; bdfe
; push entries_to_read
; push start_number ; from 0
;----------------------------------------------------------------
xfs_dir_get_bdfes:
DEBUGF 1,"xfs_dir_get_bdfes: %d entries from %d\n",[esp+8],[esp+4]
sub esp, 4 ; local vars
push ecx edx esi edi
mov ebx, [esp + 36] ; src
mov edx, [esp + 32] ; dst
mov ecx, [esp + 24] ; start_number
; define directory ondisk format and jump to corresponding label
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_LOCAL
jne .not_shortdir
jmp .shortdir
.not_shortdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_blockdir
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
cmp eax, 1
jne .not_blockdir
jmp .blockdir
.not_blockdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_leafdir
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
cmp eax, 4
ja .not_leafdir
jmp .leafdir
.not_leafdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_nodedir
jmp .nodedir
.not_nodedir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE
jne .not_btreedir
jmp .btreedir
.not_btreedir:
movi eax, ERROR_FS_FAIL
jmp .error
; short form directory (all the data fits into inode)
.shortdir:
;DEBUGF 1,"shortdir\n",
movzx eax, word[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count]
test al, al ; is count zero?
jnz @f ; if not, use it (i8count must be zero then)
shr eax, 8 ; use i8count
@@:
add eax, 1 ; '..' and '.' are implicit
mov dword[edx + 0], 1 ; version
mov [edx + 8], eax ; total entries
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
mov [ebp + XFS.entries_read], eax
; inode numbers are often saved as 4 bytes (iff they fit)
; compute the length of inode numbers
mov eax, 4 ; 4 by default
cmp byte[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.i8count], 0
je @f
add eax, eax ; 4+4=8, iff i8count != 0
@@:
mov dword[edx + 12], 0 ; reserved
mov dword[edx + 16], 0 ;
mov dword[edx + 20], 0 ;
mov dword[edx + 24], 0 ;
mov dword[edx + 28], 0 ;
add edx, 32
lea esi, [ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent + eax]
dec ecx
js .shortdir.fill
; skip some entries if the first entry to read is not 0
.shortdir.skip:
test ecx, ecx
jz .shortdir.skipped
movzx edi, byte[esi + xfs_dir2_sf_entry.namelen]
lea esi, [esi + xfs_dir2_sf_entry.name + edi]
add esi, eax
dec ecx
jnz .shortdir.skip
mov ecx, [esp + 28] ; entries to read
jmp .shortdir.skipped
.shortdir.fill:
mov ecx, [esp + 28] ; total number
test ecx, ecx
jz .quit
push ecx
;DEBUGF 1,"ecx: %d\n",ecx
lea edi, [edx + 40] ; get file name offset
;DEBUGF 1,"filename: ..\n"
mov dword[edi], '..'
mov edi, edx
push eax ebx edx esi
stdcall xfs_get_inode_number_sf, dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count], dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent + 4], dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent]
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
; test eax, eax
; jnz .error
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ebx eax
jnz .error
mov ecx, [esp + 44] ; file name encding
mov [edx + 4], ecx
add edx, 304 ; ASCII only for now
pop ecx
dec ecx
jz .quit
; push ecx
; lea edi, [edx + 40]
;DEBUGF 1,"filename: .\n"
; mov dword[edi], '.'
; mov edi, edx
; push eax edx
; stdcall xfs_get_inode_info, [ebp + XFS.cur_inode], edi
; test eax, eax
; pop edx eax
; jnz .error
; mov ecx, [esp + 44]
; mov [edx + 4], ecx
; add edx, 304 ; ASCII only for now
; pop ecx
; dec ecx
; jz .quit
; we skipped some entries
; now we fill min(required, present) number of bdfe's
.shortdir.skipped:
;DEBUGF 1,"ecx: %d\n",ecx
push ecx
movzx ecx, byte[esi + xfs_dir2_sf_entry.namelen]
add esi, xfs_dir2_sf_entry.name
lea edi, [edx + 40] ; bdfe offset of file name
;DEBUGF 1,"filename: |%s|\n",esi
rep movsb
mov word[edi], 0 ; terminator (ASCIIZ)
push eax ebx ecx edx esi
; push edx ; for xfs_get_inode_info
mov edi, edx
stdcall xfs_get_inode_number_sf, dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count], [esi + 4], [esi]
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
; test eax, eax
; jnz .error
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ecx ebx eax
jnz .error
mov ecx, [esp + 44] ; file name encoding
mov [edx + 4], ecx
add edx, 304 ; ASCII only for now
add esi, eax
pop ecx
dec ecx
jnz .shortdir.skipped
jmp .quit
.blockdir:
;DEBUGF 1,"blockdir\n"
push edx
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
stdcall xfs_extent_unpack, eax
;DEBUGF 1,"extent.br_startoff : 0x%x%x\n",[ebp+XFS.extent.br_startoff+4],[ebp+XFS.extent.br_startoff+0]
;DEBUGF 1,"extent.br_startblock: 0x%x%x\n",[ebp+XFS.extent.br_startblock+4],[ebp+XFS.extent.br_startblock+0]
;DEBUGF 1,"extent.br_blockcount: %d\n",[ebp+XFS.extent.br_blockcount]
;DEBUGF 1,"extent.br_state : %d\n",[ebp+XFS.extent.br_state]
stdcall xfs_read_dirblock, dword[ebp + XFS.extent.br_startblock + 0], dword[ebp + XFS.extent.br_startblock + 4], [ebp + XFS.cur_dirblock]
test eax, eax
pop edx
jnz .error
;DEBUGF 1,"dirblock signature: %s\n",[ebp+XFS.cur_dirblock]
mov ebx, [ebp + XFS.cur_dirblock]
mov dword[edx + 0], 1 ; version
mov eax, [ebp + XFS.dirblocksize]
mov ecx, [ebx + eax - sizeof.xfs_dir2_block_tail + xfs_dir2_block_tail.stale]
mov eax, [ebx + eax - sizeof.xfs_dir2_block_tail + xfs_dir2_block_tail.count]
bswap ecx
bswap eax
sub eax, ecx ; actual number of entries = count - stale
mov [edx + 8], eax ; total entries
;DEBUGF 1,"total entries: %d\n",eax
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
mov [ebp + XFS.entries_read], eax
;DEBUGF 1,"actually read entries: %d\n",eax
mov dword[edx + 12], 0 ; reserved
mov dword[edx + 16], 0 ;
mov dword[edx + 20], 0 ;
mov dword[edx + 24], 0 ;
mov dword[edx + 28], 0 ;
add ebx, xfs_dir2_block.u
mov ecx, [esp + 24] ; start entry number
; also means how many to skip
test ecx, ecx
jz .blockdir.skipped
.blockdir.skip:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .blockdir.skip
@@:
movzx eax, [ebx + xfs_dir2_data_union.xentry.namelen]
lea ebx, [ebx + xfs_dir2_data_union.xentry.name + eax + 2] ; 2 bytes for 'tag'
add ebx, 7 ; align on 8 bytes
and ebx, not 7
dec ecx
jnz .blockdir.skip
.blockdir.skipped:
mov ecx, [edx + 4] ; actually read entries
test ecx, ecx
jz .quit
add edx, 32 ; set edx to the first bdfe
.blockdir.next_entry:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_NULL
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .blockdir.next_entry
@@:
push ecx
push eax ebx ecx edx esi
mov edi, edx
mov edx, dword[ebx + xfs_dir2_data_union.xentry.inumber + 0]
mov eax, dword[ebx + xfs_dir2_data_union.xentry.inumber + 4]
bswap edx
bswap eax
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ecx ebx eax
jnz .error
mov ecx, [esp + 44]
mov [edx + 4], ecx
lea edi, [edx + 40]
movzx ecx, byte[ebx + xfs_dir2_data_union.xentry.namelen]
lea esi, [ebx + xfs_dir2_data_union.xentry.name]
;DEBUGF 1,"filename: |%s|\n",esi
rep movsb
; call utf8_to_cp866
mov word[edi], 0 ; terminator
lea ebx, [esi + 2] ; skip 'tag'
add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes
and ebx, not 7
add edx, 304
pop ecx
dec ecx
jnz .blockdir.next_entry
jmp .quit
.leafdir:
;DEBUGF 1,"readdir: leaf\n"
mov [ebp + XFS.cur_inode_save], ebx
push ebx ecx edx
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, [ebp + XFS.dir2_leaf_offset_blocks], 0, edx, 0xffffffff, 0xffffffff
mov ecx, eax
and ecx, edx
inc ecx
pop edx ecx ebx
jz .error
mov eax, [ebp + XFS.cur_dirblock]
movzx ecx, word[eax + xfs_dir2_leaf.hdr.stale]
movzx eax, word[eax + xfs_dir2_leaf.hdr.count]
xchg cl, ch
xchg al, ah
sub eax, ecx
;DEBUGF 1,"total count: %d\n",eax
mov dword[edx + 0], 1 ; version
mov [edx + 8], eax ; total entries
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
mov dword[edx + 12], 0 ; reserved
mov dword[edx + 16], 0 ;
mov dword[edx + 20], 0 ;
mov dword[edx + 24], 0 ;
mov dword[edx + 28], 0 ;
mov eax, [ebp + XFS.cur_dirblock]
add eax, [ebp + XFS.dirblocksize]
mov [ebp + XFS.max_dirblockaddr], eax
mov dword[ebp + XFS.next_block_num + 0], 0
mov dword[ebp + XFS.next_block_num + 4], 0
mov ebx, [ebp + XFS.max_dirblockaddr] ; to read dirblock immediately
mov ecx, [esp + 24] ; start number
test ecx, ecx
jz .leafdir.skipped
.leafdir.skip:
cmp ebx, [ebp + XFS.max_dirblockaddr]
jne @f
push ecx edx
mov ebx, [ebp + XFS.cur_inode_save]
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
mov ecx, eax
and ecx, edx
inc ecx
jz .error
add eax, 1
adc edx, 0
mov dword[ebp + XFS.next_block_num + 0], eax
mov dword[ebp + XFS.next_block_num + 4], edx
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, sizeof.xfs_dir2_data_hdr
pop edx ecx
@@:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .leafdir.skip
@@:
movzx eax, [ebx + xfs_dir2_data_union.xentry.namelen]
lea ebx, [ebx + xfs_dir2_data_union.xentry.name + eax + 2] ; 2 for 'tag'
add ebx, 7
and ebx, not 7
dec ecx
jnz .leafdir.skip
.leafdir.skipped:
mov [ebp + XFS.entries_read], 0
mov ecx, [edx + 4] ; actually read entries
test ecx, ecx
jz .quit
add edx, 32 ; first bdfe entry
.leafdir.next_entry:
;DEBUGF 1,"next_extry\n"
cmp ebx, [ebp + XFS.max_dirblockaddr]
jne .leafdir.process_current_block
push ecx edx
mov ebx, [ebp + XFS.cur_inode_save]
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
pop edx ecx
jmp .quit
@@:
add eax, 1
adc edx, 0
mov dword[ebp + XFS.next_block_num + 0], eax
mov dword[ebp + XFS.next_block_num + 4], edx
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, sizeof.xfs_dir2_data_hdr
pop edx ecx
.leafdir.process_current_block:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .leafdir.next_entry
@@:
push eax ebx ecx edx esi
mov edi, edx
mov edx, dword[ebx + xfs_dir2_data_union.xentry.inumber + 0]
mov eax, dword[ebx + xfs_dir2_data_union.xentry.inumber + 4]
bswap edx
bswap eax
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ecx ebx eax
jnz .error
push ecx
mov ecx, [esp + 44]
mov [edx + 4], ecx
lea edi, [edx + 40]
movzx ecx, byte[ebx + xfs_dir2_data_union.xentry.namelen]
lea esi, [ebx + xfs_dir2_data_union.xentry.name]
;DEBUGF 1,"filename: |%s|\n",esi
rep movsb
pop ecx
mov word[edi], 0
lea ebx, [esi + 2] ; skip 'tag'
add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes
and ebx, not 7
add edx, 304 ; ASCII only for now
inc [ebp + XFS.entries_read]
dec ecx
jnz .leafdir.next_entry
jmp .quit
.nodedir:
;DEBUGF 1,"readdir: node\n"
push edx
mov [ebp + XFS.cur_inode_save], ebx
mov [ebp + XFS.entries_read], 0
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_dir2_node_get_numfiles, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks]
pop edx
test eax, eax
jnz .error
mov eax, [ebp + XFS.entries_read]
mov [ebp + XFS.entries_read], 0
;DEBUGF 1,"numfiles: %d\n",eax
mov dword[edx + 0], 1 ; version
mov [edx + 8], eax ; total entries
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
mov dword[edx + 12], 0 ; reserved
mov dword[edx + 16], 0 ;
mov dword[edx + 20], 0 ;
mov dword[edx + 24], 0 ;
mov dword[edx + 28], 0 ;
mov eax, [ebp + XFS.cur_dirblock]
add eax, [ebp + XFS.dirblocksize]
mov [ebp + XFS.max_dirblockaddr], eax
mov dword[ebp + XFS.next_block_num + 0], 0
mov dword[ebp + XFS.next_block_num + 4], 0
mov ebx, [ebp + XFS.max_dirblockaddr] ; to read dirblock immediately
mov ecx, [esp + 24] ; start number
test ecx, ecx
jz .leafdir.skipped
jmp .leafdir.skip
.btreedir:
;DEBUGF 1,"readdir: btree\n"
mov [ebp + XFS.cur_inode_save], ebx
push ebx edx
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
mov [ebp + XFS.ro_nextents], eax
mov eax, [ebp + XFS.inodesize]
sub eax, xfs_inode.di_u
sub eax, sizeof.xfs_bmdr_block
shr eax, 4
;DEBUGF 1,"maxnumresc: %d\n",eax
mov edx, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 0]
mov eax, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 4]
bswap eax
bswap edx
mov ebx, [ebp + XFS.cur_block]
;DEBUGF 1,"read_block: %x %x ",edx,eax
stdcall xfs_read_block
pop edx ebx
test eax, eax
jnz .error
;DEBUGF 1,"ok\n"
mov ebx, [ebp + XFS.cur_block]
push edx
mov [ebp + XFS.entries_read], 0
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
stdcall xfs_dir2_node_get_numfiles, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks]
pop edx
test eax, eax
jnz .error
mov eax, [ebp + XFS.entries_read]
mov [ebp + XFS.entries_read], 0
;DEBUGF 1,"numfiles: %d\n",eax
mov dword[edx + 0], 1 ; version
mov [edx + 8], eax ; total entries
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
mov dword[edx + 12], 0
mov dword[edx + 16], 0
mov dword[edx + 20], 0
mov dword[edx + 24], 0
mov dword[edx + 28], 0
mov eax, [ebp + XFS.cur_dirblock] ; fsblock?
add eax, [ebp + XFS.dirblocksize]
mov [ebp + XFS.max_dirblockaddr], eax
mov dword[ebp + XFS.next_block_num + 0], 0
mov dword[ebp + XFS.next_block_num + 4], 0
mov ebx, [ebp + XFS.max_dirblockaddr] ; to read dirblock immediately
mov ecx, [esp + 24] ; start number
test ecx, ecx
jz .btreedir.skipped
; jmp .btreedir.skip
.btreedir.skip:
cmp ebx, [ebp + XFS.max_dirblockaddr]
jne @f
push ecx edx
mov ebx, [ebp + XFS.cur_block]
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jz .error
add eax, 1
adc edx, 0
mov dword[ebp + XFS.next_block_num + 0], eax
mov dword[ebp + XFS.next_block_num + 4], edx
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, sizeof.xfs_dir2_data_hdr
pop edx ecx
@@:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .btreedir.skip
@@:
movzx eax, [ebx + xfs_dir2_data_union.xentry.namelen]
lea ebx, [ebx + xfs_dir2_data_union.xentry.name + eax + 2] ; 2 for 'tag'
add ebx, 7
and ebx, not 7
dec ecx
jnz .btreedir.skip
.btreedir.skipped:
mov [ebp + XFS.entries_read], 0
mov ecx, [edx + 4] ; actually read entries
test ecx, ecx
jz .quit
add edx, 32
.btreedir.next_entry:
;mov eax, [ebp + XFS.entries_read]
;DEBUGF 1,"next_extry: %d\n",eax
cmp ebx, [ebp + XFS.max_dirblockaddr]
jne .btreedir.process_current_block
push ecx edx
mov ebx, [ebp + XFS.cur_block]
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
pop edx ecx
jmp .quit
@@:
add eax, 1
adc edx, 0
mov dword[ebp + XFS.next_block_num + 0], eax
mov dword[ebp + XFS.next_block_num + 4], edx
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, sizeof.xfs_dir2_data_hdr
pop edx ecx
.btreedir.process_current_block:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .btreedir.next_entry
@@:
push eax ebx ecx edx esi
mov edi, edx
mov edx, dword[ebx + xfs_dir2_data_union.xentry.inumber + 0]
mov eax, dword[ebx + xfs_dir2_data_union.xentry.inumber + 4]
bswap edx
bswap eax
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ecx ebx eax
jnz .error
push ecx
mov ecx, [esp + 44]
mov [edx + 4], ecx
lea edi, [edx + 40]
movzx ecx, byte[ebx + xfs_dir2_data_union.xentry.namelen]
lea esi, [ebx + xfs_dir2_data_union.xentry.name]
;DEBUGF 1,"filename: |%s|\n",esi
rep movsb
pop ecx
mov word[edi], 0
lea ebx, [esi + 2] ; skip 'tag'
add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes
and ebx, not 7
add edx, 304
inc [ebp + XFS.entries_read]
dec ecx
jnz .btreedir.next_entry
jmp .quit
.quit:
pop edi esi edx ecx
add esp, 4 ; pop vars
xor eax, eax
; mov ebx, [esp + 8]
mov ebx, [ebp + XFS.entries_read]
DEBUGF 1,"xfs_dir_get_bdfes done: %d\n",ebx
ret 20
.error:
pop edi esi edx ecx
add esp, 4 ; pop vars
mov eax, ERROR_FS_FAIL
movi ebx, -1
ret 20
;----------------------------------------------------------------
; push inode_hi
; push inode_lo
; push name
;----------------------------------------------------------------
xfs_get_inode_short:
; this function searches for the file in _current_ dir
; it is called recursively for all the subdirs /path/to/my/file
;DEBUGF 1,"xfs_get_inode_short: %s\n",[esp+4]
mov esi, [esp + 4] ; name
movzx eax, word[esi]
cmp eax, '.' ; current dir; it is already read, just return
je .quit
cmp eax, './' ; same thing
je .quit
; read inode
mov eax, [esp + 8] ; inode_lo
mov edx, [esp + 12] ; inode_hi
stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode]
test eax, eax
movi eax, ERROR_FS_FAIL
jnz .error
; find file name in directory
; switch directory ondisk format
mov ebx, edx
mov [ebp + XFS.cur_inode_save], ebx
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_LOCAL
jne .not_shortdir
;DEBUGF 1,"dir: shortdir\n"
jmp .shortdir
.not_shortdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_blockdir
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
cmp eax, 1
jne .not_blockdir
jmp .blockdir
.not_blockdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_leafdir
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
cmp eax, 4
ja .not_leafdir
jmp .leafdir
.not_leafdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_nodedir
jmp .nodedir
.not_nodedir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE
jne .not_btreedir
jmp .btreedir
.not_btreedir:
DEBUGF 1,"NOT IMPLEMENTED: DIR FORMAT\n"
jmp .error
.shortdir:
.shortdir.check_parent:
; parent inode number in shortform directories is always implicit, check this case
mov eax, [esi]
and eax, 0x00ffffff
cmp eax, '..'
je .shortdir.parent2
cmp eax, '../'
je .shortdir.parent3
jmp .shortdir.common
.shortdir.parent3:
inc esi
.shortdir.parent2:
add esi, 2
add ebx, xfs_inode.di_u
stdcall xfs_get_inode_number_sf, dword[ebx + xfs_dir2_sf_hdr.count], dword[ebx + xfs_dir2_sf_hdr.parent + 4], dword[ebx + xfs_dir2_sf_hdr.parent]
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
; not a parent inode?
; search in the list, all the other files are stored uniformly
.shortdir.common:
mov eax, 4
movzx edx, word[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count] ; read count (byte) and i8count (byte) at once
test dl, dl ; is count zero?
jnz @f
shr edx, 8 ; use i8count
add eax, eax ; inode_num size
@@:
lea edi, [ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent + eax]
.next_name:
movzx ecx, byte[edi + xfs_dir2_sf_entry.namelen]
add edi, xfs_dir2_sf_entry.name
mov esi, [esp + 4]
;DEBUGF 1,"esi: %s\n",esi
;DEBUGF 1,"edi: %s\n",edi
repe cmpsb
jne @f
cmp byte[esi], 0 ; HINT: use adc here?
je .found
cmp byte[esi], '/'
je .found_inc
@@:
add edi, ecx
add edi, eax
dec edx
jnz .next_name
movi eax, ERROR_FILE_NOT_FOUND
jmp .error
.found_inc: ; increment esi to skip '/' symbol
; this means esi always points to valid file name or zero terminator byte
inc esi
.found:
stdcall xfs_get_inode_number_sf, dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count], [edi + 4], [edi]
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
.blockdir:
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
stdcall xfs_extent_unpack, eax
stdcall xfs_read_dirblock, dword[ebp + XFS.extent.br_startblock + 0], dword[ebp + XFS.extent.br_startblock + 4], [ebp + XFS.cur_dirblock]
test eax, eax
jnz .error
;DEBUGF 1,"dirblock signature: %s\n",[ebp+XFS.cur_dirblock]
mov ebx, [ebp + XFS.cur_dirblock]
mov eax, [ebp + XFS.dirblocksize]
mov eax, [ebx + eax - sizeof.xfs_dir2_block_tail + xfs_dir2_block_tail.count]
; note that we don't subtract xfs_dir2_block_tail.stale here,
; since we need the number of leaf entries rather than file number
bswap eax
add ebx, [ebp + XFS.dirblocksize]
; mov ecx, sizeof.xfs_dir2_leaf_entry
imul ecx, eax, sizeof.xfs_dir2_leaf_entry
sub ebx, sizeof.xfs_dir2_block_tail
sub ebx, ecx
shr ecx, 3
push ecx ; for xfs_get_inode_by_hash
push ebx ; for xfs_get_inode_by_hash
mov edi, esi
xor eax, eax
mov ecx, 4096 ; MAX_PATH_LEN
repne scasb
movi eax, ERROR_FS_FAIL
jne .error
neg ecx
add ecx, 4096 ; MAX_PATH_LEN
dec ecx
mov edx, ecx
;DEBUGF 1,"strlen total : %d\n",edx
mov edi, esi
mov eax, '/'
mov ecx, edx
repne scasb
jne @f
inc ecx
@@:
neg ecx
add ecx, edx
;DEBUGF 1,"strlen current: %d\n",ecx
stdcall xfs_hashname, esi, ecx
add esi, ecx
cmp byte[esi], '/'
jne @f
inc esi
@@:
;DEBUGF 1,"hashed: 0x%x\n",eax
; bswap eax
stdcall xfs_get_addr_by_hash
bswap eax
;DEBUGF 1,"got address: 0x%x\n",eax
cmp eax, -1
jne @f
movi eax, ERROR_FILE_NOT_FOUND
mov ebx, -1
jmp .error
@@:
shl eax, 3
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, eax
mov edx, [ebx + 0]
mov eax, [ebx + 4]
bswap edx
bswap eax
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
.leafdir:
;DEBUGF 1,"dirblock signature: %s\n",[ebp+XFS.cur_dirblock]
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, [ebp + XFS.dir2_leaf_offset_blocks], 0, edx, -1, -1
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jz .error
mov ebx, [ebp + XFS.cur_dirblock]
movzx eax, [ebx + xfs_dir2_leaf.hdr.count]
; note that we don't subtract xfs_dir2_leaf.hdr.stale here,
; since we need the number of leaf entries rather than file number
xchg al, ah
add ebx, xfs_dir2_leaf.ents
; imul ecx, eax, sizeof.xfs_dir2_leaf_entry
; shr ecx, 3
push eax ; for xfs_get_addr_by_hash: len
push ebx ; for xfs_get_addr_by_hash: base
mov edi, esi
xor eax, eax
mov ecx, 4096 ; MAX_PATH_LEN
repne scasb
movi eax, ERROR_FS_FAIL
jne .error
neg ecx
add ecx, 4096
dec ecx
mov edx, ecx
;DEBUGF 1,"strlen total : %d\n",edx
mov edi, esi
mov eax, '/'
mov ecx, edx
repne scasb
jne @f
inc ecx
@@:
neg ecx
add ecx, edx
;DEBUGF 1,"strlen current: %d\n",ecx
stdcall xfs_hashname, esi, ecx
add esi, ecx
cmp byte[esi], '/'
jne @f
inc esi
@@:
;DEBUGF 1,"hashed: 0x%x\n",eax
stdcall xfs_get_addr_by_hash
bswap eax
;DEBUGF 1,"got address: 0x%x\n",eax
cmp eax, -1
jne @f
movi eax, ERROR_FILE_NOT_FOUND
mov ebx, -1
jmp .error
@@:
mov ebx, [ebp + XFS.cur_inode_save]
push esi edi
xor edi, edi
mov esi, eax
shld edi, esi, 3 ; get offset
shl esi, 3 ; 2^3 = 8 byte align
mov edx, esi
mov ecx, [ebp + XFS.dirblklog]
add ecx, [ebp + XFS.blocklog]
mov eax, 1
shl eax, cl
dec eax
and edx, eax
push edx
shrd esi, edi, cl
shr edi, cl
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, esi, edi, edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
pop edx
pop edi esi
mov ecx, eax
and ecx, edx
inc ecx
jz .error
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, edx
mov edx, [ebx + 0]
mov eax, [ebx + 4]
bswap edx
bswap eax
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
.nodedir:
;DEBUGF 1,"lookupdir: node\n"
mov [ebp + XFS.cur_inode_save], ebx
mov edi, esi
xor eax, eax
mov ecx, 4096 ; MAX_PATH_LEN
repne scasb
movi eax, ERROR_FS_FAIL
jne .error
neg ecx
add ecx, 4096 ; MAX_PATH_LEN
dec ecx
mov edx, ecx
;DEBUGF 1,"strlen total : %d\n",edx
mov edi, esi
mov eax, '/'
mov ecx, edx
repne scasb
jne @f
inc ecx
@@:
neg ecx
add ecx, edx
;DEBUGF 1,"strlen current: %d\n",ecx
stdcall xfs_hashname, esi, ecx
add esi, ecx
cmp byte[esi], '/'
jne @f
inc esi
@@:
;DEBUGF 1,"hashed: 0x%x\n",eax
push edi edx
mov edi, eax
mov [ebp + XFS.entries_read], 0
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_dir2_lookupdir_node, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks], edi
pop edx edi
test eax, eax
jnz .error
bswap ecx
;DEBUGF 1,"got address: 0x%x\n",ecx
mov ebx, [ebp + XFS.cur_inode_save]
push esi edi
xor edi, edi
mov esi, ecx
shld edi, esi, 3 ; get offset
shl esi, 3 ; 8 byte align
mov edx, esi
mov ecx, [ebp + XFS.dirblklog]
add ecx, [ebp + XFS.blocklog]
mov eax, 1
shl eax, cl
dec eax
and edx, eax
push edx
shrd esi, edi, cl
shr edi, cl
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, esi, edi, edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
pop edx
pop edi esi
mov ecx, eax
and ecx, edx
inc ecx
jz .error
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, edx
mov edx, [ebx + 0]
mov eax, [ebx + 4]
bswap edx
bswap eax
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
.btreedir:
DEBUGF 1,"lookupdir: btree\n"
mov [ebp + XFS.cur_inode_save], ebx
push ebx edx
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
mov [ebp + XFS.ro_nextents], eax
mov eax, [ebp + XFS.inodesize]
sub eax, xfs_inode.di_u
sub eax, sizeof.xfs_bmdr_block
shr eax, 4 ; FIXME forkoff
;DEBUGF 1,"maxnumresc: %d\n",eax
mov edx, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 0]
mov eax, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 4]
bswap eax
bswap edx
mov ebx, [ebp + XFS.cur_block]
;DEBUGF 1,"read_block: %x %x ",edx,eax
stdcall xfs_read_block
pop edx ebx
test eax, eax
jnz .error
;DEBUGF 1,"ok\n"
mov ebx, [ebp + XFS.cur_block]
mov edi, esi
xor eax, eax
mov ecx, 4096 ; MAX_PATH_LEN
repne scasb
movi eax, ERROR_FS_FAIL
jne .error
neg ecx
add ecx, 4096
dec ecx
mov edx, ecx
DEBUGF 1,"strlen total : %d\n",edx
mov edi, esi
mov eax, '/'
mov ecx, edx
repne scasb
jne @f
inc ecx
@@:
neg ecx
add ecx, edx
DEBUGF 1,"strlen current: %d\n",ecx
stdcall xfs_hashname, esi, ecx
add esi, ecx
cmp byte[esi], '/'
jne @f
inc esi
@@:
DEBUGF 1,"hashed: 0x%x\n",eax
push edi edx
mov edi, eax
mov [ebp + XFS.entries_read], 0
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
;push eax
;mov eax, [ebp + XFS.dir2_leaf_offset_blocks]
;DEBUGF 1,": 0x%x %d\n",eax,eax
;pop eax
stdcall xfs_dir2_lookupdir_node, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks], edi
pop edx edi
test eax, eax
jnz .error
bswap ecx
DEBUGF 1,"got address: 0x%x\n",ecx
mov ebx, [ebp + XFS.cur_block]
push esi edi
xor edi, edi
mov esi, ecx
shld edi, esi, 3 ; get offset
shl esi, 3
mov edx, esi
mov ecx, [ebp + XFS.dirblklog]
add ecx, [ebp + XFS.blocklog]
mov eax, 1
shl eax, cl
dec eax
and edx, eax
push edx
shrd esi, edi, cl
shr edi, cl
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
stdcall xfs_extent_list_read_dirblock, eax, esi, edi, edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
pop edx
pop edi esi
mov ecx, eax
and ecx, edx
inc ecx
jz .error
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, edx
mov edx, [ebx + 0]
mov eax, [ebx + 4]
bswap edx
bswap eax
DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
.quit:
ret 12
.error:
xor eax, eax
mov edx, eax
ret 12
;----------------------------------------------------------------
; push name
; call xfs_get_inode
; test eax, eax
;----------------------------------------------------------------
xfs_get_inode:
; call xfs_get_inode_short until file is found / error returned
;DEBUGF 1,"getting inode of: %s\n",[esp+4]
push ebx esi edi
; start from the root inode
mov edx, dword[ebp + XFS.rootino + 4] ; hi
mov eax, dword[ebp + XFS.rootino + 0] ; lo
mov esi, [esp + 16] ; name
.next_dir:
cmp byte[esi], 0
je .found
;DEBUGF 1,"next_level: |%s|\n",esi
stdcall xfs_get_inode_short, esi, eax, edx
test edx, edx
jnz @f
test eax, eax
jz .error
@@:
jmp .next_dir ; file name found, go to next directory level
.found:
.quit:
pop edi esi ebx
ret 4
.error:
pop edi esi ebx
xor eax, eax
mov edx, eax
ret 4
;----------------------------------------------------------------
; xfs_ReadFolder - XFS implementation of reading a folder
; in: ebp = pointer to XFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
xfs_ReadFolder:
; to read folder
; 1. lock partition
; 2. find inode number
; 3. read this inode
; 4. get bdfe's
; 5. unlock partition
; 1.
call xfs_lock
push ecx edx esi edi
; 2.
push ebx esi edi
add esi, [esp + 32] ; directory name
;DEBUGF 1,"xfs_ReadFolder: |%s|\n",esi
stdcall xfs_get_inode, esi
pop edi esi ebx
mov ecx, edx
or ecx, eax
jnz @f
movi eax, ERROR_FILE_NOT_FOUND
@@:
; 3.
stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode]
test eax, eax
movi eax, ERROR_FS_FAIL
jnz .error
; 4.
mov eax, [ebx + 8] ; encoding
and eax, 1
stdcall xfs_dir_get_bdfes, [ebx + 4], [ebx + 12], [ebx + 16], edx, eax
test eax, eax
jnz .error
.quit:
;DEBUGF 1,"\n\n"
pop edi esi edx ecx
; 5.
call xfs_unlock
xor eax, eax
ret
.error:
;DEBUGF 1,"\n\n"
pop edi esi edx ecx
push eax
call xfs_unlock
pop eax
ret
;----------------------------------------------------------------
; push inode_num_hi
; push inode_num_lo
; push [count]
; call xfs_get_inode_number_sf
;----------------------------------------------------------------
xfs_get_inode_number_sf:
; inode numbers in short form directories may be 4 or 8 bytes long
; determine the length in run time and read inode number at given address
cmp byte[esp + 4 + xfs_dir2_sf_hdr.i8count], 0 ; i8count == 0 means 4 byte per inode number
je .i4bytes
.i8bytes:
mov edx, [esp + 12] ; hi
mov eax, [esp + 8] ; lo
bswap edx ; big endian
bswap eax
ret 12
.i4bytes:
xor edx, edx ; no hi
mov eax, [esp + 12] ; hi = lo
bswap eax ; big endian
ret 12
;----------------------------------------------------------------
; push dest
; push src
; call xfs_get_inode_info
;----------------------------------------------------------------
xfs_get_inode_info:
; get access time and other file properties
; useful for browsing directories
; called for each dir entry
;DEBUGF 1,"get_inode_info\n"
xor eax, eax
mov edx, [esp + 4]
movzx ecx, word[edx + xfs_inode.di_core.di_mode]
xchg cl, ch
;DEBUGF 1,"di_mode: %x\n",ecx
test ecx, S_IFDIR ; directory?
jz @f
mov eax, 0x10 ; set directory flag
@@:
mov edi, [esp + 8]
mov [edi + 0], eax
mov eax, dword[edx + xfs_inode.di_core.di_size + 0] ; hi
bswap eax
mov dword[edi + 36], eax ; file size hi
;DEBUGF 1,"file_size hi: %d\n",eax
mov eax, dword[edx + xfs_inode.di_core.di_size + 4] ; lo
bswap eax
mov dword[edi + 32], eax ; file size lo
;DEBUGF 1,"file_size lo: %d\n",eax
add edi, 8
mov eax, [edx + xfs_inode.di_core.di_ctime.t_sec]
bswap eax
push edx
xor edx, edx
add eax, 3054539008 ;(369 * 365 + 89) * 24 * 3600
adc edx, 2
call ntfs_datetime_to_bdfe.sec
pop edx
mov eax, [edx + xfs_inode.di_core.di_atime.t_sec]
bswap eax
push edx
xor edx, edx
add eax, 3054539008 ;(369 * 365 + 89) * 24 * 3600
adc edx, 2
call ntfs_datetime_to_bdfe.sec
pop edx
mov eax, [edx + xfs_inode.di_core.di_mtime.t_sec]
bswap eax
push edx
xor edx, edx
add eax, 3054539008 ;(369 * 365 + 89) * 24 * 3600
adc edx, 2
call ntfs_datetime_to_bdfe.sec
pop edx
.quit:
xor eax, eax
ret 8
.error:
movi eax, ERROR_FS_FAIL
ret 8
;----------------------------------------------------------------
; push extent_data
; call xfs_extent_unpack
;----------------------------------------------------------------
xfs_extent_unpack:
; extents come as packet 128bit bitfields
; lets unpack them to access internal fields
; write result to the XFS.extent structure
push eax ebx ecx edx
mov ebx, [esp + 20]
xor eax, eax
mov edx, [ebx + 0]
bswap edx
test edx, 0x80000000 ; mask, see documentation
setnz al
mov [ebp + XFS.extent.br_state], eax
and edx, 0x7fffffff ; mask
mov eax, [ebx + 4]
bswap eax
shrd eax, edx, 9
shr edx, 9
mov dword[ebp + XFS.extent.br_startoff + 0], eax
mov dword[ebp + XFS.extent.br_startoff + 4], edx
mov edx, [ebx + 4]
mov eax, [ebx + 8]
mov ecx, [ebx + 12]
bswap edx
bswap eax
bswap ecx
and edx, 0x000001ff ; mask
shrd ecx, eax, 21
shrd eax, edx, 21
mov dword[ebp + XFS.extent.br_startblock + 0], ecx
mov dword[ebp + XFS.extent.br_startblock + 4], eax
mov eax, [ebx + 12]
bswap eax
and eax, 0x001fffff ; mask
mov [ebp + XFS.extent.br_blockcount], eax
pop edx ecx ebx eax
;DEBUGF 1,"extent.br_startoff : %d %d\n",[ebp+XFS.extent.br_startoff+4],[ebp+XFS.extent.br_startoff+0]
;DEBUGF 1,"extent.br_startblock: %d %d\n",[ebp+XFS.extent.br_startblock+4],[ebp+XFS.extent.br_startblock+0]
;DEBUGF 1,"extent.br_blockcount: %d\n",[ebp+XFS.extent.br_blockcount]
;DEBUGF 1,"extent.br_state : %d\n",[ebp+XFS.extent.br_state]
ret 4
;----------------------------------------------------------------
; push namelen
; push name
; call xfs_hashname
;----------------------------------------------------------------
xfs_hashname: ; xfs_da_hashname
; simple hash function
; never fails)
push ecx esi
xor eax, eax
mov esi, [esp + 12] ; name
mov ecx, [esp + 16] ; namelen
;mov esi, '.'
;mov ecx, 1
;DEBUGF 1,"hashname: %d %s\n",ecx,esi
@@:
rol eax, 7
xor al, [esi]
add esi, 1
loop @b
pop esi ecx
ret 8
;----------------------------------------------------------------
; push len
; push base
; eax -- hash value
; call xfs_get_addr_by_hash
;----------------------------------------------------------------
xfs_get_addr_by_hash:
; look for the directory entry offset by its file name hash
; allows fast file search for block, leaf and node directories
; binary (ternary) search
;DEBUGF 1,"get_addr_by_hash\n"
push ebx esi
mov ebx, [esp + 12] ; left
mov edx, [esp + 16] ; len
.next:
mov ecx, edx
; jecxz .error
test ecx, ecx
jz .error
shr ecx, 1
mov esi, [ebx + ecx*8 + xfs_dir2_leaf_entry.hashval]
bswap esi
;DEBUGF 1,"cmp 0x%x",esi
cmp eax, esi
jb .below
ja .above
mov eax, [ebx + ecx*8 + xfs_dir2_leaf_entry.address]
pop esi ebx
ret 8
.below:
;DEBUGF 1,"b\n"
mov edx, ecx
jmp .next
.above:
;DEBUGF 1,"a\n"
lea ebx, [ebx + ecx*8 + 8]
sub edx, ecx
dec edx
jmp .next
.error:
mov eax, -1
pop esi ebx
ret 8
;----------------------------------------------------------------
; xfs_GetFileInfo - XFS implementation of getting file info
; in: ebp = pointer to XFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
xfs_GetFileInfo:
; lock partition
; get inode number by file name
; read inode
; get info
; unlock partition
push ecx edx esi edi
call xfs_lock
add esi, [esp + 20] ; name
;DEBUGF 1,"xfs_GetFileInfo: |%s|\n",esi
stdcall xfs_get_inode, esi
mov ecx, edx
or ecx, eax
jnz @f
movi eax, ERROR_FILE_NOT_FOUND
jmp .error
@@:
stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode]
test eax, eax
movi eax, ERROR_FS_FAIL
jnz .error
stdcall xfs_get_inode_info, edx, [ebx + 16]
.quit:
call xfs_unlock
pop edi esi edx ecx
xor eax, eax
;DEBUGF 1,"quit\n\n"
ret
.error:
call xfs_unlock
pop edi esi edx ecx
;DEBUGF 1,"error\n\n"
ret
;----------------------------------------------------------------
; xfs_Read - XFS implementation of reading a file
; in: ebp = pointer to XFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
xfs_Read:
push ebx ecx edx esi edi
call xfs_lock
add esi, [esp + 24]
;DEBUGF 1,"xfs_Read: %d %d |%s|\n",[ebx+4],[ebx+12],esi
stdcall xfs_get_inode, esi
mov ecx, edx
or ecx, eax
jnz @f
movi eax, ERROR_FILE_NOT_FOUND
jmp .error
@@:
stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode]
test eax, eax
movi eax, ERROR_FS_FAIL
jnz .error
mov [ebp + XFS.cur_inode_save], edx
cmp byte[edx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_extent_list
jmp .extent_list
.not_extent_list:
cmp byte[edx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE
jne .not_btree
jmp .btree
.not_btree:
DEBUGF 1,"XFS: NOT IMPLEMENTED: FILE FORMAT\n"
movi eax, ERROR_FS_FAIL
jmp .error
.extent_list:
mov ecx, [ebx + 12] ; bytes to read
mov edi, [ebx + 16] ; buffer for data
mov esi, [ebx + 8] ; offset_hi
mov ebx, [ebx + 4] ; offset_lo
mov eax, dword[edx + xfs_inode.di_core.di_size + 4] ; lo
bswap eax
mov dword[ebp + XFS.bytes_left_in_file + 0], eax ; lo
mov eax, dword[edx + xfs_inode.di_core.di_size + 0] ; hi
bswap eax
mov dword[ebp + XFS.bytes_left_in_file + 4], eax ; hi
mov eax, [edx + xfs_inode.di_core.di_nextents]
bswap eax
mov [ebp + XFS.left_extents], eax
mov dword[ebp + XFS.bytes_read], 0 ; actually read bytes
xor eax, eax ; extent offset in list
.extent_list.next_extent:
;DEBUGF 1,"extent_list.next_extent, eax: 0x%x\n",eax
;DEBUGF 1,"bytes_to_read: %d\n",ecx
;DEBUGF 1,"cur file offset: %d %d\n",esi,ebx
;DEBUGF 1,"esp: 0x%x\n",esp
cmp [ebp + XFS.left_extents], 0
jne @f
test ecx, ecx
jz .quit
movi eax, ERROR_END_OF_FILE
jmp .error
@@:
push eax
lea eax, [edx + xfs_inode.di_u + eax + xfs_bmbt_rec.l0]
stdcall xfs_extent_unpack, eax
pop eax
dec [ebp + XFS.left_extents]
add eax, sizeof.xfs_bmbt_rec
push eax ebx ecx edx esi
mov ecx, [ebp + XFS.blocklog]
shrd ebx, esi, cl
shr esi, cl
cmp esi, dword[ebp + XFS.extent.br_startoff + 4]
jb .extent_list.to_hole ; handle sparse files
ja @f
cmp ebx, dword[ebp + XFS.extent.br_startoff + 0]
jb .extent_list.to_hole ; handle sparse files
je .extent_list.to_extent ; read from the start of current extent
@@:
xor edx, edx
mov eax, [ebp + XFS.extent.br_blockcount]
add eax, dword[ebp + XFS.extent.br_startoff + 0]
adc edx, dword[ebp + XFS.extent.br_startoff + 4]
;DEBUGF 1,"br_startoff: %d %d\n",edx,eax
cmp esi, edx
ja .extent_list.skip_extent
jb .extent_list.to_extent
cmp ebx, eax
jae .extent_list.skip_extent
jmp .extent_list.to_extent
.extent_list.to_hole:
;DEBUGF 1,"extent_list.to_hole\n"
pop esi edx ecx ebx eax
jmp .extent_list.read_hole
.extent_list.to_extent:
;DEBUGF 1,"extent_list.to_extent\n"
pop esi edx ecx ebx eax
jmp .extent_list.read_extent
.extent_list.skip_extent:
;DEBUGF 1,"extent_list.skip_extent\n"
pop esi edx ecx ebx eax
jmp .extent_list.next_extent
.extent_list.read_hole:
;DEBUGF 1,"hole: offt: 0x%x%x ",esi,ebx
push eax edx
mov eax, dword[ebp + XFS.extent.br_startoff + 0]
mov edx, dword[ebp + XFS.extent.br_startoff + 4]
push esi ebx
mov ebx, ecx
sub eax, ebx ; get hole_size, it is 64 bit
sbb edx, 0 ; now edx:eax contains the size of hole
;DEBUGF 1,"size: 0x%x%x\n",edx,eax
jnz @f ; if hole size >= 2^32, write bytes_to_read zero bytes
cmp eax, ecx ; if hole size >= bytes_to_read, write bytes_to_read zeros
jae @f
mov ecx, eax ; if hole is < than bytes_to_read, write hole size zeros
@@:
sub ebx, ecx ; bytes_to_read - hole_size = left_to_read
add dword[esp + 0], ecx ; update pushed file offset
adc dword[esp + 4], 0
xor eax, eax ; hole is made of zeros
rep stosb
mov ecx, ebx
pop ebx esi
test ecx, ecx ; all requested bytes are read?
pop edx eax
jz .quit
jmp .extent_list.read_extent ; continue from the start of unpacked extent
.extent_list.read_extent:
;DEBUGF 1,"extent_list.read_extent\n"
push eax ebx ecx edx esi
mov eax, ebx
mov edx, esi
mov ecx, [ebp + XFS.blocklog]
shrd eax, edx, cl
shr edx, cl
sub eax, dword[ebp + XFS.extent.br_startoff + 0] ; skip esi:ebx ?
sbb edx, dword[ebp + XFS.extent.br_startoff + 4]
sub [ebp + XFS.extent.br_blockcount], eax
add dword[ebp + XFS.extent.br_startblock + 0], eax
adc dword[ebp + XFS.extent.br_startblock + 4], 0
.extent_list.read_extent.next_block:
;DEBUGF 1,"extent_list.read_extent.next_block\n"
cmp [ebp + XFS.extent.br_blockcount], 0 ; out of blocks in current extent?
jne @f
pop esi edx ecx ebx eax
jmp .extent_list.next_extent ; go to next extent
@@:
mov eax, dword[ebp + XFS.extent.br_startblock + 0]
mov edx, dword[ebp + XFS.extent.br_startblock + 4]
push ebx
mov ebx, [ebp + XFS.cur_block]
;DEBUGF 1,"read block: 0x%x%x\n",edx,eax
stdcall xfs_read_block
test eax, eax
pop ebx
jz @f
pop esi edx ecx ebx eax
movi eax, ERROR_FS_FAIL
jmp .error
@@:
dec [ebp + XFS.extent.br_blockcount]
add dword[ebp + XFS.extent.br_startblock + 0], 1
adc dword[ebp + XFS.extent.br_startblock + 4], 0
mov esi, [ebp + XFS.cur_block]
mov ecx, [ebp + XFS.blocklog]
mov eax, 1
shl eax, cl
dec eax ; get blocklog mask
and eax, ebx ; offset in current block
add esi, eax
neg eax
add eax, [ebp + XFS.blocksize]
mov ecx, [esp + 8] ; pushed ecx, bytes_to_read
cmp ecx, eax ; is current block enough?
jbe @f ; if so, read bytes_to_read bytes
mov ecx, eax ; otherwise read the block up to the end
@@:
sub [esp + 8], ecx ; left_to_read
add [esp + 12], ecx ; update current file offset, pushed ebx
sub dword[ebp + XFS.bytes_left_in_file + 0], ecx
sbb dword[ebp + XFS.bytes_left_in_file + 4], 0
jnc @f
add dword[ebp + XFS.bytes_left_in_file + 0], ecx
mov ecx, dword[ebp + XFS.bytes_left_in_file + 0]
mov dword[ebp + XFS.bytes_left_in_file + 0], 0
mov dword[ebp + XFS.bytes_left_in_file + 4], 0
@@:
add [ebp + XFS.bytes_read], ecx
adc [esp + 0], dword 0 ; pushed esi
;DEBUGF 1,"read data: %d\n",ecx
rep movsb
mov ecx, [esp + 8]
;DEBUGF 1,"left_to_read: %d\n",ecx
xor ebx, ebx
test ecx, ecx
jz @f
cmp dword[ebp + XFS.bytes_left_in_file + 4], 0
jne .extent_list.read_extent.next_block
cmp dword[ebp + XFS.bytes_left_in_file + 0], 0
jne .extent_list.read_extent.next_block
@@:
pop esi edx ecx ebx eax
jmp .quit
.btree:
mov ecx, [ebx + 12] ; bytes to read
mov [ebp + XFS.bytes_to_read], ecx
mov edi, [ebx + 16] ; buffer for data
mov esi, [ebx + 8] ; offset_hi
mov ebx, [ebx + 4] ; offset_lo
mov dword[ebp + XFS.file_offset + 0], ebx
mov dword[ebp + XFS.file_offset + 4], esi
mov [ebp + XFS.buffer_pos], edi
mov eax, dword[edx + xfs_inode.di_core.di_size + 4] ; lo
bswap eax
mov dword[ebp + XFS.bytes_left_in_file + 0], eax ; lo
mov eax, dword[edx + xfs_inode.di_core.di_size + 0] ; hi
bswap eax
mov dword[ebp + XFS.bytes_left_in_file + 4], eax ; hi
mov eax, [edx + xfs_inode.di_core.di_nextents]
bswap eax
mov [ebp + XFS.left_extents], eax
mov dword[ebp + XFS.bytes_read], 0 ; actually read bytes
push ebx ecx edx esi edi
mov [ebp + XFS.eof], 0
mov eax, dword[ebp + XFS.file_offset + 0]
mov edx, dword[ebp + XFS.file_offset + 4]
add eax, [ebp + XFS.bytes_to_read]
adc edx, 0
sub eax, dword[ebp + XFS.bytes_left_in_file + 0]
sbb edx, dword[ebp + XFS.bytes_left_in_file + 4]
jc @f ; file_offset + bytes_to_read < file_size
jz @f ; file_offset + bytes_to_read = file_size
mov [ebp + XFS.eof], 1
cmp edx, 0
jne .error.eof
sub dword[ebp + XFS.bytes_to_read], eax
jc .error.eof
jz .error.eof
@@:
stdcall xfs_btree_read, 0, 0, 1
pop edi esi edx ecx ebx
test eax, eax
jnz .error
cmp [ebp + XFS.eof], 1
jne .quit
jmp .error.eof
.quit:
call xfs_unlock
pop edi esi edx ecx ebx
xor eax, eax
mov ebx, [ebp + XFS.bytes_read]
;DEBUGF 1,"quit: %d\n\n",ebx
ret
.error.eof:
movi eax, ERROR_END_OF_FILE
.error:
;DEBUGF 1,"error\n\n"
call xfs_unlock
pop edi esi edx ecx ebx
mov ebx, [ebp + XFS.bytes_read]
ret
;----------------------------------------------------------------
; push max_offset_hi
; push max_offset_lo
; push nextents
; push block_number_hi
; push block_number_lo
; push extent_list
; -1 / read block number
;----------------------------------------------------------------
xfs_extent_list_read_dirblock: ; skips holes
;DEBUGF 1,"xfs_extent_list_read_dirblock\n"
push ebx esi edi
;mov eax, [esp+28]
;DEBUGF 1,"nextents: %d\n",eax
;mov eax, [esp+20]
;mov edx, [esp+24]
;DEBUGF 1,"block_number: 0x%x%x\n",edx,eax
;mov eax, [esp+32]
;mov edx, [esp+36]
;DEBUGF 1,"max_addr : 0x%x%x\n",edx,eax
mov ebx, [esp + 16]
mov esi, [esp + 20]
mov edi, [esp + 24]
; mov ecx, [esp + 28] ; nextents
.next_extent:
;DEBUGF 1,"next_extent\n"
dec dword[esp + 28]
js .error
stdcall xfs_extent_unpack, ebx
add ebx, sizeof.xfs_bmbt_rec ; next extent
mov edx, dword[ebp + XFS.extent.br_startoff + 4]
mov eax, dword[ebp + XFS.extent.br_startoff + 0]
cmp edx, [esp + 36] ; max_offset_hi
ja .error
jb @f
cmp eax, [esp + 32] ; max_offset_lo
jae .error
@@:
cmp edi, edx
jb .hole
ja .check_count
cmp esi, eax
jb .hole
ja .check_count
jmp .read_block
.hole:
;DEBUGF 1,"hole\n"
mov esi, eax
mov edi, edx
jmp .read_block
.check_count:
;DEBUGF 1,"check_count\n"
add eax, [ebp + XFS.extent.br_blockcount]
adc edx, 0
cmp edi, edx
ja .next_extent
jb .read_block
cmp esi, eax
jae .next_extent
; jmp .read_block
.read_block:
;DEBUGF 1,"read_block\n"
push esi edi
sub esi, dword[ebp + XFS.extent.br_startoff + 0]
sbb edi, dword[ebp + XFS.extent.br_startoff + 4]
add esi, dword[ebp + XFS.extent.br_startblock + 0]
adc edi, dword[ebp + XFS.extent.br_startblock + 4]
stdcall xfs_read_dirblock, esi, edi, [ebp + XFS.cur_dirblock]
pop edx eax
.quit:
;DEBUGF 1,"xfs_extent_list_read_dirblock: quit\n"
pop edi esi ebx
ret 24
.error:
;DEBUGF 1,"xfs_extent_list_read_dirblock: error\n"
xor eax, eax
dec eax
mov edx, eax
pop edi esi ebx
ret 24
;----------------------------------------------------------------
; push dirblock_num
; push nextents
; push extent_list
;----------------------------------------------------------------
xfs_dir2_node_get_numfiles:
; unfortunately, we need to set 'total entries' field
; this often requires additional effort, since there is no such a number in most directory ondisk formats
;DEBUGF 1,"xfs_dir2_node_get_numfiles\n"
push ebx ecx edx esi edi
mov eax, [esp + 24]
mov edx, [esp + 28]
mov esi, [esp + 32]
stdcall xfs_extent_list_read_dirblock, eax, esi, 0, edx, -1, -1
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
movi eax, ERROR_FS_FAIL
jmp .error
@@:
mov ebx, [ebp + XFS.cur_dirblock]
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC
je .node
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC
je .leaf
mov eax, ERROR_FS_FAIL
jmp .error
.node:
;DEBUGF 1,".node\n"
mov edi, [ebx + xfs_da_intnode.hdr.info.forw]
bswap edi
mov eax, [esp + 24]
mov edx, [esp + 28]
mov esi, [ebx + xfs_da_intnode.btree.before]
bswap esi
stdcall xfs_dir2_node_get_numfiles, eax, edx, esi
test eax, eax
jnz .error
jmp .common
.leaf:
;DEBUGF 1,".leaf\n"
movzx ecx, word[ebx + xfs_dir2_leaf.hdr.count]
xchg cl, ch
movzx eax, word[ebx + xfs_dir2_leaf.hdr.stale]
xchg al, ah
sub ecx, eax
add [ebp + XFS.entries_read], ecx
mov edi, [ebx + xfs_dir2_leaf.hdr.info.forw]
bswap edi
jmp .common
.common:
test edi, edi
jz .quit
mov esi, edi
mov eax, [esp + 24]
mov edx, [esp + 28]
stdcall xfs_dir2_node_get_numfiles, eax, edx, esi
test eax, eax
jnz .error
jmp .quit
.quit:
;DEBUGF 1,".quit\n"
pop edi esi edx ecx ebx
xor eax, eax
ret 12
.error:
;DEBUGF 1,".error\n"
pop edi esi edx ecx ebx
movi eax, ERROR_FS_FAIL
ret 12
;----------------------------------------------------------------
; push hash
; push dirblock_num
; push nextents
; push extent_list
;----------------------------------------------------------------
xfs_dir2_lookupdir_node:
DEBUGF 1,"xfs_dir2_lookupdir_node\n"
push ebx edx esi edi
mov eax, [esp + 20]
mov edx, [esp + 24]
mov esi, [esp + 28]
DEBUGF 1,"read dirblock: 0x%x %d\n",esi,esi
stdcall xfs_extent_list_read_dirblock, eax, esi, 0, edx, -1, -1
DEBUGF 1,"dirblock read: 0x%x%x\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
movi eax, ERROR_FS_FAIL
jmp .error
@@:
DEBUGF 1,"checkpoint #1\n"
mov ebx, [ebp + XFS.cur_dirblock]
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC
je .node
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC
je .leaf
mov eax, ERROR_FS_FAIL
DEBUGF 1,"checkpoint #2\n"
jmp .error
.node:
DEBUGF 1,".node\n"
mov edi, [esp + 32] ; hash
movzx ecx, word[ebx + xfs_da_intnode.hdr.count]
xchg cl, ch
mov [ebp + XFS.left_leaves], ecx
xor ecx, ecx
.node.next_leaf:
mov esi, [ebx + xfs_da_intnode.btree + ecx*sizeof.xfs_da_node_entry + xfs_da_node_entry.hashval]
bswap esi
cmp edi, esi
jbe .node.leaf_found
inc ecx
cmp ecx, [ebp + XFS.left_leaves]
jne .node.next_leaf
mov eax, ERROR_FILE_NOT_FOUND
jmp .error
@@:
.node.leaf_found:
mov eax, [esp + 20]
mov edx, [esp + 24]
mov esi, [ebx + xfs_da_intnode.btree + ecx*sizeof.xfs_da_node_entry + xfs_da_node_entry.before]
bswap esi
stdcall xfs_dir2_lookupdir_node, eax, edx, esi, edi
test eax, eax
jz .quit
movi eax, ERROR_FILE_NOT_FOUND
jmp .error
.leaf:
DEBUGF 1,".leaf\n"
movzx ecx, [ebx + xfs_dir2_leaf.hdr.count]
xchg cl, ch
lea esi, [ebx + xfs_dir2_leaf.ents]
mov eax, [esp + 32]
stdcall xfs_get_addr_by_hash, esi, ecx
cmp eax, -1
je .error
mov ecx, eax
jmp .quit
.quit:
DEBUGF 1,".quit\n"
pop edi esi edx ebx
xor eax, eax
ret 16
.error:
DEBUGF 1,".error\n"
pop edi esi edx ebx
ret 16
;----------------------------------------------------------------
; push dirblock_num
; push nextents
; push extent_list
;----------------------------------------------------------------
xfs_dir2_btree_get_numfiles:
;DEBUGF 1,"xfs_dir2_node_get_numfiles\n"
push ebx ecx edx esi edi
mov eax, [esp + 24]
mov edx, [esp + 28]
mov esi, [esp + 32]
stdcall xfs_extent_list_read_dirblock, eax, esi, 0, edx, -1, -1
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
movi eax, ERROR_FS_FAIL
jmp .error
@@:
mov ebx, [ebp + XFS.cur_dirblock]
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC
je .node
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC
je .leaf
mov eax, ERROR_FS_FAIL
jmp .error
.node:
;DEBUGF 1,".node\n"
mov edi, [ebx + xfs_da_intnode.hdr.info.forw]
bswap edi
mov eax, [esp + 24]
mov edx, [esp + 28]
mov esi, [ebx + xfs_da_intnode.btree.before]
bswap esi
stdcall xfs_dir2_node_get_numfiles, eax, edx, esi
test eax, eax
jnz .error
jmp .common
.leaf:
;DEBUGF 1,".leaf\n"
movzx ecx, word[ebx + xfs_dir2_leaf.hdr.count]
xchg cl, ch
movzx eax, word[ebx + xfs_dir2_leaf.hdr.stale]
xchg al, ah
sub ecx, eax
add [ebp + XFS.entries_read], ecx
mov edi, [ebx + xfs_dir2_leaf.hdr.info.forw]
bswap edi
jmp .common
.common:
test edi, edi
jz .quit
mov esi, edi
mov eax, [esp + 24]
mov edx, [esp + 28]
stdcall xfs_dir2_node_get_numfiles, eax, edx, esi
test eax, eax
jnz .error
jmp .quit
.quit:
;DEBUGF 1,".quit\n"
pop edi esi edx ecx ebx
xor eax, eax
ret 12
.error:
;DEBUGF 1,".error\n"
pop edi esi edx ecx ebx
movi eax, ERROR_FS_FAIL
ret 12
;----------------------------------------------------------------
; push is_root
; push block_hi
; push block_lo
;----------------------------------------------------------------
xfs_btree_read:
push ebx ecx edx esi edi
cmp dword[esp + 32], 1 ; is root?
je .root
jmp .not_root
.root:
DEBUGF 1,".root\n"
mov ebx, [ebp + XFS.cur_inode_save]
add ebx, xfs_inode.di_u
movzx edx, [ebx + xfs_bmdr_block.bb_numrecs]
xchg dl, dh
dec edx
add ebx, sizeof.xfs_bmdr_block
xor eax, eax
dec eax
.root.next_key:
DEBUGF 1,".root.next_key\n"
cmp [ebp + XFS.bytes_to_read], 0
je .quit
inc eax
cmp eax, edx ; out of keys?
ja .root.key_found ; there is no length field, so try the last key
lea edi, [ebx + sizeof.xfs_bmbt_key*eax + 0]
lea esi, [ebx + sizeof.xfs_bmbt_key*eax + 4]
bswap edi
bswap esi
mov ecx, [ebp + XFS.blocklog]
shld edi, esi, cl
shl esi, cl
cmp edi, dword[ebp + XFS.file_offset + 4]
ja .root.prev_or_hole
jb .root.next_key
cmp esi, dword[ebp + XFS.file_offset + 0]
ja .root.prev_or_hole
jb .root.next_key
jmp .root.key_found
.root.prev_or_hole:
DEBUGF 1,".root.prev_or_hole\n"
test eax, eax
jz .root.hole
dec eax
jmp .root.key_found
.root.hole:
DEBUGF 1,".root.hole\n"
push eax edx esi edi
mov ecx, [ebp + XFS.blocklog]
shld edi, esi, cl
shl esi, cl
sub esi, dword[ebp + XFS.file_offset + 0]
sbb edi, dword[ebp + XFS.file_offset + 4]
mov ecx, [ebp + XFS.bytes_to_read]
cmp edi, 0 ; hole size >= 2^32
jne @f
cmp ecx, esi
jbe @f
mov ecx, esi
@@:
add dword[ebp + XFS.file_offset + 0], ecx
adc dword[ebp + XFS.file_offset + 4], 0
sub [ebp + XFS.bytes_to_read], ecx
xor eax, eax
mov edi, [ebp + XFS.buffer_pos]
rep stosb
mov [ebp + XFS.buffer_pos], edi
pop edi esi edx eax
jmp .root.next_key
.root.key_found:
DEBUGF 1,".root.key_found\n"
mov edx, [ebp + XFS.cur_inode_save]
mov eax, [ebp + XFS.inodesize]
sub eax, xfs_inode.di_u
cmp [edx + xfs_inode.di_core.di_forkoff], 0
je @f
movzx eax, [edx + xfs_inode.di_core.di_forkoff]
shl eax, XFS_DIR2_DATA_ALIGN_LOG ; 3
@@:
sub eax, sizeof.xfs_bmdr_block
shr eax, 4 ;log2(sizeof.xfs_bmbt_key + sizeof.xfs_bmdr_ptr)
mov edx, [ebx + sizeof.xfs_bmbt_key*eax + 0] ; hi
mov eax, [ebx + sizeof.xfs_bmbt_key*eax + 4] ; hi
bswap edx
bswap eax
stdcall xfs_btree_read, eax, edx, 0
test eax, eax
jnz .error
jmp .root.next_key
.not_root:
DEBUGF 1,".root.not_root\n"
mov eax, [esp + 24] ; block_lo
mov edx, [esp + 28] ; block_hi
mov ebx, [ebp + XFS.cur_block]
stdcall xfs_read_block
test eax, eax
jnz .error
mov ebx, [ebp + XFS.cur_block]
cmp [ebx + xfs_bmbt_block.bb_magic], XFS_BMAP_MAGIC
jne .error
cmp [ebx + xfs_bmbt_block.bb_level], 0 ; leaf?
je .leaf
jmp .node
.node:
; mov eax, [ebp + XFS.blocksize]
; sub eax, sizeof.xfs_bmbt_block
; shr eax, 4 ; maxnumrecs
mov eax, dword[ebp + XFS.file_offset + 0] ; lo
mov edx, dword[ebp + XFS.file_offset + 4] ; hi
movzx edx, [ebx + xfs_bmbt_block.bb_numrecs]
xchg dl, dh
dec edx
add ebx, sizeof.xfs_bmbt_block
xor eax, eax
dec eax
.node.next_key:
push eax ecx edx esi edi
mov eax, [esp + 44] ; block_lo
mov edx, [esp + 48] ; block_hi
mov ebx, [ebp + XFS.cur_block]
stdcall xfs_read_block
test eax, eax
jnz .error
mov ebx, [ebp + XFS.cur_block]
add ebx, sizeof.xfs_bmbt_block
pop edi esi edx ecx eax
cmp [ebp + XFS.bytes_to_read], 0
je .quit
inc eax
cmp eax, edx ; out of keys?
ja .node.key_found ; there is no length field, so try the last key
lea edi, [ebx + sizeof.xfs_bmbt_key*eax + 0]
lea esi, [ebx + sizeof.xfs_bmbt_key*eax + 4]
bswap edi
bswap esi
mov ecx, [ebp + XFS.blocklog]
shld edi, esi, cl
shl esi, cl
cmp edi, dword[ebp + XFS.file_offset + 4]
ja .node.prev_or_hole
jb .node.next_key
cmp esi, dword[ebp + XFS.file_offset + 0]
ja .node.prev_or_hole
jb .node.next_key
jmp .node.key_found
.node.prev_or_hole:
test eax, eax
jz .node.hole
dec eax
jmp .node.key_found
.node.hole:
push eax edx esi edi
mov ecx, [ebp + XFS.blocklog]
shld edi, esi, cl
shl esi, cl
sub esi, dword[ebp + XFS.file_offset + 0]
sbb edi, dword[ebp + XFS.file_offset + 4]
mov ecx, [ebp + XFS.bytes_to_read]
cmp edi, 0 ; hole size >= 2^32
jne @f
cmp ecx, esi
jbe @f
mov ecx, esi
@@:
add dword[ebp + XFS.file_offset + 0], ecx
adc dword[ebp + XFS.file_offset + 4], 0
sub [ebp + XFS.bytes_to_read], ecx
xor eax, eax
mov edi, [ebp + XFS.buffer_pos]
rep stosb
mov [ebp + XFS.buffer_pos], edi
pop edi esi edx eax
jmp .node.next_key
.node.key_found:
mov edx, [ebp + XFS.cur_inode_save]
mov eax, [ebp + XFS.inodesize]
sub eax, xfs_inode.di_u
cmp [edx + xfs_inode.di_core.di_forkoff], 0
je @f
movzx eax, [edx + xfs_inode.di_core.di_forkoff]
shl eax, XFS_DIR2_DATA_ALIGN_LOG ; 3
@@:
sub eax, sizeof.xfs_bmdr_block
shr eax, 4 ;log2(sizeof.xfs_bmbt_key + sizeof.xfs_bmdr_ptr)
mov edx, [ebx + sizeof.xfs_bmbt_key*eax + 0] ; hi
mov eax, [ebx + sizeof.xfs_bmbt_key*eax + 4] ; hi
bswap edx
bswap eax
stdcall xfs_btree_read, eax, edx, 0
test eax, eax
jnz .error
jmp .node.next_key
jmp .quit
.leaf:
jmp .quit
.error:
pop edi esi edx ecx ebx
movi eax, ERROR_FS_FAIL
ret 4
.quit:
pop edi esi edx ecx ebx
xor eax, eax
ret 4
;----------------------------------------------------------------
; push nextents
; push extent_list
; push file_offset_hi
; push file_offset_lo
;----------------------------------------------------------------
;xfs_extent_list_read:
; push ebx 0 edx esi edi ; zero means actually_read_bytes
;
; .quit:
; pop edi esi edx ecx ebx
; xor eax, eax
; ret 24
; .error:
; pop edi esi edx ecx ebx
; ret 24