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 |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |