Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 4264 → Rev 4265

/kernel/branches/Kolibri-acpi/fs/ext2.inc
File deleted
Property changes:
Deleted: svn:keywords
-Rev
\ No newline at end of property
/kernel/branches/Kolibri-acpi/fs/ext2/blocks.inc
0,0 → 1,409
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains ext2 block handling code. ;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under the terms of the new BSD license. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;---------------------------------------------------------------------
; Write ext2 block from memory to disk.
; Input: eax = i_block (block number in ext2 terms);
; ebx = buffer address
; ebp = pointer to EXTFS
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_block_write:
push edx ebx ecx
 
mov edx, fs_write32_sys
jmp ext2_block_modify
 
;---------------------------------------------------------------------
; Read ext2 block from disk to memory.
; Input: eax = i_block (block number in ext2 terms);
; ebx = address of where to read block
; ebp = pointer to EXTFS
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_block_read:
push edx ebx ecx
mov edx, fs_read32_sys
jmp ext2_block_modify
 
;---------------------------------------------------------------------
; Modify ext2 block.
; Input: eax = i_block (block number in ext2 terms);
; ebx = I/O buffer address;
; edx = fs_read/write32_sys
; ebp = pointer to EXTFS
; edx, ebx, ecx on stack.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_block_modify:
; Get block number in hard-disk terms in eax.
mov ecx, [ebp + EXTFS.log_block_size]
shl eax, cl
mov ecx, eax
push [ebp + EXTFS.count_block_in_block]
 
@@:
mov eax, ecx
call edx
test eax, eax
jnz .fail
inc ecx
add ebx, 512
dec dword[esp]
jnz @B
 
xor eax, eax
@@:
pop ecx
pop ecx ebx edx
ret
 
.fail:
mov eax, ERROR_DEVICE
jmp @B
 
;---------------------------------------------------------------------
; Zeroes a block.
; Input: ebx = block ID.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_block_zero:
push ebx
 
mov eax, ebx
mov ebx, [ebp + EXTFS.ext2_temp_block]
 
call ext2_block_read
test eax, eax
jnz .return
 
push edi ecx
xor eax, eax
mov ecx, [ebp + EXTFS.block_size]
mov edi, [ebp + EXTFS.ext2_temp_block]
rep stosb
pop ecx edi
 
mov eax, [esp]
call ext2_block_write
 
.return:
pop ebx
ret
 
;---------------------------------------------------------------------
; Allocates a block.
; Input: eax = inode ID for "preference".
; ebp = pointer to EXTFS.
; Output: Block marked as set in block group.
; eax = error code.
; ebx = block ID.
;---------------------------------------------------------------------
ext2_block_alloc:
push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_count]
push EXT2_BLOCK_GROUP_DESC.free_blocks_count
push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group]
 
lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count]
push ebx
 
push ext2_bg_read_blk_bitmap
 
call ext2_resource_alloc
 
ret
 
;---------------------------------------------------------------------
; Zero-allocates a block.
; Input: eax = inode ID for "preference".
; ebp = pointer to EXTFS.
; Output: Block marked as set in block group.
; eax = error code.
; ebx = block ID.
;---------------------------------------------------------------------
ext2_block_calloc:
call ext2_block_alloc
test eax, eax
jnz @F
 
call ext2_block_zero
@@:
ret
 
;---------------------------------------------------------------------
; Frees a block.
; Input: eax = block ID.
; ebp = pointer to EXTFS.
; Output: Block marked as free in block group.
; eax = error code.
;---------------------------------------------------------------------
ext2_block_free:
push edi ecx
 
mov edi, ext2_bg_read_blk_bitmap
xor ecx, ecx
call ext2_resource_free
 
pop ecx edi
ret
 
;---------------------------------------------------------------------
; Find parent from file path in block.
; Input: esi = file path.
; ebx = pointer to directory block.
; ebp = pointer to EXTFS structure.
; Output: esi = name without parent, or not changed.
; ebx = directory record matched.
;---------------------------------------------------------------------
ext2_block_find_parent:
sub esp, 256 ; Space for EXT2 filename.
mov edx, ebx
add edx, [ebp + EXTFS.block_size] ; Save block end.
 
.start_rec:
cmp [ebx + EXT2_DIR_STRUC.inode], 0
jz .next_rec
 
mov edi, esp
push esi
movzx ecx, [ebx + EXT2_DIR_STRUC.name_len]
lea esi, [ebx + EXT2_DIR_STRUC.name]
call utf8_to_cp866
 
mov ecx, edi
lea edi, [esp + 4]
sub ecx, edi ; Number of bytes in resulting string.
 
mov esi, [esp]
 
; esi: original file path.
; edi: converted string stored on stack.
; ecx: size of converted string.
@@:
; If no bytes left in resulting string, test it.
jecxz .test_find
dec ecx
 
lodsb
call char_toupper
 
mov ah, [edi]
inc edi
xchg al, ah
call char_toupper
 
; If both are same, check next byte.
cmp al, ah
je @B
@@: ; Doesn't match.
pop esi
.next_rec:
movzx eax, [ebx + EXT2_DIR_STRUC.rec_len]
add ebx, eax ; Go to next record.
cmp ebx, edx ; Check if this is the end.
jb .start_rec
add esp, 256
ret
 
.test_find:
cmp byte [esi], 0
je .ret ; The end reached.
cmp byte [esi], '/' ; If not end of directory name, not matched.
jne @B
inc esi
 
.ret:
add esp, 256 + 4
ret
 
;---------------------------------------------------------------------
; Finds free space in a directory block, modifying last entry appropriately.
; Input: ebp = pointer to EXTFS.
; ecx = size of free space required.
; [EXTFS.ext2_temp_block] contains the block relevant.
; Output: edi = free entry.
; rec_len of free entry is set.
; eax = error code; if the block doesn't link to the next one, this is 0x00000001 on failure.
; ; else, 0xFFFFFFFF.
;---------------------------------------------------------------------
ext2_block_find_fspace:
push ebx edx
 
mov edi, [ebp + EXTFS.ext2_temp_block]
mov edx, edi
add edx, [ebp + EXTFS.block_size]
 
@@:
movzx eax, [edi + EXT2_DIR_STRUC.rec_len]
test eax, eax
jz .zero_len
 
cmp [edi + EXT2_DIR_STRUC.inode], 0
je .unused_entry
 
; It's a used entry, so see if we can fit it between current one and next.
; Subtract the size used by the name and the structure from rec_len.
movzx ebx, [edi + EXT2_DIR_STRUC.name_len]
add ebx, 8 + 3
and ebx, 0xfffffffc ; Align it on the next 4-byte boundary.
 
sub eax, ebx
add edi, ebx
cmp eax, ecx
jb .next_iter
 
sub edi, ebx
mov [edi + EXT2_DIR_STRUC.rec_len], bx ; Make previous entry point to us.
add edi, ebx
 
mov [edi + EXT2_DIR_STRUC.rec_len], ax ; Make current entry point to next one.
jmp .found
 
.unused_entry:
; It's an unused inode.
cmp eax, ecx
jge .found
 
.next_iter:
add edi, eax
cmp edi, edx
jb @B
 
.not_found:
xor eax, eax
not eax
jmp .ret
 
; Zero length entry means we have the rest of the block for us.
.zero_len:
mov eax, edx
sub eax, edi
 
; Point to next block.
mov [edi + EXT2_DIR_STRUC.rec_len], ax
 
cmp eax, ecx
jge .fits
 
mov [edi + EXT2_DIR_STRUC.inode], 0
 
; It doesn't fit, but the block doesn't link to the next block.
xor eax, eax
inc eax
jmp .ret
 
.fits:
mov [edi + EXT2_DIR_STRUC.rec_len], cx
 
.found:
xor eax, eax
 
.ret:
pop edx ebx
ret
 
;---------------------------------------------------------------------
; Gets the block group's descriptor.
; Input: eax = block group.
; Output: eax = if zero, error; else, points to block group descriptor.
; [EXTFS.ext2_temp_block] contains relevant block.
; ebp = pointer to EXTFS.
;---------------------------------------------------------------------
ext2_bg_read_desc:
push edx ebx
mov edx, 32
mul edx ; Get index of descriptor in global_desc_table.
 
; eax: block group descriptor offset relative to global descriptor table start
; Find the block this block descriptor is in.
div [ebp + EXTFS.block_size]
add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block]
inc eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail
 
add ebx, edx ; edx: local index of descriptor inside block
mov eax, ebx
 
.return:
pop ebx edx
ret
 
.fail:
xor eax, eax
jmp .return
 
;---------------------------------------------------------------------
; Writes a block group's descriptor.
; Input: eax = block group.
; [EXTFS.ext2_temp_data] contains the block relevant.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_bg_write_desc:
push edx ebx
mov edx, 32
mul edx ; Get index of descriptor in global_desc_table.
 
; eax: block group descriptor offset relative to global descriptor table start
; Find the block this block descriptor is in.
div [ebp + EXTFS.block_size]
add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block]
inc eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
 
.return:
pop ebx edx
ret
 
;---------------------------------------------------------------------
; Gets the block group's block bitmap.
; Input: eax = block group.
; Output: eax = if zero, error; else, points to block group descriptor.
; ebx = block bitmap's block (hard disk).
;---------------------------------------------------------------------
ext2_bg_read_blk_bitmap:
push ecx
 
call ext2_bg_read_desc
test eax, eax
jz .fail
 
mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.block_bitmap] ; Block number of block group bitmap - in ext2 terms.
 
.return:
pop ecx
ret
 
.fail:
xor eax, eax
jmp .return
 
;---------------------------------------------------------------------
; Updates superblock, plus backups.
; Input: ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_sb_update:
push ebx
 
mov eax, 2
lea ebx, [ebp + EXTFS.superblock]
call fs_write32_sys
 
pop ebx
ret
/kernel/branches/Kolibri-acpi/fs/ext2/ext2.asm
0,0 → 1,1718
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains ext2 initialization, plus syscall handling code. ;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under the terms of the new BSD license. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
include 'ext2.inc'
include 'blocks.inc'
include 'inode.inc'
include 'resource.inc'
 
iglobal
align 4
ext2_user_functions:
dd ext2_free
dd (ext2_user_functions_end - ext2_user_functions - 4) / 4
dd ext2_Read
dd ext2_ReadFolder
dd ext2_Rewrite
dd ext2_Write
dd ext2_SetFileEnd
dd ext2_GetFileInfo
dd ext2_SetFileInfo
dd 0
dd ext2_Delete
dd ext2_CreateFolder
ext2_user_functions_end:
endg
 
;---------------------------------------------------------------------
; Locks up an ext2 partition.
; Input: ebp = pointer to EXTFS.
;---------------------------------------------------------------------
proc ext2_lock
lea ecx, [ebp + EXTFS.lock]
jmp mutex_lock
endp
 
;---------------------------------------------------------------------
; Unlocks up an ext2 partition.
; Input: ebp = pointer to EXTFS.
;---------------------------------------------------------------------
proc ext2_unlock
lea ecx, [ebp + EXTFS.lock]
jmp mutex_unlock
endp
 
;---------------------------------------------------------------------
; Check if it's a valid ext* superblock.
; Input: ebp: first three fields of PARTITION structure.
; ebx + 512: points to 512-bytes buffer that can be used for anything.
; Output: eax: clear if can't create partition; set to EXTFS otherwise.
;---------------------------------------------------------------------
proc ext2_create_partition
push ebx
 
mov eax, 2 ; Superblock starts at 1024-bytes.
add ebx, 512 ; Get pointer to fs-specific buffer.
call fs_read32_sys
test eax, eax
jnz .fail
 
; Allowed 1KiB, 2KiB, 4KiB, 8KiB.
cmp [ebx + EXT2_SB_STRUC.log_block_size], 3
ja .fail
 
cmp [ebx + EXT2_SB_STRUC.magic], EXT2_SUPER_MAGIC
jne .fail
 
cmp [ebx + EXT2_SB_STRUC.state], EXT2_VALID_FS
jne .fail
 
; Can't have no inodes per group.
cmp [ebx + EXT2_SB_STRUC.inodes_per_group], 0
je .fail
 
; If incompatible features required, unusable superblock.
mov eax, [ebx + EXT2_SB_STRUC.feature_incompat]
test eax, not EXT4_FEATURE_INCOMPAT_SUPP
jz .setup
 
.fail:
; Not a (valid/usable) EXT2 superblock.
pop ebx
xor eax, eax
ret
 
.setup:
movi eax, sizeof.EXTFS
call malloc
test eax, eax
jz ext2_create_partition.fail
 
; Store the first sector field.
mov ecx, dword[ebp + PARTITION.FirstSector]
mov dword[eax + EXTFS.FirstSector], ecx
mov ecx, dword [ebp + PARTITION.FirstSector+4]
mov dword [eax + EXTFS.FirstSector+4], ecx
 
; The length field.
mov ecx, dword[ebp + PARTITION.Length]
mov dword[eax + EXTFS.Length], ecx
mov ecx, dword[ebp + PARTITION.Length+4]
mov dword[eax + EXTFS.Length+4], ecx
 
; The disk field.
mov ecx, [ebp + PARTITION.Disk]
mov [eax + EXTFS.Disk], ecx
 
mov [eax + EXTFS.FSUserFunctions], ext2_user_functions
 
push ebp esi edi
 
mov ebp, eax
lea ecx, [eax + EXTFS.lock]
call mutex_init
 
; Copy superblock from buffer to reserved memory.
mov esi, ebx
lea edi, [ebp + EXTFS.superblock]
mov ecx, 512/4
rep movsd
 
; Get total groups.
mov eax, [ebx + EXT2_SB_STRUC.blocks_count]
sub eax, [ebx + EXT2_SB_STRUC.first_data_block]
dec eax
xor edx, edx
div [ebx + EXT2_SB_STRUC.blocks_per_group]
inc eax
mov [ebp + EXTFS.groups_count], eax
 
; Get log(block_size), such that 1,2,3,4 equ 1KiB,2KiB,4KiB,8KiB.
mov ecx, [ebx + EXT2_SB_STRUC.log_block_size]
inc ecx
mov [ebp + EXTFS.log_block_size], ecx
 
; 512-byte blocks in ext2 blocks.
mov eax, 1
shl eax, cl
mov [ebp + EXTFS.count_block_in_block], eax
 
