kolibrios/kernel/trunk/fs/ext2/inode.inc

1851 lines
50 KiB
PHP
Raw Normal View History

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; 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