;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Contains ext2 inode handling code.                           ;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$


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