; Get block_size/4 (we'll find square later).
shl eax, 7
mov [ebp + EXTFS.count_pointer_in_block], eax
mov edx, eax
 
; Get block size.
shl eax, 2
mov [ebp + EXTFS.block_size], eax
 
; Save block size for 2 kernel_alloc calls.
push eax eax
 
mov eax, edx
mul edx
mov [ebp + EXTFS.count_pointer_in_block_square], eax
 
; Have temporary block storage for get_inode procedure, and one for global procedure.
KERNEL_ALLOC [ebp + EXTFS.ext2_save_block], .error
KERNEL_ALLOC [ebp + EXTFS.ext2_temp_block], .error
mov [ebp + EXTFS.partition_flags], 0x00000000
mov eax, [ebx + EXT2_SB_STRUC.feature_ro_compat]
and eax, not EXT2_FEATURE_RO_COMPAT_SUPP
jnz .read_only
 
mov eax, [ebx + EXT2_SB_STRUC.feature_incompat]
and eax, EXT4_FEATURE_INCOMPAT_W_NOT_SUPP
jz @F
 
.read_only:
; Mark as read-only.
or [ebp + EXTFS.partition_flags], EXT2_RO
@@:
mov ecx, [ebx + EXT2_SB_STRUC.blocks_per_group]
mov [ebp + EXTFS.blocks_per_group], ecx
 
movzx ecx, word[ebx + EXT2_SB_STRUC.inode_size]
mov [ebp + EXTFS.inode_size], ecx
 
; Allocate for three inodes (loop would be overkill).
push ecx ecx ecx
 
KERNEL_ALLOC [ebp + EXTFS.ext2_save_inode], .error
KERNEL_ALLOC [ebp + EXTFS.ext2_temp_inode], .error
KERNEL_ALLOC [ebp + EXTFS.root_inode], .error
 
; Read root inode.
mov ebx, eax
mov eax, EXT2_ROOT_INO
call ext2_inode_read
 
test eax, eax
jnz .error
 
;call ext2_sb_update
; Sync the disk.
;mov esi, [ebp + PARTITION.Disk]
;call disk_sync ; eax contains error code, if any.
 
mov eax, ebp ; Return pointer to EXTFS.
pop edi esi ebp ebx
ret
 
; Error in setting up.
.error:
; Free save block.
KERNEL_FREE [ebp + EXTFS.ext2_save_block], .fail
 
; Temporary block.
KERNEL_FREE [ebp + EXTFS.ext2_temp_block], .fail
 
; All inodes.
KERNEL_FREE [ebp + EXTFS.ext2_save_inode], .fail
KERNEL_FREE [ebp + EXTFS.ext2_temp_inode], .fail
KERNEL_FREE [ebp + EXTFS.root_inode], .fail
 
mov eax, ebp
call free
 
jmp .fail
endp
 
; FUNCTIONS PROVIDED BY SYSCALLS.
 
;---------------------------------------------------------------------
; Frees up all ext2 structures.
; Input: eax = pointer to EXTFS.
;---------------------------------------------------------------------
proc ext2_free
push ebp
 
xchg ebp, eax
stdcall kernel_free, [ebp+EXTFS.ext2_save_block]
stdcall kernel_free, [ebp+EXTFS.ext2_temp_block]
stdcall kernel_free, [ebp+EXTFS.ext2_save_inode]
stdcall kernel_free, [ebp+EXTFS.ext2_temp_inode]
stdcall kernel_free, [ebp+EXTFS.root_inode]
 
xchg ebp, eax
call free
 
pop ebp
ret
endp
 
;---------------------------------------------------------------------
; Read disk folder.
; Input: ebp = pointer to EXTFS structure.
; esi + [esp + 4] = file name.
; ebx = pointer to parameters from sysfunc 70.
; Output: ebx = blocks read (or 0xFFFFFFFF, folder not found)
; eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_ReadFolder:
;DEBUGF 1, "Reading folder.\n"
call ext2_lock
cmp byte [esi], 0
jz .root_folder
 
push ebx
stdcall ext2_inode_find, [esp + 4 + 4] ; Get inode.
pop ebx
 
mov esi, [ebp + EXTFS.ext2_save_inode]
test eax, eax
jnz .error_ret
 
; If not a directory, then return with error.
test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR
jz .error_not_found
jmp @F
 
.root_folder:
mov esi, [ebp + EXTFS.root_inode]
test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR
jz .error_root
 
; Copy the inode.
mov edi, [ebp + EXTFS.ext2_save_inode]
mov ecx, [ebp + EXTFS.inode_size]
shr ecx, 2
push edi
rep movsd
pop esi
 
@@:
cmp [esi + EXT2_INODE_STRUC.i_size], 0 ; Folder is empty.
je .error_empty_dir
mov edx, [ebx + 16]
push edx ; Result address [edi + 28].
push 0 ; End of the current block in folder [edi + 24]
push dword[ebx + 12] ; Blocks to read [edi + 20]
push dword[ebx + 4] ; The first wanted file [edi + 16]
push dword[ebx + 8] ; Flags [edi + 12]
push 0 ; Read files [edi + 8]
push 0 ; Files in folder [edi + 4]
push 0 ; Number of blocks read in dir (and current block index) [edi]
 
; Fill header with zeroes.
mov edi, edx
mov ecx, 32/4
rep stosd
mov edi, esp ; edi = pointer to local variables.
add edx, 32 ; edx = mem to return.
 
xor ecx, ecx ; Get number of first block.
call ext2_inode_get_block
test eax, eax
jnz .error_get_block
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_read ; Read the block.
test eax, eax
jnz .error_get_block
 
mov eax, ebx ; esi: current directory record
add eax, [ebp + EXTFS.block_size]
mov [edi + 24], eax
 
mov ecx, [edi + 16] ; ecx = first wanted (flags ommited)
 
.find_wanted_start:
jecxz .find_wanted_end
.find_wanted_cycle:
cmp [ebx + EXT2_DIR_STRUC.inode], 0 ; Don't count unused inode in total files.
jz @F
 
inc dword [edi + 4] ; EXT2 files in folder.
dec ecx
@@:
movzx eax, [ebx + EXT2_DIR_STRUC.rec_len]
cmp eax, 12 ; Minimum record length.
jb .error_bad_len
test eax, 0x3 ; Record length must be divisible by four.
jnz .error_bad_len
 
sub [esi + EXT2_INODE_STRUC.i_size], eax ; Subtract "processed record" length directly from inode.
add ebx, eax ; Go to next record.
cmp ebx, [edi + 24] ; If not reached the next block, continue.
jb .find_wanted_start
 
push .find_wanted_start
.end_block: ; Get the next block.
cmp [esi + EXT2_INODE_STRUC.i_size], 0
jle .end_dir
 
inc dword [edi] ; Number of blocks read.
; Read the next block.
push ecx
mov ecx, [edi]
call ext2_inode_get_block
test eax, eax
jnz .error_get_block
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_read
test eax, eax
jnz .error_get_block
pop ecx
 
mov eax, ebx
add eax, [ebp + EXTFS.block_size]
mov [edi + 24], eax ; Update the end of the current block variable.
ret
 
.wanted_end:
loop .find_wanted_cycle ; Skip files till we reach wanted one.
; First requisite file.
.find_wanted_end:
mov ecx, [edi + 20]
.wanted_start: ; Look for first_wanted + count.
jecxz .wanted_end
 
cmp [ebx + EXT2_DIR_STRUC.inode], 0 ; if (inode == 0): not used;
jz .empty_rec
; Increment "files in dir" and "read files" count.
inc dword [edi + 8]
inc dword [edi + 4]
 
push edi ecx
mov edi, edx ; Zero out till the name field.
xor eax, eax
mov ecx, 40 / 4
rep stosd
pop ecx edi
 
push ebx edi edx
mov eax, [ebx + EXT2_DIR_STRUC.inode] ; Get the child inode.
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_read
test eax, eax
jnz .error_read_subinode
 
lea edi, [edx + 8]
 
mov eax, [ebx + EXT2_INODE_STRUC.i_ctime] ; Convert time in NTFS format.
xor edx, edx
add eax, 3054539008 ; (369 * 365 + 89) * 24 * 3600
adc edx, 2
call ntfs_datetime_to_bdfe.sec
 
mov eax, [ebx + EXT2_INODE_STRUC.i_atime]
xor edx, edx
add eax, 3054539008
adc edx, 2
call ntfs_datetime_to_bdfe.sec
 
mov eax, [ebx + EXT2_INODE_STRUC.i_mtime]
xor edx, edx
add eax, 3054539008
adc edx, 2
call ntfs_datetime_to_bdfe.sec
 
pop edx
test [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR ; If folder, don't report size.
jnz @F
 
mov eax, [ebx + EXT2_INODE_STRUC.i_size] ; Low size
stosd
mov eax, [ebx + EXT2_INODE_STRUC.i_dir_acl] ; High size
stosd
 
xor dword [edx], FS_FT_DIR ; Mark as file.
@@:
xor dword [edx], FS_FT_DIR ; Mark as directory.
 
; Copy name after converting from UTF-8 to CP866.
push ecx esi
mov esi, [esp + 12]
movzx ecx, [esi + EXT2_DIR_STRUC.name_len]
lea edi, [edx + 40]
lea esi, [esi + EXT2_DIR_STRUC.name]
call utf8_to_cp866
and byte [edi], 0
pop esi ecx edi ebx
 
cmp byte [edx + 40], '.' ; If it begins with ".", mark it as hidden.
jne @F
or dword [edx], FS_FT_HIDDEN
@@:
add edx, 40 + 264 ; Go to next record.
dec ecx
.empty_rec:
movzx eax, [ebx + EXT2_DIR_STRUC.rec_len]
 
cmp eax, 12 ; Illegal length.
jb .error_bad_len
test eax, 0x3 ; Not a multiple of four.
jnz .error_bad_len
 
sub [esi + EXT2_INODE_STRUC.i_size], eax ; Subtract directly from the inode.
add ebx, eax
cmp ebx, [edi + 24] ; Are we at the end of the block?
jb .wanted_start
 
push .wanted_start
jmp .end_block
 
.end_dir: ; End of the directory.
call ext2_unlock
mov edx, [edi + 28] ; Address of where to return data.
mov ebx, [edi + 8] ; EXT2_read_in_folder
mov ecx, [edi + 4] ; EXT2_files_in_folder
mov dword [edx], 1 ; Version
mov [edx + 4], ebx
mov [edx + 8], ecx
lea esp, [edi + 32]
xor eax, eax ; Reserved in current implementation.
lea edi, [edx + 12]
mov ecx, 20 / 4
rep stosd
 
;DEBUGF 1, "Returning with: %x.\n", eax
ret
 
.error_bad_len:
mov eax, ERROR_FS_FAIL
 
.error_read_subinode:
.error_get_block:
; Fix the stack.
lea esp, [edi + 32]
 
.error_ret:
or ebx, -1
push eax
call ext2_unlock
pop eax
;DEBUGF 1, "Returning with: %x.\n", eax
ret
.error_empty_dir: ; inode of folder without blocks.
.error_root: ; Root has to be a folder.
mov eax, ERROR_FS_FAIL
jmp .error_ret
 
.error_not_found: ; Directory not found.
mov eax, ERROR_FILE_NOT_FOUND
jmp .error_ret
 
;---------------------------------------------------------------------
; Read file from the hard disk.
; Input: esi + [esp + 4] = points to file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: ebx = bytes read (0xFFFFFFFF -> file not found)
; eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_Read:
;DEBUGF 1, "Attempting read.\n"
call ext2_lock
cmp byte [esi], 0
jnz @F
 
.this_is_nofile:
call ext2_unlock
or ebx, -1
mov eax, ERROR_ACCESS_DENIED
ret
 
@@:
push ebx
stdcall ext2_inode_find, [esp + 4 + 4]
pop ebx
 
mov esi, [ebp + EXTFS.ext2_save_inode]
test eax, eax
jz @F
 
call ext2_unlock
or ebx, -1
mov eax, ERROR_FILE_NOT_FOUND
ret
 
@@:
mov ax, [esi + EXT2_INODE_STRUC.i_mode]
and ax, EXT2_S_IFMT ; Leave the file format in AX.
 
; Check if file.
cmp ax, EXT2_S_IFREG
jne .this_is_nofile
 
mov edi, [ebx + 16]
mov ecx, [ebx + 12]
 
mov eax, [ebx + 4]
mov edx, [ebx + 8] ; edx:eax = start byte number.
 
; Check if file is big enough for us.
cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx
ja .size_greater
jb .size_less
 
cmp [esi + EXT2_INODE_STRUC.i_size], eax
ja .size_greater
 
.size_less:
call ext2_unlock
xor ebx, ebx
mov eax, ERROR_END_OF_FILE
ret
@@:
.size_greater:
add eax, ecx ; Get last byte.
adc edx, 0
 
; Check if we've to read whole file, or till requested.
cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx
ja .read_till_requested
jb .read_whole_file
cmp [esi + EXT2_INODE_STRUC.i_size], eax
jae .read_till_requested
 
.read_whole_file:
push 1 ; Read till the end of file.
mov ecx, [esi + EXT2_INODE_STRUC.i_size]
sub ecx, [ebx + 4] ; To read = (size - starting byte)
jmp @F
 
.read_till_requested:
push 0 ; Read as much as requested.
 
@@:
; ecx = bytes to read.
; edi = return memory
; [esi] = starting byte.
 
push ecx ; Number of bytes to read.
; Get part of the first block.
mov edx, [ebx + 8]
mov eax, [ebx + 4]
div [ebp + EXTFS.block_size]
 
push eax ; Save block counter to stack.
push ecx
mov ecx, eax
call ext2_inode_get_block
test eax, eax
jnz .error_at_first_block
 
mov ebx, [ebp + EXTFS.ext2_save_block]
mov eax, ecx
call ext2_block_read
test eax, eax
jnz .error_at_first_block
 
pop ecx
; Get index inside block.
add ebx, edx
 
neg edx
add edx, [ebp + EXTFS.block_size] ; Get number of bytes in this block.
 
; If it's smaller than total bytes to read, then only one block.
cmp ecx, edx
jbe .only_one_block
 
mov eax, ecx
sub eax, edx
mov ecx, edx
 
push esi
mov esi, ebx
rep movsb ; Copy part of 1st block.
pop esi
 
; eax -> bytes to read.
.calc_blocks_count:
mov ebx, edi ; Read the block in ebx.
xor edx, edx
div [ebp + EXTFS.block_size] ; Get number of bytes in last block in edx.
mov edi, eax ; Get number of blocks in edi.
 
@@:
; Test if all blocks are done.
test edi, edi
jz .finish_block
inc dword [esp]
mov ecx, [esp]
call ext2_inode_get_block
 
test eax, eax
jnz .error_at_read_cycle
 
mov eax, ecx ; ebx already contains desired values.
call ext2_block_read
 
test eax, eax
jnz .error_at_read_cycle
 
add ebx, [ebp + EXTFS.block_size]
 
dec edi
jmp @B
 
; In edx -- number of bytes in the last block.
.finish_block:
test edx, edx
jz .end_read
 
pop ecx ; Pop block counter in ECX.
inc ecx
call ext2_inode_get_block
test eax, eax
jnz .error_at_finish_block
 
mov edi, ebx
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_read
 
test eax, eax
jnz .error_at_finish_block
 
mov ecx, edx
mov esi, ebx
rep movsb ; Copy last piece of block.
jmp @F
 
.end_read:
pop ecx ; Pop block counter in ECX.
@@:
pop ebx ; Number of bytes read.
call ext2_unlock
pop eax ; If we were asked to read more, say EOF.
test eax, eax
jz @F
 
mov eax, ERROR_END_OF_FILE
ret
@@:
xor eax, eax
;DEBUGF 1, "Returning with: %x.\n", eax
ret
.only_one_block:
mov esi, ebx
rep movsb ; Copy last piece of block.
jmp .end_read
.error_at_first_block:
pop edx
.error_at_read_cycle:
pop ebx
.error_at_finish_block:
pop ecx edx
or ebx, -1
push eax
call ext2_unlock
pop eax
 
;DEBUGF 1, "Returning with: %x.\n", eax
ret
 
;---------------------------------------------------------------------
; Read file information from block device.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_GetFileInfo:
;DEBUGF 1, "Calling for file info, for: %s.\n", esi
call ext2_lock
mov edx, [ebx + 16]
cmp byte [esi], 0
jz .is_root
 
push edx
stdcall ext2_inode_find, [esp + 4 + 4]
mov ebx, edx
pop edx
mov esi, [ebp + EXTFS.ext2_save_inode]
test eax, eax
jz @F
 
push eax
call ext2_unlock
pop eax
;DEBUGF 1, "Returning with: %x.\n", eax
ret
 
.is_root:
xor ebx, ebx ; Clear out first char, since we don't want to set hidden flag on root.
mov esi, [ebp + EXTFS.root_inode]
 
@@:
xor eax, eax
mov edi, edx
mov ecx, 40/4
rep stosd ; Zero fill buffer.
 
cmp bl, '.'
jne @F
or dword [edx], FS_FT_HIDDEN
 
@@:
test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR
jnz @F ; If a directory, don't put in file size.
 
mov eax, [esi + EXT2_INODE_STRUC.i_size] ; Low file size.
mov ebx, [esi + EXT2_INODE_STRUC.i_dir_acl] ; High file size.
mov dword [edx+32], eax
mov dword [edx+36], ebx
 
xor dword [edx], FS_FT_DIR ; Next XOR will clean this, to mark it as a file.
@@:
xor dword [edx], FS_FT_DIR ; Mark as directory.
 
lea edi, [edx + 8]
 
; Store all time.
mov eax, [esi + EXT2_INODE_STRUC.i_ctime]
xor edx, edx
add eax, 3054539008
adc edx, 2
call ntfs_datetime_to_bdfe.sec
 
mov eax, [esi + EXT2_INODE_STRUC.i_atime]
xor edx, edx
add eax, 3054539008
adc edx, 2
call ntfs_datetime_to_bdfe.sec
 
mov eax, [esi + EXT2_INODE_STRUC.i_mtime]
xor edx, edx
add eax, 3054539008
adc edx, 2
call ntfs_datetime_to_bdfe.sec
 
call ext2_unlock
xor eax, eax
;DEBUGF 1, "Returning with: %x.\n", eax
ret
 
;---------------------------------------------------------------------
; Set file information for block device.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_SetFileInfo:
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
 
mov eax, ERROR_UNSUPPORTED_FS
ret
 
@@:
push edx esi edi ebx
call ext2_lock
mov edx, [ebx + 16]
 
; Is this read-only?
test [ebp + EXTFS.partition_flags], EXT2_RO
jnz .fail
 
; Not supported for root.
cmp byte [esi], 0
je .fail
 
.get_inode:
push edx
stdcall ext2_inode_find, [esp + 4 + 20]
pop edx
 
test eax, eax
jnz @F
 
; Save inode number.
push esi
mov esi, [ebp + EXTFS.ext2_save_inode]
 
; From the BDFE, we ignore read-only file flags, hidden file flags;
; We ignore system file flags, file was archived or not.
 
; Also ignored is file creation time. ext2 stores "inode modification"
; time in the ctime field, which is updated by the respective inode_write
; procedure, and any writes on it would be overwritten anyway.
 
; Access time.
lea edi, [esi + EXT2_INODE_STRUC.i_atime]
lea esi, [edx + 16]
call bdfe_to_unix_time
 
; Modification time.
add esi, 8
add edi, 8
call bdfe_to_unix_time
 
mov ebx, [ebp + EXTFS.ext2_save_inode] ; Get address of inode into ebx.
pop eax ; Get inode number in eax.
call ext2_inode_write ; eax contains error code, if any.
test eax, eax
jnz @F
 
call ext2_sb_update
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
 
@@:
push eax
call ext2_unlock
pop eax
 
pop ebx edi esi edx
ret
 
.fail:
call ext2_sb_update
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
 
mov eax, ERROR_UNSUPPORTED_FS
jmp @B
 
;---------------------------------------------------------------------
; Set file information for block device.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_Delete:
;DEBUGF 1, "Attempting Delete.\n"
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
 
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ebx ecx edx esi edi
call ext2_lock
 
add esi, [esp + 20 + 4]
 
; Can't delete root.
cmp byte [esi], 0
jz .error_access_denied
 
push esi
stdcall ext2_inode_find, 0
mov ebx, esi
pop esi
 
test eax, eax
jnz .error_access_denied
 
mov edx, [ebp + EXTFS.ext2_save_inode]
movzx edx, [edx + EXT2_INODE_STRUC.i_mode]
and edx, EXT2_S_IFMT ; Get the mask.
cmp edx, EXT2_S_IFDIR
jne @F ; If not a directory, we don't need to check if it's empty.
 
call ext2_dir_empty ; 0 means directory is empty.
 
test eax, eax
jnz .error_access_denied
 
@@:
; Find parent.
call ext2_inode_find_parent
test eax, eax
jnz .error_access_denied
mov eax, esi
 
; Save file/dir & parent inode.
push ebx eax
 
cmp edx, EXT2_S_IFDIR
jne @F
 
; Unlink '.'
mov eax, [esp + 4]
call ext2_inode_unlink
cmp eax, 0xFFFFFFFF
je .error_stack8
 
; Unlink '..'
mov eax, [esp + 4]
mov ebx, [esp]
call ext2_inode_unlink
cmp eax, 0xFFFFFFFF
je .error_stack8
 
@@:
pop eax
mov ebx, [esp]
; Unlink the inode.
call ext2_inode_unlink
cmp eax, 0xFFFFFFFF
je .error_stack4
; If hardlinks aren't zero, shouldn't completely free.
test eax, eax
jz @F
 
add esp, 4
jmp .disk_sync
 
@@:
; Read the inode.
mov eax, [esp]
mov ebx, [ebp + EXTFS.ext2_save_inode]
call ext2_inode_read
test eax, eax
jnz .error_stack4
; Free inode data.
mov esi, [ebp + EXTFS.ext2_save_inode]
xor ecx, ecx
 
@@:
push ecx
call ext2_inode_get_block
test eax, eax
jnz .error_stack8
mov eax, ecx
pop ecx
 
; If 0, we're done.
test eax, eax
jz @F
 
call ext2_block_free
test eax, eax
jnz .error_stack4
 
inc ecx
jmp @B
 
@@:
; Free indirect blocks.
call ext2_inode_free_indirect_blocks
test eax, eax
jnz .error_stack4
 
; Clear the inode, and add deletion time.
mov edi, [ebp + EXTFS.ext2_save_inode]
xor eax, eax
mov ecx, [ebp + EXTFS.inode_size]
rep stosb
 
mov edi, [ebp + EXTFS.ext2_save_inode]
add edi, EXT2_INODE_STRUC.i_dtime
call current_unix_time
 
; Write the inode.
mov eax, [esp]
mov ebx, [ebp + EXTFS.ext2_save_inode]
call ext2_inode_write
test eax, eax
jnz .error_stack4
 
; Check if directory.
cmp edx, EXT2_S_IFDIR
jne @F
 
; If it is, decrement used_dirs_count.
 
; Get block group.
mov eax, [esp]
dec eax
xor edx, edx
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group]
 
push eax
call ext2_bg_read_desc
test eax, eax
jz .error_stack8
 
dec [eax + EXT2_BLOCK_GROUP_DESC.used_dirs_count]
pop eax
call ext2_bg_write_desc
 
@@:
pop eax
call ext2_inode_free
test eax, eax
jnz .error_access_denied
 
.disk_sync:
call ext2_sb_update
 
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
 
.return:
push eax
call ext2_unlock
pop eax
 
pop edi esi edx ecx ebx
;DEBUGF 1, "And returning with: %x.\n", eax
ret
 
.error_stack8:
add esp, 4
.error_stack4:
add esp, 4
.error_access_denied:
call ext2_sb_update
 
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
 
mov eax, ERROR_ACCESS_DENIED
jmp .return
 
;---------------------------------------------------------------------
; Set file information for block device.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_CreateFolder:
;DEBUGF 1, "Attempting to create folder.\n"
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
 
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ebx ecx edx esi edi
call ext2_lock
 
add esi, [esp + 20 + 4]
 
; Can't create root, but for CreateFolder already existing directory is success.
cmp byte [esi], 0
jz .success
 
push esi
stdcall ext2_inode_find, 0
pop esi
 
; If the directory is there, we've succeeded.
test eax, eax
jz .success
 
; Find parent.
call ext2_inode_find_parent
test eax, eax
jnz .error
 
; Inode ID for preference.
mov eax, esi
call ext2_inode_alloc
test eax, eax
jnz .error_full
 
; Save allocated inode in EDX; filename is in EDI; parent ID in ESI.
mov edx, ebx
 
push edi
 
xor al, al
mov edi, [ebp + EXTFS.ext2_temp_inode]
mov ecx, [ebp + EXTFS.inode_size]
rep stosb
 
mov edi, [ebp + EXTFS.ext2_temp_inode]
add edi, EXT2_INODE_STRUC.i_atime
call current_unix_time
 
add edi, 8
call current_unix_time
 
pop edi
 
mov ebx, [ebp + EXTFS.ext2_temp_inode]
mov [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR or PERMISSIONS
mov eax, edx
call ext2_inode_write
test eax, eax
jnz .error
 
; Link to self.
push edx esi
 
mov eax, edx
mov ebx, eax
mov dl, EXT2_FT_DIR
mov esi, self_link
call ext2_inode_link
 
pop esi edx
 
test eax, eax
jnz .error
 
; Link to parent.
push edx esi
 
mov eax, ebx
mov ebx, esi
mov dl, EXT2_FT_DIR
mov esi, parent_link
call ext2_inode_link
 
pop esi edx
 
test eax, eax
jnz .error
 
; Link parent to child.
mov eax, esi
mov ebx, edx
mov esi, edi
mov dl, EXT2_FT_DIR
call ext2_inode_link
test eax, eax
jnz .error
 
; Get block group descriptor for allocated inode's block.
mov eax, ebx
dec eax
xor edx, edx
; EAX = block group.
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group]
mov edx, eax
 
call ext2_bg_read_desc
test eax, eax
jz .error
 
inc [eax + EXT2_BLOCK_GROUP_DESC.used_dirs_count]
mov eax, edx
call ext2_bg_write_desc
test eax, eax
jnz .error
 
.success:
call ext2_sb_update
 
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
 
.return:
push eax
call ext2_unlock
pop eax
 
pop edi esi edx ecx ebx
;DEBUGF 1, "Returning with: %x.\n", eax
ret
 
.error:
call ext2_sb_update
 
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
mov eax, ERROR_ACCESS_DENIED
jmp .return
 
.error_full:
mov eax, ERROR_DISK_FULL
jmp .return
 
self_link db ".", 0
parent_link db "..", 0
 
;---------------------------------------------------------------------
; Rewrite a file.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
; ebx = bytes written.
;---------------------------------------------------------------------
ext2_Rewrite:
;DEBUGF 1, "Attempting Rewrite.\n"
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
 
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ecx edx esi edi
pushad
 
call ext2_lock
 
add esi, [esp + 16 + 32 + 4]
; Can't create root.
cmp byte [esi], 0
jz .error_access_denied
 
push esi
stdcall ext2_inode_find, 0
pop esi
 
; If the file is there, delete it.
test eax, eax
jnz @F
 
pushad
 
push eax
call ext2_unlock
pop eax
 
push dword 0x00000000
call ext2_Delete
add esp, 4
 
push eax
call ext2_lock
pop eax
 
test eax, eax
jnz .error_access_denied_delete
 
popad
@@:
; Find parent.
call ext2_inode_find_parent
test eax, eax
jnz .error_access_denied
 
; Inode ID for preference.
mov eax, esi
call ext2_inode_alloc
test eax, eax
jnz .error_full
 
; Save allocated inode in EDX; filename is in EDI; parent ID in ESI.
mov edx, ebx
 
push edi
 
xor al, al
mov edi, [ebp + EXTFS.ext2_temp_inode]
mov ecx, [ebp + EXTFS.inode_size]
rep stosb
 
mov edi, [ebp + EXTFS.ext2_temp_inode]
add edi, EXT2_INODE_STRUC.i_atime
call current_unix_time
 
add edi, 8
call current_unix_time
 
pop edi
 
mov ebx, [ebp + EXTFS.ext2_temp_inode]
mov [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG or PERMISSIONS
mov eax, edx
call ext2_inode_write
test eax, eax
jnz .error
 
; Link parent to child.
mov eax, esi
mov ebx, edx
mov esi, edi
mov dl, EXT2_FT_REG_FILE
call ext2_inode_link
test eax, eax
jnz .error
 
popad
push eax
call ext2_unlock
pop eax
 
push dword 0x00000000
call ext2_Write
add esp, 4
 
push eax
call ext2_lock
pop eax
 
.success:
push eax
call ext2_sb_update
 
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
pop eax
 
.return:
push eax
call ext2_unlock
pop eax
 
pop edi esi edx ecx
 
;DEBUGF 1, "And returning with: %x.\n", eax
ret
 
.error:
mov eax, ERROR_ACCESS_DENIED
jmp .success
 
.error_access_denied_delete:
popad
 
.error_access_denied:
popad
xor ebx, ebx
 
mov eax, ERROR_ACCESS_DENIED
jmp .return
 
.error_full:
popad
xor ebx, ebx
 
mov eax, ERROR_DISK_FULL
jmp .return
 
;---------------------------------------------------------------------
; Write to a file.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
; ebx = number of bytes written.
;---------------------------------------------------------------------
ext2_Write:
;DEBUGF 1, "Attempting write, "
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
 
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ecx edx esi edi
call ext2_lock
 
add esi, [esp + 16 + 4]
 
; Can't write to root.
cmp byte [esi], 0
jz .error
 
push ebx ecx edx
stdcall ext2_inode_find, 0
pop edx ecx ebx
; If file not there, error.
xor ecx, ecx
test eax, eax
jnz .error_file_not_found
 
; Save the inode.
push esi
 
; Check if it's a file.
mov edx, [ebp + EXTFS.ext2_save_inode]
test [edx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG
jz .error
 
mov eax, esi
mov ecx, [ebx + 4]
 
call ext2_inode_extend
xor ecx, ecx
test eax, eax
jnz .error_device
 
; ECX contains the size to write, and ESI points to it.
mov ecx, [ebx + 0x0C]
mov esi, [ebx + 0x10]
 
; Save the size of the inode.
mov eax, [edx + EXT2_INODE_STRUC.i_size]
push eax
 
xor edx, edx
div [ebp + EXTFS.block_size]
 
test edx, edx
jz .start_aligned
 
; Start isn't aligned, so deal with the non-aligned bytes.
mov ebx, [ebp + EXTFS.block_size]
sub ebx, edx
 
cmp ebx, ecx
jbe @F
 
; If the size to copy fits in current block, limit to that, instead of the entire block.
mov ebx, ecx
 
@@:
; Copy EBX bytes, in EAX indexed block.
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jnz .error_inode_size
 
push ecx
mov ecx, ebx
mov edi, ebx
add edi, edx
rep movsb
 
pop ecx
 
; Write the block.
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
 
add [esp], ebx
sub ecx, ebx
jz .write_inode
 
.start_aligned:
cmp ecx, [ebp + EXTFS.block_size]
jb @F
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
 
push eax
mov edx, [esp + 8]
call ext2_inode_blank_entry
test eax, eax
pop eax
jnz .error_inode_size
 
push ecx
 
mov ecx, [ebp + EXTFS.block_size]
mov edi, [ebp + EXTFS.ext2_save_block]
rep movsb
 
pop ecx
 
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
 
mov eax, [ebp + EXTFS.block_size]
sub ecx, eax
add [esp], eax
jmp .start_aligned
 
; Handle the remaining bytes.
@@:
test ecx, ecx
jz .write_inode
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
 
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jz @F
 
push eax
mov edx, [esp + 8]
 
call ext2_inode_blank_entry
test eax, eax
pop eax
jnz .error_inode_size
 
@@:
push ecx
mov edi, [ebp + EXTFS.ext2_save_block]
rep movsb
pop ecx
 
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
 
add [esp], ecx
xor ecx, ecx
 
.write_inode:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
 
call ext2_inode_write
test eax, eax
jnz .error_device
 
.success:
call ext2_sb_update
 
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
 
.return:
push eax
call ext2_unlock
pop eax
 
add esp, 4
 
mov ebx, [esp + 12]
sub ebx, ecx
pop edi esi edx ecx
 
;DEBUGF 1, "and returning with: %x.\n", eax
ret
 
.error:
mov eax, ERROR_ACCESS_DENIED
jmp .return
 
.error_file_not_found:
mov eax, ERROR_FILE_NOT_FOUND
jmp .return
 
.error_inode_size:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
 
call ext2_inode_write
 
.error_device:
call ext2_sb_update
 
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
 
mov eax, ERROR_DEVICE
jmp .return
 
;---------------------------------------------------------------------
; Set the end of a file.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_SetFileEnd:
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
 
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ebx ecx edx esi edi
call ext2_lock
 
add esi, [esp + 20 + 4]
 
; Can't write to root.
cmp byte [esi], 0
jz .error
 
stdcall ext2_inode_find, 0
; If file not there, error.
test eax, eax
jnz .error_file_not_found
 
; Check if it's a file.
mov edx, [ebp + EXTFS.ext2_save_inode]
cmp [edx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG
jne .error
 
mov eax, esi
mov ecx, [ebx + 4]
call ext2_inode_extend
test eax, eax
jnz .error_disk_full
 
mov eax, esi
call ext2_inode_truncate
test eax, eax
jnz .error_disk_full
 
mov eax, esi
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_write
 
call ext2_sb_update
 
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
 
.return:
push eax
call ext2_unlock
pop eax
 
pop edi esi edx ecx ebx
ret
 
.error:
mov eax, ERROR_ACCESS_DENIED
jmp .return
 
.error_file_not_found:
mov eax, ERROR_FILE_NOT_FOUND
jmp .return
 
.error_disk_full:
call ext2_sb_update
 
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
 
mov eax, ERROR_DISK_FULL
jmp .return
/kernel/branches/Kolibri-acpi/fs/ext2/ext2.inc
0,0 → 1,670
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains ext2 structures, and macros. ;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under the terms of the new BSD license. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; Future jobs for driver, in order of preference:
; * clean up existing extents support.
; * add b-tree directories support.
; * add long file support.
; * add journal support.
; * add minor features that come with ext3/4.
 
; Recommended move to some kernel-wide bitmap handling code (with a bit of abstraction, of course).
 
;---------------------------------------------------------------------
; Clears a bit.
; Input: eax = index into bitmap.
; [EXTFS.ext2_save_block] = address of bitmap.
; ebp = address of EXTFS.
; Output: Bit cleared.
; eax = non-zero, if already cleared.
;---------------------------------------------------------------------
bitmap_clear_bit:
push ebx ecx edx
 
xor edx, edx
mov ecx, 8
div ecx
 
add eax, [ebp + EXTFS.ext2_save_block]
; Get the mask.
mov ebx, 1
mov ecx, edx
shl ebx, cl
 
test [eax], ebx
jz .cleared
 
not ebx
and [eax], ebx
 
xor eax, eax
.return:
pop edx ecx ebx
ret
 
; Already cleared.
.cleared:
xor eax, eax
not eax
jmp .return
 
;---------------------------------------------------------------------
; Finds free bit in the bitmap.
; Input: ecx = number of bits in the bitmap.
; [EXTFS.ext2_save_block] = address of bitmap.
; ebp = address of EXTFS.
; Output: eax = index of free bit in the bitmap; marked set.
; 0xFFFFFFFF if no free bit found.
;---------------------------------------------------------------------
ext2_find_free_bit:
bitmap_find_free_bit:
push esi ebx ecx edx
mov esi, [ebp + EXTFS.ext2_save_block]
 
; Get total DWORDS in eax; total bits in last dword, if any, in edx.
xor edx, edx
mov eax, ecx
mov ecx, 32
div ecx
 
mov ecx, eax
xor eax, eax
push edx
 
test ecx, ecx
jz .last_bits
 
; Check in the DWORDS.
.dwords:
mov ebx, [esi]
not ebx
 
bsf edx, ebx
 
; If 0, then the original value would be 0xFFFFFFFF, hence no free bits.
jz @F
 
; We found the value. Let's return with it.
add esp, 4
 
add eax, edx
jmp .return
@@:
add esi, 4
add eax, 32
loop .dwords
 
.last_bits:
; Check in the last few bits.
pop ecx
test ecx, ecx
jz @F
mov ebx, [esi]
not ebx
bsf ebx, edx
 
; If 0, no free bits.
jz @F
 
; If free bit is greater than the last known bit, then error.
cmp edx, ecx
jg @F
 
add eax, edx
jmp .return
 
@@:
; Didn't find any free bits.
xor eax, eax
not eax
jmp @F
 
.return:
mov ecx, edx
mov edx, 1
shl edx, cl
or [esi], edx
 
@@:
pop edx ecx ebx esi
ret
 
; Recommended move to some kernel-wide string handling code.
;---------------------------------------------------------------------
; Find the length of a string.
; Input: esi = source.
; Output: length in ecx
;---------------------------------------------------------------------
strlen:
push eax esi
xor ecx, ecx
 
@@:
lodsb
test al, al
jz .ret
 
inc ecx
jmp @B
 
.ret:
pop esi eax
ret
 
;---------------------------------------------------------------------
; Convert UTF-8 string to ASCII-string (codepage 866)
; Input: esi = source.
; edi = buffer.
; ecx = length of source.
; Output: destroys eax, esi, edi
;---------------------------------------------------------------------
utf8_to_cp866:
; Check for zero-length string.
jecxz .return
 
.start:
lodsw
cmp al, 0x80
jb .ascii
 
xchg al, ah ; Big-endian.
cmp ax, 0xd080
jz .yo1
 
cmp ax, 0xd191
jz .yo2
 
cmp ax, 0xd090
jb .unk
 
cmp ax, 0xd180
jb .rus1
 
cmp ax, 0xd190
jb .rus2
 
.unk:
mov al, '_'
jmp .doit
.yo1:
mov al, 0xf0 ; Ё capital.
jmp .doit
 
.yo2:
mov al, 0xf1 ; ё small.
jmp .doit
 
.rus1:
sub ax, 0xd090 - 0x80
jmp .doit
 
.rus2:
sub ax, 0xd18f - 0xEF
.doit:
stosb
sub ecx, 2
ja .start
ret
 
.ascii:
stosb
dec esi
dec ecx
jnz .start
.return:
ret
 
; Recommended move to some kernel-wide time handling code.
 
; Total cumulative seconds till each month.
cumulative_seconds_in_month:
.january: dd 0 * (60 * 60 * 24)
.february: dd 31 * (60 * 60 * 24)
.march: dd 59 * (60 * 60 * 24)
.april: dd 90 * (60 * 60 * 24)
.may: dd 120 * (60 * 60 * 24)
.june: dd 151 * (60 * 60 * 24)
.july: dd 181 * (60 * 60 * 24)
.august: dd 212 * (60 * 60 * 24)
.september: dd 243 * (60 * 60 * 24)
.october: dd 273 * (60 * 60 * 24)
.november: dd 304 * (60 * 60 * 24)
.december: dd 334 * (60 * 60 * 24)
 
current_bdfe_time:
dd 0
current_bdfe_date:
dd 0
 
;---------------------------------------------------------------------
; Stores current unix time.
; Input: edi = buffer to output Unix time.
;---------------------------------------------------------------------
current_unix_time:
push eax esi
mov esi, current_bdfe_time
; Just a small observation:
; The CMOS is a pretty bad source to get time from. One shouldn't rely on it,
; since it messes up the time by tiny bits. Of course, this is all technical,
; but one can look it up on the osdev wiki. What is better is to get the time
; from CMOS during boot, then update system time using a more accurate timer.
; I'll probably add that after the Summer of Code, so TODO! TODO! TODO!.
 
; Get time from CMOS.
; Seconds.
mov al, 0x00
out 0x70, al
in al, 0x71
call bcd2bin
mov [esi + 0], al
 
; Minute.
mov al, 0x02
out 0x70, al
in al, 0x71
call bcd2bin
mov [esi + 1], al
 
; Hour.
mov al, 0x04
out 0x70, al
in al, 0x71
call bcd2bin
mov [esi + 2], al
 
; Get date.
 
; Day.
mov al, 0x7
out 0x70, al
in al, 0x71
call bcd2bin
mov [esi + 4], al
 
; Month.
mov al, 0x8
out 0x70, al
in al, 0x71
call bcd2bin
mov [esi + 5], al
 
; Year.
mov al, 0x9
out 0x70, al
in al, 0x71
call bcd2bin
add ax, 2000 ; CMOS only returns last two digits.
; Note that everywhere in KolibriOS this is used.
; This is hacky, since the RTC can be incorrectly set
; to something before 2000.
mov [esi + 6], ax
 
call bdfe_to_unix_time
pop esi eax
ret
 
;---------------------------------------------------------------------
; Convert time+date from BDFE to Unix time.
; Input: esi = pointer to BDFE time+date.
; edi = buffer to output Unix time.
;---------------------------------------------------------------------
bdfe_to_unix_time:
push eax ebx ecx edx
mov dword[edi], 0x00000000
; The minimum representable time is 1901-12-13.
cmp word[esi + 6], 1901
jb .ret
jg .max
 
cmp byte[esi + 5], 12
jb .ret
 
cmp byte[esi + 4], 13
jbe .ret
jg .convert
 
; Check if it is more than the maximum representable time.
.max:
; The maximum representable time is 2038-01-19.
cmp word[esi + 6], 2038
jg .ret
jb .convert
 
cmp byte[esi + 5], 1
jg .ret
 
cmp byte[esi + 4], 19
jge .ret
 
; Convert the time.
.convert:
; Get if current year is leap year in ECX.
xor ecx, ecx
mov ebx, 4
xor edx, edx
 
cmp word[esi + 6], 1970
jb .negative
 
movzx eax, word[esi + 6] ; Year.
cmp byte[esi + 5], 3 ; If the month is less than March, than that year doesn't matter.
jge @F
 
test eax, 3
; Not a leap year.
jnz @F
 
inc ecx
@@:
; Number of leap years between two years = ((end date - 1)/4) - (1970/4)
dec eax
div ebx
sub eax, 1970/4
 
; EAX is the number of leap years.
add eax, ecx
mov ecx, (60 * 60 * 24) ; Seconds in a day.
mul ecx
 
; Account for leap years, i.e., one day extra for each.
add [edi], eax
 
; Get total days in EAX.
movzx eax, byte[esi + 4]
dec eax
mul ecx
 
; Account for days.
add [edi], eax
 
; Account for month.
movzx eax, byte[esi + 5]
dec eax
mov eax, [cumulative_seconds_in_month + (eax * 4)]
add [edi], eax
 
; Account for year.
movzx eax, word[esi + 6]
sub eax, 1970
mov ecx, (60 * 60 * 24) * 365 ; Seconds in a year.
mul ecx
add [edi], eax
 
; Seconds.
movzx eax, byte[esi + 0]
add [edi], eax
 
; Minutes.
movzx eax, byte[esi + 1]
mov ecx, 60
mul ecx
add [edi], eax
 
; Hours.
movzx eax, byte[esi + 2]
mov ecx, (60 * 60)
mul ecx
add [edi], eax
 
; The time wanted is before the epoch; handle it here.
.negative:
; TODO.
 
.ret:
pop edx ecx ebx eax
ret
 
; Recommended move to some kernel-wide alloc handling code.
macro KERNEL_ALLOC store, label
{
call kernel_alloc
mov store, eax
test eax, eax
jz label
}
 
macro KERNEL_FREE data, label
{
cmp data, 0
jz label
push data
call kernel_free
}
 
struct EXTFS PARTITION
lock MUTEX
partition_flags dd ?
log_block_size dd ?
block_size dd ?
count_block_in_block dd ?
blocks_per_group dd ?
global_desc_table dd ?
root_inode dd ? ; Pointer to root inode in memory.
inode_size dd ?
count_pointer_in_block dd ? ; (block_size / 4)
count_pointer_in_block_square dd ? ; (block_size / 4)**2
ext2_save_block dd ? ; Block for 1 global procedure.
ext2_temp_block dd ? ; Block for small procedures.
ext2_save_inode dd ? ; inode for global procedures.
ext2_temp_inode dd ? ; inode for small procedures.
groups_count dd ?
superblock rd 1024/4
ends
 
; EXT2 revisions.
EXT2_GOOD_OLD_REV = 0
 
; For fs_type.
FS_TYPE_UNDEFINED = 0
FS_TYPE_EXT = 2
 
; Some set inodes.
EXT2_BAD_INO = 1
EXT2_ROOT_INO = 2
EXT2_ACL_IDX_INO = 3
EXT2_ACL_DATA_INO = 4
EXT2_BOOT_LOADER_INO = 5
EXT2_UNDEL_DIR_INO = 6
 
; EXT2_SUPER_MAGIC.
EXT2_SUPER_MAGIC = 0xEF53
EXT2_VALID_FS = 1
 
; Flags defining i_mode values.
EXT2_S_IFMT = 0xF000 ; Mask for file type.
 
EXT2_S_IFREG = 0x8000 ; Regular file.
EXT2_S_IFDIR = 0x4000 ; Directory.
 
EXT2_S_IRUSR = 0x0100 ; User read
EXT2_S_IWUSR = 0x0080 ; User write
EXT2_S_IXUSR = 0x0040 ; User execute
EXT2_S_IRGRP = 0x0020 ; Group read
EXT2_S_IWGRP = 0x0010 ; Group write
EXT2_S_IXGRP = 0x0008 ; Group execute
EXT2_S_IROTH = 0x0004 ; Others read
EXT2_S_IWOTH = 0x0002 ; Others write
EXT2_S_IXOTH = 0x0001 ; Others execute
 
PERMISSIONS = EXT2_S_IRUSR or EXT2_S_IWUSR \
or EXT2_S_IRGRP or EXT2_S_IWGRP \
or EXT2_S_IROTH or EXT2_S_IWOTH
 
; File type defining values in directory entry.
EXT2_FT_REG_FILE = 1 ; Regular file.
EXT2_FT_DIR = 2 ; Directory.
 
; Flags used by KolibriOS.
FS_FT_HIDDEN = 2
FS_FT_DIR = 0x10 ; Directory.
 
; ext2 partition flags.
EXT2_RO = 0x01
 
FS_FT_ASCII = 0 ; Name in ASCII.
FS_FT_UNICODE = 1 ; Name in Unicode.
 
EXT2_FEATURE_INCOMPAT_FILETYPE = 0x0002 ; Have file type in directory entry.
EXT4_FEATURE_INCOMPAT_EXTENTS = 0x0040 ; Extents.
EXT4_FEATURE_INCOMPAT_FLEX_BG = 0x0200 ; Flexible block groups.
 
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER = 0x0001 ; Sparse Superblock
EXT2_FEATURE_RO_COMPAT_LARGE_FILE = 0x0002 ; Large file support (64-bit file size)
 
; Implemented ext[2,3,4] features.
EXT4_FEATURE_INCOMPAT_SUPP = EXT2_FEATURE_INCOMPAT_FILETYPE \
or EXT4_FEATURE_INCOMPAT_EXTENTS \
or EXT4_FEATURE_INCOMPAT_FLEX_BG
 
; Implemented features which otherwise require "read-only" mount.
EXT2_FEATURE_RO_COMPAT_SUPP = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER \
or EXT2_FEATURE_RO_COMPAT_LARGE_FILE
 
; ext4 features not support for write.
EXT4_FEATURE_INCOMPAT_W_NOT_SUPP = EXT4_FEATURE_INCOMPAT_EXTENTS \
or EXT4_FEATURE_INCOMPAT_FLEX_BG
 
; Flags specified in i_flags.
EXT2_EXTENTS_FL = 0x00080000 ; Extents.
 
struct EXT2_INODE_STRUC
i_mode dw ?
i_uid dw ?
i_size dd ?
i_atime dd ?
i_ctime dd ?
i_mtime dd ?
i_dtime dd ?
i_gid dw ?
i_links_count dw ?
i_blocks dd ?
i_flags dd ?
i_osd1 dd ?
i_block rd 15
i_generation dd ?
i_file_acl dd ?
i_dir_acl dd ?
i_faddr dd ?
i_osd2 dd ? ; 12 bytes.
ends
 
struct EXT2_DIR_STRUC
inode dd ?
rec_len dw ?
name_len db ?
file_type db ?
name db ? ; 255 (max) bytes.
ends
 
struct EXT2_BLOCK_GROUP_DESC
block_bitmap dd ? ; +0
inode_bitmap dd ? ; +4
inode_table dd ? ; +8
free_blocks_count dw ? ; +12
free_inodes_count dw ? ; +14
used_dirs_count dw ? ; +16
pad dw ? ; +18
reserved rb 12 ; +20
ends
 
struct EXT2_SB_STRUC
inodes_count dd ? ; +0
blocks_count dd ? ; +4
r_block_count dd ? ; +8
free_block_count dd ? ; +12
free_inodes_count dd ? ; +16
first_data_block dd ? ; +20
log_block_size dd ? ; +24
log_frag_size dd ? ; +28
blocks_per_group dd ? ; +32
frags_per_group dd ? ; +36
inodes_per_group dd ? ; +40
mtime dd ? ; +44
wtime dd ? ; +48
mnt_count dw ? ; +52
max_mnt_count dw ? ; +54
magic dw ? ; +56
state dw ? ; +58
errors dw ? ; +60
minor_rev_level dw ? ; +62
lastcheck dd ? ; +64
check_intervals dd ? ; +68
creator_os dd ? ; +72
rev_level dd ? ; +76
def_resuid dw ? ; +80
def_resgid dw ? ; +82
first_ino dd ? ; +84
inode_size dw ? ; +88
block_group_nr dw ? ; +90
feature_compat dd ? ; +92
feature_incompat dd ? ; +96
feature_ro_compat dd ? ; +100
uuid rb 16 ; +104
volume_name rb 16 ; +120
last_mounted rb 64 ; +136
algo_bitmap dd ? ; +200
prealloc_blocks db ? ; +204
preallock_dir_blocks db ? ; +205
reserved_gdt_blocks dw ? ; +206
journal_uuid rb 16 ; +208
journal_inum dd ? ; +224
journal_dev dd ? ; +228
last_orphan dd ? ; +232
hash_seed rd 4 ; +236
def_hash_version db ? ; +252
reserved rb 3 ; +253 (reserved)
default_mount_options dd ? ; +256
first_meta_bg dd ? ; +260
mkfs_time dd ? ; +264
jnl_blocks rd 17 ; +268
blocks_count_hi dd ? ; +336
r_blocks_count_hi dd ? ; +340
free_blocks_count_hi dd ? ; +344
min_extra_isize dw ? ; +348
want_extra_isize dw ? ; +350
flags dd ? ; +352
raid_stride dw ? ; +356
mmp_interval dw ? ; +358
mmp_block dq ? ; +360
raid_stripe_width dd ? ; +368
log_groups_per_flex db ? ; +372
ends
 
; Header block extents.
struct EXT4_EXTENT_HEADER
eh_magic dw ? ; Magic value of 0xF30A, for ext4.
eh_entries dw ? ; Number of blocks covered by the extent.
eh_max dw ? ; Capacity of entries.
eh_depth dw ? ; Tree depth (if 0, extents in the array are not extent indexes)
eh_generation dd ? ; ???
ends
 
; Extent.
struct EXT4_EXTENT
ee_block dd ? ; First logical block extent covers.
ee_len dw ? ; Number of blocks covered by extent.
ee_start_hi dw ? ; Upper 16 bits of 48-bit address (unused in KOS)
ee_start_lo dd ? ; Lower 32 bits of 48-bit address.
ends
 
; Index on-disk structure; pointer to block of extents/indexes.
struct EXT4_EXTENT_IDX
ei_block dd ? ; Covers logical blocks from here.
ei_leaf_lo dd ? ; Lower 32-bits of pointer to the physical block of the next level.
ei_leaf_hi dw ? ; Higher 16-bits (unused in KOS).
ei_unused dw ? ; Reserved.
ends
/kernel/branches/Kolibri-acpi/fs/ext2/inode.inc
0,0 → 1,1850
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains ext2 inode handling code. ;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under the terms of the new BSD license. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;---------------------------------------------------------------------
; Receives block number from extent-based inode.
; Input: ecx = number of block in inode
; esi = address of extent header
; ebp = pointer to EXTFS
; Output: ecx = address of next block, if successful
; eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext4_block_recursive_search:
cmp word [esi + EXT4_EXTENT_HEADER.eh_magic], 0xF30A ;EXT4_EXT_MAGIC
jne .fail
movzx ebx, [esi + EXT4_EXTENT_HEADER.eh_entries]
add esi, sizeof.EXT4_EXTENT_HEADER
cmp word [esi - sizeof.EXT4_EXTENT_HEADER + EXT4_EXTENT_HEADER.eh_depth], 0
je .leaf_block ;листовой ли это блок?
;не листовой блок, а индексный ; eax - ext4_extent_idx
test ebx, ebx
jz .fail ;пустой индексный блок -> ошибка
 
;цикл по индексам экстентов
@@:
cmp ebx, 1 ;у индексов не хранится длина,
je .end_search_index ;поэтому, если остался последний - то это нужный
cmp ecx, [esi + EXT4_EXTENT_IDX.ei_block]
jb .fail
cmp ecx, [esi + sizeof.EXT4_EXTENT_IDX + EXT4_EXTENT_IDX.ei_block] ;блок слeдующего индекса
jb .end_search_index ;следующий дальше - значит текущий, то что нам нужен
add esi, sizeof.EXT4_EXTENT_IDX
dec ebx
jmp @B
 
.end_search_index:
;ebp указывает на нужный extent_idx, считываем следующий блок
mov ebx, [ebp + EXTFS.ext2_temp_block]
mov eax, [esi + EXT4_EXTENT_IDX.ei_leaf_lo]
call ext2_block_read
test eax, eax
jnz .fail
mov esi, ebx
jmp ext4_block_recursive_search ;рекурсивно прыгаем в начало
.leaf_block: ;листовой блок esi - ext4_extent
;цикл по экстентам
@@:
test ebx, ebx
jz .fail ;ни один узел не подошел - ошибка
 
mov edx, [esi + EXT4_EXTENT.ee_block]
cmp ecx, edx
jb .fail ;если меньше, значит он был в предыдущих блоках -> ошибка
 
movzx edi, [esi + EXT4_EXTENT.ee_len]
add edx, edi
cmp ecx, edx
jb .end_search_extent ;нашли нужный блок
add esi, sizeof.EXT4_EXTENT
dec ebx
jmp @B
.end_search_extent:
mov edx, [esi + EXT4_EXTENT.ee_start_lo]
sub ecx, [esi + EXT4_EXTENT.ee_block] ;разница в ext4 блоках
add ecx, edx
xor eax, eax
ret
 
.fail:
mov eax, ERROR_FS_FAIL
ret
 
;---------------------------------------------------------------------
; Frees triply indirect block.
; Input: eax = triply indirect block.
; [ebp + EXTFS.ext2_save_inode] = the inode.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_free_triply_indirect:
push ebx edx
 
test eax, eax
jz .success
push eax
; Read the triple indirect block.
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
pop eax
jnz .fail
 
; Free the triple indirect block.
call ext2_block_free
test eax, eax
jnz .fail
 
mov edx, ebx
add edx, [ebp + EXTFS.block_size]
 
@@:
mov eax, [ebx]
test eax, eax
jz .success
 
call ext2_inode_free_doubly_indirect
cmp eax, 1
je .success
cmp eax, 0xFFFFFFFF
je .fail
 
add ebx, 4
cmp ebx, edx
jb @B
 
.success:
xor eax, eax
.ret:
pop edx ebx
ret
 
.fail:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Frees double indirect block.
; Input: eax = double indirect block.
; [ebp + EXTFS.ext2_save_inode] = the inode.
; Output: eax = error code, 1 implies finished, ~0 implies error
;---------------------------------------------------------------------
ext2_inode_free_doubly_indirect:
push ebx edx
 
test eax, eax
jz .complete
push eax
; Read the double indirect block.
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
pop eax
jnz .fail
 
call ext2_block_free
test eax, eax
jnz .fail
 
mov edx, ebx
add edx, [ebp + EXTFS.block_size]
 
@@:
mov eax, [ebx]
test eax, eax
jz .complete
 
call ext2_block_free
test eax, eax
jnz .fail
 
add ebx, 4
cmp ebx, edx
jb @B
 
.success:
xor eax, eax
.ret:
pop edx ebx
ret
 
.complete:
xor eax, eax
inc eax
jmp .ret
 
.fail:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Frees all indirect blocks.
; Input: ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_save_inode] = the inode.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_free_indirect_blocks:
push edi
 
mov edi, [ebp + EXTFS.ext2_save_inode]
 
; Free indirect block.
mov eax, [edi + EXT2_INODE_STRUC.i_block + 12*4]
test eax, eax
jz .success
 
call ext2_block_free
test eax, eax
jnz .fail
 
mov eax, [edi + EXT2_INODE_STRUC.i_block + 13*4]
call ext2_inode_free_doubly_indirect
cmp eax, 1
je .success
cmp eax, 0xFFFFFFFF
je .fail
 
mov eax, [edi + EXT2_INODE_STRUC.i_block + 14*4]
call ext2_inode_free_triply_indirect
test eax, eax
jnz .fail
 
.success:
xor eax, eax
.ret:
pop edi
ret
 
.fail:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Allocates block for inode.
; Input: esi = address of inode
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_calloc_block:
push ecx
 
; TODO: fix to have correct preference.
mov eax, EXT2_ROOT_INO
call ext2_block_calloc
test eax, eax
jnz .fail
 
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
mov eax, 2
shl eax, cl
add [esi + EXT2_INODE_STRUC.i_blocks], eax
 
.success:
xor eax, eax
.ret:
pop ecx
ret
 
.fail:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Sets block ID for indirect-addressing inode.
; Input: ecx = index of block in inode
; edi = block ID to set to
; esi = address of inode
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_set_block:
push ebx ecx edx
 
; 0 to 11: direct blocks.
cmp ecx, 12
jb .direct_block
 
; Indirect blocks
sub ecx, 12
cmp ecx, [ebp + EXTFS.count_pointer_in_block]
jb .indirect_block
 
; Double indirect blocks.
sub ecx, [ebp + EXTFS.count_pointer_in_block]
cmp ecx, [ebp + EXTFS.count_pointer_in_block_square]
jb .double_indirect_block
 
; Triple indirect blocks.
sub ecx, [ebp + EXTFS.count_pointer_in_block_square]
 
; Get triply-indirect block in temp_block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4]
test eax, eax
jnz @F
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc
 
mov [esi + EXT2_INODE_STRUC.i_block + 14*4], ebx
mov eax, ebx
 
@@:
push eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail_alloc_4
 
; Get index in triply-indirect block.
xor edx, edx
mov eax, ecx
div [ebp + EXTFS.count_pointer_in_block_square]
 
; eax: index in triply-indirect block, edx: index in doubly-indirect block.
lea ecx, [ebx + eax*4]
mov eax, [ebx + eax*4]
test eax, eax
jnz @F
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc_4
 
mov [ecx], ebx
 
mov eax, [esp]
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .fail_alloc_4
 
mov eax, [ecx]
@@:
mov [esp], eax
call ext2_block_read
test eax, eax
jnz .fail_alloc_4
 
mov eax, edx
jmp @F
 
.double_indirect_block:
; Get doubly-indirect block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4]
test eax, eax
jnz .double_indirect_present
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc
 
mov [esi + EXT2_INODE_STRUC.i_block + 13*4], ebx
mov eax, ebx
 
.double_indirect_present:
; Save block we're at.
push eax
 
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail_alloc_4
 
mov eax, ecx
@@:
xor edx, edx
div [ebp + EXTFS.count_pointer_in_block]
 
; eax: index in doubly-indirect block, edx: index in indirect block.
lea ecx, [ebx + edx*4]
push ecx
 
lea ecx, [ebx + eax*4]
cmp dword[ecx], 0
jne @F
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc_8
 
mov [ecx], ebx
 
mov eax, [esp + 4]
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .fail_alloc_8
 
@@:
mov eax, [ecx]
push eax
call ext2_block_read
test eax, eax
jnz .fail_alloc_12
 
pop eax
pop ecx
mov [ecx], edi
call ext2_block_write
 
add esp, 4
jmp .return
 
.indirect_block:
; Get index of indirect block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4]
test eax, eax
jnz @F
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc
mov [esi + EXT2_INODE_STRUC.i_block + 12*4], ebx
mov eax, ebx
@@:
push eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail_alloc_4
; Get the block ID.
mov [ebx + ecx*4], edi
pop eax
call ext2_block_write
jmp .return
 
