360e379fc7
git-svn-id: svn://kolibrios.org@9047 a494cfbc-eb01-0410-851d-a64ba20cac60
2125 lines
67 KiB
NASM
2125 lines
67 KiB
NASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; ;;
|
|
;; 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
|