Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 4428 → Rev 4429

/kernel/branches/kolibri-process/fs/ext2/blocks.inc
0,0 → 1,409
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains ext2 block handling code. ;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under the terms of the new BSD license. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;---------------------------------------------------------------------
; Write ext2 block from memory to disk.
; Input: eax = i_block (block number in ext2 terms);
; ebx = buffer address
; ebp = pointer to EXTFS
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_block_write:
push edx ebx ecx
 
mov edx, fs_write32_sys
jmp ext2_block_modify
 
;---------------------------------------------------------------------
; Read ext2 block from disk to memory.
; Input: eax = i_block (block number in ext2 terms);
; ebx = address of where to read block
; ebp = pointer to EXTFS
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_block_read:
push edx ebx ecx
mov edx, fs_read32_sys
jmp ext2_block_modify
 
;---------------------------------------------------------------------
; Modify ext2 block.
; Input: eax = i_block (block number in ext2 terms);
; ebx = I/O buffer address;
; edx = fs_read/write32_sys
; ebp = pointer to EXTFS
; edx, ebx, ecx on stack.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_block_modify:
; Get block number in hard-disk terms in eax.
mov ecx, [ebp + EXTFS.log_block_size]
shl eax, cl
mov ecx, eax
push [ebp + EXTFS.count_block_in_block]
 
@@:
mov eax, ecx
call edx
test eax, eax
jnz .fail
inc ecx
add ebx, 512
dec dword[esp]
jnz @B
 
xor eax, eax
@@:
pop ecx
pop ecx ebx edx
ret
 
.fail:
mov eax, ERROR_DEVICE
jmp @B
 
;---------------------------------------------------------------------
; Zeroes a block.
; Input: ebx = block ID.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_block_zero:
push ebx
 
mov eax, ebx
mov ebx, [ebp + EXTFS.ext2_temp_block]
 
call ext2_block_read
test eax, eax
jnz .return
 
push edi ecx
xor eax, eax
mov ecx, [ebp + EXTFS.block_size]
mov edi, [ebp + EXTFS.ext2_temp_block]
rep stosb
pop ecx edi
 
mov eax, [esp]
call ext2_block_write
 
.return:
pop ebx
ret
 
;---------------------------------------------------------------------
; Allocates a block.
; Input: eax = inode ID for "preference".
; ebp = pointer to EXTFS.
; Output: Block marked as set in block group.
; eax = error code.
; ebx = block ID.
;---------------------------------------------------------------------
ext2_block_alloc:
push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_count]
push EXT2_BLOCK_GROUP_DESC.free_blocks_count
push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group]
 
lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count]
push ebx
 
push ext2_bg_read_blk_bitmap
 
call ext2_resource_alloc
 
ret
 
;---------------------------------------------------------------------
; Zero-allocates a block.
; Input: eax = inode ID for "preference".
; ebp = pointer to EXTFS.
; Output: Block marked as set in block group.
; eax = error code.
; ebx = block ID.
;---------------------------------------------------------------------
ext2_block_calloc:
call ext2_block_alloc
test eax, eax
jnz @F
 
call ext2_block_zero
@@:
ret
 
;---------------------------------------------------------------------
; Frees a block.
; Input: eax = block ID.
; ebp = pointer to EXTFS.
; Output: Block marked as free in block group.
; eax = error code.
;---------------------------------------------------------------------
ext2_block_free:
push edi ecx
 
mov edi, ext2_bg_read_blk_bitmap
xor ecx, ecx
call ext2_resource_free
 
pop ecx edi
ret
 
;---------------------------------------------------------------------
; Find parent from file path in block.
; Input: esi = file path.
; ebx = pointer to directory block.
; ebp = pointer to EXTFS structure.
; Output: esi = name without parent, or not changed.
; ebx = directory record matched.
;---------------------------------------------------------------------
ext2_block_find_parent:
sub esp, 256 ; Space for EXT2 filename.
mov edx, ebx
add edx, [ebp + EXTFS.block_size] ; Save block end.
 
.start_rec:
cmp [ebx + EXT2_DIR_STRUC.inode], 0
jz .next_rec
 
mov edi, esp
push esi
movzx ecx, [ebx + EXT2_DIR_STRUC.name_len]
lea esi, [ebx + EXT2_DIR_STRUC.name]
call utf8_to_cp866
 
mov ecx, edi
lea edi, [esp + 4]
sub ecx, edi ; Number of bytes in resulting string.
 
mov esi, [esp]
 
; esi: original file path.
; edi: converted string stored on stack.
; ecx: size of converted string.
@@:
; If no bytes left in resulting string, test it.
jecxz .test_find
dec ecx
 
lodsb
call char_toupper
 
mov ah, [edi]
inc edi
xchg al, ah
call char_toupper
 
; If both are same, check next byte.
cmp al, ah
je @B
@@: ; Doesn't match.
pop esi
.next_rec:
movzx eax, [ebx + EXT2_DIR_STRUC.rec_len]
add ebx, eax ; Go to next record.
cmp ebx, edx ; Check if this is the end.
jb .start_rec
add esp, 256
ret
 
.test_find:
cmp byte [esi], 0
je .ret ; The end reached.
cmp byte [esi], '/' ; If not end of directory name, not matched.
jne @B
inc esi
 
.ret:
add esp, 256 + 4
ret
 
;---------------------------------------------------------------------
; Finds free space in a directory block, modifying last entry appropriately.
; Input: ebp = pointer to EXTFS.
; ecx = size of free space required.
; [EXTFS.ext2_temp_block] contains the block relevant.
; Output: edi = free entry.
; rec_len of free entry is set.
; eax = error code; if the block doesn't link to the next one, this is 0x00000001 on failure.
; ; else, 0xFFFFFFFF.
;---------------------------------------------------------------------
ext2_block_find_fspace:
push ebx edx
 
mov edi, [ebp + EXTFS.ext2_temp_block]
mov edx, edi
add edx, [ebp + EXTFS.block_size]
 
@@:
movzx eax, [edi + EXT2_DIR_STRUC.rec_len]
test eax, eax
jz .zero_len
 
cmp [edi + EXT2_DIR_STRUC.inode], 0
je .unused_entry
 
; It's a used entry, so see if we can fit it between current one and next.
; Subtract the size used by the name and the structure from rec_len.
movzx ebx, [edi + EXT2_DIR_STRUC.name_len]
add ebx, 8 + 3
and ebx, 0xfffffffc ; Align it on the next 4-byte boundary.
 
sub eax, ebx
add edi, ebx
cmp eax, ecx
jb .next_iter
 
sub edi, ebx
mov [edi + EXT2_DIR_STRUC.rec_len], bx ; Make previous entry point to us.
add edi, ebx
 
mov [edi + EXT2_DIR_STRUC.rec_len], ax ; Make current entry point to next one.
jmp .found
 
.unused_entry:
; It's an unused inode.
cmp eax, ecx
jge .found
 
.next_iter:
add edi, eax
cmp edi, edx
jb @B
 
.not_found:
xor eax, eax
not eax
jmp .ret
 
; Zero length entry means we have the rest of the block for us.
.zero_len:
mov eax, edx
sub eax, edi
 
; Point to next block.
mov [edi + EXT2_DIR_STRUC.rec_len], ax
 
cmp eax, ecx
jge .fits
 
mov [edi + EXT2_DIR_STRUC.inode], 0
 
; It doesn't fit, but the block doesn't link to the next block.
xor eax, eax
inc eax
jmp .ret
 
.fits:
mov [edi + EXT2_DIR_STRUC.rec_len], cx
 
.found:
xor eax, eax
 
.ret:
pop edx ebx
ret
 
;---------------------------------------------------------------------
; Gets the block group's descriptor.
; Input: eax = block group.
; Output: eax = if zero, error; else, points to block group descriptor.
; [EXTFS.ext2_temp_block] contains relevant block.
; ebp = pointer to EXTFS.
;---------------------------------------------------------------------
ext2_bg_read_desc:
push edx ebx
mov edx, 32
mul edx ; Get index of descriptor in global_desc_table.
 
; eax: block group descriptor offset relative to global descriptor table start
; Find the block this block descriptor is in.
div [ebp + EXTFS.block_size]
add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block]
inc eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail
 
add ebx, edx ; edx: local index of descriptor inside block
mov eax, ebx
 
.return:
pop ebx edx
ret
 
.fail:
xor eax, eax
jmp .return
 
;---------------------------------------------------------------------
; Writes a block group's descriptor.
; Input: eax = block group.
; [EXTFS.ext2_temp_data] contains the block relevant.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_bg_write_desc:
push edx ebx
mov edx, 32
mul edx ; Get index of descriptor in global_desc_table.
 
; eax: block group descriptor offset relative to global descriptor table start
; Find the block this block descriptor is in.
div [ebp + EXTFS.block_size]
add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block]
inc eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
 
.return:
pop ebx edx
ret
 
;---------------------------------------------------------------------
; Gets the block group's block bitmap.
; Input: eax = block group.
; Output: eax = if zero, error; else, points to block group descriptor.
; ebx = block bitmap's block (hard disk).
;---------------------------------------------------------------------
ext2_bg_read_blk_bitmap:
push ecx
 
call ext2_bg_read_desc
test eax, eax
jz .fail
 
mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.block_bitmap] ; Block number of block group bitmap - in ext2 terms.
 
.return:
pop ecx
ret
 
.fail:
xor eax, eax
jmp .return
 
;---------------------------------------------------------------------
; Updates superblock, plus backups.
; Input: ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_sb_update:
push ebx
 
mov eax, 2
lea ebx, [ebp + EXTFS.superblock]
call fs_write32_sys
 
pop ebx
ret
/kernel/branches/kolibri-process/fs/ext2/ext2.asm
0,0 → 1,1718
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; 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.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
 
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
/kernel/branches/kolibri-process/fs/ext2/ext2.inc
0,0 → 1,670
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains ext2 structures, and macros. ;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under the terms of the new BSD license. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
; Future jobs for driver, in order of preference:
; * clean up existing extents support.
; * add b-tree directories support.
; * add long file support.
; * add journal support.
; * add minor features that come with ext3/4.
 
; Recommended move to some kernel-wide bitmap handling code (with a bit of abstraction, of course).
 
;---------------------------------------------------------------------
; 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.
 
EXT2_S_IRUSR = 0x0100 ; User read
EXT2_S_IWUSR = 0x0080 ; User write
EXT2_S_IXUSR = 0x0040 ; User execute
EXT2_S_IRGRP = 0x0020 ; Group read
EXT2_S_IWGRP = 0x0010 ; Group write
EXT2_S_IXGRP = 0x0008 ; Group execute
EXT2_S_IROTH = 0x0004 ; Others read
EXT2_S_IWOTH = 0x0002 ; Others write
EXT2_S_IXOTH = 0x0001 ; Others execute
 
PERMISSIONS = EXT2_S_IRUSR or EXT2_S_IWUSR \
or EXT2_S_IRGRP or EXT2_S_IWGRP \
or EXT2_S_IROTH or EXT2_S_IWOTH
 
; 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
/kernel/branches/kolibri-process/fs/ext2/inode.inc
0,0 → 1,1850
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains ext2 inode handling code. ;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under the terms of the new BSD license. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;---------------------------------------------------------------------
; Receives block number from extent-based inode.
; Input: ecx = number of block in inode
; esi = address of extent header
; ebp = pointer to EXTFS
; Output: ecx = address of next block, if successful
; eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext4_block_recursive_search:
cmp word [esi + EXT4_EXTENT_HEADER.eh_magic], 0xF30A ;EXT4_EXT_MAGIC
jne .fail
movzx ebx, [esi + EXT4_EXTENT_HEADER.eh_entries]
add esi, sizeof.EXT4_EXTENT_HEADER
cmp word [esi - sizeof.EXT4_EXTENT_HEADER + EXT4_EXTENT_HEADER.eh_depth], 0
je .leaf_block ;листовой ли это блок?
;не листовой блок, а индексный ; eax - ext4_extent_idx
test ebx, ebx
jz .fail ;пустой индексный блок -> ошибка
 
;цикл по индексам экстентов
@@:
cmp ebx, 1 ;у индексов не хранится длина,
je .end_search_index ;поэтому, если остался последний - то это нужный
cmp ecx, [esi + EXT4_EXTENT_IDX.ei_block]
jb .fail
cmp ecx, [esi + sizeof.EXT4_EXTENT_IDX + EXT4_EXTENT_IDX.ei_block] ;блок слeдующего индекса
jb .end_search_index ;следующий дальше - значит текущий, то что нам нужен
add esi, sizeof.EXT4_EXTENT_IDX
dec ebx
jmp @B
 
.end_search_index:
;ebp указывает на нужный extent_idx, считываем следующий блок
mov ebx, [ebp + EXTFS.ext2_temp_block]
mov eax, [esi + EXT4_EXTENT_IDX.ei_leaf_lo]
call ext2_block_read
test eax, eax
jnz .fail
mov esi, ebx
jmp ext4_block_recursive_search ;рекурсивно прыгаем в начало
.leaf_block: ;листовой блок esi - ext4_extent
;цикл по экстентам
@@:
test ebx, ebx
jz .fail ;ни один узел не подошел - ошибка
 
mov edx, [esi + EXT4_EXTENT.ee_block]
cmp ecx, edx
jb .fail ;если меньше, значит он был в предыдущих блоках -> ошибка
 
movzx edi, [esi + EXT4_EXTENT.ee_len]
add edx, edi
cmp ecx, edx
jb .end_search_extent ;нашли нужный блок
add esi, sizeof.EXT4_EXTENT
dec ebx
jmp @B
.end_search_extent:
mov edx, [esi + EXT4_EXTENT.ee_start_lo]
sub ecx, [esi + EXT4_EXTENT.ee_block] ;разница в ext4 блоках
add ecx, edx
xor eax, eax
ret
 
.fail:
mov eax, ERROR_FS_FAIL
ret
 
;---------------------------------------------------------------------
; Frees triply indirect block.
; Input: eax = triply indirect block.
; [ebp + EXTFS.ext2_save_inode] = the inode.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_free_triply_indirect:
push ebx edx
 
test eax, eax
jz .success
push eax
; Read the triple indirect block.
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
pop eax
jnz .fail
 
; Free the triple indirect block.
call ext2_block_free
test eax, eax
jnz .fail
 
mov edx, ebx
add edx, [ebp + EXTFS.block_size]
 
@@:
mov eax, [ebx]
test eax, eax
jz .success
 
call ext2_inode_free_doubly_indirect
cmp eax, 1
je .success
cmp eax, 0xFFFFFFFF
je .fail
 
add ebx, 4
cmp ebx, edx
jb @B
 
.success:
xor eax, eax
.ret:
pop edx ebx
ret
 
.fail:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Frees double indirect block.
; Input: eax = double indirect block.
; [ebp + EXTFS.ext2_save_inode] = the inode.
; Output: eax = error code, 1 implies finished, ~0 implies error
;---------------------------------------------------------------------
ext2_inode_free_doubly_indirect:
push ebx edx
 
test eax, eax
jz .complete
push eax
; Read the double indirect block.
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
pop eax
jnz .fail
 
call ext2_block_free
test eax, eax
jnz .fail
 
mov edx, ebx
add edx, [ebp + EXTFS.block_size]
 
@@:
mov eax, [ebx]
test eax, eax
jz .complete
 
call ext2_block_free
test eax, eax
jnz .fail
 
add ebx, 4
cmp ebx, edx
jb @B
 
.success:
xor eax, eax
.ret:
pop edx ebx
ret
 
.complete:
xor eax, eax
inc eax
jmp .ret
 
.fail:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Frees all indirect blocks.
; Input: ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_save_inode] = the inode.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_free_indirect_blocks:
push edi
 
mov edi, [ebp + EXTFS.ext2_save_inode]
 
; Free indirect block.
mov eax, [edi + EXT2_INODE_STRUC.i_block + 12*4]
test eax, eax
jz .success
 
call ext2_block_free
test eax, eax
jnz .fail
 
mov eax, [edi + EXT2_INODE_STRUC.i_block + 13*4]
call ext2_inode_free_doubly_indirect
cmp eax, 1
je .success
cmp eax, 0xFFFFFFFF
je .fail
 
mov eax, [edi + EXT2_INODE_STRUC.i_block + 14*4]
call ext2_inode_free_triply_indirect
test eax, eax
jnz .fail
 
.success:
xor eax, eax
.ret:
pop edi
ret
 
.fail:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Allocates block for inode.
; Input: esi = address of inode
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_calloc_block:
push ecx
 
; TODO: fix to have correct preference.
mov eax, EXT2_ROOT_INO
call ext2_block_calloc
test eax, eax
jnz .fail
 
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
mov eax, 2
shl eax, cl
add [esi + EXT2_INODE_STRUC.i_blocks], eax
 
.success:
xor eax, eax
.ret:
pop ecx
ret
 
.fail:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Sets block ID for indirect-addressing inode.
; Input: ecx = index of block in inode
; edi = block ID to set to
; esi = address of inode
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_set_block:
push ebx ecx edx
 
; 0 to 11: direct blocks.
cmp ecx, 12
jb .direct_block
 
; Indirect blocks
sub ecx, 12
cmp ecx, [ebp + EXTFS.count_pointer_in_block]
jb .indirect_block
 
; Double indirect blocks.
sub ecx, [ebp + EXTFS.count_pointer_in_block]
cmp ecx, [ebp + EXTFS.count_pointer_in_block_square]
jb .double_indirect_block
 
; Triple indirect blocks.
sub ecx, [ebp + EXTFS.count_pointer_in_block_square]
 
; Get triply-indirect block in temp_block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4]
test eax, eax
jnz @F
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc
 
mov [esi + EXT2_INODE_STRUC.i_block + 14*4], ebx
mov eax, ebx
 
@@:
push eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail_alloc_4
 
; Get index in triply-indirect block.
xor edx, edx
mov eax, ecx
div [ebp + EXTFS.count_pointer_in_block_square]
 
; eax: index in triply-indirect block, edx: index in doubly-indirect block.
lea ecx, [ebx + eax*4]
mov eax, [ebx + eax*4]
test eax, eax
jnz @F
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc_4
 
mov [ecx], ebx
 
mov eax, [esp]
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .fail_alloc_4
 
mov eax, [ecx]
@@:
mov [esp], eax
call ext2_block_read
test eax, eax
jnz .fail_alloc_4
 
mov eax, edx
jmp @F
 
.double_indirect_block:
; Get doubly-indirect block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4]
test eax, eax
jnz .double_indirect_present
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc
 
mov [esi + EXT2_INODE_STRUC.i_block + 13*4], ebx
mov eax, ebx
 
.double_indirect_present:
; Save block we're at.
push eax
 
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail_alloc_4
 
mov eax, ecx
@@:
xor edx, edx
div [ebp + EXTFS.count_pointer_in_block]
 
; eax: index in doubly-indirect block, edx: index in indirect block.
lea ecx, [ebx + edx*4]
push ecx
 
lea ecx, [ebx + eax*4]
cmp dword[ecx], 0
jne @F
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc_8
 
mov [ecx], ebx
 
mov eax, [esp + 4]
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .fail_alloc_8
 
@@:
mov eax, [ecx]
push eax
call ext2_block_read
test eax, eax
jnz .fail_alloc_12
 
pop eax
pop ecx
mov [ecx], edi
call ext2_block_write
 
add esp, 4
jmp .return
 
.indirect_block:
; Get index of indirect block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4]
test eax, eax
jnz @F
 
call ext2_inode_calloc_block
test eax, eax
jnz .fail_alloc
mov [esi + EXT2_INODE_STRUC.i_block + 12*4], ebx
mov eax, ebx
@@:
push eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail_alloc_4
; Get the block ID.
mov [ebx + ecx*4], edi
pop eax
call ext2_block_write
jmp .return
 
.direct_block:
mov [esi + EXT2_INODE_STRUC.i_block + ecx*4], edi
xor eax, eax
 
.return:
pop edx ecx ebx
ret
 
.fail_alloc:
xor eax, eax
not eax
jmp .return
 
.fail_alloc_12:
add esp, 4
.fail_alloc_8:
add esp, 4
.fail_alloc_4:
add esp, 4
jmp .fail_alloc
 
;---------------------------------------------------------------------
; Receives block ID from indirect-addressing inode.
; Input: ecx = index of block in inode
; esi = address of inode
; ebp = pointer to EXTFS
; Output: ecx = block ID, if successful
; eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_get_block:
; If inode is extent-based, use ext4_block_recursive_search.
test [esi + EXT2_INODE_STRUC.i_flags], EXT2_EXTENTS_FL
jz @F
 
pushad
 
; Get extent header in EBP.
add esi, EXT2_INODE_STRUC.i_block
call ext4_block_recursive_search
mov PUSHAD_ECX, ecx
mov PUSHAD_EAX, eax
 
popad
ret
 
@@:
; 0 to 11: direct blocks.
cmp ecx, 12
jb .get_direct_block
 
; Indirect blocks
sub ecx, 12
cmp ecx, [ebp + EXTFS.count_pointer_in_block]
jb .get_indirect_block
 
; Double indirect blocks.
sub ecx, [ebp + EXTFS.count_pointer_in_block]
cmp ecx, [ebp + EXTFS.count_pointer_in_block_square]
jb .get_double_indirect_block
 
; Triple indirect blocks.
sub ecx, [ebp + EXTFS.count_pointer_in_block_square]
push edx ebx
 
; Get triply-indirect block in temp_block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4]
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail
 
; Get index in triply-indirect block.
xor edx, edx
mov eax, ecx
div [ebp + EXTFS.count_pointer_in_block_square]
 
; eax: index in triply-indirect block, edx: index in doubly-indirect block.
mov eax, [ebx + eax*4]
test eax, eax
jz .fail_triple_indirect_block
 
call ext2_block_read
test eax, eax
jnz .fail
 
mov eax, edx
jmp @F
 
.get_double_indirect_block:
push edx ebx
 
; Get doubly-indirect block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4]
test eax, eax
jz .fail_double_indirect_block
 
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail
 
mov eax, ecx
@@:
xor edx, edx
div [ebp + EXTFS.count_pointer_in_block]
 
; eax: index in doubly-indirect block, edx: index in indirect block.
mov eax, [ebx + eax*4]
test eax, eax
jz .fail_double_indirect_block
 
call ext2_block_read
test eax, eax
jnz .fail
 
mov ecx, [ebx + edx*4]
.fail:
pop ebx edx
 
ret
 
.get_indirect_block:
push ebx
 
; Get index of indirect block.
mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4]
test eax, eax
jz .fail_indirect_block
 
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz @F
mov ecx, [ebx + ecx*4]
@@:
pop ebx
 
ret
 
.get_direct_block:
mov ecx, [esi + EXT2_INODE_STRUC.i_block + ecx*4]
xor eax, eax
 
ret
 
.fail_indirect_block:
pop ebx
 
.fail_triple_indirect_block:
xor eax, eax
xor ecx, ecx
ret
 
.fail_double_indirect_block:
pop ebx edx
jmp .fail_triple_indirect_block
 
;---------------------------------------------------------------------
; Get block containing inode.
; Input: eax = inode number.
; ebp = pointer to EXTFS.
; Output: ebx = block (hard disk) containing inode.
; edx = index inside block.
; eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_read_block_of_inode:
pushad
 
dec eax
xor edx, edx
; EAX = block group.
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group]
 
push edx ; Index in group.
 
mov edx, 32
mul edx ; Get index of descriptor in global_desc_table.
 
; eax: inode group offset relative to global descriptor table start
; Find the block this block descriptor is in.
div [ebp + EXTFS.block_size]
add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block]
inc eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .return
 
add ebx, edx ; edx: local index of descriptor inside block
mov eax, [ebx + EXT2_BLOCK_GROUP_DESC.inode_table] ; Block number of inode table - in ext2 terms.
mov ecx, [ebp + EXTFS.log_block_size]
shl eax, cl
 
; eax: points to inode table on HDD.
mov esi, eax
 
; Add local address of inode.
pop eax
mov ecx, [ebp + EXTFS.inode_size]
mul ecx ; (index * inode_size)
 
mov ebp, 512
div ebp ; Divide by hard disk block size.
 
add eax, esi ; Found block to read.
mov ebx, eax ; Get it inside ebx.
 
xor eax, eax
.return:
mov PUSHAD_EAX, eax
mov PUSHAD_EBX, ebx
mov PUSHAD_EDX, edx
 
popad
ret
 
;---------------------------------------------------------------------
; Sets content of inode by number.
; Input: eax = inode number.
; ebx = address from where to write inode content.
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_write:
push edx edi esi ecx ebx
mov esi, ebx
 
; Ext2 actually stores time of modification of inode in ctime.
lea edi, [ebx + EXT2_INODE_STRUC.i_ctime]
call current_unix_time
 
; Get block where inode is situated.
call ext2_read_block_of_inode
test eax, eax
jnz .error
 
mov eax, ebx ; Get block into EAX.
mov ebx, [ebp + EXTFS.ext2_temp_block]
 
mov ecx, eax ; Save block.
call fs_read32_sys
test eax, eax
jz @F
 
.error:
mov eax, ERROR_DEVICE
jmp .return
 
@@:
mov eax, ecx
mov ecx, [ebp + EXTFS.inode_size]
mov edi, edx ; The index into the block.
add edi, ebx
rep movsb
 
; Write the block.
call fs_write32_sys
 
.return:
pop ebx ecx esi edi edx
ret
 
;---------------------------------------------------------------------
; Get content of inode by number.
; Input: eax = inode number.
; ebx = address where to store inode content.
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
;---------------------------------------------------------------------
ext2_inode_read:
push edx edi esi ecx ebx
mov edi, ebx
 
; Get block where inode is situated.
call ext2_read_block_of_inode
test eax, eax
jnz .error
 
mov eax, ebx ; Get block into EAX.
mov ebx, [ebp + EXTFS.ext2_temp_block]
call fs_read32_sys
test eax, eax
jz @F
 
.error:
mov eax, ERROR_DEVICE
jmp .return
 
@@:
mov ecx, [ebp + EXTFS.inode_size]
mov esi, edx ; The index into the inode.
add esi, ebx
rep movsb
 
xor eax, eax
.return:
pop ebx ecx esi edi edx
ret
 
;---------------------------------------------------------------------
; Seek inode from the path.
; Input: esi + [esp + 4] = name.
; ebp = pointer to EXTFS.
; Output: eax = error code (0 implies no error)
; esi = inode number.
; dl = first byte of file/folder name.
; [ext2_data.ext2_save_inode] stores the inode.
;---------------------------------------------------------------------
ext2_inode_find:
mov edx, [ebp + EXTFS.root_inode]
 
; Check for empty root.
cmp [edx + EXT2_INODE_STRUC.i_blocks], 0
je .error_empty_root
 
; Check for root.
cmp byte[esi], 0
jne .next_path_part
 
push edi ecx
mov esi, [ebp + EXTFS.root_inode]
mov edi, [ebp + EXTFS.ext2_save_inode]
mov ecx, [ebp + EXTFS.inode_size]
rep movsb
pop ecx edi
 
xor eax, eax
xor dl, dl
mov esi, EXT2_ROOT_INO
ret 4
.next_path_part:
push [edx + EXT2_INODE_STRUC.i_blocks]
xor ecx, ecx
 
.folder_block_cycle:
push ecx
xchg esi, edx
call ext2_inode_get_block
xchg esi, edx
test eax, eax
jnz .error_get_inode_block
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_save_block] ; Get directory records from directory.
call ext2_block_read
test eax, eax
jnz .error_get_block
push esi
push edx
call ext2_block_find_parent
pop edx
pop edi ecx
 
cmp edi, esi ; Did something match?
je .next_folder_block ; No, move to next block.
cmp byte [esi], 0 ; Reached the "end" of path successfully.
jnz @F
cmp dword[esp + 8], 0
je .get_inode_ret
mov esi, [esp + 8]
mov dword[esp + 8], 0
 
@@:
mov eax, [ebx + EXT2_DIR_STRUC.inode]
mov ebx, [ebp + EXTFS.ext2_save_inode]
call ext2_inode_read
test eax, eax
jnz .error_get_inode
 
movzx eax, [ebx + EXT2_INODE_STRUC.i_mode]
and eax, EXT2_S_IFMT ; Get the mask.
cmp eax, EXT2_S_IFDIR
jne .not_found ; Matched till part, but directory entry we got doesn't point to folder.
 
pop ecx ; Stack top contains number of blocks.
mov edx, ebx
jmp .next_path_part
.next_folder_block:
; Next block in current folder.
pop eax ; Get blocks counter.
sub eax, [ebp + EXTFS.count_block_in_block]
jle .not_found
push eax
inc ecx
jmp .folder_block_cycle
 
.not_found:
mov eax, ERROR_FILE_NOT_FOUND
ret 4
 
.get_inode_ret:
pop ecx ; Stack top contains number of blocks.
 
mov dl, [ebx + EXT2_DIR_STRUC.name] ; First character of file-name.
mov eax, [ebx + EXT2_DIR_STRUC.inode]
mov ebx, [ebp + EXTFS.ext2_save_inode]
mov esi, eax
 
; If we can't get the inode, eax contains the error.
call ext2_inode_read
ret 4
 
.error_get_inode_block:
.error_get_block:
pop ecx
.error_get_inode:
pop ebx
.error_empty_root:
mov eax, ERROR_FS_FAIL
ret 4
 
;---------------------------------------------------------------------
; Seeks parent inode from path.
; Input: esi = path.
; ebp = pointer to EXTFS.
; Output: eax = error code.
; esi = inode.
; edi = pointer to file name.
;---------------------------------------------------------------------
ext2_inode_find_parent:
push esi
xor edi, edi
 
.loop:
cmp byte[esi], '/'
jne @F
 
mov edi, esi
inc esi
jmp .loop
 
@@:
inc esi
cmp byte[esi - 1], 0
jne .loop
 
; If it was just a filename (without any additional directories),
; use the last byte as "parent path".
cmp edi, 0
jne @F
 
pop edi
dec esi
jmp .get_inode
 
; It had some additional directories, so handle it that way.
@@:
mov byte[edi], 0
inc edi
pop esi
 
.get_inode:
push ebx edx
stdcall ext2_inode_find, 0
pop edx ebx
 
.return:
ret
 
;---------------------------------------------------------------------
; Link an inode.
; Input: eax = inode on which to link.
; ebx = inode to link.
; dl = file type.
; esi = name.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_link:
push eax
push esi edi ebx ecx edx
 
; Get string length, and then directory entry structure size.
call strlen
add ecx, 8
 
push esi ebx ecx
 
xor ecx, ecx
mov esi, [ebp + EXTFS.ext2_temp_inode]
mov ebx, esi
 
call ext2_inode_read
test eax, eax
jnz .error_inode_read
 
; Get the maximum addressible i_block index by (i_blocks/(2 << s_log_block_size)).
; Note that i_blocks contains number of reserved 512B blocks, which is why we've to
; find out the ext2 blocks.
mov eax, 2
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
shl eax, cl
mov ecx, eax
 
mov eax, [esi + EXT2_INODE_STRUC.i_blocks]
xor edx, edx
 
div ecx
 
; EAX is the maximum index inside i_block we can go.
push eax
push dword 0
 
; ECX contains the "block inside i_block" index.
xor ecx, ecx
@@:
call ext2_inode_get_block
test eax, eax
jnz .error_get_inode_block
test ecx, ecx
jz .alloc_block ; We've got no block here, so allocate one.
 
push ecx ; Save block number.
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .error_block_read
 
; Try to find free space in current block.
mov ecx, [esp + 8]
call ext2_block_find_fspace
test eax, eax
jz .found
 
cmp eax, 0x00000001
jne .next_iter
 
; This block wasn't linking to the next block, so fix that, and use the next one.
; Write the block.
pop eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .error_get_inode_block
 
inc dword [esp]
mov ecx, [esp]
call ext2_inode_get_block
test eax, eax
jnz .error_get_inode_block
 
test ecx, ecx
jz .alloc_block
 
; If there was a block there, prepare it for our use!
push ecx
jmp .prepare_block
 
.next_iter:
add esp, 4
 
inc dword [esp]
mov ecx, [esp]
cmp ecx, [esp + 4]
jbe @B
 
.alloc_block:
mov eax, [esp + 12] ; Get inode ID of what we're linking.
call ext2_block_calloc
test eax, eax
jnz .error_get_inode_block
 
mov ecx, [esp] ; Get the index of it inside the inode.
mov edi, ebx ; And what to set to.
call ext2_inode_set_block
test eax, eax
jnz .error_get_inode_block
 
; Update i_size.
mov eax, [ebp + EXTFS.block_size]
add [esi + EXT2_INODE_STRUC.i_size], eax
 
; Update i_blocks.
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
mov eax, 2
shl eax, cl
add [esi + EXT2_INODE_STRUC.i_blocks], eax
 
; Write the inode.
mov eax, [esp + 40]
mov ebx, esi
call ext2_inode_write
test eax, eax
jnz .error_get_inode_block
 
push edi ; Save the block we just allocated.
 
; If we've allocated/using-old-block outside of loop, prepare it.
.prepare_block:
mov eax, [esp]
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .error_block_read
 
mov edi, ebx
mov eax, [ebp + EXTFS.block_size]
mov [edi + EXT2_DIR_STRUC.rec_len], ax
 
.found:
pop edx
add esp, 8
pop ecx ebx esi
 
push ebx
mov [edi], ebx ; Save inode.
 
mov eax, [esp + 4] ; Get EDX off the stack -- contains the file_type.
cmp [ebp + EXTFS.superblock + EXT2_SB_STRUC.rev_level], EXT2_GOOD_OLD_REV
je .name
 
; Set the file-type.
mov [edi + EXT2_DIR_STRUC.file_type], al
 
.name:
; Save name.
sub ecx, 8
mov [edi + EXT2_DIR_STRUC.name_len], cl
add edi, 8
rep movsb
 
; Write block.
mov eax, edx
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .error_block_write
 
mov eax, [esp]
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_read
test eax, eax
jnz .error_block_write
 
pop eax
inc [ebx + EXT2_INODE_STRUC.i_links_count]
call ext2_inode_write
test eax, eax
jnz .error
 
xor eax, eax
.ret:
pop edx ecx ebx edi esi
add esp, 4
ret
 
.error_block_read:
add esp, 4
.error_get_inode_block:
add esp, 8
.error_inode_read:
add esp, 8
.error_block_write:
add esp, 4
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Unlink an inode.
; Input: eax = inode from which to unlink.
; ebx = inode to unlink.
; ebp = pointer to EXTFS.
; Output: eax = number of links to inode, after unlinking (0xFFFFFFFF implies error)
;---------------------------------------------------------------------
ext2_inode_unlink:
push ebx ecx edx esi edi
 
push ebx
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_read
 
test eax, eax
jnz .fail_get_inode
 
; The index into the inode block data.
push dword 0
mov esi, [ebp + EXTFS.ext2_temp_inode]
.loop:
mov ecx, [esp]
call ext2_inode_get_block
 
test eax, eax
jnz .fail_loop
test ecx, ecx
jz .fail_loop
 
mov eax, ecx
mov edi, eax
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .fail_loop
 
; edi -> block.
.first_dir_entry:
mov eax, [esp + 4]
cmp [ebx], eax
jne @F
 
mov dword[ebx], 0 ; inode.
mov word[ebx + 6], 0 ; name_len + file_type.
jmp .write_block
 
@@:
mov edx, ebx
add edx, [ebp + EXTFS.block_size]
push edx
 
mov edx, ebx
movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len]
add ebx, ecx
 
.dir_entry:
cmp [ebx], eax
jne @F
 
mov cx, [ebx + EXT2_DIR_STRUC.rec_len]
add [edx + EXT2_DIR_STRUC.rec_len], cx
add esp, 4
jmp .write_block
 
@@:
mov edx, ebx
movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len]
; If it's a zero length entry, error.
test ecx, ecx
jz .fail_inode
 
add ebx, ecx
 
cmp ebx, [esp]
jb .dir_entry
 
add esp, 4
inc dword[esp]
jmp .loop
 
.write_block:
mov eax, edi
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .fail_loop
 
add esp, 4
mov ebx, [ebp + EXTFS.ext2_temp_inode]
mov eax, [esp]
call ext2_inode_read
test eax, eax
jnz .fail_get_inode
 
dec word[ebx + EXT2_INODE_STRUC.i_links_count]
movzx eax, word[ebx + EXT2_INODE_STRUC.i_links_count]
push eax
 
mov eax, [esp + 4]
call ext2_inode_write
test eax, eax
jnz .fail_loop
 
pop eax
add esp, 4
.return:
pop edi esi edx ecx ebx
ret
 
.fail_inode:
add esp, 4
 
.fail_loop:
add esp, 4
 
.fail_get_inode:
add esp, 4
 
.fail:
xor eax, eax
not eax
jmp .return
 
;---------------------------------------------------------------------
; Checks if a directory is empty.
; Input: ebx = inode to check.
; ebp = pointer to EXTFS.
; [EXTFS.ext2_save_inode] = points to saved inode.
; Output: eax = 0 signifies empty directory.
;---------------------------------------------------------------------
ext2_dir_empty:
push ebx ecx edx
; The index into the inode block data.
push dword 0
mov esi, [ebp + EXTFS.ext2_save_inode]
.loop:
mov ecx, [esp]
call ext2_inode_get_block
 
; Treat a failure as not-empty.
test eax, eax
jnz .not_empty
test ecx, ecx
jz .empty
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_read
test eax, eax
jnz .not_empty
 
mov edx, ebx
add edx, [ebp + EXTFS.block_size]
 
movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len]
add ebx, ecx
 
.dir_entry:
; Process entry.
cmp byte[ebx + EXT2_DIR_STRUC.name_len], 1
jne @F
 
cmp byte[ebx + EXT2_DIR_STRUC.name], '.'
jne .not_empty
 
@@:
cmp byte[ebx + EXT2_DIR_STRUC.name_len], 2
jne .not_empty
 
cmp word[ebx + EXT2_DIR_STRUC.name], '..'
jne .not_empty
 
@@:
movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len]
add ebx, ecx
 
cmp ebx, edx
jb .dir_entry
 
inc dword[esp]
jmp .loop
 
.empty:
xor eax, eax
.return:
add esp, 4
pop edx ecx ebx
ret
 
.not_empty:
xor eax, eax
not eax
jmp .return
 
;---------------------------------------------------------------------
; Gets the block group's inode bitmap.
; Input: eax = block group.
; Output: eax = if zero, error; else, points to block group descriptor.
; ebx = inode bitmap's block (hard disk).
;---------------------------------------------------------------------
ext2_bg_read_inode_bitmap:
push ecx
 
call ext2_bg_read_desc
test eax, eax
jz .fail
 
mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.inode_bitmap] ; Block number of inode bitmap - in ext2 terms.
 
.return:
pop ecx
ret
 
.fail:
xor eax, eax
jmp .return
 
;---------------------------------------------------------------------
; Allocates a inode.
; Input: eax = inode ID for "preference".
; ebp = pointer to EXTFS.
; Output: Inode marked as set in inode group.
; eax = error code.
; ebx = inode ID.
;---------------------------------------------------------------------
ext2_inode_alloc:
push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_count]
push EXT2_BLOCK_GROUP_DESC.free_inodes_count
push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group]
 
lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_inodes_count]
push ebx
 
push ext2_bg_read_inode_bitmap
 
call ext2_resource_alloc
 
; Inode table starts with 1.
inc ebx
 
ret
 
;---------------------------------------------------------------------
; Frees a inode.
; Input: eax = inode ID.
; ebp = pointer to EXTFS.
; Output: inode marked as free in block group.
; eax = error code.
;---------------------------------------------------------------------
ext2_inode_free:
push edi ecx
 
; Inode table starts with 1.
dec eax
 
mov edi, ext2_bg_read_inode_bitmap
xor ecx, ecx
inc cl
call ext2_resource_free
 
pop ecx edi
ret
 
;---------------------------------------------------------------------
; Blanks a particular entry in an inode.
; Input: eax = index into block.
; edx = inode.
; ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_temp_inode] = the inode.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_blank_entry:
push ebx ecx edx edi esi
 
mov edi, eax
 
mov ecx, eax
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_get_block
test eax, eax
jnz .error
 
test ecx, ecx
jz .allocate
 
mov edx, ecx
mov ecx, [ebp + EXTFS.block_size]
mov edi, [ebp + EXTFS.ext2_temp_block]
xor eax, eax
rep stosb
 
mov eax, edx
mov ebx, [ebp + EXTFS.ext2_temp_block]
call ext2_block_write
test eax, eax
jnz .error
 
jmp .success
 
; Need to allocate a block.
.allocate:
mov eax, edx
call ext2_block_calloc
test eax, eax
jnz .error
 
mov ecx, edi
mov edi, ebx
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_set_block
test eax, eax
jnz .error
 
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
mov eax, 2
shl eax, cl
add [esi + EXT2_INODE_STRUC.i_blocks], eax
 
.success:
xor eax, eax
 
.ret:
pop esi edi edx ecx ebx
ret
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Frees a particular entry in an inode.
; Input: eax = index into block.
; ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_temp_inode] = the inode.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_free_entry:
push ebx ecx edi esi
 
mov edi, eax
 
mov ecx, eax
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_get_block
test eax, eax
jnz .error
 
test ecx, ecx
jz .success
 
mov eax, ecx
call ext2_block_free
test eax, eax
jnz .error
 
mov ecx, edi
xor edi, edi
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_set_block
 
mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size]
mov eax, 2
shl eax, cl
sub [esi + EXT2_INODE_STRUC.i_blocks], eax
 
.success:
xor eax, eax
 
.ret:
pop esi edi ecx ebx
ret
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Reads a particular entry from an inode.
; Input: eax = index into block.
; ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_temp_inode] = the inode.
; Output: eax = error code.
; [ebp + EXTFS.ext2_save_block] = the read block.
;---------------------------------------------------------------------
ext2_inode_read_entry:
push ebx ecx edx esi
 
mov ecx, eax
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_get_block
test eax, eax
jnz .error
 
test ecx, ecx
jz .error
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_read
test eax, eax
jnz .error
 
.ret:
pop esi edx ecx ebx
ret
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Writes a particular entry from an inode.
; Input: eax = index into block.
; ebp = pointer to EXTFS.
; [ebp + EXTFS.ext2_temp_inode] = the inode.
; [ebp + EXTFS.ext2_save_block] = the block to write.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_write_entry:
push ebx ecx edx esi
 
mov ecx, eax
mov esi, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_get_block
test eax, eax
jnz .error
 
test ecx, ecx
jz .error
 
mov eax, ecx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_write
test eax, eax
jnz .error
 
.ret:
pop esi edx ecx ebx
ret
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Extends inode to said size.
; Input: eax = inode ID.
; ecx = size to extend to.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_extend:
push ebx ecx edx esi edi
 
; Save the inode.
push eax
 
; Read the inode.
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_read
test eax, eax
jnz .error
 
mov eax, [ebx + EXT2_INODE_STRUC.i_size]
cmp eax, ecx
jge .success
 
; Save the size of the inode.
push eax
 
; ECX contains the size we've to write.
sub ecx, eax
xor edx, edx
div [ebp + EXTFS.block_size]
 
test edx, edx
jz .start_aligned
 
; Start isn't aligned, so deal with the non-aligned bytes.
mov esi, [ebp + EXTFS.block_size]
sub esi, edx
 
cmp esi, ecx
jbe @F
 
; If the size to entend to fits in current block, limit to that.
mov esi, ecx
 
@@:
; Clear ESI bytes, in EAX indexed block.
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jnz .error_inode_size
 
push eax ecx
xor eax, eax
mov ecx, esi
mov edi, ebx
add edi, edx
 
rep stosb
 
pop ecx eax
 
; Write the block.
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
add [esp], esi
sub ecx, esi
jz .write_inode
 
.start_aligned:
cmp ecx, [ebp + EXTFS.block_size]
jb @F
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
 
mov edx, [esp + 4]
call ext2_inode_blank_entry
 
test eax, eax
jnz .error_inode_size
 
mov eax, [ebp + EXTFS.block_size]
sub ecx, eax
add [esp], eax
jmp .start_aligned
 
; Handle the remaining bytes.
@@:
test ecx, ecx
jz .write_inode
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
 
mov edx, [esp + 4]
call ext2_inode_blank_entry
 
test eax, eax
jnz .error_inode_size
add [esp], ecx
 
.write_inode:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
 
test eax, eax
jnz .error
 
.success:
xor eax, eax
 
.ret:
add esp, 4
 
pop edi esi edx ecx ebx
ret
 
.error_inode_size:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
 
.error:
xor eax, eax
not eax
jmp .ret
 
;---------------------------------------------------------------------
; Truncates inode to said size.
; Input: eax = inode ID.
; ecx = size to truncate to.
; ebp = pointer to EXTFS.
; Output: eax = error code.
;---------------------------------------------------------------------
ext2_inode_truncate:
push ebx ecx edx esi edi
 
; Save the inode.
push eax
 
; Read the inode.
mov ebx, [ebp + EXTFS.ext2_temp_inode]
call ext2_inode_read
test eax, eax
jnz .error
 
mov eax, [ebx + EXT2_INODE_STRUC.i_size]
cmp ecx, eax
jge .success
 
; Save the size of the inode.
push eax
 
; ECX contains the size we've to truncate.
sub ecx, eax
not ecx
inc ecx
xor edx, edx
div [ebp + EXTFS.block_size]
 
test edx, edx
jz .start_aligned
 
; Start isn't aligned, so deal with the non-aligned bytes.
mov esi, edx
 
cmp esi, ecx
jbe @F
 
; If the size to truncate is smaller than the un-aligned bytes
; we're going to have to mark neccessary bytes from the EOF
; as 0.
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jnz .error_inode_size
 
mov edi, [ebp + EXTFS.ext2_save_block]
sub edx, ecx
add edi, edx
 
push ecx eax
xor eax, eax
rep stosb
pop eax ecx
 
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
 
sub [esp], ecx
jmp .write_inode
 
@@:
; Since ECX is greater than or equal to the bytes here un-aligned
; just free the block.
call ext2_inode_free_entry
 
sub [esp], esi
sub ecx, esi
jz .write_inode
 
.start_aligned:
cmp ecx, [ebp + EXTFS.block_size]
jb @F
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
dec eax
 
call ext2_inode_free_entry
 
test eax, eax
jnz .error_inode_size
 
mov eax, [ebp + EXTFS.block_size]
sub ecx, eax
sub [esp], eax
jmp .start_aligned
 
; Handle the remaining bytes.
@@:
test ecx, ecx
jz .write_inode
 
mov eax, [esp]
xor edx, edx
div [ebp + EXTFS.block_size]
dec eax
 
push eax
call ext2_inode_read_entry
test eax, eax
pop eax
jnz .error_inode_size
 
mov edi, [ebp + EXTFS.ext2_save_block]
mov edx, [ebp + EXTFS.block_size]
sub edx, ecx
add edi, edx
 
push ecx eax
xor eax, eax
rep stosb
pop eax ecx
 
call ext2_inode_write_entry
test eax, eax
jnz .error_inode_size
 
sub [esp], ecx
 
.write_inode:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
 
test eax, eax
jnz .error
 
.success:
xor eax, eax
 
.ret:
add esp, 4
 
pop edi esi edx ecx ebx
ret
 
.error_inode_size:
mov ebx, [ebp + EXTFS.ext2_temp_inode]
pop eax
mov [ebx + EXT2_INODE_STRUC.i_size], eax
mov eax, [esp]
call ext2_inode_write
 
.error:
xor eax, eax
not eax
jmp .ret
/kernel/branches/kolibri-process/fs/ext2/resource.inc
0,0 → 1,223
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Contains common resource allocation + freeing code. ;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under the terms of the new BSD license. ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;---------------------------------------------------------------------
; Frees a resource (block/inode).
; Input: eax = resource ID.
; edi = function pointer of ext2_bg_*_bitmap form, to
; get bitmap of resource.
; ecx = 0, block; 1, inode.
; ebp = pointer to EXTFS.
; Output: Block marked as free in block group.
; eax = error code.
;---------------------------------------------------------------------
ext2_resource_free:
push ebx edx esi
 
; Get block group.
sub eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block]
xor edx, edx
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group]
push eax edx
 
call edi
test eax, eax
jz .fail
mov esi, eax
 
; Read the bitmap.
mov eax, ebx
mov edx, eax
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_read
test eax, eax
jnz .fail
 
pop eax
; Mark bit free.
call bitmap_clear_bit
test eax, eax
jz @F
 
; No need to save anything.
xor eax, eax
 
add esp, 4
jmp .return
 
@@:
mov eax, edx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_write
test eax, eax
jnz .fail
 
; Read the descriptor.
mov eax, [esp]
call ext2_bg_read_desc
test eax, eax
jz .fail_bg_desc_read
 
lea eax, [eax + EXT2_BLOCK_GROUP_DESC.free_blocks_count]
shl ecx, 1
add eax, ecx
inc word[eax]
 
lea eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count]
shl ecx, 1
add eax, ecx
inc dword[eax]
 
pop eax
call ext2_bg_write_desc
 
.return:
pop esi edx ebx
ret
 
.fail:
add esp, 4
.fail_bg_desc_read:
add esp, 4
xor eax, eax
not eax
jmp .return
 
;---------------------------------------------------------------------
; Allocates a resource.
; Input: eax = inode ID for "preference".
; ebp = pointer to EXTFS.
; [esp + 4], func pointer to ext2_bg_*_bitmap
; [esp + 8], pointer to free_*_count in SB.
; [esp + 12], *_per_group
; [esp + 16], offset to free_*_count in bg descriptor.
; [esp + 20], *_count
; Output: Resource marked as set in block group.
; eax = error code.
; ebx = resource ID.
;---------------------------------------------------------------------
ext2_resource_alloc:
; Block allocation is a pretty serious area, since bad allocation
; can lead to fragmentation. Thus, the best way to allocate that
; comes to mind is to allocate around an inode as much as possible.
; On the other hand, this isn't about a single inode/file/directory,
; and focusing just around the preferred inode would lead to
; congestion. Thus, after much thought, the chosen allocation algorithm
; is to search forward, then backward.
push ecx edx esi edi
 
cmp dword[esp + 16 + 8], 0
jnz @F
 
; No free blocks.
xor eax, eax
not eax
pop edi esi edx ecx
ret 20
 
@@:
; Calculate which block group the preferred inode belongs to.
dec eax
xor edx, edx
; EAX = block group.
div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group]
push eax
push eax
 
mov edi, .forward
 
.test_block_group:
call dword[esp + 16 + 8 + 4]
test eax, eax
jz .fail
mov esi, eax
 
mov eax, ebx
mov edx, eax
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_read
test eax, eax
jnz .fail
 
mov ecx, [esp + 16 + 8 + 12]
call ext2_find_free_bit
cmp eax, 0xFFFFFFFF
jne @F
 
mov eax, edi
jmp eax
 
@@:
mov ecx, eax
 
mov eax, edx
mov ebx, [ebp + EXTFS.ext2_save_block]
call ext2_block_write
test eax, eax
jnz .fail
 
; ecx: the index of the matched entry.
; [esp]: block group where we found.
; [esp + 4]: starting block group.
; esi: block group descriptor.
mov eax, [esp] ; Index of block group in which we found.
mul dword[esp + 16 + 8 + 12]
add eax, ecx
mov ebx, eax
 
mov eax, [esp + 16 + 8 + 8]
dec dword[eax]
 
mov eax, esi
add eax, [esp + 16 + 8 + 16]
dec word[eax]
 
pop eax
call ext2_bg_write_desc
 
add esp, 4
jmp .return
 
; Continue forward.
.forward:
inc dword[esp]
mov eax, [esp]
mul dword[esp + 16 + 8 + 12]
cmp eax, [esp + 16 + 8 + 20]
jbe @F
 
; We need to go backward.
mov eax, [esp + 4]
mov [esp], eax
mov edi, .backward
jmp .backward
 
@@:
mov eax, [esp]
jmp .test_block_group
 
; Continue backward.
.backward:
cmp dword[esp], 0
je .fail
 
dec dword[esp]
mov eax, [esp]
jmp .test_block_group
 
.return:
pop edi esi edx ecx
ret 20
 
.fail:
add esp, 8
xor eax, eax
not eax
jmp .return
/kernel/branches/kolibri-process/fs/fat.inc
0,0 → 1,3705
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;; FAT32.INC ;;
;; ;;
;; FAT functions for KolibriOS ;;
;; ;;
;; Copyright 2002 Paolo Minazzi, paolo.minazzi@inwind.it ;;
;; ;;
;; See file COPYING for details ;;
;; 04.02.2007 LFN create folder - diamond ;;
;; 08.10.2006 LFN delete file/folder - diamond ;;
;; 20.08.2006 LFN set file size (truncate/extend) - diamond ;;
;; 17.08.2006 LFN write/append to file - diamond ;;
;; 23.06.2006 LFN start application - diamond ;;
;; 15.06.2006 LFN get/set file/folder info - diamond ;;
;; 27.05.2006 LFN create/rewrite file - diamond ;;
;; 04.05.2006 LFN read folder - diamond ;;
;; 29.04.2006 Elimination of hangup after the ;;
;; expiration hd_wait_timeout - Mario79 ;;
;; 23.04.2006 LFN read file - diamond ;;
;; 28.01.2006 find all Fat16/32 partition in all input point ;;
;; to MBR, see file part_set.inc - Mario79 ;;
;; 15.01.2005 get file size/attr/date, file_append - ATV ;;
;; 04.12.2004 skip volume label, file delete bug fixed - ATV ;;
;; 29.11.2004 get_free_FAT changed, append dir bug fixed - ATV ;;
;; 23.11.2004 don't allow overwrite dir with file - ATV ;;
;; 18.11.2004 get_disk_info and more error codes - ATV ;;
;; 17.11.2004 set_FAT/get_FAT and disk cache rewritten - ATV ;;
;; 10.11.2004 removedir clear whole directory structure - ATV ;;
;; 08.11.2004 rename - ATV ;;
;; 30.10.2004 file_read return also dirsize in bytes - ATV ;;
;; 20.10.2004 Makedir/Removedir - ATV ;;
;; 14.10.2004 Partition chain/Fat16 - ATV (thanks drh3xx) ;;
;; 06.9.2004 Fix free space by Mario79 added - MH ;;
;; 24.5.2004 Write back buffer for File_write -VT ;;
;; 20.5.2004 File_read function to work with syscall 58 - VT ;;
;; 30.3.2004 Error parameters at function return - VT ;;
;; 01.5.2002 Bugfix in device write - VT ;;
;; 20.5.2002 Hd status check - VT ;;
;; 29.6.2002 Improved fat32 verification - VT ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 4273 $
 
 
cache_max equ 1919 ; max. is 1919*512+0x610000=0x6ffe00
 
PUSHAD_EAX equ [esp+28]
PUSHAD_ECX equ [esp+24]
PUSHAD_EDX equ [esp+20]
PUSHAD_EBX equ [esp+16]
PUSHAD_EBP equ [esp+8]
PUSHAD_ESI equ [esp+4]
PUSHAD_EDI equ [esp+0]
 
; Internal data for every FAT partition.
struct FAT PARTITION
fs_type db ?
fat16_root db 0 ; flag for fat16 rootdir
fat_change db 0 ; 1=fat has changed
db ? ; alignment
Lock MUTEX ? ; currently operations with one partition
; can not be executed in parallel since the
; legacy code is not ready; this mutex guards
; all operations
SECTORS_PER_FAT dd 0x1f3a
NUMBER_OF_FATS dd 0x2
SECTORS_PER_CLUSTER dd 0x8
BYTES_PER_SECTOR dd 0x200 ; Note: if BPS <> 512 need lots of changes
ROOT_CLUSTER dd 2 ; first rootdir cluster
FAT_START dd 0 ; start of fat table
ROOT_START dd 0 ; start of rootdir (only fat16)
ROOT_SECTORS dd 0 ; count of rootdir sectors (only fat16)
DATA_START dd 0 ; start of data area (=first cluster 2)
LAST_CLUSTER dd 0 ; last availabe cluster
ADR_FSINFO dd 0 ; used only by fat32
 
fatRESERVED dd 0x0FFFFFF6
fatBAD dd 0x0FFFFFF7
fatEND dd 0x0FFFFFF8
fatMASK dd 0x0FFFFFFF
 
fatStartScan dd 2
 
cluster_tmp dd 0 ; used by analyze_directory
; and analyze_directory_to_write
 
longname_sec1 dd 0 ; used by analyze_directory to save 2 previous
longname_sec2 dd 0 ; directory sectors for delete long filename
 
fat_in_cache dd -1
 
; For FAT16/FAT32, this points to 512-byte buffer for the current sector of FAT.
; For FAT12, the entire FAT structure is read
; and unpacked from 12bit per cluster to word per cluster.
;
; Note: work with unpacked copy of FAT12 means
; additional memory and additional code for packing/unpacking.
; I'm not sure that the economy justifies the cost, but anyway,
; there is how work was done before my edits, and I'm just keeping the principle.
fat_cache_ptr dd ?
fat12_unpacked_ptr dd ?
buffer rb 512
fsinfo_buffer rb 512
ends
 
uglobal
align 4
partition_count dd 0 ; partitions found by set_FAT32_variables
 
hd_error dd 0 ; set by wait_for_sector_buffer
hd_setup dd 0
hd_wait_timeout dd 0
 
cache_search_start dd 0 ; used by find_empty_slot
endg
 
uglobal
align 4
Sector512: ; label for dev_hdcd.inc
buffer:
times 512 db 0
endg
 
iglobal
align 4
fat_user_functions:
dd fat_free
dd (fat_user_functions_end - fat_user_functions - 4) / 4
dd fat_Read
dd fat_ReadFolder
dd fat_Rewrite
dd fat_Write
dd fat_SetFileEnd
dd fat_GetFileInfo
dd fat_SetFileInfo
dd 0
dd fat_Delete
dd fat_CreateFolder
fat_user_functions_end:
endg
 
; these labels are located before the main function to make
; most of jumps to these be short
fat_create_partition.free_return0:
mov eax, ebp
call free
pop ebp
fat_create_partition.return0:
xor eax, eax
ret
fat_create_partition:
; bootsector must have been successfully read
cmp dword [esp+4], 0
jnz .return0
; bootsector signature must be correct
cmp word [ebx+0x1fe], 0xaa55
jnz .return0
; sectors per cluster must be nonzero
cmp byte [ebx+0xd], 0
jz .return0
; bytes per sector must be 0x200
cmp word [ebx+0xb], 0x200
jnz .return0
; number of fats must be nonzero
cmp byte [ebx+0x10], 0
jz .return0
; The only reason to be invalid partition now is FAT12. Since the test for
; FAT size requires knowledge of some calculated values, which are also used
; in the normal operation, let's hope for the best and allocate data now; if
; it will prove wrong, just deallocate it.
movi eax, sizeof.FAT
call malloc
test eax, eax
jz .return0
mov ecx, dword [ebp+PARTITION.FirstSector]
mov dword [eax+FAT.FirstSector], ecx
mov ecx, dword [ebp+PARTITION.FirstSector+4]
mov dword [eax+FAT.FirstSector+4], ecx
mov ecx, dword [ebp+PARTITION.Length]
mov dword [eax+FAT.Length], ecx
mov ecx, dword [ebp+PARTITION.Length+4]
mov dword [eax+FAT.Length+4], ecx
mov ecx, [ebp+PARTITION.Disk]
mov [eax+FAT.Disk], ecx
mov [eax+FAT.FSUserFunctions], fat_user_functions
or [eax+FAT.fat_in_cache], -1
mov [eax+FAT.fat_change], 0
push ebp
mov ebp, eax
 
lea ecx, [ebp+FAT.Lock]
call mutex_init
 
movzx eax, word [ebx+0xe] ; sectors reserved
mov [ebp+FAT.FAT_START], eax
 
movzx eax, byte [ebx+0xd] ; sectors per cluster
mov [ebp+FAT.SECTORS_PER_CLUSTER], eax
 
movzx ecx, word [ebx+0xb] ; bytes per sector
mov [ebp+FAT.BYTES_PER_SECTOR], ecx
 
movzx eax, word [ebx+0x11] ; count of rootdir entries (=0 fat32)
shl eax, 5 ; mul 32
dec ecx
add eax, ecx ; round up if not equal count
inc ecx ; bytes per sector
xor edx, edx
div ecx
mov [ebp+FAT.ROOT_SECTORS], eax ; count of rootdir sectors
 
movzx eax, word [ebx+0x16] ; sectors per fat <65536
test eax, eax
jnz @f
mov eax, [ebx+0x24] ; sectors per fat
@@:
mov [ebp+FAT.SECTORS_PER_FAT], eax
 
movzx eax, byte [ebx+0x10] ; number of fats
mov [ebp+FAT.NUMBER_OF_FATS], eax
mul [ebp+FAT.SECTORS_PER_FAT]
test edx, edx
jnz .free_return0
add eax, [ebp+FAT.FAT_START]
jc .free_return0
mov [ebp+FAT.ROOT_START], eax ; rootdir = fat_start + fat_size * fat_count
add eax, [ebp+FAT.ROOT_SECTORS] ; rootdir sectors should be 0 on fat32
jc .free_return0
mov [ebp+FAT.DATA_START], eax ; data area = rootdir + rootdir_size
 
movzx eax, word [ebx+0x13] ; total sector count <65536
test eax, eax
jnz @f
mov eax, [ebx+0x20] ; total sector count
@@:
; total sector count must not exceed partition size
cmp dword [ebp+FAT.Length+4], 0
jnz @f
cmp eax, dword [ebp+FAT.Length]
ja .free_return0
@@:
mov dword [ebp+FAT.Length], eax
and dword [ebp+FAT.Length+4], 0
sub eax, [ebp+FAT.DATA_START] ; eax = count of data sectors
jc .free_return0
xor edx, edx
div [ebp+FAT.SECTORS_PER_CLUSTER]
inc eax
mov [ebp+FAT.LAST_CLUSTER], eax
dec eax ; cluster count
jz .free_return0
mov [ebp+FAT.fatStartScan], 2
 
; limits by Microsoft Hardware White Paper v1.03
cmp eax, 4085 ; 0xff5
jb .fat12
cmp eax, 65525 ; 0xfff5
jb .fat16
.fat32:
mov eax, [ebx+0x2c] ; rootdir cluster
mov [ebp+FAT.ROOT_CLUSTER], eax
movzx eax, word [ebx+0x30]
mov [ebp+FAT.ADR_FSINFO], eax
push ebx
add ebx, 512
call fs_read32_sys
test eax, eax
jnz @f
mov eax, [ebx+0x1ec]
cmp eax, -1
jz @f
mov [ebp+FAT.fatStartScan], eax
@@:
pop ebx
mov [ebp+FAT.fatRESERVED], 0x0FFFFFF6
mov [ebp+FAT.fatBAD], 0x0FFFFFF7
mov [ebp+FAT.fatEND], 0x0FFFFFF8
mov [ebp+FAT.fatMASK], 0x0FFFFFFF
mov al, 32
.fat_not_12_finalize:
mov [ebp+FAT.fs_type], al
; For FAT16 and FAT32, allocate 512 bytes for FAT cache.
mov eax, 512
call malloc
test eax, eax
jz .free_return0
mov [ebp+FAT.fat_cache_ptr], eax
mov eax, ebp
pop ebp
ret
.fat16:
and [ebp+FAT.ROOT_CLUSTER], 0
mov [ebp+FAT.fatRESERVED], 0x0000FFF6
mov [ebp+FAT.fatBAD], 0x0000FFF7
mov [ebp+FAT.fatEND], 0x0000FFF8
mov [ebp+FAT.fatMASK], 0x0000FFFF
mov al, 16
jmp .fat_not_12_finalize
.fat12:
and [ebp+FAT.ROOT_CLUSTER], 0
mov [ebp+FAT.fatRESERVED], 0xFF6
mov [ebp+FAT.fatBAD], 0xFF7
mov [ebp+FAT.fatEND], 0xFFF
mov [ebp+FAT.fatMASK], 0xFFF
mov al, 12
mov [ebp+FAT.fs_type], al
; For FAT12, allocate&read data for entire table:
; calculate A = ALIGN_UP(NUM_CLUSTERS, 8),
; calculatefatchain/restorefatchain will process A items,
; allocate ALIGN_UP(A*3/2, 512) bytes for FAT table plus A*2 bytes for unpacked data.
mov eax, [ebp+FAT.LAST_CLUSTER]
and eax, not 7
add eax, 8
mov edx, eax
lea eax, [eax*3]
add eax, 512*2-1
shr eax, 10
shl eax, 9
lea eax, [eax+edx*2]
call malloc
test eax, eax
jz .free_return0
; Read ALIGN_UP(NUM_CLUSTERS*3/2, 512) bytes.
; Note that this can be less than allocated, this is ok,
; overallocation simplifies calculatefatchain/restorefatchain.
push ebx
mov [ebp+FAT.fat_cache_ptr], eax
mov edx, [ebp+FAT.LAST_CLUSTER]
lea edx, [(edx+1)*3 + 512*2-1]
shr edx, 10
xchg eax, ebx
xor eax, eax
.read_fat:
push eax
add eax, [ebp+FAT.FAT_START]
call fs_read32_sys
test eax, eax
pop eax
jz @f
dbgstr 'Failed to read FAT table'
mov eax, [ebp+FAT.fat_cache_ptr]
call free
pop ebx
jmp .free_return0
@@:
add ebx, 512
inc eax
cmp eax, edx
jb .read_fat
mov [ebp+FAT.fat12_unpacked_ptr], ebx
call calculatefatchain
pop ebx
mov eax, ebp
pop ebp
ret
 
fat_free:
push eax
mov eax, [eax+FAT.fat_cache_ptr]
call free
pop eax
jmp free
 
calculatefatchain:
 
pushad
 
mov esi, [ebp+FAT.fat_cache_ptr]
mov edi, [ebp+FAT.fat12_unpacked_ptr]
 
mov edx, [ebp+FAT.LAST_CLUSTER]
and edx, not 7
lea edx, [edi+(edx+8)*2]
push edx
 
fcnew:
mov eax, dword [esi]
mov ebx, dword [esi+4]
mov ecx, dword [esi+8]
mov edx, ecx
shr edx, 4;8 ok
shr dx, 4;7 ok
xor ch, ch
shld ecx, ebx, 20;6 ok
shr cx, 4;5 ok
shld ebx, eax, 12
and ebx, 0x0fffffff;4 ok
shr bx, 4;3 ok
shl eax, 4
and eax, 0x0fffffff;2 ok
shr ax, 4;1 ok
mov dword [edi], eax
mov dword [edi+4], ebx
mov dword [edi+8], ecx
mov dword [edi+12], edx
add edi, 16
add esi, 12
 
cmp edi, [esp]
jnz fcnew
pop eax
 
popad
ret
 
 
restorefatchain: ; restore fat chain
 
pushad
 
mov esi, [ebp+FAT.fat12_unpacked_ptr]
mov edi, [ebp+FAT.fat_cache_ptr]
 
mov edx, [ebp+FAT.LAST_CLUSTER]
and edx, not 7
lea edx, [esi+(edx+8)*2]
 
fcnew2:
mov eax, dword [esi]
mov ebx, dword [esi+4]
shl ax, 4
shl eax, 4
shl bx, 4
shr ebx, 4
shrd eax, ebx, 8
shr ebx, 8
mov dword [edi], eax
mov word [edi+4], bx
add edi, 6
add esi, 8
 
cmp esi, edx
jb fcnew2
 
mov esi, [ebp+FAT.NUMBER_OF_FATS]
mov edx, [ebp+FAT.LAST_CLUSTER]
lea edx, [(edx+1)*3 + 512*2-1]
shr edx, 10
push [ebp+FAT.FAT_START]
 
.write_fats:
xor eax, eax
mov ebx, [ebp+FAT.fat_cache_ptr]
.loop1:
push eax
add eax, [esp+4]
call fs_write32_sys
test eax, eax
pop eax
jnz .fail
add ebx, 512
inc eax
cmp eax, edx
jb .loop1
pop eax
add eax, [ebp+FAT.SECTORS_PER_FAT]
push eax
dec esi
jnz .write_fats
pop eax
 
popad
ret
.fail:
dbgstr 'Failed to save FAT'
popad
ret
 
iglobal
label fat_legal_chars byte
; 0 = not allowed
; 1 = allowed only in long names
; 3 = allowed
times 32 db 0
; ! " # $ % & ' ( ) * + , - . /
db 1,3,0,3,3,3,3,3,3,3,0,1,1,3,3,0
; 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
db 3,3,3,3,3,3,3,3,3,3,0,1,0,1,0,0
; @ A B C D E F G H I J K L M N O
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; P Q R S T U V W X Y Z [ \ ] ^ _
db 3,3,3,3,3,3,3,3,3,3,3,1,0,1,3,3
; ` a b c d e f g h i j k l m n o
db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
; p q r s t u v w x y z { | } ~
db 3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0
endg
 
fat_name_is_legal:
; in: esi->(long) name
; out: CF set <=> legal
; destroys eax
push esi
xor eax, eax
@@:
lodsb
test al, al
jz .done
cmp al, 80h
jae .big
test [fat_legal_chars+eax], 1
jnz @b
.err:
pop esi
clc
ret
.big:
; 0x80-0xAF, 0xE0-0xEF
cmp al, 0xB0
jb @b
cmp al, 0xE0
jb .err
cmp al, 0xF0
jb @b
jmp .err
.done:
sub esi, [esp]
cmp esi, 257
pop esi
ret
 
fat_next_short_name:
; in: edi->8+3 name
; out: name corrected
; CF=1 <=> error
pushad
mov ecx, 8
mov al, '~'
std
push edi
add edi, 7
repnz scasb
pop edi
cld
jz .tilde
; tilde is not found, insert "~1" at end
add edi, 6
cmp word [edi], ' '
jnz .insert_tilde
@@:
dec edi
cmp byte [edi], ' '
jz @b
inc edi
.insert_tilde:
mov word [edi], '~1'
popad
clc
ret
.tilde:
push edi
add edi, 7
xor ecx, ecx
@@:
; after tilde may be only digits and trailing spaces
cmp byte [edi], '~'
jz .break
cmp byte [edi], ' '
jz .space
cmp byte [edi], '9'
jnz .found
dec edi
jmp @b
.space:
dec edi
inc ecx
jmp @b
.found:
inc byte [edi]
add dword [esp], 8
jmp .zerorest
.break:
jecxz .noplace
inc edi
mov al, '1'
@@:
xchg al, [edi]
inc edi
cmp al, ' '
mov al, '0'
jnz @b
.succ:
pop edi
popad
clc
ret
.noplace:
dec edi
cmp edi, [esp]
jz .err
add dword [esp], 8
mov word [edi], '~1'
inc edi
inc edi
@@:
mov byte [edi], '0'
.zerorest:
inc edi
cmp edi, [esp]
jb @b
pop edi
popad
;clc ; automatically
ret
.err:
pop edi
popad
stc
ret
 
fat_gen_short_name:
; in: esi->long name
; edi->buffer (8+3=11 chars)
; out: buffer filled
pushad
mov eax, ' '
push edi
stosd
stosd
stosd
pop edi
xor eax, eax
movi ebx, 8
lea ecx, [edi+8]
.loop:
lodsb
test al, al
jz .done
call char_toupper
cmp al, ' '
jz .space
cmp al, 80h
ja .big
test [fat_legal_chars+eax], 2
jnz .symbol
.inv_symbol:
mov al, '_'
or bh, 1
.symbol:
cmp al, '.'
jz .dot
.normal_symbol:
dec bl
jns .store
mov bl, 0
.space:
or bh, 1
jmp .loop
.store:
stosb
jmp .loop
.big:
cmp al, 0xB0
jb .normal_symbol
cmp al, 0xE0
jb .inv_symbol
cmp al, 0xF0
jb .normal_symbol
jmp .inv_symbol
.dot:
test bh, 2
jz .firstdot
pop ebx
add ebx, edi
sub ebx, ecx
push ebx
cmp ebx, ecx
jb @f
pop ebx
push ecx
@@:
cmp edi, ecx
jbe .skip
@@:
dec edi
mov al, [edi]
dec ebx
mov [ebx], al
mov byte [edi], ' '
cmp edi, ecx
ja @b
.skip:
mov bh, 3
jmp @f
.firstdot:
cmp bl, 8
jz .space
push edi
or bh, 2
@@:
mov edi, ecx
mov bl, 3
jmp .loop
.done:
test bh, 2
jz @f
pop edi
@@:
lea edi, [ecx-8]
test bh, 1
jz @f
call fat_next_short_name
@@:
popad
ret
 
fat12_free_space:
;---------------------------------------------
;
; returns free space in edi
; rewr.by Mihasik
;---------------------------------------------
 
push eax ebx ecx
 
mov edi, [ebp+FAT.fat12_unpacked_ptr];start of FAT
xor ax, ax;Free cluster=0x0000 in FAT
xor ebx, ebx;counter
mov ecx, [ebp+FAT.LAST_CLUSTER]
inc ecx
cld
rdfs1:
repne scasw
jnz rdfs2 ;if last cluster not 0
inc ebx
test ecx, ecx
jnz rdfs1
rdfs2:
shl ebx, 9;free clusters*512
mov edi, ebx
 
pop ecx ebx eax
ret
 
 
 
set_FAT:
;--------------------------------
; input : EAX = cluster
; EDX = value to save
; EBP = pointer to FAT structure
; output : EDX = old value
;--------------------------------
; out: CF set <=> error
push eax ebx esi
 
cmp eax, 2
jb sfc_error
cmp eax, [ebp+FAT.LAST_CLUSTER]
ja sfc_error
cmp [ebp+FAT.fs_type], 12
je set_FAT12
cmp [ebp+FAT.fs_type], 16
je sfc_1
add eax, eax
sfc_1:
add eax, eax
mov esi, 511
and esi, eax ; esi = position in fat sector
shr eax, 9 ; eax = fat sector
add eax, [ebp+FAT.FAT_START]
mov ebx, [ebp+FAT.fat_cache_ptr]
 
cmp eax, [ebp+FAT.fat_in_cache]; is fat sector already in memory?
je sfc_in_cache ; yes
 
cmp [ebp+FAT.fat_change], 0; is fat changed?
je sfc_no_change ; no
call write_fat_sector; yes. write it into disk
jc sfc_error
 
sfc_no_change:
mov [ebp+FAT.fat_in_cache], eax; save fat sector
call fs_read32_sys
test eax, eax
jne sfc_error
 
 
sfc_in_cache:
cmp [ebp+FAT.fs_type], 16
jne sfc_test32
 
sfc_set16:
xchg [ebx+esi], dx ; save new value and get old value
jmp sfc_write
 
sfc_test32:
mov eax, [ebp+FAT.fatMASK]
 
sfc_set32:
and edx, eax
xor eax, -1 ; mask for high bits
and eax, [ebx+esi] ; get high 4 bits
or eax, edx
mov edx, [ebx+esi] ; get old value
mov [ebx+esi], eax ; save new value
 
sfc_write:
mov [ebp+FAT.fat_change], 1; fat has changed
 
sfc_nonzero:
and edx, [ebp+FAT.fatMASK]
 
sfc_return:
pop esi ebx eax
ret
sfc_error:
stc
jmp sfc_return
 
set_FAT12:
test edx, 0xF000
jnz sfc_error
mov ebx, [ebp+FAT.fat12_unpacked_ptr]
xchg [ebx+eax*2], dx
mov [ebp+FAT.fat_change], 1
pop esi ebx eax
clc
ret
 
get_FAT:
;--------------------------------
; input : EAX = cluster
; EBP = pointer to FAT structure
; output : EAX = next cluster
;--------------------------------
; out: CF set <=> error
push ebx esi
 
cmp [ebp+FAT.fs_type], 12
je get_FAT12
 
cmp [ebp+FAT.fs_type], 16
je gfc_1
add eax, eax
gfc_1:
add eax, eax
mov esi, 511
and esi, eax ; esi = position in fat sector
shr eax, 9 ; eax = fat sector
add eax, [ebp+FAT.FAT_START]
mov ebx, [ebp+FAT.fat_cache_ptr]
 
cmp eax, [ebp+FAT.fat_in_cache]; is fat sector already in memory?
je gfc_in_cache
 
cmp [ebp+FAT.fat_change], 0; is fat changed?
je gfc_no_change ; no
call write_fat_sector; yes. write it into disk
jc hd_error_01
 
gfc_no_change:
mov [ebp+FAT.fat_in_cache], eax
call fs_read32_sys
test eax, eax
jne hd_error_01
 
gfc_in_cache:
mov eax, [ebx+esi]
and eax, [ebp+FAT.fatMASK]
gfc_return:
pop esi ebx
ret
hd_error_01:
stc
jmp gfc_return
 
get_FAT12:
mov ebx, [ebp+FAT.fat12_unpacked_ptr]
movzx eax, word [ebx+eax*2]
pop esi ebx
clc
ret
 
 
get_free_FAT:
;-----------------------------------------------------------
; output : if CARRY=0 EAX = # first cluster found free
; if CARRY=1 disk full
; Note : for more speed need to use fat_cache directly
;-----------------------------------------------------------
push ecx
mov ecx, [ebp+FAT.LAST_CLUSTER]; counter for full disk
mov eax, [ebp+FAT.fatStartScan]
cmp [ebp+FAT.fs_type], 12
jz get_free_FAT12
dec ecx
cmp eax, 2
jb gff_reset
 
gff_test:
cmp eax, [ebp+FAT.LAST_CLUSTER]; if above last cluster start at cluster 2
jbe gff_in_range
gff_reset:
mov eax, 2
 
gff_in_range:
push eax
call get_FAT ; get cluster state
jc gff_not_found_1
 
test eax, eax ; is it free?
pop eax
je gff_found ; yes
inc eax ; next cluster
dec ecx ; is all checked?
jnz gff_test ; no
 
gff_not_found:
pop ecx ; yes. disk is full
stc
ret
 
gff_not_found_1:
pop eax
jmp gff_not_found
 
gff_found:
lea ecx, [eax+1]
mov [ebp+FAT.fatStartScan], ecx
pop ecx
clc
ret
 
get_free_FAT12:
push edx edi
mov edi, [ebp+FAT.fat12_unpacked_ptr]
cmp eax, 2
jb .reset
cmp eax, ecx
jbe @f
.reset:
mov eax, 2
@@:
mov edx, eax
lea edi, [edi+eax*2]
sub ecx, eax
inc ecx
xor eax, eax
repnz scasw
jz .found
cmp edx, 2
jz .notfound
mov edi, [ebp+FAT.fat12_unpacked_ptr]
lea ecx, [edx-2]
repnz scasw
jnz .notfound
.found:
sub edi, [ebp+FAT.fat12_unpacked_ptr]
shr edi, 1
mov [ebp+FAT.fatStartScan], edi
lea eax, [edi-1]
pop edi edx ecx
ret
.notfound:
pop edi edx ecx
stc
ret
 
 
write_fat_sector:
;-----------------------------------------------------------
; write changed fat to disk
;-----------------------------------------------------------
push eax ebx ecx
 
mov [ebp+FAT.fat_change], 0
mov eax, [ebp+FAT.fat_in_cache]
cmp eax, -1
jz write_fat_not_used
mov ebx, [ebp+FAT.fat_cache_ptr]
mov ecx, [ebp+FAT.NUMBER_OF_FATS]
 
write_next_fat:
push eax
call fs_write32_sys
test eax, eax
pop eax
jnz write_fat_not_used
 
add eax, [ebp+FAT.SECTORS_PER_FAT]
dec ecx
jnz write_next_fat
 
write_fat_not_used:
pop ecx ebx eax
ret
 
 
 
 
 
bcd2bin:
;----------------------------------
; input : AL=BCD number (eg. 0x11)
; output : AH=0
; AL=decimal number (eg. 11)
;----------------------------------
xor ah, ah
shl ax, 4
shr al, 4
aad
ret
 
 
get_date_for_file:
;-----------------------------------------------------
; Get date from CMOS and pack day,month,year in AX
; DATE bits 0..4 : day of month 0..31
; 5..8 : month of year 1..12
; 9..15 : count of years from 1980
;-----------------------------------------------------
mov al, 0x7 ;day
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 5
 
mov al, 0x8 ;month
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 4
 
mov al, 0x9 ;year
out 0x70, al
in al, 0x71
call bcd2bin
add ax, 20 ;because CMOS return only the two last
;digit (eg. 2000 -> 00 , 2001 -> 01) and we
rol eax, 9 ;need the difference with 1980 (eg. 2001-1980)
ret
 
 
get_time_for_file:
;-----------------------------------------------------
; Get time from CMOS and pack hour,minute,second in AX
; TIME bits 0..4 : second (the low bit is lost)
; 5..10 : minute 0..59
; 11..15 : hour 0..23
;-----------------------------------------------------
mov al, 0x0 ;second
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 6
 
mov al, 0x2 ;minute
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 6
 
mov al, 0x4 ;hour
out 0x70, al
in al, 0x71
call bcd2bin
rol eax, 11
ret
 
 
set_current_time_for_entry:
;-----------------------------------------------------
; Set current time/date for file entry
; input : ebx = file entry pointer
;-----------------------------------------------------
push eax
call get_time_for_file; update files date/time
mov [ebx+22], ax
call get_date_for_file
mov [ebx+24], ax
pop eax
ret
 
 
 
add_disk_free_space:
;-----------------------------------------------------
; input : ecx = cluster count
; Note : negative = remove clusters from free space
; positive = add clusters to free space
;-----------------------------------------------------
test ecx, ecx ; no change
je add_dfs_no
cmp [ebp+FAT.fs_type], 32 ; free disk space only used by fat32
jne add_dfs_no
 
push eax ebx
mov eax, [ebp+FAT.ADR_FSINFO]
lea ebx, [ebp+FAT.fsinfo_buffer]
call fs_read32_sys
test eax, eax
jnz add_not_fs
 
cmp dword [ebx+0x1fc], 0xaa550000; check sector id
jne add_not_fs
 
add [ebx+0x1e8], ecx
push [ebp+FAT.fatStartScan]
pop dword [ebx+0x1ec]
mov eax, [ebp+FAT.ADR_FSINFO]
call fs_write32_sys
; jc add_not_fs
 
add_not_fs:
pop ebx eax
 
add_dfs_no:
ret
 
 
 
clear_cluster_chain:
;-----------------------------------------------------
; input : eax = first cluster
;-----------------------------------------------------
push eax ecx edx
xor ecx, ecx ; cluster count
 
clean_new_chain:
cmp eax, [ebp+FAT.LAST_CLUSTER]; end of file
ja delete_OK
cmp eax, 2 ; unfinished fat chain or zero length file
jb delete_OK
cmp eax, [ebp+FAT.ROOT_CLUSTER]; don't remove root cluster
jz delete_OK
 
xor edx, edx
call set_FAT ; clear fat entry
jc access_denied_01
 
inc ecx ; update cluster count
mov eax, edx ; old cluster
jmp clean_new_chain
 
delete_OK:
call add_disk_free_space; add clusters to free disk space
clc
access_denied_01:
pop edx ecx eax
ret
 
 
if 0
get_hd_info:
;-----------------------------------------------------------
; output : eax = 0 - ok
; 3 - unknown FS
; 10 - access denied
; edx = cluster size in bytes
; ebx = total clusters on disk
; ecx = free clusters on disk
;-----------------------------------------------------------
cmp [ebp+FAT.fs_type], 16
jz info_fat_ok
cmp [ebp+FAT.fs_type], 32
jz info_fat_ok
xor edx, edx
xor ebx, ebx
xor ecx, ecx
mov eax, ERROR_UNKNOWN_FS
ret
 
info_fat_ok:
; call reserve_hd1
 
xor ecx, ecx ; count of free clusters
mov eax, 2
mov ebx, [ebp+FAT.LAST_CLUSTER]
 
info_cluster:
push eax
call get_FAT ; get cluster info
jc info_access_denied
 
test eax, eax ; is it free?
jnz info_used ; no
inc ecx
 
info_used:
pop eax
inc eax
cmp eax, ebx ; is above last cluster?
jbe info_cluster ; no. test next cluster
 
dec ebx ; cluster count
imul edx, [ebp+FAT.SECTORS_PER_CLUSTER], 512; cluster size in bytes
xor eax, eax
ret
 
info_access_denied:
add esp, 4
xor edx, edx
xor ebx, ebx
xor ecx, ecx
mov eax, ERROR_ACCESS_DENIED
ret
end if
 
update_disk:
cmp [ebp+FAT.fat_change], 0 ; is fat changed?
je upd_no_change
cmp [ebp+FAT.fs_type], 12
jz .fat12
;-----------------------------------------------------------
; write changed fat and cache to disk
;-----------------------------------------------------------
 
call write_fat_sector
jc update_disk_acces_denied
jmp upd_no_change
.fat12:
call restorefatchain
mov [ebp+FAT.fat_change], 0
 
upd_no_change:
 
push esi
mov esi, [ebp+PARTITION.Disk]
call disk_sync
pop esi
update_disk_acces_denied:
ret
 
fat_lock:
lea ecx, [ebp+FAT.Lock]
jmp mutex_lock
fat_unlock:
lea ecx, [ebp+FAT.Lock]
jmp mutex_unlock
 
; \begin{diamond}
uni2ansi_str:
; convert UNICODE zero-terminated string to ASCII-string (codepage 866)
; in: esi->source, edi->buffer (may be esi=edi)
; destroys: eax,esi,edi
lodsw
test ax, ax
jz .done
cmp ax, 0x80
jb .ascii
cmp ax, 0x401
jz .yo1
cmp ax, 0x451
jz .yo2
cmp ax, 0x410
jb .unk
cmp ax, 0x440
jb .rus1
cmp ax, 0x450
jb .rus2
.unk:
mov al, '_'
jmp .doit
.yo1:
mov al, 0xF0 ; 'Ё'
jmp .doit
.yo2:
mov al, 0xF1 ; 'ё'
jmp .doit
.rus1:
; 0x410-0x43F -> 0x80-0xAF
add al, 0x70
jmp .doit
.rus2:
; 0x440-0x44F -> 0xE0-0xEF
add al, 0xA0
.ascii:
.doit:
stosb
jmp uni2ansi_str
.done:
mov byte [edi], 0
ret
 
ansi2uni_char:
; convert ANSI character in al to UNICODE character in ax, using cp866 encoding
mov ah, 0
; 0x00-0x7F - trivial map
cmp al, 0x80
jb .ret
; 0x80-0xAF -> 0x410-0x43F
cmp al, 0xB0
jae @f
add ax, 0x410-0x80
.ret:
ret
@@:
; 0xE0-0xEF -> 0x440-0x44F
cmp al, 0xE0
jb .unk
cmp al, 0xF0
jae @f
add ax, 0x440-0xE0
ret
; 0xF0 -> 0x401
; 0xF1 -> 0x451
@@:
cmp al, 0xF0 ; 'Ё'
jz .yo1
cmp al, 0xF1 ; 'ё'
jz .yo2
.unk:
mov al, '_' ; ah=0
ret
.yo1:
mov ax, 0x401
ret
.yo2:
mov ax, 0x451
ret
 
char_toupper:
; convert character to uppercase, using cp866 encoding
; in: al=symbol
; out: al=converted symbol
cmp al, 'a'
jb .ret
cmp al, 'z'
jbe .az
cmp al, 0xF1 ; 'ё'
jz .yo1
cmp al, 0xA0 ; 'а'
jb .ret
cmp al, 0xE0 ; 'р'
jb .rus1
cmp al, 0xEF ; 'я'
ja .ret
; 0xE0-0xEF -> 0x90-0x9F
sub al, 0xE0-0x90
.ret:
ret
.rus1:
; 0xA0-0xAF -> 0x80-0x8F
.az:
and al, not 0x20
ret
.yo1:
; 0xF1 -> 0xF0
dec ax
ret
 
fat_get_name:
; in: edi->FAT entry
; out: CF=1 - no valid entry
; else CF=0 and ebp->ASCIIZ-name
; (maximum length of filename is 255 (wide) symbols without trailing 0,
; but implementation requires buffer 261 words)
; destroys eax
cmp byte [edi], 0
jz .no
cmp byte [edi], 0xE5
jnz @f
.no:
stc
ret
@@:
cmp byte [edi+11], 0xF
jz .longname
test byte [edi+11], 8
jnz .no
push ecx
push edi ebp
test byte [ebp-4], 1
jnz .unicode_short
 
mov eax, [edi]
mov ecx, [edi+4]
mov [ebp], eax
mov [ebp+4], ecx
 
mov ecx, 8
@@:
cmp byte [ebp+ecx-1], ' '
loope @b
 
mov eax, [edi+8]
cmp al, ' '
je .done
shl eax, 8
mov al, '.'
 
lea ebp, [ebp+ecx+1]
mov [ebp], eax
mov ecx, 3
@@:
rol eax, 8
cmp al, ' '
jne .done
loop @b
dec ebp
.done:
and byte [ebp+ecx+1], 0 ; CF=0
pop ebp edi ecx
ret
.unicode_short:
mov ecx, 8
push ecx
@@:
mov al, [edi]
inc edi
call ansi2uni_char
mov [ebp], ax
inc ebp
inc ebp
loop @b
pop ecx
@@:
cmp word [ebp-2], ' '
jnz @f
dec ebp
dec ebp
loop @b
@@:
mov word [ebp], '.'
inc ebp
inc ebp
mov ecx, 3
push ecx
@@:
mov al, [edi]
inc edi
call ansi2uni_char
mov [ebp], ax
inc ebp
inc ebp
loop @b
pop ecx
@@:
cmp word [ebp-2], ' '
jnz @f
dec ebp
dec ebp
loop @b
dec ebp
dec ebp
@@:
and word [ebp], 0 ; CF=0
pop ebp edi ecx
ret
.longname:
; LFN
mov al, byte [edi]
and eax, 0x3F
dec eax
cmp al, 20
jae .no ; ignore invalid entries
mov word [ebp+260*2], 0 ; force null-terminating for orphans
imul eax, 13*2
add ebp, eax
test byte [edi], 0x40
jz @f
mov word [ebp+13*2], 0
@@:
push eax
; now copy name from edi to ebp ...
mov eax, [edi+1]
mov [ebp], eax ; symbols 1,2
mov eax, [edi+5]
mov [ebp+4], eax ; 3,4
mov eax, [edi+9]
mov [ebp+8], ax ; 5
mov eax, [edi+14]
mov [ebp+10], eax ; 6,7
mov eax, [edi+18]
mov [ebp+14], eax ; 8,9
mov eax, [edi+22]
mov [ebp+18], eax ; 10,11
mov eax, [edi+28]
mov [ebp+22], eax ; 12,13
; ... done
pop eax
sub ebp, eax
test eax, eax
jz @f
; if this is not first entry, more processing required
stc
ret
@@:
; if this is first entry:
test byte [ebp-4], 1
jnz .ret
; buffer at ebp contains UNICODE name, convert it to ANSI
push esi edi
mov esi, ebp
mov edi, ebp
call uni2ansi_str
pop edi esi
.ret:
clc
ret
 
fat_compare_name:
; compares ASCIIZ-names, case-insensitive (cp866 encoding)
; in: esi->name, ebp->name
; out: if names match: ZF=1 and esi->next component of name
; else: ZF=0, esi is not changed
; destroys eax
push ebp esi
.loop:
mov al, [ebp]
inc ebp
call char_toupper
push eax
lodsb
call char_toupper
cmp al, [esp]
jnz .done
pop eax
test al, al
jnz .loop
dec esi
pop eax
pop ebp
xor eax, eax ; set ZF flag
ret
.done:
cmp al, '/'
jnz @f
cmp byte [esp], 0
jnz @f
mov [esp+4], esi
@@:
pop eax
pop esi ebp
ret
 
fat_find_lfn:
; in: esi->name
; [esp+4] = next
; [esp+8] = first
; [esp+C]... - possibly parameters for first and next
; out: CF=1 - file not found, eax=error code
; else CF=0, esi->next name component, edi->direntry
pusha
lea eax, [esp+0Ch+20h]
call dword [eax-4]
jc .reterr
sub esp, 262*2 ; reserve place for LFN
push 0 ; for fat_get_name: read ASCII name
.l1:
lea ebp, [esp+4]
call fat_get_name
jc .l2
call fat_compare_name
jz .found
.l2:
mov ebp, [esp+8+262*2+4]
lea eax, [esp+0Ch+20h+262*2+4]
call dword [eax-8]
jnc .l1
add esp, 262*2+4
.reterr:
mov [esp+28], eax
stc
popa
ret
.found:
add esp, 262*2+4
mov ebp, [esp+8]
; if this is LFN entry, advance to true entry
cmp byte [edi+11], 0xF
jnz @f
lea eax, [esp+0Ch+20h]
call dword [eax-8]
jc .reterr
@@:
add esp, 8 ; CF=0
push esi
push edi
popa
ret
 
fat_time_to_bdfe:
; in: eax=FAT time
; out: eax=BDFE time
push ecx edx
mov ecx, eax
mov edx, eax
shr eax, 11
shl eax, 16 ; hours
and edx, 0x1F
add edx, edx
mov al, dl ; seconds
shr ecx, 5
and ecx, 0x3F
mov ah, cl ; minutes
pop edx ecx
ret
 
fat_date_to_bdfe:
push ecx edx
mov ecx, eax
mov edx, eax
shr eax, 9
add ax, 1980
shl eax, 16 ; year
and edx, 0x1F
mov al, dl ; day
shr ecx, 5
and ecx, 0xF
mov ah, cl ; month
pop edx ecx
ret
 
bdfe_to_fat_time:
push edx
mov edx, eax
shr eax, 16
and dh, 0x3F
shl eax, 6
or al, dh
shr dl, 1
and dl, 0x1F
shl eax, 5
or al, dl
pop edx
ret
 
bdfe_to_fat_date:
push edx
mov edx, eax
shr eax, 16
sub ax, 1980
and dh, 0xF
shl eax, 4
or al, dh
and dl, 0x1F
shl eax, 5
or al, dl
pop edx
ret
 
fat_entry_to_bdfe:
; convert FAT entry at edi to BDFE (block of data of folder entry) at esi, advance esi
; destroys eax
mov eax, [ebp-4]
mov [esi+4], eax ; ASCII/UNICODE name
fat_entry_to_bdfe2:
movzx eax, byte [edi+11]
mov [esi], eax ; attributes
movzx eax, word [edi+14]
call fat_time_to_bdfe
mov [esi+8], eax ; creation time
movzx eax, word [edi+16]
call fat_date_to_bdfe
mov [esi+12], eax ; creation date
and dword [esi+16], 0 ; last access time is not supported on FAT
movzx eax, word [edi+18]
call fat_date_to_bdfe
mov [esi+20], eax ; last access date
movzx eax, word [edi+22]
call fat_time_to_bdfe
mov [esi+24], eax ; last write time
movzx eax, word [edi+24]
call fat_date_to_bdfe
mov [esi+28], eax ; last write date
mov eax, [edi+28]
mov [esi+32], eax ; file size (low dword)
xor eax, eax
mov [esi+36], eax ; file size (high dword)
test ebp, ebp
jz .ret
push ecx edi
lea edi, [esi+40]
mov esi, ebp
test byte [esi-4], 1
jz .ansi
mov ecx, 260/2
rep movsd
mov [edi-2], ax
@@:
mov esi, edi
pop edi ecx
.ret:
ret
.ansi:
mov ecx, 264/4
rep movsd
mov [edi-1], al
jmp @b
 
bdfe_to_fat_entry:
; convert BDFE at edx to FAT entry at edi
; destroys eax
; attributes byte
test byte [edi+11], 8 ; volume label?
jnz @f
mov al, [edx]
and al, 0x27
and byte [edi+11], 0x10
or byte [edi+11], al
@@:
mov eax, [edx+8]
call bdfe_to_fat_time
mov [edi+14], ax ; creation time
mov eax, [edx+12]
call bdfe_to_fat_date
mov [edi+16], ax ; creation date
mov eax, [edx+20]
call bdfe_to_fat_date
mov [edi+18], ax ; last access date
mov eax, [edx+24]
call bdfe_to_fat_time
mov [edi+22], ax ; last write time
mov eax, [edx+28]
call bdfe_to_fat_date
mov [edi+24], ax ; last write date
ret
 
hd_find_lfn:
; in: ebp -> FAT structure
; in: esi+[esp+4] -> name
; out: CF=1 - file not found, eax=error code
; else CF=0 and edi->direntry, eax=sector
; destroys eax
push esi edi
push 0
push 0
push fat1x_root_first
push fat1x_root_next
mov eax, [ebp+FAT.ROOT_CLUSTER]
cmp [ebp+FAT.fs_type], 32
jz .fat32
.loop:
and [ebp+FAT.longname_sec1], 0
and [ebp+FAT.longname_sec2], 0
call fat_find_lfn
jc .notfound
cmp byte [esi], 0
jz .found
.continue:
test byte [edi+11], 10h
jz .notfound
and dword [esp+12], 0
mov eax, [edi+20-2]
mov ax, [edi+26] ; cluster
.fat32:
mov [esp+8], eax
mov dword [esp+4], fat_notroot_first
mov dword [esp], fat_notroot_next
jmp .loop
.notfound:
add esp, 16
pop edi esi
stc
ret 4
.found:
lea eax, [esp+4+24]
cmp dword [eax], 0
jz @f
mov esi, [eax]
and dword [eax], 0
jmp .continue
@@:
lea eax, [esp+8]
cmp dword [eax], 0
jz .root
call fat_get_sector
jmp .cmn
.root:
mov eax, [eax+4]
add eax, [ebp+FAT.ROOT_START]
.cmn:
add esp, 20 ; CF=0
pop esi
ret 4
 
;----------------------------------------------------------------
; fat_Read - FAT implementation of reading a file
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_Read:
call fat_lock
push edi
cmp byte [esi], 0
jnz @f
.noaccess:
pop edi
.noaccess_2:
call fat_unlock
or ebx, -1
mov eax, ERROR_ACCESS_DENIED
ret
 
@@:
stdcall hd_find_lfn, [esp+4+4]
jnc .found
pop edi
push eax
call fat_unlock
pop eax
or ebx, -1
ret
 
.found:
test byte [edi+11], 0x10; do not allow read directories
jnz .noaccess
cmp dword [ebx+8], 0
jz @f
xor ebx, ebx
.reteof:
call fat_unlock
mov eax, ERROR_END_OF_FILE
pop edi
ret
@@:
mov ecx, [ebx+12] ; size
mov edx, [ebx+16] ; pointer
mov ebx, [ebx+4] ; file offset
push edx
push 0
mov eax, [edi+28]
sub eax, ebx
jb .eof
cmp eax, ecx
jae @f
mov ecx, eax
mov byte [esp], 6
@@:
mov eax, [edi+20-2]
mov ax, [edi+26]
; now eax=cluster, ebx=position, ecx=count, edx=buffer for data
.new_cluster:
jecxz .new_sector
cmp eax, 2
jb .eof
cmp eax, [ebp+FAT.fatRESERVED]
jae .eof
mov [ebp+FAT.cluster_tmp], eax
dec eax
dec eax
mov edi, [ebp+FAT.SECTORS_PER_CLUSTER]
imul eax, edi
add eax, [ebp+FAT.DATA_START]
.new_sector:
test ecx, ecx
jz .done
sub ebx, 512
jae .skip
add ebx, 512
jnz .force_buf
cmp ecx, 512
jb .force_buf
; we may read directly to given buffer
push eax ebx
mov ebx, edx
call fs_read32_app
test eax, eax
pop ebx eax
jne .noaccess_1
add edx, 512
sub ecx, 512
jmp .skip
.force_buf:
; we must read sector to temporary buffer and then copy it to destination
push eax ebx
lea ebx, [ebp+FAT.buffer]
call fs_read32_app
test eax, eax
mov eax, ebx
pop ebx
jne .noaccess_3
add eax, ebx
push ecx
add ecx, ebx
cmp ecx, 512
jbe @f
mov ecx, 512
@@:
sub ecx, ebx
mov ebx, edx
call memmove
add edx, ecx
sub [esp], ecx
pop ecx
pop eax
xor ebx, ebx
.skip:
inc eax
dec edi
jnz .new_sector
mov eax, [ebp+FAT.cluster_tmp]
call get_FAT
jc .noaccess_1
 
jmp .new_cluster
.noaccess_3:
pop eax
.noaccess_1:
pop eax
push ERROR_DEVICE
.done:
mov ebx, edx
call fat_unlock
pop eax edx edi
sub ebx, edx
ret
.eof:
mov ebx, edx
pop eax edx
sub ebx, edx
jmp .reteof
 
;----------------------------------------------------------------
; fat_ReadFolder - FAT implementation of reading a folder
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_ReadFolder:
call fat_lock
mov eax, [ebp+FAT.ROOT_CLUSTER]
push edi
cmp byte [esi], 0
jz .doit
stdcall hd_find_lfn, [esp+4+4]
jnc .found
pop edi
push eax
call fat_unlock
pop eax
or ebx, -1
ret
.found:
test byte [edi+11], 0x10 ; do not allow read files
jnz .found_dir
pop edi
call fat_unlock
or ebx, -1
mov eax, ERROR_ACCESS_DENIED
ret
.found_dir:
mov eax, [edi+20-2]
mov ax, [edi+26] ; eax=cluster
.doit:
push esi
sub esp, 262*2 ; reserve space for LFN
push dword [ebx+8] ; for fat_get_name: read ANSI/UNICODE name
mov edx, [ebx+16] ; pointer to buffer
; init header
push eax
mov edi, edx
mov ecx, 32/4
xor eax, eax
rep stosd
pop eax
mov byte [edx], 1 ; version
mov esi, edi ; esi points to BDFE
mov ecx, [ebx+12] ; number of blocks to read
mov ebx, [ebx+4] ; index of the first block
.new_cluster:
mov [ebp+FAT.cluster_tmp], eax
test eax, eax
jnz @f
cmp [ebp+FAT.fs_type], 32
jz .notfound
mov eax, [ebp+FAT.ROOT_START]
push [ebp+FAT.ROOT_SECTORS]
push ebx
jmp .new_sector
@@:
dec eax
dec eax
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
push [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
push ebx
.new_sector:
lea ebx, [ebp+FAT.buffer]
mov edi, ebx
push eax
call fs_read32_sys
test eax, eax
pop eax
jnz .notfound2
add ebx, 512
push eax
.l1:
push ebp
lea ebp, [esp+20]
call fat_get_name
pop ebp
jc .l2
cmp byte [edi+11], 0xF
jnz .do_bdfe
add edi, 0x20
cmp edi, ebx
jb .do_bdfe
pop eax
inc eax
dec dword [esp+4]
jnz @f
mov eax, [ebp+FAT.cluster_tmp]
test eax, eax
jz .done
call get_FAT
jc .notfound2
cmp eax, 2
jb .done
cmp eax, [ebp+FAT.fatRESERVED]
jae .done
push eax
mov eax, [ebp+FAT.SECTORS_PER_CLUSTER]
mov [esp+8], eax
pop eax
mov [ebp+FAT.cluster_tmp], eax
dec eax
dec eax
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
@@:
lea ebx, [ebp+FAT.buffer]
mov edi, ebx
push eax
call fs_read32_sys
test eax, eax
pop eax
jnz .notfound2
add ebx, 512
push eax
.do_bdfe:
inc dword [edx+8] ; new file found
dec dword [esp+4]
jns .l2
dec ecx
js .l2
inc dword [edx+4] ; new file block copied
push ebp
lea ebp, [esp+20]
call fat_entry_to_bdfe
pop ebp
.l2:
add edi, 0x20
cmp edi, ebx
jb .l1
pop eax
inc eax
dec dword [esp+4]
jnz .new_sector
mov eax, [ebp+FAT.cluster_tmp]
test eax, eax
jz .done
call get_FAT
jc .notfound2
cmp eax, 2
jb .done
cmp eax, [ebp+FAT.fatRESERVED]
jae .done
push eax
mov eax, [ebp+FAT.SECTORS_PER_CLUSTER]
mov [esp+8], eax
pop eax
pop ebx
add esp, 4
jmp .new_cluster
.notfound2:
add esp, 8
.notfound:
add esp, 262*2+4
pop esi edi
mov ebx, [edx+4]
call fat_unlock
mov eax, ERROR_DEVICE
ret
.done:
add esp, 262*2+4+8
mov ebx, [edx+4]
xor eax, eax
dec ecx
js @f
mov al, ERROR_END_OF_FILE
@@:
push eax
call fat_unlock
pop eax
pop esi edi
ret
 
fat1x_root_next:
push ecx
lea ecx, [ebp+FAT.buffer+0x200-0x20]
cmp edi, ecx
jae fat1x_root_next_sector
pop ecx
add edi, 0x20
ret ; CF=0
fat1x_root_next_sector:
; read next sector
push [ebp+FAT.longname_sec2]
pop [ebp+FAT.longname_sec1]
mov ecx, [eax+4]
push ecx
add ecx, [ebp+FAT.ROOT_START]
mov [ebp+FAT.longname_sec2], ecx
pop ecx
inc ecx
mov [eax+4], ecx
cmp ecx, [ebp+FAT.ROOT_SECTORS]
pop ecx
jb fat1x_root_first
mov eax, ERROR_FILE_NOT_FOUND
stc
ret
fat1x_root_first:
mov eax, [eax+4]
add eax, [ebp+FAT.ROOT_START]
push ebx
lea edi, [ebp+FAT.buffer]
mov ebx, edi
call fs_read32_sys
pop ebx
test eax, eax
jnz .readerr
ret ; CF=0
.readerr:
mov eax, ERROR_DEVICE
stc
ret
.notfound:
mov eax, ERROR_FILE_NOT_FOUND
stc
ret
fat1x_root_begin_write:
push edi eax
call fat1x_root_first
pop eax edi
ret
fat1x_root_end_write:
pusha
mov eax, [eax+4]
add eax, [ebp+FAT.ROOT_START]
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
popa
ret
fat1x_root_next_write:
push ecx
lea ecx, [ebp+FAT.buffer+0x200]
cmp edi, ecx
jae @f
pop ecx
ret
@@:
call fat1x_root_end_write
jmp fat1x_root_next_sector
fat1x_root_extend_dir:
stc
ret
 
fat_notroot_next:
push ecx
lea ecx, [ebp+FAT.buffer+0x200-0x20]
cmp edi, ecx
jae fat_notroot_next_sector
pop ecx
add edi, 0x20
ret ; CF=0
fat_notroot_next_sector:
push [ebp+FAT.longname_sec2]
pop [ebp+FAT.longname_sec1]
push eax
call fat_get_sector
mov [ebp+FAT.longname_sec2], eax
pop eax
mov ecx, [eax+4]
inc ecx
cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
jae fat_notroot_next_cluster
mov [eax+4], ecx
jmp @f
fat_notroot_next_cluster:
push eax
mov eax, [eax]
call get_FAT
mov ecx, eax
pop eax
jc fat_notroot_first.deverr
cmp ecx, 2
jb fat_notroot_next_err
cmp ecx, [ebp+FAT.fatRESERVED]
jae fat_notroot_next_err
mov [eax], ecx
and dword [eax+4], 0
@@:
pop ecx
fat_notroot_first:
call fat_get_sector
push ebx
lea edi, [ebp+FAT.buffer]
mov ebx, edi
call fs_read32_sys
pop ebx
test eax, eax
jz .ret ; CF=0
push ecx
.deverr:
pop ecx
mov eax, ERROR_DEVICE
stc
.ret:
ret
fat_notroot_next_err:
pop ecx
mov eax, ERROR_FILE_NOT_FOUND
stc
ret
fat_notroot_begin_write:
push eax edi
call fat_notroot_first
pop edi eax
ret
fat_notroot_end_write:
call fat_get_sector
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
pop ebx
ret
fat_notroot_next_write:
push ecx
lea ecx, [ebp+FAT.buffer+0x200]
cmp edi, ecx
jae @f
pop ecx
ret
@@:
push eax
call fat_notroot_end_write
pop eax
jmp fat_notroot_next_sector
fat_notroot_extend_dir:
push eax
call get_free_FAT
jnc .found
pop eax
ret ; CF=1
.found:
push edx
mov edx, [ebp+FAT.fatEND]
call set_FAT
jc .writeerr
mov edx, eax
mov eax, [esp+4]
mov eax, [eax]
push edx
call set_FAT
pop edx
jnc @f
.writeerr:
pop edx
pop eax
stc
ret
@@:
push ecx
or ecx, -1
call add_disk_free_space
; zero new cluster
mov ecx, 512/4
lea edi, [ebp+FAT.buffer]
push edi
xor eax, eax
rep stosd
pop edi
pop ecx
mov eax, [esp+4]
mov [eax], edx
and dword [eax+4], 0
pop edx
mov eax, [eax]
dec eax
dec eax
push ebx ecx
mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
imul eax, ecx
add eax, [ebp+FAT.DATA_START]
mov ebx, edi
@@:
push eax
call fs_write32_sys
pop eax
inc eax
loop @b
pop ecx ebx eax
clc
ret
 
fat_get_sector:
push ecx
mov ecx, [eax]
dec ecx
dec ecx
imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
add ecx, [ebp+FAT.DATA_START]
add ecx, [eax+4]
mov eax, ecx
pop ecx
ret
 
fshrad:
call fat_unlock
mov eax, ERROR_ACCESS_DENIED
xor ebx, ebx
ret
 
;----------------------------------------------------------------
; fat_CreateFolder - FAT implementation of creating a folder
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_CreateFolder:
push 1
jmp fat_Rewrite.common
 
;----------------------------------------------------------------
; fat_Rewrite - FAT implementation of creating a new file
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_Rewrite:
push 0
.common:
call fat_lock
pop eax
cmp byte [esi], 0
jz fshrad
mov ecx, [ebx+12]
mov edx, [ebx+16]
pushad
xor edi, edi
mov edx, [esp+4+20h]
push esi
test edx, edx
jz @f
mov esi, edx
@@:
lodsb
test al, al
jz @f
cmp al, '/'
jnz @b
lea edi, [esi-1]
jmp @b
@@:
pop esi
test edi, edi
jnz .noroot
test edx, edx
jnz .hasebp
mov edx, [ebp+FAT.ROOT_CLUSTER]
cmp [ebp+FAT.fs_type], 32
jz .pushnotroot
xor edx, edx
push edx
push fat1x_root_extend_dir
push fat1x_root_end_write
push fat1x_root_next_write
push fat1x_root_begin_write
push edx
push edx
push fat1x_root_first
push fat1x_root_next
jmp .common1
.hasebp:
mov eax, ERROR_ACCESS_DENIED
cmp byte [edx], 0
jz .ret1
stdcall hd_find_lfn, 0
mov esi, [esp+4+20h]
jc .ret1
jmp .common0
.noroot:
mov eax, ERROR_ACCESS_DENIED
cmp byte [edi+1], 0
jz .ret1
; check existence
mov byte [edi], 0
push edi
stdcall hd_find_lfn, [esp+4+24h]
pop esi
mov byte [esi], '/'
jnc @f
.notfound0:
mov eax, ERROR_FILE_NOT_FOUND
.ret1:
mov [esp+28], eax
call fat_unlock
popad
xor ebx, ebx
ret
@@:
inc esi
.common0:
test byte [edi+11], 0x10 ; must be directory
mov eax, ERROR_ACCESS_DENIED
jz .ret1
mov edx, [edi+20-2]
mov dx, [edi+26] ; ebp=cluster
mov eax, ERROR_FAT_TABLE
cmp edx, 2
jb .ret1
.pushnotroot:
push edx
push fat_notroot_extend_dir
push fat_notroot_end_write
push fat_notroot_next_write
push fat_notroot_begin_write
push 0
push edx
push fat_notroot_first
push fat_notroot_next
.common1:
call fat_find_lfn
jc .notfound
; found
test byte [edi+11], 10h
jz .exists_file
; found directory; if we are creating directory, return OK,
; if we are creating file, say "access denied"
add esp, 36
call fat_unlock
popad
test al, al
mov eax, ERROR_ACCESS_DENIED
jz @f
mov al, 0
@@:
xor ebx, ebx
ret
.exists_file:
; found file; if we are creating directory, return "access denied",
; if we are creating file, delete existing file and continue
cmp byte [esp+36+28], 0
jz @f
add esp, 36
call fat_unlock
popad
mov eax, ERROR_ACCESS_DENIED
xor ebx, ebx
ret
@@:
; delete FAT chain
push edi
xor eax, eax
mov dword [edi+28], eax ; zero size
xor ecx, ecx
mov eax, [edi+20-2]
mov ax, [edi+26]
mov word [edi+20], cx
mov word [edi+26], cx
test eax, eax
jz .done1
@@:
cmp eax, [ebp+FAT.fatRESERVED]
jae .done1
push edx
xor edx, edx
call set_FAT
mov eax, edx
pop edx
jc .done1
inc ecx
jmp @b
.done1:
pop edi
call get_time_for_file
mov [edi+22], ax
call get_date_for_file
mov [edi+24], ax
mov [edi+18], ax
or byte [edi+11], 20h ; set 'archive' attribute
jmp .doit
.notfound:
; file is not found; generate short name
call fat_name_is_legal
jc @f
add esp, 36
call fat_unlock
popad
mov eax, ERROR_FILE_NOT_FOUND
xor ebx, ebx
ret
@@:
sub esp, 12
mov edi, esp
call fat_gen_short_name
.test_short_name_loop:
push esi edi ecx
mov esi, edi
lea eax, [esp+12+12+8]
mov edx, [eax+24]
mov [eax], edx
and dword [eax+4], 0
call dword [eax-4]
jc .found
.test_short_name_entry:
cmp byte [edi+11], 0xF
jz .test_short_name_cont
mov ecx, 11
push esi edi
repz cmpsb
pop edi esi
jz .short_name_found
.test_short_name_cont:
lea eax, [esp+12+12+8]
call dword [eax-8]
jnc .test_short_name_entry
jmp .found
.short_name_found:
pop ecx edi esi
call fat_next_short_name
jnc .test_short_name_loop
.disk_full:
add esp, 12+36
call fat_unlock
popa
mov eax, ERROR_DISK_FULL
xor ebx, ebx
ret
.found:
pop ecx edi esi
; now find space in directory
; we need to save LFN <=> LFN is not equal to short name <=> generated name contains '~'
mov al, '~'
push ecx edi
mov ecx, 8
repnz scasb
movi eax, 1 ; 1 entry
jnz .notilde
; we need ceil(strlen(esi)/13) additional entries = floor((strlen(esi)+12+13)/13) total
xor eax, eax
@@:
cmp byte [esi], 0
jz @f
inc esi
inc eax
jmp @b
@@:
sub esi, eax
add eax, 12+13
mov ecx, 13
push edx
cdq
div ecx
pop edx
.notilde:
push -1
push -1
push -1
; find <eax> successive entries in directory
xor ecx, ecx
push eax
lea eax, [esp+16+8+12+8]
mov edx, [eax+24]
mov [eax], edx
and dword [eax+4], 0
call dword [eax-4]
pop eax
jnc .scan_dir
.fsfrfe3:
add esp, 12+8+12+36
call fat_unlock
popad
mov eax, ERROR_DEVICE
xor ebx, ebx
ret
.scan_dir:
cmp byte [edi], 0
jz .free
cmp byte [edi], 0xE5
jz .free
xor ecx, ecx
.scan_cont:
push eax
lea eax, [esp+16+8+12+8]
call dword [eax-8]
mov edx, eax
pop eax
jnc .scan_dir
cmp edx, ERROR_DEVICE
jz .fsfrfe3
push eax
lea eax, [esp+16+8+12+8]
call dword [eax+20] ; extend directory
pop eax
jnc .scan_dir
add esp, 12+8+12+36
call fat_unlock
popad
mov eax, ERROR_DISK_FULL
xor ebx, ebx
ret
.free:
test ecx, ecx
jnz @f
mov [esp], edi
mov ecx, [esp+12+8+12+8]
mov [esp+4], ecx
mov ecx, [esp+12+8+12+12]
mov [esp+8], ecx
xor ecx, ecx
@@:
inc ecx
cmp ecx, eax
jb .scan_cont
; found!
push esi ecx
; If creating a directory, allocate one data cluster now and fail immediately
; if this is impossible. This prevents from creating an invalid directory entry
; on a full disk.
; yup, the argument is quite non-intuitive... but what should I do if
; the entire function uses such arguments? BTW, it refers to al from pushad,
; which in turn is filled with 0 in fat_Rewrite and 1 in fat_CreateFolder.
cmp byte [esp+8+12+8+12+36+28], 0
jz .no.preallocate.folder.data
call get_free_FAT
jnc @f
add esp, 8+12+8
jmp .disk_full
@@:
mov [esp+8+12+8+12+36+20], eax ; store the cluster somewhere
.no.preallocate.folder.data:
; calculate name checksum
mov esi, [esp+8+12]
mov ecx, 11
xor eax, eax
@@:
ror al, 1
add al, [esi]
inc esi
loop @b
pop ecx esi
pop edi
pop dword [esp+8+12+12]
pop dword [esp+8+12+12]
; edi points to first entry in free chunk
dec ecx
jz .nolfn
push esi
push eax
lea eax, [esp+8+8+12+8]
call dword [eax+8] ; begin write
mov al, 40h
.writelfn:
or al, cl
mov esi, [esp+4]
push ecx
dec ecx
imul ecx, 13
add esi, ecx
stosb
mov cl, 5
call fat_read_symbols
mov ax, 0xF
stosw
mov al, [esp+4]
stosb
mov cl, 6
call fat_read_symbols
xor eax, eax
stosw
mov cl, 2
call fat_read_symbols
pop ecx
lea eax, [esp+8+8+12+8]
call dword [eax+12] ; next write
xor eax, eax
loop .writelfn
pop eax
pop esi
; lea eax, [esp+8+12+8]
; call dword [eax+16] ; end write
.nolfn:
xchg esi, [esp]
mov ecx, 11
rep movsb
mov word [edi], 20h ; attributes
sub edi, 11
pop esi ecx
add esp, 12
mov byte [edi+13], 0 ; tenths of a second at file creation time
call get_time_for_file
mov [edi+14], ax ; creation time
mov [edi+22], ax ; last write time
call get_date_for_file
mov [edi+16], ax ; creation date
mov [edi+24], ax ; last write date
mov [edi+18], ax ; last access date
xor ecx, ecx
mov word [edi+20], cx ; high word of cluster
mov word [edi+26], cx ; low word of cluster - to be filled
mov dword [edi+28], ecx ; file size - to be filled
cmp byte [esp+36+28], cl
jz .doit
; create directory
mov byte [edi+11], 10h ; attributes: folder
mov esi, edi
lea eax, [esp+8]
call dword [eax+16] ; flush directory
mov eax, [esp+36+20] ; extract saved cluster
mov [esp+36+20], edi ; this is needed for calculating arg of add_disk_free_space!
push ecx
mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
shl ecx, 9
push ecx
push edi
jmp .doit2
.doit:
mov esi, [esp+36+20]
lea eax, [esp+8]
call dword [eax+16] ; flush directory
push ecx
mov ecx, [esp+4+36+24]
push ecx
push edi
test ecx, ecx
jz .done
call get_free_FAT
jc .diskfull
.doit2:
push eax
mov [edi+26], ax
shr eax, 16
mov [edi+20], ax
lea eax, [esp+16+8]
call dword [eax+16] ; flush directory
pop eax
push edx
mov edx, [ebp+FAT.fatEND]
call set_FAT
pop edx
.write_cluster:
push eax
dec eax
dec eax
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
push [ebp+FAT.SECTORS_PER_CLUSTER]
; write data
.write_sector:
cmp byte [esp+20+36+28], 0
jnz .writedir
mov ecx, 512
cmp dword [esp+12], ecx
jb .writeshort
; we can write directly from given buffer
mov ebx, esi
add esi, ecx
jmp .writecommon
.writeshort:
mov ecx, [esp+12]
push ecx
lea edi, [ebp+FAT.buffer]
mov ebx, edi
rep movsb
.writedircont:
lea ecx, [ebp+FAT.buffer+0x200]
sub ecx, edi
push eax
xor eax, eax
rep stosb
pop eax
pop ecx
.writecommon:
push eax
call fs_write32_app
test eax, eax
pop eax
jnz .writeerr
inc eax
sub dword [esp+12], ecx
jz .writedone
dec dword [esp]
jnz .write_sector
pop eax
; allocate new cluster
pop eax
mov ecx, eax
call get_free_FAT
jc .diskfull
push edx
mov edx, [ebp+FAT.fatEND]
call set_FAT
xchg eax, ecx
mov edx, ecx
call set_FAT
pop edx
xchg eax, ecx
jmp .write_cluster
.diskfull:
mov eax, ERROR_DISK_FULL
jmp .ret
.writeerr:
pop eax eax
sub esi, ecx
mov eax, ERROR_DEVICE
jmp .ret
.writedone:
pop eax eax
.done:
xor eax, eax
.ret:
pop edi ecx
sub esi, [esp+4+36+20]
mov [esp+4+36+28], eax
mov [esp+4+36+16], esi
lea eax, [esp+12]
call dword [eax+8]
mov [edi+28], esi
call dword [eax+16]
mov [esp+36+16], ebx
lea eax, [esi+511]
shr eax, 9
mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
lea eax, [eax+ecx-1]
xor edx, edx
div ecx
pop ecx
sub ecx, eax
call add_disk_free_space
add esp, 36
call update_disk
call fat_unlock
popad
ret
.writedir:
push 512
lea edi, [ebp+FAT.buffer]
mov ebx, edi
mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
shl ecx, 9
cmp ecx, [esp+16]
jnz .writedircont
dec dword [esp+20]
push esi
mov ecx, 32/4
rep movsd
pop esi
mov dword [edi-32], '. '
mov dword [edi-32+4], ' '
mov dword [edi-32+8], ' '
mov byte [edi-32+11], 10h
push esi
mov ecx, 32/4
rep movsd
pop esi
mov dword [edi-32], '.. '
mov dword [edi-32+4], ' '
mov dword [edi-32+8], ' '
mov byte [edi-32+11], 10h
mov ecx, [esp+20+36]
cmp ecx, [ebp+FAT.ROOT_CLUSTER]
jnz @f
xor ecx, ecx
@@:
mov word [edi-32+26], cx
shr ecx, 16
mov [edi-32+20], cx
jmp .writedircont
 
fat_read_symbol:
or ax, -1
test esi, esi
jz .retFFFF
lodsb
test al, al
jnz ansi2uni_char
xor eax, eax
xor esi, esi
.retFFFF:
ret
 
fat_read_symbols:
call fat_read_symbol
stosw
loop fat_read_symbols
ret
 
 
fat_Write.access_denied:
push ERROR_ACCESS_DENIED
fat_Write.ret0:
pop eax
xor ebx, ebx
ret
 
fat_Write.ret11:
push ERROR_DEVICE
jmp fat_Write.ret0
 
;----------------------------------------------------------------
; fat_Write - FAT implementation of writing to file
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_Write:
cmp byte [esi], 0
jz .access_denied
call fat_lock
push edi
stdcall hd_find_lfn, [esp+4+4]
jnc .found
pop edi
push eax
call fat_unlock
jmp .ret0
.found:
; FAT does not support files larger than 4GB
cmp dword [ebx+8], 0
jz @f
.eof:
pop edi
push ERROR_END_OF_FILE
call fat_unlock
jmp .ret0
@@:
mov ecx, [ebx+12]
mov edx, [ebx+16]
mov ebx, [ebx+4]
; now edi points to direntry, ebx=start byte to write,
; ecx=number of bytes to write, edx=data pointer
 
; extend file if needed
add ecx, ebx
jc .eof ; FAT does not support files larger than 4GB
push edx
push eax ; save directory sector
push 0 ; return value=0
 
call get_time_for_file
mov [edi+22], ax ; last write time
call get_date_for_file
mov [edi+24], ax ; last write date
mov [edi+18], ax ; last access date
 
push dword [edi+28] ; save current file size
cmp ecx, [edi+28]
jbe .length_ok
cmp ecx, ebx
jz .length_ok
call hd_extend_file
jnc .length_ok
mov [esp+4], eax
; hd_extend_file can return three error codes: FAT table error, device error or disk full.
; First two cases are fatal errors, in third case we may write some data
cmp al, ERROR_DISK_FULL
jz .disk_full
call fat_unlock
pop eax
pop eax
pop ecx
pop edx
pop edi
xor ebx, ebx
ret
.disk_full:
; correct number of bytes to write
mov ecx, [edi+28]
cmp ecx, ebx
ja .length_ok
push 0
.ret:
pop eax
sub edx, [esp+12]
mov ebx, edx ; ebx=number of written bytes
call update_disk
test eax, eax
jz @f
mov byte [esp+4], ERROR_DEVICE
@@:
call fat_unlock
pop eax
pop eax
pop ecx
pop edx
pop edi
ret
.length_ok:
mov esi, [edi+28]
mov eax, [edi+20-2]
mov ax, [edi+26]
mov edi, eax ; edi=current cluster
push 0 ; current sector in cluster
; save directory
mov eax, [esp+12]
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
pop ebx
test eax, eax
jz @f
.device_err:
mov byte [esp+8], ERROR_DEVICE
jmp .ret
.fat_err:
mov byte [esp+8], ERROR_FAT_TABLE
jmp .ret
@@:
 
; now ebx=start pos, ecx=end pos, both lie inside file
sub ecx, ebx
jz .ret
.write_loop:
; skip unmodified sectors
cmp dword [esp+4], 0x200
jb .modify
sub ebx, 0x200
jae .skip
add ebx, 0x200
.modify:
; get length of data in current sector
push ecx
sub ebx, 0x200
jb .hasdata
neg ebx
xor ecx, ecx
jmp @f
.hasdata:
neg ebx
cmp ecx, ebx
jbe @f
mov ecx, ebx
@@:
; get current sector number
mov eax, edi
dec eax
dec eax
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
add eax, [esp+4]
; load sector if needed
cmp dword [esp+8], 0 ; we don't need to read uninitialized data
jz .noread
cmp ecx, 0x200 ; we don't need to read sector if it is fully rewritten
jz .noread
cmp ecx, esi ; (same for the last sector)
jz .noread
push eax ebx
lea ebx, [ebp+FAT.buffer]
call fs_read32_app
test eax, eax
pop ebx eax
jz @f
.device_err2:
pop ecx
jmp .device_err
@@:
.noread:
; zero uninitialized data if file was extended (because hd_extend_file does not this)
push eax ecx edi
xor eax, eax
mov ecx, 0x200
sub ecx, [esp+8+12]
jbe @f
lea edi, [ebp+FAT.buffer]
add edi, [esp+8+12]
rep stosb
@@:
; zero uninitialized data in the last sector
mov ecx, 0x200
sub ecx, esi
jbe @f
lea edi, [ebp+FAT.buffer+esi]
rep stosb
@@:
pop edi ecx
; copy new data
mov eax, edx
neg ebx
jecxz @f
lea ebx, [ebp+FAT.buffer+0x200+ebx]
call memmove
xor ebx, ebx
@@:
pop eax
; save sector
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_app
pop ebx
test eax, eax
jnz .device_err2
add edx, ecx
sub [esp], ecx
pop ecx
jz .ret
.skip:
; next sector
pop eax
inc eax
push eax
cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER]
jb @f
and dword [esp], 0
mov eax, edi
call get_FAT
mov edi, eax
jc .device_err
cmp edi, 2
jb .fat_err
cmp edi, [ebp+FAT.fatRESERVED]
jae .fat_err
@@:
sub esi, 0x200
jae @f
xor esi, esi
@@:
sub dword [esp+4], 0x200
jae @f
and dword [esp+4], 0
@@:
jmp .write_loop
 
hd_extend_file.zero_size:
xor eax, eax
jmp hd_extend_file.start_extend
 
; extends file on hd to given size (new data area is undefined)
; in: edi->direntry, ecx=new size
; out: CF=0 => OK, eax=0
; CF=1 => error, eax=code (ERROR_FAT_TABLE or ERROR_DISK_FULL or ERROR_DEVICE)
hd_extend_file:
push esi
mov esi, [ebp+FAT.SECTORS_PER_CLUSTER]
imul esi, [ebp+FAT.BYTES_PER_SECTOR]
push ecx
; find the last cluster of file
mov eax, [edi+20-2]
mov ax, [edi+26]
mov ecx, [edi+28]
jecxz .zero_size
.last_loop:
sub ecx, esi
jbe .last_found
call get_FAT
jnc @f
.device_err:
pop ecx
.device_err2:
pop esi
push ERROR_DEVICE
.ret_err:
pop eax
stc
ret
@@:
cmp eax, 2
jb .fat_err
cmp eax, [ebp+FAT.fatRESERVED]
jb .last_loop
.fat_err:
pop ecx esi
push ERROR_FAT_TABLE
jmp .ret_err
.last_found:
push eax
call get_FAT
jnc @f
pop eax
jmp .device_err
@@:
cmp eax, [ebp+FAT.fatRESERVED]
pop eax
jb .fat_err
; set length to full number of clusters
sub [edi+28], ecx
.start_extend:
pop ecx
; now do extend
push edx
mov edx, 2 ; start scan from cluster 2
.extend_loop:
cmp [edi+28], ecx
jae .extend_done
; add new cluster
push eax
call get_free_FAT
jc .disk_full
mov edx, [ebp+FAT.fatEND]
call set_FAT
mov edx, eax
pop eax
test eax, eax
jz .first_cluster
push edx
call set_FAT
pop edx
jmp @f
.first_cluster:
ror edx, 16
mov [edi+20], dx
ror edx, 16
mov [edi+26], dx
@@:
push ecx
mov ecx, -1
call add_disk_free_space
pop ecx
mov eax, edx
add [edi+28], esi
jmp .extend_loop
.extend_done:
mov [edi+28], ecx
pop edx esi
xor eax, eax ; CF=0
ret
.device_err3:
pop edx
jmp .device_err2
.disk_full:
pop eax edx esi
movi eax, ERROR_DISK_FULL
stc
ret
 
fat_update_datetime:
call get_time_for_file
mov [edi+22], ax ; last write time
call get_date_for_file
mov [edi+24], ax ; last write date
mov [edi+18], ax ; last access date
ret
 
 
;----------------------------------------------------------------
; fat_SetFileEnd - FAT implementation of setting end-of-file
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_SetFileEnd:
call fat_lock
push edi
cmp byte [esi], 0
jnz @f
.access_denied:
push ERROR_ACCESS_DENIED
.ret:
call fat_unlock
pop eax
pop edi
ret
@@:
stdcall hd_find_lfn, [esp+4+4]
jnc @f
.reteax:
push eax
jmp .ret
@@:
; must not be directory
test byte [edi+11], 10h
jnz .access_denied
; file size must not exceed 4 Gb
cmp dword [ebx+8], 0
jz @f
push ERROR_END_OF_FILE
jmp .ret
@@:
push eax ; save directory sector
; set file modification date/time to current
call fat_update_datetime
mov eax, [ebx+4]
cmp eax, [edi+28]
jb .truncate
ja .expand
pop eax
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
test eax, eax
jz @f
push ERROR_DEVICE
jmp .ret
@@:
push 0
jmp .ret
.expand:
push ebx ebp ecx
push dword [edi+28] ; save old size
mov ecx, eax
call hd_extend_file
push eax ; return code
jnc .expand_ok
cmp al, ERROR_DISK_FULL
jz .disk_full
.pop_ret:
call update_disk
pop eax ecx ecx ebp ebx ecx
jmp .reteax
.expand_ok:
.disk_full:
; save directory
mov eax, [edi+28]
xchg eax, [esp+20]
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
test eax, eax
mov eax, [edi+20-2]
mov ax, [edi+26]
mov edi, eax
jz @f
.pop_ret11:
mov byte [esp], ERROR_DEVICE
jmp .pop_ret
@@:
test edi, edi
jz .pop_ret
; now zero new data
push 0
; edi=current cluster, [esp]=sector in cluster
; [esp+24]=new size, [esp+8]=old size, [esp+4]=return code
.zero_loop:
cmp edi, 2
jb .error_fat
cmp edi, [ebp+FAT.fatRESERVED]
jae .error_fat
sub dword [esp+8], 0x200
jae .next_cluster
lea eax, [edi-2]
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
add eax, [esp]
cmp dword [esp+8], -0x200
jz .noread
push eax
lea ebx, [ebp+FAT.buffer]
call fs_read32_app
test eax, eax
pop eax
jnz .err_next
.noread:
mov ecx, [esp+8]
neg ecx
push edi
lea edi, [ebp+FAT.buffer+0x200]
add edi, [esp+12]
push eax
xor eax, eax
mov [esp+16], eax
rep stosb
pop eax
pop edi
call fs_write32_app
test eax, eax
jz .next_cluster
.err_next:
mov byte [esp+4], ERROR_DEVICE
.next_cluster:
pop eax
sub dword [esp+20], 0x200
jbe .pop_ret
inc eax
push eax
cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER]
jb .zero_loop
and dword [esp], 0
mov eax, edi
call get_FAT
mov edi, eax
jnc .zero_loop
pop eax
jmp .pop_ret11
.truncate:
mov [edi+28], eax
push ecx
mov ecx, [edi+20-2]
mov cx, [edi+26]
push eax
test eax, eax
jz .zero_size
; find new last cluster
@@:
cmp ecx, 2
jb .error_fat2
cmp ecx, [ebp+FAT.fatRESERVED]
jae .error_fat2
mov eax, [ebp+FAT.SECTORS_PER_CLUSTER]
shl eax, 9
sub [esp], eax
jbe @f
mov eax, ecx
call get_FAT
mov ecx, eax
jnc @b
.device_err3:
pop eax ecx eax edi
call update_disk
call fat_unlock
movi eax, ERROR_DEVICE
ret
@@:
; we will zero data at the end of last sector - remember it
push ecx
; terminate FAT chain
push edx
mov eax, ecx
mov edx, [ebp+FAT.fatEND]
call set_FAT
mov eax, edx
pop edx
jnc @f
.device_err4:
pop ecx
jmp .device_err3
.zero_size:
and word [edi+20], 0
and word [edi+26], 0
push 0
mov eax, ecx
@@:
; delete FAT chain
call clear_cluster_chain
jc .device_err4
; save directory
mov eax, [esp+12]
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
pop ebx
test eax, eax
jnz .device_err4
; zero last sector, ignore errors
pop ecx
pop eax
dec ecx
imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
add ecx, [ebp+FAT.DATA_START]
push eax
sar eax, 9
add ecx, eax
pop eax
and eax, 0x1FF
jz .truncate_done
push ebx eax
mov eax, ecx
lea ebx, [ebp+FAT.buffer]
call fs_read32_app
pop eax
lea edi, [ebp+FAT.buffer+eax]
push ecx
mov ecx, 0x200
sub ecx, eax
xor eax, eax
rep stosb
pop eax
call fs_write32_app
pop ebx
.truncate_done:
pop ecx eax edi
call update_disk
call fat_unlock
xor eax, eax
ret
.error_fat:
pop eax
mov byte [esp], ERROR_FAT_TABLE
jmp .pop_ret
.error_fat2:
pop eax ecx eax edi
call update_disk
call fat_unlock
movi eax, ERROR_FAT_TABLE
ret
 
;----------------------------------------------------------------
; fat_GetFileInfo - FAT implementation of getting file info
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_GetFileInfo:
cmp byte [esi], 0
jnz @f
mov eax, 2
ret
@@:
push edi
call fat_lock
stdcall hd_find_lfn, [esp+4+4]
jc .error
push ebp
xor ebp, ebp
mov esi, [ebx+16]
mov dword [esi+4], ebp
call fat_entry_to_bdfe2
pop ebp
call fat_unlock
xor eax, eax
pop edi
ret
.error:
push eax
call fat_unlock
pop eax
pop edi
ret
 
;----------------------------------------------------------------
; fat_SetFileInfo - FAT implementation of setting file info
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_SetFileInfo:
cmp byte [esi], 0
jnz @f
mov eax, 2
ret
@@:
push edi
call fat_lock
stdcall hd_find_lfn, [esp+4+4]
jc .error
push eax
mov edx, [ebx+16]
call bdfe_to_fat_entry
pop eax
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
call update_disk
call fat_unlock
pop edi
xor eax, eax
ret
.error:
push eax
call fat_unlock
pop eax
pop edi
ret
 
;----------------------------------------------------------------
; fat_Delete - FAT implementation of deleting a file/folder
; in: ebp = pointer to FAT structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
fat_Delete:
call fat_lock
cmp byte [esi], 0
jnz @f
; cannot delete root!
.access_denied:
push ERROR_ACCESS_DENIED
.pop_ret:
call fat_unlock
pop eax
xor ebx, ebx
ret
@@:
and [ebp+FAT.longname_sec1], 0
and [ebp+FAT.longname_sec2], 0
push edi
stdcall hd_find_lfn, [esp+4+4]
jnc .found
pop edi
push ERROR_FILE_NOT_FOUND
jmp .pop_ret
.found:
cmp dword [edi], '. '
jz .access_denied2
cmp dword [edi], '.. '
jz .access_denied2
test byte [edi+11], 10h
jz .dodel
; we can delete only empty folders!
pushad
mov esi, [edi+20-2]
mov si, [edi+26]
xor ecx, ecx
lea eax, [esi-2]
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
lea ebx, [ebp+FAT.buffer]
call fs_read32_sys
test eax, eax
jnz .err1
lea eax, [ebx+0x200]
add ebx, 2*0x20
.checkempty:
cmp byte [ebx], 0
jz .empty
cmp byte [ebx], 0xE5
jnz .notempty
add ebx, 0x20
cmp ebx, eax
jb .checkempty
inc ecx
cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER]
jb @f
mov eax, esi
call get_FAT
jc .err1
cmp eax, 2
jb .error_fat
cmp eax, [ebp+FAT.fatRESERVED]
jae .empty
mov esi, eax
xor ecx, ecx
@@:
lea eax, [esi-2]
imul eax, [ebp+FAT.SECTORS_PER_CLUSTER]
add eax, [ebp+FAT.DATA_START]
add eax, ecx
lea ebx, [ebp+FAT.buffer]
call fs_read32_sys
test eax, eax
lea eax, [ebx+0x200]
jz .checkempty
.err1:
popad
.err2:
pop edi
call fat_unlock
movi eax, ERROR_DEVICE
ret
.error_fat:
popad
pop edi
call fat_unlock
movi eax, ERROR_FAT_TABLE
ret
.notempty:
popad
.access_denied2:
pop edi
call fat_unlock
movi eax, ERROR_ACCESS_DENIED
ret
.empty:
popad
push eax ebx
lea ebx, [ebp+FAT.buffer]
call fs_read32_sys
test eax, eax
pop ebx eax
jnz .err2
.dodel:
push eax
mov eax, [edi+20-2]
mov ax, [edi+26]
xchg eax, [esp]
; delete folder entry
mov byte [edi], 0xE5
; delete LFN (if present)
.lfndel:
lea edx, [ebp+FAT.buffer]
cmp edi, edx
ja @f
cmp [ebp+FAT.longname_sec2], 0
jz .lfndone
push [ebp+FAT.longname_sec2]
push [ebp+FAT.longname_sec1]
pop [ebp+FAT.longname_sec2]
and [ebp+FAT.longname_sec1], 0
push ebx
mov ebx, edx
call fs_write32_sys
mov eax, [esp+4]
call fs_read32_sys
pop ebx
pop eax
lea edi, [ebp+FAT.buffer+0x200]
@@:
sub edi, 0x20
cmp byte [edi], 0xE5
jz .lfndone
cmp byte [edi+11], 0xF
jnz .lfndone
mov byte [edi], 0xE5
jmp .lfndel
.lfndone:
push ebx
lea ebx, [ebp+FAT.buffer]
call fs_write32_sys
pop ebx
; delete FAT chain
pop eax
call clear_cluster_chain
call update_disk
call fat_unlock
pop edi
xor eax, eax
ret
 
; \end{diamond}
/kernel/branches/kolibri-process/fs/fs_lfn.inc
0,0 → 1,797
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 4277 $
 
ERROR_SUCCESS = 0
ERROR_DISK_BASE = 1
ERROR_UNSUPPORTED_FS = 2
ERROR_UNKNOWN_FS = 3
ERROR_PARTITION = 4
ERROR_FILE_NOT_FOUND = 5
ERROR_END_OF_FILE = 6
ERROR_MEMORY_POINTER = 7
ERROR_DISK_FULL = 8
ERROR_FAT_TABLE = 9 ;deprecated
ERROR_FS_FAIL = 9
ERROR_ACCESS_DENIED = 10
ERROR_DEVICE = 11
 
image_of_eax EQU esp+32
image_of_ebx EQU esp+20
 
; System function 70 - files with long names (LFN)
; diamond, 2006
 
iglobal
; in this table names must be in lowercase
rootdirs:
;**********************************************
db 3,'cd0'
dd fs_OnCd0
dd fs_NextCd
db 3,'cd1'
dd fs_OnCd1
dd fs_NextCd
db 3,'cd2'
dd fs_OnCd2
dd fs_NextCd
db 3,'cd3'
dd fs_OnCd3
dd fs_NextCd
;***********************************************
db 0
 
 
virtual_root_query:
;**********************************************
dd fs_HasCd0
db 'cd0',0
dd fs_HasCd1
db 'cd1',0
dd fs_HasCd2
db 'cd2',0
dd fs_HasCd3
db 'cd3',0
;**********************************************
dd 0
endg
 
file_system_lfn_protected:
pushad
call protect_from_terminate
call file_system_lfn
call unprotect_from_terminate
popad
mov [image_of_eax], eax
mov [image_of_ebx], ebx
ret
 
file_system_lfn:
; in: ebx->fileinfo block
; operation codes:
; 0 : read file
; 1 : read folder
; 2 : create/rewrite file
; 3 : write/append to file
; 4 : set end of file
; 5 : get file/directory attributes structure
; 6 : set file/directory attributes structure
; 7 : start application
; 8 : delete file
; 9 : create directory
 
; parse file name
lea esi, [ebx+20]
lodsb
test al, al
jnz @f
mov esi, [esi]
lodsb
@@:
cmp al, '/'
jz .notcurdir
dec esi
mov ebp, esi
test al, al
jnz @f
xor ebp, ebp
@@:
mov esi, [current_slot]
mov esi, [esi+APPDATA.cur_dir]
jmp .parse_normal
.notcurdir:
cmp byte [esi], 0
jz .rootdir
call process_replace_file_name
.parse_normal:
cmp dword [ebx], 7
jne @F
mov edx, [ebx+4]
mov ebx, [ebx+8]
call fs_execute; esi+ebp, ebx, edx
mov [image_of_eax], eax
ret
@@:
mov edi, rootdirs-8
xor ecx, ecx
push esi
.scan1:
pop esi
add edi, ecx
scasd
scasd
mov cl, byte [edi]
test cl, cl
jz .notfound_try
inc edi
push esi
@@:
lodsb
or al, 20h
scasb
loopz @b
jnz .scan1
lodsb
cmp al, '/'
jz .found1
test al, al
jnz .scan1
pop eax
; directory /xxx
.maindir:
mov esi, [edi+4]
.maindir_noesi:
cmp dword [ebx], 1
jnz .access_denied
xor eax, eax
mov ebp, [ebx+12] ;количество блоков для считывания
mov edx, [ebx+16] ;куда записывать рузельтат
; add edx, std_application_base_address
push dword [ebx+4] ; first block
mov ebx, [ebx+8] ; flags
; ebx=flags, [esp]=first block, ebp=number of blocks, edx=return area, esi='Next' handler
mov edi, edx
push ecx
mov ecx, 32/4
rep stosd
pop ecx
mov byte [edx], 1 ; version
.maindir_loop:
call esi
jc .maindir_done
inc dword [edx+8]
dec dword [esp]
jns .maindir_loop
dec ebp
js .maindir_loop
inc dword [edx+4]
mov dword [edi], 0x10 ; attributes: folder
mov dword [edi+4], 1 ; name type: UNICODE
push eax
xor eax, eax
add edi, 8
push ecx
mov ecx, 40/4-2
rep stosd
pop ecx
pop eax
push eax edx
; convert number in eax to decimal UNICODE string
push edi
push ecx
push -'0'
mov ecx, 10
@@:
xor edx, edx
div ecx
push edx
test eax, eax
jnz @b
@@:
pop eax
add al, '0'
stosb
test bl, 1 ; UNICODE name?
jz .ansi2
mov byte [edi], 0
inc edi
.ansi2:
test al, al
jnz @b
mov byte [edi-1], 0
pop ecx
pop edi
; UNICODE name length is 520 bytes, ANSI - 264
add edi, 520
test bl, 1
jnz @f
sub edi, 520-264
@@:
pop edx eax
jmp .maindir_loop
.maindir_done:
pop eax
mov ebx, [edx+4]
xor eax, eax
dec ebp
js @f
mov al, ERROR_END_OF_FILE
@@:
mov [image_of_eax], eax
mov [image_of_ebx], ebx
ret
; directory /
.rootdir:
cmp dword [ebx], 1 ; read folder?
jz .readroot
.access_denied:
mov dword [image_of_eax], 10 ; access denied
ret
 
.readroot:
; virtual root folder - special handler
mov ebp, [ebx+12]
mov edx, [ebx+16]
; add edx, std_application_base_address
push dword [ebx+4] ; first block
mov ebx, [ebx+8] ; flags
xor eax, eax
; eax=0, [esp]=first block, ebx=flags, ebp=number of blocks, edx=return area
mov edi, edx
mov ecx, 32/4
rep stosd
mov byte [edx], 1 ; version
sub esp, 16
.readroot_ah_loop2:
push edi
lea edi, [esp+4]
call dyndisk_enum_root
pop edi
test eax, eax
jz .readroot_done_dynamic
inc dword [edx+8]
dec dword [esp+16]
jns .readroot_ah_loop2
dec ebp
js .readroot_ah_loop2
push eax
xor eax, eax
inc dword [edx+4]
mov dword [edi], 0x10 ; attributes: folder
mov dword [edi+4], ebx
add edi, 8
mov ecx, 40/4-2
rep stosd
push esi edi
lea esi, [esp+12]
@@:
lodsb
stosb
test bl, 1
jz .ansi3
mov byte [edi], 0
inc edi
.ansi3:
test al, al
jnz @b
pop edi esi eax
add edi, 520
test bl, 1
jnz .readroot_ah_loop2
sub edi, 520-264
jmp .readroot_ah_loop2
.readroot_done_dynamic:
add esp, 16
mov esi, virtual_root_query
.readroot_loop:
cmp dword [esi], eax
jz .readroot_done
call dword [esi]
add esi, 4
test eax, eax
jnz @f
.readroot_next:
or ecx, -1
xchg esi, edi
repnz scasb
xchg esi, edi
jmp .readroot_loop
@@:
xor eax, eax
inc dword [edx+8]
dec dword [esp]
jns .readroot_next
dec ebp
js .readroot_next
inc dword [edx+4]
mov dword [edi], 0x10 ; attributes: folder
mov dword [edi+4], ebx ; name type: UNICODE
add edi, 8
mov ecx, 40/4-2
rep stosd
push edi
@@:
lodsb
stosb
test bl, 1
jz .ansi
mov byte [edi], 0
inc edi
.ansi:
test eax, eax
jnz @b
pop edi
add edi, 520
test bl, 1
jnz .readroot_loop
sub edi, 520-264
jmp .readroot_loop
.readroot_done:
pop eax
mov ebx, [edx+4]
xor eax, eax
dec ebp
js @f
mov al, ERROR_END_OF_FILE
@@:
mov [image_of_eax], eax
mov [image_of_ebx], ebx
ret
.notfound_try:
call dyndisk_handler
.notfound:
mov dword [image_of_eax], ERROR_FILE_NOT_FOUND
and dword [image_of_ebx], 0
ret
 
.notfounda:
cmp edi, esp
jnz .notfound
call dword [edi+4]
add esp, 16
jmp .notfound
 
.found1:
pop eax
cmp byte [esi], 0
jz .maindir
.found2:
; read partition number
xor ecx, ecx
xor eax, eax
@@:
lodsb
cmp al, '/'
jz .done1
test al, al
jz .done1
sub al, '0'
cmp al, 9
ja .notfounda
lea ecx, [ecx*5]
lea ecx, [ecx*2+eax]
jmp @b
.done1:
jecxz .notfounda
test al, al
jnz @f
dec esi
@@:
cmp byte [esi], 0
jnz @f
test ebp, ebp
jz @f
mov esi, ebp
xor ebp, ebp
@@:
; now [edi] contains handler address, ecx - partition number,
; esi points to ASCIIZ string - rest of name
jmp dword [edi]
 
; handlers for devices
; in: ecx = 0 => query virtual directory /xxx
; in: ecx = partition number
; esi -> relative (for device) name
; ebx -> fileinfo
; ebp = 0 or pointer to rest of name from folder addressed by esi
; out: [image_of_eax]=image of eax, [image_of_ebx]=image of ebx
 
fs_NotImplemented:
mov eax, 2
ret
 
;*******************************************************
fs_OnCd0:
call reserve_cd
mov [ChannelNumber], 1
mov [DiskNumber], 0
push 6
push 1
jmp fs_OnCd
fs_OnCd1:
call reserve_cd
mov [ChannelNumber], 1
mov [DiskNumber], 1
push 4
push 2
jmp fs_OnCd
fs_OnCd2:
call reserve_cd
mov [ChannelNumber], 2
mov [DiskNumber], 0
push 2
push 3
jmp fs_OnCd
fs_OnCd3:
call reserve_cd
mov [ChannelNumber], 2
mov [DiskNumber], 1
push 0
push 4
fs_OnCd:
call reserve_cd_channel
pop eax
mov [cdpos], eax
pop eax
cmp ecx, 0x100
jae .nf
push ecx ebx
mov cl, al
mov bl, [DRIVE_DATA+1]
shr bl, cl
test bl, 2
pop ebx ecx
 
jnz @f
.nf:
call free_cd_channel
and [cd_status], 0
mov dword [image_of_eax], 5 ; not found
ret
@@:
mov ecx, [ebx+12]
mov edx, [ebx+16]
; add edx, std_application_base_address
mov eax, [ebx]
cmp eax, fs_NumCdServices
jae .not_impl
add ebx, 4
call dword [fs_CdServices + eax*4]
call free_cd_channel
and [cd_status], 0
mov [image_of_eax], eax
mov [image_of_ebx], ebx
ret
.not_impl:
call free_cd_channel
and [cd_status], 0
mov dword [image_of_eax], 2 ; not implemented
ret
 
fs_CdServices:
dd fs_CdRead
dd fs_CdReadFolder
dd fs_NotImplemented
dd fs_NotImplemented
dd fs_NotImplemented
dd fs_CdGetFileInfo
dd fs_NotImplemented
dd 0
dd fs_NotImplemented
dd fs_NotImplemented
fs_NumCdServices = ($ - fs_CdServices)/4
 
;*******************************************************
fs_HasCd0:
test byte [DRIVE_DATA+1], 10000000b
setnz al
ret
fs_HasCd1:
test byte [DRIVE_DATA+1], 00100000b
setnz al
ret
fs_HasCd2:
test byte [DRIVE_DATA+1], 00001000b
setnz al
ret
fs_HasCd3:
test byte [DRIVE_DATA+1], 00000010b
setnz al
ret
;*******************************************************
 
; fs_NextXXX functions:
; in: eax = partition number, from which start to scan
; out: CF=1 => no more partitions
; CF=0 => eax=next partition number
 
;*******************************************************
fs_NextCd:
; we always have /cdX/1
test eax, eax
stc
jnz @f
mov al, 1
clc
@@:
ret
;*******************************************************
 
;-----------------------------------------------------------------------------
process_replace_file_name:
; in
; esi - path with filename(f.70)
;
; out
; ebp - full filename
pushfd
cli
mov ebp, [full_file_name_table]
xor edi, edi
.loop:
cmp edi, [full_file_name_table.size]
jae .notfound
push esi edi
shl edi, 7 ; edi*128
add edi, ebp
@@:
cmp byte [edi], 0 ; end of dir_name
jz .dest_done
lodsb
test al, al
jz .cont
or al, 20h ; 32 - space char
scasb
jz @b
jmp .cont
.dest_done:
cmp byte [esi], 0
jz .found
cmp byte [esi], '/'
jnz .cont
inc esi
jmp .found
.cont:
pop edi esi
inc edi
jmp .loop
.found:
pop edi eax
shl edi, 7 ; edi*128
add edi, ebp
mov ebp, esi
cmp byte [esi], 0
lea esi, [edi+64]
jnz .ret
.notfound:
xor ebp, ebp
.ret:
popfd
ret
;-----------------------------------------------------------------------------
uglobal
lock_flag_for_f30_3 rb 1
endg
sys_current_directory:
; mov esi, [current_slot]
; mov esi, [esi+APPDATA.cur_dir]
; mov edx, esi
 
;get length string of appdata.cur_dir
mov eax, [current_slot]
mov edi, [eax+APPDATA.cur_dir]
 
dec ebx
jz .set
dec ebx
jz .get
dec ebx
jz .mount_additional_directory
ret
 
.mount_additional_directory:
; sysfunction 30.2: [for app] eax=30,ebx=3,ecx->dir name+dir path (128)
; for our code: nothing
 
; check lock of the function
cmp [lock_flag_for_f30_3], 1
je @f
mov esi, ecx
mov edi, sysdir_name1
; copying fake directory name
mov ecx, 63
pushfd
cli
cld
rep movsb
; terminator of name, in case if we get the inlet trash
inc esi
xor eax, eax
stosb
; copying real directory path for mounting
mov ecx, 63
rep movsb
; terminator of name, in case if we get the inlet trash
xor eax, eax
stosb
; increase the pointer of inputs for procedure "process_replace_file_name"
mov [full_file_name_table.size], 2
; block the ability to call f.30.3 because for one session is necessary
; for us only once
mov [lock_flag_for_f30_3], 1
popfd
@@:
ret
.get:
; sysfunction 30.2: [for app] eax=30,ebx=2,ecx->buffer,edx=len
; for our code: ebx->buffer,ecx=len
max_cur_dir equ 0x1000
 
mov ebx, edi
 
push ecx
push edi
 
xor eax, eax
mov ecx, max_cur_dir
 
repne scasb ;find zerro at and string
jnz .error ; no zero in cur_dir: internal error, should not happen
 
sub edi, ebx ;lenght for copy
inc edi
mov [esp+32+8], edi ;return in eax
 
cmp edx, edi
jbe @f
mov edx, edi
@@:
;source string
pop esi
;destination string
pop edi
cmp edx, 1
jbe .ret
 
mov al, '/' ;start string with '/'
stosb
mov ecx, edx
rep movsb ;copy string
.ret:
ret
 
.error:
add esp, 8
or dword [esp+32], -1 ;error not found zerro at string ->[eax+APPDATA.cur_dir]
ret
.set:
; sysfunction 30.1: [for app] eax=30,ebx=1,ecx->string
; for our code: ebx->string to set
; use generic resolver with APPDATA.cur_dir as destination
push max_cur_dir ;0x1000
push edi ;destination
mov ebx, ecx
call get_full_file_name
ret
 
; in: ebx = file name, [esp+4] = destination, [esp+8] = sizeof destination
; destroys all registers except ebp,esp
get_full_file_name:
push ebp
mov esi, [current_slot]
mov esi, [esi+APPDATA.cur_dir]
mov edx, esi
@@:
inc esi
cmp byte [esi-1], 0
jnz @b
dec esi
cmp byte [ebx], '/'
jz .set_absolute
; string gives relative path
mov edi, [esp+8] ; destination
.relative:
cmp byte [ebx], 0
jz .set_ok
cmp word [ebx], '.'
jz .set_ok
cmp word [ebx], './'
jnz @f
add ebx, 2
jmp .relative
@@:
cmp word [ebx], '..'
jnz .doset_relative
cmp byte [ebx+2], 0
jz @f
cmp byte [ebx+2], '/'
jnz .doset_relative
@@:
dec esi
cmp byte [esi], '/'
jnz @b
add ebx, 3
jmp .relative
.set_ok:
cmp edx, edi ; is destination equal to APPDATA.cur_dir?
jz .set_ok.cur_dir
sub esi, edx
cmp esi, [esp+12]
jb .set_ok.copy
.fail:
mov byte [edi], 0
xor eax, eax ; fail
pop ebp
ret 8
.set_ok.copy:
mov ecx, esi
mov esi, edx
rep movsb
mov byte [edi], 0
.ret.ok:
mov al, 1 ; ok
pop ebp
ret 8
.set_ok.cur_dir:
mov byte [esi], 0
jmp .ret.ok
.doset_relative:
cmp edx, edi
jz .doset_relative.cur_dir
sub esi, edx
cmp esi, [esp+12]
jae .fail
mov ecx, esi
mov esi, edx
mov edx, edi
rep movsb
jmp .doset_relative.copy
.doset_relative.cur_dir:
mov edi, esi
.doset_relative.copy:
add edx, [esp+12]
mov byte [edi], '/'
inc edi
cmp edi, edx
jae .overflow
@@:
mov al, [ebx]
inc ebx
stosb
test al, al
jz .ret.ok
cmp edi, edx
jb @b
.overflow:
dec edi
jmp .fail
.set_absolute:
lea esi, [ebx+1]
call process_replace_file_name
mov edi, [esp+8]
mov edx, [esp+12]
add edx, edi
.set_copy:
lodsb
stosb
test al, al
jz .set_part2
.set_copy_cont:
cmp edi, edx
jb .set_copy
jmp .overflow
.set_part2:
mov esi, ebp
xor ebp, ebp
test esi, esi
jz .ret.ok
mov byte [edi-1], '/'
jmp .set_copy_cont
/kernel/branches/kolibri-process/fs/iso9660.inc
0,0 → 1,759
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3742 $
 
 
uglobal
cd_current_pointer_of_input dd 0
cd_current_pointer_of_input_2 dd 0
cd_mem_location dd 0
cd_counter_block dd 0
IDE_Channel_1 db 0
IDE_Channel_2 db 0
endg
 
reserve_cd:
 
cli
cmp [cd_status], 0
je reserve_ok2
 
sti
call change_task
jmp reserve_cd
 
reserve_ok2:
 
push eax
mov eax, [CURRENT_TASK]
shl eax, 5
mov eax, [eax+CURRENT_TASK+TASKDATA.pid]
mov [cd_status], eax
pop eax
sti
ret
 
reserve_cd_channel:
cmp [ChannelNumber], 1
jne .IDE_Channel_2
.IDE_Channel_1:
pushad
mov ecx, ide_channel1_mutex
call mutex_lock
mov [IDE_Channel_1], 1
popad
ret
.IDE_Channel_2:
pushad
mov ecx, ide_channel2_mutex
call mutex_lock
mov [IDE_Channel_2], 1
popad
ret
 
free_cd_channel:
cmp [ChannelNumber], 1
jne .IDE_Channel_2
.IDE_Channel_1:
mov [IDE_Channel_1], 0
pushad
mov ecx, ide_channel1_mutex
call mutex_unlock
popad
ret
.IDE_Channel_2:
mov [IDE_Channel_2], 0
pushad
mov ecx, ide_channel2_mutex
call mutex_unlock
popad
ret
 
uglobal
cd_status dd 0
endg
 
;----------------------------------------------------------------
;
; fs_CdRead - LFN variant for reading CD disk
;
; esi points to filename /dir1/dir2/.../dirn/file,0
; ebx pointer to 64-bit number = first wanted byte, 0+
; may be ebx=0 - start from first byte
; ecx number of bytes to read, 0+
; edx mem location to return data
;
; ret ebx = bytes read or 0xffffffff file not found
; eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
fs_CdRead:
push edi
cmp byte [esi], 0
jnz @f
.noaccess:
pop edi
.noaccess_2:
or ebx, -1
mov eax, ERROR_ACCESS_DENIED
ret
 
.noaccess_3:
pop eax edx ecx edi
jmp .noaccess_2
 
@@:
call cd_find_lfn
jnc .found
pop edi
cmp [DevErrorCode], 0
jne .noaccess_2
or ebx, -1
mov eax, ERROR_FILE_NOT_FOUND
ret
 
.found:
mov edi, [cd_current_pointer_of_input]
test byte [edi+25], 10b; do not allow read directories
jnz .noaccess
test ebx, ebx
jz .l1
cmp dword [ebx+4], 0
jz @f
xor ebx, ebx
.reteof:
mov eax, 6; end of file
pop edi
ret
@@:
mov ebx, [ebx]
.l1:
push ecx edx
push 0
mov eax, [edi+10] ; реальный размер файловой секции
sub eax, ebx
jb .eof
cmp eax, ecx
jae @f
mov ecx, eax
mov byte [esp], 6
@@:
mov eax, [edi+2]
mov [CDSectorAddress], eax
; now eax=cluster, ebx=position, ecx=count, edx=buffer for data
.new_sector:
test ecx, ecx
jz .done
sub ebx, 2048
jae .next
add ebx, 2048
jnz .incomplete_sector
cmp ecx, 2048
jb .incomplete_sector
; we may read and memmove complete sector
mov [CDDataBuf_pointer], edx
call ReadCDWRetr; читаем сектор файла
cmp [DevErrorCode], 0
jne .noaccess_3
add edx, 2048
sub ecx, 2048
.next:
inc dword [CDSectorAddress]
jmp .new_sector
.incomplete_sector:
; we must read and memmove incomplete sector
mov [CDDataBuf_pointer], CDDataBuf
call ReadCDWRetr; читаем сектор файла
cmp [DevErrorCode], 0
jne .noaccess_3
push ecx
add ecx, ebx
cmp ecx, 2048
jbe @f
mov ecx, 2048
@@:
sub ecx, ebx
push edi esi ecx
mov edi, edx
lea esi, [CDDataBuf + ebx]
cld
rep movsb
pop ecx esi edi
add edx, ecx
sub [esp], ecx
pop ecx
xor ebx, ebx
jmp .next
 
.done:
mov ebx, edx
pop eax edx ecx edi
sub ebx, edx
ret
.eof:
mov ebx, edx
pop eax edx ecx
sub ebx, edx
jmp .reteof
 
;----------------------------------------------------------------
;
; fs_CdReadFolder - LFN variant for reading CD disk folder
;
; esi points to filename /dir1/dir2/.../dirn/file,0
; ebx pointer to structure 32-bit number = first wanted block, 0+
; & flags (bitfields)
; flags: bit 0: 0=ANSI names, 1=UNICODE names
; ecx number of blocks to read, 0+
; edx mem location to return data
;
; ret ebx = blocks read or 0xffffffff folder not found
; eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
fs_CdReadFolder:
push edi
call cd_find_lfn
jnc .found
pop edi
cmp [DevErrorCode], 0
jne .noaccess_1
or ebx, -1
mov eax, ERROR_FILE_NOT_FOUND
ret
.found:
mov edi, [cd_current_pointer_of_input]
test byte [edi+25], 10b ; do not allow read directories
jnz .found_dir
pop edi
.noaccess_1:
or ebx, -1
mov eax, ERROR_ACCESS_DENIED
ret
.found_dir:
mov eax, [edi+2] ; eax=cluster
mov [CDSectorAddress], eax
mov eax, [edi+10] ; размер директрории
.doit:
; init header
push eax ecx
mov edi, edx
mov ecx, 32/4
xor eax, eax
rep stosd
pop ecx eax
mov byte [edx], 1 ; version
mov [cd_mem_location], edx
add [cd_mem_location], 32
; начинаем переброску БДВК в УСВК
;.mainloop:
mov [cd_counter_block], dword 0
dec dword [CDSectorAddress]
push ecx
.read_to_buffer:
inc dword [CDSectorAddress]
mov [CDDataBuf_pointer], CDDataBuf
call ReadCDWRetr ; читаем сектор директории
cmp [DevErrorCode], 0
jne .noaccess_1
call .get_names_from_buffer
sub eax, 2048
; директория закончилась?
ja .read_to_buffer
mov edi, [cd_counter_block]
mov [edx+8], edi
mov edi, [ebx]
sub [edx+4], edi
xor eax, eax
dec ecx
js @f
mov al, ERROR_END_OF_FILE
@@:
pop ecx edi
mov ebx, [edx+4]
ret
 
.get_names_from_buffer:
mov [cd_current_pointer_of_input_2], CDDataBuf
push eax esi edi edx
.get_names_from_buffer_1:
call cd_get_name
jc .end_buffer
inc dword [cd_counter_block]
mov eax, [cd_counter_block]
cmp [ebx], eax
jae .get_names_from_buffer_1
test ecx, ecx
jz .get_names_from_buffer_1
mov edi, [cd_counter_block]
mov [edx+4], edi
dec ecx
mov esi, ebp
mov edi, [cd_mem_location]
add edi, 40
test dword [ebx+4], 1; 0=ANSI, 1=UNICODE
jnz .unicode
; jmp .unicode
.ansi:
cmp [cd_counter_block], 2
jbe .ansi_parent_directory
cld
lodsw
xchg ah, al
call uni2ansi_char
cld
stosb
; проверка конца файла
mov ax, [esi]
cmp ax, word 3B00h; сепаратор конца файла ';'
je .cd_get_parameters_of_file_1
; проверка для файлов не заканчивающихся сепаратором
movzx eax, byte [ebp-33]
add eax, ebp
sub eax, 34
cmp esi, eax
je .cd_get_parameters_of_file_1
; проверка конца папки
movzx eax, byte [ebp-1]
add eax, ebp
cmp esi, eax
jb .ansi
.cd_get_parameters_of_file_1:
mov [edi], byte 0
call cd_get_parameters_of_file
add [cd_mem_location], 304
jmp .get_names_from_buffer_1
 
.ansi_parent_directory:
cmp [cd_counter_block], 2
je @f
mov [edi], byte '.'
inc edi
jmp .cd_get_parameters_of_file_1
@@:
mov [edi], word '..'
add edi, 2
jmp .cd_get_parameters_of_file_1
 
.unicode:
cmp [cd_counter_block], 2
jbe .unicode_parent_directory
cld
movsw
; проверка конца файла
mov ax, [esi]
cmp ax, word 3B00h; сепаратор конца файла ';'
je .cd_get_parameters_of_file_2
; проверка для файлов не заканчивающихся сепаратором
movzx eax, byte [ebp-33]
add eax, ebp
sub eax, 34
cmp esi, eax
je .cd_get_parameters_of_file_2
; проверка конца папки
movzx eax, byte [ebp-1]
add eax, ebp
cmp esi, eax
jb .unicode
.cd_get_parameters_of_file_2:
mov [edi], word 0
call cd_get_parameters_of_file
add [cd_mem_location], 560
jmp .get_names_from_buffer_1
 
.unicode_parent_directory:
cmp [cd_counter_block], 2
je @f
mov [edi], word 2E00h; '.'
add edi, 2
jmp .cd_get_parameters_of_file_2
@@:
mov [edi], dword 2E002E00h; '..'
add edi, 4
jmp .cd_get_parameters_of_file_2
 
.end_buffer:
pop edx edi esi eax
ret
 
cd_get_parameters_of_file:
mov edi, [cd_mem_location]
cd_get_parameters_of_file_1:
; получаем атрибуты файла
xor eax, eax
; файл не архивировался
inc eax
shl eax, 1
; это каталог?
test [ebp-8], byte 2
jz .file
inc eax
.file:
; метка тома не как в FAT, в этом виде отсутсвует
; файл не является системным
shl eax, 3
; файл является скрытым? (атрибут существование)
test [ebp-8], byte 1
jz .hidden
inc eax
.hidden:
shl eax, 1
; файл всегда только для чтения, так как это CD
inc eax
mov [edi], eax
; получаем время для файла
;час
movzx eax, byte [ebp-12]
shl eax, 8
;минута
mov al, [ebp-11]
shl eax, 8
;секунда
mov al, [ebp-10]
;время создания файла
mov [edi+8], eax
;время последнего доступа
mov [edi+16], eax
;время последней записи
mov [edi+24], eax
; получаем дату для файла
;год
movzx eax, byte [ebp-15]
add eax, 1900
shl eax, 8
;месяц
mov al, [ebp-14]
shl eax, 8
;день
mov al, [ebp-13]
;дата создания файла
mov [edi+12], eax
;время последнего доступа
mov [edi+20], eax
;время последней записи
mov [edi+28], eax
; получаем тип данных имени
xor eax, eax
test dword [ebx+4], 1; 0=ANSI, 1=UNICODE
jnz .unicode_1
mov [edi+4], eax
jmp @f
.unicode_1:
inc eax
mov [edi+4], eax
@@:
; получаем размер файла в байтах
xor eax, eax
mov [edi+32+4], eax
mov eax, [ebp-23]
mov [edi+32], eax
ret
 
;----------------------------------------------------------------
;
; fs_CdGetFileInfo - LFN variant for CD
; get file/directory attributes structure
;
;----------------------------------------------------------------
fs_CdGetFileInfo:
cmp byte [esi], 0
jnz @f
mov eax, 2
ret
@@:
push edi
call cd_find_lfn
pushfd
cmp [DevErrorCode], 0
jz @f
popfd
pop edi
mov eax, 11
ret
@@:
popfd
jnc @f
pop edi
mov eax, ERROR_FILE_NOT_FOUND
ret
@@:
 
mov edi, edx
push ebp
mov ebp, [cd_current_pointer_of_input]
add ebp, 33
call cd_get_parameters_of_file_1
pop ebp
and dword [edi+4], 0
pop edi
xor eax, eax
ret
 
;----------------------------------------------------------------
cd_find_lfn:
mov [cd_appl_data], 0
; in: esi+ebp -> name
; out: CF=1 - file not found
; else CF=0 and [cd_current_pointer_of_input] direntry
push eax esi
; 16 сектор начало набора дескрипторов томов
 
call WaitUnitReady
cmp [DevErrorCode], 0
jne .access_denied
 
call prevent_medium_removal
; тестовое чтение
mov [CDSectorAddress], dword 16
mov [CDDataBuf_pointer], CDDataBuf
call ReadCDWRetr;_1
cmp [DevErrorCode], 0
jne .access_denied
 
; вычисление последней сессии
call WaitUnitReady
cmp [DevErrorCode], 0
jne .access_denied
call Read_TOC
mov ah, [CDDataBuf+4+4]
mov al, [CDDataBuf+4+5]
shl eax, 16
mov ah, [CDDataBuf+4+6]
mov al, [CDDataBuf+4+7]
add eax, 15
mov [CDSectorAddress], eax
; mov [CDSectorAddress],dword 15
mov [CDDataBuf_pointer], CDDataBuf
 
.start:
inc dword [CDSectorAddress]
call ReadCDWRetr;_1
cmp [DevErrorCode], 0
jne .access_denied
 
.start_check:
; проверка на вшивость
cmp [CDDataBuf+1], dword 'CD00'
jne .access_denied
cmp [CDDataBuf+5], byte '1'
jne .access_denied
; сектор является терминатором набор дескрипторов томов?
cmp [CDDataBuf], byte 0xff
je .access_denied
; сектор является дополнительным и улучшенным дескриптором тома?
cmp [CDDataBuf], byte 0x2
jne .start
; сектор является дополнительным дескриптором тома?
cmp [CDDataBuf+6], byte 0x1
jne .start
 
; параметры root директрории
mov eax, [CDDataBuf+0x9c+2]; начало root директрории
mov [CDSectorAddress], eax
mov eax, [CDDataBuf+0x9c+10]; размер root директрории
cmp byte [esi], 0
jnz @f
mov [cd_current_pointer_of_input], CDDataBuf+0x9c
jmp .done
@@:
; начинаем поиск
.mainloop:
dec dword [CDSectorAddress]
.read_to_buffer:
inc dword [CDSectorAddress]
mov [CDDataBuf_pointer], CDDataBuf
call ReadCDWRetr ; читаем сектор директории
cmp [DevErrorCode], 0
jne .access_denied
push ebp
call cd_find_name_in_buffer
pop ebp
jnc .found
sub eax, 2048
; директория закончилась?
cmp eax, 0
ja .read_to_buffer
; нет искомого элемента цепочки
.access_denied:
pop esi eax
mov [cd_appl_data], 1
stc
ret
; искомый элемент цепочки найден
.found:
; конец пути файла
cmp byte [esi-1], 0
jz .done
.nested:
mov eax, [cd_current_pointer_of_input]
push dword [eax+2]
pop dword [CDSectorAddress] ; начало директории
mov eax, [eax+2+8]; размер директории
jmp .mainloop
; указатель файла найден
.done:
test ebp, ebp
jz @f
mov esi, ebp
xor ebp, ebp
jmp .nested
@@:
pop esi eax
mov [cd_appl_data], 1
clc
ret
 
cd_find_name_in_buffer:
mov [cd_current_pointer_of_input_2], CDDataBuf
.start:
call cd_get_name
jc .not_found
call cd_compare_name
jc .start
.found:
clc
ret
.not_found:
stc
ret
 
cd_get_name:
push eax
mov ebp, [cd_current_pointer_of_input_2]
mov [cd_current_pointer_of_input], ebp
mov eax, [ebp]
test eax, eax ; входы закончились?
jz .next_sector
cmp ebp, CDDataBuf+2048 ; буфер закончился?
jae .next_sector
movzx eax, byte [ebp]
add [cd_current_pointer_of_input_2], eax; следующий вход каталога
add ebp, 33; указатель установлен на начало имени
pop eax
clc
ret
.next_sector:
pop eax
stc
ret
 
cd_compare_name:
; compares ASCIIZ-names, case-insensitive (cp866 encoding)
; in: esi->name, ebp->name
; out: if names match: ZF=1 and esi->next component of name
; else: ZF=0, esi is not changed
; destroys eax
push esi eax edi
mov edi, ebp
.loop:
cld
lodsb
push eax
call char_todown
call ansi2uni_char
xchg ah, al
scasw
pop eax
je .coincides
call char_toupper
call ansi2uni_char
xchg ah, al
sub edi, 2
scasw
jne .name_not_coincide
.coincides:
cmp [esi], byte '/'; разделитель пути, конец имени текущего элемента
je .done
cmp [esi], byte 0; разделитель пути, конец имени текущего элемента
je .done
jmp .loop
.name_not_coincide:
pop edi eax esi
stc
ret
.done:
; проверка конца файла
cmp [edi], word 3B00h; сепаратор конца файла ';'
je .done_1
; проверка для файлов не заканчивающихся сепаратором
movzx eax, byte [ebp-33]
add eax, ebp
sub eax, 34
cmp edi, eax
je .done_1
; проверка конца папки
movzx eax, byte [ebp-1]
add eax, ebp
cmp edi, eax
jne .name_not_coincide
.done_1:
pop edi eax
add esp, 4
inc esi
clc
ret
 
char_todown:
; convert character to uppercase, using cp866 encoding
; in: al=symbol
; out: al=converted symbol
cmp al, 'A'
jb .ret
cmp al, 'Z'
jbe .az
cmp al, 0x80 ; 'А'
jb .ret
cmp al, 0x90 ; 'Р'
jb .rus1
cmp al, 0x9F ; 'Я'
ja .ret
; 0x90-0x9F -> 0xE0-0xEF
add al, 0xE0-0x90
.ret:
ret
.rus1:
; 0x80-0x8F -> 0xA0-0xAF
.az:
add al, 0x20
ret
 
uni2ansi_char:
; convert UNICODE character in al to ANSI character in ax, using cp866 encoding
; in: ax=UNICODE character
; out: al=converted ANSI character
cmp ax, 0x80
jb .ascii
cmp ax, 0x401
jz .yo1
cmp ax, 0x451
jz .yo2
cmp ax, 0x410
jb .unk
cmp ax, 0x440
jb .rus1
cmp ax, 0x450
jb .rus2
.unk:
mov al, '_'
jmp .doit
.yo1:
mov al, 0xF0 ; 'Ё' in cp866
jmp .doit
.yo2:
mov al, 0xF1 ; 'ё' in cp866
jmp .doit
.rus1:
; 0x410-0x43F -> 0x80-0xAF
add al, 0x70
jmp .doit
.rus2:
; 0x440-0x44F -> 0xE0-0xEF
add al, 0xA0
.ascii:
.doit:
ret
/kernel/branches/kolibri-process/fs/ntfs.inc
0,0 → 1,1926
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
$Revision: 3742 $
 
struct NTFS PARTITION
Lock MUTEX ? ; currently operations with one partition
; can not be executed in parallel since the
; legacy code is not ready; this mutex guards
; all operations
sectors_per_cluster dd ?
mft_cluster dd ?
mftmirr_cluster dd ?
frs_size dd ? ; FRS size in bytes
iab_size dd ? ; IndexAllocationBuffer size in bytes
frs_buffer dd ?
iab_buffer dd ?
mft_retrieval dd ?
mft_retrieval_size dd ?
mft_retrieval_alloc dd ?
mft_retrieval_end dd ?
cur_index_size dd ?
cur_index_buf dd ?
 
ntfs_cur_attr dd ?
ntfs_cur_iRecord dd ?
ntfs_cur_offs dd ? ; in sectors
ntfs_cur_size dd ? ; in sectors
ntfs_cur_buf dd ?
ntfs_cur_read dd ? ; [output]
ntfs_bCanContinue db ?
rb 3
 
cur_subnode_size dd ?
ntfs_attr_iRecord dd ?
ntfs_attr_iBaseRecord dd ?
ntfs_attr_offs dd ?
ntfs_attr_list dd ?
ntfs_attr_size dq ?
ntfs_cur_tail dd ?
 
ntfs_attrlist_buf rb 0x400
ntfs_attrlist_mft_buf rb 0x400
ntfs_bitmap_buf rb 0x400
ends
 
iglobal
align 4
ntfs_user_functions:
dd ntfs_free
dd (ntfs_user_functions_end - ntfs_user_functions - 4) / 4
dd ntfs_Read
dd ntfs_ReadFolder
dd ntfs_Rewrite
dd ntfs_Write
dd ntfs_SetFileEnd
dd ntfs_GetFileInfo
dd ntfs_SetFileInfo
dd 0
dd ntfs_Delete
dd ntfs_CreateFolder
ntfs_user_functions_end:
endg
 
ntfs_test_bootsec:
; in: ebx->buffer, edx=size of partition
; out: CF set <=> invalid
; 1. Name=='NTFS '
cmp dword [ebx+3], 'NTFS'
jnz .no
cmp dword [ebx+7], ' '
jnz .no
; 2. Number of bytes per sector is the same as for physical device
; (that is, 0x200 for hard disk)
cmp word [ebx+11], 0x200
jnz .no
; 3. Number of sectors per cluster must be power of 2
movzx eax, byte [ebx+13]
dec eax
js .no
test al, [ebx+13]
jnz .no
; 4. FAT parameters must be zero
cmp word [ebx+14], 0
jnz .no
cmp dword [ebx+16], 0
jnz .no
cmp byte [ebx+20], 0
jnz .no
cmp word [ebx+22], 0
jnz .no
cmp dword [ebx+32], 0
jnz .no
; 5. Number of sectors <= partition size
cmp dword [ebx+0x2C], 0
ja .no
cmp [ebx+0x28], edx
ja .no
; 6. $MFT and $MFTMirr clusters must be within partition
cmp dword [ebx+0x34], 0
ja .no
push edx
movzx eax, byte [ebx+13]
mul dword [ebx+0x30]
test edx, edx
pop edx
jnz .no
cmp eax, edx
ja .no
cmp dword [ebx+0x3C], 0
ja .no
push edx
movzx eax, byte [ebx+13]
mul dword [ebx+0x38]
test edx, edx
pop edx
jnz .no
cmp eax, edx
ja .no
; 7. Clusters per FRS must be either negative and in [-31,-9] or positive and power of 2
movsx eax, byte [ebx+0x40]
cmp al, -31
jl .no
cmp al, -9
jle @f
dec eax
js .no
test [ebx+0x40], al
jnz .no
@@:
; 8. Same for clusters per IndexAllocationBuffer
movsx eax, byte [ebx+0x44]
cmp al, -31
jl .no
cmp al, -9
jle @f
dec eax
js .no
test [ebx+0x44], al
jnz .no
@@:
; OK, this is correct NTFS bootsector
clc
ret
.no:
; No, this bootsector isn't NTFS
stc
ret
 
proc ntfs_create_partition
mov edx, dword [ebp+PARTITION.Length]
cmp dword [esp+4], 0
jz .boot_read_ok
add ebx, 512
lea eax, [edx-1]
call fs_read32_sys
test eax, eax
jnz @f
call ntfs_test_bootsec
jnc .ntfs_setup
@@:
mov eax, edx
shr eax, 1
call fs_read32_sys
test eax, eax
jnz .nope ; no chance...
.boot_read_ok:
call ntfs_test_bootsec
jnc .ntfs_setup
.nope:
xor eax, eax
jmp .exit
 
.ntfs_setup:
; By given bootsector, initialize some NTFS variables
movi eax, sizeof.NTFS
call malloc
test eax, eax
jz .exit
mov ecx, dword [ebp+PARTITION.FirstSector]
mov dword [eax+NTFS.FirstSector], ecx
mov ecx, dword [ebp+PARTITION.FirstSector+4]
mov dword [eax+NTFS.FirstSector+4], ecx
mov ecx, dword [ebp+PARTITION.Length]
mov dword [eax+NTFS.Length], ecx
mov ecx, dword [ebp+PARTITION.Length+4]
mov dword [eax+NTFS.Length+4], ecx
mov ecx, [ebp+PARTITION.Disk]
mov [eax+NTFS.Disk], ecx
mov [eax+NTFS.FSUserFunctions], ntfs_user_functions
push ebx ebp esi
mov ebp, eax
 
lea ecx, [ebp+NTFS.Lock]
call mutex_init
 
movzx eax, byte [ebx+13]
mov [ebp+NTFS.sectors_per_cluster], eax
mov eax, [ebx+0x28]
mov dword [ebp+NTFS.Length], eax
and dword [ebp+NTFS.Length+4], 0
mov eax, [ebx+0x30]
mov [ebp+NTFS.mft_cluster], eax
mov eax, [ebx+0x38]
mov [ebp+NTFS.mftmirr_cluster], eax
movsx eax, byte [ebx+0x40]
test eax, eax
js .1
mul [ebp+NTFS.sectors_per_cluster]
shl eax, 9
jmp .2
.1:
neg eax
mov ecx, eax
mov eax, 1
shl eax, cl
.2:
mov [ebp+NTFS.frs_size], eax
movsx eax, byte [ebx+0x44]
test eax, eax
js .3
mul [ebp+NTFS.sectors_per_cluster]
shl eax, 9
jmp .4
.3:
neg eax
mov ecx, eax
mov eax, 1
shl eax, cl
.4:
mov [ebp+NTFS.iab_size], eax
; allocate space for buffers
add eax, [ebp+NTFS.frs_size]
push eax
call kernel_alloc
test eax, eax
jz .fail_free
mov [ebp+NTFS.frs_buffer], eax
add eax, [ebp+NTFS.frs_size]
mov [ebp+NTFS.iab_buffer], eax
; read $MFT disposition
mov eax, [ebp+NTFS.mft_cluster]
mul [ebp+NTFS.sectors_per_cluster]
call ntfs_read_frs_sector
test eax, eax
jnz .usemirr
cmp dword [ebx], 'FILE'
jnz .usemirr
call ntfs_restore_usa_frs
jnc .mftok
.usemirr:
mov eax, [ebp+NTFS.mftmirr_cluster]
mul [ebp+NTFS.sectors_per_cluster]
call ntfs_read_frs_sector
test eax, eax
jnz @f
cmp dword [ebx], 'FILE'
jnz @f
call ntfs_restore_usa_frs
jnc .mftok
@@:
; $MFT and $MFTMirr invalid!
.fail_free_frs:
push [ebp+NTFS.frs_buffer]
call kernel_free
.fail_free:
mov eax, ebp
call free
xor eax, eax
.pop_exit:
pop esi ebp ebx
.exit:
cmp dword [esp+4], 0
jz @f
sub ebx, 512
@@:
ret
.fail_free_mft:
push [ebp+NTFS.mft_retrieval]
call kernel_free
jmp .fail_free_frs
.mftok:
; read $MFT table retrieval information
; start with one page, increase if not enough (when MFT too fragmented)
push ebx
push 0x1000
call kernel_alloc
pop ebx
test eax, eax
jz .fail_free_frs
mov [ebp+NTFS.mft_retrieval], eax
and [ebp+NTFS.mft_retrieval_size], 0
mov [ebp+NTFS.mft_retrieval_alloc], 0x1000/8
; $MFT base record must contain unnamed non-resident $DATA attribute
movzx eax, word [ebx+14h]
add eax, ebx
.scandata:
cmp dword [eax], -1
jz .fail_free_mft
cmp dword [eax], 0x80
jnz @f
cmp byte [eax+9], 0
jz .founddata
@@:
add eax, [eax+4]
jmp .scandata
.founddata:
cmp byte [eax+8], 0
jz .fail_free_mft
; load first portion of $DATA attribute retrieval information
mov edx, [eax+0x18]
mov [ebp+NTFS.mft_retrieval_end], edx
mov esi, eax
movzx eax, word [eax+0x20]
add esi, eax
sub esp, 10h
.scanmcb:
call ntfs_decode_mcb_entry
jnc .scanmcbend
call .get_mft_retrieval_ptr
mov edx, [esp] ; block length
mov [eax], edx
mov edx, [esp+8] ; block addr (relative)
mov [eax+4], edx
inc [ebp+NTFS.mft_retrieval_size]
jmp .scanmcb
.scanmcbend:
add esp, 10h
; there may be other portions of $DATA attribute in auxiliary records;
; if they will be needed, they will be loaded later
 
mov [ebp+NTFS.cur_index_size], 0x1000/0x200
push 0x1000
call kernel_alloc
test eax, eax
jz .fail_free_mft
mov [ebp+NTFS.cur_index_buf], eax
 
mov eax, ebp
jmp .pop_exit
endp
 
.get_mft_retrieval_ptr:
pushad
mov eax, [ebp+NTFS.mft_retrieval_size]
cmp eax, [ebp+NTFS.mft_retrieval_alloc]
jnz .ok
add eax, 0x1000/8
mov [ebp+NTFS.mft_retrieval_alloc], eax
shl eax, 3
push eax
call kernel_alloc
test eax, eax
jnz @f
popad
add esp, 14h
jmp .fail_free_mft
@@:
mov esi, [ebp+NTFS.mft_retrieval]
mov edi, eax
mov ecx, [ebp+NTFS.mft_retrieval_size]
add ecx, ecx
rep movsd
push [ebp+NTFS.mft_retrieval]
mov [ebp+NTFS.mft_retrieval], eax
call kernel_free
mov eax, [ebp+NTFS.mft_retrieval_size]
.ok:
shl eax, 3
add eax, [ebp+NTFS.mft_retrieval]
mov [esp+28], eax
popad
ret
 
proc ntfs_free
push ebx
xchg ebx, eax
stdcall kernel_free, [ebx+NTFS.frs_buffer]
stdcall kernel_free, [ebx+NTFS.mft_retrieval]
stdcall kernel_free, [ebx+NTFS.cur_index_buf]
xchg ebx, eax
call free
pop ebx
ret
endp
 
proc ntfs_lock
lea ecx, [ebp+NTFS.Lock]
jmp mutex_lock
endp
 
proc ntfs_unlock
lea ecx, [ebp+NTFS.Lock]
jmp mutex_unlock
endp
 
ntfs_read_frs_sector:
push ecx
mov ebx, [ebp+NTFS.frs_buffer]
push ebx
mov ecx, [ebp+NTFS.frs_size]
shr ecx, 9
push ecx
mov ecx, eax
@@:
mov eax, ecx
call fs_read32_sys
test eax, eax
jnz .fail
add ebx, 0x200
inc ecx
dec dword [esp]
jnz @b
pop eax
.fail:
pop ebx
pop ecx
ret
 
ntfs_read_attr:
; in: variables in ebp+NTFS.*
; out: [ebp+NTFS.ntfs_cur_read]
; out: CF=1 => notfound, in this case eax=0 => disk ok, otherwise eax=disk error code
xor eax, eax
pushad
and [ebp+NTFS.ntfs_cur_read], 0
cmp [ebp+NTFS.ntfs_cur_iRecord], 0
jnz .nomft
cmp [ebp+NTFS.ntfs_cur_attr], 0x80
jnz .nomft
mov eax, [ebp+NTFS.mft_retrieval_end]
inc eax
mul [ebp+NTFS.sectors_per_cluster]
cmp eax, [ebp+NTFS.ntfs_cur_offs]
jbe .nomft
; precalculated part of $Mft $DATA
mov esi, [ebp+NTFS.mft_retrieval]
mov eax, [ebp+NTFS.ntfs_cur_offs]
xor edx, edx
div [ebp+NTFS.sectors_per_cluster]
; eax = VCN, edx = offset in sectors from beginning of cluster
xor ecx, ecx ; ecx will contain LCN
.mftscan:
add ecx, [esi+4]
sub eax, [esi]
jb @f
add esi, 8
push eax
mov eax, [ebp+NTFS.mft_retrieval_end]
shl eax, 3
add eax, [ebp+NTFS.mft_retrieval]
cmp eax, esi
pop eax
jnz .mftscan
jmp .nomft
@@:
push ecx
add ecx, eax
add ecx, [esi]
push eax
push edx
mov eax, [ebp+NTFS.sectors_per_cluster]
mul ecx
; eax = sector on partition
pop edx
add eax, edx
mov ebx, [ebp+NTFS.ntfs_cur_buf]
pop ecx
neg ecx
imul ecx, [ebp+NTFS.sectors_per_cluster]
sub ecx, edx
cmp ecx, [ebp+NTFS.ntfs_cur_size]
jb @f
mov ecx, [ebp+NTFS.ntfs_cur_size]
@@:
; ecx = number of sequential sectors to read
push eax
call fs_read32_sys
pop edx
test eax, eax
jnz .errread
add [ebp+NTFS.ntfs_cur_read], 0x200
dec [ebp+NTFS.ntfs_cur_size]
inc [ebp+NTFS.ntfs_cur_offs]
add ebx, 0x200
mov [ebp+NTFS.ntfs_cur_buf], ebx
lea eax, [edx+1]
loop @b
pop ecx
xor eax, eax
xor edx, edx
cmp [ebp+NTFS.ntfs_cur_size], eax
jz @f
add esi, 8
push eax
mov eax, [ebp+NTFS.mft_retrieval_end]
shl eax, 3
add eax, [ebp+NTFS.mft_retrieval]
cmp eax, esi
pop eax
jz .nomft
jmp .mftscan
@@:
popad
ret
.errread:
pop ecx
.errret:
mov [esp+28], eax
stc
popad
ret
.nomft:
; 1. Read file record.
; N.B. This will do recursive call of read_attr for $MFT::$Data.
mov eax, [ebp+NTFS.ntfs_cur_iRecord]
mov [ebp+NTFS.ntfs_attr_iRecord], eax
and [ebp+NTFS.ntfs_attr_list], 0
or dword [ebp+NTFS.ntfs_attr_size], -1
or dword [ebp+NTFS.ntfs_attr_size+4], -1
or [ebp+NTFS.ntfs_attr_iBaseRecord], -1
call ntfs_read_file_record
jc .errret
; 2. Find required attribute.
mov eax, [ebp+NTFS.frs_buffer]
; a) For auxiliary records, read base record
; N.B. If base record is present,
; base iRecord may be 0 (for $Mft), but SequenceNumber is nonzero
cmp dword [eax+24h], 0
jz @f
mov eax, [eax+20h]
; test eax, eax
; jz @f
.beginfindattr:
mov [ebp+NTFS.ntfs_attr_iRecord], eax
call ntfs_read_file_record
jc .errret
@@:
; b) Scan for required attribute and for $ATTR_LIST
mov eax, [ebp+NTFS.frs_buffer]
movzx ecx, word [eax+14h]
add eax, ecx
mov ecx, [ebp+NTFS.ntfs_cur_attr]
and [ebp+NTFS.ntfs_attr_offs], 0
.scanattr:
cmp dword [eax], -1
jz .scandone
cmp dword [eax], ecx
jz .okattr
cmp [ebp+NTFS.ntfs_attr_iBaseRecord], -1
jnz .scancont
cmp dword [eax], 0x20 ; $ATTR_LIST
jnz .scancont
mov [ebp+NTFS.ntfs_attr_list], eax
jmp .scancont
.okattr:
; ignore named $DATA attributes (aka NTFS streams)
cmp ecx, 0x80
jnz @f
cmp byte [eax+9], 0
jnz .scancont
@@:
mov [ebp+NTFS.ntfs_attr_offs], eax
.scancont:
add eax, [eax+4]
jmp .scanattr
.continue:
pushad
and [ebp+NTFS.ntfs_cur_read], 0
.scandone:
; c) Check for required offset and length
mov ecx, [ebp+NTFS.ntfs_attr_offs]
jecxz .noattr
push [ebp+NTFS.ntfs_cur_size]
push [ebp+NTFS.ntfs_cur_read]
call .doreadattr
pop edx
pop ecx
jc @f
cmp [ebp+NTFS.ntfs_bCanContinue], 0
jz @f
sub edx, [ebp+NTFS.ntfs_cur_read]
neg edx
shr edx, 9
sub ecx, edx
mov [ebp+NTFS.ntfs_cur_size], ecx
jnz .not_in_cur
@@:
popad
ret
.noattr:
.not_in_cur:
cmp [ebp+NTFS.ntfs_cur_attr], 0x20
jz @f
mov ecx, [ebp+NTFS.ntfs_attr_list]
test ecx, ecx
jnz .lookattr
.ret_is_attr:
and dword [esp+28], 0
cmp [ebp+NTFS.ntfs_attr_offs], 1 ; CF set <=> ntfs_attr_offs == 0
popad
ret
.lookattr:
; required attribute or required offset was not found in base record;
; it may be present in auxiliary records;
; scan $ATTR_LIST
mov eax, [ebp+NTFS.ntfs_attr_iBaseRecord]
cmp eax, -1
jz @f
call ntfs_read_file_record
jc .errret
or [ebp+NTFS.ntfs_attr_iBaseRecord], -1
@@:
push [ebp+NTFS.ntfs_cur_offs]
push [ebp+NTFS.ntfs_cur_size]
push [ebp+NTFS.ntfs_cur_read]
push [ebp+NTFS.ntfs_cur_buf]
push dword [ebp+NTFS.ntfs_attr_size]
push dword [ebp+NTFS.ntfs_attr_size+4]
or dword [ebp+NTFS.ntfs_attr_size], -1
or dword [ebp+NTFS.ntfs_attr_size+4], -1
and [ebp+NTFS.ntfs_cur_offs], 0
mov [ebp+NTFS.ntfs_cur_size], 2
and [ebp+NTFS.ntfs_cur_read], 0
lea eax, [ebp+NTFS.ntfs_attrlist_buf]
cmp [ebp+NTFS.ntfs_cur_iRecord], 0
jnz @f
lea eax, [ebp+NTFS.ntfs_attrlist_mft_buf]
@@:
mov [ebp+NTFS.ntfs_cur_buf], eax
push eax
call .doreadattr
pop esi
mov edx, 1
pop dword [ebp+NTFS.ntfs_attr_size+4]
pop dword [ebp+NTFS.ntfs_attr_size]
mov ecx, [ebp+NTFS.ntfs_cur_read]
pop [ebp+NTFS.ntfs_cur_buf]
pop [ebp+NTFS.ntfs_cur_read]
pop [ebp+NTFS.ntfs_cur_size]
pop [ebp+NTFS.ntfs_cur_offs]
jc .errret
or edi, -1
lea ecx, [ecx+esi-1Ah]
.scanliststart:
push ecx
mov eax, [ebp+NTFS.ntfs_cur_attr]
.scanlist:
cmp esi, [esp]
jae .scanlistdone
cmp eax, [esi]
jz @f
.scanlistcont:
movzx ecx, word [esi+4]
add esi, ecx
jmp .scanlist
@@:
; ignore named $DATA attributes (aka NTFS streams)
cmp eax, 0x80
jnz @f
cmp byte [esi+6], 0
jnz .scanlistcont
@@:
push eax
mov eax, [esi+8]
test eax, eax
jnz .testf
mov eax, dword [ebp+NTFS.ntfs_attr_size]
and eax, dword [ebp+NTFS.ntfs_attr_size+4]
cmp eax, -1
jnz .testfz
; if attribute is in auxiliary records, its size is defined only in first
mov eax, [esi+10h]
call ntfs_read_file_record
jnc @f
.errret_pop:
pop ecx ecx
jmp .errret
.errret2_pop:
xor eax, eax
jmp .errret_pop
@@:
mov eax, [ebp+NTFS.frs_buffer]
movzx ecx, word [eax+14h]
add eax, ecx
mov ecx, [ebp+NTFS.ntfs_cur_attr]
@@:
cmp dword [eax], -1
jz .errret2_pop
cmp dword [eax], ecx
jz @f
.l1:
add eax, [eax+4]
jmp @b
@@:
cmp eax, 0x80
jnz @f
cmp byte [eax+9], 0
jnz .l1
@@:
cmp byte [eax+8], 0
jnz .sdnores
mov eax, [eax+10h]
mov dword [ebp+NTFS.ntfs_attr_size], eax
and dword [ebp+NTFS.ntfs_attr_size+4], 0
jmp .testfz
.sdnores:
mov ecx, [eax+30h]
mov dword [ebp+NTFS.ntfs_attr_size], ecx
mov ecx, [eax+34h]
mov dword [ebp+NTFS.ntfs_attr_size+4], ecx
.testfz:
xor eax, eax
.testf:
imul eax, [ebp+NTFS.sectors_per_cluster]
cmp eax, [ebp+NTFS.ntfs_cur_offs]
pop eax
ja @f
mov edi, [esi+10h] ; keep previous iRecord
jmp .scanlistcont
@@:
pop ecx
.scanlistfound:
cmp edi, -1
jnz @f
popad
ret
@@:
mov eax, [ebp+NTFS.ntfs_cur_iRecord]
mov [ebp+NTFS.ntfs_attr_iBaseRecord], eax
mov eax, edi
jmp .beginfindattr
.scanlistdone:
pop ecx
sub ecx, ebp
sub ecx, NTFS.ntfs_attrlist_buf-1Ah
cmp [ebp+NTFS.ntfs_cur_iRecord], 0
jnz @f
sub ecx, NTFS.ntfs_attrlist_mft_buf-NTFS.ntfs_attrlist_buf
@@:
cmp ecx, 0x400
jnz .scanlistfound
inc edx
push esi edi
lea esi, [ebp+NTFS.ntfs_attrlist_buf+0x200]
lea edi, [ebp+NTFS.ntfs_attrlist_buf]
cmp [ebp+NTFS.ntfs_cur_iRecord], 0
jnz @f
lea esi, [ebp+NTFS.ntfs_attrlist_mft_buf+0x200]
lea edi, [ebp+NTFS.ntfs_attrlist_mft_buf]
@@:
mov ecx, 0x200/4
rep movsd
mov eax, edi
pop edi esi
sub esi, 0x200
push [ebp+NTFS.ntfs_cur_offs]
push [ebp+NTFS.ntfs_cur_size]
push [ebp+NTFS.ntfs_cur_read]
push [ebp+NTFS.ntfs_cur_buf]
push dword [ebp+NTFS.ntfs_attr_size]
push dword [ebp+NTFS.ntfs_attr_size+4]
or dword [ebp+NTFS.ntfs_attr_size], -1
or dword [ebp+NTFS.ntfs_attr_size+4], -1
mov [ebp+NTFS.ntfs_cur_offs], edx
mov [ebp+NTFS.ntfs_cur_size], 1
and [ebp+NTFS.ntfs_cur_read], 0
mov [ebp+NTFS.ntfs_cur_buf], eax
mov ecx, [ebp+NTFS.ntfs_attr_list]
push esi edx edi
call .doreadattr
pop edi edx esi
mov ecx, [ebp+NTFS.ntfs_cur_read]
pop dword [ebp+NTFS.ntfs_attr_size+4]
pop dword [ebp+NTFS.ntfs_attr_size]
pop [ebp+NTFS.ntfs_cur_buf]
pop [ebp+NTFS.ntfs_cur_read]
pop [ebp+NTFS.ntfs_cur_size]
pop [ebp+NTFS.ntfs_cur_offs]
jc .errret
lea ecx, [ecx+ebp+NTFS.ntfs_attrlist_buf+0x200-0x1A]
cmp [ebp+NTFS.ntfs_cur_iRecord], 0
jnz .scanliststart
add ecx, NTFS.ntfs_attrlist_mft_buf-NTFS.ntfs_attrlist_buf
jmp .scanliststart
 
.doreadattr:
mov [ebp+NTFS.ntfs_bCanContinue], 0
cmp byte [ecx+8], 0
jnz .nonresident
mov eax, [ecx+10h] ; length
mov esi, eax
mov edx, [ebp+NTFS.ntfs_cur_offs]
shr eax, 9
cmp eax, edx
jb .okret
shl edx, 9
sub esi, edx
movzx eax, word [ecx+14h]
add edx, eax
add edx, ecx ; edx -> data
mov eax, [ebp+NTFS.ntfs_cur_size]
cmp eax, (0xFFFFFFFF shr 9)+1
jbe @f
mov eax, (0xFFFFFFFF shr 9)+1
@@:
shl eax, 9
cmp eax, esi
jbe @f
mov eax, esi
@@:
; eax = length, edx -> data
mov [ebp+NTFS.ntfs_cur_read], eax
mov ecx, eax
mov eax, edx
mov ebx, [ebp+NTFS.ntfs_cur_buf]
call memmove
and [ebp+NTFS.ntfs_cur_size], 0 ; CF=0
ret
.nonresident:
; Not all auxiliary records contain correct FileSize info
mov eax, dword [ebp+NTFS.ntfs_attr_size]
mov edx, dword [ebp+NTFS.ntfs_attr_size+4]
push eax
and eax, edx
cmp eax, -1
pop eax
jnz @f
mov eax, [ecx+30h] ; FileSize
mov edx, [ecx+34h]
mov dword [ebp+NTFS.ntfs_attr_size], eax
mov dword [ebp+NTFS.ntfs_attr_size+4], edx
@@:
add eax, 0x1FF
adc edx, 0
shrd eax, edx, 9
sub eax, [ebp+NTFS.ntfs_cur_offs]
ja @f
; return with nothing read
and [ebp+NTFS.ntfs_cur_size], 0
.okret:
clc
ret
@@:
; reduce read length
and [ebp+NTFS.ntfs_cur_tail], 0
cmp [ebp+NTFS.ntfs_cur_size], eax
jb @f
mov [ebp+NTFS.ntfs_cur_size], eax
mov eax, dword [ebp+NTFS.ntfs_attr_size]
and eax, 0x1FF
mov [ebp+NTFS.ntfs_cur_tail], eax
@@:
cmp [ebp+NTFS.ntfs_cur_size], 0
jz .okret
mov eax, [ebp+NTFS.ntfs_cur_offs]
xor edx, edx
div [ebp+NTFS.sectors_per_cluster]
sub eax, [ecx+10h] ; first_vbo
jb .okret
; eax = cluster, edx = starting sector
sub esp, 10h
movzx esi, word [ecx+20h] ; mcb_info_ofs
add esi, ecx
xor edi, edi
.readloop:
call ntfs_decode_mcb_entry
jnc .break
add edi, [esp+8]
sub eax, [esp]
jae .readloop
push ecx
push eax
add eax, [esp+8]
add eax, edi
imul eax, [ebp+NTFS.sectors_per_cluster]
add eax, edx
pop ecx
neg ecx
imul ecx, [ebp+NTFS.sectors_per_cluster]
sub ecx, edx
cmp ecx, [ebp+NTFS.ntfs_cur_size]
jb @f
mov ecx, [ebp+NTFS.ntfs_cur_size]
@@:
mov ebx, [ebp+NTFS.ntfs_cur_buf]
@@:
push eax
cmp [ebp+NTFS.ntfs_cur_attr], 0x80
jnz .sys
cmp [ebp+NTFS.ntfs_cur_iRecord], 0
jz .sys
call fs_read32_app
jmp .appsys
.sys:
call fs_read32_sys
.appsys:
pop edx
test eax, eax
jnz .errread2
add ebx, 0x200
mov [ebp+NTFS.ntfs_cur_buf], ebx
lea eax, [edx+1]
add [ebp+NTFS.ntfs_cur_read], 0x200
dec [ebp+NTFS.ntfs_cur_size]
inc [ebp+NTFS.ntfs_cur_offs]
loop @b
pop ecx
xor eax, eax
xor edx, edx
cmp [ebp+NTFS.ntfs_cur_size], 0
jnz .readloop
add esp, 10h
mov eax, [ebp+NTFS.ntfs_cur_tail]
test eax, eax
jz @f
sub eax, 0x200
add [ebp+NTFS.ntfs_cur_read], eax
@@:
clc
ret
.errread2:
pop ecx
add esp, 10h
stc
ret
.break:
add esp, 10h ; CF=0
mov [ebp+NTFS.ntfs_bCanContinue], 1
ret
 
ntfs_read_file_record:
; in: eax=iRecord
; out: [ebp+NTFS.frs_buffer] contains information
; CF=1 - failed, in this case eax=0 => something with FS, eax nonzero => disk error
; Read attr $DATA of $Mft, starting from eax*[ebp+NTFS.frs_size]
push ecx edx
mov ecx, [ebp+NTFS.frs_size]
mul ecx
shrd eax, edx, 9
shr edx, 9
jnz .errret
push [ebp+NTFS.ntfs_attr_iRecord]
push [ebp+NTFS.ntfs_attr_iBaseRecord]
push [ebp+NTFS.ntfs_attr_offs]
push [ebp+NTFS.ntfs_attr_list]
push dword [ebp+NTFS.ntfs_attr_size+4]
push dword [ebp+NTFS.ntfs_attr_size]
push [ebp+NTFS.ntfs_cur_iRecord]
push [ebp+NTFS.ntfs_cur_attr]
push [ebp+NTFS.ntfs_cur_offs]
push [ebp+NTFS.ntfs_cur_size]
push [ebp+NTFS.ntfs_cur_buf]
push [ebp+NTFS.ntfs_cur_read]
mov [ebp+NTFS.ntfs_cur_attr], 0x80 ; $DATA
and [ebp+NTFS.ntfs_cur_iRecord], 0 ; $Mft
mov [ebp+NTFS.ntfs_cur_offs], eax
shr ecx, 9
mov [ebp+NTFS.ntfs_cur_size], ecx
mov eax, [ebp+NTFS.frs_buffer]
mov [ebp+NTFS.ntfs_cur_buf], eax
call ntfs_read_attr
mov edx, [ebp+NTFS.ntfs_cur_read]
pop [ebp+NTFS.ntfs_cur_read]
pop [ebp+NTFS.ntfs_cur_buf]
pop [ebp+NTFS.ntfs_cur_size]
pop [ebp+NTFS.ntfs_cur_offs]
pop [ebp+NTFS.ntfs_cur_attr]
pop [ebp+NTFS.ntfs_cur_iRecord]
pop dword [ebp+NTFS.ntfs_attr_size]
pop dword [ebp+NTFS.ntfs_attr_size+4]
pop [ebp+NTFS.ntfs_attr_list]
pop [ebp+NTFS.ntfs_attr_offs]
pop [ebp+NTFS.ntfs_attr_iBaseRecord]
pop [ebp+NTFS.ntfs_attr_iRecord]
jc .ret
cmp edx, [ebp+NTFS.frs_size]
jnz .errret
mov eax, [ebp+NTFS.frs_buffer]
cmp dword [eax], 'FILE'
jnz .errret
push ebx
mov ebx, eax
call ntfs_restore_usa_frs
pop ebx
jc .errret
.ret:
pop edx ecx
ret
.errret:
pop edx ecx
xor eax, eax
stc
ret
 
ntfs_restore_usa_frs:
mov eax, [ebp+NTFS.frs_size]
ntfs_restore_usa:
pushad
shr eax, 9
mov ecx, eax
inc eax
cmp [ebx+6], ax
jnz .err
movzx eax, word [ebx+4]
lea esi, [eax+ebx]
lodsw
mov edx, eax
lea edi, [ebx+0x1FE]
@@:
cmp [edi], dx
jnz .err
lodsw
stosw
add edi, 0x1FE
loop @b
popad
clc
ret
.err:
popad
stc
ret
 
ntfs_decode_mcb_entry:
push eax ecx edi
lea edi, [esp+16]
xor eax, eax
lodsb
test al, al
jz .end
mov ecx, eax
and ecx, 0xF
cmp ecx, 8
ja .end
push ecx
rep movsb
pop ecx
sub ecx, 8
neg ecx
cmp byte [esi-1], 80h
jae .end
push eax
xor eax, eax
rep stosb
pop ecx
shr ecx, 4
cmp ecx, 8
ja .end
push ecx
rep movsb
pop ecx
sub ecx, 8
neg ecx
cmp byte [esi-1], 80h
cmc
sbb eax, eax
rep stosb
stc
.end:
pop edi ecx eax
ret
 
unichar_toupper:
push eax
call uni2ansi_char
cmp al, '_'
jz .unk
add esp, 4
call char_toupper
jmp ansi2uni_char
.unk:
pop eax
ret
 
ntfs_find_lfn:
; in: esi+[esp+4] -> name
; out: CF=1 - file not found
; else CF=0, [ebp+NTFS.ntfs_cur_iRecord] valid, eax->record in parent directory
mov [ebp+NTFS.ntfs_cur_iRecord], 5 ; start parse from root cluster
.doit2:
mov [ebp+NTFS.ntfs_cur_attr], 0x90 ; $INDEX_ROOT
and [ebp+NTFS.ntfs_cur_offs], 0
mov eax, [ebp+NTFS.cur_index_size]
mov [ebp+NTFS.ntfs_cur_size], eax
mov eax, [ebp+NTFS.cur_index_buf]
mov [ebp+NTFS.ntfs_cur_buf], eax
call ntfs_read_attr
jnc @f
.ret:
ret 4
@@:
xor eax, eax
cmp [ebp+NTFS.ntfs_cur_read], 0x20
jc .ret
pushad
mov esi, [ebp+NTFS.cur_index_buf]
mov eax, [esi+14h]
add eax, 10h
cmp [ebp+NTFS.ntfs_cur_read], eax
jae .readok1
add eax, 1FFh
shr eax, 9
cmp eax, [ebp+NTFS.cur_index_size]
ja @f
.stc_ret:
popad
stc
ret 4
@@:
; reallocate
push eax
push [ebp+NTFS.cur_index_buf]
call kernel_free
pop eax
mov [ebp+NTFS.cur_index_size], eax
push eax
call kernel_alloc
test eax, eax
jnz @f
and [ebp+NTFS.cur_index_size], 0
and [ebp+NTFS.cur_index_buf], 0
jmp .stc_ret
@@:
mov [ebp+NTFS.cur_index_buf], eax
popad
jmp .doit2
.readok1:
mov edx, [esi+8] ; subnode_size
shr edx, 9
cmp edx, [ebp+NTFS.cur_index_size]
jbe .ok2
push esi edx
push edx
call kernel_alloc
pop edx esi
test eax, eax
jz .stc_ret
mov edi, eax
mov ecx, [ebp+NTFS.cur_index_size]
shl ecx, 9-2
rep movsd
mov esi, eax
mov [ebp+NTFS.cur_index_size], edx
push esi edx
push [ebp+NTFS.cur_index_buf]
call kernel_free
pop edx esi
mov [ebp+NTFS.cur_index_buf], esi
.ok2:
add esi, 10h
mov edi, [esp+4]
; edi -> name, esi -> current index data, edx = subnode size
.scanloop:
add esi, [esi]
.scanloopint:
test byte [esi+0Ch], 2
jnz .subnode
push esi
add esi, 0x52
movzx ecx, byte [esi-2]
push edi
@@:
lodsw
call unichar_toupper
push eax
mov al, [edi]
inc edi
cmp al, '/'
jz .slash
call char_toupper
call ansi2uni_char
cmp ax, [esp]
pop eax
loopz @b
jz .found
pop edi
pop esi
jb .subnode
.scanloopcont:
movzx eax, word [esi+8]
add esi, eax
jmp .scanloopint
.slash:
pop eax
pop edi
pop esi
.subnode:
test byte [esi+0Ch], 1
jz .notfound
movzx eax, word [esi+8]
mov eax, [esi+eax-8]
imul eax, [ebp+NTFS.sectors_per_cluster]
mov [ebp+NTFS.ntfs_cur_offs], eax
mov [ebp+NTFS.ntfs_cur_attr], 0xA0 ; $INDEX_ALLOCATION
mov [ebp+NTFS.ntfs_cur_size], edx
mov eax, [ebp+NTFS.cur_index_buf]
mov esi, eax
mov [ebp+NTFS.ntfs_cur_buf], eax
push edx
call ntfs_read_attr
pop edx
mov eax, edx
shl eax, 9
cmp [ebp+NTFS.ntfs_cur_read], eax
jnz .notfound
cmp dword [esi], 'INDX'
jnz .notfound
mov ebx, esi
call ntfs_restore_usa
jc .notfound
add esi, 0x18
jmp .scanloop
.notfound:
popad
stc
ret 4
.found:
cmp byte [edi], 0
jz .done
cmp byte [edi], '/'
jz .next
pop edi
pop esi
jmp .scanloopcont
.done:
.next:
pop esi
pop esi
mov eax, [esi]
mov [ebp+NTFS.ntfs_cur_iRecord], eax
mov [esp+1Ch], esi
mov [esp+4], edi
popad
inc esi
cmp byte [esi-1], 0
jnz .doit2
cmp dword [esp+4], 0
jz @f
mov esi, [esp+4]
mov dword [esp+4], 0
jmp .doit2
@@:
ret 4
 
;----------------------------------------------------------------
; ntfs_Read - NTFS implementation of reading a file
; in: ebp = pointer to NTFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
ntfs_Read:
cmp byte [esi], 0
jnz @f
or ebx, -1
movi eax, ERROR_ACCESS_DENIED
ret
@@:
call ntfs_lock
stdcall ntfs_find_lfn, [esp+4]
jnc .found
call ntfs_unlock
or ebx, -1
movi eax, ERROR_FILE_NOT_FOUND
ret
.found:
mov [ebp+NTFS.ntfs_cur_attr], 0x80 ; $DATA
and [ebp+NTFS.ntfs_cur_offs], 0
and [ebp+NTFS.ntfs_cur_size], 0
call ntfs_read_attr
jnc @f
call ntfs_unlock
or ebx, -1
movi eax, ERROR_ACCESS_DENIED
ret
@@:
pushad
and dword [esp+10h], 0
xor eax, eax
cmp dword [ebx+8], 0x200
jb @f
.eof0:
popad
xor ebx, ebx
.eof:
movi eax, ERROR_END_OF_FILE
push eax
call ntfs_unlock
pop eax
ret
@@:
mov ecx, [ebx+12]
mov edx, [ebx+16]
mov eax, [ebx+4]
test eax, 0x1FF
jz .alignedstart
push edx
mov edx, [ebx+8]
shrd eax, edx, 9
pop edx
mov [ebp+NTFS.ntfs_cur_offs], eax
mov [ebp+NTFS.ntfs_cur_size], 1
lea eax, [ebp+NTFS.ntfs_bitmap_buf]
mov [ebp+NTFS.ntfs_cur_buf], eax
call ntfs_read_attr.continue
mov eax, [ebx+4]
and eax, 0x1FF
lea esi, [ebp+NTFS.ntfs_bitmap_buf+eax]
sub eax, [ebp+NTFS.ntfs_cur_read]
jae .eof0
neg eax
push ecx
cmp ecx, eax
jb @f
mov ecx, eax
@@:
mov [esp+10h+4], ecx
mov edi, edx
rep movsb
mov edx, edi
pop ecx
sub ecx, [esp+10h]
jnz @f
.retok:
popad
call ntfs_unlock
xor eax, eax
ret
@@:
cmp [ebp+NTFS.ntfs_cur_read], 0x200
jz .alignedstart
.eof_ebx:
popad
jmp .eof
.alignedstart:
mov eax, [ebx+4]
push edx
mov edx, [ebx+8]
add eax, 511
adc edx, 0
shrd eax, edx, 9
pop edx
.zero1:
mov [ebp+NTFS.ntfs_cur_offs], eax
mov [ebp+NTFS.ntfs_cur_buf], edx
mov eax, ecx
shr eax, 9
mov [ebp+NTFS.ntfs_cur_size], eax
add eax, [ebp+NTFS.ntfs_cur_offs]
push eax
call ntfs_read_attr.continue
pop [ebp+NTFS.ntfs_cur_offs]
mov eax, [ebp+NTFS.ntfs_cur_read]
add [esp+10h], eax
mov eax, ecx
and eax, not 0x1FF
cmp [ebp+NTFS.ntfs_cur_read], eax
jnz .eof_ebx
and ecx, 0x1FF
jz .retok
add edx, [ebp+NTFS.ntfs_cur_read]
mov [ebp+NTFS.ntfs_cur_size], 1
lea eax, [ebp+NTFS.ntfs_bitmap_buf]
mov [ebp+NTFS.ntfs_cur_buf], eax
call ntfs_read_attr.continue
cmp [ebp+NTFS.ntfs_cur_read], ecx
jb @f
mov [ebp+NTFS.ntfs_cur_read], ecx
@@:
xchg ecx, [ebp+NTFS.ntfs_cur_read]
push ecx
mov edi, edx
lea esi, [ebp+NTFS.ntfs_bitmap_buf]
add [esp+10h+4], ecx
rep movsb
pop ecx
xor eax, eax
cmp ecx, [ebp+NTFS.ntfs_cur_read]
jz @f
mov al, ERROR_END_OF_FILE
@@:
mov [esp+1Ch], eax
call ntfs_unlock
popad
ret
 
;----------------------------------------------------------------
; ntfs_ReadFolder - NTFS implementation of reading a folder
; in: ebp = pointer to NTFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
ntfs_ReadFolder:
call ntfs_lock
mov eax, 5 ; root cluster
cmp byte [esi], 0
jz .doit
stdcall ntfs_find_lfn, [esp+4]
jnc .doit2
.notfound:
or ebx, -1
push ERROR_FILE_NOT_FOUND
.pop_ret:
call ntfs_unlock
pop eax
ret
.doit:
mov [ebp+NTFS.ntfs_cur_iRecord], eax
.doit2:
mov [ebp+NTFS.ntfs_cur_attr], 0x10 ; $STANDARD_INFORMATION
and [ebp+NTFS.ntfs_cur_offs], 0
mov [ebp+NTFS.ntfs_cur_size], 1
lea eax, [ebp+NTFS.ntfs_bitmap_buf]
mov [ebp+NTFS.ntfs_cur_buf], eax
call ntfs_read_attr
jc .notfound
mov [ebp+NTFS.ntfs_cur_attr], 0x90 ; $INDEX_ROOT
and [ebp+NTFS.ntfs_cur_offs], 0
mov eax, [ebp+NTFS.cur_index_size]
mov [ebp+NTFS.ntfs_cur_size], eax
mov eax, [ebp+NTFS.cur_index_buf]
mov [ebp+NTFS.ntfs_cur_buf], eax
call ntfs_read_attr
jnc .ok
test eax, eax
jz .notfound
or ebx, -1
push 11
jmp .pop_ret
.ok:
cmp [ebp+NTFS.ntfs_cur_read], 0x20
jae @f
or ebx, -1
.fserr:
push ERROR_FAT_TABLE
jmp .pop_ret
@@:
pushad
mov esi, [ebp+NTFS.cur_index_buf]
mov eax, [esi+14h]
add eax, 10h
cmp [ebp+NTFS.ntfs_cur_read], eax
jae .readok1
add eax, 1FFh
shr eax, 9
cmp eax, [ebp+NTFS.cur_index_size]
ja @f
popad
jmp .fserr
@@:
; reallocate
push eax
push [ebp+NTFS.cur_index_buf]
call kernel_free
pop eax
mov [ebp+NTFS.cur_index_size], eax
push eax
call kernel_alloc
test eax, eax
jnz @f
and [ebp+NTFS.cur_index_size], 0
and [ebp+NTFS.cur_index_buf], 0
.nomem:
call ntfs_unlock
popad
or ebx, -1
movi eax, 12
ret
@@:
mov [ebp+NTFS.cur_index_buf], eax
popad
jmp .doit2
.readok1:
mov edx, [esi+8] ; subnode_size
shr edx, 9
mov [ebp+NTFS.cur_subnode_size], edx
cmp edx, [ebp+NTFS.cur_index_size]
jbe .ok2
push esi edx
push edx
call kernel_alloc
pop edx esi
test eax, eax
jz .nomem
mov edi, eax
mov ecx, [ebp+NTFS.cur_index_size]
shl ecx, 9-2
rep movsd
mov esi, eax
mov [ebp+NTFS.cur_index_size], edx
push [ebp+NTFS.cur_index_buf]
call kernel_free
mov [ebp+NTFS.cur_index_buf], esi
.ok2:
add esi, 10h
mov edx, [ebx+16]
push dword [ebx+8] ; read ANSI/UNICODE name
; init header
mov edi, edx
mov ecx, 32/4
xor eax, eax
rep stosd
mov byte [edx], 1 ; version
mov ecx, [ebx+12]
mov ebx, [ebx+4]
push edx
mov edx, esp
; edi -> BDFE, esi -> current index data, ebx = first wanted block,
; ecx = number of blocks to read
; edx -> parameters block: dd <output>, dd <flags>
cmp [ebp+NTFS.ntfs_cur_iRecord], 5
jz .skip_specials
; dot and dotdot entries
push esi
xor esi, esi
call .add_special_entry
inc esi
call .add_special_entry
pop esi
.skip_specials:
; at first, dump index root
add esi, [esi]
.dump_root:
test byte [esi+0Ch], 2
jnz .dump_root_done
call .add_entry
movzx eax, word [esi+8]
add esi, eax
jmp .dump_root
.dump_root_done:
; now dump all subnodes
push ecx edi
lea edi, [ebp+NTFS.ntfs_bitmap_buf]
mov [ebp+NTFS.ntfs_cur_buf], edi
mov ecx, 0x400/4
xor eax, eax
rep stosd
mov [ebp+NTFS.ntfs_cur_attr], 0xB0 ; $BITMAP
and [ebp+NTFS.ntfs_cur_offs], 0
mov [ebp+NTFS.ntfs_cur_size], 2
call ntfs_read_attr
pop edi ecx
push 0 ; save offset in $BITMAP attribute
and [ebp+NTFS.ntfs_cur_offs], 0
.dumploop:
mov [ebp+NTFS.ntfs_cur_attr], 0xA0
mov eax, [ebp+NTFS.cur_subnode_size]
mov [ebp+NTFS.ntfs_cur_size], eax
mov eax, [ebp+NTFS.cur_index_buf]
mov esi, eax
mov [ebp+NTFS.ntfs_cur_buf], eax
push [ebp+NTFS.ntfs_cur_offs]
mov eax, [ebp+NTFS.ntfs_cur_offs]
imul eax, [ebp+NTFS.cur_subnode_size]
mov [ebp+NTFS.ntfs_cur_offs], eax
call ntfs_read_attr
pop [ebp+NTFS.ntfs_cur_offs]
mov eax, [ebp+NTFS.cur_subnode_size]
shl eax, 9
cmp [ebp+NTFS.ntfs_cur_read], eax
jnz .done
push eax
mov eax, [ebp+NTFS.ntfs_cur_offs]
and eax, 0x400*8-1
bt dword [ebp+NTFS.ntfs_bitmap_buf], eax
pop eax
jnc .dump_subnode_done
cmp dword [esi], 'INDX'
jnz .dump_subnode_done
push ebx
mov ebx, esi
call ntfs_restore_usa
pop ebx
jc .dump_subnode_done
add esi, 0x18
add esi, [esi]
.dump_subnode:
test byte [esi+0Ch], 2
jnz .dump_subnode_done
call .add_entry
movzx eax, word [esi+8]
add esi, eax
jmp .dump_subnode
.dump_subnode_done:
inc [ebp+NTFS.ntfs_cur_offs]
test [ebp+NTFS.ntfs_cur_offs], 0x400*8-1
jnz .dumploop
mov [ebp+NTFS.ntfs_cur_attr], 0xB0
push ecx edi
lea edi, [ebp+NTFS.ntfs_bitmap_buf]
mov [ebp+NTFS.ntfs_cur_buf], edi
mov ecx, 0x400/4
xor eax, eax
rep stosd
pop edi ecx
pop eax
push [ebp+NTFS.ntfs_cur_offs]
inc eax
mov [ebp+NTFS.ntfs_cur_offs], eax
mov [ebp+NTFS.ntfs_cur_size], 2
push eax
call ntfs_read_attr
pop eax
pop [ebp+NTFS.ntfs_cur_offs]
push eax
jmp .dumploop
.done:
pop eax
pop edx
mov ebx, [edx+4]
pop edx
xor eax, eax
dec ecx
js @f
mov al, ERROR_END_OF_FILE
@@:
mov [esp+1Ch], eax
mov [esp+10h], ebx
call ntfs_unlock
popad
ret
 
.add_special_entry:
mov eax, [edx]
inc dword [eax+8] ; new file found
dec ebx
jns .ret
dec ecx
js .ret
inc dword [eax+4] ; new file block copied
mov eax, [edx+4]
mov [edi+4], eax
; mov eax, dword [ntfs_bitmap_buf+0x20]
; or al, 0x10
mov eax, 0x10
stosd
scasd
push edx
mov eax, dword [ebp+NTFS.ntfs_bitmap_buf]
mov edx, dword [ebp+NTFS.ntfs_bitmap_buf+4]
call ntfs_datetime_to_bdfe
mov eax, dword [ebp+NTFS.ntfs_bitmap_buf+0x18]
mov edx, dword [ebp+NTFS.ntfs_bitmap_buf+0x1C]
call ntfs_datetime_to_bdfe
mov eax, dword [ebp+NTFS.ntfs_bitmap_buf+8]
mov edx, dword [ebp+NTFS.ntfs_bitmap_buf+0xC]
call ntfs_datetime_to_bdfe
pop edx
xor eax, eax
stosd
stosd
mov al, '.'
push edi ecx
lea ecx, [esi+1]
test byte [edi-0x24], 1
jz @f
rep stosw
pop ecx
xor eax, eax
stosw
pop edi
add edi, 520
ret
@@:
rep stosb
pop ecx
xor eax, eax
stosb
pop edi
add edi, 264
.ret:
ret
 
.add_entry:
; do not return DOS 8.3 names
cmp byte [esi+0x51], 2
jz .ret
; do not return system files
; ... note that there will be no bad effects if system files also were reported ...
cmp dword [esi], 0x10
jb .ret
mov eax, [edx]
inc dword [eax+8] ; new file found
dec ebx
jns .ret
dec ecx
js .ret
inc dword [eax+4] ; new file block copied
mov eax, [edx+4] ; flags
call ntfs_direntry_to_bdfe
push ecx esi edi
movzx ecx, byte [esi+0x50]
add esi, 0x52
test byte [edi-0x24], 1
jz .ansi
shr ecx, 1
rep movsd
adc ecx, ecx
rep movsw
and word [edi], 0
pop edi
add edi, 520
pop esi ecx
ret
.ansi:
jecxz .skip
@@:
lodsw
call uni2ansi_char
stosb
loop @b
.skip:
xor al, al
stosb
pop edi
add edi, 264
pop esi ecx
ret
 
ntfs_direntry_to_bdfe:
mov [edi+4], eax ; ANSI/UNICODE name
mov eax, [esi+48h]
test eax, 0x10000000
jz @f
and eax, not 0x10000000
or al, 0x10
@@:
stosd
scasd
push edx
mov eax, [esi+0x18]
mov edx, [esi+0x1C]
call ntfs_datetime_to_bdfe
mov eax, [esi+0x30]
mov edx, [esi+0x34]
call ntfs_datetime_to_bdfe
mov eax, [esi+0x20]
mov edx, [esi+0x24]
call ntfs_datetime_to_bdfe
pop edx
mov eax, [esi+0x40]
stosd
mov eax, [esi+0x44]
stosd
ret
 
iglobal
_24 dd 24
_60 dd 60
_10000000 dd 10000000
days400year dd 365*400+100-4+1
days100year dd 365*100+25-1
days4year dd 365*4+1
days1year dd 365
months dd 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
months2 dd 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
_400 dd 400
_100 dd 100
endg
 
ntfs_datetime_to_bdfe:
; edx:eax = number of 100-nanosecond intervals since January 1, 1601, in UTC
push eax
mov eax, edx
xor edx, edx
div [_10000000]
xchg eax, [esp]
div [_10000000]
pop edx
.sec:
; edx:eax = number of seconds since January 1, 1601
push eax
mov eax, edx
xor edx, edx
div [_60]
xchg eax, [esp]
div [_60]
mov [edi], dl
pop edx
; edx:eax = number of minutes
div [_60]
mov [edi+1], dl
; eax = number of hours (note that 2^64/(10^7*60*60) < 2^32)
xor edx, edx
div [_24]
mov [edi+2], dl
mov [edi+3], byte 0
; eax = number of days since January 1, 1601
xor edx, edx
div [days400year]
imul eax, 400
add eax, 1601
mov [edi+6], ax
mov eax, edx
xor edx, edx
div [days100year]
cmp al, 4
jnz @f
dec eax
add edx, [days100year]
@@:
imul eax, 100
add [edi+6], ax
mov eax, edx
xor edx, edx
div [days4year]
shl eax, 2
add [edi+6], ax
mov eax, edx
xor edx, edx
div [days1year]
cmp al, 4
jnz @f
dec eax
add edx, [days1year]
@@:
add [edi+6], ax
push esi edx
mov esi, months
movzx eax, word [edi+6]
test al, 3
jnz .noleap
xor edx, edx
push eax
div [_400]
pop eax
test edx, edx
jz .leap
xor edx, edx
div [_100]
test edx, edx
jz .noleap
.leap:
mov esi, months2
.noleap:
pop edx
xor eax, eax
inc eax
@@:
sub edx, [esi]
jb @f
add esi, 4
inc eax
jmp @b
@@:
add edx, [esi]
pop esi
inc edx
mov [edi+4], dl
mov [edi+5], al
add edi, 8
ret
 
;----------------------------------------------------------------
; ntfs_Rewrite - NTFS implementation of creating a new file
; in: ebp = pointer to NTFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
ntfs_Rewrite:
ntfs_CreateFolder:
xor ebx, ebx
mov eax, ERROR_UNSUPPORTED_FS
ret
 
;----------------------------------------------------------------
; ntfs_Write - NTFS implementation of writing to file
; in: ebp = pointer to NTFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
ntfs_Write:
xor ebx, ebx
mov eax, ERROR_UNSUPPORTED_FS
ret
 
ntfs_SetFileEnd:
ntfs_SetFileInfo:
ntfs_Delete:
mov eax, ERROR_UNSUPPORTED_FS
ret
 
;----------------------------------------------------------------
; ntfs_GetFileInfo - NTFS implementation of getting file info
; in: ebp = pointer to NTFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
ntfs_GetFileInfo:
cmp byte [esi], 0
jnz @f
movi eax, 2
ret
@@:
call ntfs_lock
stdcall ntfs_find_lfn, [esp+4]
jnc .doit
test eax, eax
movi eax, ERROR_FILE_NOT_FOUND
jz @f
mov al, 11
@@:
push eax
call ntfs_unlock
pop eax
ret
.doit:
push esi edi
mov esi, eax
mov edi, [ebx+16]
xor eax, eax
call ntfs_direntry_to_bdfe
pop edi esi
call ntfs_unlock
xor eax, eax
ret
 
/kernel/branches/kolibri-process/fs/parse_fn.inc
0,0 → 1,247
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
;-------------------------------------------------------------------------
;
; File path partial substitution (according to configuration)
;
;
; SPraid
;
;-------------------------------------------------------------------------
 
$Revision: 3780 $
 
 
iglobal
; pointer to memory for path replace table,
; size of one record is 128 bytes: 64 bytes for search pattern + 64 bytes for replace string
 
; start with one entry: sys -> <sysdir>
full_file_name_table dd sysdir_name
.size dd 1
 
tmp_file_name_size dd 1
endg
 
uglobal
; Parser_params will initialize: sysdir_name = "sys", sysdir_path = <sysdir>
sysdir_name rb 64
sysdir_path rb 64
sysdir_name1 rb 64
sysdir_path1 rb 64
 
; for example:
;dir_name1 db 'KolibriOS',0
; rb 64-8
;dir_path1 db 'HD0/1',0
; rb 64-6
endg
 
uglobal
tmp_file_name_table dd ?
endg
 
; use bx_from_load and init system directory /sys
proc Parser_params
locals
buff db 4 dup(?) ; for test cd
endl
mov eax, [OS_BASE+0x10000+bx_from_load]
mov ecx, sysdir_path
mov [ecx-64], dword 'sys'
cmp al, 'r'; if ram disk
jnz @f
mov [ecx], dword 'RD/?'
mov [ecx+3], byte ah
mov [ecx+4], byte 0
ret
@@:
cmp al, 'm'; if ram disk
jnz @f
mov [ecx], dword 'CD?/'; if cd disk {m}
mov [ecx+4], byte '1'
mov [ecx+5], dword '/KOL'
mov [ecx+9], dword 'IBRI'
mov [ecx+13], byte 0
.next_cd:
mov [ecx+2], byte ah
inc ah
cmp ah, '5'
je .not_found_cd
lea edx, [buff]
pushad
stdcall read_file, read_firstapp, edx, 0, 4
popad
cmp [edx], dword 'MENU'
jne .next_cd
jmp .ok
 
@@:
sub al, 49
mov [ecx], dword 'HD?/'; if hard disk
mov [ecx+2], byte al
mov [ecx+4], byte ah
mov [ecx+5], dword '/KOL'
mov [ecx+9], dword 'IBRI'
mov [ecx+13], byte 0
.ok:
.not_found_cd:
ret
endp
 
proc load_file_parse_table
stdcall kernel_alloc, 0x1000
mov [tmp_file_name_table], eax
mov edi, eax
mov esi, sysdir_name
mov ecx, 128/4
rep movsd
 
invoke ini.enum_keys, conf_fname, conf_path_sect, get_every_key
 
mov eax, [tmp_file_name_table]
mov [full_file_name_table], eax
mov eax, [tmp_file_name_size]
mov [full_file_name_table.size], eax
ret
endp
 
uglobal
def_val_1 db 0
endg
 
proc get_every_key stdcall, f_name, sec_name, key_name
mov esi, [key_name]
mov ecx, esi
cmp byte [esi], '/'
jnz @f
inc esi
@@:
mov edi, [tmp_file_name_size]
shl edi, 7
cmp edi, 0x1000
jae .stop_parse
add edi, [tmp_file_name_table]
lea ebx, [edi+64]
@@:
cmp edi, ebx
jae .skip_this_key
lodsb
test al, al
jz @f
or al, 20h
stosb
jmp @b
@@:
stosb
 
invoke ini.get_str, [f_name], [sec_name], ecx, ebx, 64, def_val_1
 
cmp byte [ebx], '/'
jnz @f
lea esi, [ebx+1]
mov edi, ebx
mov ecx, 63
rep movsb
@@:
push ebp
mov ebp, [tmp_file_name_table]
mov ecx, [tmp_file_name_size]
jecxz .noreplace
mov eax, ecx
dec eax
shl eax, 7
add ebp, eax
.replace_loop:
mov edi, ebx
mov esi, ebp
@@:
lodsb
test al, al
jz .doreplace
mov dl, [edi]
inc edi
test dl, dl
jz .replace_loop_cont
or dl, 20h
cmp al, dl
jz @b
jmp .replace_loop_cont
.doreplace:
cmp byte [edi], 0
jz @f
cmp byte [edi], '/'
jnz .replace_loop_cont
@@:
lea esi, [ebp+64]
call .replace
jc .skip_this_key2
.replace_loop_cont:
sub ebp, 128
loop .replace_loop
.noreplace:
pop ebp
 
inc [tmp_file_name_size]
.skip_this_key:
xor eax, eax
inc eax
ret
.skip_this_key2:
pop ebp
jmp .skip_this_key
.stop_parse:
xor eax, eax
ret
endp
 
proc get_every_key.replace
; in: ebx->destination, esi->first part of name, edi->second part of name
; maximum length is 64 bytes
; out: CF=1 <=> overflow
; 1) allocate temporary buffer in stack
sub esp, 64
; 2) save second part of name to temporary buffer
push esi
lea esi, [esp+4] ; esi->tmp buffer
xchg esi, edi ; edi->tmp buffer, esi->source
@@:
lodsb
stosb
test al, al
jnz @b
; 3) copy first part of name to destination
pop esi
mov edi, ebx
@@:
lodsb
test al, al
jz @f
stosb
jmp @b
@@:
; 4) restore second part of name from temporary buffer to destination
; (may cause overflow)
lea edx, [ebx+64] ; limit of destination
mov esi, esp
@@:
cmp edi, edx
jae .overflow
lodsb
stosb
test al, al
jnz @b
; all is OK
add esp, 64 ; CF is cleared
ret
.overflow:
; name is too long
add esp, 64
stc
ret
endp
/kernel/branches/kolibri-process/fs/xfs.asm
0,0 → 1,2769
include 'xfs.inc'
 
;
; This file contains XFS related code.
; For more information on XFS check sources below.
;
; 1. XFS Filesystem Structure, 2nd Edition, Revision 1. Silicon Graphics Inc. 2006
; 2. Linux source http://kernel.org
;
 
 
; test partition type (valid XFS one?)
; alloc and fill XFS (see xfs.inc) structure
; this function is called for each partition
; returns 0 (not XFS or invalid) / pointer to partition structure
xfs_create_partition:
push ebx ecx edx esi edi
cmp dword[ebx + xfs_sb.sb_magicnum], XFS_SB_MAGIC ; signature
jne .error
 
; TODO: check XFS.versionnum and XFS.features2
; print superblock params for debugging (waiting for bug reports)
 
movi eax, sizeof.XFS
call malloc
test eax, eax
jz .error
 
; standard partition initialization, common for all file systems
 
mov edi, eax
mov eax, dword[ebp + PARTITION.FirstSector]
mov dword[edi + XFS.FirstSector], eax
mov eax, dword[ebp + PARTITION.FirstSector + 4]
mov dword[edi + XFS.FirstSector + 4], eax
mov eax, dword[ebp + PARTITION.Length]
mov dword[edi + XFS.Length], eax
mov eax, dword[ebp + PARTITION.Length + 4]
mov dword[edi + XFS.Length + 4], eax
mov eax, [ebp + PARTITION.Disk]
mov [edi + XFS.Disk], eax
mov [edi + XFS.FSUserFunctions], xfs_user_functions
 
; here we initialize only one mutex so far (for the entire partition)
; XFS potentially allows parallel r/w access to several AGs, keep it in mind for SMP times
 
lea ecx, [edi + XFS.Lock]
call mutex_init
 
; read superblock and fill just allocated XFS partition structure
 
mov eax, [ebx + xfs_sb.sb_blocksize]
bswap eax ; XFS is big endian
mov [edi + XFS.blocksize], eax
movzx eax, word[ebx + xfs_sb.sb_sectsize]
xchg al, ah
mov [edi + XFS.sectsize], eax
movzx eax, word[ebx + xfs_sb.sb_versionnum]
xchg al, ah
mov [edi + XFS.versionnum], eax
mov eax, [ebx + xfs_sb.sb_features2]
bswap eax
mov [edi + XFS.features2], eax
movzx eax, word[ebx + xfs_sb.sb_inodesize]
xchg al, ah
mov [edi + XFS.inodesize], eax
movzx eax, word[ebx + xfs_sb.sb_inopblock] ; inodes per block
xchg al, ah
mov [edi + XFS.inopblock], eax
movzx eax, byte[ebx + xfs_sb.sb_blocklog] ; log2 of block size, in bytes
mov [edi + XFS.blocklog], eax
movzx eax, byte[ebx + xfs_sb.sb_sectlog]
mov [edi + XFS.sectlog], eax
movzx eax, byte[ebx + xfs_sb.sb_inodelog]
mov [edi + XFS.inodelog], eax
movzx eax, byte[ebx + xfs_sb.sb_inopblog]
mov [edi + XFS.inopblog], eax
movzx eax, byte[ebx + xfs_sb.sb_dirblklog]
mov [edi + XFS.dirblklog], eax
mov eax, dword[ebx + xfs_sb.sb_rootino + 4] ;
bswap eax ; big
mov dword[edi + XFS.rootino + 0], eax ; endian
mov eax, dword[ebx + xfs_sb.sb_rootino + 0] ; 64bit
bswap eax ; number
mov dword[edi + XFS.rootino + 4], eax ;
 
mov eax, [edi + XFS.blocksize]
mov ecx, [edi + XFS.dirblklog]
shl eax, cl
mov [edi + XFS.dirblocksize], eax ; blocks for files, dirblocks for directories
 
; sector is always smaller than block
; so precalculate shift order to allow faster sector_num->block_num conversion
 
mov ecx, [edi + XFS.blocklog]
sub ecx, [edi + XFS.sectlog]
mov [edi + XFS.blockmsectlog], ecx
 
mov eax, 1
shl eax, cl
mov [edi + XFS.sectpblock], eax
 
; shift order for inode_num->block_num conversion
 
mov eax, [edi + XFS.blocklog]
sub eax, [edi + XFS.inodelog]
mov [edi + XFS.inodetoblocklog], eax
 
mov eax, [ebx + xfs_sb.sb_agblocks]
bswap eax
mov [edi + XFS.agblocks], eax
movzx ecx, byte[ebx + xfs_sb.sb_agblklog]
mov [edi + XFS.agblklog], ecx
 
; get the mask for block numbers
; block numbers are AG relative!
; bitfield length may vary between partitions
 
mov eax, 1
shl eax, cl
dec eax
mov dword[edi + XFS.agblockmask + 0], eax
mov eax, 1
sub ecx, 32
jc @f
shl eax, cl
@@:
dec eax
mov dword[edi + XFS.agblockmask + 4], eax
 
; calculate magic offsets for directories
 
mov ecx, [edi + XFS.blocklog]
mov eax, XFS_DIR2_LEAF_OFFSET AND 0xffffffff ; lo
mov edx, XFS_DIR2_LEAF_OFFSET SHR 32 ; hi
shrd eax, edx, cl
mov [edi + XFS.dir2_leaf_offset_blocks], eax
 
mov ecx, [edi + XFS.blocklog]
mov eax, XFS_DIR2_FREE_OFFSET AND 0xffffffff ; lo
mov edx, XFS_DIR2_FREE_OFFSET SHR 32 ; hi
shrd eax, edx, cl
mov [edi + XFS.dir2_free_offset_blocks], eax
 
; mov ecx, [edi + XFS.dirblklog]
; mov eax, [edi + XFS.blocksize]
; shl eax, cl
; mov [edi + XFS.dirblocksize], eax
 
mov eax, [edi + XFS.blocksize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.cur_block], eax
 
; we do need XFS.blocksize bytes for single inode
; minimal file system structure is block, inodes are packed in blocks
 
mov eax, [edi + XFS.blocksize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.cur_inode], eax
 
; temporary inode
; used for browsing directories
 
mov eax, [edi + XFS.blocksize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.tmp_inode], eax
 
; current sector
; only for sector size structures like AGI
; inodes has usually the same size, but never store them here
 
mov eax, [edi + XFS.sectsize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.cur_sect], eax
 
; current directory block
 
mov eax, [edi + XFS.dirblocksize]
call malloc
test eax, eax
jz .error
mov [edi + XFS.cur_dirblock], eax
 
.quit:
mov eax, edi ; return pointer to allocated XFS partition structure
pop edi esi edx ecx ebx
ret
.error:
xor eax, eax
pop edi esi edx ecx ebx
ret
 
 
iglobal
align 4
xfs_user_functions:
dd xfs_free
dd (xfs_user_functions_end - xfs_user_functions - 4) / 4
dd xfs_Read
dd xfs_ReadFolder
dd 0;xfs_Rewrite
dd 0;xfs_Write
dd 0;xfs_SetFileEnd
dd xfs_GetFileInfo
dd 0;xfs_SetFileInfo
dd 0
dd 0;xfs_Delete
dd 0;xfs_CreateFolder
xfs_user_functions_end:
endg
 
 
; lock partition access mutex
proc xfs_lock
;DEBUGF 1,"xfs_lock\n"
lea ecx, [ebp + XFS.Lock]
jmp mutex_lock
endp
 
 
; unlock partition access mutex
proc xfs_unlock
;DEBUGF 1,"xfs_unlock\n"
lea ecx, [ebp + XFS.Lock]
jmp mutex_unlock
endp
 
 
; free all the allocated memory
; called on partition destroy
proc xfs_free
push ebp
xchg ebp, eax
stdcall kernel_free, [ebp + XFS.cur_block]
stdcall kernel_free, [ebp + XFS.cur_inode]
stdcall kernel_free, [ebp + XFS.cur_sect]
stdcall kernel_free, [ebp + XFS.cur_dirblock]
stdcall kernel_free, [ebp + XFS.tmp_inode]
xchg ebp, eax
call free
pop ebp
ret
endp
 
 
;---------------------------------------------------------------
; block number (AG relative)
; eax -- inode_lo
; edx -- inode_hi
; ebx -- buffer
;---------------------------------------------------------------
xfs_read_block:
push ebx esi
 
push edx
push eax
 
; XFS block numbers are AG relative
; they come in bitfield form of concatenated AG and block numbers
; to get absolute block number for fs_read32_sys we should
; 1. extract AG number (using precalculated mask)
; 2. multiply it by the AG size in blocks
; 3. add AG relative block number
 
; 1.
mov ecx, [ebp + XFS.agblklog]
shrd eax, edx, cl
shr edx, cl
; 2.
mul dword[ebp + XFS.agblocks]
pop ecx
pop esi
and ecx, dword[ebp + XFS.agblockmask + 0]
and esi, dword[ebp + XFS.agblockmask + 4]
; 3.
add eax, ecx
adc edx, esi
 
;DEBUGF 1,"read block: 0x%x%x\n",edx,eax
; there is no way to read file system block at once, therefore we
; 1. calculate the number of sectors first
; 2. and then read them in series
 
; 1.
mov ecx, [ebp + XFS.blockmsectlog]
shld edx, eax, cl
shl eax, cl
mov esi, [ebp + XFS.sectpblock]
 
; 2.
.next_sector:
push eax edx
call fs_read32_sys
mov ecx, eax
pop edx eax
test ecx, ecx
jnz .error
add eax, 1 ; be ready to fs_read64_sys
adc edx, 0
add ebx, [ebp + XFS.sectsize] ; update buffer offset
dec esi
jnz .next_sector
 
.quit:
xor eax, eax
pop esi ebx
ret
.error:
mov eax, ecx
pop esi ebx
ret
 
 
;---------------------------------------------------------------
; push buffer
; push startblock_hi
; push startblock_lo
; call xfs_read_dirblock
; test eax, eax
;---------------------------------------------------------------
xfs_read_dirblock:
;mov eax, [esp + 4]
;mov edx, [esp + 8]
;DEBUGF 1,"read dirblock at: %d %d\n",edx,eax
;DEBUGF 1,"dirblklog: %d\n",[ebp + XFS.dirblklog]
push ebx esi
 
mov eax, [esp + 12] ; startblock_lo
mov edx, [esp + 16] ; startblock_hi
mov ebx, [esp + 20] ; buffer
 
; dirblock >= block
; read dirblocks by blocks
 
mov ecx, [ebp + XFS.dirblklog]
mov esi, 1
shl esi, cl
.next_block:
push eax edx
call xfs_read_block
mov ecx, eax
pop edx eax
test ecx, ecx
jnz .error
add eax, 1 ; be ready to fs_read64_sys
adc edx, 0
add ebx, [ebp + XFS.blocksize]
dec esi
jnz .next_block
 
.quit:
xor eax, eax
pop esi ebx
ret 12
.error:
mov eax, ecx
pop esi ebx
ret 12
 
 
;---------------------------------------------------------------
; push buffer
; push inode_hi
; push inode_lo
; call xfs_read_inode
; test eax, eax
;---------------------------------------------------------------
xfs_read_inode:
;DEBUGF 1,"reading inode: 0x%x%x\n",[esp+8],[esp+4]
push ebx
mov eax, [esp + 8] ; inode_lo
mov edx, [esp + 12] ; inode_hi
mov ebx, [esp + 16] ; buffer
 
; inodes are packed into blocks
; 1. calculate block number
; 2. read the block
; 3. add inode offset to block base address
 
; 1.
mov ecx, [ebp + XFS.inodetoblocklog]
shrd eax, edx, cl
shr edx, cl
; 2.
call xfs_read_block
test eax, eax
jnz .error
 
; note that inode numbers should be first extracted from bitfields using mask
 
mov eax, [esp + 8]
mov edx, 1
mov ecx, [ebp + XFS.inopblog]
shl edx, cl
dec edx ; get inode number mask
and eax, edx ; apply mask
mov ecx, [ebp + XFS.inodelog]
shl eax, cl
add ebx, eax
 
cmp word[ebx], XFS_DINODE_MAGIC ; test signature
jne .error
.quit:
xor eax, eax
mov edx, ebx
pop ebx
ret 12
.error:
movi eax, ERROR_FS_FAIL
mov edx, ebx
pop ebx
ret 12
 
 
;----------------------------------------------------------------
; push encoding ; ASCII / UNICODE
; push src ; inode
; push dst ; bdfe
; push entries_to_read
; push start_number ; from 0
;----------------------------------------------------------------
xfs_dir_get_bdfes:
DEBUGF 1,"xfs_dir_get_bdfes: %d entries from %d\n",[esp+8],[esp+4]
sub esp, 4 ; local vars
push ecx edx esi edi
 
mov ebx, [esp + 36] ; src
mov edx, [esp + 32] ; dst
mov ecx, [esp + 24] ; start_number
 
; define directory ondisk format and jump to corresponding label
 
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_LOCAL
jne .not_shortdir
jmp .shortdir
.not_shortdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_blockdir
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
cmp eax, 1
jne .not_blockdir
jmp .blockdir
.not_blockdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_leafdir
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
cmp eax, 4
ja .not_leafdir
jmp .leafdir
.not_leafdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_nodedir
jmp .nodedir
.not_nodedir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE
jne .not_btreedir
jmp .btreedir
.not_btreedir:
movi eax, ERROR_FS_FAIL
jmp .error
 
; short form directory (all the data fits into inode)
.shortdir:
;DEBUGF 1,"shortdir\n",
movzx eax, word[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count]
test al, al ; is count zero?
jnz @f ; if not, use it (i8count must be zero then)
shr eax, 8 ; use i8count
@@:
add eax, 1 ; '..' and '.' are implicit
mov dword[edx + 0], 1 ; version
mov [edx + 8], eax ; total entries
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
mov [ebp + XFS.entries_read], eax
 
; inode numbers are often saved as 4 bytes (iff they fit)
; compute the length of inode numbers
 
mov eax, 4 ; 4 by default
cmp byte[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.i8count], 0
je @f
add eax, eax ; 4+4=8, iff i8count != 0
@@:
mov dword[edx + 12], 0 ; reserved
mov dword[edx + 16], 0 ;
mov dword[edx + 20], 0 ;
mov dword[edx + 24], 0 ;
mov dword[edx + 28], 0 ;
add edx, 32
lea esi, [ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent + eax]
dec ecx
js .shortdir.fill
 
; skip some entries if the first entry to read is not 0
 
.shortdir.skip:
test ecx, ecx
jz .shortdir.skipped
movzx edi, byte[esi + xfs_dir2_sf_entry.namelen]
lea esi, [esi + xfs_dir2_sf_entry.name + edi]
add esi, eax
dec ecx
jnz .shortdir.skip
mov ecx, [esp + 28] ; entries to read
jmp .shortdir.skipped
.shortdir.fill:
mov ecx, [esp + 28] ; total number
test ecx, ecx
jz .quit
push ecx
;DEBUGF 1,"ecx: %d\n",ecx
lea edi, [edx + 40] ; get file name offset
;DEBUGF 1,"filename: ..\n"
mov dword[edi], '..'
mov edi, edx
push eax ebx edx esi
stdcall xfs_get_inode_number_sf, dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count], dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent + 4], dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent]
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
; test eax, eax
; jnz .error
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ebx eax
jnz .error
mov ecx, [esp + 44] ; file name encding
mov [edx + 4], ecx
add edx, 304 ; ASCII only for now
pop ecx
dec ecx
jz .quit
 
; push ecx
; lea edi, [edx + 40]
;DEBUGF 1,"filename: .\n"
; mov dword[edi], '.'
; mov edi, edx
; push eax edx
; stdcall xfs_get_inode_info, [ebp + XFS.cur_inode], edi
; test eax, eax
; pop edx eax
; jnz .error
; mov ecx, [esp + 44]
; mov [edx + 4], ecx
; add edx, 304 ; ASCII only for now
; pop ecx
; dec ecx
; jz .quit
 
; we skipped some entries
; now we fill min(required, present) number of bdfe's
 
.shortdir.skipped:
;DEBUGF 1,"ecx: %d\n",ecx
push ecx
movzx ecx, byte[esi + xfs_dir2_sf_entry.namelen]
add esi, xfs_dir2_sf_entry.name
lea edi, [edx + 40] ; bdfe offset of file name
;DEBUGF 1,"filename: |%s|\n",esi
rep movsb
mov word[edi], 0 ; terminator (ASCIIZ)
 
push eax ebx ecx edx esi
; push edx ; for xfs_get_inode_info
mov edi, edx
stdcall xfs_get_inode_number_sf, dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count], [esi + 4], [esi]
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
; test eax, eax
; jnz .error
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ecx ebx eax
jnz .error
mov ecx, [esp + 44] ; file name encoding
mov [edx + 4], ecx
 
add edx, 304 ; ASCII only for now
add esi, eax
pop ecx
dec ecx
jnz .shortdir.skipped
jmp .quit
 
.blockdir:
;DEBUGF 1,"blockdir\n"
push edx
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
stdcall xfs_extent_unpack, eax
;DEBUGF 1,"extent.br_startoff : 0x%x%x\n",[ebp+XFS.extent.br_startoff+4],[ebp+XFS.extent.br_startoff+0]
;DEBUGF 1,"extent.br_startblock: 0x%x%x\n",[ebp+XFS.extent.br_startblock+4],[ebp+XFS.extent.br_startblock+0]
;DEBUGF 1,"extent.br_blockcount: %d\n",[ebp+XFS.extent.br_blockcount]
;DEBUGF 1,"extent.br_state : %d\n",[ebp+XFS.extent.br_state]
stdcall xfs_read_dirblock, dword[ebp + XFS.extent.br_startblock + 0], dword[ebp + XFS.extent.br_startblock + 4], [ebp + XFS.cur_dirblock]
test eax, eax
pop edx
jnz .error
;DEBUGF 1,"dirblock signature: %s\n",[ebp+XFS.cur_dirblock]
mov ebx, [ebp + XFS.cur_dirblock]
mov dword[edx + 0], 1 ; version
mov eax, [ebp + XFS.dirblocksize]
mov ecx, [ebx + eax - sizeof.xfs_dir2_block_tail + xfs_dir2_block_tail.stale]
mov eax, [ebx + eax - sizeof.xfs_dir2_block_tail + xfs_dir2_block_tail.count]
bswap ecx
bswap eax
sub eax, ecx ; actual number of entries = count - stale
mov [edx + 8], eax ; total entries
;DEBUGF 1,"total entries: %d\n",eax
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
mov [ebp + XFS.entries_read], eax
;DEBUGF 1,"actually read entries: %d\n",eax
mov dword[edx + 12], 0 ; reserved
mov dword[edx + 16], 0 ;
mov dword[edx + 20], 0 ;
mov dword[edx + 24], 0 ;
mov dword[edx + 28], 0 ;
add ebx, xfs_dir2_block.u
 
mov ecx, [esp + 24] ; start entry number
; also means how many to skip
test ecx, ecx
jz .blockdir.skipped
.blockdir.skip:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .blockdir.skip
@@:
movzx eax, [ebx + xfs_dir2_data_union.xentry.namelen]
lea ebx, [ebx + xfs_dir2_data_union.xentry.name + eax + 2] ; 2 bytes for 'tag'
add ebx, 7 ; align on 8 bytes
and ebx, not 7
dec ecx
jnz .blockdir.skip
.blockdir.skipped:
mov ecx, [edx + 4] ; actually read entries
test ecx, ecx
jz .quit
add edx, 32 ; set edx to the first bdfe
.blockdir.next_entry:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_NULL
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .blockdir.next_entry
@@:
push ecx
push eax ebx ecx edx esi
mov edi, edx
mov edx, dword[ebx + xfs_dir2_data_union.xentry.inumber + 0]
mov eax, dword[ebx + xfs_dir2_data_union.xentry.inumber + 4]
bswap edx
bswap eax
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ecx ebx eax
jnz .error
mov ecx, [esp + 44]
mov [edx + 4], ecx
lea edi, [edx + 40]
movzx ecx, byte[ebx + xfs_dir2_data_union.xentry.namelen]
lea esi, [ebx + xfs_dir2_data_union.xentry.name]
;DEBUGF 1,"filename: |%s|\n",esi
rep movsb
; call utf8_to_cp866
mov word[edi], 0 ; terminator
lea ebx, [esi + 2] ; skip 'tag'
add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes
and ebx, not 7
add edx, 304
pop ecx
dec ecx
jnz .blockdir.next_entry
jmp .quit
 
.leafdir:
;DEBUGF 1,"readdir: leaf\n"
mov [ebp + XFS.cur_inode_save], ebx
push ebx ecx edx
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, [ebp + XFS.dir2_leaf_offset_blocks], 0, edx, 0xffffffff, 0xffffffff
mov ecx, eax
and ecx, edx
inc ecx
pop edx ecx ebx
jz .error
 
mov eax, [ebp + XFS.cur_dirblock]
movzx ecx, word[eax + xfs_dir2_leaf.hdr.stale]
movzx eax, word[eax + xfs_dir2_leaf.hdr.count]
xchg cl, ch
xchg al, ah
sub eax, ecx
;DEBUGF 1,"total count: %d\n",eax
 
mov dword[edx + 0], 1 ; version
mov [edx + 8], eax ; total entries
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
 
mov dword[edx + 12], 0 ; reserved
mov dword[edx + 16], 0 ;
mov dword[edx + 20], 0 ;
mov dword[edx + 24], 0 ;
mov dword[edx + 28], 0 ;
 
mov eax, [ebp + XFS.cur_dirblock]
add eax, [ebp + XFS.dirblocksize]
mov [ebp + XFS.max_dirblockaddr], eax
mov dword[ebp + XFS.next_block_num + 0], 0
mov dword[ebp + XFS.next_block_num + 4], 0
 
mov ebx, [ebp + XFS.max_dirblockaddr] ; to read dirblock immediately
mov ecx, [esp + 24] ; start number
test ecx, ecx
jz .leafdir.skipped
.leafdir.skip:
cmp ebx, [ebp + XFS.max_dirblockaddr]
jne @f
push ecx edx
mov ebx, [ebp + XFS.cur_inode_save]
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
mov ecx, eax
and ecx, edx
inc ecx
jz .error
add eax, 1
adc edx, 0
mov dword[ebp + XFS.next_block_num + 0], eax
mov dword[ebp + XFS.next_block_num + 4], edx
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, sizeof.xfs_dir2_data_hdr
pop edx ecx
@@:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .leafdir.skip
@@:
movzx eax, [ebx + xfs_dir2_data_union.xentry.namelen]
lea ebx, [ebx + xfs_dir2_data_union.xentry.name + eax + 2] ; 2 for 'tag'
add ebx, 7
and ebx, not 7
dec ecx
jnz .leafdir.skip
.leafdir.skipped:
mov [ebp + XFS.entries_read], 0
mov ecx, [edx + 4] ; actually read entries
test ecx, ecx
jz .quit
add edx, 32 ; first bdfe entry
.leafdir.next_entry:
;DEBUGF 1,"next_extry\n"
cmp ebx, [ebp + XFS.max_dirblockaddr]
jne .leafdir.process_current_block
push ecx edx
mov ebx, [ebp + XFS.cur_inode_save]
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
pop edx ecx
jmp .quit
@@:
add eax, 1
adc edx, 0
mov dword[ebp + XFS.next_block_num + 0], eax
mov dword[ebp + XFS.next_block_num + 4], edx
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, sizeof.xfs_dir2_data_hdr
pop edx ecx
.leafdir.process_current_block:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .leafdir.next_entry
@@:
push eax ebx ecx edx esi
mov edi, edx
mov edx, dword[ebx + xfs_dir2_data_union.xentry.inumber + 0]
mov eax, dword[ebx + xfs_dir2_data_union.xentry.inumber + 4]
bswap edx
bswap eax
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ecx ebx eax
jnz .error
push ecx
mov ecx, [esp + 44]
mov [edx + 4], ecx
lea edi, [edx + 40]
movzx ecx, byte[ebx + xfs_dir2_data_union.xentry.namelen]
lea esi, [ebx + xfs_dir2_data_union.xentry.name]
;DEBUGF 1,"filename: |%s|\n",esi
rep movsb
pop ecx
mov word[edi], 0
lea ebx, [esi + 2] ; skip 'tag'
add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes
and ebx, not 7
add edx, 304 ; ASCII only for now
inc [ebp + XFS.entries_read]
dec ecx
jnz .leafdir.next_entry
jmp .quit
 
.nodedir:
;DEBUGF 1,"readdir: node\n"
push edx
mov [ebp + XFS.cur_inode_save], ebx
mov [ebp + XFS.entries_read], 0
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_dir2_node_get_numfiles, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks]
pop edx
test eax, eax
jnz .error
mov eax, [ebp + XFS.entries_read]
mov [ebp + XFS.entries_read], 0
;DEBUGF 1,"numfiles: %d\n",eax
mov dword[edx + 0], 1 ; version
mov [edx + 8], eax ; total entries
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
 
mov dword[edx + 12], 0 ; reserved
mov dword[edx + 16], 0 ;
mov dword[edx + 20], 0 ;
mov dword[edx + 24], 0 ;
mov dword[edx + 28], 0 ;
 
mov eax, [ebp + XFS.cur_dirblock]
add eax, [ebp + XFS.dirblocksize]
mov [ebp + XFS.max_dirblockaddr], eax
mov dword[ebp + XFS.next_block_num + 0], 0
mov dword[ebp + XFS.next_block_num + 4], 0
 
mov ebx, [ebp + XFS.max_dirblockaddr] ; to read dirblock immediately
mov ecx, [esp + 24] ; start number
test ecx, ecx
jz .leafdir.skipped
jmp .leafdir.skip
 
.btreedir:
;DEBUGF 1,"readdir: btree\n"
mov [ebp + XFS.cur_inode_save], ebx
push ebx edx
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
mov [ebp + XFS.ro_nextents], eax
mov eax, [ebp + XFS.inodesize]
sub eax, xfs_inode.di_u
sub eax, sizeof.xfs_bmdr_block
shr eax, 4
;DEBUGF 1,"maxnumresc: %d\n",eax
mov edx, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 0]
mov eax, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 4]
bswap eax
bswap edx
mov ebx, [ebp + XFS.cur_block]
;DEBUGF 1,"read_block: %x %x ",edx,eax
stdcall xfs_read_block
pop edx ebx
test eax, eax
jnz .error
;DEBUGF 1,"ok\n"
 
mov ebx, [ebp + XFS.cur_block]
push edx
mov [ebp + XFS.entries_read], 0
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
stdcall xfs_dir2_node_get_numfiles, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks]
pop edx
test eax, eax
jnz .error
mov eax, [ebp + XFS.entries_read]
mov [ebp + XFS.entries_read], 0
;DEBUGF 1,"numfiles: %d\n",eax
 
mov dword[edx + 0], 1 ; version
mov [edx + 8], eax ; total entries
sub eax, [esp + 24] ; start number
cmp eax, [esp + 28] ; entries to read
jbe @f
mov eax, [esp + 28]
@@:
mov [esp + 28], eax
mov [edx + 4], eax ; number of actually read entries
 
mov dword[edx + 12], 0
mov dword[edx + 16], 0
mov dword[edx + 20], 0
mov dword[edx + 24], 0
mov dword[edx + 28], 0
 
mov eax, [ebp + XFS.cur_dirblock] ; fsblock?
add eax, [ebp + XFS.dirblocksize]
mov [ebp + XFS.max_dirblockaddr], eax
mov dword[ebp + XFS.next_block_num + 0], 0
mov dword[ebp + XFS.next_block_num + 4], 0
 
mov ebx, [ebp + XFS.max_dirblockaddr] ; to read dirblock immediately
mov ecx, [esp + 24] ; start number
test ecx, ecx
jz .btreedir.skipped
; jmp .btreedir.skip
.btreedir.skip:
cmp ebx, [ebp + XFS.max_dirblockaddr]
jne @f
push ecx edx
mov ebx, [ebp + XFS.cur_block]
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jz .error
add eax, 1
adc edx, 0
mov dword[ebp + XFS.next_block_num + 0], eax
mov dword[ebp + XFS.next_block_num + 4], edx
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, sizeof.xfs_dir2_data_hdr
pop edx ecx
@@:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .btreedir.skip
@@:
movzx eax, [ebx + xfs_dir2_data_union.xentry.namelen]
lea ebx, [ebx + xfs_dir2_data_union.xentry.name + eax + 2] ; 2 for 'tag'
add ebx, 7
and ebx, not 7
dec ecx
jnz .btreedir.skip
.btreedir.skipped:
mov [ebp + XFS.entries_read], 0
mov ecx, [edx + 4] ; actually read entries
test ecx, ecx
jz .quit
add edx, 32
.btreedir.next_entry:
;mov eax, [ebp + XFS.entries_read]
;DEBUGF 1,"next_extry: %d\n",eax
cmp ebx, [ebp + XFS.max_dirblockaddr]
jne .btreedir.process_current_block
push ecx edx
mov ebx, [ebp + XFS.cur_block]
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
stdcall xfs_extent_list_read_dirblock, eax, dword[ebp + XFS.next_block_num + 0], dword[ebp + XFS.next_block_num + 4], edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
pop edx ecx
jmp .quit
@@:
add eax, 1
adc edx, 0
mov dword[ebp + XFS.next_block_num + 0], eax
mov dword[ebp + XFS.next_block_num + 4], edx
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, sizeof.xfs_dir2_data_hdr
pop edx ecx
.btreedir.process_current_block:
cmp word[ebx + xfs_dir2_data_union.unused.freetag], XFS_DIR2_DATA_FREE_TAG
jne @f
movzx eax, word[ebx + xfs_dir2_data_union.unused.length]
xchg al, ah
add ebx, eax
jmp .btreedir.next_entry
@@:
push eax ebx ecx edx esi
mov edi, edx
mov edx, dword[ebx + xfs_dir2_data_union.xentry.inumber + 0]
mov eax, dword[ebx + xfs_dir2_data_union.xentry.inumber + 4]
bswap edx
bswap eax
stdcall xfs_read_inode, eax, edx, [ebp + XFS.tmp_inode]
stdcall xfs_get_inode_info, edx, edi
test eax, eax
pop esi edx ecx ebx eax
jnz .error
push ecx
mov ecx, [esp + 44]
mov [edx + 4], ecx
lea edi, [edx + 40]
movzx ecx, byte[ebx + xfs_dir2_data_union.xentry.namelen]
lea esi, [ebx + xfs_dir2_data_union.xentry.name]
;DEBUGF 1,"filename: |%s|\n",esi
rep movsb
pop ecx
mov word[edi], 0
lea ebx, [esi + 2] ; skip 'tag'
add ebx, 7 ; xfs_dir2_data_entries are aligned to 8 bytes
and ebx, not 7
add edx, 304
inc [ebp + XFS.entries_read]
dec ecx
jnz .btreedir.next_entry
jmp .quit
 
 
.quit:
pop edi esi edx ecx
add esp, 4 ; pop vars
xor eax, eax
; mov ebx, [esp + 8]
mov ebx, [ebp + XFS.entries_read]
DEBUGF 1,"xfs_dir_get_bdfes done: %d\n",ebx
ret 20
.error:
pop edi esi edx ecx
add esp, 4 ; pop vars
mov eax, ERROR_FS_FAIL
movi ebx, -1
ret 20
 
 
;----------------------------------------------------------------
; push inode_hi
; push inode_lo
; push name
;----------------------------------------------------------------
xfs_get_inode_short:
; this function searches for the file in _current_ dir
; it is called recursively for all the subdirs /path/to/my/file
 
;DEBUGF 1,"xfs_get_inode_short: %s\n",[esp+4]
mov esi, [esp + 4] ; name
movzx eax, word[esi]
cmp eax, '.' ; current dir; it is already read, just return
je .quit
cmp eax, './' ; same thing
je .quit
 
; read inode
 
mov eax, [esp + 8] ; inode_lo
mov edx, [esp + 12] ; inode_hi
stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode]
test eax, eax
movi eax, ERROR_FS_FAIL
jnz .error
 
; find file name in directory
; switch directory ondisk format
 
mov ebx, edx
mov [ebp + XFS.cur_inode_save], ebx
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_LOCAL
jne .not_shortdir
;DEBUGF 1,"dir: shortdir\n"
jmp .shortdir
.not_shortdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_blockdir
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
cmp eax, 1
jne .not_blockdir
jmp .blockdir
.not_blockdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_leafdir
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
cmp eax, 4
ja .not_leafdir
jmp .leafdir
.not_leafdir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_nodedir
jmp .nodedir
.not_nodedir:
cmp byte[ebx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE
jne .not_btreedir
jmp .btreedir
.not_btreedir:
DEBUGF 1,"NOT IMPLEMENTED: DIR FORMAT\n"
jmp .error
 
.shortdir:
.shortdir.check_parent:
; parent inode number in shortform directories is always implicit, check this case
mov eax, [esi]
and eax, 0x00ffffff
cmp eax, '..'
je .shortdir.parent2
cmp eax, '../'
je .shortdir.parent3
jmp .shortdir.common
.shortdir.parent3:
inc esi
.shortdir.parent2:
add esi, 2
add ebx, xfs_inode.di_u
stdcall xfs_get_inode_number_sf, dword[ebx + xfs_dir2_sf_hdr.count], dword[ebx + xfs_dir2_sf_hdr.parent + 4], dword[ebx + xfs_dir2_sf_hdr.parent]
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
 
; not a parent inode?
; search in the list, all the other files are stored uniformly
 
.shortdir.common:
mov eax, 4
movzx edx, word[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count] ; read count (byte) and i8count (byte) at once
test dl, dl ; is count zero?
jnz @f
shr edx, 8 ; use i8count
add eax, eax ; inode_num size
@@:
lea edi, [ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.parent + eax]
 
.next_name:
movzx ecx, byte[edi + xfs_dir2_sf_entry.namelen]
add edi, xfs_dir2_sf_entry.name
mov esi, [esp + 4]
;DEBUGF 1,"esi: %s\n",esi
;DEBUGF 1,"edi: %s\n",edi
repe cmpsb
jne @f
cmp byte[esi], 0 ; HINT: use adc here?
je .found
cmp byte[esi], '/'
je .found_inc
@@:
add edi, ecx
add edi, eax
dec edx
jnz .next_name
movi eax, ERROR_FILE_NOT_FOUND
jmp .error
.found_inc: ; increment esi to skip '/' symbol
; this means esi always points to valid file name or zero terminator byte
inc esi
.found:
stdcall xfs_get_inode_number_sf, dword[ebx + xfs_inode.di_u + xfs_dir2_sf_hdr.count], [edi + 4], [edi]
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
 
.blockdir:
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
stdcall xfs_extent_unpack, eax
stdcall xfs_read_dirblock, dword[ebp + XFS.extent.br_startblock + 0], dword[ebp + XFS.extent.br_startblock + 4], [ebp + XFS.cur_dirblock]
test eax, eax
jnz .error
;DEBUGF 1,"dirblock signature: %s\n",[ebp+XFS.cur_dirblock]
mov ebx, [ebp + XFS.cur_dirblock]
mov eax, [ebp + XFS.dirblocksize]
mov eax, [ebx + eax - sizeof.xfs_dir2_block_tail + xfs_dir2_block_tail.count]
; note that we don't subtract xfs_dir2_block_tail.stale here,
; since we need the number of leaf entries rather than file number
bswap eax
add ebx, [ebp + XFS.dirblocksize]
; mov ecx, sizeof.xfs_dir2_leaf_entry
imul ecx, eax, sizeof.xfs_dir2_leaf_entry
sub ebx, sizeof.xfs_dir2_block_tail
sub ebx, ecx
shr ecx, 3
push ecx ; for xfs_get_inode_by_hash
push ebx ; for xfs_get_inode_by_hash
 
mov edi, esi
xor eax, eax
mov ecx, 4096 ; MAX_PATH_LEN
repne scasb
movi eax, ERROR_FS_FAIL
jne .error
neg ecx
add ecx, 4096 ; MAX_PATH_LEN
dec ecx
mov edx, ecx
;DEBUGF 1,"strlen total : %d\n",edx
mov edi, esi
mov eax, '/'
mov ecx, edx
repne scasb
jne @f
inc ecx
@@:
neg ecx
add ecx, edx
;DEBUGF 1,"strlen current: %d\n",ecx
stdcall xfs_hashname, esi, ecx
add esi, ecx
cmp byte[esi], '/'
jne @f
inc esi
@@:
;DEBUGF 1,"hashed: 0x%x\n",eax
; bswap eax
stdcall xfs_get_addr_by_hash
bswap eax
;DEBUGF 1,"got address: 0x%x\n",eax
cmp eax, -1
jne @f
movi eax, ERROR_FILE_NOT_FOUND
mov ebx, -1
jmp .error
@@:
shl eax, 3
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, eax
mov edx, [ebx + 0]
mov eax, [ebx + 4]
bswap edx
bswap eax
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
 
.leafdir:
;DEBUGF 1,"dirblock signature: %s\n",[ebp+XFS.cur_dirblock]
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, [ebp + XFS.dir2_leaf_offset_blocks], 0, edx, -1, -1
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jz .error
 
mov ebx, [ebp + XFS.cur_dirblock]
movzx eax, [ebx + xfs_dir2_leaf.hdr.count]
; note that we don't subtract xfs_dir2_leaf.hdr.stale here,
; since we need the number of leaf entries rather than file number
xchg al, ah
add ebx, xfs_dir2_leaf.ents
; imul ecx, eax, sizeof.xfs_dir2_leaf_entry
; shr ecx, 3
push eax ; for xfs_get_addr_by_hash: len
push ebx ; for xfs_get_addr_by_hash: base
 
mov edi, esi
xor eax, eax
mov ecx, 4096 ; MAX_PATH_LEN
repne scasb
movi eax, ERROR_FS_FAIL
jne .error
neg ecx
add ecx, 4096
dec ecx
mov edx, ecx
;DEBUGF 1,"strlen total : %d\n",edx
mov edi, esi
mov eax, '/'
mov ecx, edx
repne scasb
jne @f
inc ecx
@@:
neg ecx
add ecx, edx
;DEBUGF 1,"strlen current: %d\n",ecx
stdcall xfs_hashname, esi, ecx
add esi, ecx
cmp byte[esi], '/'
jne @f
inc esi
@@:
;DEBUGF 1,"hashed: 0x%x\n",eax
stdcall xfs_get_addr_by_hash
bswap eax
;DEBUGF 1,"got address: 0x%x\n",eax
cmp eax, -1
jne @f
movi eax, ERROR_FILE_NOT_FOUND
mov ebx, -1
jmp .error
@@:
 
mov ebx, [ebp + XFS.cur_inode_save]
push esi edi
xor edi, edi
mov esi, eax
shld edi, esi, 3 ; get offset
shl esi, 3 ; 2^3 = 8 byte align
mov edx, esi
mov ecx, [ebp + XFS.dirblklog]
add ecx, [ebp + XFS.blocklog]
mov eax, 1
shl eax, cl
dec eax
and edx, eax
push edx
shrd esi, edi, cl
shr edi, cl
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, esi, edi, edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
pop edx
pop edi esi
mov ecx, eax
and ecx, edx
inc ecx
jz .error
 
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, edx
mov edx, [ebx + 0]
mov eax, [ebx + 4]
bswap edx
bswap eax
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
 
.nodedir:
;DEBUGF 1,"lookupdir: node\n"
mov [ebp + XFS.cur_inode_save], ebx
 
mov edi, esi
xor eax, eax
mov ecx, 4096 ; MAX_PATH_LEN
repne scasb
movi eax, ERROR_FS_FAIL
jne .error
neg ecx
add ecx, 4096 ; MAX_PATH_LEN
dec ecx
mov edx, ecx
;DEBUGF 1,"strlen total : %d\n",edx
mov edi, esi
mov eax, '/'
mov ecx, edx
repne scasb
jne @f
inc ecx
@@:
neg ecx
add ecx, edx
;DEBUGF 1,"strlen current: %d\n",ecx
stdcall xfs_hashname, esi, ecx
add esi, ecx
cmp byte[esi], '/'
jne @f
inc esi
@@:
;DEBUGF 1,"hashed: 0x%x\n",eax
push edi edx
mov edi, eax
mov [ebp + XFS.entries_read], 0
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_dir2_lookupdir_node, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks], edi
pop edx edi
test eax, eax
jnz .error
bswap ecx
;DEBUGF 1,"got address: 0x%x\n",ecx
 
mov ebx, [ebp + XFS.cur_inode_save]
push esi edi
xor edi, edi
mov esi, ecx
shld edi, esi, 3 ; get offset
shl esi, 3 ; 8 byte align
mov edx, esi
mov ecx, [ebp + XFS.dirblklog]
add ecx, [ebp + XFS.blocklog]
mov eax, 1
shl eax, cl
dec eax
and edx, eax
push edx
shrd esi, edi, cl
shr edi, cl
lea eax, [ebx + xfs_inode.di_u + xfs_bmbt_rec.l0]
mov edx, [ebx + xfs_inode.di_core.di_nextents]
bswap edx
stdcall xfs_extent_list_read_dirblock, eax, esi, edi, edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
pop edx
pop edi esi
mov ecx, eax
and ecx, edx
inc ecx
jz .error
 
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, edx
mov edx, [ebx + 0]
mov eax, [ebx + 4]
bswap edx
bswap eax
;DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
 
.btreedir:
DEBUGF 1,"lookupdir: btree\n"
mov [ebp + XFS.cur_inode_save], ebx
 
push ebx edx
mov eax, [ebx + xfs_inode.di_core.di_nextents]
bswap eax
mov [ebp + XFS.ro_nextents], eax
mov eax, [ebp + XFS.inodesize]
sub eax, xfs_inode.di_u
sub eax, sizeof.xfs_bmdr_block
shr eax, 4 ; FIXME forkoff
;DEBUGF 1,"maxnumresc: %d\n",eax
mov edx, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 0]
mov eax, dword[ebx + xfs_inode.di_u + sizeof.xfs_bmdr_block + sizeof.xfs_bmbt_key*eax + 4]
bswap eax
bswap edx
mov ebx, [ebp + XFS.cur_block]
;DEBUGF 1,"read_block: %x %x ",edx,eax
stdcall xfs_read_block
pop edx ebx
test eax, eax
jnz .error
;DEBUGF 1,"ok\n"
mov ebx, [ebp + XFS.cur_block]
 
mov edi, esi
xor eax, eax
mov ecx, 4096 ; MAX_PATH_LEN
repne scasb
movi eax, ERROR_FS_FAIL
jne .error
neg ecx
add ecx, 4096
dec ecx
mov edx, ecx
DEBUGF 1,"strlen total : %d\n",edx
mov edi, esi
mov eax, '/'
mov ecx, edx
repne scasb
jne @f
inc ecx
@@:
neg ecx
add ecx, edx
DEBUGF 1,"strlen current: %d\n",ecx
stdcall xfs_hashname, esi, ecx
add esi, ecx
cmp byte[esi], '/'
jne @f
inc esi
@@:
DEBUGF 1,"hashed: 0x%x\n",eax
push edi edx
mov edi, eax
mov [ebp + XFS.entries_read], 0
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
;push eax
;mov eax, [ebp + XFS.dir2_leaf_offset_blocks]
;DEBUGF 1,": 0x%x %d\n",eax,eax
;pop eax
stdcall xfs_dir2_lookupdir_node, eax, edx, [ebp + XFS.dir2_leaf_offset_blocks], edi
pop edx edi
test eax, eax
jnz .error
bswap ecx
DEBUGF 1,"got address: 0x%x\n",ecx
 
mov ebx, [ebp + XFS.cur_block]
push esi edi
xor edi, edi
mov esi, ecx
shld edi, esi, 3 ; get offset
shl esi, 3
mov edx, esi
mov ecx, [ebp + XFS.dirblklog]
add ecx, [ebp + XFS.blocklog]
mov eax, 1
shl eax, cl
dec eax
and edx, eax
push edx
shrd esi, edi, cl
shr edi, cl
lea eax, [ebx + sizeof.xfs_bmbt_block]
mov edx, [ebp + XFS.ro_nextents]
stdcall xfs_extent_list_read_dirblock, eax, esi, edi, edx, [ebp + XFS.dir2_leaf_offset_blocks], 0
;DEBUGF 1,"RETVALUE: %d %d\n",edx,eax
pop edx
pop edi esi
mov ecx, eax
and ecx, edx
inc ecx
jz .error
 
mov ebx, [ebp + XFS.cur_dirblock]
add ebx, edx
mov edx, [ebx + 0]
mov eax, [ebx + 4]
bswap edx
bswap eax
DEBUGF 1,"found inode: 0x%x%x\n",edx,eax
jmp .quit
 
.quit:
ret 12
.error:
xor eax, eax
mov edx, eax
ret 12
 
 
;----------------------------------------------------------------
; push name
; call xfs_get_inode
; test eax, eax
;----------------------------------------------------------------
xfs_get_inode:
; call xfs_get_inode_short until file is found / error returned
 
;DEBUGF 1,"getting inode of: %s\n",[esp+4]
push ebx esi edi
 
; start from the root inode
 
mov edx, dword[ebp + XFS.rootino + 4] ; hi
mov eax, dword[ebp + XFS.rootino + 0] ; lo
mov esi, [esp + 16] ; name
 
.next_dir:
cmp byte[esi], 0
je .found
 
;DEBUGF 1,"next_level: |%s|\n",esi
stdcall xfs_get_inode_short, esi, eax, edx
test edx, edx
jnz @f
test eax, eax
jz .error
@@:
jmp .next_dir ; file name found, go to next directory level
 
.found:
 
.quit:
pop edi esi ebx
ret 4
.error:
pop edi esi ebx
xor eax, eax
mov edx, eax
ret 4
 
 
;----------------------------------------------------------------
; xfs_ReadFolder - XFS implementation of reading a folder
; in: ebp = pointer to XFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
xfs_ReadFolder:
 
; to read folder
; 1. lock partition
; 2. find inode number
; 3. read this inode
; 4. get bdfe's
; 5. unlock partition
 
; 1.
call xfs_lock
push ecx edx esi edi
 
; 2.
push ebx esi edi
add esi, [esp + 32] ; directory name
;DEBUGF 1,"xfs_ReadFolder: |%s|\n",esi
stdcall xfs_get_inode, esi
pop edi esi ebx
mov ecx, edx
or ecx, eax
jnz @f
movi eax, ERROR_FILE_NOT_FOUND
@@:
 
; 3.
stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode]
test eax, eax
movi eax, ERROR_FS_FAIL
jnz .error
 
; 4.
mov eax, [ebx + 8] ; encoding
and eax, 1
stdcall xfs_dir_get_bdfes, [ebx + 4], [ebx + 12], [ebx + 16], edx, eax
test eax, eax
jnz .error
 
.quit:
;DEBUGF 1,"\n\n"
pop edi esi edx ecx
; 5.
call xfs_unlock
xor eax, eax
ret
.error:
;DEBUGF 1,"\n\n"
pop edi esi edx ecx
push eax
call xfs_unlock
pop eax
ret
 
 
;----------------------------------------------------------------
; push inode_num_hi
; push inode_num_lo
; push [count]
; call xfs_get_inode_number_sf
;----------------------------------------------------------------
xfs_get_inode_number_sf:
 
; inode numbers in short form directories may be 4 or 8 bytes long
; determine the length in run time and read inode number at given address
 
cmp byte[esp + 4 + xfs_dir2_sf_hdr.i8count], 0 ; i8count == 0 means 4 byte per inode number
je .i4bytes
.i8bytes:
mov edx, [esp + 12] ; hi
mov eax, [esp + 8] ; lo
bswap edx ; big endian
bswap eax
ret 12
.i4bytes:
xor edx, edx ; no hi
mov eax, [esp + 12] ; hi = lo
bswap eax ; big endian
ret 12
 
 
;----------------------------------------------------------------
; push dest
; push src
; call xfs_get_inode_info
;----------------------------------------------------------------
xfs_get_inode_info:
 
; get access time and other file properties
; useful for browsing directories
; called for each dir entry
 
;DEBUGF 1,"get_inode_info\n"
xor eax, eax
mov edx, [esp + 4]
movzx ecx, word[edx + xfs_inode.di_core.di_mode]
xchg cl, ch
;DEBUGF 1,"di_mode: %x\n",ecx
test ecx, S_IFDIR ; directory?
jz @f
mov eax, 0x10 ; set directory flag
@@:
 
mov edi, [esp + 8]
mov [edi + 0], eax
mov eax, dword[edx + xfs_inode.di_core.di_size + 0] ; hi
bswap eax
mov dword[edi + 36], eax ; file size hi
;DEBUGF 1,"file_size hi: %d\n",eax
mov eax, dword[edx + xfs_inode.di_core.di_size + 4] ; lo
bswap eax
mov dword[edi + 32], eax ; file size lo
;DEBUGF 1,"file_size lo: %d\n",eax
 
add edi, 8
mov eax, [edx + xfs_inode.di_core.di_ctime.t_sec]
bswap eax
push edx
xor edx, edx
add eax, 3054539008 ;(369 * 365 + 89) * 24 * 3600
adc edx, 2
call ntfs_datetime_to_bdfe.sec
pop edx
 
mov eax, [edx + xfs_inode.di_core.di_atime.t_sec]
bswap eax
push edx
xor edx, edx
add eax, 3054539008 ;(369 * 365 + 89) * 24 * 3600
adc edx, 2
call ntfs_datetime_to_bdfe.sec
pop edx
 
mov eax, [edx + xfs_inode.di_core.di_mtime.t_sec]
bswap eax
push edx
xor edx, edx
add eax, 3054539008 ;(369 * 365 + 89) * 24 * 3600
adc edx, 2
call ntfs_datetime_to_bdfe.sec
pop edx
 
.quit:
xor eax, eax
ret 8
.error:
movi eax, ERROR_FS_FAIL
ret 8
 
 
;----------------------------------------------------------------
; push extent_data
; call xfs_extent_unpack
;----------------------------------------------------------------
xfs_extent_unpack:
 
; extents come as packet 128bit bitfields
; lets unpack them to access internal fields
; write result to the XFS.extent structure
 
push eax ebx ecx edx
mov ebx, [esp + 20]
 
xor eax, eax
mov edx, [ebx + 0]
bswap edx
test edx, 0x80000000 ; mask, see documentation
setnz al
mov [ebp + XFS.extent.br_state], eax
 
and edx, 0x7fffffff ; mask
mov eax, [ebx + 4]
bswap eax
shrd eax, edx, 9
shr edx, 9
mov dword[ebp + XFS.extent.br_startoff + 0], eax
mov dword[ebp + XFS.extent.br_startoff + 4], edx
 
mov edx, [ebx + 4]
mov eax, [ebx + 8]
mov ecx, [ebx + 12]
bswap edx
bswap eax
bswap ecx
and edx, 0x000001ff ; mask
shrd ecx, eax, 21
shrd eax, edx, 21
mov dword[ebp + XFS.extent.br_startblock + 0], ecx
mov dword[ebp + XFS.extent.br_startblock + 4], eax
 
mov eax, [ebx + 12]
bswap eax
and eax, 0x001fffff ; mask
mov [ebp + XFS.extent.br_blockcount], eax
 
pop edx ecx ebx eax
;DEBUGF 1,"extent.br_startoff : %d %d\n",[ebp+XFS.extent.br_startoff+4],[ebp+XFS.extent.br_startoff+0]
;DEBUGF 1,"extent.br_startblock: %d %d\n",[ebp+XFS.extent.br_startblock+4],[ebp+XFS.extent.br_startblock+0]
;DEBUGF 1,"extent.br_blockcount: %d\n",[ebp+XFS.extent.br_blockcount]
;DEBUGF 1,"extent.br_state : %d\n",[ebp+XFS.extent.br_state]
ret 4
 
 
;----------------------------------------------------------------
; push namelen
; push name
; call xfs_hashname
;----------------------------------------------------------------
xfs_hashname: ; xfs_da_hashname
 
; simple hash function
; never fails)
 
push ecx esi
xor eax, eax
mov esi, [esp + 12] ; name
mov ecx, [esp + 16] ; namelen
;mov esi, '.'
;mov ecx, 1
;DEBUGF 1,"hashname: %d %s\n",ecx,esi
 
@@:
rol eax, 7
xor al, [esi]
add esi, 1
loop @b
 
pop esi ecx
ret 8
 
 
;----------------------------------------------------------------
; push len
; push base
; eax -- hash value
; call xfs_get_addr_by_hash
;----------------------------------------------------------------
xfs_get_addr_by_hash:
 
; look for the directory entry offset by its file name hash
; allows fast file search for block, leaf and node directories
; binary (ternary) search
 
;DEBUGF 1,"get_addr_by_hash\n"
push ebx esi
mov ebx, [esp + 12] ; left
mov edx, [esp + 16] ; len
.next:
mov ecx, edx
; jecxz .error
test ecx, ecx
jz .error
shr ecx, 1
mov esi, [ebx + ecx*8 + xfs_dir2_leaf_entry.hashval]
bswap esi
;DEBUGF 1,"cmp 0x%x",esi
cmp eax, esi
jb .below
ja .above
mov eax, [ebx + ecx*8 + xfs_dir2_leaf_entry.address]
pop esi ebx
ret 8
.below:
;DEBUGF 1,"b\n"
mov edx, ecx
jmp .next
.above:
;DEBUGF 1,"a\n"
lea ebx, [ebx + ecx*8 + 8]
sub edx, ecx
dec edx
jmp .next
.error:
mov eax, -1
pop esi ebx
ret 8
 
 
;----------------------------------------------------------------
; xfs_GetFileInfo - XFS implementation of getting file info
; in: ebp = pointer to XFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
xfs_GetFileInfo:
 
; lock partition
; get inode number by file name
; read inode
; get info
; unlock partition
 
push ecx edx esi edi
call xfs_lock
 
add esi, [esp + 20] ; name
;DEBUGF 1,"xfs_GetFileInfo: |%s|\n",esi
stdcall xfs_get_inode, esi
mov ecx, edx
or ecx, eax
jnz @f
movi eax, ERROR_FILE_NOT_FOUND
jmp .error
@@:
stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode]
test eax, eax
movi eax, ERROR_FS_FAIL
jnz .error
 
stdcall xfs_get_inode_info, edx, [ebx + 16]
 
.quit:
call xfs_unlock
pop edi esi edx ecx
xor eax, eax
;DEBUGF 1,"quit\n\n"
ret
.error:
call xfs_unlock
pop edi esi edx ecx
;DEBUGF 1,"error\n\n"
ret
 
 
;----------------------------------------------------------------
; xfs_Read - XFS implementation of reading a file
; in: ebp = pointer to XFS structure
; in: esi+[esp+4] = name
; in: ebx = pointer to parameters from sysfunc 70
; out: eax, ebx = return values for sysfunc 70
;----------------------------------------------------------------
xfs_Read:
push ebx ecx edx esi edi
call xfs_lock
 
add esi, [esp + 24]
;DEBUGF 1,"xfs_Read: %d %d |%s|\n",[ebx+4],[ebx+12],esi
stdcall xfs_get_inode, esi
mov ecx, edx
or ecx, eax
jnz @f
movi eax, ERROR_FILE_NOT_FOUND
jmp .error
@@:
stdcall xfs_read_inode, eax, edx, [ebp + XFS.cur_inode]
test eax, eax
movi eax, ERROR_FS_FAIL
jnz .error
mov [ebp + XFS.cur_inode_save], edx
 
cmp byte[edx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_EXTENTS
jne .not_extent_list
jmp .extent_list
.not_extent_list:
cmp byte[edx + xfs_inode.di_core.di_format], XFS_DINODE_FMT_BTREE
jne .not_btree
jmp .btree
.not_btree:
DEBUGF 1,"XFS: NOT IMPLEMENTED: FILE FORMAT\n"
movi eax, ERROR_FS_FAIL
jmp .error
.extent_list:
mov ecx, [ebx + 12] ; bytes to read
mov edi, [ebx + 16] ; buffer for data
mov esi, [ebx + 8] ; offset_hi
mov ebx, [ebx + 4] ; offset_lo
 
mov eax, dword[edx + xfs_inode.di_core.di_size + 4] ; lo
bswap eax
mov dword[ebp + XFS.bytes_left_in_file + 0], eax ; lo
mov eax, dword[edx + xfs_inode.di_core.di_size + 0] ; hi
bswap eax
mov dword[ebp + XFS.bytes_left_in_file + 4], eax ; hi
 
mov eax, [edx + xfs_inode.di_core.di_nextents]
bswap eax
mov [ebp + XFS.left_extents], eax
 
mov dword[ebp + XFS.bytes_read], 0 ; actually read bytes
 
xor eax, eax ; extent offset in list
.extent_list.next_extent:
;DEBUGF 1,"extent_list.next_extent, eax: 0x%x\n",eax
;DEBUGF 1,"bytes_to_read: %d\n",ecx
;DEBUGF 1,"cur file offset: %d %d\n",esi,ebx
;DEBUGF 1,"esp: 0x%x\n",esp
cmp [ebp + XFS.left_extents], 0
jne @f
test ecx, ecx
jz .quit
movi eax, ERROR_END_OF_FILE
jmp .error
@@:
push eax
lea eax, [edx + xfs_inode.di_u + eax + xfs_bmbt_rec.l0]
stdcall xfs_extent_unpack, eax
pop eax
dec [ebp + XFS.left_extents]
add eax, sizeof.xfs_bmbt_rec
push eax ebx ecx edx esi
mov ecx, [ebp + XFS.blocklog]
shrd ebx, esi, cl
shr esi, cl
cmp esi, dword[ebp + XFS.extent.br_startoff + 4]
jb .extent_list.to_hole ; handle sparse files
ja @f
cmp ebx, dword[ebp + XFS.extent.br_startoff + 0]
jb .extent_list.to_hole ; handle sparse files
je .extent_list.to_extent ; read from the start of current extent
@@:
xor edx, edx
mov eax, [ebp + XFS.extent.br_blockcount]
add eax, dword[ebp + XFS.extent.br_startoff + 0]
adc edx, dword[ebp + XFS.extent.br_startoff + 4]
;DEBUGF 1,"br_startoff: %d %d\n",edx,eax
cmp esi, edx
ja .extent_list.skip_extent
jb .extent_list.to_extent
cmp ebx, eax
jae .extent_list.skip_extent
jmp .extent_list.to_extent
.extent_list.to_hole:
;DEBUGF 1,"extent_list.to_hole\n"
pop esi edx ecx ebx eax
jmp .extent_list.read_hole
.extent_list.to_extent:
;DEBUGF 1,"extent_list.to_extent\n"
pop esi edx ecx ebx eax
jmp .extent_list.read_extent
.extent_list.skip_extent:
;DEBUGF 1,"extent_list.skip_extent\n"
pop esi edx ecx ebx eax
jmp .extent_list.next_extent
 
.extent_list.read_hole:
;DEBUGF 1,"hole: offt: 0x%x%x ",esi,ebx
push eax edx
mov eax, dword[ebp + XFS.extent.br_startoff + 0]
mov edx, dword[ebp + XFS.extent.br_startoff + 4]
push esi ebx
mov ebx, ecx
sub eax, ebx ; get hole_size, it is 64 bit
sbb edx, 0 ; now edx:eax contains the size of hole
;DEBUGF 1,"size: 0x%x%x\n",edx,eax
jnz @f ; if hole size >= 2^32, write bytes_to_read zero bytes
cmp eax, ecx ; if hole size >= bytes_to_read, write bytes_to_read zeros
jae @f
mov ecx, eax ; if hole is < than bytes_to_read, write hole size zeros
@@:
sub ebx, ecx ; bytes_to_read - hole_size = left_to_read
add dword[esp + 0], ecx ; update pushed file offset
adc dword[esp + 4], 0
xor eax, eax ; hole is made of zeros
rep stosb
mov ecx, ebx
pop ebx esi
 
test ecx, ecx ; all requested bytes are read?
pop edx eax
jz .quit
jmp .extent_list.read_extent ; continue from the start of unpacked extent
 
.extent_list.read_extent:
;DEBUGF 1,"extent_list.read_extent\n"
push eax ebx ecx edx esi
mov eax, ebx
mov edx, esi
mov ecx, [ebp + XFS.blocklog]
shrd eax, edx, cl
shr edx, cl
sub eax, dword[ebp + XFS.extent.br_startoff + 0] ; skip esi:ebx ?
sbb edx, dword[ebp + XFS.extent.br_startoff + 4]
sub [ebp + XFS.extent.br_blockcount], eax
add dword[ebp + XFS.extent.br_startblock + 0], eax
adc dword[ebp + XFS.extent.br_startblock + 4], 0
.extent_list.read_extent.next_block:
;DEBUGF 1,"extent_list.read_extent.next_block\n"
cmp [ebp + XFS.extent.br_blockcount], 0 ; out of blocks in current extent?
jne @f
pop esi edx ecx ebx eax
jmp .extent_list.next_extent ; go to next extent
@@:
mov eax, dword[ebp + XFS.extent.br_startblock + 0]
mov edx, dword[ebp + XFS.extent.br_startblock + 4]
push ebx
mov ebx, [ebp + XFS.cur_block]
;DEBUGF 1,"read block: 0x%x%x\n",edx,eax
stdcall xfs_read_block
test eax, eax
pop ebx
jz @f
pop esi edx ecx ebx eax
movi eax, ERROR_FS_FAIL
jmp .error
@@:
dec [ebp + XFS.extent.br_blockcount]
add dword[ebp + XFS.extent.br_startblock + 0], 1
adc dword[ebp + XFS.extent.br_startblock + 4], 0
mov esi, [ebp + XFS.cur_block]
mov ecx, [ebp + XFS.blocklog]
mov eax, 1
shl eax, cl
dec eax ; get blocklog mask
and eax, ebx ; offset in current block
add esi, eax
neg eax
add eax, [ebp + XFS.blocksize]
mov ecx, [esp + 8] ; pushed ecx, bytes_to_read
cmp ecx, eax ; is current block enough?
jbe @f ; if so, read bytes_to_read bytes
mov ecx, eax ; otherwise read the block up to the end
@@:
sub [esp + 8], ecx ; left_to_read
add [esp + 12], ecx ; update current file offset, pushed ebx
sub dword[ebp + XFS.bytes_left_in_file + 0], ecx
sbb dword[ebp + XFS.bytes_left_in_file + 4], 0
jnc @f
add dword[ebp + XFS.bytes_left_in_file + 0], ecx
mov ecx, dword[ebp + XFS.bytes_left_in_file + 0]
mov dword[ebp + XFS.bytes_left_in_file + 0], 0
mov dword[ebp + XFS.bytes_left_in_file + 4], 0
@@:
add [ebp + XFS.bytes_read], ecx
adc [esp + 0], dword 0 ; pushed esi
;DEBUGF 1,"read data: %d\n",ecx
rep movsb
mov ecx, [esp + 8]
;DEBUGF 1,"left_to_read: %d\n",ecx
xor ebx, ebx
test ecx, ecx
jz @f
cmp dword[ebp + XFS.bytes_left_in_file + 4], 0
jne .extent_list.read_extent.next_block
cmp dword[ebp + XFS.bytes_left_in_file + 0], 0
jne .extent_list.read_extent.next_block
@@:
pop esi edx ecx ebx eax
jmp .quit
 
.btree:
mov ecx, [ebx + 12] ; bytes to read
mov [ebp + XFS.bytes_to_read], ecx
mov edi, [ebx + 16] ; buffer for data
mov esi, [ebx + 8] ; offset_hi
mov ebx, [ebx + 4] ; offset_lo
mov dword[ebp + XFS.file_offset + 0], ebx
mov dword[ebp + XFS.file_offset + 4], esi
mov [ebp + XFS.buffer_pos], edi
 
mov eax, dword[edx + xfs_inode.di_core.di_size + 4] ; lo
bswap eax
mov dword[ebp + XFS.bytes_left_in_file + 0], eax ; lo
mov eax, dword[edx + xfs_inode.di_core.di_size + 0] ; hi
bswap eax
mov dword[ebp + XFS.bytes_left_in_file + 4], eax ; hi
 
mov eax, [edx + xfs_inode.di_core.di_nextents]
bswap eax
mov [ebp + XFS.left_extents], eax
 
mov dword[ebp + XFS.bytes_read], 0 ; actually read bytes
 
push ebx ecx edx esi edi
mov [ebp + XFS.eof], 0
mov eax, dword[ebp + XFS.file_offset + 0]
mov edx, dword[ebp + XFS.file_offset + 4]
add eax, [ebp + XFS.bytes_to_read]
adc edx, 0
sub eax, dword[ebp + XFS.bytes_left_in_file + 0]
sbb edx, dword[ebp + XFS.bytes_left_in_file + 4]
jc @f ; file_offset + bytes_to_read < file_size
jz @f ; file_offset + bytes_to_read = file_size
mov [ebp + XFS.eof], 1
cmp edx, 0
jne .error.eof
sub dword[ebp + XFS.bytes_to_read], eax
jc .error.eof
jz .error.eof
@@:
stdcall xfs_btree_read, 0, 0, 1
pop edi esi edx ecx ebx
test eax, eax
jnz .error
cmp [ebp + XFS.eof], 1
jne .quit
jmp .error.eof
 
 
.quit:
call xfs_unlock
pop edi esi edx ecx ebx
xor eax, eax
mov ebx, [ebp + XFS.bytes_read]
;DEBUGF 1,"quit: %d\n\n",ebx
ret
.error.eof:
movi eax, ERROR_END_OF_FILE
.error:
;DEBUGF 1,"error\n\n"
call xfs_unlock
pop edi esi edx ecx ebx
mov ebx, [ebp + XFS.bytes_read]
ret
 
 
;----------------------------------------------------------------
; push max_offset_hi
; push max_offset_lo
; push nextents
; push block_number_hi
; push block_number_lo
; push extent_list
; -1 / read block number
;----------------------------------------------------------------
xfs_extent_list_read_dirblock: ; skips holes
;DEBUGF 1,"xfs_extent_list_read_dirblock\n"
push ebx esi edi
;mov eax, [esp+28]
;DEBUGF 1,"nextents: %d\n",eax
;mov eax, [esp+20]
;mov edx, [esp+24]
;DEBUGF 1,"block_number: 0x%x%x\n",edx,eax
;mov eax, [esp+32]
;mov edx, [esp+36]
;DEBUGF 1,"max_addr : 0x%x%x\n",edx,eax
mov ebx, [esp + 16]
mov esi, [esp + 20]
mov edi, [esp + 24]
; mov ecx, [esp + 28] ; nextents
.next_extent:
;DEBUGF 1,"next_extent\n"
dec dword[esp + 28]
js .error
stdcall xfs_extent_unpack, ebx
add ebx, sizeof.xfs_bmbt_rec ; next extent
mov edx, dword[ebp + XFS.extent.br_startoff + 4]
mov eax, dword[ebp + XFS.extent.br_startoff + 0]
cmp edx, [esp + 36] ; max_offset_hi
ja .error
jb @f
cmp eax, [esp + 32] ; max_offset_lo
jae .error
@@:
cmp edi, edx
jb .hole
ja .check_count
cmp esi, eax
jb .hole
ja .check_count
jmp .read_block
.hole:
;DEBUGF 1,"hole\n"
mov esi, eax
mov edi, edx
jmp .read_block
.check_count:
;DEBUGF 1,"check_count\n"
add eax, [ebp + XFS.extent.br_blockcount]
adc edx, 0
cmp edi, edx
ja .next_extent
jb .read_block
cmp esi, eax
jae .next_extent
; jmp .read_block
.read_block:
;DEBUGF 1,"read_block\n"
push esi edi
sub esi, dword[ebp + XFS.extent.br_startoff + 0]
sbb edi, dword[ebp + XFS.extent.br_startoff + 4]
add esi, dword[ebp + XFS.extent.br_startblock + 0]
adc edi, dword[ebp + XFS.extent.br_startblock + 4]
stdcall xfs_read_dirblock, esi, edi, [ebp + XFS.cur_dirblock]
pop edx eax
.quit:
;DEBUGF 1,"xfs_extent_list_read_dirblock: quit\n"
pop edi esi ebx
ret 24
.error:
;DEBUGF 1,"xfs_extent_list_read_dirblock: error\n"
xor eax, eax
dec eax
mov edx, eax
pop edi esi ebx
ret 24
 
 
;----------------------------------------------------------------
; push dirblock_num
; push nextents
; push extent_list
;----------------------------------------------------------------
xfs_dir2_node_get_numfiles:
 
; unfortunately, we need to set 'total entries' field
; this often requires additional effort, since there is no such a number in most directory ondisk formats
 
;DEBUGF 1,"xfs_dir2_node_get_numfiles\n"
push ebx ecx edx esi edi
 
mov eax, [esp + 24]
mov edx, [esp + 28]
mov esi, [esp + 32]
stdcall xfs_extent_list_read_dirblock, eax, esi, 0, edx, -1, -1
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
movi eax, ERROR_FS_FAIL
jmp .error
@@:
mov ebx, [ebp + XFS.cur_dirblock]
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC
je .node
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC
je .leaf
mov eax, ERROR_FS_FAIL
jmp .error
 
.node:
;DEBUGF 1,".node\n"
mov edi, [ebx + xfs_da_intnode.hdr.info.forw]
bswap edi
mov eax, [esp + 24]
mov edx, [esp + 28]
mov esi, [ebx + xfs_da_intnode.btree.before]
bswap esi
stdcall xfs_dir2_node_get_numfiles, eax, edx, esi
test eax, eax
jnz .error
jmp .common
.leaf:
;DEBUGF 1,".leaf\n"
movzx ecx, word[ebx + xfs_dir2_leaf.hdr.count]
xchg cl, ch
movzx eax, word[ebx + xfs_dir2_leaf.hdr.stale]
xchg al, ah
sub ecx, eax
add [ebp + XFS.entries_read], ecx
mov edi, [ebx + xfs_dir2_leaf.hdr.info.forw]
bswap edi
jmp .common
 
.common:
test edi, edi
jz .quit
mov esi, edi
mov eax, [esp + 24]
mov edx, [esp + 28]
stdcall xfs_dir2_node_get_numfiles, eax, edx, esi
test eax, eax
jnz .error
jmp .quit
 
.quit:
;DEBUGF 1,".quit\n"
pop edi esi edx ecx ebx
xor eax, eax
ret 12
.error:
;DEBUGF 1,".error\n"
pop edi esi edx ecx ebx
movi eax, ERROR_FS_FAIL
ret 12
 
 
;----------------------------------------------------------------
; push hash
; push dirblock_num
; push nextents
; push extent_list
;----------------------------------------------------------------
xfs_dir2_lookupdir_node:
DEBUGF 1,"xfs_dir2_lookupdir_node\n"
push ebx edx esi edi
 
mov eax, [esp + 20]
mov edx, [esp + 24]
mov esi, [esp + 28]
DEBUGF 1,"read dirblock: 0x%x %d\n",esi,esi
stdcall xfs_extent_list_read_dirblock, eax, esi, 0, edx, -1, -1
DEBUGF 1,"dirblock read: 0x%x%x\n",edx,eax
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
movi eax, ERROR_FS_FAIL
jmp .error
@@:
DEBUGF 1,"checkpoint #1\n"
mov ebx, [ebp + XFS.cur_dirblock]
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC
je .node
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC
je .leaf
mov eax, ERROR_FS_FAIL
DEBUGF 1,"checkpoint #2\n"
jmp .error
 
.node:
DEBUGF 1,".node\n"
mov edi, [esp + 32] ; hash
movzx ecx, word[ebx + xfs_da_intnode.hdr.count]
xchg cl, ch
mov [ebp + XFS.left_leaves], ecx
xor ecx, ecx
.node.next_leaf:
mov esi, [ebx + xfs_da_intnode.btree + ecx*sizeof.xfs_da_node_entry + xfs_da_node_entry.hashval]
bswap esi
cmp edi, esi
jbe .node.leaf_found
inc ecx
cmp ecx, [ebp + XFS.left_leaves]
jne .node.next_leaf
mov eax, ERROR_FILE_NOT_FOUND
jmp .error
@@:
.node.leaf_found:
mov eax, [esp + 20]
mov edx, [esp + 24]
mov esi, [ebx + xfs_da_intnode.btree + ecx*sizeof.xfs_da_node_entry + xfs_da_node_entry.before]
bswap esi
stdcall xfs_dir2_lookupdir_node, eax, edx, esi, edi
test eax, eax
jz .quit
movi eax, ERROR_FILE_NOT_FOUND
jmp .error
 
.leaf:
DEBUGF 1,".leaf\n"
movzx ecx, [ebx + xfs_dir2_leaf.hdr.count]
xchg cl, ch
lea esi, [ebx + xfs_dir2_leaf.ents]
mov eax, [esp + 32]
stdcall xfs_get_addr_by_hash, esi, ecx
cmp eax, -1
je .error
mov ecx, eax
jmp .quit
 
.quit:
DEBUGF 1,".quit\n"
pop edi esi edx ebx
xor eax, eax
ret 16
.error:
DEBUGF 1,".error\n"
pop edi esi edx ebx
ret 16
 
 
;----------------------------------------------------------------
; push dirblock_num
; push nextents
; push extent_list
;----------------------------------------------------------------
xfs_dir2_btree_get_numfiles:
;DEBUGF 1,"xfs_dir2_node_get_numfiles\n"
push ebx ecx edx esi edi
 
mov eax, [esp + 24]
mov edx, [esp + 28]
mov esi, [esp + 32]
stdcall xfs_extent_list_read_dirblock, eax, esi, 0, edx, -1, -1
mov ecx, eax
and ecx, edx
inc ecx
jnz @f
movi eax, ERROR_FS_FAIL
jmp .error
@@:
mov ebx, [ebp + XFS.cur_dirblock]
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DA_NODE_MAGIC
je .node
cmp word[ebx + xfs_da_intnode.hdr.info.magic], XFS_DIR2_LEAFN_MAGIC
je .leaf
mov eax, ERROR_FS_FAIL
jmp .error
 
.node:
;DEBUGF 1,".node\n"
mov edi, [ebx + xfs_da_intnode.hdr.info.forw]
bswap edi
mov eax, [esp + 24]
mov edx, [esp + 28]
mov esi, [ebx + xfs_da_intnode.btree.before]
bswap esi
stdcall xfs_dir2_node_get_numfiles, eax, edx, esi
test eax, eax
jnz .error
jmp .common
.leaf:
;DEBUGF 1,".leaf\n"
movzx ecx, word[ebx + xfs_dir2_leaf.hdr.count]
xchg cl, ch
movzx eax, word[ebx + xfs_dir2_leaf.hdr.stale]
xchg al, ah
sub ecx, eax
add [ebp + XFS.entries_read], ecx
mov edi, [ebx + xfs_dir2_leaf.hdr.info.forw]
bswap edi
jmp .common
 
.common:
test edi, edi
jz .quit
mov esi, edi
mov eax, [esp + 24]
mov edx, [esp + 28]
stdcall xfs_dir2_node_get_numfiles, eax, edx, esi
test eax, eax
jnz .error
jmp .quit
 
.quit:
;DEBUGF 1,".quit\n"
pop edi esi edx ecx ebx
xor eax, eax
ret 12
.error:
;DEBUGF 1,".error\n"
pop edi esi edx ecx ebx
movi eax, ERROR_FS_FAIL
ret 12
 
 
;----------------------------------------------------------------
; push is_root
; push block_hi
; push block_lo
;----------------------------------------------------------------
xfs_btree_read:
push ebx ecx edx esi edi
cmp dword[esp + 32], 1 ; is root?
je .root
jmp .not_root
.root:
DEBUGF 1,".root\n"
mov ebx, [ebp + XFS.cur_inode_save]
add ebx, xfs_inode.di_u
movzx edx, [ebx + xfs_bmdr_block.bb_numrecs]
xchg dl, dh
dec edx
add ebx, sizeof.xfs_bmdr_block
xor eax, eax
dec eax
.root.next_key:
DEBUGF 1,".root.next_key\n"
cmp [ebp + XFS.bytes_to_read], 0
je .quit
inc eax
cmp eax, edx ; out of keys?
ja .root.key_found ; there is no length field, so try the last key
lea edi, [ebx + sizeof.xfs_bmbt_key*eax + 0]
lea esi, [ebx + sizeof.xfs_bmbt_key*eax + 4]
bswap edi
bswap esi
mov ecx, [ebp + XFS.blocklog]
shld edi, esi, cl
shl esi, cl
cmp edi, dword[ebp + XFS.file_offset + 4]
ja .root.prev_or_hole
jb .root.next_key
cmp esi, dword[ebp + XFS.file_offset + 0]
ja .root.prev_or_hole
jb .root.next_key
jmp .root.key_found
.root.prev_or_hole:
DEBUGF 1,".root.prev_or_hole\n"
test eax, eax
jz .root.hole
dec eax
jmp .root.key_found
.root.hole:
DEBUGF 1,".root.hole\n"
push eax edx esi edi
mov ecx, [ebp + XFS.blocklog]
shld edi, esi, cl
shl esi, cl
sub esi, dword[ebp + XFS.file_offset + 0]
sbb edi, dword[ebp + XFS.file_offset + 4]
mov ecx, [ebp + XFS.bytes_to_read]
cmp edi, 0 ; hole size >= 2^32
jne @f
cmp ecx, esi
jbe @f
mov ecx, esi
@@:
add dword[ebp + XFS.file_offset + 0], ecx
adc dword[ebp + XFS.file_offset + 4], 0
sub [ebp + XFS.bytes_to_read], ecx
xor eax, eax
mov edi, [ebp + XFS.buffer_pos]
rep stosb
mov [ebp + XFS.buffer_pos], edi
pop edi esi edx eax
jmp .root.next_key
.root.key_found:
DEBUGF 1,".root.key_found\n"
mov edx, [ebp + XFS.cur_inode_save]
mov eax, [ebp + XFS.inodesize]
sub eax, xfs_inode.di_u
cmp [edx + xfs_inode.di_core.di_forkoff], 0
je @f
movzx eax, [edx + xfs_inode.di_core.di_forkoff]
shl eax, XFS_DIR2_DATA_ALIGN_LOG ; 3
@@:
sub eax, sizeof.xfs_bmdr_block
shr eax, 4 ;log2(sizeof.xfs_bmbt_key + sizeof.xfs_bmdr_ptr)
mov edx, [ebx + sizeof.xfs_bmbt_key*eax + 0] ; hi
mov eax, [ebx + sizeof.xfs_bmbt_key*eax + 4] ; hi
bswap edx
bswap eax
stdcall xfs_btree_read, eax, edx, 0
test eax, eax
jnz .error
jmp .root.next_key
 
.not_root:
DEBUGF 1,".root.not_root\n"
mov eax, [esp + 24] ; block_lo
mov edx, [esp + 28] ; block_hi
mov ebx, [ebp + XFS.cur_block]
stdcall xfs_read_block
test eax, eax
jnz .error
mov ebx, [ebp + XFS.cur_block]
 
cmp [ebx + xfs_bmbt_block.bb_magic], XFS_BMAP_MAGIC
jne .error
cmp [ebx + xfs_bmbt_block.bb_level], 0 ; leaf?
je .leaf
jmp .node
 
.node:
; mov eax, [ebp + XFS.blocksize]
; sub eax, sizeof.xfs_bmbt_block
; shr eax, 4 ; maxnumrecs
mov eax, dword[ebp + XFS.file_offset + 0] ; lo
mov edx, dword[ebp + XFS.file_offset + 4] ; hi
movzx edx, [ebx + xfs_bmbt_block.bb_numrecs]
xchg dl, dh
dec edx
add ebx, sizeof.xfs_bmbt_block
xor eax, eax
dec eax
.node.next_key:
push eax ecx edx esi edi
mov eax, [esp + 44] ; block_lo
mov edx, [esp + 48] ; block_hi
mov ebx, [ebp + XFS.cur_block]
stdcall xfs_read_block
test eax, eax
jnz .error
mov ebx, [ebp + XFS.cur_block]
add ebx, sizeof.xfs_bmbt_block
pop edi esi edx ecx eax
cmp [ebp + XFS.bytes_to_read], 0
je .quit
inc eax
cmp eax, edx ; out of keys?
ja .node.key_found ; there is no length field, so try the last key
lea edi, [ebx + sizeof.xfs_bmbt_key*eax + 0]
lea esi, [ebx + sizeof.xfs_bmbt_key*eax + 4]
bswap edi
bswap esi
mov ecx, [ebp + XFS.blocklog]
shld edi, esi, cl
shl esi, cl
cmp edi, dword[ebp + XFS.file_offset + 4]
ja .node.prev_or_hole
jb .node.next_key
cmp esi, dword[ebp + XFS.file_offset + 0]
ja .node.prev_or_hole
jb .node.next_key
jmp .node.key_found
.node.prev_or_hole:
test eax, eax
jz .node.hole
dec eax
jmp .node.key_found
.node.hole:
push eax edx esi edi
mov ecx, [ebp + XFS.blocklog]
shld edi, esi, cl
shl esi, cl
sub esi, dword[ebp + XFS.file_offset + 0]
sbb edi, dword[ebp + XFS.file_offset + 4]
mov ecx, [ebp + XFS.bytes_to_read]
cmp edi, 0 ; hole size >= 2^32
jne @f
cmp ecx, esi
jbe @f
mov ecx, esi
@@:
add dword[ebp + XFS.file_offset + 0], ecx
adc dword[ebp + XFS.file_offset + 4], 0
sub [ebp + XFS.bytes_to_read], ecx
xor eax, eax
mov edi, [ebp + XFS.buffer_pos]
rep stosb
mov [ebp + XFS.buffer_pos], edi
pop edi esi edx eax
jmp .node.next_key
.node.key_found:
mov edx, [ebp + XFS.cur_inode_save]
mov eax, [ebp + XFS.inodesize]
sub eax, xfs_inode.di_u
cmp [edx + xfs_inode.di_core.di_forkoff], 0
je @f
movzx eax, [edx + xfs_inode.di_core.di_forkoff]
shl eax, XFS_DIR2_DATA_ALIGN_LOG ; 3
@@:
sub eax, sizeof.xfs_bmdr_block
shr eax, 4 ;log2(sizeof.xfs_bmbt_key + sizeof.xfs_bmdr_ptr)
mov edx, [ebx + sizeof.xfs_bmbt_key*eax + 0] ; hi
mov eax, [ebx + sizeof.xfs_bmbt_key*eax + 4] ; hi
bswap edx
bswap eax
stdcall xfs_btree_read, eax, edx, 0
test eax, eax
jnz .error
jmp .node.next_key
jmp .quit
 
.leaf:
jmp .quit
 
.error:
pop edi esi edx ecx ebx
movi eax, ERROR_FS_FAIL
ret 4
.quit:
pop edi esi edx ecx ebx
xor eax, eax
ret 4
 
 
;----------------------------------------------------------------
; push nextents
; push extent_list
; push file_offset_hi
; push file_offset_lo
;----------------------------------------------------------------
;xfs_extent_list_read:
; push ebx 0 edx esi edi ; zero means actually_read_bytes
;
; .quit:
; pop edi esi edx ecx ebx
; xor eax, eax
; ret 24
; .error:
; pop edi esi edx ecx ebx
; ret 24
/kernel/branches/kolibri-process/fs/xfs.inc
0,0 → 1,518
; from stat.h
; distinguish file types
S_IFMT = 0170000o ; These bits determine file type.
S_IFDIR = 0040000o ; Directory.
S_IFCHR = 0020000o ; Character device.
S_IFBLK = 0060000o ; Block device.
S_IFREG = 0100000o ; Regular file.
S_IFIFO = 0010000o ; FIFO.
S_IFLNK = 0120000o ; Symbolic link.
S_IFSOCK = 0140000o ; Socket.
; end stat.h
 
 
; XFS null constant: empty fields must be all ones, not zeros!
XFS_NULL = -1
 
 
; static sector numbers
XFS_SECT_SB = 0
XFS_SECT_AGF = 1
XFS_SECT_AGI = 2
XFS_SECT_AGFL = 3
 
 
; signatures of file system structures
; 'string' numbers are treated by fasm as big endian
XFS_SB_MAGIC = 'XFSB'
XFS_AGF_MAGIC = 'XAGF'
XFS_AGI_MAGIC = 'XAGI'
XFS_ABTB_MAGIC = 'ABTB'
XFS_ABTC_MAGIC = 'ABTC'
XFS_IBT_MAGIC = 'IABT'
XFS_DINODE_MAGIC = 'IN'
XFS_BMAP_MAGIC = 'BMAP'
XFS_DA_NODE_MAGIC = 0xbefe ; those are little endian here
XFS_ATTR_LEAF_MAGIC = 0xeefb ; but big endian in docs
XFS_DIR2_LEAF1_MAGIC = 0xf1d2 ; pay attention!
XFS_DIR2_LEAFN_MAGIC = 0xffd2 ;
XFS_DIR2_BLOCK_MAGIC = 'XD2B'
XFS_DIR2_DATA_MAGIC = 'XD2D'
XFS_DIR2_FREE_MAGIC = 'XD2F'
XFS_DQUOT_MAGIC = 'DQ'
 
 
; bitfield lengths for packed extent
; MSB to LSB / left to right
BMBT_EXNTFLAG_BITLEN = 1
BMBT_STARTOFF_BITLEN = 54
BMBT_STARTBLOCK_BITLEN = 52
BMBT_BLOCKCOUNT_BITLEN = 21
 
 
; those constants are taken from linux source (xfs_dir2_leaf.h)
; they are magic infile offsets for directories
XFS_DIR2_DATA_ALIGN_LOG = 3 ; i.e., 8 bytes
XFS_DIR2_LEAF_SPACE = 1
XFS_DIR2_SPACE_SIZE = (1 SHL (32 + XFS_DIR2_DATA_ALIGN_LOG))
XFS_DIR2_LEAF_OFFSET = (XFS_DIR2_LEAF_SPACE * XFS_DIR2_SPACE_SIZE)
XFS_DIR2_FREE_SPACE = 2
XFS_DIR2_SPACE_SIZE = (1 SHL (32 + XFS_DIR2_DATA_ALIGN_LOG))
XFS_DIR2_FREE_OFFSET = (XFS_DIR2_FREE_SPACE * XFS_DIR2_SPACE_SIZE)
 
 
; data section magic constants for directories (xfs_dir2_data.h)
XFS_DIR2_DATA_FD_COUNT = 3
XFS_DIR2_DATA_FREE_TAG = 0xffff
 
 
; valid inode formats
; enum xfs_dinode_fmt (xfs_dinode.h)
XFS_DINODE_FMT_DEV = 0 ; xfs_dev_t
XFS_DINODE_FMT_LOCAL = 1 ; one inode is enough (shortdir)
XFS_DINODE_FMT_EXTENTS = 2 ; one or more extents (leafdir, nodedir, regular files)
XFS_DINODE_FMT_BTREE = 3 ; highly fragmented files or really huge directories
XFS_DINODE_FMT_UUID = 4 ; uuid_t
 
 
; size of the unlinked inode hash table in the agi
XFS_AGI_UNLINKED_BUCKETS = 64
 
 
; possible extent states
; enum xfs_exntst_t (xfs_bmap_btree.h)
XFS_EXT_NORM = 0
XFS_EXT_UNWRITTEN = 1
XFS_EXT_DMAPI_OFFLINE = 2
XFS_EXT_INVALID = 3
 
 
; values for inode core flags / di_flags (xfs_dinode.h)
XFS_DIFLAG_REALTIME_BIT = 0 ; file's blocks come from rt area
XFS_DIFLAG_PREALLOC_BIT = 1 ; file space has been preallocated
XFS_DIFLAG_NEWRTBM_BIT = 2 ; for rtbitmap inode, new format
XFS_DIFLAG_IMMUTABLE_BIT = 3 ; inode is immutable
XFS_DIFLAG_APPEND_BIT = 4 ; inode is append-only
XFS_DIFLAG_SYNC_BIT = 5 ; inode is written synchronously
XFS_DIFLAG_NOATIME_BIT = 6 ; do not update atime
XFS_DIFLAG_NODUMP_BIT = 7 ; do not dump
XFS_DIFLAG_RTINHERIT_BIT = 8 ; create with realtime bit set
XFS_DIFLAG_PROJINHERIT_BIT = 9 ; create with parents projid
XFS_DIFLAG_NOSYMLINKS_BIT = 10 ; disallow symlink creation
XFS_DIFLAG_EXTSIZE_BIT = 11 ; inode extent size allocator hint
XFS_DIFLAG_EXTSZINHERIT_BIT = 12 ; inherit inode extent size
XFS_DIFLAG_NODEFRAG_BIT = 13 ; do not reorganize/defragment
XFS_DIFLAG_FILESTREAM_BIT = 14 ; use filestream allocator
XFS_DIFLAG_REALTIME = (1 SHL XFS_DIFLAG_REALTIME_BIT)
XFS_DIFLAG_PREALLOC = (1 SHL XFS_DIFLAG_PREALLOC_BIT)
XFS_DIFLAG_NEWRTBM = (1 SHL XFS_DIFLAG_NEWRTBM_BIT)
XFS_DIFLAG_IMMUTABLE = (1 SHL XFS_DIFLAG_IMMUTABLE_BIT)
XFS_DIFLAG_APPEND = (1 SHL XFS_DIFLAG_APPEND_BIT)
XFS_DIFLAG_SYNC = (1 SHL XFS_DIFLAG_SYNC_BIT)
XFS_DIFLAG_NOATIME = (1 SHL XFS_DIFLAG_NOATIME_BIT)
XFS_DIFLAG_NODUMP = (1 SHL XFS_DIFLAG_NODUMP_BIT)
XFS_DIFLAG_RTINHERIT = (1 SHL XFS_DIFLAG_RTINHERIT_BIT)
XFS_DIFLAG_PROJINHERIT = (1 SHL XFS_DIFLAG_PROJINHERIT_BIT)
XFS_DIFLAG_NOSYMLINKS = (1 SHL XFS_DIFLAG_NOSYMLINKS_BIT)
XFS_DIFLAG_EXTSIZE = (1 SHL XFS_DIFLAG_EXTSIZE_BIT)
XFS_DIFLAG_EXTSZINHERIT = (1 SHL XFS_DIFLAG_EXTSZINHERIT_BIT)
XFS_DIFLAG_NODEFRAG = (1 SHL XFS_DIFLAG_NODEFRAG_BIT)
XFS_DIFLAG_FILESTREAM = (1 SHL XFS_DIFLAG_FILESTREAM_BIT)
 
 
; superblock _ondisk_ structure (xfs_sb.h)
; this is _not_ the partition structure
; for XFS partition structure see XFS below
struct xfs_sb
sb_magicnum dd ? ; signature, must be XFS_SB_MAGIC
sb_blocksize dd ? ; block is the minimal file system unit, in bytes
sb_dblocks dq ? ; number of data blocks
sb_rblocks dq ? ; number of realtime blocks (not supported yet!)
sb_rextents dq ? ; number of realtime extents (not supported yet!)
sb_uuid rb 16 ; file system unique identifier
sb_logstart dq ? ; starting block of log (for internal journal; journals on separate devices are not supported!)
sb_rootino dq ? ; root inode number
sb_rbmino dq ? ; bitmap inode for realtime extents (ignored)
sb_rsumino dq ? ; summary inode for rt bitmap (ignored)
sb_rextsize dd ? ; realtime extent size, blocks
sb_agblocks dd ? ; size of an allocation group (the last one may be smaller!)
sb_agcount dd ? ; number of allocation groups
sb_rbmblocks dd ? ; number of rt bitmap blocks
sb_logblocks dd ? ; number of log blocks
sb_versionnum dw ? ; header version == XFS_SB_VERSION
sb_sectsize dw ? ; volume sector size in bytes (only 512B sectors are supported)
sb_inodesize dw ? ; inode size, bytes
sb_inopblock dw ? ; inodes per block
sb_fname rb 12 ; inodes per block (aka label)
sb_blocklog db ? ; log2 of sb_blocksize
sb_sectlog db ? ; log2 of sb_blocksize
sb_inodelog db ? ; log2 of sb_inodesize
sb_inopblog db ? ; log2 of sb_inopblock
sb_agblklog db ? ; log2 of sb_agblocks (rounded up!)
sb_rextslog db ? ; log2 of sb_rextents
sb_inprogress db ? ; mkfs is in progress, don't mount
sb_imax_pct db ? ; max % of fs for inode space
; statistics
sb_icount dq ? ; allocated inodes
sb_ifree dq ? ; free inodes
sb_fdblocks dq ? ; free data blocks
sb_frextents dq ? ; free realtime extents
 
sb_uquotino dq ? ; user quota inode
sb_gquotino dq ? ; group quota inode
sb_qflags dw ? ; quota flags
sb_flags db ? ; misc. flags
sb_shared_vn db ? ; shared version number
sb_inoalignmt dd ? ; inode chunk alignment, fsblocks
sb_unit dd ? ; stripe or raid unit
sb_width dd ? ; stripe or raid width
sb_dirblklog db ? ; log2 of dir block size (fsbs)
sb_logsectlog db ? ; log2 of the log sector size
sb_logsectsize dw ? ; sector size for the log, bytes
sb_logsunit dd ? ; stripe unit size for the log
sb_features2 dd ? ; additional feature bits
ends
 
 
; allocation group inode (xfs_ag.h)
struct xfs_agi
agi_magicnum dd ? ; magic number == XFS_AGI_MAGIC
agi_versionnum dd ? ; header version == XFS_AGI_VERSION
agi_seqno dd ? ; sequence number starting from 0
agi_length dd ? ; size in blocks of a.g.
agi_count dd ? ; count of allocated inodes
agi_root dd ? ; root of inode btree
agi_level dd ? ; levels in inode btree
agi_freecount dd ? ; number of free inodes
agi_newino dd ? ; new inode just allocated
agi_dirino dd ? ; last directory inode chunk
agi_unlinked rd XFS_AGI_UNLINKED_BUCKETS ; Hash table of inodes which have been unlinked but are still being referenced
ends
 
 
; superblock structure of b+tree node/leaf (same structure, bb_level matters)
struct xfs_btree_sblock
bb_magic dd ?
bb_level dw ? ; distinguishes nodeds and leaves
bb_numrecs dw ?
bb_leftsib dd ?
bb_rightsib dd ?
ends
 
 
; record of b+tree inode
struct xfs_inobt_rec
ir_startino dd ?
ir_freecount dd ?
ir_free dq ?
ends
 
 
; structure to store create, access and modification time in inode core
struct xfs_timestamp
t_sec dd ?
t_nsec dd ? ; nanoseconds
ends
 
 
; inode core structure: basic information about file
struct xfs_dinode_core
di_magic dw ? ; inode magic = XFS_DINODE_MAGIC
di_mode dw ? ; mode and type of file
di_version db ? ; inode version
di_format db ? ; format of di_c data
di_onlink dw ? ; old number of links to file
di_uid dd ? ; owner's user id
di_gid dd ? ; owner's group id
di_nlink dd ? ; number of links to file
di_projid dw ? ; owner's project id
di_pad rb 8 ; unused, zeroed space
di_flushiter dw ? ; incremented on flush
di_atime xfs_timestamp ; time last accessed
di_mtime xfs_timestamp ; time last modified
di_ctime xfs_timestamp ; time created/inode modified
di_size dq ? ; number of bytes in file
di_nblocks dq ? ; number of direct & btree blocks used
di_extsize dd ? ; basic/minimum extent size for file
di_nextents dd ? ; number of extents in data fork
di_anextents dw ? ; number of extents in attribute fork
di_forkoff db ? ; attr fork offs, <<3 for 64b align
di_aformat db ? ; format of attr fork's data
di_dmevmask dd ? ; DMIG event mask
di_dmstate dw ? ; DMIG state info
di_flags dw ? ; random flags, XFS_DIFLAG_...
di_gen dd ? ; generation number
ends
 
 
; shortform dir header
struct xfs_dir2_sf_hdr
count db ? ; the number of directory entries, used only if each inode number fits 4 bytes; zero otherwise
i8count db ? ; the number of directory entries, used only when count is zero
parent dq ? ; parent inode number: xfs_dir2_inou_t (4 or 8 bytes)
ends
 
 
; shortform dir entry
struct xfs_dir2_sf_entry
namelen db ? ; actual name length (ASCII)
offset rb 2 ; saved offset
name db ? ; name, variable size
; inumber dq ? ; xfs_dir2_inou_t
ends
 
 
; active entry in a data block
; aligned to 8 bytes
; tag appears as the last 2 bytes
struct xfs_dir2_data_entry
inumber dq ? ; inode number
namelen db ? ; name length
name db ? ; name bytes, no null
; tag dw ? ; starting offset of us
ends
 
 
; unused entry in a data block
; aligned to 8 bytes
; tag appears as the last 2 bytes
struct xfs_dir2_data_unused
freetag dw ? ; XFS_DIR2_DATA_FREE_TAG
length dw ? ; total free length
; tag dw ? ; starting offset of us
ends
 
 
; generic data entry
struct xfs_dir2_data_union
union
xentry xfs_dir2_data_entry
unused xfs_dir2_data_unused
ends
ends
 
 
; describe a free area in the data block
; the freespace will be formatted as a xfs_dir2_data_unused_t
struct xfs_dir2_data_free
offset dw ? ; start of freespace
length dw ? ; length of freespace
ends
 
 
; header for the data blocks
; always at the beginning of a directory-sized block
; the code knows that XFS_DIR2_DATA_FD_COUNT is 3
struct xfs_dir2_data_hdr
magic dd ? ; XFS_DIR2_DATA_MAGIC or XFS_DIR2_BLOCK_MAGIC
bestfree xfs_dir2_data_free
bestfree2 xfs_dir2_data_free
bestfree3 xfs_dir2_data_free
ends
 
 
; leaf block entry
struct xfs_dir2_leaf_entry
hashval dd ? ; hash value of name
address dd ? ; address of data entry
ends
 
 
; the tail of directory block
struct xfs_dir2_block_tail
count dd ? ; count of leaf entries
stale dd ? ; count of stale leaf entries
ends
 
 
; generic single-block structure, for xfs_db
struct xfs_dir2_block
hdr xfs_dir2_data_hdr
u xfs_dir2_data_union
; leaf xfs_dir2_leaf_entry
; tail xfs_dir2_block_tail
ends
 
 
;
struct xfs_dir2_data
hdr xfs_dir2_data_hdr ; magic XFS_DIR2_DATA_MAGIC
u xfs_dir2_data_union
ends
 
 
;
struct xfs_da_blkinfo
forw dd ? ; previous block in list
back dd ? ; following block in list
magic dw ? ; validity check on block
pad dw ? ; unused
ends
 
 
; leaf block header
struct xfs_dir2_leaf_hdr
info xfs_da_blkinfo ; header for da routines
count dw ? ; count of entries
stale dw ? ; count of stale entries
ends
 
 
; leaf block tail
struct xfs_dir2_leaf_tail
bestcount dd ?
ends
 
 
; leaf block
; bests and tail are at the end of the block for single-leaf only
; (magic = XFS_DIR2_LEAF1_MAGIC not XFS_DIR2_LEAFN_MAGIC)
struct xfs_dir2_leaf
hdr xfs_dir2_leaf_hdr ; leaf header
ents xfs_dir2_leaf_entry ; entries
; bests dw ? ; best free counts
; tail xfs_dir2_leaf_tail ; leaf tail
ends
 
 
; header of 'free' block part
struct xfs_dir2_free_hdr
magic dd ? ; XFS_DIR2_FREE_MAGIC
firstdb dd ? ; db of first entry
nvalid dd ? ; count of valid entries
nused dd ? ; count of used entries
ends
 
 
; 'free' part of directiry block
struct xfs_dir2_free
hdr xfs_dir2_free_hdr ; block header
bests dw ? ; best free counts
; unused entries are -1 (XFS_NULL)
ends
 
 
; b+tree node header
struct xfs_da_node_hdr
info xfs_da_blkinfo
count dw ?
level dw ?
ends
 
 
; b+tree node
struct xfs_da_node_entry
hashval dd ? ; hash value for this descendant
before dd ? ; Btree block before this key
ends
 
 
;
struct xfs_da_intnode
hdr xfs_da_node_hdr
btree xfs_da_node_entry
ends
 
 
; packet extent
struct xfs_bmbt_rec
l0 dq ?
l1 dq ?
ends
 
 
; unpacked extent
struct xfs_bmbt_irec
br_startoff dq ? ; starting file offset
br_startblock dq ? ; starting block number
br_blockcount dd ? ; number of blocks
br_state dd ? ; extent state
ends
 
 
; bmap root header, on-disk form only
struct xfs_bmdr_block
bb_level dw ? ; 0 is a leaf
bb_numrecs dw ? ; current number of data records
ends
 
 
; key structure for non-leaf levels of the tree
struct xfs_bmbt_key
br_startoff dq ? ; starting file offset
ends
 
 
sizeof.xfs_bmbt_ptr = 8 ; workaround
sizeof.xfs_bmdr_ptr = 8 ; workaround
 
 
; long form header: bmap btrees
; xfs_btree_lblock is xfs_bmbt_block (xfs_btree.h)
struct xfs_bmbt_block
bb_magic dd ? ; magic number for block type
bb_level dw ? ; 0 is a leaf
bb_numrecs dw ? ; current number of data records
bb_leftsib dq ? ; left sibling block or NULLDFSBNO
bb_rightsib dq ? ; right sibling block or NULLDFSBNO
ends
 
 
; high level inode structure
struct xfs_inode
di_core xfs_dinode_core ; main info, aka core
di_next_unlinked dd ? ; unlinked but still used inode (if any, XFS_NULL otherwise)
di_u db ? ; data fork inode part
; di_a db ? ; data attribute
ends
 
 
; internal data for every XFS partition
; this _is_ XFS partition structure
; most fields are unpacked or bswap'ed values from the superblock, so see xfs_sb structure above
struct XFS PARTITION
Lock MUTEX ? ; access mutex
blocksize dd ?
sectsize dd ?
dirblocksize dd ?
rootino dq ?
cur_block dd ?
cur_inode dd ?
cur_sect dd ?
cur_dirblock dd ?
tmp_inode dd ?
versionnum dd ?
features2 dd ?
inodesize dd ?
inopblock dd ?
blocklog dd ?
sectlog dd ?
inodelog dd ?
inopblog dd ?
agblklog dd ?
blockmsectlog dd ?
inodetoblocklog dd ?
dirblklog dd ?
sectpblock dd ?
agblocks dd ?
; helpers, temporary vars, etc
agblockmask dq ?
extent xfs_bmbt_irec
left_extents dd ?
left_leaves dd ?
bytes_to_read dd ?
bytes_read dd ?
entries_read dd ?
file_offset dq ?
max_dirblockaddr dd ?
next_block_num dq ?
dir2_leaf_offset_blocks dd ?
dir2_free_offset_blocks dd ?
cur_inode_save dd ?
bytes_left_in_file dq ?
ro_nextents dd ?
bb_ptrs dd ?
maxnumrecs dd ?
buffer_pos dd ?
eof dd ?
ends