.direct_block:
mov [esi + EXT2_INODE_STRUC.i_block + ecx*4], edi
xor eax, eax
 
.return:
pop edx ecx ebx
ret
 
.fail_alloc:
xor eax, eax
not eax
jmp .return
 
.fail_alloc_12:
add esp, 4
.fail_alloc_8:
add esp, 4
.fail_alloc_4:
add esp, 4
jmp .fail_alloc
 
;---------------------------------------------------------------------
; Receives block ID from indirect-addressing inode.
; Input: ecx = index of block in inode
; esi = address of inode
; ebp = pointer to EXTFS
; Output: ecx = block ID, if successful
; eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_get_block:
; If inode is extent-based, use ext4_block_recursive_search.
test [esi + EXT2_INODE_STRUC.i_flags], EXT2_EXTENTS_FL
jz @F
 
pushad
 
; Get extent header in EBP.
add esi, EXT2_INODE_STRUC.i_block
call ext4_block_recursive_search
mov PUSHAD_ECX, ecx
mov PUSHAD_EAX, eax
 
popad
ret
 
@@:
; 0 to 11: direct blocks.
cmp ecx, 12
jb .get_direct_block
 
; Indirect blocks
sub ecx, 12
cmp ecx, [ebp + EXTFS.count_pointer_in_block]
jb .get_indirect_block
 
