From 7365b0cf3fe6adc4738a07e7daadf2a49486cbb0 Mon Sep 17 00:00:00 2001 From: shikhin Date: Mon, 21 Oct 2013 09:30:07 +0000 Subject: [PATCH] Finished ext2 write support, under KSoC. git-svn-id: svn://kolibrios.org@4066 a494cfbc-eb01-0410-851d-a64ba20cac60 --- kernel/trunk/docs/sysfuncs.txt | 42 +- kernel/trunk/fs/ext2/blocks.inc | 409 +++++++ kernel/trunk/fs/ext2/ext2.asm | 530 ++++++++- kernel/trunk/fs/ext2/ext2.inc | 8 + kernel/trunk/fs/ext2/inode.inc | 1850 +++++++++++++++++++++++++++++ kernel/trunk/fs/ext2/resource.inc | 223 ++++ 6 files changed, 3025 insertions(+), 37 deletions(-) create mode 100644 kernel/trunk/fs/ext2/blocks.inc create mode 100644 kernel/trunk/fs/ext2/inode.inc create mode 100644 kernel/trunk/fs/ext2/resource.inc diff --git a/kernel/trunk/docs/sysfuncs.txt b/kernel/trunk/docs/sysfuncs.txt index ac91390557..5c61a99cf2 100644 --- a/kernel/trunk/docs/sysfuncs.txt +++ b/kernel/trunk/docs/sysfuncs.txt @@ -1566,26 +1566,26 @@ Returned value: Remarks: * The function is supported only for ATAPI devices (CD and DVD). * An example of usage of the function is the application CD_tray. - -====================================================================== -======= Function 25 - put image area on the background layer. ======== -====================================================================== -Paramters: - * eax = 25 - function number - * ebx = pointer to the previously allocated memory area, - where placed the source images in a format BBGGRRTTBBGGRRTT... - * ecx = [size on axis x]*65536 + [size on axis y] - * edx = [coordinate on axis x]*65536 + [coordinate on axis y] -Returned value: - * function does not return value -Remarks: - * Coordinates of the image are coordinates of the upper left corner - of the image relative to the screen. - * Size of the image in bytes is 4*xsize*ysize - * TT - byte pointer of transparency, at current version: - 1 to FF - opaque, 0 - transparent. - * The function places the image directly to LFB. It is not for - background image f.15. Options f.15 to f.25 does not make sense. + +====================================================================== +======= Function 25 - put image area on the background layer. ======== +====================================================================== +Paramters: + * eax = 25 - function number + * ebx = pointer to the previously allocated memory area, + where placed the source images in a format BBGGRRTTBBGGRRTT... + * ecx = [size on axis x]*65536 + [size on axis y] + * edx = [coordinate on axis x]*65536 + [coordinate on axis y] +Returned value: + * function does not return value +Remarks: + * Coordinates of the image are coordinates of the upper left corner + of the image relative to the screen. + * Size of the image in bytes is 4*xsize*ysize + * TT - byte pointer of transparency, at current version: + 1 to FF - opaque, 0 - transparent. + * The function places the image directly to LFB. It is not for + background image f.15. Options f.15 to f.25 does not make sense. ====================================================================== ======== Function 26, subfunction 1 - get MPU MIDI base port. ======== @@ -3953,7 +3953,7 @@ Format of the information structure: * +0: dword: 2 = subfunction number * +4: dword: 0 (reserved) * +8: dword: 0 (reserved) - * +12 = +0xC: dword: number of bytes to read + * +12 = +0xC: dword: number of bytes to write * +16 = +0x10: dword: pointer to data * +20 = +0x14: ASCIIZ-name of file, the rules of names forming are given in the general description diff --git a/kernel/trunk/fs/ext2/blocks.inc b/kernel/trunk/fs/ext2/blocks.inc new file mode 100644 index 0000000000..9492ad826e --- /dev/null +++ b/kernel/trunk/fs/ext2/blocks.inc @@ -0,0 +1,409 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 block handling code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;--------------------------------------------------------------------- +; Write ext2 block from memory to disk. +; Input: eax = i_block (block number in ext2 terms); +; ebx = buffer address +; ebp = pointer to EXTFS +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_block_write: + push edx ebx ecx + + mov edx, fs_write32_sys + jmp ext2_block_modify + +;--------------------------------------------------------------------- +; Read ext2 block from disk to memory. +; Input: eax = i_block (block number in ext2 terms); +; ebx = address of where to read block +; ebp = pointer to EXTFS +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_block_read: + push edx ebx ecx + + mov edx, fs_read32_sys + jmp ext2_block_modify + +;--------------------------------------------------------------------- +; Modify ext2 block. +; Input: eax = i_block (block number in ext2 terms); +; ebx = I/O buffer address; +; edx = fs_read/write32_sys +; ebp = pointer to EXTFS +; edx, ebx, ecx on stack. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_block_modify: + ; Get block number in hard-disk terms in eax. + mov ecx, [ebp + EXTFS.log_block_size] + shl eax, cl + mov ecx, eax + push [ebp + EXTFS.count_block_in_block] + + @@: + mov eax, ecx + call edx + test eax, eax + jnz .fail + + inc ecx + add ebx, 512 + dec dword[esp] + jnz @B + + xor eax, eax + @@: + pop ecx + pop ecx ebx edx + ret + + .fail: + mov eax, ERROR_DEVICE + jmp @B + +;--------------------------------------------------------------------- +; Zeroes a block. +; Input: ebx = block ID. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_block_zero: + push ebx + + mov eax, ebx + mov ebx, [ebp + EXTFS.ext2_temp_block] + + call ext2_block_read + test eax, eax + jnz .return + + push edi ecx + xor eax, eax + mov ecx, [ebp + EXTFS.block_size] + mov edi, [ebp + EXTFS.ext2_temp_block] + rep stosb + pop ecx edi + + mov eax, [esp] + call ext2_block_write + + .return: + pop ebx + ret + +;--------------------------------------------------------------------- +; Allocates a block. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; Output: Block marked as set in block group. +; eax = error code. +; ebx = block ID. +;--------------------------------------------------------------------- +ext2_block_alloc: + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_count] + push EXT2_BLOCK_GROUP_DESC.free_blocks_count + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group] + + lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count] + push ebx + + push ext2_bg_read_blk_bitmap + + call ext2_resource_alloc + + ret + +;--------------------------------------------------------------------- +; Zero-allocates a block. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; Output: Block marked as set in block group. +; eax = error code. +; ebx = block ID. +;--------------------------------------------------------------------- +ext2_block_calloc: + call ext2_block_alloc + test eax, eax + jnz @F + + call ext2_block_zero + @@: + ret + +;--------------------------------------------------------------------- +; Frees a block. +; Input: eax = block ID. +; ebp = pointer to EXTFS. +; Output: Block marked as free in block group. +; eax = error code. +;--------------------------------------------------------------------- +ext2_block_free: + push edi ecx + + mov edi, ext2_bg_read_blk_bitmap + xor ecx, ecx + call ext2_resource_free + + pop ecx edi + ret + +;--------------------------------------------------------------------- +; Find parent from file path in block. +; Input: esi = file path. +; ebx = pointer to directory block. +; ebp = pointer to EXTFS structure. +; Output: esi = name without parent, or not changed. +; ebx = directory record matched. +;--------------------------------------------------------------------- +ext2_block_find_parent: + sub esp, 256 ; Space for EXT2 filename. + mov edx, ebx + add edx, [ebp + EXTFS.block_size] ; Save block end. + + .start_rec: + cmp [ebx + EXT2_DIR_STRUC.inode], 0 + jz .next_rec + + mov edi, esp + push esi + movzx ecx, [ebx + EXT2_DIR_STRUC.name_len] + lea esi, [ebx + EXT2_DIR_STRUC.name] + call utf8_to_cp866 + + mov ecx, edi + lea edi, [esp + 4] + sub ecx, edi ; Number of bytes in resulting string. + + mov esi, [esp] + + ; esi: original file path. + ; edi: converted string stored on stack. + ; ecx: size of converted string. + @@: + ; If no bytes left in resulting string, test it. + jecxz .test_find + dec ecx + + lodsb + call char_toupper + + mov ah, [edi] + inc edi + xchg al, ah + call char_toupper + + ; If both are same, check next byte. + cmp al, ah + je @B + @@: ; Doesn't match. + pop esi + + .next_rec: + movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, eax ; Go to next record. + cmp ebx, edx ; Check if this is the end. + jb .start_rec + + add esp, 256 + ret + + .test_find: + cmp byte [esi], 0 + je .ret ; The end reached. + cmp byte [esi], '/' ; If not end of directory name, not matched. + jne @B + inc esi + + .ret: + add esp, 256 + 4 + ret + +;--------------------------------------------------------------------- +; Finds free space in a directory block, modifying last entry appropriately. +; Input: ebp = pointer to EXTFS. +; ecx = size of free space required. +; [EXTFS.ext2_temp_block] contains the block relevant. +; Output: edi = free entry. +; rec_len of free entry is set. +; eax = error code; if the block doesn't link to the next one, this is 0x00000001 on failure. +; ; else, 0xFFFFFFFF. +;--------------------------------------------------------------------- +ext2_block_find_fspace: + push ebx edx + + mov edi, [ebp + EXTFS.ext2_temp_block] + mov edx, edi + add edx, [ebp + EXTFS.block_size] + + @@: + movzx eax, [edi + EXT2_DIR_STRUC.rec_len] + test eax, eax + jz .zero_len + + cmp [edi + EXT2_DIR_STRUC.inode], 0 + je .unused_entry + + ; It's a used entry, so see if we can fit it between current one and next. + ; Subtract the size used by the name and the structure from rec_len. + movzx ebx, [edi + EXT2_DIR_STRUC.name_len] + add ebx, 8 + 3 + and ebx, 0xfffffffc ; Align it on the next 4-byte boundary. + + sub eax, ebx + add edi, ebx + cmp eax, ecx + jb .next_iter + + sub edi, ebx + mov [edi + EXT2_DIR_STRUC.rec_len], bx ; Make previous entry point to us. + add edi, ebx + + mov [edi + EXT2_DIR_STRUC.rec_len], ax ; Make current entry point to next one. + jmp .found + + .unused_entry: + ; It's an unused inode. + cmp eax, ecx + jge .found + + .next_iter: + add edi, eax + cmp edi, edx + jb @B + + .not_found: + xor eax, eax + not eax + jmp .ret + + ; Zero length entry means we have the rest of the block for us. + .zero_len: + mov eax, edx + sub eax, edi + + ; Point to next block. + mov [edi + EXT2_DIR_STRUC.rec_len], ax + + cmp eax, ecx + jge .fits + + mov [edi + EXT2_DIR_STRUC.inode], 0 + + ; It doesn't fit, but the block doesn't link to the next block. + xor eax, eax + inc eax + jmp .ret + + .fits: + mov [edi + EXT2_DIR_STRUC.rec_len], cx + + .found: + xor eax, eax + + .ret: + pop edx ebx + ret + +;--------------------------------------------------------------------- +; Gets the block group's descriptor. +; Input: eax = block group. +; Output: eax = if zero, error; else, points to block group descriptor. +; [EXTFS.ext2_temp_block] contains relevant block. +; ebp = pointer to EXTFS. +;--------------------------------------------------------------------- +ext2_bg_read_desc: + push edx ebx + mov edx, 32 + mul edx ; Get index of descriptor in global_desc_table. + + ; eax: block group descriptor offset relative to global descriptor table start + ; Find the block this block descriptor is in. + div [ebp + EXTFS.block_size] + add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + inc eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail + + add ebx, edx ; edx: local index of descriptor inside block + mov eax, ebx + + .return: + pop ebx edx + ret + + .fail: + xor eax, eax + jmp .return + +;--------------------------------------------------------------------- +; Writes a block group's descriptor. +; Input: eax = block group. +; [EXTFS.ext2_temp_data] contains the block relevant. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_bg_write_desc: + push edx ebx + mov edx, 32 + mul edx ; Get index of descriptor in global_desc_table. + + ; eax: block group descriptor offset relative to global descriptor table start + ; Find the block this block descriptor is in. + div [ebp + EXTFS.block_size] + add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + inc eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + + .return: + pop ebx edx + ret + +;--------------------------------------------------------------------- +; Gets the block group's block bitmap. +; Input: eax = block group. +; Output: eax = if zero, error; else, points to block group descriptor. +; ebx = block bitmap's block (hard disk). +;--------------------------------------------------------------------- +ext2_bg_read_blk_bitmap: + push ecx + + call ext2_bg_read_desc + test eax, eax + jz .fail + + mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.block_bitmap] ; Block number of block group bitmap - in ext2 terms. + + .return: + pop ecx + ret + + .fail: + xor eax, eax + jmp .return + +;--------------------------------------------------------------------- +; Updates superblock, plus backups. +; Input: ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_sb_update: + push ebx + + mov eax, 2 + lea ebx, [ebp + EXTFS.superblock] + call fs_write32_sys + + pop ebx + ret diff --git a/kernel/trunk/fs/ext2/ext2.asm b/kernel/trunk/fs/ext2/ext2.asm index af3c38023f..13c5301552 100644 --- a/kernel/trunk/fs/ext2/ext2.asm +++ b/kernel/trunk/fs/ext2/ext2.asm @@ -8,9 +8,9 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; include 'ext2.inc' -include 'blocks.asm' -include 'inode.asm' -include 'resource.asm' +include 'blocks.inc' +include 'inode.inc' +include 'resource.inc' iglobal align 4 @@ -197,6 +197,11 @@ proc ext2_create_partition test eax, eax jnz .error + ;call ext2_sb_update + ; Sync the disk. + ;mov esi, [ebp + PARTITION.Disk] + ;call disk_sync ; eax contains error code, if any. + mov eax, ebp ; Return pointer to EXTFS. pop edi esi ebp ebx ret @@ -252,6 +257,7 @@ endp ; eax = error code (0 implies no error) ;--------------------------------------------------------------------- ext2_ReadFolder: + ;DEBUGF 1, "Reading folder.\n" call ext2_lock cmp byte [esi], 0 jz .root_folder @@ -306,7 +312,7 @@ ext2_ReadFolder: add edx, 32 ; edx = mem to return. xor ecx, ecx ; Get number of first block. - call ext2_get_inode_block + call ext2_inode_get_block test eax, eax jnz .error_get_block @@ -355,7 +361,7 @@ ext2_ReadFolder: ; Read the next block. push ecx mov ecx, [edi] - call ext2_get_inode_block + call ext2_inode_get_block test eax, eax jnz .error_get_block @@ -482,6 +488,8 @@ ext2_ReadFolder: lea edi, [edx + 12] mov ecx, 20 / 4 rep stosd + + ;DEBUGF 1, "Returning with: %x.\n", eax ret .error_bad_len: @@ -497,7 +505,7 @@ ext2_ReadFolder: push eax call ext2_unlock pop eax - + ;DEBUGF 1, "Returning with: %x.\n", eax ret .error_empty_dir: ; inode of folder without blocks. @@ -518,6 +526,7 @@ ext2_ReadFolder: ; eax = error code (0 implies no error) ;--------------------------------------------------------------------- ext2_Read: + ;DEBUGF 1, "Attempting read.\n" call ext2_lock cmp byte [esi], 0 jnz @F @@ -607,7 +616,7 @@ ext2_Read: push ecx mov ecx, eax - call ext2_get_inode_block + call ext2_inode_get_block test eax, eax jnz .error_at_first_block @@ -651,7 +660,7 @@ ext2_Read: inc dword [esp] mov ecx, [esp] - call ext2_get_inode_block + call ext2_inode_get_block test eax, eax jnz .error_at_read_cycle @@ -674,7 +683,7 @@ ext2_Read: pop ecx ; Pop block counter in ECX. inc ecx - call ext2_get_inode_block + call ext2_inode_get_block test eax, eax jnz .error_at_finish_block @@ -705,6 +714,7 @@ ext2_Read: ret @@: xor eax, eax + ;DEBUGF 1, "Returning with: %x.\n", eax ret .only_one_block: @@ -722,6 +732,8 @@ ext2_Read: push eax call ext2_unlock pop eax + + ;DEBUGF 1, "Returning with: %x.\n", eax ret ;--------------------------------------------------------------------- @@ -732,6 +744,7 @@ ext2_Read: ; Output: eax = error code. ;--------------------------------------------------------------------- ext2_GetFileInfo: + ;DEBUGF 1, "Calling for file info.\n" call ext2_lock mov edx, [ebx + 16] cmp byte [esi], 0 @@ -749,6 +762,8 @@ ext2_GetFileInfo: push eax call ext2_unlock pop eax + + ;DEBUGF 1, "Returning with: %x.\n", eax ret .is_root: @@ -801,6 +816,7 @@ ext2_GetFileInfo: call ext2_unlock xor eax, eax + ;DEBUGF 1, "Returning with: %x.\n", eax ret ;--------------------------------------------------------------------- @@ -811,6 +827,13 @@ ext2_GetFileInfo: ; Output: eax = error code. ;--------------------------------------------------------------------- ext2_SetFileInfo: + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: push edx esi edi ebx call ext2_lock mov edx, [ebx + 16] @@ -888,6 +911,14 @@ ext2_SetFileInfo: ; Output: eax = error code. ;--------------------------------------------------------------------- ext2_Delete: + ;DEBUGF 1, "Attempting Delete.\n" + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: push ebx ecx edx esi edi call ext2_lock @@ -971,7 +1002,7 @@ ext2_Delete: @@: push ecx - call ext2_get_inode_block + call ext2_inode_get_block test eax, eax jnz .error_stack8 mov eax, ecx @@ -989,6 +1020,11 @@ ext2_Delete: jmp @B @@: + ; Free indirect blocks. + call ext2_inode_free_indirect_blocks + test eax, eax + jnz .error_stack4 + ; Clear the inode, and add deletion time. mov edi, [ebp + EXTFS.ext2_save_inode] xor eax, eax @@ -1047,6 +1083,7 @@ ext2_Delete: pop eax pop edi esi edx ecx ebx + ;DEBUGF 1, "And returning with: %x.\n", eax ret .error_stack8: @@ -1071,12 +1108,20 @@ ext2_Delete: ; Output: eax = error code. ;--------------------------------------------------------------------- ext2_CreateFolder: + ;DEBUGF 1, "Attempting to create folder.\n" + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: push ebx ecx edx esi edi call ext2_lock add esi, [esp + 20 + 4] - ; Can't create root, but for CreateFile already existing directory is success. + ; Can't create root, but for CreateFolder already existing directory is success. cmp byte [esi], 0 jz .success @@ -1194,6 +1239,7 @@ ext2_CreateFolder: pop eax pop edi esi edx ecx ebx + ;DEBUGF 1, "Returning with: %x.\n", eax ret .error: @@ -1210,12 +1256,464 @@ ext2_CreateFolder: mov eax, ERROR_DISK_FULL jmp .return -self_link: db ".", 0 -parent_link: db "..", 0 +self_link db ".", 0 +parent_link db "..", 0 +;--------------------------------------------------------------------- +; Rewrite a file. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +; ebx = bytes written. +;--------------------------------------------------------------------- ext2_Rewrite: -ext2_Write: -ext2_SetFileEnd: - xor ebx, ebx + ;DEBUGF 1, "Attempting Rewrite.\n" + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + mov eax, ERROR_UNSUPPORTED_FS ret + + @@: + push ecx edx esi edi + pushad + + call ext2_lock + + add esi, [esp + 16 + 32 + 4] + ; Can't create root. + cmp byte [esi], 0 + jz .error_access_denied + + push esi + stdcall ext2_inode_find, 0 + pop esi + + ; If the file is there, delete it. + test eax, eax + jnz @F + + pushad + + push eax + call ext2_unlock + pop eax + + push dword 0x00000000 + call ext2_Delete + add esp, 4 + + push eax + call ext2_lock + pop eax + + test eax, eax + jnz .error_access_denied_delete + + popad + @@: + ; Find parent. + call ext2_inode_find_parent + test eax, eax + jnz .error_access_denied + + ; Inode ID for preference. + mov eax, esi + call ext2_inode_alloc + test eax, eax + jnz .error_full + + ; Save allocated inode in EDX; filename is in EDI; parent ID in ESI. + mov edx, ebx + + push edi + + xor al, al + mov edi, [ebp + EXTFS.ext2_temp_inode] + mov ecx, [ebp + EXTFS.inode_size] + rep stosb + + mov edi, [ebp + EXTFS.ext2_temp_inode] + add edi, EXT2_INODE_STRUC.i_atime + call current_unix_time + + add edi, 8 + call current_unix_time + + pop edi + + mov ebx, [ebp + EXTFS.ext2_temp_inode] + mov [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG + mov eax, edx + call ext2_inode_write + test eax, eax + jnz .error + + ; Link parent to child. + mov eax, esi + mov ebx, edx + mov esi, edi + mov dl, EXT2_FT_REG_FILE + call ext2_inode_link + test eax, eax + jnz .error + + popad + push eax + call ext2_unlock + pop eax + + push dword 0x00000000 + call ext2_Write + add esp, 4 + + push eax + call ext2_lock + pop eax + + .success: + push eax + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + pop eax + + .return: + push eax + call ext2_unlock + pop eax + + pop edi esi edx ecx + + ;DEBUGF 1, "And returning with: %x.\n", eax + ret + + .error: + mov eax, ERROR_ACCESS_DENIED + jmp .success + + .error_access_denied_delete: + popad + + .error_access_denied: + popad + xor ebx, ebx + + mov eax, ERROR_ACCESS_DENIED + jmp .return + + .error_full: + popad + xor ebx, ebx + + mov eax, ERROR_DISK_FULL + jmp .return + +;--------------------------------------------------------------------- +; Write to a file. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +; ebx = number of bytes written. +;--------------------------------------------------------------------- +ext2_Write: + ;DEBUGF 1, "Attempting write, " + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: + push ecx edx esi edi + call ext2_lock + + add esi, [esp + 16 + 4] + + ; Can't write to root. + cmp byte [esi], 0 + jz .error + + push ebx ecx edx + stdcall ext2_inode_find, 0 + pop edx ecx ebx + ; If file not there, error. + xor ecx, ecx + test eax, eax + jnz .error_file_not_found + + ; Save the inode. + push esi + + ; Check if it's a file. + mov edx, [ebp + EXTFS.ext2_save_inode] + cmp [edx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG + jne .error + + mov eax, esi + mov ecx, [ebx + 4] + + call ext2_inode_extend + xor ecx, ecx + test eax, eax + jnz .error_device + + ; ECX contains the size to write, and ESI points to it. + mov ecx, [ebx + 0x0C] + mov esi, [ebx + 0x10] + + ; Save the size of the inode. + mov eax, [edx + EXT2_INODE_STRUC.i_size] + push eax + + xor edx, edx + div [ebp + EXTFS.block_size] + + test edx, edx + jz .start_aligned + + ; Start isn't aligned, so deal with the non-aligned bytes. + mov ebx, [ebp + EXTFS.block_size] + sub ebx, edx + + cmp ebx, ecx + jbe @F + + ; If the size to copy fits in current block, limit to that, instead of the entire block. + mov ebx, ecx + + @@: + ; Copy EBX bytes, in EAX indexed block. + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jnz .error_inode_size + + push ecx + + mov ecx, ebx + mov edi, ebx + add edi, edx + rep movsb + + pop ecx + + ; Write the block. + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + add [esp], ebx + sub ecx, ebx + jz .write_inode + + .start_aligned: + cmp ecx, [ebp + EXTFS.block_size] + jb @F + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + + push eax + mov edx, [esp + 8] + call ext2_inode_blank_entry + test eax, eax + pop eax + jnz .error_inode_size + + push ecx + + mov ecx, [ebp + EXTFS.block_size] + mov edi, [ebp + EXTFS.ext2_save_block] + rep movsb + + pop ecx + + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + mov eax, [ebp + EXTFS.block_size] + sub ecx, eax + add [esp], eax + jmp .start_aligned + + ; Handle the remaining bytes. + @@: + test ecx, ecx + jz .write_inode + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jz @F + + push eax + mov edx, [esp + 8] + + call ext2_inode_blank_entry + test eax, eax + pop eax + jnz .error_inode_size + + @@: + push ecx + mov edi, [ebp + EXTFS.ext2_save_block] + rep movsb + pop ecx + + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + add [esp], ecx + xor ecx, ecx + + .write_inode: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + + call ext2_inode_write + test eax, eax + jnz .error_device + + .success: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + .return: + push eax + call ext2_unlock + pop eax + + add esp, 4 + + mov ebx, [esp + 12] + sub ebx, ecx + pop edi esi edx ecx + + ;DEBUGF 1, "and returning with: %x.\n", eax + ret + + .error: + mov eax, ERROR_ACCESS_DENIED + jmp .return + + .error_file_not_found: + mov eax, ERROR_FILE_NOT_FOUND + jmp .return + + .error_inode_size: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + + call ext2_inode_write + + .error_device: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_DEVICE + jmp .return + +;--------------------------------------------------------------------- +; Set the end of a file. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_SetFileEnd: + test [ebp + EXTFS.partition_flags], EXT2_RO + jz @F + + mov eax, ERROR_UNSUPPORTED_FS + ret + + @@: + push ebx ecx edx esi edi + call ext2_lock + + add esi, [esp + 20 + 4] + + ; Can't write to root. + cmp byte [esi], 0 + jz .error + + stdcall ext2_inode_find, 0 + ; If file not there, error. + test eax, eax + jnz .error_file_not_found + + ; Check if it's a file. + mov edx, [ebp + EXTFS.ext2_save_inode] + cmp [edx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG + jne .error + + mov eax, esi + mov ecx, [ebx + 4] + call ext2_inode_extend + test eax, eax + jnz .error_disk_full + + mov eax, esi + call ext2_inode_truncate + test eax, eax + jnz .error_disk_full + + mov eax, esi + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_write + + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + .return: + push eax + call ext2_unlock + pop eax + + pop edi esi edx ecx ebx + ret + + .error: + mov eax, ERROR_ACCESS_DENIED + jmp .return + + .error_file_not_found: + mov eax, ERROR_FILE_NOT_FOUND + jmp .return + + .error_disk_full: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_DISK_FULL + jmp .return diff --git a/kernel/trunk/fs/ext2/ext2.inc b/kernel/trunk/fs/ext2/ext2.inc index 36df347808..68d2595fb6 100644 --- a/kernel/trunk/fs/ext2/ext2.inc +++ b/kernel/trunk/fs/ext2/ext2.inc @@ -7,6 +7,13 @@ ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Future jobs for driver, in order of preference: +; * clean up existing extents support. +; * add b-tree directories support. +; * add long file support. +; * add journal support. +; * add minor features that come with ext3/4. + ; Recommended move to some kernel-wide bitmap handling code (with a bit of abstraction, of course). ;--------------------------------------------------------------------- @@ -86,6 +93,7 @@ bitmap_find_free_bit: ; We found the value. Let's return with it. add esp, 4 + add eax, edx jmp .return diff --git a/kernel/trunk/fs/ext2/inode.inc b/kernel/trunk/fs/ext2/inode.inc new file mode 100644 index 0000000000..288def75a5 --- /dev/null +++ b/kernel/trunk/fs/ext2/inode.inc @@ -0,0 +1,1850 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 inode handling code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;--------------------------------------------------------------------- +; Receives block number from extent-based inode. +; Input: ecx = number of block in inode +; esi = address of extent header +; ebp = pointer to EXTFS +; Output: ecx = address of next block, if successful +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext4_block_recursive_search: + cmp word [esi + EXT4_EXTENT_HEADER.eh_magic], 0xF30A ;EXT4_EXT_MAGIC + jne .fail + + movzx ebx, [esi + EXT4_EXTENT_HEADER.eh_entries] + add esi, sizeof.EXT4_EXTENT_HEADER + cmp word [esi - sizeof.EXT4_EXTENT_HEADER + EXT4_EXTENT_HEADER.eh_depth], 0 + je .leaf_block ;листовой ли это блок? + + ;не листовой блок, а индексный ; eax - ext4_extent_idx + test ebx, ebx + jz .fail ;пустой индексный блок -> ошибка + + ;цикл по индексам экстентов + @@: + cmp ebx, 1 ;у индексов не хранится длина, + je .end_search_index ;поэтому, если остался последний - то это нужный + + cmp ecx, [esi + EXT4_EXTENT_IDX.ei_block] + jb .fail + + cmp ecx, [esi + sizeof.EXT4_EXTENT_IDX + EXT4_EXTENT_IDX.ei_block] ;блок слeдующего индекса + jb .end_search_index ;следующий дальше - значит текущий, то что нам нужен + + add esi, sizeof.EXT4_EXTENT_IDX + dec ebx + jmp @B + + .end_search_index: + ;ebp указывает на нужный extent_idx, считываем следующий блок + mov ebx, [ebp + EXTFS.ext2_temp_block] + mov eax, [esi + EXT4_EXTENT_IDX.ei_leaf_lo] + call ext2_block_read + test eax, eax + jnz .fail + mov esi, ebx + jmp ext4_block_recursive_search ;рекурсивно прыгаем в начало + + .leaf_block: ;листовой блок esi - ext4_extent + ;цикл по экстентам + @@: + test ebx, ebx + jz .fail ;ни один узел не подошел - ошибка + + mov edx, [esi + EXT4_EXTENT.ee_block] + cmp ecx, edx + jb .fail ;если меньше, значит он был в предыдущих блоках -> ошибка + + movzx edi, [esi + EXT4_EXTENT.ee_len] + add edx, edi + cmp ecx, edx + jb .end_search_extent ;нашли нужный блок + + add esi, sizeof.EXT4_EXTENT + dec ebx + jmp @B + + .end_search_extent: + mov edx, [esi + EXT4_EXTENT.ee_start_lo] + sub ecx, [esi + EXT4_EXTENT.ee_block] ;разница в ext4 блоках + add ecx, edx + xor eax, eax + ret + + .fail: + mov eax, ERROR_FS_FAIL + ret + +;--------------------------------------------------------------------- +; Frees triply indirect block. +; Input: eax = triply indirect block. +; [ebp + EXTFS.ext2_save_inode] = the inode. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_free_triply_indirect: + push ebx edx + + test eax, eax + jz .success + push eax + ; Read the triple indirect block. + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + pop eax + jnz .fail + + ; Free the triple indirect block. + call ext2_block_free + test eax, eax + jnz .fail + + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + + @@: + mov eax, [ebx] + test eax, eax + jz .success + + call ext2_inode_free_doubly_indirect + cmp eax, 1 + je .success + cmp eax, 0xFFFFFFFF + je .fail + + add ebx, 4 + cmp ebx, edx + jb @B + + .success: + xor eax, eax + .ret: + pop edx ebx + ret + + .fail: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Frees double indirect block. +; Input: eax = double indirect block. +; [ebp + EXTFS.ext2_save_inode] = the inode. +; Output: eax = error code, 1 implies finished, ~0 implies error +;--------------------------------------------------------------------- +ext2_inode_free_doubly_indirect: + push ebx edx + + test eax, eax + jz .complete + push eax + ; Read the double indirect block. + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + pop eax + jnz .fail + + call ext2_block_free + test eax, eax + jnz .fail + + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + + @@: + mov eax, [ebx] + test eax, eax + jz .complete + + call ext2_block_free + test eax, eax + jnz .fail + + add ebx, 4 + cmp ebx, edx + jb @B + + .success: + xor eax, eax + .ret: + pop edx ebx + ret + + .complete: + xor eax, eax + inc eax + jmp .ret + + .fail: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Frees all indirect blocks. +; Input: ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_save_inode] = the inode. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_free_indirect_blocks: + push edi + + mov edi, [ebp + EXTFS.ext2_save_inode] + + ; Free indirect block. + mov eax, [edi + EXT2_INODE_STRUC.i_block + 12*4] + test eax, eax + jz .success + + call ext2_block_free + test eax, eax + jnz .fail + + mov eax, [edi + EXT2_INODE_STRUC.i_block + 13*4] + call ext2_inode_free_doubly_indirect + cmp eax, 1 + je .success + cmp eax, 0xFFFFFFFF + je .fail + + mov eax, [edi + EXT2_INODE_STRUC.i_block + 14*4] + call ext2_inode_free_triply_indirect + test eax, eax + jnz .fail + + .success: + xor eax, eax + .ret: + pop edi + ret + + .fail: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Allocates block for inode. +; Input: esi = address of inode +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_calloc_block: + push ecx + + ; TODO: fix to have correct preference. + mov eax, EXT2_ROOT_INO + call ext2_block_calloc + test eax, eax + jnz .fail + + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + mov eax, 2 + shl eax, cl + add [esi + EXT2_INODE_STRUC.i_blocks], eax + + .success: + xor eax, eax + .ret: + pop ecx + ret + + .fail: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Sets block ID for indirect-addressing inode. +; Input: ecx = index of block in inode +; edi = block ID to set to +; esi = address of inode +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_set_block: + push ebx ecx edx + + ; 0 to 11: direct blocks. + cmp ecx, 12 + jb .direct_block + + ; Indirect blocks + sub ecx, 12 + cmp ecx, [ebp + EXTFS.count_pointer_in_block] + jb .indirect_block + + ; Double indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block] + cmp ecx, [ebp + EXTFS.count_pointer_in_block_square] + jb .double_indirect_block + + ; Triple indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block_square] + + ; Get triply-indirect block in temp_block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4] + test eax, eax + jnz @F + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc + + mov [esi + EXT2_INODE_STRUC.i_block + 14*4], ebx + mov eax, ebx + + @@: + push eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + ; Get index in triply-indirect block. + xor edx, edx + mov eax, ecx + div [ebp + EXTFS.count_pointer_in_block_square] + + ; eax: index in triply-indirect block, edx: index in doubly-indirect block. + lea ecx, [ebx + eax*4] + mov eax, [ebx + eax*4] + test eax, eax + jnz @F + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc_4 + + mov [ecx], ebx + + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .fail_alloc_4 + + mov eax, [ecx] + @@: + mov [esp], eax + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + mov eax, edx + jmp @F + + .double_indirect_block: + ; Get doubly-indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4] + test eax, eax + jnz .double_indirect_present + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc + + mov [esi + EXT2_INODE_STRUC.i_block + 13*4], ebx + mov eax, ebx + + .double_indirect_present: + ; Save block we're at. + push eax + + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + mov eax, ecx + @@: + xor edx, edx + div [ebp + EXTFS.count_pointer_in_block] + + ; eax: index in doubly-indirect block, edx: index in indirect block. + lea ecx, [ebx + edx*4] + push ecx + + lea ecx, [ebx + eax*4] + cmp dword[ecx], 0 + jne @F + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc_8 + + mov [ecx], ebx + + mov eax, [esp + 4] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .fail_alloc_8 + + @@: + mov eax, [ecx] + push eax + call ext2_block_read + test eax, eax + jnz .fail_alloc_12 + + pop eax + pop ecx + mov [ecx], edi + call ext2_block_write + + add esp, 4 + jmp .return + + .indirect_block: + ; Get index of indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4] + test eax, eax + jnz @F + + call ext2_inode_calloc_block + test eax, eax + jnz .fail_alloc + + mov [esi + EXT2_INODE_STRUC.i_block + 12*4], ebx + mov eax, ebx + + @@: + push eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + ; Get the block ID. + mov [ebx + ecx*4], edi + pop eax + call ext2_block_write + jmp .return + + .direct_block: + mov [esi + EXT2_INODE_STRUC.i_block + ecx*4], edi + xor eax, eax + + .return: + pop edx ecx ebx + ret + + .fail_alloc: + xor eax, eax + not eax + jmp .return + + .fail_alloc_12: + add esp, 4 + .fail_alloc_8: + add esp, 4 + .fail_alloc_4: + add esp, 4 + jmp .fail_alloc + +;--------------------------------------------------------------------- +; Receives block ID from indirect-addressing inode. +; Input: ecx = index of block in inode +; esi = address of inode +; ebp = pointer to EXTFS +; Output: ecx = block ID, if successful +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_get_block: + ; If inode is extent-based, use ext4_block_recursive_search. + test [esi + EXT2_INODE_STRUC.i_flags], EXT2_EXTENTS_FL + jz @F + + pushad + + ; Get extent header in EBP. + add esi, EXT2_INODE_STRUC.i_block + call ext4_block_recursive_search + mov PUSHAD_ECX, ecx + mov PUSHAD_EAX, eax + + popad + ret + + @@: + ; 0 to 11: direct blocks. + cmp ecx, 12 + jb .get_direct_block + + ; Indirect blocks + sub ecx, 12 + cmp ecx, [ebp + EXTFS.count_pointer_in_block] + jb .get_indirect_block + + ; Double indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block] + cmp ecx, [ebp + EXTFS.count_pointer_in_block_square] + jb .get_double_indirect_block + + ; Triple indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block_square] + push edx ebx + + ; Get triply-indirect block in temp_block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail + + ; Get index in triply-indirect block. + xor edx, edx + mov eax, ecx + div [ebp + EXTFS.count_pointer_in_block_square] + + ; eax: index in triply-indirect block, edx: index in doubly-indirect block. + mov eax, [ebx + eax*4] + test eax, eax + jz .fail_triple_indirect_block + + call ext2_block_read + test eax, eax + jnz .fail + + mov eax, edx + jmp @F + + .get_double_indirect_block: + push edx ebx + + ; Get doubly-indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4] + test eax, eax + jz .fail_double_indirect_block + + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail + + mov eax, ecx + @@: + xor edx, edx + div [ebp + EXTFS.count_pointer_in_block] + + ; eax: index in doubly-indirect block, edx: index in indirect block. + mov eax, [ebx + eax*4] + test eax, eax + jz .fail_double_indirect_block + + call ext2_block_read + test eax, eax + jnz .fail + + mov ecx, [ebx + edx*4] + .fail: + pop ebx edx + + ret + + .get_indirect_block: + push ebx + + ; Get index of indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4] + test eax, eax + jz .fail_indirect_block + + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz @F + + mov ecx, [ebx + ecx*4] + @@: + pop ebx + + ret + + .get_direct_block: + mov ecx, [esi + EXT2_INODE_STRUC.i_block + ecx*4] + xor eax, eax + + ret + + .fail_indirect_block: + pop ebx + + .fail_triple_indirect_block: + xor eax, eax + xor ecx, ecx + ret + + .fail_double_indirect_block: + pop ebx edx + jmp .fail_triple_indirect_block + +;--------------------------------------------------------------------- +; Get block containing inode. +; Input: eax = inode number. +; ebp = pointer to EXTFS. +; Output: ebx = block (hard disk) containing inode. +; edx = index inside block. +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_read_block_of_inode: + pushad + + dec eax + xor edx, edx + + ; EAX = block group. + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + + push edx ; Index in group. + + mov edx, 32 + mul edx ; Get index of descriptor in global_desc_table. + + ; eax: inode group offset relative to global descriptor table start + ; Find the block this block descriptor is in. + div [ebp + EXTFS.block_size] + add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + inc eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .return + + add ebx, edx ; edx: local index of descriptor inside block + mov eax, [ebx + EXT2_BLOCK_GROUP_DESC.inode_table] ; Block number of inode table - in ext2 terms. + mov ecx, [ebp + EXTFS.log_block_size] + shl eax, cl + + ; eax: points to inode table on HDD. + mov esi, eax + + ; Add local address of inode. + pop eax + mov ecx, [ebp + EXTFS.inode_size] + mul ecx ; (index * inode_size) + + mov ebp, 512 + div ebp ; Divide by hard disk block size. + + add eax, esi ; Found block to read. + mov ebx, eax ; Get it inside ebx. + + xor eax, eax + .return: + mov PUSHAD_EAX, eax + mov PUSHAD_EBX, ebx + mov PUSHAD_EDX, edx + + popad + ret + +;--------------------------------------------------------------------- +; Sets content of inode by number. +; Input: eax = inode number. +; ebx = address from where to write inode content. +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_write: + push edx edi esi ecx ebx + mov esi, ebx + + ; Ext2 actually stores time of modification of inode in ctime. + lea edi, [ebx + EXT2_INODE_STRUC.i_ctime] + call current_unix_time + + ; Get block where inode is situated. + call ext2_read_block_of_inode + test eax, eax + jnz .error + + mov eax, ebx ; Get block into EAX. + mov ebx, [ebp + EXTFS.ext2_temp_block] + + mov ecx, eax ; Save block. + call fs_read32_sys + test eax, eax + jz @F + + .error: + mov eax, ERROR_DEVICE + jmp .return + + @@: + mov eax, ecx + mov ecx, [ebp + EXTFS.inode_size] + mov edi, edx ; The index into the block. + add edi, ebx + rep movsb + + ; Write the block. + call fs_write32_sys + + .return: + pop ebx ecx esi edi edx + ret + +;--------------------------------------------------------------------- +; Get content of inode by number. +; Input: eax = inode number. +; ebx = address where to store inode content. +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_read: + push edx edi esi ecx ebx + mov edi, ebx + + ; Get block where inode is situated. + call ext2_read_block_of_inode + test eax, eax + jnz .error + + mov eax, ebx ; Get block into EAX. + mov ebx, [ebp + EXTFS.ext2_temp_block] + call fs_read32_sys + test eax, eax + jz @F + + .error: + mov eax, ERROR_DEVICE + jmp .return + + @@: + mov ecx, [ebp + EXTFS.inode_size] + mov esi, edx ; The index into the inode. + add esi, ebx + rep movsb + + xor eax, eax + .return: + pop ebx ecx esi edi edx + ret + +;--------------------------------------------------------------------- +; Seek inode from the path. +; Input: esi + [esp + 4] = name. +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +; esi = inode number. +; dl = first byte of file/folder name. +; [ext2_data.ext2_save_inode] stores the inode. +;--------------------------------------------------------------------- +ext2_inode_find: + mov edx, [ebp + EXTFS.root_inode] + + ; Check for empty root. + cmp [edx + EXT2_INODE_STRUC.i_blocks], 0 + je .error_empty_root + + ; Check for root. + cmp byte[esi], 0 + jne .next_path_part + + push edi ecx + mov esi, [ebp + EXTFS.root_inode] + mov edi, [ebp + EXTFS.ext2_save_inode] + mov ecx, [ebp + EXTFS.inode_size] + rep movsb + pop ecx edi + + xor eax, eax + xor dl, dl + mov esi, EXT2_ROOT_INO + ret 4 + + .next_path_part: + push [edx + EXT2_INODE_STRUC.i_blocks] + xor ecx, ecx + + .folder_block_cycle: + push ecx + xchg esi, edx + call ext2_inode_get_block + xchg esi, edx + test eax, eax + jnz .error_get_inode_block + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] ; Get directory records from directory. + call ext2_block_read + test eax, eax + jnz .error_get_block + + push esi + push edx + call ext2_block_find_parent + pop edx + pop edi ecx + + cmp edi, esi ; Did something match? + je .next_folder_block ; No, move to next block. + + cmp byte [esi], 0 ; Reached the "end" of path successfully. + jnz @F + cmp dword[esp + 8], 0 + je .get_inode_ret + mov esi, [esp + 8] + mov dword[esp + 8], 0 + + @@: + mov eax, [ebx + EXT2_DIR_STRUC.inode] + mov ebx, [ebp + EXTFS.ext2_save_inode] + call ext2_inode_read + test eax, eax + jnz .error_get_inode + + movzx eax, [ebx + EXT2_INODE_STRUC.i_mode] + and eax, EXT2_S_IFMT ; Get the mask. + cmp eax, EXT2_S_IFDIR + jne .not_found ; Matched till part, but directory entry we got doesn't point to folder. + + pop ecx ; Stack top contains number of blocks. + mov edx, ebx + jmp .next_path_part + + .next_folder_block: + ; Next block in current folder. + pop eax ; Get blocks counter. + sub eax, [ebp + EXTFS.count_block_in_block] + jle .not_found + + push eax + inc ecx + jmp .folder_block_cycle + + .not_found: + mov eax, ERROR_FILE_NOT_FOUND + ret 4 + + .get_inode_ret: + pop ecx ; Stack top contains number of blocks. + + mov dl, [ebx + EXT2_DIR_STRUC.name] ; First character of file-name. + mov eax, [ebx + EXT2_DIR_STRUC.inode] + mov ebx, [ebp + EXTFS.ext2_save_inode] + mov esi, eax + + ; If we can't get the inode, eax contains the error. + call ext2_inode_read + ret 4 + + .error_get_inode_block: + .error_get_block: + pop ecx + .error_get_inode: + pop ebx + .error_empty_root: + mov eax, ERROR_FS_FAIL + ret 4 + +;--------------------------------------------------------------------- +; Seeks parent inode from path. +; Input: esi = path. +; ebp = pointer to EXTFS. +; Output: eax = error code. +; esi = inode. +; edi = pointer to file name. +;--------------------------------------------------------------------- +ext2_inode_find_parent: + push esi + xor edi, edi + + .loop: + cmp byte[esi], '/' + jne @F + + mov edi, esi + inc esi + jmp .loop + + @@: + inc esi + cmp byte[esi - 1], 0 + jne .loop + + ; If it was just a filename (without any additional directories), + ; use the last byte as "parent path". + cmp edi, 0 + jne @F + + pop edi + dec esi + jmp .get_inode + + ; It had some additional directories, so handle it that way. + @@: + mov byte[edi], 0 + inc edi + pop esi + + .get_inode: + push ebx edx + stdcall ext2_inode_find, 0 + pop edx ebx + + .return: + ret + +;--------------------------------------------------------------------- +; Link an inode. +; Input: eax = inode on which to link. +; ebx = inode to link. +; dl = file type. +; esi = name. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_link: + push eax + push esi edi ebx ecx edx + + ; Get string length, and then directory entry structure size. + call strlen + add ecx, 8 + + push esi ebx ecx + + xor ecx, ecx + mov esi, [ebp + EXTFS.ext2_temp_inode] + mov ebx, esi + + call ext2_inode_read + test eax, eax + jnz .error_inode_read + + ; Get the maximum addressible i_block index by (i_blocks/(2 << s_log_block_size)). + ; Note that i_blocks contains number of reserved 512B blocks, which is why we've to + ; find out the ext2 blocks. + mov eax, 2 + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + shl eax, cl + mov ecx, eax + + mov eax, [esi + EXT2_INODE_STRUC.i_blocks] + xor edx, edx + + div ecx + + ; EAX is the maximum index inside i_block we can go. + push eax + push dword 0 + + ; ECX contains the "block inside i_block" index. + xor ecx, ecx + @@: + call ext2_inode_get_block + test eax, eax + jnz .error_get_inode_block + test ecx, ecx + jz .alloc_block ; We've got no block here, so allocate one. + + push ecx ; Save block number. + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .error_block_read + + ; Try to find free space in current block. + mov ecx, [esp + 8] + call ext2_block_find_fspace + test eax, eax + jz .found + + cmp eax, 0x00000001 + jne .next_iter + + ; This block wasn't linking to the next block, so fix that, and use the next one. + ; Write the block. + pop eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .error_get_inode_block + + inc dword [esp] + mov ecx, [esp] + call ext2_inode_get_block + test eax, eax + jnz .error_get_inode_block + + test ecx, ecx + jz .alloc_block + + ; If there was a block there, prepare it for our use! + push ecx + jmp .prepare_block + + .next_iter: + add esp, 4 + + inc dword [esp] + mov ecx, [esp] + cmp ecx, [esp + 4] + jbe @B + + .alloc_block: + mov eax, [esp + 12] ; Get inode ID of what we're linking. + call ext2_block_calloc + test eax, eax + jnz .error_get_inode_block + + mov ecx, [esp] ; Get the index of it inside the inode. + mov edi, ebx ; And what to set to. + call ext2_inode_set_block + test eax, eax + jnz .error_get_inode_block + + ; Update i_size. + mov eax, [ebp + EXTFS.block_size] + add [esi + EXT2_INODE_STRUC.i_size], eax + + ; Update i_blocks. + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + mov eax, 2 + shl eax, cl + add [esi + EXT2_INODE_STRUC.i_blocks], eax + + ; Write the inode. + mov eax, [esp + 40] + mov ebx, esi + call ext2_inode_write + test eax, eax + jnz .error_get_inode_block + + push edi ; Save the block we just allocated. + + ; If we've allocated/using-old-block outside of loop, prepare it. + .prepare_block: + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .error_block_read + + mov edi, ebx + mov eax, [ebp + EXTFS.block_size] + mov [edi + EXT2_DIR_STRUC.rec_len], ax + + .found: + pop edx + add esp, 8 + pop ecx ebx esi + + push ebx + mov [edi], ebx ; Save inode. + + mov eax, [esp + 4] ; Get EDX off the stack -- contains the file_type. + cmp [ebp + EXTFS.superblock + EXT2_SB_STRUC.rev_level], EXT2_GOOD_OLD_REV + je .name + + ; Set the file-type. + mov [edi + EXT2_DIR_STRUC.file_type], al + + .name: + ; Save name. + sub ecx, 8 + mov [edi + EXT2_DIR_STRUC.name_len], cl + add edi, 8 + rep movsb + + ; Write block. + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .error_block_write + + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + test eax, eax + jnz .error_block_write + + pop eax + inc [ebx + EXT2_INODE_STRUC.i_links_count] + call ext2_inode_write + test eax, eax + jnz .error + + xor eax, eax + .ret: + pop edx ecx ebx edi esi + add esp, 4 + ret + + .error_block_read: + add esp, 4 + .error_get_inode_block: + add esp, 8 + .error_inode_read: + add esp, 8 + .error_block_write: + add esp, 4 + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Unlink an inode. +; Input: eax = inode from which to unlink. +; ebx = inode to unlink. +; ebp = pointer to EXTFS. +; Output: eax = number of links to inode, after unlinking (0xFFFFFFFF implies error) +;--------------------------------------------------------------------- +ext2_inode_unlink: + push ebx ecx edx esi edi + + push ebx + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + + test eax, eax + jnz .fail_get_inode + + ; The index into the inode block data. + push dword 0 + mov esi, [ebp + EXTFS.ext2_temp_inode] + + .loop: + mov ecx, [esp] + call ext2_inode_get_block + + test eax, eax + jnz .fail_loop + test ecx, ecx + jz .fail_loop + + mov eax, ecx + mov edi, eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_loop + + ; edi -> block. + .first_dir_entry: + mov eax, [esp + 4] + cmp [ebx], eax + jne @F + + mov dword[ebx], 0 ; inode. + mov word[ebx + 6], 0 ; name_len + file_type. + jmp .write_block + + @@: + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + push edx + + mov edx, ebx + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, ecx + + .dir_entry: + cmp [ebx], eax + jne @F + + mov cx, [ebx + EXT2_DIR_STRUC.rec_len] + add [edx + EXT2_DIR_STRUC.rec_len], cx + add esp, 4 + jmp .write_block + + @@: + mov edx, ebx + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + + ; If it's a zero length entry, error. + test ecx, ecx + jz .fail_inode + + add ebx, ecx + + cmp ebx, [esp] + jb .dir_entry + + add esp, 4 + inc dword[esp] + jmp .loop + + .write_block: + mov eax, edi + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .fail_loop + + add esp, 4 + mov ebx, [ebp + EXTFS.ext2_temp_inode] + mov eax, [esp] + call ext2_inode_read + test eax, eax + jnz .fail_get_inode + + dec word[ebx + EXT2_INODE_STRUC.i_links_count] + movzx eax, word[ebx + EXT2_INODE_STRUC.i_links_count] + push eax + + mov eax, [esp + 4] + call ext2_inode_write + test eax, eax + jnz .fail_loop + + pop eax + add esp, 4 + .return: + pop edi esi edx ecx ebx + ret + + .fail_inode: + add esp, 4 + + .fail_loop: + add esp, 4 + + .fail_get_inode: + add esp, 4 + + .fail: + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Checks if a directory is empty. +; Input: ebx = inode to check. +; ebp = pointer to EXTFS. +; [EXTFS.ext2_save_inode] = points to saved inode. +; Output: eax = 0 signifies empty directory. +;--------------------------------------------------------------------- +ext2_dir_empty: + push ebx ecx edx + + ; The index into the inode block data. + push dword 0 + mov esi, [ebp + EXTFS.ext2_save_inode] + + .loop: + mov ecx, [esp] + call ext2_inode_get_block + + ; Treat a failure as not-empty. + test eax, eax + jnz .not_empty + test ecx, ecx + jz .empty + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .not_empty + + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, ecx + + .dir_entry: + ; Process entry. + cmp byte[ebx + EXT2_DIR_STRUC.name_len], 1 + jne @F + + cmp byte[ebx + EXT2_DIR_STRUC.name], '.' + jne .not_empty + + @@: + cmp byte[ebx + EXT2_DIR_STRUC.name_len], 2 + jne .not_empty + + cmp word[ebx + EXT2_DIR_STRUC.name], '..' + jne .not_empty + + @@: + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, ecx + + cmp ebx, edx + jb .dir_entry + + inc dword[esp] + jmp .loop + + .empty: + xor eax, eax + .return: + add esp, 4 + pop edx ecx ebx + ret + + .not_empty: + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Gets the block group's inode bitmap. +; Input: eax = block group. +; Output: eax = if zero, error; else, points to block group descriptor. +; ebx = inode bitmap's block (hard disk). +;--------------------------------------------------------------------- +ext2_bg_read_inode_bitmap: + push ecx + + call ext2_bg_read_desc + test eax, eax + jz .fail + + mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.inode_bitmap] ; Block number of inode bitmap - in ext2 terms. + + .return: + pop ecx + ret + + .fail: + xor eax, eax + jmp .return + +;--------------------------------------------------------------------- +; Allocates a inode. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; Output: Inode marked as set in inode group. +; eax = error code. +; ebx = inode ID. +;--------------------------------------------------------------------- +ext2_inode_alloc: + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_count] + push EXT2_BLOCK_GROUP_DESC.free_inodes_count + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + + lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_inodes_count] + push ebx + + push ext2_bg_read_inode_bitmap + + call ext2_resource_alloc + + ; Inode table starts with 1. + inc ebx + + ret + +;--------------------------------------------------------------------- +; Frees a inode. +; Input: eax = inode ID. +; ebp = pointer to EXTFS. +; Output: inode marked as free in block group. +; eax = error code. +;--------------------------------------------------------------------- +ext2_inode_free: + push edi ecx + + ; Inode table starts with 1. + dec eax + + mov edi, ext2_bg_read_inode_bitmap + xor ecx, ecx + inc cl + call ext2_resource_free + + pop ecx edi + ret + +;--------------------------------------------------------------------- +; Blanks a particular entry in an inode. +; Input: eax = index into block. +; edx = inode. +; ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_temp_inode] = the inode. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_blank_entry: + push ebx ecx edx edi esi + + mov edi, eax + + mov ecx, eax + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_get_block + test eax, eax + jnz .error + + test ecx, ecx + jz .allocate + + mov edx, ecx + mov ecx, [ebp + EXTFS.block_size] + mov edi, [ebp + EXTFS.ext2_temp_block] + xor eax, eax + rep stosb + + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .error + + jmp .success + + ; Need to allocate a block. + .allocate: + mov eax, edx + call ext2_block_calloc + test eax, eax + jnz .error + + mov ecx, edi + mov edi, ebx + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_set_block + test eax, eax + jnz .error + + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + mov eax, 2 + shl eax, cl + add [esi + EXT2_INODE_STRUC.i_blocks], eax + + .success: + xor eax, eax + + .ret: + pop esi edi edx ecx ebx + ret + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Frees a particular entry in an inode. +; Input: eax = index into block. +; ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_temp_inode] = the inode. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_free_entry: + push ebx ecx edi esi + + mov edi, eax + + mov ecx, eax + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_get_block + test eax, eax + jnz .error + + test ecx, ecx + jz .success + + mov eax, ecx + call ext2_block_free + test eax, eax + jnz .error + + mov ecx, edi + xor edi, edi + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_set_block + + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + mov eax, 2 + shl eax, cl + sub [esi + EXT2_INODE_STRUC.i_blocks], eax + + .success: + xor eax, eax + + .ret: + pop esi edi ecx ebx + ret + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Reads a particular entry from an inode. +; Input: eax = index into block. +; ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_temp_inode] = the inode. +; Output: eax = error code. +; [ebp + EXTFS.ext2_save_block] = the read block. +;--------------------------------------------------------------------- +ext2_inode_read_entry: + push ebx ecx edx esi + + mov ecx, eax + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_get_block + test eax, eax + jnz .error + + test ecx, ecx + jz .error + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .error + + .ret: + pop esi edx ecx ebx + ret + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Writes a particular entry from an inode. +; Input: eax = index into block. +; ebp = pointer to EXTFS. +; [ebp + EXTFS.ext2_temp_inode] = the inode. +; [ebp + EXTFS.ext2_save_block] = the block to write. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_write_entry: + push ebx ecx edx esi + + mov ecx, eax + mov esi, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_get_block + test eax, eax + jnz .error + + test ecx, ecx + jz .error + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_write + test eax, eax + jnz .error + + .ret: + pop esi edx ecx ebx + ret + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Extends inode to said size. +; Input: eax = inode ID. +; ecx = size to extend to. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_extend: + push ebx ecx edx esi edi + + ; Save the inode. + push eax + + ; Read the inode. + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + test eax, eax + jnz .error + + mov eax, [ebx + EXT2_INODE_STRUC.i_size] + cmp eax, ecx + jge .success + + ; Save the size of the inode. + push eax + + ; ECX contains the size we've to write. + sub ecx, eax + xor edx, edx + div [ebp + EXTFS.block_size] + + test edx, edx + jz .start_aligned + + ; Start isn't aligned, so deal with the non-aligned bytes. + mov esi, [ebp + EXTFS.block_size] + sub esi, edx + + cmp esi, ecx + jbe @F + + ; If the size to entend to fits in current block, limit to that. + mov esi, ecx + + @@: + ; Clear ESI bytes, in EAX indexed block. + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jnz .error_inode_size + + push eax ecx + + xor eax, eax + mov ecx, esi + mov edi, ebx + add edi, edx + + rep stosb + + pop ecx eax + + ; Write the block. + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + add [esp], esi + sub ecx, esi + jz .write_inode + + .start_aligned: + cmp ecx, [ebp + EXTFS.block_size] + jb @F + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + + mov edx, [esp + 4] + call ext2_inode_blank_entry + + test eax, eax + jnz .error_inode_size + + mov eax, [ebp + EXTFS.block_size] + sub ecx, eax + add [esp], eax + jmp .start_aligned + + ; Handle the remaining bytes. + @@: + test ecx, ecx + jz .write_inode + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + + mov edx, [esp + 4] + call ext2_inode_blank_entry + + test eax, eax + jnz .error_inode_size + add [esp], ecx + + .write_inode: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + call ext2_inode_write + + test eax, eax + jnz .error + + .success: + xor eax, eax + + .ret: + add esp, 4 + + pop edi esi edx ecx ebx + ret + + .error_inode_size: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + call ext2_inode_write + + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Truncates inode to said size. +; Input: eax = inode ID. +; ecx = size to truncate to. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_truncate: + push ebx ecx edx esi edi + + ; Save the inode. + push eax + + ; Read the inode. + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + test eax, eax + jnz .error + + mov eax, [ebx + EXT2_INODE_STRUC.i_size] + cmp ecx, eax + jge .success + + ; Save the size of the inode. + push eax + + ; ECX contains the size we've to truncate. + sub ecx, eax + not ecx + inc ecx + xor edx, edx + div [ebp + EXTFS.block_size] + + test edx, edx + jz .start_aligned + + ; Start isn't aligned, so deal with the non-aligned bytes. + mov esi, edx + + cmp esi, ecx + jbe @F + + ; If the size to truncate is smaller than the un-aligned bytes + ; we're going to have to mark neccessary bytes from the EOF + ; as 0. + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jnz .error_inode_size + + mov edi, [ebp + EXTFS.ext2_save_block] + sub edx, ecx + add edi, edx + + push ecx eax + xor eax, eax + rep stosb + pop eax ecx + + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + sub [esp], ecx + jmp .write_inode + + @@: + ; Since ECX is greater than or equal to the bytes here un-aligned + ; just free the block. + call ext2_inode_free_entry + + sub [esp], esi + sub ecx, esi + jz .write_inode + + .start_aligned: + cmp ecx, [ebp + EXTFS.block_size] + jb @F + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + dec eax + + call ext2_inode_free_entry + + test eax, eax + jnz .error_inode_size + + mov eax, [ebp + EXTFS.block_size] + sub ecx, eax + sub [esp], eax + jmp .start_aligned + + ; Handle the remaining bytes. + @@: + test ecx, ecx + jz .write_inode + + mov eax, [esp] + xor edx, edx + div [ebp + EXTFS.block_size] + dec eax + + push eax + call ext2_inode_read_entry + test eax, eax + pop eax + jnz .error_inode_size + + mov edi, [ebp + EXTFS.ext2_save_block] + mov edx, [ebp + EXTFS.block_size] + sub edx, ecx + add edi, edx + + push ecx eax + xor eax, eax + rep stosb + pop eax ecx + + call ext2_inode_write_entry + test eax, eax + jnz .error_inode_size + + sub [esp], ecx + + .write_inode: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + call ext2_inode_write + + test eax, eax + jnz .error + + .success: + xor eax, eax + + .ret: + add esp, 4 + + pop edi esi edx ecx ebx + ret + + .error_inode_size: + mov ebx, [ebp + EXTFS.ext2_temp_inode] + pop eax + mov [ebx + EXT2_INODE_STRUC.i_size], eax + mov eax, [esp] + call ext2_inode_write + + .error: + xor eax, eax + not eax + jmp .ret diff --git a/kernel/trunk/fs/ext2/resource.inc b/kernel/trunk/fs/ext2/resource.inc new file mode 100644 index 0000000000..41be87c26f --- /dev/null +++ b/kernel/trunk/fs/ext2/resource.inc @@ -0,0 +1,223 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains common resource allocation + freeing code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;--------------------------------------------------------------------- +; Frees a resource (block/inode). +; Input: eax = resource ID. +; edi = function pointer of ext2_bg_*_bitmap form, to +; get bitmap of resource. +; ecx = 0, block; 1, inode. +; ebp = pointer to EXTFS. +; Output: Block marked as free in block group. +; eax = error code. +;--------------------------------------------------------------------- +ext2_resource_free: + push ebx edx esi + + ; Get block group. + sub eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + xor edx, edx + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group] + push eax edx + + call edi + test eax, eax + jz .fail + mov esi, eax + + ; Read the bitmap. + mov eax, ebx + mov edx, eax + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .fail + + pop eax + ; Mark bit free. + call bitmap_clear_bit + test eax, eax + jz @F + + ; No need to save anything. + xor eax, eax + + add esp, 4 + jmp .return + + @@: + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_write + test eax, eax + jnz .fail + + ; Read the descriptor. + mov eax, [esp] + call ext2_bg_read_desc + test eax, eax + jz .fail_bg_desc_read + + lea eax, [eax + EXT2_BLOCK_GROUP_DESC.free_blocks_count] + shl ecx, 1 + add eax, ecx + inc word[eax] + + lea eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count] + shl ecx, 1 + add eax, ecx + inc dword[eax] + + pop eax + call ext2_bg_write_desc + + .return: + pop esi edx ebx + ret + + .fail: + add esp, 4 + .fail_bg_desc_read: + add esp, 4 + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Allocates a resource. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; [esp + 4], func pointer to ext2_bg_*_bitmap +; [esp + 8], pointer to free_*_count in SB. +; [esp + 12], *_per_group +; [esp + 16], offset to free_*_count in bg descriptor. +; [esp + 20], *_count +; Output: Resource marked as set in block group. +; eax = error code. +; ebx = resource ID. +;--------------------------------------------------------------------- +ext2_resource_alloc: + ; Block allocation is a pretty serious area, since bad allocation + ; can lead to fragmentation. Thus, the best way to allocate that + ; comes to mind is to allocate around an inode as much as possible. + ; On the other hand, this isn't about a single inode/file/directory, + ; and focusing just around the preferred inode would lead to + ; congestion. Thus, after much thought, the chosen allocation algorithm + ; is to search forward, then backward. + push ecx edx esi edi + + cmp dword[esp + 16 + 8], 0 + jnz @F + + ; No free blocks. + xor eax, eax + not eax + pop edi esi edx ecx + ret 20 + + @@: + ; Calculate which block group the preferred inode belongs to. + dec eax + xor edx, edx + + ; EAX = block group. + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + push eax + push eax + + mov edi, .forward + + .test_block_group: + call dword[esp + 16 + 8 + 4] + test eax, eax + jz .fail + mov esi, eax + + mov eax, ebx + mov edx, eax + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .fail + + mov ecx, [esp + 16 + 8 + 12] + call ext2_find_free_bit + cmp eax, 0xFFFFFFFF + jne @F + + mov eax, edi + jmp eax + + @@: + mov ecx, eax + + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_write + test eax, eax + jnz .fail + + ; ecx: the index of the matched entry. + ; [esp]: block group where we found. + ; [esp + 4]: starting block group. + ; esi: block group descriptor. + mov eax, [esp] ; Index of block group in which we found. + mul dword[esp + 16 + 8 + 12] + add eax, ecx + mov ebx, eax + + mov eax, [esp + 16 + 8 + 8] + dec dword[eax] + + mov eax, esi + add eax, [esp + 16 + 8 + 16] + dec word[eax] + + pop eax + call ext2_bg_write_desc + + add esp, 4 + jmp .return + + ; Continue forward. + .forward: + inc dword[esp] + mov eax, [esp] + mul dword[esp + 16 + 8 + 12] + cmp eax, [esp + 16 + 8 + 20] + jbe @F + + ; We need to go backward. + mov eax, [esp + 4] + mov [esp], eax + mov edi, .backward + jmp .backward + + @@: + mov eax, [esp] + jmp .test_block_group + + ; Continue backward. + .backward: + cmp dword[esp], 0 + je .fail + + dec dword[esp] + mov eax, [esp] + jmp .test_block_group + + .return: + pop edi esi edx ecx + ret 20 + + .fail: + add esp, 8 + xor eax, eax + not eax + jmp .return \ No newline at end of file