Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 3934 → Rev 3935

/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