; Double indirect blocks.
sub ecx, [ebp + EXTFS.count_pointer_in_block]
cmp ecx, [ebp + EXTFS.count_pointer_in_block_square]
jb .get_double_indirect_block
 
; Triple indirect blocks.
sub ecx, [ebp + EXTFS.count_pointer_in_block_square]
push edx ebx
 
; Get triply-indirect block in temp_block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4]
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail
 
; Get index in triply-indirect block.
xor edx, edx
mov eax, ecx
div [ebp + EXTFS.count_pointer_in_block_square]
 
; eax: index in triply-indirect block, edx: index in doubly-indirect block.
mov eax, [ebx + eax*4]
test eax, eax
jz .fail_triple_indirect_block
 
call ext2_block_read
test eax, eax
jnz .fail
 
mov eax, edx
jmp @F
 
.get_double_indirect_block:
push edx ebx
 
; Get doubly-indirect block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4]
test eax, eax
jz .fail_double_indirect_block
 
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail
 
mov eax, ecx
@@:
xor edx, edx
div [ebp + EXTFS.count_pointer_in_block]
 
; eax: index in doubly-indirect block, edx: index in indirect block.
mov eax, [ebx + eax*4]
test eax, eax
jz .fail_double_indirect_block
 
call ext2_block_read
test eax, eax
jnz .fail
 
