/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 |