kolibrios-gitea/kernel/trunk/fs/ext2/ext2.asm

1724 lines
49 KiB
NASM
Raw Normal View History

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains ext2 initialization, plus syscall handling code. ;;
;; ;;
;; Copyright (C) KolibriOS team 2013-2014. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
$Revision$
include 'ext2.inc'
include 'blocks.inc'
include 'inode.inc'
include 'resource.inc'
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
cmp dword [esi+DISK.MediaInfo.SectorSize], 512
jnz .fail
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
;call ext2_sb_update
; Sync the disk.
;mov esi, [ebp + PARTITION.Disk]
;call disk_sync ; eax contains error code, if any.
mov eax, ebp ; Return pointer to EXTFS.
pop edi esi ebp ebx
ret
; 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:
;DEBUGF 1, "Reading folder.\n"
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_inode_get_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_inode_get_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
;DEBUGF 1, "Returning with: %x.\n", eax
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
;DEBUGF 1, "Returning with: %x.\n", 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:
;DEBUGF 1, "Attempting read.\n"
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_inode_get_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_inode_get_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_inode_get_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
;DEBUGF 1, "Returning with: %x.\n", 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
;DEBUGF 1, "Returning with: %x.\n", 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:
;DEBUGF 1, "Calling for file info, for: %s.\n", esi
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
;DEBUGF 1, "Returning with: %x.\n", 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
;DEBUGF 1, "Returning with: %x.\n", 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:
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push edx esi edi ebx
call ext2_lock
mov edx, [ebx + 16]
; 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:
;DEBUGF 1, "Attempting Delete.\n"
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ebx ecx edx esi edi
call ext2_lock
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_inode_get_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
@@:
; Free indirect blocks.
call ext2_inode_free_indirect_blocks
test eax, eax
jnz .error_stack4
; Clear the inode, and add deletion time.
mov edi, [ebp + EXTFS.ext2_save_inode]
xor eax, eax
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
;DEBUGF 1, "And returning with: %x.\n", eax
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:
;DEBUGF 1, "Attempting to create folder.\n"
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ebx ecx edx esi edi
call ext2_lock
add esi, [esp + 20 + 4]
; Can't create root, but for CreateFolder 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 or PERMISSIONS
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
;DEBUGF 1, "Returning with: %x.\n", eax
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
;---------------------------------------------------------------------
; Rewrite a file.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
; ebx = bytes written.
;---------------------------------------------------------------------
ext2_Rewrite:
;DEBUGF 1, "Attempting Rewrite.\n"
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ecx edx esi edi
pushad
call ext2_lock
add esi, [esp + 16 + 32 + 4]
; Can't create root.
cmp byte [esi], 0
jz .error_access_denied
push esi
stdcall ext2_inode_find, 0
pop esi
; If the file is there, delete it.
test eax, eax
jnz @F
pushad
push eax
call ext2_unlock
pop eax
push dword 0x00000000
call ext2_Delete
add esp, 4
push eax
call ext2_lock
pop eax
test eax, eax
jnz .error_access_denied_delete
popad
@@:
; Find parent.
call ext2_inode_find_parent
test eax, eax
jnz .error_access_denied
; Inode ID for preference.
mov eax, esi
call ext2_inode_alloc
test eax, eax
jnz .error_full
; Save allocated inode in EDX; filename is in EDI; parent ID in ESI.
mov edx, ebx
push edi
xor al, al
mov edi, [ebp + EXTFS.ext2_temp_inode]
mov ecx, [ebp + EXTFS.inode_size]
rep stosb
mov edi, [ebp + EXTFS.ext2_temp_inode]
add edi, EXT2_INODE_STRUC.i_atime
call current_unix_time
add edi, 8
call current_unix_time
pop edi
mov ebx, [ebp + EXTFS.ext2_temp_inode]
mov [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG or PERMISSIONS
mov eax, edx
call ext2_inode_write
test eax, eax
jnz .error
; Link parent to child.
mov eax, esi
mov ebx, edx
mov esi, edi
mov dl, EXT2_FT_REG_FILE
call ext2_inode_link
test eax, eax
jnz .error
popad
push eax
call ext2_unlock
pop eax
push dword 0x00000000
call ext2_Write
add esp, 4
push eax
call ext2_lock
pop eax
.success:
push eax
call ext2_sb_update
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
pop eax
.return:
push eax
call ext2_unlock
pop eax
pop edi esi edx ecx
;DEBUGF 1, "And returning with: %x.\n", eax
ret
.error:
mov eax, ERROR_ACCESS_DENIED
jmp .success
.error_access_denied_delete:
popad
.error_access_denied:
popad
xor ebx, ebx
mov eax, ERROR_ACCESS_DENIED
jmp .return
.error_full:
popad
xor ebx, ebx
mov eax, ERROR_DISK_FULL
jmp .return
;---------------------------------------------------------------------
; Write to a file.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
; ebx = number of bytes written.
;---------------------------------------------------------------------
ext2_Write:
;DEBUGF 1, "Attempting write, "
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ecx edx esi edi
call ext2_lock
add esi, [esp + 16 + 4]
; Can't write to root.
cmp byte [esi], 0
jz .error
push ebx ecx edx
stdcall ext2_inode_find, 0
pop edx ecx ebx
; If file not there, error.
xor ecx, ecx
test eax, eax
jnz .error_file_not_found
; Save the inode.
push esi
; Check if it's a file.
mov edx, [ebp + EXTFS.ext2_save_inode]
test [edx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG
jz .error
mov eax, esi
mov ecx, [ebx + 4]
call ext2_inode_extend
xor ecx, ecx
test eax, eax
jnz .error_device
; ECX contains the size to write, and ESI points to it.
mov ecx, [ebx + 0x0C]
mov esi, [ebx + 0x10]
; Save the size of the inode.
mov eax, [edx + EXT2_INODE_STRUC.i_size]
push eax
xor edx, edx
div [ebp + EXTFS.block_size]
test edx, edx
jz .start_aligned
; Start isn't aligned, so deal with the non-aligned bytes.
mov ebx, [ebp + EXTFS.block_size]
sub ebx, edx
cmp ebx, ecx
jbe @F
; If the size to copy fits in current block, limit to that, instead of the entire block.
mov ebx, ecx
@@:
; Copy EBX bytes, in EAX indexed block.
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jnz .error_inode_size
push ecx
mov ecx, ebx
mov edi, ebx
add edi, edx
rep movsb
pop ecx
; Write the block.
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
add [esp], ebx
sub ecx, ebx
jz .write_inode
.start_aligned:
cmp ecx, [ebp + EXTFS.block_size]
jb @F
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
push eax
mov edx, [esp + 8]
call ext2_inode_blank_entry
test eax, eax
pop eax
jnz .error_inode_size
push ecx
mov ecx, [ebp + EXTFS.block_size]
mov edi, [ebp + EXTFS.ext2_save_block]
rep movsb
pop ecx
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
mov eax, [ebp + EXTFS.block_size]
sub ecx, eax
add [esp], eax
jmp .start_aligned
; Handle the remaining bytes.
@@:
test ecx, ecx
jz .write_inode
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jz @F
push eax
mov edx, [esp + 8]
call ext2_inode_blank_entry
test eax, eax
pop eax
jnz .error_inode_size
@@:
push ecx
mov edi, [ebp + EXTFS.ext2_save_block]
rep movsb
pop ecx
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
add [esp], ecx
xor ecx, ecx
.write_inode:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
test eax, eax
jnz .error_device
.success:
call ext2_sb_update
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
.return:
push eax
call ext2_unlock
pop eax
add esp, 4
mov ebx, [esp + 12]
sub ebx, ecx
pop edi esi edx ecx
;DEBUGF 1, "and returning with: %x.\n", eax
ret
.error:
mov eax, ERROR_ACCESS_DENIED
jmp .return
.error_file_not_found:
mov eax, ERROR_FILE_NOT_FOUND
jmp .return
.error_inode_size:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
.error_device:
call ext2_sb_update
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
mov eax, ERROR_DEVICE
jmp .return
;---------------------------------------------------------------------
; Set the end of a file.
; Input: esi + [esp + 4] = file name.
; ebx = pointer to paramteres from sysfunc 70.
; ebp = pointer to EXTFS structure.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_SetFileEnd:
test [ebp + EXTFS.partition_flags], EXT2_RO
jz @F
mov eax, ERROR_UNSUPPORTED_FS
ret
@@:
push ebx ecx edx esi edi
call ext2_lock
add esi, [esp + 20 + 4]
; Can't write to root.
cmp byte [esi], 0
jz .error
stdcall ext2_inode_find, 0
; If file not there, error.
test eax, eax
jnz .error_file_not_found
; Check if it's a file.
mov edx, [ebp + EXTFS.ext2_save_inode]
cmp [edx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFREG
jne .error
mov eax, esi
mov ecx, [ebx + 4]
call ext2_inode_extend
test eax, eax
jnz .error_disk_full
mov eax, esi
call ext2_inode_truncate
test eax, eax
jnz .error_disk_full
mov eax, esi
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_write
call ext2_sb_update
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
.return:
push eax
call ext2_unlock
pop eax
pop edi esi edx ecx ebx
ret
.error:
mov eax, ERROR_ACCESS_DENIED
jmp .return
.error_file_not_found:
mov eax, ERROR_FILE_NOT_FOUND
jmp .return
.error_disk_full:
call ext2_sb_update
; Sync the disk.
mov esi, [ebp + PARTITION.Disk]
call disk_sync ; eax contains error code, if any.
mov eax, ERROR_DISK_FULL
jmp .return