mov ecx, [ebx + edx*4]
.fail:
pop ebx edx
 
ret
 
.get_indirect_block:
push ebx
 
; Get index of indirect block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4]
test eax, eax
jz .fail_indirect_block
 
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz @F
mov ecx, [ebx + ecx*4]
@@:
pop ebx
 
ret
 
.get_direct_block:
mov ecx, [esi + EXT2_INODE_STRUC.i_block + ecx*4]
xor eax, eax
 
ret
 
.fail_indirect_block:
pop ebx
 
.fail_triple_indirect_block:
xor eax, eax
xor ecx, ecx
ret
 
.fail_double_indirect_block:
pop ebx edx
jmp .fail_triple_indirect_block
 
;---------------------------------------------------------------------
; Get block containing inode.
; Input: eax = inode number.
; ebp = pointer to EXTFS.
; Output: ebx = block (hard disk) containing inode.
; edx = index inside block.
; eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_read_block_of_inode:
pushad
 
dec eax
xor edx, edx
; EAX = block group.
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group]
 
push edx ; Index in group.
 
mov edx, 32
mul edx ; Get index of descriptor in global_desc_table.
 
; eax: inode group offset relative to global descriptor table start
; Find the block this block descriptor is in.
div [ebp + EXTFS.block_size]
add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block]
inc eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .return
 
add ebx, edx ; edx: local index of descriptor inside block
mov eax, [ebx + EXT2_BLOCK_GROUP_DESC.inode_table] ; Block number of inode table - in ext2 terms.
mov ecx, [ebp + EXTFS.log_block_size]
shl eax, cl
 
; eax: points to inode table on HDD.
mov esi, eax
 
; Add local address of inode.
pop eax
mov ecx, [ebp + EXTFS.inode_size]
mul ecx ; (index * inode_size)
 
mov ebp, 512
div ebp ; Divide by hard disk block size.
 
add eax, esi ; Found block to read.
mov ebx, eax ; Get it inside ebx.
 
