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