kolibrios-fun/kernel/trunk/fs/xfs.asm

2125 lines
67 KiB
NASM
Raw Normal View History

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2013-2020. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
$Revision$
include 'xfs.inc'
macro omit_frame_pointer_prologue procname,flag,parmbytes,localbytes,reglist {
local loc
loc = (localbytes+3) and (not 3)
if localbytes
sub esp, loc
end if
irps reg, reglist \{ push reg \}
counter = 0
irps reg, reglist \{counter = counter+1 \}
parmbase@proc equ esp+counter*4+loc+4
localbase@proc equ esp
}
macro omit_frame_pointer_epilogue procname,flag,parmbytes,localbytes,reglist {
local loc
loc = (localbytes+3) and (not 3)
irps reg, reglist \{ reverse pop reg \}
if localbytes
lea esp, [esp+loc]
end if
if flag and 10000b
retn
else
retn parmbytes
end if
}
prologue@proc equ omit_frame_pointer_prologue
epilogue@proc equ omit_frame_pointer_epilogue
macro movbe reg, arg {
if CPUID_MOVBE eq Y
movbe reg, arg
else
mov reg, arg
if reg in <eax,ebx,ecx,edx,esi,edi,ebp,esp>
bswap reg
else if ax eq reg
xchg al, ah
else if bx eq reg
xchg bl, bh
else if cx eq reg
xchg cl, ch
else if dx eq reg
xchg dl, dh
else
err
end if
end if
}
;
; This file contains XFS related code.
; For more information on XFS check links and source below.
;
; 1. https://xfs.wiki.kernel.org/
;
; 2. XFS Algorithms & Data Structures:
; git://git.kernel.org/pub/scm/fs/xfs/xfs-documentation.git
; https://mirrors.edge.kernel.org/pub/linux/utils/fs/xfs/docs/xfs_filesystem_structure.pdf
;
; 3. Linux source at https://www.kernel.org/
; git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
; /fs/xfs
;
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
xfs._.user_functions_end:
endg
; test partition type (valid XFS one?)
; alloc and fill XFS (see xfs.inc) structure
; this function is called for each partition
; return 0 (not XFS or invalid) / pointer to partition structure
proc xfs_create_partition uses ebx esi edi
; check XFS signature
cmp [ebx+xfs_sb.sb_magicnum], XFS_SB_MAGIC
jnz .error_nofree
; test for supported feature flags and version in sb_versionnum
movzx eax, [ebx+xfs_sb.sb_versionnum]
xchg al, ah
; allow only known and supported features
; return error otherwise
test eax, NOT XFS_SB_VERSION_SUPPORTED
jnz .error_nofree
; version < 4 obsolete, not supported
; version = 4,5 supported
; version > 5 unknown
and al, XFS_SB_VERSION_NUMBITS
cmp al, 4
jb .error_nofree
cmp al, 5
ja .error_nofree
; if MOREBITS bit is set, additional feature flags are in sb_features2
test eax, XFS_SB_VERSION_MOREBITSBIT
jz @f
movbe eax, [ebx+xfs_sb.sb_features2]
test eax, NOT XFS_SB_VERSION2_SUPPORTED
jnz .error_nofree
@@:
movbe eax, [ebx+xfs_sb.sb_features_incompat]
test eax, NOT XFS_SB_FEAT_INCOMPAT_SUPPORTED
jnz .error_nofree
; all presented features are either supported or don't affect reading
movi eax, sizeof.XFS
call malloc
mov edi, eax
test eax, eax
jz .error
; standard partition initialization, common for all file systems
mov eax, dword[ebp+PARTITION.FirstSector+DQ.lo]
mov dword[edi+XFS.FirstSector+DQ.lo], eax
mov eax, dword[ebp+PARTITION.FirstSector+DQ.hi]
mov dword[edi+XFS.FirstSector+DQ.hi], eax
mov eax, dword[ebp+PARTITION.Length+DQ.lo]
mov dword[edi+XFS.Length+DQ.lo], eax
mov eax, dword[ebp+PARTITION.Length+DQ.hi]
mov dword[edi+XFS.Length+DQ.hi], eax
mov eax, [ebp+PARTITION.Disk]
mov [edi+XFS.Disk], eax
mov [edi+XFS.FSUserFunctions], xfs._.user_functions
; here we initialize only one mutex (for the entire partition)
; XFS potentially allows parallel r/w access to different AGs, keep it in mind
lea ecx, [edi+XFS.Lock]
call mutex_init
; movzx eax, [ebx+xfs_sb.sb_sectsize]
; xchg al, ah
mov eax, [eax+DISK.MediaInfo.SectorSize]
mov [edi+XFS.sectsize], eax
movbe eax, [ebx+xfs_sb.sb_blocksize]
mov [edi+XFS.blocksize], eax
movzx eax, [ebx+xfs_sb.sb_versionnum]
xchg al, ah
mov [edi+XFS.versionnum], eax
and eax, XFS_SB_VERSION_NUMBITS
mov [edi+XFS.version], eax
movbe eax, [ebx+xfs_sb.sb_features2]
mov [edi+XFS.features2], eax
cmp [edi+XFS.version], 5
jz .v5
.v4:
mov [edi+XFS.inode_core_size], sizeof.xfs_dinode_core
test eax, XFS_SB_VERSION2_FTYPE
setnz al
movzx eax, al
mov [edi+XFS.ftype_size], eax
mov [edi+XFS.dir_block_magic], XFS_DIR2_BLOCK_MAGIC
mov [edi+XFS.dir_data_magic], XFS_DIR2_DATA_MAGIC
mov [edi+XFS.dir_leaf1_magic], XFS_DIR2_LEAF1_MAGIC
mov [edi+XFS.dir_leafn_magic], XFS_DIR2_LEAFN_MAGIC
mov [edi+XFS.da_node_magic], XFS_DA_NODE_MAGIC
mov [edi+XFS.bmap_magic], XFS_BMAP_MAGIC
mov [edi+XFS.dir_block_size], sizeof.xfs_dir2_data_hdr
mov [edi+XFS.bmbt_block_size], sizeof.xfs_bmbt_block
mov [edi+XFS.da_blkinfo_size], sizeof.xfs_da_blkinfo
jmp .vcommon
.v5:
mov [edi+XFS.inode_core_size], sizeof.xfs_dinode3_core
movbe eax, [ebx+xfs_sb.sb_features_incompat]
mov [edi+XFS.features_incompat], eax
test eax, XFS_SB_FEAT_INCOMPAT_FTYPE
setnz al
movzx eax, al
mov [edi+XFS.ftype_size], eax
mov [edi+XFS.dir_block_magic], XFS_DIR3_BLOCK_MAGIC
mov [edi+XFS.dir_data_magic], XFS_DIR3_DATA_MAGIC
mov [edi+XFS.dir_leaf1_magic], XFS_DIR3_LEAF1_MAGIC
mov [edi+XFS.dir_leafn_magic], XFS_DIR3_LEAFN_MAGIC
mov [edi+XFS.da_node_magic], XFS_DA3_NODE_MAGIC
mov [edi+XFS.bmap_magic], XFS_BMAP3_MAGIC
mov [edi+XFS.dir_block_size], sizeof.xfs_dir3_data_hdr
mov [edi+XFS.bmbt_block_size], sizeof.xfs_bmbt3_block
mov [edi+XFS.da_blkinfo_size], sizeof.xfs_da3_blkinfo
.vcommon:
movzx eax, [ebx+xfs_sb.sb_inodesize]
xchg al, ah
mov [edi+XFS.inodesize], eax
movzx eax, [ebx+xfs_sb.sb_inopblock]
xchg al, ah
mov [edi+XFS.inopblock], eax
movzx eax, [ebx+xfs_sb.sb_blocklog]
mov [edi+XFS.blocklog], eax
; movzx eax, [ebx+xfs_sb.sb_sectlog]
mov eax, [edi+XFS.sectsize]
bsf eax, eax
mov [edi+XFS.sectlog], eax
movzx eax, [ebx+xfs_sb.sb_inodelog]
mov [edi+XFS.inodelog], eax
movzx eax, [ebx+xfs_sb.sb_inopblog]
mov [edi+XFS.inopblog], eax
movzx ecx, [ebx+xfs_sb.sb_dirblklog]
mov [edi+XFS.dirblklog], ecx
movi eax, 1
shl eax, cl
mov [edi+XFS.blkpdirblk], eax
movbe eax, [ebx+xfs_sb.sb_rootino.hi]
mov [edi+XFS.rootino.lo], eax
movbe eax, [ebx+xfs_sb.sb_rootino.lo]
mov [edi+XFS.rootino.hi], eax
mov eax, [edi+XFS.blocksize]
mov ecx, [edi+XFS.dirblklog]
shl eax, cl
mov [edi+XFS.dirblocksize], eax ; blocks are for files, dirblocks are 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.sectpblog], ecx
mov eax, 1
shl eax, cl
mov [edi+XFS.sectpblock], eax
movbe eax, [ebx+xfs_sb.sb_agblocks]
mov [edi+XFS.agblocks], eax
movzx ecx, [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
xor edx, edx
shld edx, eax, cl
shl eax, cl
sub eax, 1
sbb edx, 0
mov [edi+XFS.agblockmask.lo], eax
mov [edi+XFS.agblockmask.hi], edx
; 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
shr edx, cl
mov [edi+XFS.dir2_leaf_offset_blocks.lo], eax
mov [edi+XFS.dir2_leaf_offset_blocks.hi], edx
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
shr edx, cl
mov [edi+XFS.dir2_free_offset_blocks.lo], eax
mov [edi+XFS.dir2_free_offset_blocks.hi], edx
; allocate memory for temp block, dirblock, inode, etc
mov eax, [edi+XFS.blocksize]
call malloc
mov [edi+XFS.cur_block], eax
test eax, eax
jz .error
mov eax, [edi+XFS.blocksize]
call malloc
mov [edi+XFS.cur_block_data], eax
test eax, eax
jz .error
; we do need XFS.blocksize bytes for single inode
; minimal file system structure is block, inodes are packed in blocks
; FIXME
mov eax, [edi+XFS.blocksize]
call malloc
mov [edi+XFS.cur_inode], eax
test eax, eax
jz .error
mov eax, [edi+XFS.blocksize]
call malloc
test eax, eax
jz .error
mov [edi+XFS.tmp_inode], eax
; current sector
; only for sector sized structures like AGF
; inodes usually fit this size, but not always!
; therefore never store inode here
mov eax, [edi+XFS.sectsize]
call malloc
mov [edi+XFS.cur_sect], eax
test eax, eax
jz .error
mov eax, [edi+XFS.dirblocksize]
call malloc
mov [edi+XFS.cur_dirblock], eax
test eax, eax
jz .error
.quit:
; return pointer to allocated XFS partition structure
mov eax, edi
ret
.error:
mov eax, edi
call xfs._.free
.error_nofree:
xor eax, eax
ret
endp
; lock partition access mutex
xfs._.lock:
lea ecx, [ebp+XFS.Lock]
jmp mutex_lock
; unlock partition access mutex
xfs._.unlock:
lea ecx, [ebp+XFS.Lock]
jmp mutex_unlock
; free all the allocated memory
; called on partition destroy
; or during failed initialization from xfs_create_partition
xfs._.free:
test eax, eax
jz .done
push ebx
mov ebx, eax
; freeing order must correspond the order of
; allocation in xfs_create_partition
mov eax, [ebx+XFS.cur_block]
test eax, eax
jz .done
call free
mov eax, [ebx+XFS.cur_block_data]
test eax, eax
jz .done
call free
mov eax, [ebx+XFS.cur_inode]
test eax, eax
jz .done
call free
mov eax, [ebx+XFS.tmp_inode]
test eax, eax
jz .done
call free
mov eax, [ebx+XFS.cur_sect]
test eax, eax
jz .done
call free
mov eax, [ebx+XFS.cur_dirblock]
test eax, eax
jz .done
call free
mov eax, ebx
call free
pop ebx
.done:
ret
;---------------------------------------------------------------
; block number
; eax -- inode_lo
; edx -- inode_hi
; ebx -- buffer
;---------------------------------------------------------------
proc xfs._.read_block
movi ecx, 1
call xfs._.read_blocks
ret
endp
proc xfs._.blkrel2sectabs uses esi
push edx 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_read64_sys we should
; 1. get AG number and multiply it by the AG size in blocks
; 2. extract and add AG relative block number
; 1.
mov ecx, [ebp+XFS.agblklog]
shrd eax, edx, cl
shr edx, cl
mul [ebp+XFS.agblocks]
; 2.
pop ecx esi
and ecx, [ebp+XFS.agblockmask.lo]
and esi, [ebp+XFS.agblockmask.hi]
add eax, ecx
adc edx, esi
mov ecx, [ebp+XFS.sectpblog]
shld edx, eax, cl
shl eax, cl
ret
endp
;---------------------------------------------------------------
; start block number
; edx:eax -- block
; ebx -- buffer
; ecx -- count
;---------------------------------------------------------------
proc xfs._.read_blocks
push ecx
call xfs._.blkrel2sectabs
pop ecx
imul ecx, [ebp+XFS.sectpblock]
call fs_read64_sys
test eax, eax
ret
endp
proc xfs._.read_dirblock uses ebx, _startblock:qword, _buffer
mov eax, dword[_startblock+DQ.lo]
mov edx, dword[_startblock+DQ.hi]
mov ebx, [_buffer]
mov ecx, [ebp+XFS.blkpdirblk]
call xfs._.read_blocks
ret
endp
;---------------------------------------------------------------
; test eax, eax
;---------------------------------------------------------------
proc xfs_read_inode uses ebx, _inode_lo, _inode_hi, _buffer
mov eax, [_inode_lo]
mov edx, [_inode_hi]
mov ebx, [_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.inopblog]
shrd eax, edx, cl
shr edx, cl
; 2.
call xfs._.read_block
jnz .error
; inode numbers should be first extracted from bitfields by mask
mov eax, [_inode_lo]
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
xor eax, eax
cmp [ebx+xfs_inode.di_core.di_magic], XFS_DINODE_MAGIC
jz .quit
movi eax, ERROR_FS_FAIL
.quit:
mov edx, ebx
.error:
ret
endp
; skip ecx first entries
proc xfs._.dir_sf_skip _count
mov ecx, [_count]
.next:
dec ecx
js .quit
dec [ebp+XFS.entries_left_in_dir]
js .quit
.self:
bts [ebp+XFS.dir_sf_self_done], 0
jc .parent
jmp .next
.parent:
bts [ebp+XFS.dir_sf_parent_done], 0
jc .common
jmp .next
.common:
movzx eax, [esi+xfs_dir2_sf_entry.namelen]
add esi, xfs_dir2_sf_entry.name
add esi, eax
add esi, [ebp+XFS.ftype_size]
add esi, [ebp+XFS.shortform_inodelen]
jmp .next
.quit:
ret
endp
proc xfs._.dir_sf_read uses edi, _count
locals
_dst dd ?
endl
.next:
dec [_count]
js .quit
dec [ebp+XFS.entries_left_in_dir]
js .quit
mov [_dst], edx
.self:
bts [ebp+XFS.dir_sf_self_done], 0
jc .parent
lea edi, [edx+bdfe.name]
mov dword[edi], '.'
stdcall xfs_get_inode_info, [ebp+XFS.cur_inode], edx
jmp .common
.parent:
bts [ebp+XFS.dir_sf_parent_done], 0
jc .not_special
lea edi, [edx+bdfe.name] ; get file name offset
mov dword[edi], '..' ; terminator included
mov edi, edx
lea edx, [ebx+xfs_dir2_sf.hdr.parent]
call xfs._.get_inode_number_sf
stdcall xfs_read_inode, eax, edx, [ebp+XFS.tmp_inode]
test eax, eax
jnz .error
stdcall xfs_get_inode_info, edx, edi
jmp .common
.not_special:
movzx ecx, [esi+xfs_dir2_sf_entry.namelen]
add esi, xfs_dir2_sf_entry.name
lea edi, [edx+bdfe.name]
stdcall xfs._.copy_filename
add esi, [ebp+XFS.ftype_size]
mov edi, edx
mov edx, esi
call xfs._.get_inode_number_sf
stdcall xfs_read_inode, eax, edx, [ebp+XFS.tmp_inode]
test eax, eax
jnz .error
stdcall xfs_get_inode_info, edx, edi
add esi, [ebp+XFS.shortform_inodelen]
.common:
mov edx, [_dst]
mov eax, [ebp+XFS.bdfe_nameenc]
mov [edx+bdfe.nameenc], eax
add edx, [ebp+XFS.bdfe_len]
inc [ebp+XFS.entries_read]
jmp .next
.quit:
xor eax, eax
.error:
ret
endp
proc xfs._.readdir_sf uses esi, _src, _dst
mov ebx, [_src]
mov edx, [_dst]
mov [ebp+XFS.dir_sf_self_done], 0
mov [ebp+XFS.dir_sf_parent_done], 0
mov [ebp+XFS.entries_read], 0
movzx eax, [ebx+xfs_dir2_sf.hdr.count]
; '..' and '.' are implicit
add eax, 2
mov [ebp+XFS.entries_left_in_dir], eax
mov [edx+bdfe_hdr.total_cnt], eax
; inode numbers are often saved as 4 bytes (iff they fit)
; compute the length of inode numbers
; 8 iff i8count != 0, 4 otherwise
cmp [ebx+xfs_dir2_sf.hdr.i8count], 0
setnz al
lea eax, [eax*4+4]
mov [ebp+XFS.shortform_inodelen], eax
add edx, sizeof.bdfe_hdr
lea esi, [ebx+xfs_dir2_sf.hdr.parent+eax]
stdcall xfs._.dir_sf_skip, [ebp+XFS.entries_to_skip]
stdcall xfs._.dir_sf_read, [ebp+XFS.requested_cnt]
ret
endp
proc xfs._.readdir_block _literal_area, _out_buf
mov ebx, [_literal_area]
mov [ebp+XFS.entries_read], 0
mov eax, ebx
mov ebx, [ebp+XFS.cur_dirblock]
stdcall xfs._.extent_unpack, eax
stdcall xfs._.read_dirblock, [ebp+XFS.extent.br_startblock.lo], [ebp+XFS.extent.br_startblock.hi], ebx
mov edx, [_out_buf]
jnz .error
mov eax, [ebp+XFS.dir_block_magic]
cmp [ebx+xfs_dir2_block.hdr.magic], eax
movi eax, ERROR_FS_FAIL
jnz .error
mov eax, [ebp+XFS.dirblocksize]
movbe ecx, [ebx+eax-sizeof.xfs_dir2_block_tail+xfs_dir2_block_tail.stale]
movbe eax, [ebx+eax-sizeof.xfs_dir2_block_tail+xfs_dir2_block_tail.count]
sub eax, ecx ; actual number of entries = count - stale
mov [ebp+XFS.entries_left_in_dir], eax
mov [edx+bdfe_hdr.total_cnt], eax
add ebx, [ebp+XFS.dir_block_size]
add edx, sizeof.bdfe_hdr
mov [_out_buf], edx
lea edi, [_out_buf]
.next:
movi eax, ERROR_SUCCESS
cmp [ebp+XFS.requested_cnt], 0
jz .quit
cmp [ebp+XFS.entries_left_in_dir], 0
jz .quit
stdcall xfs._.dir_entry_skip_read, edi
jz .next
.error:
.quit:
ret
endp
proc xfs._.readdir_leaf_node uses esi, _inode_data, _out_buf
mov ebx, [_inode_data]
mov edx, [_out_buf]
mov [ebp+XFS.cur_inode_save], ebx
mov [ebp+XFS.entries_read], 0
mov eax, ebx
add eax, [ebp+XFS.inode_core_size]
movbe edx, [ebx+xfs_inode.di_core.di_nextents]
mov ecx, [ebp+XFS.dir2_leaf_offset_blocks.lo]
mov [ebp+XFS.offset_begin.lo], ecx
mov ecx, [ebp+XFS.dir2_leaf_offset_blocks.hi]
mov [ebp+XFS.offset_begin.hi], ecx
mov ecx, [ebp+XFS.dir2_free_offset_blocks.lo]
mov [ebp+XFS.offset_end.lo], ecx
mov ecx, [ebp+XFS.dir2_free_offset_blocks.hi]
mov [ebp+XFS.offset_end.hi], ecx
stdcall xfs._.walk_extent_list, edx, eax, xfs._.extent_iterate_dirblocks, xfs._.leafn_calc_entries, 0
jnz .error
mov eax, [ebp+XFS.entries_read]
mov edx, [_out_buf]
mov [edx+bdfe_hdr.total_cnt], eax
mov [ebp+XFS.entries_left_in_dir], eax
add [_out_buf], sizeof.bdfe_hdr
mov [ebp+XFS.entries_read], 0
movbe edx, [ebx+xfs_inode.di_core.di_nextents]
mov eax, ebx
add eax, [ebp+XFS.inode_core_size]
lea ecx, [_out_buf]
push ecx
mov [ebp+XFS.offset_begin.lo], 0
mov [ebp+XFS.offset_begin.hi], 0
mov ecx, [ebp+XFS.dir2_leaf_offset_blocks.lo]
mov [ebp+XFS.offset_end.lo], ecx
mov ecx, [ebp+XFS.dir2_leaf_offset_blocks.hi]
mov [ebp+XFS.offset_end.hi], ecx
pop ecx
stdcall xfs._.walk_extent_list, edx, eax, xfs._.extent_iterate_dirblocks, xfs._.dir_btree_skip_read, ecx
; jnz .error
.error:
.quit:
ret
endp
proc xfs._.dir_entry_skip_read uses esi edi, _arg
cmp [ebx+xfs_dir2_data_union.unused.freetag], XFS_NULL
jnz @f
movzx eax, [ebx+xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .quit
@@:
cmp [ebp+XFS.entries_to_skip], 0
jz .read
.skip:
dec [ebp+XFS.entries_to_skip]
movzx ecx, [ebx+xfs_dir2_data_union.xentry.namelen]
lea ebx, [ebx+xfs_dir2_data_union.xentry.name+ecx+2]
add ebx, [ebp+XFS.ftype_size]
jmp .common
.read:
dec [ebp+XFS.requested_cnt]
inc [ebp+XFS.entries_read]
mov edi, [_arg]
mov edi, [edi]
movbe edx, [ebx+xfs_dir2_data_union.xentry.inumber.lo]
movbe eax, [ebx+xfs_dir2_data_union.xentry.inumber.hi]
stdcall xfs_read_inode, eax, edx, [ebp+XFS.tmp_inode]
stdcall xfs_get_inode_info, edx, edi
jnz .error
mov edx, [_arg]
mov edx, [edx]
mov ecx, [ebp+XFS.bdfe_nameenc]
mov [edx+bdfe.nameenc], ecx
lea edi, [edx+bdfe.name]
movzx ecx, [ebx+xfs_dir2_data_union.xentry.namelen]
lea esi, [ebx+xfs_dir2_data_union.xentry.name]
stdcall xfs._.copy_filename
lea ebx, [esi+2] ; skip 'tag'
add ebx, [ebp+XFS.ftype_size]
mov eax, [_arg]
mov edx, [eax]
add edx, [ebp+XFS.bdfe_len]
mov [eax], edx
.common:
sub ebx, [ebp+XFS.cur_dirblock]
add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes
and ebx, not 7
add ebx, [ebp+XFS.cur_dirblock]
dec [ebp+XFS.entries_left_in_dir]
.quit:
movi eax, ERROR_SUCCESS
cmp esp, esp
.error:
ret
endp
proc xfs._.dir_btree_skip_read uses ebx ecx edx esi edi, _cur_dirblock, _offset_lo, _offset_hi, _arg
mov ebx, [_cur_dirblock]
mov eax, [ebp+XFS.dir_data_magic]
cmp [ebx+xfs_dir2_block.hdr.magic], eax
movi eax, ERROR_FS_FAIL
jnz .error
mov eax, ebx
add eax, [ebp+XFS.dirblocksize]
mov [ebp+XFS.max_dirblockaddr], eax
; add ebx, xfs_dir2_block.u
add ebx, [ebp+XFS.dir_block_size]
.next:
movi eax, ERROR_SUCCESS
cmp [ebp+XFS.requested_cnt], 0
jz .quit
cmp [ebp+XFS.entries_left_in_dir], 0
jz .quit
cmp ebx, [ebp+XFS.max_dirblockaddr]
jz .quit
stdcall xfs._.dir_entry_skip_read, [_arg]
jz .next
.error:
.quit:
ret
endp
proc xfs._.readdir_btree uses esi, _inode_data, _out_buf
mov [ebp+XFS.cur_inode_save], ebx
mov [ebp+XFS.entries_read], 0
mov eax, [ebp+XFS.inodesize]
sub eax, xfs_inode.di_u
movzx ecx, [ebx+xfs_inode.di_core.di_forkoff]
jecxz @f
shl ecx, 3
mov eax, ecx
@@:
lea edx, [ebx+xfs_inode.di_u]
mov ecx, [ebp+XFS.dir2_leaf_offset_blocks.lo]
mov [ebp+XFS.offset_begin.lo], ecx
mov ecx, [ebp+XFS.dir2_leaf_offset_blocks.hi]
mov [ebp+XFS.offset_begin.hi], ecx
mov ecx, [ebp+XFS.dir2_free_offset_blocks.lo]
mov [ebp+XFS.offset_end.lo], ecx
mov ecx, [ebp+XFS.dir2_free_offset_blocks.hi]
mov [ebp+XFS.offset_end.hi], ecx
stdcall xfs._.walk_btree, edx, eax, xfs._.extent_iterate_dirblocks, xfs._.leafn_calc_entries, 0, 1
mov eax, [ebp+XFS.entries_read]
mov edx, [_out_buf]
mov [edx+bdfe_hdr.total_cnt], eax
mov [ebp+XFS.entries_left_in_dir], eax
mov [ebp+XFS.entries_read], 0
add [_out_buf], sizeof.bdfe_hdr
mov eax, [ebp+XFS.inodesize]
sub eax, xfs_inode.di_u
movzx ecx, [ebx+xfs_inode.di_core.di_forkoff]
jecxz @f
shl ecx, 3
mov eax, ecx
@@:
lea edx, [ebx+xfs_inode.di_u]
mov [ebp+XFS.offset_begin.lo], 0
mov [ebp+XFS.offset_begin.hi], 0
mov ecx, [ebp+XFS.dir2_leaf_offset_blocks.lo]
mov [ebp+XFS.offset_end.lo], ecx
mov ecx, [ebp+XFS.dir2_leaf_offset_blocks.hi]
mov [ebp+XFS.offset_end.hi], ecx
mov ecx, [_out_buf]
push ecx
mov ecx, esp
stdcall xfs._.walk_btree, edx, eax, xfs._.extent_iterate_dirblocks, xfs._.dir_btree_skip_read, ecx, 1
pop ecx
.error:
.quit:
ret
endp
proc xfs._.copy_filename uses eax
mov eax, [ebp+XFS.bdfe_nameenc]
cmp eax, 3
jz .utf8
cmp eax, 2
jz .utf16
.cp866:
call unicode.utf8.decode
call unicode.cp866.encode
stosb
test ecx, ecx
jnz .cp866
mov byte[edi], 0
jmp .done
.utf16:
call unicode.utf8.decode
call unicode.utf16.encode
stosw
shr eax, 16
jz @f
stosw
@@:
test ecx, ecx
jnz .utf16
mov word[edi], 0
jmp .done
.utf8:
rep movsb
mov byte[edi], 0
.done:
ret
endp
;----------------------------------------------------------------
; src ; inode
; dst ; bdfe
; start_number ; from 0
;----------------------------------------------------------------
proc xfs._.readdir uses ebx esi edi, _start_number, _entries_to_read, _dst, _src, _encoding
mov ecx, [_start_number]
mov [ebp+XFS.entries_to_skip], ecx
mov eax, [_entries_to_read]
mov [ebp+XFS.requested_cnt], eax
mov eax, [_encoding]
mov [ebp+XFS.bdfe_nameenc], eax
mov ecx, 304
cmp eax, 1 ; CP866
jbe @f
mov ecx, 560
@@:
mov [ebp+XFS.bdfe_len], ecx
mov edx, [_dst]
mov [ebp+XFS.bdfe_buf], edx
mov ebx, [_src]
mov [ebp+XFS.cur_inode_save], ebx
mov [edx+bdfe_hdr.version], 1
mov [edx+bdfe_hdr.zeroed+0x00], 0
mov [edx+bdfe_hdr.zeroed+0x04], 0
mov [edx+bdfe_hdr.zeroed+0x08], 0
mov [edx+bdfe_hdr.zeroed+0x0c], 0
mov [edx+bdfe_hdr.zeroed+0x10], 0
movzx eax, [ebx+xfs_inode.di_core.di_format]
; switch directory ondisk format and jump to corresponding label
cmp eax, XFS_DINODE_FMT_LOCAL
jnz @f
add ebx, [ebp+XFS.inode_core_size]
stdcall xfs._.readdir_sf, ebx, [_dst]
test eax, eax
jnz .error
jmp .quit
@@:
cmp eax, XFS_DINODE_FMT_BTREE
jnz @f
stdcall xfs._.readdir_btree, ebx, [_dst]
jmp .quit
@@:
cmp eax, XFS_DINODE_FMT_EXTENTS
movi eax, ERROR_FS_FAIL
jnz .error
call xfs._.get_last_dirblock
test eax, eax
jnz @f
add ebx, [ebp+XFS.inode_core_size]
stdcall xfs._.readdir_block, ebx, [_dst]
jmp .quit
@@:
stdcall xfs._.readdir_leaf_node, ebx, [_dst]
jmp .quit
.quit:
mov edx, [_dst]
mov ebx, [ebp+XFS.entries_read]
mov [edx+bdfe_hdr.read_cnt], ebx
xor eax, eax
.error:
ret
endp
; returns edx:eax inode or 0
proc xfs._.lookup_sf _name, _len
add ebx, [ebp+XFS.inode_core_size]
mov esi, [_name]
mov ecx, [_len]
cmp ecx, 2
ja .common
jz .check_parent
.check_self:
cmp byte[esi], '.'
jnz .common
mov eax, [ebp+XFS.inode_self.lo]
mov edx, [ebp+XFS.inode_self.hi]
jmp .quit
.check_parent:
cmp word[esi], '..'
jnz .common
lea edx, [ebx+xfs_dir2_sf.hdr.parent]
call xfs._.get_inode_number_sf
jmp .quit
.common:
movzx edx, [ebx+xfs_dir2_sf.hdr.count]
movi eax, 0
cmp [ebx+xfs_dir2_sf.hdr.i8count], 0
setnz al
lea eax, [eax*4+4]
lea edi, [ebx+xfs_dir2_sf.hdr.parent+eax]
.next_name:
dec edx
jns @f
movi eax, ERROR_FILE_NOT_FOUND
jmp .error
@@:
movzx ecx, [edi+xfs_dir2_sf_entry.namelen]
add edi, xfs_dir2_sf_entry.name
mov esi, [_name]
cmp ecx, [_len]
jnz @f
repz cmpsb
jz .found
@@:
add edi, [ebp+XFS.ftype_size]
add edi, ecx
add edi, eax
jmp .next_name
.found:
add edi, [ebp+XFS.ftype_size]
mov edx, edi
call xfs._.get_inode_number_sf
.quit:
cmp esp, esp
.error:
ret
endp
proc xfs._.lookup_block uses esi, _name, _len
add ebx, [ebp+XFS.inode_core_size]
mov eax, ebx
mov ebx, [ebp+XFS.cur_dirblock]
stdcall xfs._.extent_unpack, eax
stdcall xfs._.read_dirblock, [ebp+XFS.extent.br_startblock.lo], [ebp+XFS.extent.br_startblock.hi], ebx
jnz .error
mov eax, [ebp+XFS.dir_block_magic]
cmp [ebx+xfs_dir2_block.hdr.magic], eax
movi eax, ERROR_FS_FAIL
jnz .error
stdcall xfs_hashname, [_name+4], [_len]
add ebx, [ebp+XFS.dirblocksize]
movbe ecx, [ebx-sizeof.xfs_dir2_block_tail+xfs_dir2_block_tail.count]
lea edx, [ecx*sizeof.xfs_dir2_leaf_entry+sizeof.xfs_dir2_block_tail]
sub ebx, edx
stdcall xfs._.get_addr_by_hash, ebx, ecx
jnz .error
mov ebx, [ebp+XFS.cur_dirblock]
movbe edx, [ebx+eax*XFS_DIR2_DATA_ALIGN+xfs_dir2_data_entry.inumber.lo]
movbe eax, [ebx+eax*XFS_DIR2_DATA_ALIGN+xfs_dir2_data_entry.inumber.hi]
.quit:
.error:
ret
endp
proc xfs._.get_inode_by_addr uses ebx esi edi, _inode_buf
xor edx, edx
shld edx, eax, XFS_DIR2_DATA_ALIGN_LOG
shl eax, XFS_DIR2_DATA_ALIGN_LOG
mov esi, [ebp+XFS.dirblocksize]
dec esi
and esi, eax
mov ecx, [ebp+XFS.blocklog]
add ecx, [ebp+XFS.dirblklog]
shrd eax, edx, cl
shr edx, cl
mov ecx, [ebp+XFS.dirblklog]
shld edx, eax, cl
shl eax, cl
mov ebx, [_inode_buf]
cmp [ebx+xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jz .extents
cmp [ebx+xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE
jz .btree
jmp .error
.extents:
movbe ecx, [ebx+xfs_inode.di_core.di_nextents]
add ebx, [ebp+XFS.inode_core_size]
mov [ebp+XFS.offset_begin.lo], eax
mov [ebp+XFS.offset_begin.hi], edx
stdcall xfs._.extent_list.seek, ecx
stdcall xfs._.read_dirblock, [ebp+XFS.extent.br_startblock.lo], [ebp+XFS.extent.br_startblock.hi], [ebp+XFS.cur_dirblock]
jnz .error
jmp .common
.btree:
movzx ecx, [ebx+xfs_inode.di_core.di_forkoff]
shl ecx, 3
test ecx, ecx
jnz @f
mov ecx, [ebp+XFS.inodesize]
sub ecx, [ebp+XFS.inode_core_size]
@@:
add ebx, [ebp+XFS.inode_core_size]
stdcall xfs._.btree_read_block, ebx, ecx, eax, edx, [ebp+XFS.cur_dirblock]
.common:
mov ebx, [ebp+XFS.cur_dirblock]
mov eax, [ebp+XFS.dir_data_magic]
cmp [ebx+xfs_dir2_block.hdr.magic], eax
movi eax, ERROR_FS_FAIL
jnz .error
movbe edx, [ebx+esi+xfs_dir2_data_entry.inumber.lo]
movbe eax, [ebx+esi+xfs_dir2_data_entry.inumber.hi]
.error:
.quit:
ret
endp
proc xfs._.lookup_leaf uses ebx esi edi, _name, _len
movbe ecx, [ebx+xfs_inode.di_core.di_nextents]
add ebx, [ebp+XFS.inode_core_size]
mov eax, [ebp+XFS.dir2_leaf_offset_blocks.lo]
mov [ebp+XFS.offset_begin.lo], ecx
mov eax, [ebp+XFS.dir2_leaf_offset_blocks.hi]
mov [ebp+XFS.offset_begin.hi], ecx
stdcall xfs._.extent_list.seek, ecx
stdcall xfs._.read_dirblock, [ebp+XFS.extent.br_startblock.lo], [ebp+XFS.extent.br_startblock.hi], [ebp+XFS.cur_dirblock]
jnz .error
mov ebx, [ebp+XFS.cur_dirblock]
movzx eax, [ebp+XFS.dir_leaf1_magic]
cmp [ebx+xfs_dir2_leaf.hdr.info.magic], ax
movi eax, ERROR_FS_FAIL
jnz .error
stdcall xfs_hashname, [_name+4], [_len]
cmp [ebp+XFS.version], 5
jz .v5
.v4:
movzx ecx, [ebx+xfs_dir2_leaf.hdr.count]
xchg cl, ch
add ebx, xfs_dir2_leaf.ents
jmp .vcommon
.v5:
movzx ecx, [ebx+xfs_dir3_leaf.hdr.count]
xchg cl, ch
add ebx, xfs_dir3_leaf.ents
.vcommon:
stdcall xfs._.get_addr_by_hash, ebx, ecx
jnz .error
stdcall xfs._.get_inode_by_addr, [ebp+XFS.cur_inode_save]
.quit:
.error:
ret
endp
proc xfs._.lookup_node uses ebx esi edi, _name, _len
locals
.hash dd ?
endl
mov [ebp+XFS.cur_inode_save], ebx
stdcall xfs_hashname, [_name+4], [_len]
mov [.hash], eax
mov eax, ebx
add eax, [ebp+XFS.inode_core_size]
movbe edx, [ebx+xfs_inode.di_core.di_nextents]
mov esi, [ebp+XFS.dir2_leaf_offset_blocks.lo]
.begin:
mov ebx, [ebp+XFS.cur_inode_save]
mov eax, ebx
add eax, [ebp+XFS.inode_core_size]
movbe edx, [ebx+xfs_inode.di_core.di_nextents]
mov ebx, eax
mov [ebp+XFS.offset_begin.lo], esi
mov [ebp+XFS.offset_begin.hi], 0
stdcall xfs._.extent_list.seek, edx
stdcall xfs._.read_dirblock, [ebp+XFS.extent.br_startblock.lo], [ebp+XFS.extent.br_startblock.hi], [ebp+XFS.cur_dirblock]
jnz .error
mov ebx, [ebp+XFS.cur_dirblock]
movzx eax, [ebp+XFS.da_node_magic]
cmp [ebx+xfs_da_intnode.hdr.info.magic], ax
jz .node
movzx eax, [ebp+XFS.dir_leafn_magic]
cmp [ebx+xfs_dir2_leaf.hdr.info.magic], ax
jz .leaf
movi eax, ERROR_FS_FAIL
jmp .error
.node:
cmp [ebp+XFS.version], 5
jz .node.v5
.node.v4:
lea eax, [ebx+sizeof.xfs_da_intnode]
movzx edx, [ebx+xfs_da_intnode.hdr.count]
jmp .node.vcommon
.node.v5:
lea eax, [ebx+sizeof.xfs_da3_intnode]
movzx edx, [ebx+xfs_da3_intnode.hdr.count]
.node.vcommon:
xchg dl, dh
stdcall xfs._.get_before_by_hashval, eax, edx, [.hash]
jnz .error
mov esi, eax
jmp .begin
.leaf:
cmp [ebp+XFS.version], 5
jz .leaf.v5
.leaf.v4:
movzx ecx, [ebx+xfs_dir2_leaf.hdr.count]
xchg cl, ch
add ebx, xfs_dir2_leaf.ents
jmp .leaf.vcommon
.leaf.v5:
movzx ecx, [ebx+xfs_dir3_leaf.hdr.count]
xchg cl, ch
add ebx, xfs_dir3_leaf.ents
.leaf.vcommon:
mov eax, [.hash]
stdcall xfs._.get_addr_by_hash, ebx, ecx
jnz .error
stdcall xfs._.get_inode_by_addr, [ebp+XFS.cur_inode_save]
.quit:
cmp esp, esp
ret
.error:
test esp, esp
ret
endp
proc xfs._.lookup_btree uses ebx esi edi, _name, _len
locals
.hash dd ?
endl
mov [ebp+XFS.cur_inode_save], ebx
stdcall xfs_hashname, [_name+4], [_len]
mov [.hash], eax
mov edx, [ebp+XFS.dir2_leaf_offset_blocks.hi]
mov eax, [ebp+XFS.dir2_leaf_offset_blocks.lo]
jmp .next_level.first
.next_level:
lea eax, [ebx+sizeof.xfs_da_intnode]
movzx edx, [ebx+xfs_da_intnode.hdr.count]
xchg dl, dh
stdcall xfs._.get_before_by_hashval, eax, edx, [.hash]
jnz .error
xor edx, edx
.next_level.first:
mov ebx, [ebp+XFS.cur_inode_save]
movzx ecx, [ebx+xfs_inode.di_core.di_forkoff]
shl ecx, 3
test ecx, ecx
jnz @f
mov ecx, [ebp+XFS.inodesize]
sub ecx, xfs_inode.di_u
@@:
add ebx, xfs_inode.di_u
stdcall xfs._.btree_read_block, ebx, ecx, eax, edx, [ebp+XFS.cur_dirblock]
mov ebx, [ebp+XFS.cur_dirblock]
cmp [ebx+xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC
jz .next_level
cmp [ebx+xfs_dir2_leaf.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC
jz .leafn
cmp [ebx+xfs_dir2_leaf.hdr.info.magic], XFS_DIR2_LEAF1_MAGIC
jnz .error
mov eax, [.hash]
movzx ecx, [ebx+xfs_dir2_leaf.hdr.count]
xchg cl, ch
add ebx, xfs_dir2_leaf.ents
stdcall xfs._.get_addr_by_hash, ebx, ecx
jnz .error
mov ebx, [ebp+XFS.cur_dirblock]
jmp .got_addr
.leafn:
movzx ecx, [ebx+xfs_dir2_leaf.hdr.count]
xchg cl, ch
add ebx, xfs_dir2_leaf.ents
mov eax, [.hash]
stdcall xfs._.get_addr_by_hash, ebx, ecx
jnz .error
mov ebx, [ebp+XFS.cur_block]
.got_addr:
stdcall xfs._.get_inode_by_addr, [ebp+XFS.cur_inode_save]
.quit:
cmp esp, esp
ret
.error:
test esp, esp
ret
endp
; search for the _name in _inode dir
; called for each /path/component/to/my/file
; out:
; ZF/zf = ok/fail
; edx:eax = inode/garbage:error
proc xfs._.get_inode_short uses esi, _inode:qword, _len, _name
mov esi, [_name]
mov eax, dword[_inode+DQ.lo]
mov edx, dword[_inode+DQ.hi]
stdcall xfs_read_inode, eax, edx, [ebp+XFS.cur_inode]
test eax, eax
movi eax, ERROR_FS_FAIL
jnz .error
; switch directory ondisk format
mov ebx, edx
mov [ebp+XFS.cur_inode_save], ebx
movzx eax, [ebx+xfs_inode.di_core.di_format]
cmp eax, XFS_DINODE_FMT_LOCAL
mov edi, xfs._.lookup_sf
jz .lookup
cmp eax, XFS_DINODE_FMT_BTREE
mov edi, xfs._.lookup_btree
jz .lookup
cmp eax, XFS_DINODE_FMT_EXTENTS
jnz .error
call xfs._.get_last_dirblock
test eax, eax
mov edi, xfs._.lookup_block
jz .lookup
cmp edx, [ebp+XFS.dir2_free_offset_blocks.hi]
mov edi, xfs._.lookup_node
ja .lookup
cmp eax, [ebp+XFS.dir2_free_offset_blocks.lo]
jae .lookup
mov edi, xfs._.lookup_leaf
.lookup:
stdcall edi, [_name+4], [_len]
.error:
ret
endp
; ZF/zf = ok/fail
; edx:eax = inode/garbage:error
proc xfs_get_inode uses ebx esi edi, _name
; call *._.get_inode_short until file is found / error returned
; start from the root inode
mov eax, [ebp+XFS.rootino.lo]
mov edx, [ebp+XFS.rootino.hi]
mov esi, [_name]
.next_dir:
@@:
cmp byte[esi], '/'
jnz @f
inc esi
jmp @b
@@:
cmp byte[esi], 0
jz .found
push esi
inc esi
@@:
cmp byte[esi], 0
jz @f
cmp byte[esi], '/'
jz @f
inc esi
jmp @b
@@:
mov ecx, esi
sub ecx, [esp]
mov [ebp+XFS.inode_self.lo], eax
mov [ebp+XFS.inode_self.hi], edx
stdcall xfs._.get_inode_short, eax, edx, ecx ; esi pushed above
jz .next_dir
.error:
.found:
ret
endp
; in: ebp = pointer to XFS structure
; in: esi
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
; out: [edx] -- f70.1 out structure
proc xfs_ReadFolder uses esi edi
call xfs._.lock
stdcall xfs_get_inode, esi
jnz .error
stdcall xfs_read_inode, eax, edx, [ebp+XFS.cur_inode]
test eax, eax
jnz .error
stdcall xfs._.readdir, [ebx+f70s1arg.start_idx], [ebx+f70s1arg.count], [ebx+f70s1arg.buf], edx, [ebx+f70s1arg.encoding]
test eax, eax
jnz .error
mov edx, [ebx+f70s1arg.buf]
mov ecx, [ebx+f70s1arg.count]
cmp [edx+bdfe_hdr.read_cnt], ecx
jz .quit
movi eax, ERROR_END_OF_FILE
.quit:
mov ebx, [edx+bdfe_hdr.read_cnt]
.error:
push eax
call xfs._.unlock
pop eax
ret
endp
; edx -- pointer to inode number in big endian
; ZF -- must be set at exit
proc xfs._.get_inode_number_sf
cmp [ebx+xfs_dir2_sf.hdr.i8count], 0
jz .i4bytes
.i8bytes:
movbe eax, [edx+DQ.hi]
movbe edx, [edx+DQ.lo]
ret
.i4bytes:
movbe eax, [edx+DQ.lo]
xor edx, edx
ret
endp
proc xfs_get_inode_info uses ebx, _src, _dst
; get access time and other file properties
; useful for browsing directories
; called for each dir entry
xor eax, eax
mov edx, [_src]
movzx ecx, [edx+xfs_inode.di_core.di_mode]
xchg cl, ch
test ecx, S_IFDIR
jz @f
movi eax, 0x10 ; set directory flag
@@:
mov edi, [_dst]
mov [edi+bdfe.attr], eax
movbe eax, [edx+xfs_inode.di_core.di_size.lo]
mov [edi+bdfe.size.hi], eax
movbe eax, [edx+xfs_inode.di_core.di_size.hi]
mov [edi+bdfe.size.lo], eax
add edi, 8
movbe eax, [edx+xfs_inode.di_core.di_ctime.t_sec]
push edx
sub eax, 978307200 ; 01.01.1970-01.01.2001 = (365*31+8)*24*60*60
call fsTime2bdfe
pop edx
movbe eax, [edx+xfs_inode.di_core.di_atime.t_sec]
push edx
sub eax, 978307200 ; 01.01.1970-01.01.2001 = (365*31+8)*24*60*60
call fsTime2bdfe
pop edx
movbe eax, [edx+xfs_inode.di_core.di_mtime.t_sec]
push edx
sub eax, 978307200 ; 01.01.1970-01.01.2001 = (365*31+8)*24*60*60
call fsTime2bdfe
pop edx
movi eax, ERROR_SUCCESS
cmp esp, esp
ret
endp
proc xfs._.extent_unpack uses eax ebx ecx edx, _extent_data
; extents come as packet 128bit bitfields
; unpack them to access internal fields
; write result to the XFS.extent structure
mov ebx, [_extent_data]
xor eax, eax
movbe edx, [ebx+0]
test edx, 0x80000000 ; mask, see documentation
setnz al
mov [ebp+XFS.extent.br_state], eax
and edx, 0x7fffffff ; mask
movbe eax, [ebx+4]
shrd eax, edx, 9
shr edx, 9
mov [ebp+XFS.extent.br_startoff.lo], eax
mov [ebp+XFS.extent.br_startoff.hi], edx
movbe edx, [ebx+4]
movbe eax, [ebx+8]
movbe ecx, [ebx+12]
and edx, 0x000001ff ; mask
shrd ecx, eax, 21
shrd eax, edx, 21
mov [ebp+XFS.extent.br_startblock.lo], ecx
mov [ebp+XFS.extent.br_startblock.hi], eax
movbe eax, [ebx+12]
and eax, 0x001fffff ; mask
mov [ebp+XFS.extent.br_blockcount], eax
ret
endp
proc xfs_hashname uses ecx esi, _name, _len
xor eax, eax
mov esi, [_name]
mov ecx, [_len]
@@:
rol eax, 7
xor al, [esi]
add esi, 1
dec ecx
jnz @b
ret
endp
; eax -- hash value
proc xfs._.get_addr_by_hash uses ebx esi, _base, _len
; look for the directory entry offset by its file name hash
; allows fast file search for block, leaf and node directories
; binary (ternary) search
mov ebx, [_base]
mov edx, [_len]
.next:
mov ecx, edx
; jecxz .error
test ecx, ecx
jz .not_found
shr ecx, 1
movbe esi, [ebx+ecx*sizeof.xfs_dir2_leaf_entry+xfs_dir2_leaf_entry.hashval]
cmp eax, esi
jb .below
ja .above
movbe eax, [ebx+ecx*sizeof.xfs_dir2_leaf_entry+xfs_dir2_leaf_entry.address]
ret
.below:
mov edx, ecx
jmp .next
.above:
lea ebx, [ebx+(ecx+1)*sizeof.xfs_dir2_leaf_entry]
sub edx, ecx
dec edx
jmp .next
.not_found:
movi eax, ERROR_FILE_NOT_FOUND
test esp, esp
ret
endp
;----------------------------------------------------------------
; xfs_GetFileInfo: XFS implementation of getting file info
; in: ebp = pointer to XFS structure
; in: esi = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
proc xfs_GetFileInfo uses ecx edx esi edi
call xfs._.lock
stdcall xfs_get_inode, esi
jnz .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+f70s5arg.buf]
.quit:
call xfs._.unlock
xor eax, eax
ret
.error:
push eax
call xfs._.unlock
pop eax
ret
endp
proc xfs._.file.read_extent uses ebx ecx edx, _callback, _callback_data
mov eax, [ebp+XFS.file_offset.lo]
mov edx, [ebp+XFS.file_offset.hi]
mov esi, [ebp+XFS.extent.br_startoff.lo]
mov edi, [ebp+XFS.extent.br_startoff.hi]
mov ecx, [ebp+XFS.blocklog]
shld edi, esi, cl
shl esi, cl
cmp edx, edi
jb .hole
ja .try_head
cmp eax, esi
ja .try_head
jz .try_match
.hole:
sub esi, eax
sbb edi, edx
movi ecx, -1
test edi, edi
jnz @f
mov ecx, esi
@@:
cmp ecx, [ebp+XFS.bytes_to_read]
jbe @f
mov ecx, [ebp+XFS.bytes_to_read]
@@:
mov edi, [ebp+XFS.file_buffer]
xor eax, eax
sub [ebp+XFS.bytes_to_read], ecx
sub [ebp+XFS.bytes_left_in_file.lo], ecx
sbb [ebp+XFS.bytes_left_in_file.hi], 0
add [ebp+XFS.bytes_read], ecx
add [ebp+XFS.file_buffer], ecx
add [ebp+XFS.file_offset.lo], ecx
adc [ebp+XFS.file_offset.hi], 0
rep stosb
cmp [ebp+XFS.bytes_to_read], 0
jz .quit
jmp .try_match
.try_head:
mov eax, [ebp+XFS.file_offset.lo]
mov ecx, [ebp+XFS.blocksize]
dec ecx
test eax, ecx
jz .try_match
.head:
mov eax, [ebp+XFS.extent.br_startblock.lo]
mov edx, [ebp+XFS.extent.br_startblock.hi]
mov ebx, [ebp+XFS.cur_block_data]
stdcall xfs._.read_block
mov esi, [ebp+XFS.cur_block_data]
mov edi, [ebp+XFS.file_buffer]
mov eax, [ebp+XFS.file_offset.lo]
mov ecx, [ebp+XFS.blocksize]
dec ecx
and eax, ecx
add esi, eax
inc ecx
sub ecx, eax
cmp ecx, [ebp+XFS.bytes_to_read]
jbe @f
mov ecx, [ebp+XFS.bytes_to_read]
@@:
sub [ebp+XFS.bytes_to_read], ecx
sub [ebp+XFS.bytes_left_in_file.lo], ecx
sbb [ebp+XFS.bytes_left_in_file.hi], 0
add [ebp+XFS.bytes_read], ecx
add [ebp+XFS.file_buffer], ecx
add [ebp+XFS.file_offset.lo], ecx
adc [ebp+XFS.file_offset.hi], 0
rep movsb
add [ebp+XFS.extent.br_startoff.lo], 1
adc [ebp+XFS.extent.br_startoff.hi], 0
add [ebp+XFS.extent.br_startblock.lo], 1
adc [ebp+XFS.extent.br_startblock.hi], 0
dec [ebp+XFS.extent.br_blockcount]
; cmp [ebp+XFS.bytes_to_read], 0
jz .quit
.try_match:
mov eax, [ebp+XFS.bytes_to_read]
test eax, eax
jz .quit
cmp eax, [ebp+XFS.blocksize]
jb .tail
mov ecx, [ebp+XFS.blocklog]
shr eax, cl
cmp eax, [ebp+XFS.extent.br_blockcount]
jbe @f
mov eax, [ebp+XFS.extent.br_blockcount]
@@:
mov ecx, eax
mov eax, [ebp+XFS.extent.br_startblock.lo]
mov edx, [ebp+XFS.extent.br_startblock.hi]
mov ebx, [ebp+XFS.file_buffer]
push ecx
stdcall xfs._.read_blocks
pop eax
add [ebp+XFS.extent.br_startoff.lo], eax
adc [ebp+XFS.extent.br_startoff.hi], 0
add [ebp+XFS.extent.br_startblock.lo], eax
adc [ebp+XFS.extent.br_startblock.hi], 0
sub [ebp+XFS.extent.br_blockcount], eax
imul eax, [ebp+XFS.blocksize]
sub [ebp+XFS.bytes_to_read], eax
sub [ebp+XFS.bytes_left_in_file.lo], eax
sbb [ebp+XFS.bytes_left_in_file.hi], 0
add [ebp+XFS.bytes_read], eax
add [ebp+XFS.file_buffer], eax
add [ebp+XFS.file_offset.lo], eax
adc [ebp+XFS.file_offset.hi], 0
; cmp [ebp+XFS.bytes_to_read], 0
cmp [ebp+XFS.extent.br_blockcount], 0
jz .quit
.tail:
mov eax, [ebp+XFS.extent.br_startblock.lo]
mov edx, [ebp+XFS.extent.br_startblock.hi]
mov ebx, [ebp+XFS.cur_block_data]
stdcall xfs._.read_block
mov ecx, [ebp+XFS.bytes_to_read]
cmp [ebp+XFS.bytes_left_in_file.hi], 0
jnz @f
cmp ecx, [ebp+XFS.bytes_left_in_file.lo]
jbe @f
mov ecx, [ebp+XFS.bytes_left_in_file.lo]
@@:
mov esi, [ebp+XFS.cur_block_data]
mov edi, [ebp+XFS.file_buffer]
mov eax, ecx
rep movsb
add [ebp+XFS.bytes_read], eax
sub [ebp+XFS.bytes_to_read], eax
sub [ebp+XFS.bytes_left_in_file.lo], eax
sbb [ebp+XFS.bytes_left_in_file.hi], 0
add [ebp+XFS.file_buffer], eax
add [ebp+XFS.file_offset.lo], eax
adc [ebp+XFS.file_offset.hi], 0
add [ebp+XFS.extent.br_startoff.lo], 1
adc [ebp+XFS.extent.br_startoff.hi], 0
add [ebp+XFS.extent.br_startblock.lo], 1
adc [ebp+XFS.extent.br_startblock.hi], 0
dec [ebp+XFS.extent.br_blockcount]
.quit:
mov esi, [ebp+XFS.extent.br_startoff.lo]
mov edi, [ebp+XFS.extent.br_startoff.hi]
movi eax, ERROR_SUCCESS
cmp esp, esp
ret
endp
;----------------------------------------------------------------
; in: ebp = pointer to XFS structure
; in: esi = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
proc xfs_Read uses ecx edx esi edi
locals
.offset_begin DQ ?
.offset_end DQ ?
endl
call xfs._.lock
mov [ebp+XFS.bytes_read], 0
mov eax, [ebx+f70s0arg.count]
mov [ebp+XFS.bytes_to_read], eax
test eax, eax
jz .quit
mov eax, [ebx+f70s0arg.buf]
mov [ebp+XFS.file_buffer], eax
mov eax, [ebx+f70s0arg.offset.hi]
mov [ebp+XFS.file_offset.hi], eax
mov eax, [ebx+f70s0arg.offset.lo]
mov [ebp+XFS.file_offset.lo], eax
stdcall xfs_get_inode, esi
jnz .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
mov ebx, edx
; precompute .offset_begin
mov esi, [ebp+XFS.file_offset.lo]
mov edi, [ebp+XFS.file_offset.hi]
mov ecx, [ebp+XFS.blocklog]
shrd esi, edi, cl
shr edi, cl
mov [.offset_begin.lo], esi
mov [.offset_begin.hi], edi
; precompute .offset_end
mov esi, [ebp+XFS.file_offset.lo]
mov edi, [ebp+XFS.file_offset.hi]
add esi, [ebp+XFS.bytes_to_read]
adc edi, 0
mov ecx, [ebp+XFS.blocksize]
dec ecx
add esi, ecx
adc edi, 0
mov ecx, [ebp+XFS.blocklog]
shrd esi, edi, cl
shr edi, cl
mov [.offset_end.lo], esi
mov [.offset_end.hi], edi
movbe ecx, [ebx+xfs_inode.di_core.di_size.hi]
movbe edx, [ebx+xfs_inode.di_core.di_size.lo]
mov [ebp+XFS.bytes_left_in_file.lo], ecx
mov [ebp+XFS.bytes_left_in_file.hi], edx
sub ecx, [ebp+XFS.file_offset.lo]
sbb edx, [ebp+XFS.file_offset.hi]
movi eax, ERROR_END_OF_FILE
jb .error
mov [ebp+XFS.eof], 0
test edx, edx
jnz @f
cmp ecx, [ebp+XFS.bytes_to_read]
jae @f
mov [ebp+XFS.eof], ERROR_END_OF_FILE
mov [ebp+XFS.bytes_to_read], ecx
@@:
cmp [ebx+xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE
jz .btree
.extent_list:
mov eax, ebx
add eax, [ebp+XFS.inode_core_size]
movbe edx, [ebx+xfs_inode.di_core.di_nextents]
mov ecx, [.offset_begin.lo]
mov [ebp+XFS.offset_begin.lo], ecx
mov ecx, [.offset_begin.hi]
mov [ebp+XFS.offset_begin.hi], ecx
mov ecx, [.offset_end.lo]
mov [ebp+XFS.offset_end.lo], ecx
mov ecx, [.offset_end.hi]
mov [ebp+XFS.offset_end.hi], ecx
stdcall xfs._.walk_extent_list, edx, eax, xfs._.file.read_extent, 0, 0
jnz .error
jmp .hole_check
.btree:
mov eax, [ebp+XFS.inodesize]
sub eax, [ebp+XFS.inode_core_size]
movzx ecx, [ebx+xfs_inode.di_core.di_forkoff]
jecxz @f
shl ecx, 3
mov eax, ecx
@@:
mov edx, ebx
add edx, [ebp+XFS.inode_core_size]
mov ecx, [.offset_begin.lo]
mov [ebp+XFS.offset_begin.lo], ecx
mov ecx, [.offset_begin.hi]
mov [ebp+XFS.offset_begin.hi], ecx
mov ecx, [.offset_end.lo]
mov [ebp+XFS.offset_end.lo], ecx
mov ecx, [.offset_end.hi]
mov [ebp+XFS.offset_end.hi], ecx
stdcall xfs._.walk_btree, edx, eax, xfs._.file.read_extent, 0, 0, 1
.hole_check:
cmp [ebp+XFS.bytes_left_in_file.hi], 0
jnz @f
cmp [ebp+XFS.bytes_left_in_file.lo], 0
jz .hole_done
@@:
cmp [ebp+XFS.bytes_to_read], 0
jz .hole_done
mov ebx, [ebp+XFS.cur_inode_save]
movbe edx, [ebx+xfs_inode.di_core.di_size.lo]
movbe eax, [ebx+xfs_inode.di_core.di_size.hi]
sub eax, [ebp+XFS.file_offset.lo]
sbb edx, [ebp+XFS.file_offset.hi]
jc .hole_done
mov ecx, [ebp+XFS.bytes_to_read]
test edx, edx
jnz .hole_read
cmp eax, [ebp+XFS.bytes_to_read]
jae .hole_read
mov ecx, eax
jmp .hole_read
.hole_read:
sub [ebp+XFS.bytes_to_read], ecx
add [ebp+XFS.bytes_read], ecx
mov edi, [ebp+XFS.file_buffer]
xor eax, eax
rep stosb
.hole_done:
.quit:
mov eax, [ebp+XFS.eof]
.error:
push eax
call xfs._.unlock
pop eax
mov ebx, [ebp+XFS.bytes_read]
ret
endp
proc xfs._.leafn_calc_entries uses ebx ecx edx esi edi, _cur_dirblock, _offset_lo, _offset_hi, _arg
mov edx, [_cur_dirblock]
movzx eax, [ebp+XFS.da_node_magic]
cmp [edx+xfs_dir2_leaf.hdr.info.magic], ax
jz .quit
cmp [ebp+XFS.version], 5
jnz @f
add edx, xfs_dir3_leaf.hdr.count-xfs_dir2_leaf.hdr.count
@@:
movzx eax, [edx+xfs_dir2_leaf.hdr.count]
movzx ecx, [edx+xfs_dir2_leaf.hdr.stale]
xchg al, ah
xchg cl, ch
sub eax, ecx
add [ebp+XFS.entries_read], eax
.quit:
movi eax, ERROR_SUCCESS
cmp esp, esp
ret
endp
proc xfs._.get_before_by_hashval uses ebx edx esi edi, _base, _count, _hash
mov edi, [_hash]
mov edx, [_count]
xor ecx, ecx
.node.next:
movbe eax, [ebx+xfs_da_intnode.btree+ecx*sizeof.xfs_da_node_entry+xfs_da_node_entry.hashval]
cmp [ebp+XFS.version], 5
jnz @f
movbe eax, [ebx+xfs_da3_intnode.btree+ecx*sizeof.xfs_da_node_entry+xfs_da_node_entry.hashval]
@@:
cmp eax, edi
ja .node.leaf_found
inc ecx
cmp ecx, edx
jnz .node.next
movi eax, ERROR_FILE_NOT_FOUND
test esp, esp
jmp .error
.node.leaf_found:
movbe eax, [ebx+xfs_da_intnode.btree+ecx*sizeof.xfs_da_node_entry+xfs_da_node_entry.before]
cmp [ebp+XFS.version], 5
jnz @f
movbe eax, [ebx+xfs_da3_intnode.btree+ecx*sizeof.xfs_da_node_entry+xfs_da_node_entry.before]
@@:
jmp .quit
.error:
test esp, esp
ret
.quit:
cmp esp, esp
ret
endp
proc xfs._.long_btree.seek uses ebx esi edi, _ptr, _size
mov ebx, [_ptr]
mov esi, [_size]
sub esi, sizeof.xfs_bmdr_block
shr esi, 4
shl esi, 3
movzx eax, [ebx+xfs_bmdr_block.bb_level]
movzx ecx, [ebx+xfs_bmdr_block.bb_numrecs]
xchg cl, ch
add ebx, sizeof.xfs_bmdr_block
jmp .common
.not_root:
mov esi, [ebp+XFS.blocksize]
sub esi, sizeof.xfs_bmbt_block
shr esi, 4
shl esi, 3
movzx eax, [ebx+xfs_bmbt_block.bb_level]
movzx ecx, [ebx+xfs_bmbt_block.bb_numrecs]
xchg cl, ch
add ebx, sizeof.xfs_bmbt_block
.common:
test eax, eax
jz .leaf
.node:
.next_rec:
dec ecx
js .error
movbe eax, [ebx+ecx*sizeof.xfs_bmbt_key+xfs_bmbt_key.br_startoff.lo]
cmp [ebp+XFS.offset_begin.hi], eax
ja .node_found
jb .next_rec
movbe eax, [ebx+ecx*sizeof.xfs_bmbt_key+xfs_bmbt_key.br_startoff.hi]
cmp [ebp+XFS.offset_begin.lo], eax
jae .node_found
jmp .next_rec
.node_found:
add ebx, esi
movbe edx, [ebx+ecx*sizeof.xfs_bmbt_ptr+xfs_bmbt_ptr.lo]
movbe eax, [ebx+ecx*sizeof.xfs_bmbt_ptr+xfs_bmbt_ptr.hi]
mov ebx, [ebp+XFS.cur_block]
stdcall xfs._.read_block
test eax, eax
jnz .error
mov ebx, [ebp+XFS.cur_block]
jmp .not_root
.leaf:
jmp .quit
.error:
.quit:
ret
endp
proc xfs._.walk_btree uses ebx esi edi, _ptr, _size, _callback_extent, _callback_block, _callback_data, _is_root
stdcall xfs._.long_btree.seek, [_ptr+4], [_size]
mov [_is_root], 0
.begin:
mov ebx, [ebp+XFS.cur_block]
mov eax, [ebp+XFS.bmap_magic]
cmp [ebx+xfs_bmbt_block.bb_magic], eax
movi eax, ERROR_FS_FAIL
jnz .error
movzx ecx, [ebx+xfs_bmbt_block.bb_numrecs]
xchg cl, ch
add ebx, [ebp+XFS.bmbt_block_size]
stdcall xfs._.walk_extent_list, ecx, ebx, [_callback_extent+8], [_callback_block+4], [_callback_data]
jnz .error
mov esi, [ebp+XFS.offset_begin.lo]
mov edi, [ebp+XFS.offset_begin.hi]
cmp edi, [ebp+XFS.offset_end.hi]
ja .quit
cmp esi, [ebp+XFS.offset_end.lo]
jae .quit
sub ebx, [ebp+XFS.bmbt_block_size]
movbe edx, [ebx+xfs_bmbt_block.bb_rightsib.lo]
movbe eax, [ebx+xfs_bmbt_block.bb_rightsib.hi]
mov ecx, eax
and ecx, edx
inc ecx
jz .quit
mov ebx, [ebp+XFS.cur_block]
stdcall xfs._.read_block
jnz .error
jmp .begin
.error:
.quit:
ret
endp
proc xfs._.btree_read_block uses ebx esi edi, _tree, _size, _block_lo, _block_hi, _buf
mov eax, [_block_lo]
mov [ebp+XFS.offset_begin.lo], eax
mov eax, [_block_hi]
mov [ebp+XFS.offset_begin.hi], eax
stdcall xfs._.long_btree.seek, [_tree+4], [_size]
jnz .error
mov ebx, [ebp+XFS.cur_block]
mov eax, [ebp+XFS.bmap_magic]
cmp [ebx+xfs_bmbt_block.bb_magic], eax
jnz .error
movzx ecx, [ebx+xfs_bmbt_block.bb_numrecs]
xchg cl, ch
add ebx, [ebp+XFS.bmbt_block_size]
mov eax, [_block_lo]
mov [ebp+XFS.offset_begin.lo], eax
mov eax, [_block_hi]
mov [ebp+XFS.offset_begin.hi], eax
stdcall xfs._.extent_list.seek, ecx
stdcall xfs._.read_dirblock, [ebp+XFS.extent.br_startblock.lo], [ebp+XFS.extent.br_startblock.hi], [_buf]
.error:
.quit:
ret
endp
proc xfs._.extent_list.seek uses esi, _count
sub ebx, sizeof.xfs_bmbt_rec
inc [_count]
.find_low:
add ebx, sizeof.xfs_bmbt_rec
dec [_count]
jz .quit
stdcall xfs._.extent_unpack, ebx
mov eax, [ebp+XFS.extent.br_startoff.lo]
mov edx, [ebp+XFS.extent.br_startoff.hi]
mov esi, [ebp+XFS.extent.br_blockcount]
add eax, esi
adc edx, 0
cmp edx, [ebp+XFS.offset_begin.hi]
ja .low_found
jb .find_low
cmp eax, [ebp+XFS.offset_begin.lo]
ja .low_found
jmp .find_low
.low_found:
add ebx, sizeof.xfs_bmbt_rec
mov eax, [ebp+XFS.offset_begin.lo]
mov edx, [ebp+XFS.offset_begin.hi]
mov esi, eax
sub esi, [ebp+XFS.extent.br_startoff.lo]
jbe .quit
; same br_blockcount for block and dirblock?
mov [ebp+XFS.extent.br_startoff.lo], eax
mov [ebp+XFS.extent.br_startoff.hi], edx
sub [ebp+XFS.extent.br_blockcount], esi
add [ebp+XFS.extent.br_startblock.lo], esi
adc [ebp+XFS.extent.br_startblock.hi], 0
jmp .quit
.quit:
mov eax, [_count]
ret
endp
proc xfs._.extent_iterate_dirblocks _callback, _callback_data
.check_high:
cmp edi, [ebp+XFS.offset_end.hi]
ja .quit
jb .read_dirblock
cmp esi, [ebp+XFS.offset_end.lo]
jae .quit
.read_dirblock:
stdcall xfs._.read_dirblock, [ebp+XFS.extent.br_startblock.lo], [ebp+XFS.extent.br_startblock.hi], [ebp+XFS.cur_dirblock]
mov edx, [ebp+XFS.cur_dirblock]
mov eax, [_callback]
stdcall eax, edx, esi, edi, [_callback_data]
test eax, eax
jnz .error
mov eax, [ebp+XFS.blkpdirblk]
add esi, eax
adc edi, 0
add [ebp+XFS.extent.br_startblock.lo], eax
adc [ebp+XFS.extent.br_startblock.hi], 0
sub [ebp+XFS.extent.br_blockcount], eax
jnz .check_high
.error:
.quit:
ret
endp
proc xfs._.walk_extent_list uses ebx esi edi, _count, _ptr, _callback_extent, _callback_block, _callback_data
mov ebx, [_ptr]
stdcall xfs._.extent_list.seek, [_count]
mov [_count], eax
dec [_count]
js .quit
jmp .next_extent.decoded
.next_extent:
stdcall xfs._.extent_unpack, ebx
add ebx, sizeof.xfs_bmbt_rec
.next_extent.decoded:
mov eax, [ebp+XFS.extent.br_blockcount]
add [ebp+XFS.offset_begin.lo], eax
adc [ebp+XFS.offset_begin.hi], 0
mov esi, [ebp+XFS.extent.br_startoff.lo]
mov edi, [ebp+XFS.extent.br_startoff.hi]
stdcall [_callback_extent+8], [_callback_block+4], [_callback_data]
jnz .error
cmp edi, [ebp+XFS.offset_end.hi]
ja .quit
jb @f
cmp esi, [ebp+XFS.offset_end.lo]
jae .quit
@@:
dec [_count]
js .quit
jmp .next_extent
.quit:
movi eax, ERROR_SUCCESS
.error:
test eax, eax
ret
endp
proc xfs._.get_last_dirblock uses ecx
movbe eax, [ebx+xfs_inode.di_core.di_nextents]
assert (sizeof.xfs_bmbt_rec AND (sizeof.xfs_bmbt_rec - 1)) = 0
shl eax, BSF sizeof.xfs_bmbt_rec
add eax, [ebp+XFS.inode_core_size]
lea eax, [ebx+eax-sizeof.xfs_bmbt_rec]
stdcall xfs._.extent_unpack, eax
xor edx, edx
mov eax, [ebp+XFS.extent.br_blockcount]
mov ecx, [ebp+XFS.dirblklog]
shr eax, cl
dec eax
add eax, [ebp+XFS.extent.br_startoff.lo]
adc edx, [ebp+XFS.extent.br_startoff.hi]
ret
endp
restore prologue@proc,epilogue@proc
restore movbe