xor eax, eax
.return:
mov PUSHAD_EAX, eax
mov PUSHAD_EBX, ebx
mov PUSHAD_EDX, edx
 
popad
ret
 
;---------------------------------------------------------------------
; Sets content of inode by number.
; Input: eax = inode number.
; ebx = address from where to write inode content.
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_write:
push edx edi esi ecx ebx
mov esi, ebx
 
; Ext2 actually stores time of modification of inode in ctime.
lea edi, [ebx + EXT2_INODE_STRUC.i_ctime]
call current_unix_time
 
; Get block where inode is situated.
call ext2_read_block_of_inode
test eax, eax
jnz .error
 
mov eax, ebx ; Get block into EAX.
mov ebx, [ebp + EXTFS.ext2_temp_block]
 
mov ecx, eax ; Save block.
call fs_read32_sys
test eax, eax
jz @F
 
.error:
mov eax, ERROR_DEVICE
jmp .return
 
@@:
mov eax, ecx
mov ecx, [ebp + EXTFS.inode_size]
mov edi, edx ; The index into the block.
add edi, ebx
rep movsb
 
; Write the block.
call fs_write32_sys
 
.return:
pop ebx ecx esi edi edx
ret
 
;---------------------------------------------------------------------
; Get content of inode by number.
; Input: eax = inode number.
; ebx = address where to store inode content.
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_read:
push edx edi esi ecx ebx
mov edi, ebx
 
; Get block where inode is situated.
call ext2_read_block_of_inode
test eax, eax
jnz .error
 
mov eax, ebx ; Get block into EAX.
mov ebx, [ebp + EXTFS.ext2_temp_block]
call fs_read32_sys
test eax, eax
jz @F
 
.error:
mov eax, ERROR_DEVICE
jmp .return
 
@@:
mov ecx, [ebp + EXTFS.inode_size]
mov esi, edx ; The index into the inode.
add esi, ebx
rep movsb
 
xor eax, eax
.return:
pop ebx ecx esi edi edx
ret
 
;---------------------------------------------------------------------
; Seek inode from the path.
; Input: esi + [esp + 4] = name.
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
; esi = inode number.
; dl = first byte of file/folder name.
; [ext2_data.ext2_save_inode] stores the inode.
;---------------------------------------------------------------------
ext2_inode_find:
mov edx, [ebp + EXTFS.root_inode]
 
; Check for empty root.
cmp [edx + EXT2_INODE_STRUC.i_blocks], 0
je .error_empty_root
 
; Check for root.
cmp byte[esi], 0
jne .next_path_part
 
push edi ecx
mov esi, [ebp + EXTFS.root_inode]
mov edi, [ebp + EXTFS.ext2_save_inode]
mov ecx, [ebp + EXTFS.inode_size]
rep movsb
pop ecx edi
 
xor eax, eax
xor dl, dl
mov esi, EXT2_ROOT_INO
ret 4
.next_path_part:
push [edx + EXT2_INODE_STRUC.i_blocks]
xor ecx, ecx
 
.folder_block_cycle:
push ecx
xchg esi, edx
call ext2_inode_get_block
xchg esi, edx
test eax, eax
jnz .error_get_inode_block
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_save_block] ; Get directory records from directory.
call ext2_block_read
test eax, eax
jnz .error_get_block
push esi
push edx
call ext2_block_find_parent
pop edx
pop edi ecx
 
cmp edi, esi ; Did something match?
je .next_folder_block ; No, move to next block.
cmp byte [esi], 0 ; Reached the "end" of path successfully.
jnz @F
cmp dword[esp + 8], 0
je .get_inode_ret
mov esi, [esp + 8]
mov dword[esp + 8], 0
 
@@:
mov eax, [ebx + EXT2_DIR_STRUC.inode]
mov ebx, [ebp + EXTFS.ext2_save_inode]
call ext2_inode_read
test eax, eax
jnz .error_get_inode
 
movzx eax, [ebx + EXT2_INODE_STRUC.i_mode]
and eax, EXT2_S_IFMT ; Get the mask.
cmp eax, EXT2_S_IFDIR
jne .not_found ; Matched till part, but directory entry we got doesn't point to folder.
 
pop ecx ; Stack top contains number of blocks.
mov edx, ebx
jmp .next_path_part
.next_folder_block:
; Next block in current folder.
pop eax ; Get blocks counter.
sub eax, [ebp + EXTFS.count_block_in_block]
jle .not_found
push eax
inc ecx
jmp .folder_block_cycle
 
.not_found:
mov eax, ERROR_FILE_NOT_FOUND
ret 4
 
.get_inode_ret:
pop ecx ; Stack top contains number of blocks.
 
mov dl, [ebx + EXT2_DIR_STRUC.name] ; First character of file-name.
mov eax, [ebx + EXT2_DIR_STRUC.inode]
mov ebx, [ebp + EXTFS.ext2_save_inode]
mov esi, eax
 
; If we can't get the inode, eax contains the error.
call ext2_inode_read
ret 4
 
.error_get_inode_block:
.error_get_block:
pop ecx
.error_get_inode:
pop ebx
.error_empty_root:
mov eax, ERROR_FS_FAIL
ret 4
 
;---------------------------------------------------------------------
; Seeks parent inode from path.
; Input: esi = path.
; ebp = pointer to EXTFS.
; Output: eax = error code.
; esi = inode.
; edi = pointer to file name.
;---------------------------------------------------------------------
ext2_inode_find_parent:
push esi
xor edi, edi
 
.loop:
cmp byte[esi], '/'
jne @F
 
mov edi, esi
inc esi
jmp .loop
 
@@:
inc esi
cmp byte[esi - 1], 0
jne .loop
 
; If it was just a filename (without any additional directories),
; use the last byte as "parent path".
cmp edi, 0
jne @F
 
pop edi
dec esi
jmp .get_inode
 
; It had some additional directories, so handle it that way.
@@:
mov byte[edi], 0
inc edi
pop esi
 
.get_inode:
push ebx edx
stdcall ext2_inode_find, 0
pop edx ebx
 
.return:
ret
 
;---------------------------------------------------------------------
; Link an inode.
; Input: eax = inode on which to link.
; ebx = inode to link.
; dl = file type.
; esi = name.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_link:
push eax
push esi edi ebx ecx edx
 
; Get string length, and then directory entry structure size.
call strlen
add ecx, 8
 
push esi ebx ecx
 
xor ecx, ecx
mov esi, [ebp + EXTFS.ext2_temp_inode]
mov ebx, esi
 
call ext2_inode_read
test eax, eax
jnz .error_inode_read
 
; Get the maximum addressible i_block index by (i_blocks/(2 << s_log_block_size)).
; Note that i_blocks contains number of reserved 512B blocks, which is why we've to
; find out the ext2 blocks.
mov eax, 2
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
shl eax, cl
mov ecx, eax
 
mov eax, [esi + EXT2_INODE_STRUC.i_blocks]
xor edx, edx
 
div ecx
 
; EAX is the maximum index inside i_block we can go.
push eax
push dword 0
 
; ECX contains the "block inside i_block" index.
xor ecx, ecx
@@:
call ext2_inode_get_block
test eax, eax
jnz .error_get_inode_block
test ecx, ecx
jz .alloc_block ; We've got no block here, so allocate one.
 
push ecx ; Save block number.
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .error_block_read
 
; Try to find free space in current block.
mov ecx, [esp + 8]
call ext2_block_find_fspace
test eax, eax
jz .found
 
cmp eax, 0x00000001
jne .next_iter
 
; This block wasn't linking to the next block, so fix that, and use the next one.
; Write the block.
pop eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .error_get_inode_block
 
inc dword [esp]
mov ecx, [esp]
call ext2_inode_get_block
test eax, eax
jnz .error_get_inode_block
 
test ecx, ecx
jz .alloc_block
 
; If there was a block there, prepare it for our use!
push ecx
jmp .prepare_block
 
.next_iter:
add esp, 4
 
inc dword [esp]
mov ecx, [esp]
cmp ecx, [esp + 4]
jbe @B
 
.alloc_block:
mov eax, [esp + 12] ; Get inode ID of what we're linking.
call ext2_block_calloc
test eax, eax
jnz .error_get_inode_block
 
mov ecx, [esp] ; Get the index of it inside the inode.
mov edi, ebx ; And what to set to.
call ext2_inode_set_block
test eax, eax
jnz .error_get_inode_block
 
; Update i_size.
mov eax, [ebp + EXTFS.block_size]
add [esi + EXT2_INODE_STRUC.i_size], eax
 
; Update i_blocks.
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
mov eax, 2
shl eax, cl
add [esi + EXT2_INODE_STRUC.i_blocks], eax
 
; Write the inode.
mov eax, [esp + 40]
mov ebx, esi
call ext2_inode_write
test eax, eax
jnz .error_get_inode_block
 
push edi ; Save the block we just allocated.
 
; If we've allocated/using-old-block outside of loop, prepare it.
.prepare_block:
mov eax, [esp]
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .error_block_read
 
mov edi, ebx
mov eax, [ebp + EXTFS.block_size]
mov [edi + EXT2_DIR_STRUC.rec_len], ax
 
.found:
pop edx
add esp, 8
pop ecx ebx esi
 
push ebx
mov [edi], ebx ; Save inode.
 
mov eax, [esp + 4] ; Get EDX off the stack -- contains the file_type.
cmp [ebp + EXTFS.superblock + EXT2_SB_STRUC.rev_level], EXT2_GOOD_OLD_REV
je .name
 
; Set the file-type.
mov [edi + EXT2_DIR_STRUC.file_type], al
 
.name:
; Save name.
sub ecx, 8
mov [edi + EXT2_DIR_STRUC.name_len], cl
add edi, 8
rep movsb
 
; Write block.
mov eax, edx
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .error_block_write
 
mov eax, [esp]
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_read
test eax, eax
jnz .error_block_write
 
pop eax
inc [ebx + EXT2_INODE_STRUC.i_links_count]
call ext2_inode_write
test eax, eax
jnz .error
 
xor eax, eax
.ret:
pop edx ecx ebx edi esi
add esp, 4
ret
 
.error_block_read:
add esp, 4
.error_get_inode_block:
add esp, 8
.error_inode_read:
add esp, 8
.error_block_write:
add esp, 4
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Unlink an inode.
; Input: eax = inode from which to unlink.
; ebx = inode to unlink.
; ebp = pointer to EXTFS.
; Output: eax = number of links to inode, after unlinking (0xFFFFFFFF implies error)
;---------------------------------------------------------------------
ext2_inode_unlink:
push ebx ecx edx esi edi
 
push ebx
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_read
 
test eax, eax
jnz .fail_get_inode
 
; The index into the inode block data.
push dword 0
mov esi, [ebp + EXTFS.ext2_temp_inode]
.loop:
mov ecx, [esp]
call ext2_inode_get_block
 
test eax, eax
jnz .fail_loop
test ecx, ecx
jz .fail_loop
 
mov eax, ecx
mov edi, eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail_loop
 
; edi -> block.
.first_dir_entry:
mov eax, [esp + 4]
cmp [ebx], eax
jne @F
 
mov dword[ebx], 0 ; inode.
mov word[ebx + 6], 0 ; name_len + file_type.
jmp .write_block
 
@@:
mov edx, ebx
add edx, [ebp + EXTFS.block_size]
push edx
 
mov edx, ebx
movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len]
add ebx, ecx
 
.dir_entry:
cmp [ebx], eax
jne @F
 
mov cx, [ebx + EXT2_DIR_STRUC.rec_len]
add [edx + EXT2_DIR_STRUC.rec_len], cx
add esp, 4
jmp .write_block
 
@@:
mov edx, ebx
movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len]
; If it's a zero length entry, error.
test ecx, ecx
jz .fail_inode
 
add ebx, ecx
 
cmp ebx, [esp]
jb .dir_entry
 
add esp, 4
inc dword[esp]
jmp .loop
 
.write_block:
mov eax, edi
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .fail_loop
 
add esp, 4
mov ebx, [ebp + EXTFS.ext2_temp_inode]
mov eax, [esp]
call ext2_inode_read
test eax, eax
jnz .fail_get_inode
 
dec word[ebx + EXT2_INODE_STRUC.i_links_count]
movzx eax, word[ebx + EXT2_INODE_STRUC.i_links_count]
push eax
 
mov eax, [esp + 4]
call ext2_inode_write
test eax, eax
jnz .fail_loop
 
pop eax
add esp, 4
.return:
pop edi esi edx ecx ebx
ret
 
.fail_inode:
add esp, 4
 
.fail_loop:
add esp, 4
 
.fail_get_inode:
add esp, 4
 
.fail:
xor eax, eax
not eax
jmp .return
 
;---------------------------------------------------------------------
; Checks if a directory is empty.
; Input: ebx = inode to check.
; ebp = pointer to EXTFS.
; [EXTFS.ext2_save_inode] = points to saved inode.
; Output: eax = 0 signifies empty directory.
;---------------------------------------------------------------------
ext2_dir_empty:
push ebx ecx edx
; The index into the inode block data.
push dword 0
mov esi, [ebp + EXTFS.ext2_save_inode]
.loop:
mov ecx, [esp]
call ext2_inode_get_block
 
; Treat a failure as not-empty.
test eax, eax
jnz .not_empty
test ecx, ecx
jz .empty
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .not_empty
 
mov edx, ebx
add edx, [ebp + EXTFS.block_size]
 
movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len]
add ebx, ecx
 
.dir_entry:
; Process entry.
cmp byte[ebx + EXT2_DIR_STRUC.name_len], 1
jne @F
 
cmp byte[ebx + EXT2_DIR_STRUC.name], '.'
jne .not_empty
 
@@:
cmp byte[ebx + EXT2_DIR_STRUC.name_len], 2
jne .not_empty
 
cmp word[ebx + EXT2_DIR_STRUC.name], '..'
jne .not_empty
 
@@:
movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len]
add ebx, ecx
 
cmp ebx, edx
jb .dir_entry
 
inc dword[esp]
jmp .loop
 
.empty:
xor eax, eax
.return:
add esp, 4
pop edx ecx ebx
ret
 
.not_empty:
xor eax, eax
not eax
jmp .return
 
;---------------------------------------------------------------------
; Gets the block group's inode bitmap.
; Input: eax = block group.
; Output: eax = if zero, error; else, points to block group descriptor.
; ebx = inode bitmap's block (hard disk).
;---------------------------------------------------------------------
ext2_bg_read_inode_bitmap:
push ecx
 
call ext2_bg_read_desc
test eax, eax
jz .fail
 
mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.inode_bitmap] ; Block number of inode bitmap - in ext2 terms.
 
.return:
pop ecx
ret
 
.fail:
xor eax, eax
jmp .return
 
;---------------------------------------------------------------------
; Allocates a inode.
; Input: eax = inode ID for "preference".
; ebp = pointer to EXTFS.
; Output: Inode marked as set in inode group.
; eax = error code.
; ebx = inode ID.
;---------------------------------------------------------------------
ext2_inode_alloc:
push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_count]
push EXT2_BLOCK_GROUP_DESC.free_inodes_count
push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group]
 
lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_inodes_count]
push ebx
 
push ext2_bg_read_inode_bitmap
 
call ext2_resource_alloc
 
; Inode table starts with 1.
inc ebx
 
ret
 
;---------------------------------------------------------------------
; Frees a inode.
; Input: eax = inode ID.
; ebp = pointer to EXTFS.
; Output: inode marked as free in block group.
; eax = error code.
;---------------------------------------------------------------------
ext2_inode_free:
push edi ecx
 
; Inode table starts with 1.
dec eax
 
mov edi, ext2_bg_read_inode_bitmap
xor ecx, ecx
inc cl
call ext2_resource_free
 
pop ecx edi
ret
 
;---------------------------------------------------------------------
; Blanks a particular entry in an inode.
; Input: eax = index into block.
; edx = inode.
; ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_temp_inode] = the inode.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_blank_entry:
push ebx ecx edx edi esi
 
mov edi, eax
 
mov ecx, eax
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_get_block
test eax, eax
jnz .error
 
test ecx, ecx
jz .allocate
 
