/kernel/trunk/fs/ext2.inc |
---|
File deleted |
Property changes: |
Deleted: svn:eol-style |
-native |
\ No newline at end of property |
Deleted: svn:keywords |
-Rev |
\ No newline at end of property |
/kernel/trunk/fs/ext2/blocks.asm |
---|
0,0 → 1,408 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; 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 |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/fs/ext2/ext2.asm |
---|
0,0 → 1,1221 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Contains ext2 initialization, plus syscall handling code. ;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under the terms of the new BSD license. ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
include 'ext2.inc' |
include 'blocks.asm' |
include 'inode.asm' |
include 'resource.asm' |
iglobal |
align 4 |
ext2_user_functions: |
dd ext2_free |
dd (ext2_user_functions_end - ext2_user_functions - 4) / 4 |
dd ext2_Read |
dd ext2_ReadFolder |
dd ext2_Rewrite |
dd ext2_Write |
dd ext2_SetFileEnd |
dd ext2_GetFileInfo |
dd ext2_SetFileInfo |
dd 0 |
dd ext2_Delete |
dd ext2_CreateFolder |
ext2_user_functions_end: |
endg |
;--------------------------------------------------------------------- |
; Locks up an ext2 partition. |
; Input: ebp = pointer to EXTFS. |
;--------------------------------------------------------------------- |
proc ext2_lock |
lea ecx, [ebp + EXTFS.lock] |
jmp mutex_lock |
endp |
;--------------------------------------------------------------------- |
; Unlocks up an ext2 partition. |
; Input: ebp = pointer to EXTFS. |
;--------------------------------------------------------------------- |
proc ext2_unlock |
lea ecx, [ebp + EXTFS.lock] |
jmp mutex_unlock |
endp |
;--------------------------------------------------------------------- |
; Check if it's a valid ext* superblock. |
; Input: ebp: first three fields of PARTITION structure. |
; ebx + 512: points to 512-bytes buffer that can be used for anything. |
; Output: eax: clear if can't create partition; set to EXTFS otherwise. |
;--------------------------------------------------------------------- |
proc ext2_create_partition |
push ebx |
mov eax, 2 ; Superblock starts at 1024-bytes. |
add ebx, 512 ; Get pointer to fs-specific buffer. |
call fs_read32_sys |
test eax, eax |
jnz .fail |
; Allowed 1KiB, 2KiB, 4KiB, 8KiB. |
cmp [ebx + EXT2_SB_STRUC.log_block_size], 3 |
ja .fail |
cmp [ebx + EXT2_SB_STRUC.magic], EXT2_SUPER_MAGIC |
jne .fail |
cmp [ebx + EXT2_SB_STRUC.state], EXT2_VALID_FS |
jne .fail |
; Can't have no inodes per group. |
cmp [ebx + EXT2_SB_STRUC.inodes_per_group], 0 |
je .fail |
; If incompatible features required, unusable superblock. |
mov eax, [ebx + EXT2_SB_STRUC.feature_incompat] |
test eax, not EXT4_FEATURE_INCOMPAT_SUPP |
jz .setup |
.fail: |
; Not a (valid/usable) EXT2 superblock. |
pop ebx |
xor eax, eax |
ret |
.setup: |
movi eax, sizeof.EXTFS |
call malloc |
test eax, eax |
jz ext2_create_partition.fail |
; Store the first sector field. |
mov ecx, dword[ebp + PARTITION.FirstSector] |
mov dword[eax + EXTFS.FirstSector], ecx |
mov ecx, dword [ebp + PARTITION.FirstSector+4] |
mov dword [eax + EXTFS.FirstSector+4], ecx |
; The length field. |
mov ecx, dword[ebp + PARTITION.Length] |
mov dword[eax + EXTFS.Length], ecx |
mov ecx, dword[ebp + PARTITION.Length+4] |
mov dword[eax + EXTFS.Length+4], ecx |
; The disk field. |
mov ecx, [ebp + PARTITION.Disk] |
mov [eax + EXTFS.Disk], ecx |
mov [eax + EXTFS.FSUserFunctions], ext2_user_functions |
push ebp esi edi |
mov ebp, eax |
lea ecx, [eax + EXTFS.lock] |
call mutex_init |
; Copy superblock from buffer to reserved memory. |
mov esi, ebx |
lea edi, [ebp + EXTFS.superblock] |
mov ecx, 512/4 |
rep movsd |
; Get total groups. |
mov eax, [ebx + EXT2_SB_STRUC.blocks_count] |
sub eax, [ebx + EXT2_SB_STRUC.first_data_block] |
dec eax |
xor edx, edx |
div [ebx + EXT2_SB_STRUC.blocks_per_group] |
inc eax |
mov [ebp + EXTFS.groups_count], eax |
; Get log(block_size), such that 1,2,3,4 equ 1KiB,2KiB,4KiB,8KiB. |
mov ecx, [ebx + EXT2_SB_STRUC.log_block_size] |
inc ecx |
mov [ebp + EXTFS.log_block_size], ecx |
; 512-byte blocks in ext2 blocks. |
mov eax, 1 |
shl eax, cl |
mov [ebp + EXTFS.count_block_in_block], eax |
; Get block_size/4 (we'll find square later). |
shl eax, 7 |
mov [ebp + EXTFS.count_pointer_in_block], eax |
mov edx, eax |
; Get block size. |
shl eax, 2 |
mov [ebp + EXTFS.block_size], eax |
; Save block size for 2 kernel_alloc calls. |
push eax eax |
mov eax, edx |
mul edx |
mov [ebp + EXTFS.count_pointer_in_block_square], eax |
; Have temporary block storage for get_inode procedure, and one for global procedure. |
KERNEL_ALLOC [ebp + EXTFS.ext2_save_block], .error |
KERNEL_ALLOC [ebp + EXTFS.ext2_temp_block], .error |
mov [ebp + EXTFS.partition_flags], 0x00000000 |
mov eax, [ebx + EXT2_SB_STRUC.feature_ro_compat] |
and eax, not EXT2_FEATURE_RO_COMPAT_SUPP |
jnz .read_only |
mov eax, [ebx + EXT2_SB_STRUC.feature_incompat] |
and eax, EXT4_FEATURE_INCOMPAT_W_NOT_SUPP |
jz @F |
.read_only: |
; Mark as read-only. |
or [ebp + EXTFS.partition_flags], EXT2_RO |
@@: |
mov ecx, [ebx + EXT2_SB_STRUC.blocks_per_group] |
mov [ebp + EXTFS.blocks_per_group], ecx |
movzx ecx, word[ebx + EXT2_SB_STRUC.inode_size] |
mov [ebp + EXTFS.inode_size], ecx |
; Allocate for three inodes (loop would be overkill). |
push ecx ecx ecx |
KERNEL_ALLOC [ebp + EXTFS.ext2_save_inode], .error |
KERNEL_ALLOC [ebp + EXTFS.ext2_temp_inode], .error |
KERNEL_ALLOC [ebp + EXTFS.root_inode], .error |
; Read root inode. |
mov ebx, eax |
mov eax, EXT2_ROOT_INO |
call ext2_inode_read |
test eax, eax |
jnz .error |
mov eax, ebp ; Return pointer to EXTFS. |
pop edi esi ebp ebx |
ret |
; Error in setting up. |
.error: |
; Free save block. |
KERNEL_FREE [ebp + EXTFS.ext2_save_block], .fail |
; Temporary block. |
KERNEL_FREE [ebp + EXTFS.ext2_temp_block], .fail |
; All inodes. |
KERNEL_FREE [ebp + EXTFS.ext2_save_inode], .fail |
KERNEL_FREE [ebp + EXTFS.ext2_temp_inode], .fail |
KERNEL_FREE [ebp + EXTFS.root_inode], .fail |
mov eax, ebp |
call free |
jmp .fail |
endp |
; FUNCTIONS PROVIDED BY SYSCALLS. |
;--------------------------------------------------------------------- |
; Frees up all ext2 structures. |
; Input: eax = pointer to EXTFS. |
;--------------------------------------------------------------------- |
proc ext2_free |
push ebp |
xchg ebp, eax |
stdcall kernel_free, [ebp+EXTFS.ext2_save_block] |
stdcall kernel_free, [ebp+EXTFS.ext2_temp_block] |
stdcall kernel_free, [ebp+EXTFS.ext2_save_inode] |
stdcall kernel_free, [ebp+EXTFS.ext2_temp_inode] |
stdcall kernel_free, [ebp+EXTFS.root_inode] |
xchg ebp, eax |
call free |
pop ebp |
ret |
endp |
;--------------------------------------------------------------------- |
; Read disk folder. |
; Input: ebp = pointer to EXTFS structure. |
; esi + [esp + 4] = file name. |
; ebx = pointer to parameters from sysfunc 70. |
; Output: ebx = blocks read (or 0xFFFFFFFF, folder not found) |
; eax = error code (0 implies no error) |
;--------------------------------------------------------------------- |
ext2_ReadFolder: |
call ext2_lock |
cmp byte [esi], 0 |
jz .root_folder |
push ebx |
stdcall ext2_inode_find, [esp + 4 + 4] ; Get inode. |
pop ebx |
mov esi, [ebp + EXTFS.ext2_save_inode] |
test eax, eax |
jnz .error_ret |
; If not a directory, then return with error. |
test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR |
jz .error_not_found |
jmp @F |
.root_folder: |
mov esi, [ebp + EXTFS.root_inode] |
test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR |
jz .error_root |
; Copy the inode. |
mov edi, [ebp + EXTFS.ext2_save_inode] |
mov ecx, [ebp + EXTFS.inode_size] |
shr ecx, 2 |
push edi |
rep movsd |
pop esi |
@@: |
cmp [esi + EXT2_INODE_STRUC.i_size], 0 ; Folder is empty. |
je .error_empty_dir |
mov edx, [ebx + 16] |
push edx ; Result address [edi + 28]. |
push 0 ; End of the current block in folder [edi + 24] |
push dword[ebx + 12] ; Blocks to read [edi + 20] |
push dword[ebx + 4] ; The first wanted file [edi + 16] |
push dword[ebx + 8] ; Flags [edi + 12] |
push 0 ; Read files [edi + 8] |
push 0 ; Files in folder [edi + 4] |
push 0 ; Number of blocks read in dir (and current block index) [edi] |
; Fill header with zeroes. |
mov edi, edx |
mov ecx, 32/4 |
rep stosd |
mov edi, esp ; edi = pointer to local variables. |
add edx, 32 ; edx = mem to return. |
xor ecx, ecx ; Get number of first block. |
call ext2_get_inode_block |
test eax, eax |
jnz .error_get_block |
mov eax, ecx |
mov ebx, [ebp + EXTFS.ext2_save_block] |
call ext2_block_read ; Read the block. |
test eax, eax |
jnz .error_get_block |
mov eax, ebx ; esi: current directory record |
add eax, [ebp + EXTFS.block_size] |
mov [edi + 24], eax |
mov ecx, [edi + 16] ; ecx = first wanted (flags ommited) |
.find_wanted_start: |
jecxz .find_wanted_end |
.find_wanted_cycle: |
cmp [ebx + EXT2_DIR_STRUC.inode], 0 ; Don't count unused inode in total files. |
jz @F |
inc dword [edi + 4] ; EXT2 files in folder. |
dec ecx |
@@: |
movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] |
cmp eax, 12 ; Minimum record length. |
jb .error_bad_len |
test eax, 0x3 ; Record length must be divisible by four. |
jnz .error_bad_len |
sub [esi + EXT2_INODE_STRUC.i_size], eax ; Subtract "processed record" length directly from inode. |
add ebx, eax ; Go to next record. |
cmp ebx, [edi + 24] ; If not reached the next block, continue. |
jb .find_wanted_start |
push .find_wanted_start |
.end_block: ; Get the next block. |
cmp [esi + EXT2_INODE_STRUC.i_size], 0 |
jle .end_dir |
inc dword [edi] ; Number of blocks read. |
; Read the next block. |
push ecx |
mov ecx, [edi] |
call ext2_get_inode_block |
test eax, eax |
jnz .error_get_block |
mov eax, ecx |
mov ebx, [ebp + EXTFS.ext2_save_block] |
call ext2_block_read |
test eax, eax |
jnz .error_get_block |
pop ecx |
mov eax, ebx |
add eax, [ebp + EXTFS.block_size] |
mov [edi + 24], eax ; Update the end of the current block variable. |
ret |
.wanted_end: |
loop .find_wanted_cycle ; Skip files till we reach wanted one. |
; First requisite file. |
.find_wanted_end: |
mov ecx, [edi + 20] |
.wanted_start: ; Look for first_wanted + count. |
jecxz .wanted_end |
cmp [ebx + EXT2_DIR_STRUC.inode], 0 ; if (inode == 0): not used; |
jz .empty_rec |
; Increment "files in dir" and "read files" count. |
inc dword [edi + 8] |
inc dword [edi + 4] |
push edi ecx |
mov edi, edx ; Zero out till the name field. |
xor eax, eax |
mov ecx, 40 / 4 |
rep stosd |
pop ecx edi |
push ebx edi edx |
mov eax, [ebx + EXT2_DIR_STRUC.inode] ; Get the child inode. |
mov ebx, [ebp + EXTFS.ext2_temp_inode] |
call ext2_inode_read |
test eax, eax |
jnz .error_read_subinode |
lea edi, [edx + 8] |
mov eax, [ebx + EXT2_INODE_STRUC.i_ctime] ; Convert time in NTFS format. |
xor edx, edx |
add eax, 3054539008 ; (369 * 365 + 89) * 24 * 3600 |
adc edx, 2 |
call ntfs_datetime_to_bdfe.sec |
mov eax, [ebx + EXT2_INODE_STRUC.i_atime] |
xor edx, edx |
add eax, 3054539008 |
adc edx, 2 |
call ntfs_datetime_to_bdfe.sec |
mov eax, [ebx + EXT2_INODE_STRUC.i_mtime] |
xor edx, edx |
add eax, 3054539008 |
adc edx, 2 |
call ntfs_datetime_to_bdfe.sec |
pop edx |
test [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR ; If folder, don't report size. |
jnz @F |
mov eax, [ebx + EXT2_INODE_STRUC.i_size] ; Low size |
stosd |
mov eax, [ebx + EXT2_INODE_STRUC.i_dir_acl] ; High size |
stosd |
xor dword [edx], FS_FT_DIR ; Mark as file. |
@@: |
xor dword [edx], FS_FT_DIR ; Mark as directory. |
; Copy name after converting from UTF-8 to CP866. |
push ecx esi |
mov esi, [esp + 12] |
movzx ecx, [esi + EXT2_DIR_STRUC.name_len] |
lea edi, [edx + 40] |
lea esi, [esi + EXT2_DIR_STRUC.name] |
call utf8_to_cp866 |
and byte [edi], 0 |
pop esi ecx edi ebx |
cmp byte [edx + 40], '.' ; If it begins with ".", mark it as hidden. |
jne @F |
or dword [edx], FS_FT_HIDDEN |
@@: |
add edx, 40 + 264 ; Go to next record. |
dec ecx |
.empty_rec: |
movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] |
cmp eax, 12 ; Illegal length. |
jb .error_bad_len |
test eax, 0x3 ; Not a multiple of four. |
jnz .error_bad_len |
sub [esi + EXT2_INODE_STRUC.i_size], eax ; Subtract directly from the inode. |
add ebx, eax |
cmp ebx, [edi + 24] ; Are we at the end of the block? |
jb .wanted_start |
push .wanted_start |
jmp .end_block |
.end_dir: ; End of the directory. |
call ext2_unlock |
mov edx, [edi + 28] ; Address of where to return data. |
mov ebx, [edi + 8] ; EXT2_read_in_folder |
mov ecx, [edi + 4] ; EXT2_files_in_folder |
mov dword [edx], 1 ; Version |
mov [edx + 4], ebx |
mov [edx + 8], ecx |
lea esp, [edi + 32] |
xor eax, eax ; Reserved in current implementation. |
lea edi, [edx + 12] |
mov ecx, 20 / 4 |
rep stosd |
ret |
.error_bad_len: |
mov eax, ERROR_FS_FAIL |
.error_read_subinode: |
.error_get_block: |
; Fix the stack. |
lea esp, [edi + 32] |
.error_ret: |
or ebx, -1 |
push eax |
call ext2_unlock |
pop eax |
ret |
.error_empty_dir: ; inode of folder without blocks. |
.error_root: ; Root has to be a folder. |
mov eax, ERROR_FS_FAIL |
jmp .error_ret |
.error_not_found: ; Directory not found. |
mov eax, ERROR_FILE_NOT_FOUND |
jmp .error_ret |
;--------------------------------------------------------------------- |
; Read file from the hard disk. |
; Input: esi + [esp + 4] = points to file name. |
; ebx = pointer to paramteres from sysfunc 70. |
; ebp = pointer to EXTFS structure. |
; Output: ebx = bytes read (0xFFFFFFFF -> file not found) |
; eax = error code (0 implies no error) |
;--------------------------------------------------------------------- |
ext2_Read: |
call ext2_lock |
cmp byte [esi], 0 |
jnz @F |
.this_is_nofile: |
call ext2_unlock |
or ebx, -1 |
mov eax, ERROR_ACCESS_DENIED |
ret |
@@: |
push ebx |
stdcall ext2_inode_find, [esp + 4 + 4] |
pop ebx |
mov esi, [ebp + EXTFS.ext2_save_inode] |
test eax, eax |
jz @F |
call ext2_unlock |
or ebx, -1 |
mov eax, ERROR_FILE_NOT_FOUND |
ret |
@@: |
mov ax, [esi + EXT2_INODE_STRUC.i_mode] |
and ax, EXT2_S_IFMT ; Leave the file format in AX. |
; Check if file. |
cmp ax, EXT2_S_IFREG |
jne .this_is_nofile |
mov edi, [ebx + 16] |
mov ecx, [ebx + 12] |
mov eax, [ebx + 4] |
mov edx, [ebx + 8] ; edx:eax = start byte number. |
; Check if file is big enough for us. |
cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx |
ja .size_greater |
jb .size_less |
cmp [esi + EXT2_INODE_STRUC.i_size], eax |
ja .size_greater |
.size_less: |
call ext2_unlock |
xor ebx, ebx |
mov eax, ERROR_END_OF_FILE |
ret |
@@: |
.size_greater: |
add eax, ecx ; Get last byte. |
adc edx, 0 |
; Check if we've to read whole file, or till requested. |
cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx |
ja .read_till_requested |
jb .read_whole_file |
cmp [esi + EXT2_INODE_STRUC.i_size], eax |
jae .read_till_requested |
.read_whole_file: |
push 1 ; Read till the end of file. |
mov ecx, [esi + EXT2_INODE_STRUC.i_size] |
sub ecx, [ebx + 4] ; To read = (size - starting byte) |
jmp @F |
.read_till_requested: |
push 0 ; Read as much as requested. |
@@: |
; ecx = bytes to read. |
; edi = return memory |
; [esi] = starting byte. |
push ecx ; Number of bytes to read. |
; Get part of the first block. |
mov edx, [ebx + 8] |
mov eax, [ebx + 4] |
div [ebp + EXTFS.block_size] |
push eax ; Save block counter to stack. |
push ecx |
mov ecx, eax |
call ext2_get_inode_block |
test eax, eax |
jnz .error_at_first_block |
mov ebx, [ebp + EXTFS.ext2_save_block] |
mov eax, ecx |
call ext2_block_read |
test eax, eax |
jnz .error_at_first_block |
pop ecx |
; Get index inside block. |
add ebx, edx |
neg edx |
add edx, [ebp + EXTFS.block_size] ; Get number of bytes in this block. |
; If it's smaller than total bytes to read, then only one block. |
cmp ecx, edx |
jbe .only_one_block |
mov eax, ecx |
sub eax, edx |
mov ecx, edx |
push esi |
mov esi, ebx |
rep movsb ; Copy part of 1st block. |
pop esi |
; eax -> bytes to read. |
.calc_blocks_count: |
mov ebx, edi ; Read the block in ebx. |
xor edx, edx |
div [ebp + EXTFS.block_size] ; Get number of bytes in last block in edx. |
mov edi, eax ; Get number of blocks in edi. |
@@: |
; Test if all blocks are done. |
test edi, edi |
jz .finish_block |
inc dword [esp] |
mov ecx, [esp] |
call ext2_get_inode_block |
test eax, eax |
jnz .error_at_read_cycle |
mov eax, ecx ; ebx already contains desired values. |
call ext2_block_read |
test eax, eax |
jnz .error_at_read_cycle |
add ebx, [ebp + EXTFS.block_size] |
dec edi |
jmp @B |
; In edx -- number of bytes in the last block. |
.finish_block: |
test edx, edx |
jz .end_read |
pop ecx ; Pop block counter in ECX. |
inc ecx |
call ext2_get_inode_block |
test eax, eax |
jnz .error_at_finish_block |
mov edi, ebx |
mov eax, ecx |
mov ebx, [ebp + EXTFS.ext2_save_block] |
call ext2_block_read |
test eax, eax |
jnz .error_at_finish_block |
mov ecx, edx |
mov esi, ebx |
rep movsb ; Copy last piece of block. |
jmp @F |
.end_read: |
pop ecx ; Pop block counter in ECX. |
@@: |
pop ebx ; Number of bytes read. |
call ext2_unlock |
pop eax ; If we were asked to read more, say EOF. |
test eax, eax |
jz @F |
mov eax, ERROR_END_OF_FILE |
ret |
@@: |
xor eax, eax |
ret |
.only_one_block: |
mov esi, ebx |
rep movsb ; Copy last piece of block. |
jmp .end_read |
.error_at_first_block: |
pop edx |
.error_at_read_cycle: |
pop ebx |
.error_at_finish_block: |
pop ecx edx |
or ebx, -1 |
push eax |
call ext2_unlock |
pop eax |
ret |
;--------------------------------------------------------------------- |
; Read file information from block device. |
; Input: esi + [esp + 4] = file name. |
; ebx = pointer to paramteres from sysfunc 70. |
; ebp = pointer to EXTFS structure. |
; Output: eax = error code. |
;--------------------------------------------------------------------- |
ext2_GetFileInfo: |
call ext2_lock |
mov edx, [ebx + 16] |
cmp byte [esi], 0 |
jz .is_root |
push edx |
stdcall ext2_inode_find, [esp + 4 + 4] |
mov ebx, edx |
pop edx |
mov esi, [ebp + EXTFS.ext2_save_inode] |
test eax, eax |
jz @F |
push eax |
call ext2_unlock |
pop eax |
ret |
.is_root: |
xor ebx, ebx ; Clear out first char, since we don't want to set hidden flag on root. |
mov esi, [ebp + EXTFS.root_inode] |
@@: |
xor eax, eax |
mov edi, edx |
mov ecx, 40/4 |
rep stosd ; Zero fill buffer. |
cmp bl, '.' |
jne @F |
or dword [edx], FS_FT_HIDDEN |
@@: |
test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR |
jnz @F ; If a directory, don't put in file size. |
mov eax, [esi + EXT2_INODE_STRUC.i_size] ; Low file size. |
mov ebx, [esi + EXT2_INODE_STRUC.i_dir_acl] ; High file size. |
mov dword [edx+32], eax |
mov dword [edx+36], ebx |
xor dword [edx], FS_FT_DIR ; Next XOR will clean this, to mark it as a file. |
@@: |
xor dword [edx], FS_FT_DIR ; Mark as directory. |
lea edi, [edx + 8] |
; Store all time. |
mov eax, [esi + EXT2_INODE_STRUC.i_ctime] |
xor edx, edx |
add eax, 3054539008 |
adc edx, 2 |
call ntfs_datetime_to_bdfe.sec |
mov eax, [esi + EXT2_INODE_STRUC.i_atime] |
xor edx, edx |
add eax, 3054539008 |
adc edx, 2 |
call ntfs_datetime_to_bdfe.sec |
mov eax, [esi + EXT2_INODE_STRUC.i_mtime] |
xor edx, edx |
add eax, 3054539008 |
adc edx, 2 |
call ntfs_datetime_to_bdfe.sec |
call ext2_unlock |
xor eax, eax |
ret |
;--------------------------------------------------------------------- |
; Set file information for block device. |
; Input: esi + [esp + 4] = file name. |
; ebx = pointer to paramteres from sysfunc 70. |
; ebp = pointer to EXTFS structure. |
; Output: eax = error code. |
;--------------------------------------------------------------------- |
ext2_SetFileInfo: |
push edx esi edi ebx |
call ext2_lock |
mov edx, [ebx + 16] |
; Is this read-only? |
test [ebp + EXTFS.partition_flags], EXT2_RO |
jnz .fail |
; Not supported for root. |
cmp byte [esi], 0 |
je .fail |
.get_inode: |
push edx |
stdcall ext2_inode_find, [esp + 4 + 20] |
pop edx |
test eax, eax |
jnz @F |
; Save inode number. |
push esi |
mov esi, [ebp + EXTFS.ext2_save_inode] |
; From the BDFE, we ignore read-only file flags, hidden file flags; |
; We ignore system file flags, file was archived or not. |
; Also ignored is file creation time. ext2 stores "inode modification" |
; time in the ctime field, which is updated by the respective inode_write |
; procedure, and any writes on it would be overwritten anyway. |
; Access time. |
lea edi, [esi + EXT2_INODE_STRUC.i_atime] |
lea esi, [edx + 16] |
call bdfe_to_unix_time |
; Modification time. |
add esi, 8 |
add edi, 8 |
call bdfe_to_unix_time |
mov ebx, [ebp + EXTFS.ext2_save_inode] ; Get address of inode into ebx. |
pop eax ; Get inode number in eax. |
call ext2_inode_write ; eax contains error code, if any. |
test eax, eax |
jnz @F |
call ext2_sb_update |
; Sync the disk. |
mov esi, [ebp + PARTITION.Disk] |
call disk_sync ; eax contains error code, if any. |
@@: |
push eax |
call ext2_unlock |
pop eax |
pop ebx edi esi edx |
ret |
.fail: |
call ext2_sb_update |
; Sync the disk. |
mov esi, [ebp + PARTITION.Disk] |
call disk_sync ; eax contains error code, if any. |
mov eax, ERROR_UNSUPPORTED_FS |
jmp @B |
;--------------------------------------------------------------------- |
; Set file information for block device. |
; Input: esi + [esp + 4] = file name. |
; ebx = pointer to paramteres from sysfunc 70. |
; ebp = pointer to EXTFS structure. |
; Output: eax = error code. |
;--------------------------------------------------------------------- |
ext2_Delete: |
push ebx ecx edx esi edi |
call ext2_lock |
add esi, [esp + 20 + 4] |
; Can't delete root. |
cmp byte [esi], 0 |
jz .error_access_denied |
push esi |
stdcall ext2_inode_find, 0 |
mov ebx, esi |
pop esi |
test eax, eax |
jnz .error_access_denied |
mov edx, [ebp + EXTFS.ext2_save_inode] |
movzx edx, [edx + EXT2_INODE_STRUC.i_mode] |
and edx, EXT2_S_IFMT ; Get the mask. |
cmp edx, EXT2_S_IFDIR |
jne @F ; If not a directory, we don't need to check if it's empty. |
call ext2_dir_empty ; 0 means directory is empty. |
test eax, eax |
jnz .error_access_denied |
@@: |
; Find parent. |
call ext2_inode_find_parent |
test eax, eax |
jnz .error_access_denied |
mov eax, esi |
; Save file/dir & parent inode. |
push ebx eax |
cmp edx, EXT2_S_IFDIR |
jne @F |
; Unlink '.' |
mov eax, [esp + 4] |
call ext2_inode_unlink |
cmp eax, 0xFFFFFFFF |
je .error_stack8 |
; Unlink '..' |
mov eax, [esp + 4] |
mov ebx, [esp] |
call ext2_inode_unlink |
cmp eax, 0xFFFFFFFF |
je .error_stack8 |
@@: |
pop eax |
mov ebx, [esp] |
; Unlink the inode. |
call ext2_inode_unlink |
cmp eax, 0xFFFFFFFF |
je .error_stack4 |
; If hardlinks aren't zero, shouldn't completely free. |
test eax, eax |
jz @F |
add esp, 4 |
jmp .disk_sync |
@@: |
; Read the inode. |
mov eax, [esp] |
mov ebx, [ebp + EXTFS.ext2_save_inode] |
call ext2_inode_read |
test eax, eax |
jnz .error_stack4 |
; Free inode data. |
mov esi, [ebp + EXTFS.ext2_save_inode] |
xor ecx, ecx |
@@: |
push ecx |
call ext2_get_inode_block |
test eax, eax |
jnz .error_stack8 |
mov eax, ecx |
pop ecx |
; If 0, we're done. |
test eax, eax |
jz @F |
call ext2_block_free |
test eax, eax |
jnz .error_stack4 |
inc ecx |
jmp @B |
@@: |
; Clear the inode, and add deletion time. |
mov edi, [ebp + EXTFS.ext2_save_inode] |
xor eax, eax |
mov ecx, [ebp + EXTFS.inode_size] |
rep stosb |
mov edi, [ebp + EXTFS.ext2_save_inode] |
add edi, EXT2_INODE_STRUC.i_dtime |
call current_unix_time |
; Write the inode. |
mov eax, [esp] |
mov ebx, [ebp + EXTFS.ext2_save_inode] |
call ext2_inode_write |
test eax, eax |
jnz .error_stack4 |
; Check if directory. |
cmp edx, EXT2_S_IFDIR |
jne @F |
; If it is, decrement used_dirs_count. |
; Get block group. |
mov eax, [esp] |
dec eax |
xor edx, edx |
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] |
push eax |
call ext2_bg_read_desc |
test eax, eax |
jz .error_stack8 |
dec [eax + EXT2_BLOCK_GROUP_DESC.used_dirs_count] |
pop eax |
call ext2_bg_write_desc |
@@: |
pop eax |
call ext2_inode_free |
test eax, eax |
jnz .error_access_denied |
.disk_sync: |
call ext2_sb_update |
; Sync the disk. |
mov esi, [ebp + PARTITION.Disk] |
call disk_sync ; eax contains error code, if any. |
.return: |
push eax |
call ext2_unlock |
pop eax |
pop edi esi edx ecx ebx |
ret |
.error_stack8: |
add esp, 4 |
.error_stack4: |
add esp, 4 |
.error_access_denied: |
call ext2_sb_update |
; Sync the disk. |
mov esi, [ebp + PARTITION.Disk] |
call disk_sync ; eax contains error code, if any. |
mov eax, ERROR_ACCESS_DENIED |
jmp .return |
;--------------------------------------------------------------------- |
; Set file information for block device. |
; Input: esi + [esp + 4] = file name. |
; ebx = pointer to paramteres from sysfunc 70. |
; ebp = pointer to EXTFS structure. |
; Output: eax = error code. |
;--------------------------------------------------------------------- |
ext2_CreateFolder: |
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. |
cmp byte [esi], 0 |
jz .success |
push esi |
stdcall ext2_inode_find, 0 |
pop esi |
; If the directory is there, we've succeeded. |
test eax, eax |
jz .success |
; Find parent. |
call ext2_inode_find_parent |
test eax, eax |
jnz .error |
; Inode ID for preference. |
mov eax, esi |
call ext2_inode_alloc |
test eax, eax |
jnz .error_full |
; Save allocated inode in EDX; filename is in EDI; parent ID in ESI. |
mov edx, ebx |
push edi |
xor al, al |
mov edi, [ebp + EXTFS.ext2_temp_inode] |
mov ecx, [ebp + EXTFS.inode_size] |
rep stosb |
mov edi, [ebp + EXTFS.ext2_temp_inode] |
add edi, EXT2_INODE_STRUC.i_atime |
call current_unix_time |
add edi, 8 |
call current_unix_time |
pop edi |
mov ebx, [ebp + EXTFS.ext2_temp_inode] |
mov [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR |
mov eax, edx |
call ext2_inode_write |
test eax, eax |
jnz .error |
; Link to self. |
push edx esi |
mov eax, edx |
mov ebx, eax |
mov dl, EXT2_FT_DIR |
mov esi, self_link |
call ext2_inode_link |
pop esi edx |
test eax, eax |
jnz .error |
; Link to parent. |
push edx esi |
mov eax, ebx |
mov ebx, esi |
mov dl, EXT2_FT_DIR |
mov esi, parent_link |
call ext2_inode_link |
pop esi edx |
test eax, eax |
jnz .error |
; Link parent to child. |
mov eax, esi |
mov ebx, edx |
mov esi, edi |
mov dl, EXT2_FT_DIR |
call ext2_inode_link |
test eax, eax |
jnz .error |
; Get block group descriptor for allocated inode's block. |
mov eax, ebx |
dec eax |
xor edx, edx |
; EAX = block group. |
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] |
mov edx, eax |
call ext2_bg_read_desc |
test eax, eax |
jz .error |
inc [eax + EXT2_BLOCK_GROUP_DESC.used_dirs_count] |
mov eax, edx |
call ext2_bg_write_desc |
test eax, eax |
jnz .error |
.success: |
call ext2_sb_update |
; Sync the disk. |
mov esi, [ebp + PARTITION.Disk] |
call disk_sync ; eax contains error code, if any. |
.return: |
push eax |
call ext2_unlock |
pop eax |
pop edi esi edx ecx ebx |
ret |
.error: |
call ext2_sb_update |
; Sync the disk. |
mov esi, [ebp + PARTITION.Disk] |
call disk_sync ; eax contains error code, if any. |
mov eax, ERROR_ACCESS_DENIED |
jmp .return |
.error_full: |
mov eax, ERROR_DISK_FULL |
jmp .return |
self_link: db ".", 0 |
parent_link: db "..", 0 |
ext2_Rewrite: |
ext2_Write: |
ext2_SetFileEnd: |
xor ebx, ebx |
mov eax, ERROR_UNSUPPORTED_FS |
ret |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/fs/ext2/ext2.inc |
---|
0,0 → 1,648 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Contains ext2 structures, and macros. ;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under the terms of the new BSD license. ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; Recommended move to some kernel-wide bitmap handling code (with a bit of abstraction, of course). |
;--------------------------------------------------------------------- |
; Clears a bit. |
; Input: eax = index into bitmap. |
; [EXTFS.ext2_save_block] = address of bitmap. |
; ebp = address of EXTFS. |
; Output: Bit cleared. |
; eax = non-zero, if already cleared. |
;--------------------------------------------------------------------- |
bitmap_clear_bit: |
push ebx ecx edx |
xor edx, edx |
mov ecx, 8 |
div ecx |
add eax, [ebp + EXTFS.ext2_save_block] |
; Get the mask. |
mov ebx, 1 |
mov ecx, edx |
shl ebx, cl |
test [eax], ebx |
jz .cleared |
not ebx |
and [eax], ebx |
xor eax, eax |
.return: |
pop edx ecx ebx |
ret |
; Already cleared. |
.cleared: |
xor eax, eax |
not eax |
jmp .return |
;--------------------------------------------------------------------- |
; Finds free bit in the bitmap. |
; Input: ecx = number of bits in the bitmap. |
; [EXTFS.ext2_save_block] = address of bitmap. |
; ebp = address of EXTFS. |
; Output: eax = index of free bit in the bitmap; marked set. |
; 0xFFFFFFFF if no free bit found. |
;--------------------------------------------------------------------- |
ext2_find_free_bit: |
bitmap_find_free_bit: |
push esi ebx ecx edx |
mov esi, [ebp + EXTFS.ext2_save_block] |
; Get total DWORDS in eax; total bits in last dword, if any, in edx. |
xor edx, edx |
mov eax, ecx |
mov ecx, 32 |
div ecx |
mov ecx, eax |
xor eax, eax |
push edx |
test ecx, ecx |
jz .last_bits |
; Check in the DWORDS. |
.dwords: |
mov ebx, [esi] |
not ebx |
bsf edx, ebx |
; If 0, then the original value would be 0xFFFFFFFF, hence no free bits. |
jz @F |
; We found the value. Let's return with it. |
add esp, 4 |
add eax, edx |
jmp .return |
@@: |
add esi, 4 |
add eax, 32 |
loop .dwords |
.last_bits: |
; Check in the last few bits. |
pop ecx |
test ecx, ecx |
jz @F |
mov ebx, [esi] |
not ebx |
bsf ebx, edx |
; If 0, no free bits. |
jz @F |
; If free bit is greater than the last known bit, then error. |
cmp edx, ecx |
jg @F |
add eax, edx |
jmp .return |
@@: |
; Didn't find any free bits. |
xor eax, eax |
not eax |
jmp @F |
.return: |
mov ecx, edx |
mov edx, 1 |
shl edx, cl |
or [esi], edx |
@@: |
pop edx ecx ebx esi |
ret |
; Recommended move to some kernel-wide string handling code. |
;--------------------------------------------------------------------- |
; Find the length of a string. |
; Input: esi = source. |
; Output: length in ecx |
;--------------------------------------------------------------------- |
strlen: |
push eax esi |
xor ecx, ecx |
@@: |
lodsb |
test al, al |
jz .ret |
inc ecx |
jmp @B |
.ret: |
pop esi eax |
ret |
;--------------------------------------------------------------------- |
; Convert UTF-8 string to ASCII-string (codepage 866) |
; Input: esi = source. |
; edi = buffer. |
; ecx = length of source. |
; Output: destroys eax, esi, edi |
;--------------------------------------------------------------------- |
utf8_to_cp866: |
; Check for zero-length string. |
jecxz .return |
.start: |
lodsw |
cmp al, 0x80 |
jb .ascii |
xchg al, ah ; Big-endian. |
cmp ax, 0xd080 |
jz .yo1 |
cmp ax, 0xd191 |
jz .yo2 |
cmp ax, 0xd090 |
jb .unk |
cmp ax, 0xd180 |
jb .rus1 |
cmp ax, 0xd190 |
jb .rus2 |
.unk: |
mov al, '_' |
jmp .doit |
.yo1: |
mov al, 0xf0 ; Ё capital. |
jmp .doit |
.yo2: |
mov al, 0xf1 ; ё small. |
jmp .doit |
.rus1: |
sub ax, 0xd090 - 0x80 |
jmp .doit |
.rus2: |
sub ax, 0xd18f - 0xEF |
.doit: |
stosb |
sub ecx, 2 |
ja .start |
ret |
.ascii: |
stosb |
dec esi |
dec ecx |
jnz .start |
.return: |
ret |
; Recommended move to some kernel-wide time handling code. |
; Total cumulative seconds till each month. |
cumulative_seconds_in_month: |
.january: dd 0 * (60 * 60 * 24) |
.february: dd 31 * (60 * 60 * 24) |
.march: dd 59 * (60 * 60 * 24) |
.april: dd 90 * (60 * 60 * 24) |
.may: dd 120 * (60 * 60 * 24) |
.june: dd 151 * (60 * 60 * 24) |
.july: dd 181 * (60 * 60 * 24) |
.august: dd 212 * (60 * 60 * 24) |
.september: dd 243 * (60 * 60 * 24) |
.october: dd 273 * (60 * 60 * 24) |
.november: dd 304 * (60 * 60 * 24) |
.december: dd 334 * (60 * 60 * 24) |
current_bdfe_time: |
dd 0 |
current_bdfe_date: |
dd 0 |
;--------------------------------------------------------------------- |
; Stores current unix time. |
; Input: edi = buffer to output Unix time. |
;--------------------------------------------------------------------- |
current_unix_time: |
push eax esi |
mov esi, current_bdfe_time |
; Just a small observation: |
; The CMOS is a pretty bad source to get time from. One shouldn't rely on it, |
; since it messes up the time by tiny bits. Of course, this is all technical, |
; but one can look it up on the osdev wiki. What is better is to get the time |
; from CMOS during boot, then update system time using a more accurate timer. |
; I'll probably add that after the Summer of Code, so TODO! TODO! TODO!. |
; Get time from CMOS. |
; Seconds. |
mov al, 0x00 |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
mov [esi + 0], al |
; Minute. |
mov al, 0x02 |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
mov [esi + 1], al |
; Hour. |
mov al, 0x04 |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
mov [esi + 2], al |
; Get date. |
; Day. |
mov al, 0x7 |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
mov [esi + 4], al |
; Month. |
mov al, 0x8 |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
mov [esi + 5], al |
; Year. |
mov al, 0x9 |
out 0x70, al |
in al, 0x71 |
call bcd2bin |
add ax, 2000 ; CMOS only returns last two digits. |
; Note that everywhere in KolibriOS this is used. |
; This is hacky, since the RTC can be incorrectly set |
; to something before 2000. |
mov [esi + 6], ax |
call bdfe_to_unix_time |
pop esi eax |
ret |
;--------------------------------------------------------------------- |
; Convert time+date from BDFE to Unix time. |
; Input: esi = pointer to BDFE time+date. |
; edi = buffer to output Unix time. |
;--------------------------------------------------------------------- |
bdfe_to_unix_time: |
push eax ebx ecx edx |
mov dword[edi], 0x00000000 |
; The minimum representable time is 1901-12-13. |
cmp word[esi + 6], 1901 |
jb .ret |
jg .max |
cmp byte[esi + 5], 12 |
jb .ret |
cmp byte[esi + 4], 13 |
jbe .ret |
jg .convert |
; Check if it is more than the maximum representable time. |
.max: |
; The maximum representable time is 2038-01-19. |
cmp word[esi + 6], 2038 |
jg .ret |
jb .convert |
cmp byte[esi + 5], 1 |
jg .ret |
cmp byte[esi + 4], 19 |
jge .ret |
; Convert the time. |
.convert: |
; Get if current year is leap year in ECX. |
xor ecx, ecx |
mov ebx, 4 |
xor edx, edx |
cmp word[esi + 6], 1970 |
jb .negative |
movzx eax, word[esi + 6] ; Year. |
cmp byte[esi + 5], 3 ; If the month is less than March, than that year doesn't matter. |
jge @F |
test eax, 3 |
; Not a leap year. |
jnz @F |
inc ecx |
@@: |
; Number of leap years between two years = ((end date - 1)/4) - (1970/4) |
dec eax |
div ebx |
sub eax, 1970/4 |
; EAX is the number of leap years. |
add eax, ecx |
mov ecx, (60 * 60 * 24) ; Seconds in a day. |
mul ecx |
; Account for leap years, i.e., one day extra for each. |
add [edi], eax |
; Get total days in EAX. |
movzx eax, byte[esi + 4] |
dec eax |
mul ecx |
; Account for days. |
add [edi], eax |
; Account for month. |
movzx eax, byte[esi + 5] |
dec eax |
mov eax, [cumulative_seconds_in_month + (eax * 4)] |
add [edi], eax |
; Account for year. |
movzx eax, word[esi + 6] |
sub eax, 1970 |
mov ecx, (60 * 60 * 24) * 365 ; Seconds in a year. |
mul ecx |
add [edi], eax |
; Seconds. |
movzx eax, byte[esi + 0] |
add [edi], eax |
; Minutes. |
movzx eax, byte[esi + 1] |
mov ecx, 60 |
mul ecx |
add [edi], eax |
; Hours. |
movzx eax, byte[esi + 2] |
mov ecx, (60 * 60) |
mul ecx |
add [edi], eax |
; The time wanted is before the epoch; handle it here. |
.negative: |
; TODO. |
.ret: |
pop edx ecx ebx eax |
ret |
; Recommended move to some kernel-wide alloc handling code. |
macro KERNEL_ALLOC store, label |
{ |
call kernel_alloc |
mov store, eax |
test eax, eax |
jz label |
} |
macro KERNEL_FREE data, label |
{ |
cmp data, 0 |
jz label |
push data |
call kernel_free |
} |
struct EXTFS PARTITION |
lock MUTEX |
partition_flags dd ? |
log_block_size dd ? |
block_size dd ? |
count_block_in_block dd ? |
blocks_per_group dd ? |
global_desc_table dd ? |
root_inode dd ? ; Pointer to root inode in memory. |
inode_size dd ? |
count_pointer_in_block dd ? ; (block_size / 4) |
count_pointer_in_block_square dd ? ; (block_size / 4)**2 |
ext2_save_block dd ? ; Block for 1 global procedure. |
ext2_temp_block dd ? ; Block for small procedures. |
ext2_save_inode dd ? ; inode for global procedures. |
ext2_temp_inode dd ? ; inode for small procedures. |
groups_count dd ? |
superblock rd 1024/4 |
ends |
; EXT2 revisions. |
EXT2_GOOD_OLD_REV = 0 |
; For fs_type. |
FS_TYPE_UNDEFINED = 0 |
FS_TYPE_EXT = 2 |
; Some set inodes. |
EXT2_BAD_INO = 1 |
EXT2_ROOT_INO = 2 |
EXT2_ACL_IDX_INO = 3 |
EXT2_ACL_DATA_INO = 4 |
EXT2_BOOT_LOADER_INO = 5 |
EXT2_UNDEL_DIR_INO = 6 |
; EXT2_SUPER_MAGIC. |
EXT2_SUPER_MAGIC = 0xEF53 |
EXT2_VALID_FS = 1 |
; Flags defining i_mode values. |
EXT2_S_IFMT = 0xF000 ; Mask for file type. |
EXT2_S_IFREG = 0x8000 ; Regular file. |
EXT2_S_IFDIR = 0x4000 ; Directory. |
; File type defining values in directory entry. |
EXT2_FT_REG_FILE = 1 ; Regular file. |
EXT2_FT_DIR = 2 ; Directory. |
; Flags used by KolibriOS. |
FS_FT_HIDDEN = 2 |
FS_FT_DIR = 0x10 ; Directory. |
; ext2 partition flags. |
EXT2_RO = 0x01 |
FS_FT_ASCII = 0 ; Name in ASCII. |
FS_FT_UNICODE = 1 ; Name in Unicode. |
EXT2_FEATURE_INCOMPAT_FILETYPE = 0x0002 ; Have file type in directory entry. |
EXT4_FEATURE_INCOMPAT_EXTENTS = 0x0040 ; Extents. |
EXT4_FEATURE_INCOMPAT_FLEX_BG = 0x0200 ; Flexible block groups. |
EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER = 0x0001 ; Sparse Superblock |
EXT2_FEATURE_RO_COMPAT_LARGE_FILE = 0x0002 ; Large file support (64-bit file size) |
; Implemented ext[2,3,4] features. |
EXT4_FEATURE_INCOMPAT_SUPP = EXT2_FEATURE_INCOMPAT_FILETYPE \ |
or EXT4_FEATURE_INCOMPAT_EXTENTS \ |
or EXT4_FEATURE_INCOMPAT_FLEX_BG |
; Implemented features which otherwise require "read-only" mount. |
EXT2_FEATURE_RO_COMPAT_SUPP = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER \ |
or EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
; ext4 features not support for write. |
EXT4_FEATURE_INCOMPAT_W_NOT_SUPP = EXT4_FEATURE_INCOMPAT_EXTENTS \ |
or EXT4_FEATURE_INCOMPAT_FLEX_BG |
; Flags specified in i_flags. |
EXT2_EXTENTS_FL = 0x00080000 ; Extents. |
struct EXT2_INODE_STRUC |
i_mode dw ? |
i_uid dw ? |
i_size dd ? |
i_atime dd ? |
i_ctime dd ? |
i_mtime dd ? |
i_dtime dd ? |
i_gid dw ? |
i_links_count dw ? |
i_blocks dd ? |
i_flags dd ? |
i_osd1 dd ? |
i_block rd 15 |
i_generation dd ? |
i_file_acl dd ? |
i_dir_acl dd ? |
i_faddr dd ? |
i_osd2 dd ? ; 12 bytes. |
ends |
struct EXT2_DIR_STRUC |
inode dd ? |
rec_len dw ? |
name_len db ? |
file_type db ? |
name db ? ; 255 (max) bytes. |
ends |
struct EXT2_BLOCK_GROUP_DESC |
block_bitmap dd ? ; +0 |
inode_bitmap dd ? ; +4 |
inode_table dd ? ; +8 |
free_blocks_count dw ? ; +12 |
free_inodes_count dw ? ; +14 |
used_dirs_count dw ? ; +16 |
pad dw ? ; +18 |
reserved rb 12 ; +20 |
ends |
struct EXT2_SB_STRUC |
inodes_count dd ? ; +0 |
blocks_count dd ? ; +4 |
r_block_count dd ? ; +8 |
free_block_count dd ? ; +12 |
free_inodes_count dd ? ; +16 |
first_data_block dd ? ; +20 |
log_block_size dd ? ; +24 |
log_frag_size dd ? ; +28 |
blocks_per_group dd ? ; +32 |
frags_per_group dd ? ; +36 |
inodes_per_group dd ? ; +40 |
mtime dd ? ; +44 |
wtime dd ? ; +48 |
mnt_count dw ? ; +52 |
max_mnt_count dw ? ; +54 |
magic dw ? ; +56 |
state dw ? ; +58 |
errors dw ? ; +60 |
minor_rev_level dw ? ; +62 |
lastcheck dd ? ; +64 |
check_intervals dd ? ; +68 |
creator_os dd ? ; +72 |
rev_level dd ? ; +76 |
def_resuid dw ? ; +80 |
def_resgid dw ? ; +82 |
first_ino dd ? ; +84 |
inode_size dw ? ; +88 |
block_group_nr dw ? ; +90 |
feature_compat dd ? ; +92 |
feature_incompat dd ? ; +96 |
feature_ro_compat dd ? ; +100 |
uuid rb 16 ; +104 |
volume_name rb 16 ; +120 |
last_mounted rb 64 ; +136 |
algo_bitmap dd ? ; +200 |
prealloc_blocks db ? ; +204 |
preallock_dir_blocks db ? ; +205 |
reserved_gdt_blocks dw ? ; +206 |
journal_uuid rb 16 ; +208 |
journal_inum dd ? ; +224 |
journal_dev dd ? ; +228 |
last_orphan dd ? ; +232 |
hash_seed rd 4 ; +236 |
def_hash_version db ? ; +252 |
reserved rb 3 ; +253 (reserved) |
default_mount_options dd ? ; +256 |
first_meta_bg dd ? ; +260 |
mkfs_time dd ? ; +264 |
jnl_blocks rd 17 ; +268 |
blocks_count_hi dd ? ; +336 |
r_blocks_count_hi dd ? ; +340 |
free_blocks_count_hi dd ? ; +344 |
min_extra_isize dw ? ; +348 |
want_extra_isize dw ? ; +350 |
flags dd ? ; +352 |
raid_stride dw ? ; +356 |
mmp_interval dw ? ; +358 |
mmp_block dq ? ; +360 |
raid_stripe_width dd ? ; +368 |
log_groups_per_flex db ? ; +372 |
ends |
; Header block extents. |
struct EXT4_EXTENT_HEADER |
eh_magic dw ? ; Magic value of 0xF30A, for ext4. |
eh_entries dw ? ; Number of blocks covered by the extent. |
eh_max dw ? ; Capacity of entries. |
eh_depth dw ? ; Tree depth (if 0, extents in the array are not extent indexes) |
eh_generation dd ? ; ??? |
ends |
; Extent. |
struct EXT4_EXTENT |
ee_block dd ? ; First logical block extent covers. |
ee_len dw ? ; Number of blocks covered by extent. |
ee_start_hi dw ? ; Upper 16 bits of 48-bit address (unused in KOS) |
ee_start_lo dd ? ; Lower 32 bits of 48-bit address. |
ends |
; Index on-disk structure; pointer to block of extents/indexes. |
struct EXT4_EXTENT_IDX |
ei_block dd ? ; Covers logical blocks from here. |
ei_leaf_lo dd ? ; Lower 32-bits of pointer to the physical block of the next level. |
ei_leaf_hi dw ? ; Higher 16-bits (unused in KOS). |
ei_unused dw ? ; Reserved. |
ends |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/fs/ext2/inode.asm |
---|
0,0 → 1,1175 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Contains ext2 inode handling code. ;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; |
;; Distributed under the terms of the new BSD license. ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; KSOC_EXT2_WRITE_END_TODO: clean this up. |
;--------------------------------------------------------------------- |
; 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 |
;--------------------------------------------------------------------- |
; 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_set_inode_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 |
; TODO: fix to have correct preference. |
mov eax, EXT2_ROOT_INO |
call ext2_block_calloc |
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 |
mov eax, EXT2_ROOT_INO |
call ext2_block_calloc |
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 |
; TODO: fix to have correct preference. |
mov eax, EXT2_ROOT_INO |
call ext2_block_calloc |
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 |
; TODO: fix to have correct preference. |
mov eax, EXT2_ROOT_INO |
call ext2_block_calloc |
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 |
; TODO: fix to have correct preference. |
mov eax, EXT2_ROOT_INO |
call ext2_block_calloc |
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_get_inode_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] |
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] |
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] |
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] |
mov ebx, [ebp + EXTFS.ext2_temp_block] |
call ext2_block_read |
test eax, eax |
jnz @F |
; Get the block ID. |
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 |
;--------------------------------------------------------------------- |
; 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_get_inode_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_get_inode_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_get_inode_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_set_inode_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_get_inode_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_get_inode_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 |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/fs/ext2/resource.asm |
---|
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 defragmentation. 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 though, 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 |
Property changes: |
Added: svn:eol-style |
+native |
\ No newline at end of property |
/kernel/trunk/kernel32.inc |
---|
191,7 → 191,7 |
include "blkdev/rd.inc" ; ramdisk read /write |
include "fs/fs_lfn.inc" ; syscall, version 2 |
include "fs/iso9660.inc" ; read for iso9660 filesystem CD |
include "fs/ext2.inc" ; read / write for ext2 filesystem |
include "fs/ext2/ext2.asm" ; read / write for ext2 filesystem |
include "fs/xfs.asm" ; read / write for xfs filesystem |
; sound |