mov edx, ecx
mov ecx, [ebp + EXTFS.block_size]
mov edi, [ebp + EXTFS.ext2_temp_block]
xor eax, eax
rep stosb
 
mov eax, edx
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .error
 
jmp .success
 
; Need to allocate a block.
.allocate:
mov eax, edx
call ext2_block_calloc
test eax, eax
jnz .error
 
mov ecx, edi
mov edi, ebx
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_set_block
test eax, eax
jnz .error
 
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
mov eax, 2
shl eax, cl
add [esi + EXT2_INODE_STRUC.i_blocks], eax
 
.success:
xor eax, eax
 
.ret:
pop esi edi edx ecx ebx
ret
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Frees a particular entry in an inode.
; Input: eax = index into block.
; ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_temp_inode] = the inode.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_free_entry:
push ebx ecx edi esi
 
mov edi, eax
 
mov ecx, eax
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_get_block
test eax, eax
jnz .error
 
test ecx, ecx
jz .success
 
mov eax, ecx
call ext2_block_free
test eax, eax
jnz .error
 
mov ecx, edi
xor edi, edi
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_set_block
 
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
mov eax, 2
shl eax, cl
sub [esi + EXT2_INODE_STRUC.i_blocks], eax
 
.success:
xor eax, eax
 
.ret:
pop esi edi ecx ebx
ret
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Reads a particular entry from an inode.
; Input: eax = index into block.
; ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_temp_inode] = the inode.
; Output: eax = error code.
; [ebp + EXTFS.ext2_save_block] = the read block.
;---------------------------------------------------------------------
ext2_inode_read_entry:
push ebx ecx edx esi
 
mov ecx, eax
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_get_block
test eax, eax
jnz .error
 
test ecx, ecx
jz .error
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_read
test eax, eax
jnz .error
 
.ret:
pop esi edx ecx ebx
ret
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Writes a particular entry from an inode.
; Input: eax = index into block.
; ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_temp_inode] = the inode.
; [ebp + EXTFS.ext2_save_block] = the block to write.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_write_entry:
push ebx ecx edx esi
 
mov ecx, eax
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_get_block
test eax, eax
jnz .error
 
test ecx, ecx
jz .error
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_write
test eax, eax
jnz .error
 
.ret:
pop esi edx ecx ebx
ret
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Extends inode to said size.
; Input: eax = inode ID.
; ecx = size to extend to.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_extend:
push ebx ecx edx esi edi
 
; Save the inode.
push eax
 
; Read the inode.
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_read
test eax, eax
jnz .error
 
mov eax, [ebx + EXT2_INODE_STRUC.i_size]
cmp eax, ecx
jge .success
 
; Save the size of the inode.
push eax
 
; ECX contains the size we've to write.
sub ecx, eax
xor edx, edx
div [ebp + EXTFS.block_size]
 
test edx, edx
jz .start_aligned
 
; Start isn't aligned, so deal with the non-aligned bytes.
mov esi, [ebp + EXTFS.block_size]
sub esi, edx
 
cmp esi, ecx
jbe @F
 
; If the size to entend to fits in current block, limit to that.
mov esi, ecx
 
@@:
; Clear ESI bytes, in EAX indexed block.
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jnz .error_inode_size
 
push eax ecx
xor eax, eax
mov ecx, esi
mov edi, ebx
add edi, edx
 
rep stosb
 
pop ecx eax
 
; Write the block.
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
add [esp], esi
sub ecx, esi
jz .write_inode
 
.start_aligned:
cmp ecx, [ebp + EXTFS.block_size]
jb @F
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
 
mov edx, [esp + 4]
call ext2_inode_blank_entry
 
test eax, eax
jnz .error_inode_size
 
mov eax, [ebp + EXTFS.block_size]
sub ecx, eax
add [esp], eax
jmp .start_aligned
 
; Handle the remaining bytes.
@@:
test ecx, ecx
jz .write_inode
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
 
mov edx, [esp + 4]
call ext2_inode_blank_entry
 
test eax, eax
jnz .error_inode_size
add [esp], ecx
 
.write_inode:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
 
test eax, eax
jnz .error
 
.success:
xor eax, eax
 
.ret:
add esp, 4
 
pop edi esi edx ecx ebx
ret
 
.error_inode_size:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Truncates inode to said size.
; Input: eax = inode ID.
; ecx = size to truncate to.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_truncate:
push ebx ecx edx esi edi
 
; Save the inode.
push eax
 
; Read the inode.
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_read
test eax, eax
jnz .error
 
mov eax, [ebx + EXT2_INODE_STRUC.i_size]
cmp ecx, eax
jge .success
 
; Save the size of the inode.
push eax
 
; ECX contains the size we've to truncate.
sub ecx, eax
not ecx
inc ecx
xor edx, edx
div [ebp + EXTFS.block_size]
 
test edx, edx
jz .start_aligned
 
; Start isn't aligned, so deal with the non-aligned bytes.
mov esi, edx
 
cmp esi, ecx
jbe @F
 
; If the size to truncate is smaller than the un-aligned bytes
; we're going to have to mark neccessary bytes from the EOF
; as 0.
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jnz .error_inode_size
 
mov edi, [ebp + EXTFS.ext2_save_block]
sub edx, ecx
add edi, edx
 
push ecx eax
xor eax, eax
rep stosb
pop eax ecx
 
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
 
sub [esp], ecx
jmp .write_inode
 
@@:
; Since ECX is greater than or equal to the bytes here un-aligned
; just free the block.
call ext2_inode_free_entry
 
sub [esp], esi
sub ecx, esi
jz .write_inode
 
.start_aligned:
cmp ecx, [ebp + EXTFS.block_size]
jb @F
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
dec eax
 
call ext2_inode_free_entry
 
test eax, eax
jnz .error_inode_size
 
mov eax, [ebp + EXTFS.block_size]
sub ecx, eax
sub [esp], eax
jmp .start_aligned
 
; Handle the remaining bytes.
@@:
test ecx, ecx
jz .write_inode
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
dec eax
 
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jnz .error_inode_size
 
mov edi, [ebp + EXTFS.ext2_save_block]
mov edx, [ebp + EXTFS.block_size]
sub edx, ecx
add edi, edx
 
push ecx eax
xor eax, eax
rep stosb
pop eax ecx
 
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
 
sub [esp], ecx
 
.write_inode:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
 
test eax, eax
jnz .error
 
.success:
xor eax, eax
 
.ret:
add esp, 4
 
pop edi esi edx ecx ebx
ret
 
.error_inode_size:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
 
.error:
xor eax, eax
not eax
jmp .ret
/kernel/branches/Kolibri-acpi/fs/ext2/resource.inc
0,0 → 1,223
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains common resource allocation + freeing code. ;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under the terms of the new BSD license. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;---------------------------------------------------------------------
; Frees a resource (block/inode).
; Input: eax = resource ID.
; edi = function pointer of ext2_bg_*_bitmap form, to
; get bitmap of resource.
; ecx = 0, block; 1, inode.
; ebp = pointer to EXTFS.
; Output: Block marked as free in block group.
; eax = error code.
;---------------------------------------------------------------------
ext2_resource_free:
push ebx edx esi
 
; Get block group.
sub eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block]
xor edx, edx
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group]
push eax edx
 
call edi
test eax, eax
jz .fail
mov esi, eax
 
; Read the bitmap.
mov eax, ebx
mov edx, eax
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_read
test eax, eax
jnz .fail
 
pop eax
; Mark bit free.
call bitmap_clear_bit
test eax, eax
jz @F
 
; No need to save anything.
xor eax, eax
 
add esp, 4
jmp .return
 
@@:
mov eax, edx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_write
test eax, eax
jnz .fail
 
; Read the descriptor.
mov eax, [esp]
call ext2_bg_read_desc
test eax, eax
jz .fail_bg_desc_read
 
lea eax, [eax + EXT2_BLOCK_GROUP_DESC.free_blocks_count]
shl ecx, 1
add eax, ecx
inc word[eax]
 
lea eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count]
shl ecx, 1
add eax, ecx
inc dword[eax]
 
pop eax
call ext2_bg_write_desc
 
.return:
pop esi edx ebx
ret
 
.fail:
add esp, 4
.fail_bg_desc_read:
add esp, 4
xor eax, eax
not eax
jmp .return
 
;---------------------------------------------------------------------
; Allocates a resource.
; Input: eax = inode ID for "preference".
; ebp = pointer to EXTFS.
; [esp + 4], func pointer to ext2_bg_*_bitmap
; [esp + 8], pointer to free_*_count in SB.
; [esp + 12], *_per_group
; [esp + 16], offset to free_*_count in bg descriptor.
; [esp + 20], *_count
; Output: Resource marked as set in block group.
; eax = error code.
; ebx = resource ID.
;---------------------------------------------------------------------
ext2_resource_alloc:
; Block allocation is a pretty serious area, since bad allocation
; can lead to fragmentation. Thus, the best way to allocate that
; comes to mind is to allocate around an inode as much as possible.
; On the other hand, this isn't about a single inode/file/directory,
; and focusing just around the preferred inode would lead to
; congestion. Thus, after much thought, the chosen allocation algorithm
; is to search forward, then backward.
push ecx edx esi edi
 
cmp dword[esp + 16 + 8], 0
jnz @F
 
; No free blocks.
xor eax, eax
not eax
pop edi esi edx ecx
ret 20
 
@@:
; Calculate which block group the preferred inode belongs to.
dec eax
xor edx, edx
; EAX = block group.
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group]
push eax
push eax
 
mov edi, .forward
 
.test_block_group:
call dword[esp + 16 + 8 + 4]
test eax, eax
jz .fail
mov esi, eax
 
mov eax, ebx
mov edx, eax
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_read
test eax, eax
jnz .fail
 
mov ecx, [esp + 16 + 8 + 12]
call ext2_find_free_bit
cmp eax, 0xFFFFFFFF
jne @F
 
mov eax, edi
jmp eax
 
@@:
mov ecx, eax
 
mov eax, edx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_write
test eax, eax
jnz .fail
 
; ecx: the index of the matched entry.
; [esp]: block group where we found.
; [esp + 4]: starting block group.
; esi: block group descriptor.
mov eax, [esp] ; Index of block group in which we found.
mul dword[esp + 16 + 8 + 12]
add eax, ecx
mov ebx, eax
 
mov eax, [esp + 16 + 8 + 8]
dec dword[eax]
 
mov eax, esi
add eax, [esp + 16 + 8 + 16]
dec word[eax]
 
pop eax
call ext2_bg_write_desc
 
add esp, 4
jmp .return
 
; Continue forward.
.forward:
inc dword[esp]
mov eax, [esp]
mul dword[esp + 16 + 8 + 12]
cmp eax, [esp + 16 + 8 + 20]
jbe @F
 
; We need to go backward.
mov eax, [esp + 4]
mov [esp], eax
mov edi, .backward
jmp .backward
 
@@:
mov eax, [esp]
jmp .test_block_group
 
; Continue backward.
.backward:
cmp dword[esp], 0
je .fail
 
dec dword[esp]
mov eax, [esp]
jmp .test_block_group
 
.return:
pop edi esi edx ecx
ret 20
 
.fail:
add esp, 8
xor eax, eax
not eax
jmp .return
/kernel/branches/Kolibri-acpi/fs/ext2
Property changes:
Added: tsvn:logminsize
+5
\ No newline at end of property
/kernel/branches/Kolibri-acpi/fs/fs-et.inc
0,0 → 1,12
dir0:
db 'KÕVAKETAS '
db 'MÄLUKETAS '
db 'FLOPPIKETAS'
db 0
 
dir1:
db 'ESIMENE '
db 'TEINE '
db 'KOLAMS '
db 'NELJAS '
db 0
/kernel/branches/Kolibri-acpi/fs/fs.inc
24,6 → 24,8
 
if lang eq sp
include 'fs/fs-sp.inc'
else if lang eq et
include 'fs/fs-et.inc'
else
dir0:
db 'HARDDISK '
/kernel/branches/Kolibri-acpi/fs/xfs.asm
0,0 → 1,2769
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
/kernel/branches/Kolibri-acpi/fs/xfs.inc
0,0 → 1,518
; from stat.h
; distinguish file types
S_IFMT = 0170000o ; These bits determine file type.
S_IFDIR = 0040000o ; Directory.
S_IFCHR = 0020000o ; Character device.
S_IFBLK = 0060000o ; Block device.
S_IFREG = 0100000o ; Regular file.
S_IFIFO = 0010000o ; FIFO.
S_IFLNK = 0120000o ; Symbolic link.
S_IFSOCK = 0140000o ; Socket.
; end stat.h
 
 
; XFS null constant: empty fields must be all ones, not zeros!
XFS_NULL = -1
 
 
; static sector numbers
XFS_SECT_SB = 0
XFS_SECT_AGF = 1
XFS_SECT_AGI = 2
XFS_SECT_AGFL = 3
 
 
; signatures of file system structures
; 'string' numbers are treated by fasm as big endian
XFS_SB_MAGIC = 'XFSB'
XFS_AGF_MAGIC = 'XAGF'
XFS_AGI_MAGIC = 'XAGI'
XFS_ABTB_MAGIC = 'ABTB'
XFS_ABTC_MAGIC = 'ABTC'
XFS_IBT_MAGIC = 'IABT'
XFS_DINODE_MAGIC = 'IN'
XFS_BMAP_MAGIC = 'BMAP'
XFS_DA_NODE_MAGIC = 0xbefe ; those are little endian here
XFS_ATTR_LEAF_MAGIC = 0xeefb ; but big endian in docs
XFS_DIR2_LEAF1_MAGIC = 0xf1d2 ; pay attention!
XFS_DIR2_LEAFN_MAGIC = 0xffd2 ;
XFS_DIR2_BLOCK_MAGIC = 'XD2B'
XFS_DIR2_DATA_MAGIC = 'XD2D'
XFS_DIR2_FREE_MAGIC = 'XD2F'
XFS_DQUOT_MAGIC = 'DQ'
 
 
; bitfield lengths for packed extent
; MSB to LSB / left to right
BMBT_EXNTFLAG_BITLEN = 1
BMBT_STARTOFF_BITLEN = 54
BMBT_STARTBLOCK_BITLEN = 52
BMBT_BLOCKCOUNT_BITLEN = 21
 
 
; those constants are taken from linux source (xfs_dir2_leaf.h)
; they are magic infile offsets for directories
XFS_DIR2_DATA_ALIGN_LOG = 3 ; i.e., 8 bytes
XFS_DIR2_LEAF_SPACE = 1
XFS_DIR2_SPACE_SIZE = (1 SHL (32 + XFS_DIR2_DATA_ALIGN_LOG))
XFS_DIR2_LEAF_OFFSET = (XFS_DIR2_LEAF_SPACE * XFS_DIR2_SPACE_SIZE)
XFS_DIR2_FREE_SPACE = 2
XFS_DIR2_SPACE_SIZE = (1 SHL (32 + XFS_DIR2_DATA_ALIGN_LOG))
XFS_DIR2_FREE_OFFSET = (XFS_DIR2_FREE_SPACE * XFS_DIR2_SPACE_SIZE)
 
 
; data section magic constants for directories (xfs_dir2_data.h)
XFS_DIR2_DATA_FD_COUNT = 3
XFS_DIR2_DATA_FREE_TAG = 0xffff
 
 
; valid inode formats
; enum xfs_dinode_fmt (xfs_dinode.h)
XFS_DINODE_FMT_DEV = 0 ; xfs_dev_t
XFS_DINODE_FMT_LOCAL = 1 ; one inode is enough (shortdir)
XFS_DINODE_FMT_EXTENTS = 2 ; one or more extents (leafdir, nodedir, regular files)
XFS_DINODE_FMT_BTREE = 3 ; highly fragmented files or really huge directories
XFS_DINODE_FMT_UUID = 4 ; uuid_t
 
 
; size of the unlinked inode hash table in the agi
XFS_AGI_UNLINKED_BUCKETS = 64
 
 
; possible extent states
; enum xfs_exntst_t (xfs_bmap_btree.h)
XFS_EXT_NORM = 0
XFS_EXT_UNWRITTEN = 1
XFS_EXT_DMAPI_OFFLINE = 2
XFS_EXT_INVALID = 3
 
 
; values for inode core flags / di_flags (xfs_dinode.h)
XFS_DIFLAG_REALTIME_BIT = 0 ; file's blocks come from rt area
XFS_DIFLAG_PREALLOC_BIT = 1 ; file space has been preallocated
XFS_DIFLAG_NEWRTBM_BIT = 2 ; for rtbitmap inode, new format
XFS_DIFLAG_IMMUTABLE_BIT = 3 ; inode is immutable
XFS_DIFLAG_APPEND_BIT = 4 ; inode is append-only
XFS_DIFLAG_SYNC_BIT = 5 ; inode is written synchronously
XFS_DIFLAG_NOATIME_BIT = 6 ; do not update atime
XFS_DIFLAG_NODUMP_BIT = 7 ; do not dump
XFS_DIFLAG_RTINHERIT_BIT = 8 ; create with realtime bit set
XFS_DIFLAG_PROJINHERIT_BIT = 9 ; create with parents projid
XFS_DIFLAG_NOSYMLINKS_BIT = 10 ; disallow symlink creation
XFS_DIFLAG_EXTSIZE_BIT = 11 ; inode extent size allocator hint
XFS_DIFLAG_EXTSZINHERIT_BIT = 12 ; inherit inode extent size
XFS_DIFLAG_NODEFRAG_BIT = 13 ; do not reorganize/defragment
XFS_DIFLAG_FILESTREAM_BIT = 14 ; use filestream allocator
XFS_DIFLAG_REALTIME = (1 SHL XFS_DIFLAG_REALTIME_BIT)
XFS_DIFLAG_PREALLOC = (1 SHL XFS_DIFLAG_PREALLOC_BIT)
XFS_DIFLAG_NEWRTBM = (1 SHL XFS_DIFLAG_NEWRTBM_BIT)
XFS_DIFLAG_IMMUTABLE = (1 SHL XFS_DIFLAG_IMMUTABLE_BIT)
XFS_DIFLAG_APPEND = (1 SHL XFS_DIFLAG_APPEND_BIT)
XFS_DIFLAG_SYNC = (1 SHL XFS_DIFLAG_SYNC_BIT)
XFS_DIFLAG_NOATIME = (1 SHL XFS_DIFLAG_NOATIME_BIT)
XFS_DIFLAG_NODUMP = (1 SHL XFS_DIFLAG_NODUMP_BIT)
XFS_DIFLAG_RTINHERIT = (1 SHL XFS_DIFLAG_RTINHERIT_BIT)
XFS_DIFLAG_PROJINHERIT = (1 SHL XFS_DIFLAG_PROJINHERIT_BIT)
XFS_DIFLAG_NOSYMLINKS = (1 SHL XFS_DIFLAG_NOSYMLINKS_BIT)
XFS_DIFLAG_EXTSIZE = (1 SHL XFS_DIFLAG_EXTSIZE_BIT)
XFS_DIFLAG_EXTSZINHERIT = (1 SHL XFS_DIFLAG_EXTSZINHERIT_BIT)
XFS_DIFLAG_NODEFRAG = (1 SHL XFS_DIFLAG_NODEFRAG_BIT)
XFS_DIFLAG_FILESTREAM = (1 SHL XFS_DIFLAG_FILESTREAM_BIT)
 
 
; superblock _ondisk_ structure (xfs_sb.h)
; this is _not_ the partition structure
; for XFS partition structure see XFS below
struct xfs_sb
sb_magicnum dd ? ; signature, must be XFS_SB_MAGIC
sb_blocksize dd ? ; block is the minimal file system unit, in bytes
sb_dblocks dq ? ; number of data blocks
sb_rblocks dq ? ; number of realtime blocks (not supported yet!)
sb_rextents dq ? ; number of realtime extents (not supported yet!)
sb_uuid rb 16 ; file system unique identifier
sb_logstart dq ? ; starting block of log (for internal journal; journals on separate devices are not supported!)
sb_rootino dq ? ; root inode number
sb_rbmino dq ? ; bitmap inode for realtime extents (ignored)
sb_rsumino dq ? ; summary inode for rt bitmap (ignored)
sb_rextsize dd ? ; realtime extent size, blocks
sb_agblocks dd ? ; size of an allocation group (the last one may be smaller!)
sb_agcount dd ? ; number of allocation groups
sb_rbmblocks dd ? ; number of rt bitmap blocks
sb_logblocks dd ? ; number of log blocks
sb_versionnum dw ? ; header version == XFS_SB_VERSION
sb_sectsize dw ? ; volume sector size in bytes (only 512B sectors are supported)
sb_inodesize dw ? ; inode size, bytes
sb_inopblock dw ? ; inodes per block
sb_fname rb 12 ; inodes per block (aka label)
sb_blocklog db ? ; log2 of sb_blocksize
sb_sectlog db ? ; log2 of sb_blocksize
sb_inodelog db ? ; log2 of sb_inodesize
sb_inopblog db ? ; log2 of sb_inopblock
sb_agblklog db ? ; log2 of sb_agblocks (rounded up!)
sb_rextslog db ? ; log2 of sb_rextents
sb_inprogress db ? ; mkfs is in progress, don't mount
sb_imax_pct db ? ; max % of fs for inode space
; statistics
sb_icount dq ? ; allocated inodes
sb_ifree dq ? ; free inodes
sb_fdblocks dq ? ; free data blocks
sb_frextents dq ? ; free realtime extents
 
sb_uquotino dq ? ; user quota inode
sb_gquotino dq ? ; group quota inode
sb_qflags dw ? ; quota flags
sb_flags db ? ; misc. flags
sb_shared_vn db ? ; shared version number
sb_inoalignmt dd ? ; inode chunk alignment, fsblocks
sb_unit dd ? ; stripe or raid unit
sb_width dd ? ; stripe or raid width
sb_dirblklog db ? ; log2 of dir block size (fsbs)
sb_logsectlog db ? ; log2 of the log sector size
sb_logsectsize dw ? ; sector size for the log, bytes
sb_logsunit dd ? ; stripe unit size for the log
sb_features2 dd ? ; additional feature bits
ends
 
 
; allocation group inode (xfs_ag.h)
struct xfs_agi
agi_magicnum dd ? ; magic number == XFS_AGI_MAGIC
agi_versionnum dd ? ; header version == XFS_AGI_VERSION
agi_seqno dd ? ; sequence number starting from 0
agi_length dd ? ; size in blocks of a.g.
agi_count dd ? ; count of allocated inodes
agi_root dd ? ; root of inode btree
agi_level dd ? ; levels in inode btree
agi_freecount dd ? ; number of free inodes
agi_newino dd ? ; new inode just allocated
agi_dirino dd ? ; last directory inode chunk
agi_unlinked rd XFS_AGI_UNLINKED_BUCKETS ; Hash table of inodes which have been unlinked but are still being referenced
ends
 
 
; superblock structure of b+tree node/leaf (same structure, bb_level matters)
struct xfs_btree_sblock
bb_magic dd ?
bb_level dw ? ; distinguishes nodeds and leaves
bb_numrecs dw ?
bb_leftsib dd ?
bb_rightsib dd ?
ends
 
 
; record of b+tree inode
struct xfs_inobt_rec
ir_startino dd ?
ir_freecount dd ?
ir_free dq ?
ends
 
 
; structure to store create, access and modification time in inode core
struct xfs_timestamp
t_sec dd ?
t_nsec dd ? ; nanoseconds
ends
 
 
; inode core structure: basic information about file
struct xfs_dinode_core
di_magic dw ? ; inode magic = XFS_DINODE_MAGIC
di_mode dw ? ; mode and type of file
di_version db ? ; inode version
di_format db ? ; format of di_c data
di_onlink dw ? ; old number of links to file
di_uid dd ? ; owner's user id
di_gid dd ? ; owner's group id
di_nlink dd ? ; number of links to file
di_projid dw ? ; owner's project id
di_pad rb 8 ; unused, zeroed space
di_flushiter dw ? ; incremented on flush
di_atime xfs_timestamp ; time last accessed
di_mtime xfs_timestamp ; time last modified
di_ctime xfs_timestamp ; time created/inode modified
di_size dq ? ; number of bytes in file
di_nblocks dq ? ; number of direct & btree blocks used
di_extsize dd ? ; basic/minimum extent size for file
di_nextents dd ? ; number of extents in data fork
di_anextents dw ? ; number of extents in attribute fork
di_forkoff db ? ; attr fork offs, <<3 for 64b align
di_aformat db ? ; format of attr fork's data
di_dmevmask dd ? ; DMIG event mask
di_dmstate dw ? ; DMIG state info
di_flags dw ? ; random flags, XFS_DIFLAG_...
di_gen dd ? ; generation number
ends
 
 
; shortform dir header
struct xfs_dir2_sf_hdr
count db ? ; the number of directory entries, used only if each inode number fits 4 bytes; zero otherwise
i8count db ? ; the number of directory entries, used only when count is zero
parent dq ? ; parent inode number: xfs_dir2_inou_t (4 or 8 bytes)
ends
 
 
; shortform dir entry
struct xfs_dir2_sf_entry
namelen db ? ; actual name length (ASCII)
offset rb 2 ; saved offset
name db ? ; name, variable size
; inumber dq ? ; xfs_dir2_inou_t
ends
 
 
; active entry in a data block
; aligned to 8 bytes
; tag appears as the last 2 bytes
struct xfs_dir2_data_entry
inumber dq ? ; inode number
namelen db ? ; name length
name db ? ; name bytes, no null
; tag dw ? ; starting offset of us
ends
 
 
; unused entry in a data block
; aligned to 8 bytes
; tag appears as the last 2 bytes
struct xfs_dir2_data_unused
freetag dw ? ; XFS_DIR2_DATA_FREE_TAG
length dw ? ; total free length
; tag dw ? ; starting offset of us
ends
 
 
; generic data entry
struct xfs_dir2_data_union
union
xentry xfs_dir2_data_entry
unused xfs_dir2_data_unused
ends
ends
 
 
; describe a free area in the data block
; the freespace will be formatted as a xfs_dir2_data_unused_t
struct xfs_dir2_data_free
offset dw ? ; start of freespace
length dw ? ; length of freespace
ends
 
 
; header for the data blocks
; always at the beginning of a directory-sized block
; the code knows that XFS_DIR2_DATA_FD_COUNT is 3
struct xfs_dir2_data_hdr
magic dd ? ; XFS_DIR2_DATA_MAGIC or XFS_DIR2_BLOCK_MAGIC
bestfree xfs_dir2_data_free
bestfree2 xfs_dir2_data_free
bestfree3 xfs_dir2_data_free
ends
 
 
; leaf block entry
struct xfs_dir2_leaf_entry
hashval dd ? ; hash value of name
address dd ? ; address of data entry
ends
 
 
; the tail of directory block
struct xfs_dir2_block_tail
count dd ? ; count of leaf entries
stale dd ? ; count of stale leaf entries
ends
 
 
; generic single-block structure, for xfs_db
struct xfs_dir2_block
hdr xfs_dir2_data_hdr
u xfs_dir2_data_union
; leaf xfs_dir2_leaf_entry
; tail xfs_dir2_block_tail
ends
 
 
;
struct xfs_dir2_data
hdr xfs_dir2_data_hdr ; magic XFS_DIR2_DATA_MAGIC
u xfs_dir2_data_union
ends
 
 
;
struct xfs_da_blkinfo
forw dd ? ; previous block in list
back dd ? ; following block in list
magic dw ? ; validity check on block
pad dw ? ; unused
ends
 
 
; leaf block header
struct xfs_dir2_leaf_hdr
info xfs_da_blkinfo ; header for da routines
count dw ? ; count of entries
stale dw ? ; count of stale entries
ends
 
 
; leaf block tail
struct xfs_dir2_leaf_tail
bestcount dd ?
ends
 
 
; leaf block
; bests and tail are at the end of the block for single-leaf only
; (magic = XFS_DIR2_LEAF1_MAGIC not XFS_DIR2_LEAFN_MAGIC)
struct xfs_dir2_leaf
hdr xfs_dir2_leaf_hdr ; leaf header
ents xfs_dir2_leaf_entry ; entries
; bests dw ? ; best free counts
; tail xfs_dir2_leaf_tail ; leaf tail
ends
 
 
; header of 'free' block part
struct xfs_dir2_free_hdr
magic dd ? ; XFS_DIR2_FREE_MAGIC
firstdb dd ? ; db of first entry
nvalid dd ? ; count of valid entries
nused dd ? ; count of used entries
ends
 
 
; 'free' part of directiry block
struct xfs_dir2_free
hdr xfs_dir2_free_hdr ; block header
bests dw ? ; best free counts
; unused entries are -1 (XFS_NULL)
ends
 
 
; b+tree node header
struct xfs_da_node_hdr
info xfs_da_blkinfo
count dw ?
level dw ?
ends
 
 
; b+tree node
struct xfs_da_node_entry
hashval dd ? ; hash value for this descendant
before dd ? ; Btree block before this key
ends
 
 
;
struct xfs_da_intnode
hdr xfs_da_node_hdr
btree xfs_da_node_entry
ends
 
 
; packet extent
struct xfs_bmbt_rec
l0 dq ?
l1 dq ?
ends
 
 
; unpacked extent
struct xfs_bmbt_irec
br_startoff dq ? ; starting file offset
br_startblock dq ? ; starting block number
br_blockcount dd ? ; number of blocks
br_state dd ? ; extent state
ends
 
 
; bmap root header, on-disk form only
struct xfs_bmdr_block
bb_level dw ? ; 0 is a leaf
bb_numrecs dw ? ; current number of data records
ends
 
 
; key structure for non-leaf levels of the tree
struct xfs_bmbt_key
br_startoff dq ? ; starting file offset
ends
 
 
sizeof.xfs_bmbt_ptr = 8 ; workaround
sizeof.xfs_bmdr_ptr = 8 ; workaround
 
 
; long form header: bmap btrees
; xfs_btree_lblock is xfs_bmbt_block (xfs_btree.h)
struct xfs_bmbt_block
bb_magic dd ? ; magic number for block type
bb_level dw ? ; 0 is a leaf
bb_numrecs dw ? ; current number of data records
bb_leftsib dq ? ; left sibling block or NULLDFSBNO
bb_rightsib dq ? ; right sibling block or NULLDFSBNO
ends
 
 
; high level inode structure
struct xfs_inode
di_core xfs_dinode_core ; main info, aka core
di_next_unlinked dd ? ; unlinked but still used inode (if any, XFS_NULL otherwise)
di_u db ? ; data fork inode part
; di_a db ? ; data attribute
ends
 
 
; internal data for every XFS partition
; this _is_ XFS partition structure
; most fields are unpacked or bswap'ed values from the superblock, so see xfs_sb structure above
struct XFS PARTITION
Lock MUTEX ? ; access mutex
blocksize dd ?
sectsize dd ?
dirblocksize dd ?
rootino dq ?
cur_block dd ?
cur_inode dd ?
cur_sect dd ?
cur_dirblock dd ?
tmp_inode dd ?
versionnum dd ?
features2 dd ?
inodesize dd ?
inopblock dd ?
blocklog dd ?
sectlog dd ?
inodelog dd ?
inopblog dd ?
agblklog dd ?
blockmsectlog dd ?
inodetoblocklog dd ?
dirblklog dd ?
sectpblock dd ?
agblocks dd ?
; helpers, temporary vars, etc
agblockmask dq ?
extent xfs_bmbt_irec
left_extents dd ?
left_leaves dd ?
bytes_to_read dd ?
bytes_read dd ?
entries_read dd ?
file_offset dq ?
max_dirblockaddr dd ?
next_block_num dq ?
dir2_leaf_offset_blocks dd ?
dir2_free_offset_blocks dd ?
cur_inode_save dd ?
bytes_left_in_file dq ?
ro_nextents dd ?
bb_ptrs dd ?
maxnumrecs dd ?
buffer_pos dd ?
eof dd ?
ends