diff --git a/kernel/trunk/fs/ext2.inc b/kernel/trunk/fs/ext2.inc deleted file mode 100644 index d2d2597b58..0000000000 --- a/kernel/trunk/fs/ext2.inc +++ /dev/null @@ -1,1343 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; 02.02.2010 turbanoff - support 70.5 ;; -;; 23.01.2010 turbanoff - support 70.0 70.1 ;; -;; 21.06.2013 yogev_ezra - Translate Russian comments ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -$Revision$ - -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 - -;RUS: флаги, указываемые в inode файла ;ENG: flags specified in file inode -EXT2_S_IFREG = 0x8000 -EXT2_S_IFDIR = 0x4000 -EXT2_S_IFMT = 0xF000 ;RUS: маска для типа файла ;ENG: mask for file type - -;RUS: флаги, указываемые в linked list родительской папки -;ENG: flags specified in linked list of parent folder -EXT2_FT_REG_FILE = 1 ;RUS: это файл, запись в родительском каталоге - ;ENG: this is a file, record in parent catalog -EXT2_FT_DIR = 2 ;RUS: это папка ;ENG: this is a folder - -;RUS: флаги используемые KolibriOS ;ENG: flags used by KolibriOS -FS_FT_HIDDEN = 2 -FS_FT_DIR = 0x10 ;RUS: это папка ;ENG: this is a folder -FS_FT_ASCII = 0 ;RUS: имя в ascii ;ENG: name in ASCII -FS_FT_UNICODE = 1 ;RUS: имя в unicode ;ENG: name in UNICODE - -EXT2_FEATURE_INCOMPAT_FILETYPE = 0x0002 ;RUS: тип файла должен указываться в директории - ;ENG: file type must be specified in the folder -EXT4_FEATURE_INCOMPAT_EXTENTS = 0x0040 ;RUS: экстенты ;ENG: extents -EXT4_FEATURE_INCOMPAT_FLEX_BG = 0x0200 ;RUS: гибкие группы блоков ;ENG: flexible block groups - -;RUS: реализованные ext[234] features ;ENG: implemented ext[234] features -EXT4_FEATURE_INCOMPAT_SUPP = EXT2_FEATURE_INCOMPAT_FILETYPE \ - or EXT4_FEATURE_INCOMPAT_EXTENTS \ - or EXT4_FEATURE_INCOMPAT_FLEX_BG - -;RUS: флаги, указываемые для inode в i_flags ;ENG: flags specified for inode in i_flags -EXT2_EXTENTS_FL = 0x00080000 - -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 ? ; 1..12 -ends - -struct EXT2_DIR_STRUC - inode dd ? - rec_len dw ? - name_len db ? - file_type db ? - name db ? ; 0..255 -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 - 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 - -struct EXT4_EXTENT_HEADER ;RUS: заголовок блока экстентов/индексов - eh_magic dw ? ;RUS: в текущей реализации ext4 должно быть 0xF30A - eh_entries dw ? ;RUS: количество экстентов/индексов в блоке - eh_max dw ? ;RUS: max количество (используется при записи) - eh_depth dw ? ;RUS: глубина дерева (0, если это блок экстентов) - eh_generation dd ? ;??? -ends - -struct EXT4_EXTENT ;RUS: экстент ;ENG: extent - ee_block dd ? ;RUS: номер ext4 блока ;ENG: number of ext4 block - ee_len dw ? ;RUS: длина экстента ;ENG: extent length - ee_start_hi dw ? ;RUS: старшие 16 бит 48-битного адреса (пока не используются в KOS) - ;ENG: upper 16 bits of the 48-bit address (not used in KolibriOS yet) - ee_start_lo dd ? ;RUS: младшие 32 бита 48-битного адреса - ;ENG: lower 32 bits of the 48-bit address -ends - -struct EXT4_EXTENT_IDX ;RUS: индекс - указатель на блок с экстентами/индексами - ;ENG: index - pointer to block of extents/indexes - ei_block dd ? ;RUS: номер ext4 блока ;ENG: number of ext4 block - ei_leaf_lo dd ? ;RUS: младшие 32 бит 48-битного адреса - ;ENG: lower 32 bits of the 48-bit address - ei_leaf_hi dw ? ;RUS: старшие 16 бит 48-битного адреса (пока не используются в KOS) - ;ENG: upper 16 bits of the 48-bit address (not used in KolibriOS yet) - ei_unused dw ? ;RUS: зарезервировано ;ENG: reserved -ends - -struct EXTFS PARTITION - Lock MUTEX - 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 ? ;RUS: блок на глобальную 1 процедуру ;ENG: block for 1 global procedure - ext2_temp_block dd ? ;RUS: блок для мелких процедур ;ENG: block for small procedures - ext2_save_inode dd ? ;RUS: inode на глобальную процедуру ;ENG: inode for global procedure - ext2_temp_inode dd ? ;RUS: inode для мелких процедур ;ENG: inode for small procedures - groups_count dd ? - superblock rd 512/4 -ends - -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 - -proc ext2_create_partition - push ebx - - mov eax, 2 ;superblock start at 1024b - add ebx, 512 ; get pointer to fs-specific buffer - call fs_read32_sys - - cmp [ebx + EXT2_SB_STRUC.log_block_size], 3 ;0,1,2,3 - ja .no - cmp [ebx + EXT2_SB_STRUC.magic], 0xEF53 - jne .no - cmp [ebx + EXT2_SB_STRUC.state], 1 ;EXT_VALID_FS=1 - jne .no - cmp [ebx + EXT2_SB_STRUC.inodes_per_group], 0 - je .no - - mov eax, [ebx + EXT2_SB_STRUC.feature_incompat] - test eax, EXT2_FEATURE_INCOMPAT_FILETYPE - jz .no - test eax, not EXT4_FEATURE_INCOMPAT_SUPP - jz ext2_setup - - .no: - ; No, this superblock isn't EXT2 - pop ebx - xor eax, eax - ret - - ; OK, this is correct EXT2 superblock -ext2_setup: - movi eax, sizeof.EXTFS - call malloc - test eax, eax - jz ext2_create_partition.no - - 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 - 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 - 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 - - mov esi, ebx - lea edi, [ebp+EXTFS.superblock] - mov ecx, 512/4 - rep movsd ; copy sb to reserved mem - - 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 - - mov ecx, [ebx + EXT2_SB_STRUC.log_block_size] - inc ecx - mov [ebp+EXTFS.log_block_size], ecx ; 1, 2, 3, 4 equ 1kb, 2kb, 4kb, 8kb - - mov eax, 1 - shl eax, cl - mov [ebp+EXTFS.count_block_in_block], eax - - shl eax, 7 - mov [ebp+EXTFS.count_pointer_in_block], eax - mov edx, eax ;RUS: потом еще квадрат найдем ;ENG: we'll find a square later - - shl eax, 2 - mov [ebp+EXTFS.block_size], eax - - push eax eax ; 2 kernel_alloc - - mov eax, edx - mul edx - mov [ebp+EXTFS.count_pointer_in_block_square], eax - - call kernel_alloc - mov [ebp+EXTFS.ext2_save_block], eax ; and for temp block - call kernel_alloc - mov [ebp+EXTFS.ext2_temp_block], eax ; and for get_inode proc - - movzx eax, word [ebx + EXT2_SB_STRUC.inode_size] - mov ecx, [ebx + EXT2_SB_STRUC.blocks_per_group] - - mov [ebp+EXTFS.inode_size], eax - mov [ebp+EXTFS.blocks_per_group], ecx - - push eax eax eax ;3 kernel_alloc - call kernel_alloc - mov [ebp+EXTFS.ext2_save_inode], eax - call kernel_alloc - mov [ebp+EXTFS.ext2_temp_inode], eax - call kernel_alloc - mov [ebp+EXTFS.root_inode], eax - - mov ebx, eax - mov eax, EXT2_ROOT_INO - call ext2_get_inode ; read root inode - - mov eax, ebp ; return pointer to EXTFS - pop edi esi ebp ebx - ret -endp - -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 - -proc ext2_lock - lea ecx, [ebp+EXTFS.Lock] - jmp mutex_lock -endp - -proc ext2_unlock - lea ecx, [ebp+EXTFS.Lock] - jmp mutex_unlock -endp - -;================================================================== -;read ext2 block form FS to memory -;in: eax = i_block (address of block in ext2 terms) -; ebx = pointer to return memory -; ebp = pointer to EXTFS -;out: eax - error code (0 = no_error) -ext2_get_block: - push ebx ecx - mov ecx, [ebp+EXTFS.log_block_size] - shl eax, cl - mov ecx, eax - push [ebp+EXTFS.count_block_in_block] - @@: - mov eax, ecx - call fs_read32_sys - test eax, eax - jnz .fail - inc ecx - add ebx, 512 - dec dword [esp] - jnz @B - pop ecx - xor eax, eax - @@: - pop ecx ebx - ret - .fail: - mov eax, ERROR_DEVICE - jmp @B - - -;=================================================================== -;RUS: получает номер блока из extent inode ;ENG: receives block number from extent inode -;RUS: in: ecx = номер блока по порядку ;ENG: in: ecx = consecutive block number -;RUS: esi = адрес extent header`а ;ENG: esi = address of extent header -;RUS: ebp = указатель на структуру EXTFS ;ENG: ebp = pointer to EXTFS -;RUS: out: ecx - адрес очередного блока в случае успеха ;ENG: out: ecx - address of next block, if successful -;RUS: eax - номер ошибки (если равно 0, то ошибки нет) ;ENG: eax - error number (0 - 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_get_block - 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 - -;=================================================================== -;получает адрес ext2 блока из inode с определнным номером -;RUS: in: ecx = номер блока в inode (0..) ;ENG: in: ecx = number of block in inode (0..) -;RUS: esi = адрес inode ;ENG: esi = inode address -;RUS: ebp = указатель на структуру EXTFS;ENG: ebp = pointer to EXTFS -;RUS: out: ecx = адрес очередного блока ;ENG: out: ecx = next block address -;RUS: eax - error code ;ENG: eax - error code -ext2_get_inode_block: - test [esi + EXT2_INODE_STRUC.i_flags], EXT2_EXTENTS_FL - jz @F - - pushad - add esi, EXT2_INODE_STRUC.i_block ;esi - extent_header - call ext4_block_recursive_search - mov PUSHAD_ECX, ecx - mov PUSHAD_EAX, eax - popad - ret - - @@: - cmp ecx, 12 ; 0..11 - direct block address - jb .get_direct_block - - sub ecx, 12 - cmp ecx, [ebp+EXTFS.count_pointer_in_block] ; 12.. - indirect blocks - jb .get_indirect_block - - sub ecx, [ebp+EXTFS.count_pointer_in_block] - cmp ecx, [ebp+EXTFS.count_pointer_in_block_square] - jb .get_double_indirect_block - - sub ecx, [ebp+EXTFS.count_pointer_in_block_square] - ;triple indirect block - push edx ebx - - mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4] - mov ebx, [ebp+EXTFS.ext2_temp_block] - call ext2_get_block - test eax, eax - jnz .fail - - xor edx, edx - mov eax, ecx - div [ebp+EXTFS.count_pointer_in_block_square] - - ;RUS: eax - номер в полученном блоке edx - номер дальше - ;ENG: eax - current block number, edx - next block number - mov eax, [ebx + eax*4] - call ext2_get_block - test eax, eax - jnz .fail - - mov eax, edx - jmp @F - - .get_double_indirect_block: - push edx ebx - - mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4] - mov ebx, [ebp+EXTFS.ext2_temp_block] - call ext2_get_block - test eax, eax - jnz .fail - - mov eax, ecx - @@: - xor edx, edx - div [ebp+EXTFS.count_pointer_in_block] - - mov eax, [ebx + eax*4] - call ext2_get_block - test eax, eax - jnz .fail - - mov ecx, [ebx + edx*4] - .fail: - pop ebx edx - ret - - .get_indirect_block: - push ebx - mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4] - mov ebx, [ebp+EXTFS.ext2_temp_block] - call ext2_get_block - test eax, eax - jnz @F ;RUS: если не было ошибки ;ENG: if there was no error - - mov ecx, [ebx + ecx*4] ;RUS: заносим результат ;ENG: ??? - @@: - pop ebx - ret - - .get_direct_block: - mov ecx, [esi + EXT2_INODE_STRUC.i_block + ecx*4] - xor eax, eax - ret - -;=================================================================== -;get content inode by num -;in: eax = inode_num -; ebx = address of inode content -; ebp = pointer to EXTFS -;out: eax - error code -ext2_get_inode: - pushad - mov edi, ebx ;сохраним адрес inode - dec eax - xor edx, edx - - div [ebp + EXT2_SB_STRUC.inodes_per_group + EXTFS.superblock] - - push edx ;locale num in group - - mov edx, 32 - mul edx ; address block_group in global_desc_table - - ;RUS: в eax - смещение группы с inode-ом относительно начала глобальной дескрипторной таблицы - ;RUS: найдем блок в котором он находится - ;ENG: in eax - inode group offset relative to global descriptor table start - ;ENG: let's find the block this inode is in - div [ebp+EXTFS.block_size] - add eax, [ebp + EXT2_SB_STRUC.first_data_block + EXTFS.superblock] - inc eax - mov ebx, [ebp+EXTFS.ext2_temp_block] - call ext2_get_block - test eax, eax - jnz .fail - - add ebx, edx ;RUS: локальный номер в блоке ;ENG: local number inside block - mov eax, [ebx + EXT2_BLOCK_GROUP_DESC.inode_table] ;RUS: номер блока - в терминах ext2 - ;ENG: block number - in ext2 terms - mov ecx, [ebp+EXTFS.log_block_size] - shl eax, cl - ;RUS: eax - указывает на таблицу inode-ов на hdd ;ENG: eax - points to inode table on HDD - mov esi, eax ;RUS: сохраним его пока в esi ;ENG: let's save it in esi for now - - ;RUS: прибавим локальный адрес inode-а ;ENG: add local address of inode - pop eax ; index - mov ecx, [ebp+EXTFS.inode_size] - mul ecx ; (index * inode_size) - ;RUS: поделим на размер блока ;ENG: divide by block size - mov ecx, eax - and ecx, 512 - 1 - shrd eax, edx, 9 - - add eax, esi ;RUS: нашли адрес блока для чтения ;ENG: found block address to read - mov ebx, [ebp+EXTFS.ext2_temp_block] - call fs_read32_sys - test eax, eax - jnz .fail - - mov esi, ecx ;RUS: добавим "остаток" ;ENG: add the "remainder" - mov ecx, [ebp+EXTFS.inode_size] - add esi, ebx ;RUS: к адресу ;ENG: to the address - rep movsb ;RUS: копируем inode ;ENG: copy inode - xor eax, eax - .fail: - mov PUSHAD_EAX, eax - popad - ret - -;---------------------------------------------------------------- -; ext2_ReadFolder - EXT2FS implementation of reading a folder -; in: ebp = pointer to EXTFS structure -; in: esi+[esp+4] = name -; in: ebx = pointer to parameters from sysfunc 70 -; out: eax, ebx = return values for sysfunc 70 -;---------------------------------------------------------------- -ext2_ReadFolder: - call ext2_lock - cmp byte [esi], 0 - jz .root_folder - - push ebx - stdcall ext2_find_lfn, [esp+4+4] ;вернет в esi адрес inode - pop ebx - test eax, eax - jnz .error_ret - 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 - ;придется копировать 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 ;папка пуста - je .error_empty_dir - - mov edx, [ebx + 16] - push edx ;адрес результата [edi + 28] - push 0 ;конец очередного блока папки [edi + 24] - push dword [ebx +12];сколько файлов нужно прочитать [edi + 20] - push dword [ebx + 4];первый "нужный" файл [edi + 16] - push dword [ebx + 8];флаги [edi + 12] - push 0 ;[EXT2_read_in_folder] [edi + 8] - push 0 ;[EXT2_files_in_folder] [edi + 4] - push 0 ;номер блока по порядку [edi] - - mov edi, edx - mov ecx, 32/4 - rep stosd ; fill header zero - - mov edi, esp ; edi - указатель на локальные переменные - add edx, 32 ; edx = current mem for return - - xor ecx, ecx ; получим номер первого блока - call ext2_get_inode_block - test eax, eax - jnz .error_get_block - - mov eax, ecx - mov ebx, [ebp+EXTFS.ext2_save_block] - call ext2_get_block ; и считываем блок с hdd - test eax, eax - jnz .error_get_block - - mov eax, ebx ; ebx = current dir 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 ; if (inode = 0) => not used - jz @F - inc dword [edi + 4] ; EXT2_files_in_folder - dec ecx - @@: - movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] - - cmp eax, 12 ; минимальная длина записи - jb .error_bad_len - test eax, 0x3 ; длина записи должна делиться на 4 - jnz .error_bad_len - - sub [esi + EXT2_INODE_STRUC.i_size], eax ;вычитаем напрямую из структуры inode - add ebx, eax ; к следующей записи - cmp ebx, [edi + 24] ; сравниваем с концом блока - jb .find_wanted_start - - push .find_wanted_start - .end_block: ;вылетели из цикла - cmp [esi + EXT2_INODE_STRUC.i_size], 0 - jle .end_dir - - inc dword [edi] ;получаем новый блок - push ecx - mov ecx, [edi] - call ext2_get_inode_block - test eax, eax - jnz .error_get_block - - mov eax, ecx - mov ebx, [ebp+EXTFS.ext2_save_block] - call ext2_get_block - test eax, eax - jnz .error_get_block - - pop ecx - mov eax, ebx - add eax, [ebp+EXTFS.block_size] - mov [edi + 24], eax ;запомним конец блока - ret ; опять в цикл - - .wanted_end: - loop .find_wanted_cycle ; ecx 0 => -1 нужно посчитать сколько файлов - - ;дошли до первого "нужного" файла - .find_wanted_end: - mov ecx, [edi + 20] - .wanted_start: ; ищем first_wanted+count - jecxz .wanted_end - cmp [ebx + EXT2_DIR_STRUC.inode], 0 ; if (inode = 0) => not used - jz .empty_rec - inc dword [edi + 8] - inc dword [edi + 4] - - push edi ecx - mov edi, edx ;обнуляем место под очереное имя файла/папки - xor eax, eax - mov ecx, 40 / 4 - rep stosd - pop ecx edi - - push ebx edi edx - mov eax, [ebx + EXT2_DIR_STRUC.inode] ;получим дочерний inode - mov ebx, [ebp+EXTFS.ext2_temp_inode] - call ext2_get_inode - test eax, eax - jnz .error_read_subinode - - lea edi, [edx + 8] - - mov eax, [ebx + EXT2_INODE_STRUC.i_ctime] ; переведем время в ntfs формат - 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 ; для папки размер - 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 ;помечаем, что это файл(2 раза xor) - @@: - xor dword [edx], FS_FT_DIR ;помечаем, что это файл - - ;теперь скопируем имя, сконвертировав из UTF-8 в CP866 - push ecx esi ;edi уже сохранен в стеке - 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], '.' ; в linux файл, начинающийся с точки - скрытый - jne @F - or dword [edx], FS_FT_HIDDEN - @@: - - add edx, 40 + 264 ; go to next record - dec ecx ; если запись пустая ecx не надо уменьшать - .empty_rec: - movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] - cmp eax, 12 ; минимальная длина записи - jb .error_bad_len - test eax, 0x3 ; длина записи должна делиться на 4 - jnz .error_bad_len - - sub [esi + EXT2_INODE_STRUC.i_size], eax ;вычитаем напрямую из структуры inode - add ebx, eax - cmp ebx, [edi + 24] ;дошли ли до конца блока? - jb .wanted_start - - push .wanted_start ; дошли - jmp .end_block - - .end_dir: ;конец папки, когда еще не дошли до нужного файла - call ext2_unlock - mov edx, [edi + 28] ;адрес структуры результата - 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 ;RUS: зарезервировано: нули в текущей реализации - ;ENG: reserved: zeros in current implementation - lea edi, [edx + 12] - mov ecx, 20 / 4 - rep stosd - ret - - .error_bad_len: - mov eax, ERROR_FS_FAIL - .error_read_subinode: - .error_get_block: - lea esp, [edi + 32] - .error_ret: - or ebx, -1 - push eax - call ext2_unlock - pop eax - ret - - .error_empty_dir: ;RUS: inode папки без блоков ;ENG: inode of folder without blocks - .error_root: ;RUS: root - не папка ;ENG: root is not a folder - mov eax, ERROR_FS_FAIL - jmp .error_ret - - .error_not_found: ;RUS: файл не найден ;ENG: file not found - mov eax, ERROR_FILE_NOT_FOUND - jmp .error_ret - -;============================================ -;convert UTF-8 string to ASCII-string (codepage 866) -;in: ecx = length source -; esi = source -; edi = buffer -; destroys: eax,esi,edi -utf8_to_cp866: - jecxz .ret - .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 - .ret: - ret - -;---------------------------------------------------------------- -; ext2_Read - EXT2FS 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 -;---------------------------------------------------------------- -ext2_Read: - call ext2_lock - cmp byte [esi], 0 - jnz @F - - .this_is_nofile: - call ext2_unlock - or ebx, -1 - mov eax, ERROR_ACCESS_DENIED - ret - - @@: - push ebx - stdcall ext2_find_lfn, [esp+4+4] - pop ebx - 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 ;оставляем только тип inode в ax - cmp ax, EXT2_S_IFREG - jne .this_is_nofile - - mov edi, [ebx+16] ; edi = pointer to return mem - mov ecx, [ebx+12] - - mov eax, [ebx+4] - mov edx, [ebx+8] ;RUS: edx : eax - стартовый номер байта ;ENG: edx : eax - start byte number - - ;RUS: ///// сравним хватит ли нам файла или нет ;ENG: ///// check if file is big enough for us - cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx - ja .size_great - jb .size_less - - cmp [esi + EXT2_INODE_STRUC.i_size], eax - ja .size_great - - .size_less: - call ext2_unlock - xor ebx, ebx - mov eax, ERROR_END_OF_FILE - ret - - .size_great: - add eax, ecx ;RUS: add to first_wanted кол-во байт для чтения - ;ENG: add to first_wanted number of bytes to read - adc edx, 0 - - cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx - ja .size_great_great - jb .size_great_less - cmp [esi + EXT2_INODE_STRUC.i_size], eax - jae .size_great_great ; and if it's equal, no matter where we jump - - .size_great_less: - push 1 ;RUS: читаем по границе размера ;ENG: reading till the end of file - mov ecx, [esi + EXT2_INODE_STRUC.i_size] - sub ecx, [ebx+4] ;RUS: (размер - старт) = сколько читать ;ENG: to read = (size - start) - jmp @F - - .size_great_great: - push 0 ;RUS: читаем столько, сколько запросили ;ENG: reading as much as requested - - @@: - ;здесь мы точно знаем сколько байт читать - ecx - ;edi - return memory - ;esi -> first wanted - - push ecx ;количество считанных байт - - ;получим кусок из первого блока - mov edx, [ebx+8] - mov eax, [ebx+4] - div [ebp+EXTFS.block_size] - - push eax ;счетчик блоков ложим в стек - - push ecx - mov ecx, eax - call ext2_get_inode_block - test eax, eax - jnz .error_at_first_block - mov ebx, [ebp+EXTFS.ext2_save_block] - mov eax, ecx - call ext2_get_block - test eax, eax - jnz .error_at_first_block - pop ecx - add ebx, edx - - neg edx - add edx, [ebp+EXTFS.block_size] ;RUS: block_size - стартовый байт = сколько байт 1-го блока - ;ENG: block_size - start byte = number of bytes in 1st block - cmp ecx, edx - jbe .only_one_block - - mov eax, ecx - sub eax, edx - mov ecx, edx - - push esi - mov esi, ebx - rep movsb ;RUS: кусок 1-го блока ;ENG: part of 1st block - pop esi - - ;теперь в eax кол-во оставшихся байт для чтения - .calc_blocks_count: - mov ebx, edi ;чтение блока прям в ->ebx - xor edx, edx - div [ebp+EXTFS.block_size] ;кол-во байт в последнем блоке (остаток) в edx - mov edi, eax ;кол-во целых блоков в edi - @@: - test edi, edi - jz .finish_block - inc dword [esp] - mov ecx, [esp] - call ext2_get_inode_block - test eax, eax - jnz .error_at_read_cycle - - mov eax, ecx ;а ebx уже забит нужным значением - call ext2_get_block - test eax, eax - jnz .error_at_read_cycle - add ebx, [ebp+EXTFS.block_size] - - dec edi - jmp @B - - .finish_block: ;в edx - кол-во байт в последнем блоке - test edx, edx - jz .end_read - - pop ecx ;счетчик блоков -> ecx - inc ecx - call ext2_get_inode_block - test eax, eax - jnz .error_at_finish_block - - mov edi, ebx - mov eax, ecx - mov ebx, [ebp+EXTFS.ext2_save_block] - call ext2_get_block - test eax, eax - jnz .error_at_finish_block - - mov ecx, edx - mov esi, ebx - rep movsb ;кусок last блока - jmp @F - - .end_read: - pop ecx ;счетчик блоков, который хранился в стеке - @@: - pop ebx ;количество считанных байт - call ext2_unlock - pop eax ; 1 или 0 - достигли ли конца файла - test eax, eax - jz @F - - mov eax, ERROR_END_OF_FILE - ret - @@: - xor eax, eax - ret - - .only_one_block: - mov esi, ebx - rep movsb ;кусок last блока - jmp .end_read - - .error_at_first_block: - pop edx - .error_at_read_cycle: - pop ebx - .error_at_finish_block: - pop ecx edx - or ebx, -1 - push eax - call ext2_unlock - pop eax - ret - -;---------------------------------------------------------------- -; in: esi = file path -; ebx = pointer to dir block -; ebp = pointer to EXTFS structure -; out: esi - name without parent or not_changed -; ebx - dir_rec of inode children -ext2_test_block_by_name: - sub esp, 256 ;EXT2_filename - mov edx, ebx - add edx, [ebp+EXTFS.block_size] ;RUS: запомним конец блока ;ENG: 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 ;RUS: кол-во байт в получившейся строке ;ENG: number of bytes in resulting string - - mov esi, [esp] - @@: - jecxz .test_find - dec ecx - - lodsb - call char_toupper - - mov ah, [edi] - inc edi - xchg al, ah - call char_toupper - cmp al, ah - je @B - @@: ;RUS: не подошло ;ENG: didn't fit - pop esi - .next_rec: - movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] - add ebx, eax ;RUS: к след. записи ;ENG: go to next record - cmp ebx, edx ;RUS: проверим конец ли ;ENG: check if this is the end - jb .start_rec - add esp, 256 - ret - - .test_find: - cmp byte [esi], 0 - je .ret ;RUS: нашли конец ;ENG: the end reached - cmp byte [esi], '/' - jne @B - inc esi - .ret: - add esp, 256 + 4 - ret - -;======================== -;Ищет inode по строке пути -;in: esi+[esp+4] = name -; ebp = pointer to EXTFS -;out: eax - error code -; esi = inode -; dl - первый байт из имени файла/папки -ext2_find_lfn: - mov edx, [ebp+EXTFS.root_inode] - cmp [edx + EXT2_INODE_STRUC.i_blocks], 0 - je .error_empty_root - - .next_path_part: - push [edx + EXT2_INODE_STRUC.i_blocks] - xor ecx, ecx - .folder_block_cycle: - push ecx - xchg esi, edx - call ext2_get_inode_block - xchg esi, edx - test eax, eax - jnz .error_get_inode_block - - mov eax, ecx - mov ebx, [ebp+EXTFS.ext2_save_block] ;ebx = cur dir record - call ext2_get_block - test eax, eax - jnz .error_get_block - - push esi - push edx - call ext2_test_block_by_name - pop edx - pop edi ecx - - cmp edi, esi ;RUS: нашли имя? ;ENG: did we find a name? - je .next_folder_block ;RUS: не нашли -> к след. блоку ;ENG: we didn't -> moving to next block - - cmp byte [esi], 0 ;RUS: дошли до "конца" пути -> возваращаемся - ;ENG: reached the "end" of path -> returning - jnz @f - cmp dword [esp+8], 0 - jz .get_inode_ret - mov esi, [esp+8] - mov dword [esp+8], 0 - @@: - - cmp [ebx + EXT2_DIR_STRUC.file_type], EXT2_FT_DIR ;RUS: нашли, но это не папка - jne .not_found ;ENG: found, but it's not a folder - - mov eax, [ebx + EXT2_DIR_STRUC.inode] - mov ebx, [ebp+EXTFS.ext2_save_inode] ;RUS: все же папка. ;ENG: it's a folder afterall - call ext2_get_inode - test eax, eax - jnz .error_get_inode - pop ecx ;RUS: в стеке лежит кол-во блоков ;ENG: stack top contains number of blocks - mov edx, ebx - jmp .next_path_part - - .next_folder_block: - ;к следующему блоку в текущей папке - pop eax ;RUS: счетчик блоков ;ENG: 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 ;RUS: в стеке лежит кол-во блоков ;ENG: stack top contains number of blocks - mov dl, [ebx + EXT2_DIR_STRUC.name] ;RUS: в dl - первый символ () ;ENG: ??? - mov eax, [ebx + EXT2_DIR_STRUC.inode] - mov ebx, [ebp+EXTFS.ext2_save_inode] - call ext2_get_inode - mov esi, ebx - xor eax, eax - 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 - -;---------------------------------------------------------------- -; ext2_GetFileInfo - EXT2 implementation of getting file info -; in: ebp = pointer to EXTFS structure -; in: esi+[esp+4] = name -; in: ebx = pointer to parameters from sysfunc 70 -; out: eax, ebx = return values for sysfunc 70 -;---------------------------------------------------------------- -ext2_GetFileInfo: - call ext2_lock - mov edx, [ebx+16] - cmp byte [esi], 0 - jz .is_root - - push edx - stdcall ext2_find_lfn, [esp+4+4] - mov ebx, edx - pop edx - test eax, eax - jz @F - push eax - call ext2_unlock - pop eax - ret - - .is_root: - xor ebx, ebx ;RUS: root не может быть скрытым ;ENG: root cannot be hidden - mov esi, [ebp+EXTFS.root_inode] - @@: - xor eax, eax - mov edi, edx - mov ecx, 40/4 - rep stosd ; fill zero - - cmp bl, '.' - jne @F - or dword [edx], FS_FT_HIDDEN - @@: - - test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR - jnz @F - mov eax, [esi + EXT2_INODE_STRUC.i_size] ;low size - mov ebx, [esi + EXT2_INODE_STRUC.i_dir_acl] ;high size - mov dword [edx+32], eax - mov dword [edx+36], ebx - xor dword [edx], FS_FT_DIR - @@: - xor dword [edx], FS_FT_DIR - - lea edi, [edx + 8] - mov eax, [esi + EXT2_INODE_STRUC.i_ctime] - xor edx, edx - add eax, 3054539008 - adc edx, 2 - call ntfs_datetime_to_bdfe.sec - - mov eax, [esi + EXT2_INODE_STRUC.i_atime] - xor edx, edx - add eax, 3054539008 - adc edx, 2 - call ntfs_datetime_to_bdfe.sec - - mov eax, [esi + EXT2_INODE_STRUC.i_mtime] - xor edx, edx - add eax, 3054539008 - adc edx, 2 - call ntfs_datetime_to_bdfe.sec - - call ext2_unlock - xor eax, eax - ret - -ext2_Rewrite: -ext2_Write: -ext2_SetFileEnd: -ext2_SetFileInfo: -ext2_Delete: -ext2_CreateFolder: - xor ebx, ebx - mov eax, ERROR_UNSUPPORTED_FS - ret diff --git a/kernel/trunk/fs/ext2/blocks.asm b/kernel/trunk/fs/ext2/blocks.asm new file mode 100644 index 0000000000..60b562399d --- /dev/null +++ b/kernel/trunk/fs/ext2/blocks.asm @@ -0,0 +1,408 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 block handling code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;--------------------------------------------------------------------- +; Write ext2 block from memory to disk. +; Input: eax = i_block (block number in ext2 terms); +; ebx = buffer address +; ebp = pointer to EXTFS +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_block_write: + push edx ebx ecx + + mov edx, fs_write32_sys + jmp ext2_block_modify + +;--------------------------------------------------------------------- +; Read ext2 block from disk to memory. +; Input: eax = i_block (block number in ext2 terms); +; ebx = address of where to read block +; ebp = pointer to EXTFS +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_block_read: + push edx ebx ecx + + mov edx, fs_read32_sys + jmp ext2_block_modify + +;--------------------------------------------------------------------- +; Modify ext2 block. +; Input: eax = i_block (block number in ext2 terms); +; ebx = I/O buffer address; +; edx = fs_read/write32_sys +; ebp = pointer to EXTFS +; edx, ebx, ecx on stack. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_block_modify: + ; Get block number in hard-disk terms in eax. + mov ecx, [ebp + EXTFS.log_block_size] + shl eax, cl + mov ecx, eax + push [ebp + EXTFS.count_block_in_block] + + @@: + mov eax, ecx + call edx + test eax, eax + jnz .fail + + inc ecx + add ebx, 512 + dec dword[esp] + jnz @B + + xor eax, eax + @@: + pop ecx + pop ecx ebx edx + ret + + .fail: + mov eax, ERROR_DEVICE + jmp @B + +;--------------------------------------------------------------------- +; Zeroes a block. +; Input: ebx = block ID. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_block_zero: + push ebx + + mov eax, ebx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .return + + push edi ecx + xor eax, eax + mov ecx, [ebp + EXTFS.block_size] + mov edi, [ebp + EXTFS.ext2_temp_block] + rep stosb + pop ecx edi + + mov eax, [esp] + call ext2_block_write + + .return: + pop ebx + ret + +;--------------------------------------------------------------------- +; Allocates a block. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; Output: Block marked as set in block group. +; eax = error code. +; ebx = block ID. +;--------------------------------------------------------------------- +ext2_block_alloc: + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_count] + push EXT2_BLOCK_GROUP_DESC.free_blocks_count + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group] + + lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count] + push ebx + + push ext2_bg_read_blk_bitmap + + call ext2_resource_alloc + + ret + +;--------------------------------------------------------------------- +; Zero-allocates a block. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; Output: Block marked as set in block group. +; eax = error code. +; ebx = block ID. +;--------------------------------------------------------------------- +ext2_block_calloc: + call ext2_block_alloc + test eax, eax + jnz @F + + call ext2_block_zero + @@: + ret + +;--------------------------------------------------------------------- +; Frees a block. +; Input: eax = block ID. +; ebp = pointer to EXTFS. +; Output: Block marked as free in block group. +; eax = error code. +;--------------------------------------------------------------------- +ext2_block_free: + push edi ecx + + mov edi, ext2_bg_read_blk_bitmap + xor ecx, ecx + call ext2_resource_free + + pop ecx edi + ret + + +;--------------------------------------------------------------------- +; Find parent from file path in block. +; Input: esi = file path. +; ebx = pointer to directory block. +; ebp = pointer to EXTFS structure. +; Output: esi = name without parent, or not changed. +; ebx = directory record matched. +;--------------------------------------------------------------------- +ext2_block_find_parent: + sub esp, 256 ; Space for EXT2 filename. + mov edx, ebx + add edx, [ebp + EXTFS.block_size] ; Save block end. + + .start_rec: + cmp [ebx + EXT2_DIR_STRUC.inode], 0 + jz .next_rec + + mov edi, esp + push esi + movzx ecx, [ebx + EXT2_DIR_STRUC.name_len] + lea esi, [ebx + EXT2_DIR_STRUC.name] + call utf8_to_cp866 + + mov ecx, edi + lea edi, [esp + 4] + sub ecx, edi ; Number of bytes in resulting string. + + mov esi, [esp] + + ; esi: original file path. + ; edi: converted string stored on stack. + ; ecx: size of converted string. + @@: + ; If no bytes left in resulting string, test it. + jecxz .test_find + dec ecx + + lodsb + call char_toupper + + mov ah, [edi] + inc edi + xchg al, ah + call char_toupper + + ; If both are same, check next byte. + cmp al, ah + je @B + @@: ; Doesn't match. + pop esi + + .next_rec: + movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, eax ; Go to next record. + cmp ebx, edx ; Check if this is the end. + jb .start_rec + + add esp, 256 + ret + + .test_find: + cmp byte [esi], 0 + je .ret ; The end reached. + cmp byte [esi], '/' ; If not end of directory name, not matched. + jne @B + inc esi + + .ret: + add esp, 256 + 4 + ret + +;--------------------------------------------------------------------- +; Finds free space in a directory block, modifying last entry appropriately. +; Input: ebp = pointer to EXTFS. +; ecx = size of free space required. +; [EXTFS.ext2_temp_block] contains the block relevant. +; Output: edi = free entry. +; rec_len of free entry is set. +; eax = error code; if the block doesn't link to the next one, this is 0x00000001 on failure. +; ; else, 0xFFFFFFFF. +;--------------------------------------------------------------------- +ext2_block_find_fspace: + push ebx edx + + mov edi, [ebp + EXTFS.ext2_temp_block] + mov edx, edi + add edx, [ebp + EXTFS.block_size] + + @@: + movzx eax, [edi + EXT2_DIR_STRUC.rec_len] + test eax, eax + jz .zero_len + + cmp [edi + EXT2_DIR_STRUC.inode], 0 + je .unused_entry + + ; It's a used entry, so see if we can fit it between current one and next. + ; Subtract the size used by the name and the structure from rec_len. + movzx ebx, [edi + EXT2_DIR_STRUC.name_len] + add ebx, 8 + 3 + and ebx, 0xfffffffc ; Align it on the next 4-byte boundary. + + sub eax, ebx + add edi, ebx + cmp eax, ecx + jb .next_iter + + sub edi, ebx + mov [edi + EXT2_DIR_STRUC.rec_len], bx ; Make previous entry point to us. + add edi, ebx + + mov [edi + EXT2_DIR_STRUC.rec_len], ax ; Make current entry point to next one. + jmp .found + + .unused_entry: + ; It's an unused inode. + cmp eax, ecx + jge .found + + .next_iter: + add edi, eax + cmp edi, edx + jb @B + + .not_found: + xor eax, eax + not eax + jmp .ret + + ; Zero length entry means we have the rest of the block for us. + .zero_len: + mov eax, edx + sub eax, edi + + ; Point to next block. + mov [edi + EXT2_DIR_STRUC.rec_len], ax + + cmp eax, ecx + jge .fits + + mov [edi + EXT2_DIR_STRUC.inode], 0 + + ; It doesn't fit, but the block doesn't link to the next block. + xor eax, eax + inc eax + jmp .ret + + .fits: + mov [edi + EXT2_DIR_STRUC.rec_len], cx + + .found: + xor eax, eax + + .ret: + pop edx ebx + ret + +;--------------------------------------------------------------------- +; Gets the block group's descriptor. +; Input: eax = block group. +; Output: eax = if zero, error; else, points to block group descriptor. +; [EXTFS.ext2_temp_block] contains relevant block. +; ebp = pointer to EXTFS. +;--------------------------------------------------------------------- +ext2_bg_read_desc: + push edx ebx + mov edx, 32 + mul edx ; Get index of descriptor in global_desc_table. + + ; eax: block group descriptor offset relative to global descriptor table start + ; Find the block this block descriptor is in. + div [ebp + EXTFS.block_size] + add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + inc eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail + + add ebx, edx ; edx: local index of descriptor inside block + mov eax, ebx + + .return: + pop ebx edx + ret + + .fail: + xor eax, eax + jmp .return + +;--------------------------------------------------------------------- +; Writes a block group's descriptor. +; Input: eax = block group. +; [EXTFS.ext2_temp_data] contains the block relevant. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_bg_write_desc: + push edx ebx + mov edx, 32 + mul edx ; Get index of descriptor in global_desc_table. + + ; eax: block group descriptor offset relative to global descriptor table start + ; Find the block this block descriptor is in. + div [ebp + EXTFS.block_size] + add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + inc eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + + .return: + pop ebx edx + ret + +;--------------------------------------------------------------------- +; Gets the block group's block bitmap. +; Input: eax = block group. +; Output: eax = if zero, error; else, points to block group descriptor. +; ebx = block bitmap's block (hard disk). +;--------------------------------------------------------------------- +ext2_bg_read_blk_bitmap: + push ecx + + call ext2_bg_read_desc + test eax, eax + jz .fail + + mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.block_bitmap] ; Block number of block group bitmap - in ext2 terms. + + .return: + pop ecx + ret + + .fail: + xor eax, eax + jmp .return + +;--------------------------------------------------------------------- +; Updates superblock, plus backups. +; Input: ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_sb_update: + push ebx + + mov eax, 2 + lea ebx, [ebp + EXTFS.superblock] + call fs_write32_sys + + pop ebx diff --git a/kernel/trunk/fs/ext2/ext2.asm b/kernel/trunk/fs/ext2/ext2.asm new file mode 100644 index 0000000000..af3c38023f --- /dev/null +++ b/kernel/trunk/fs/ext2/ext2.asm @@ -0,0 +1,1221 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 initialization, plus syscall handling code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +include 'ext2.inc' +include 'blocks.asm' +include 'inode.asm' +include 'resource.asm' + +iglobal +align 4 +ext2_user_functions: + dd ext2_free + dd (ext2_user_functions_end - ext2_user_functions - 4) / 4 + dd ext2_Read + dd ext2_ReadFolder + dd ext2_Rewrite + dd ext2_Write + dd ext2_SetFileEnd + dd ext2_GetFileInfo + dd ext2_SetFileInfo + dd 0 + dd ext2_Delete + dd ext2_CreateFolder +ext2_user_functions_end: +endg + +;--------------------------------------------------------------------- +; Locks up an ext2 partition. +; Input: ebp = pointer to EXTFS. +;--------------------------------------------------------------------- +proc ext2_lock + lea ecx, [ebp + EXTFS.lock] + jmp mutex_lock +endp + +;--------------------------------------------------------------------- +; Unlocks up an ext2 partition. +; Input: ebp = pointer to EXTFS. +;--------------------------------------------------------------------- +proc ext2_unlock + lea ecx, [ebp + EXTFS.lock] + jmp mutex_unlock +endp + +;--------------------------------------------------------------------- +; Check if it's a valid ext* superblock. +; Input: ebp: first three fields of PARTITION structure. +; ebx + 512: points to 512-bytes buffer that can be used for anything. +; Output: eax: clear if can't create partition; set to EXTFS otherwise. +;--------------------------------------------------------------------- +proc ext2_create_partition + push ebx + + mov eax, 2 ; Superblock starts at 1024-bytes. + add ebx, 512 ; Get pointer to fs-specific buffer. + call fs_read32_sys + test eax, eax + jnz .fail + + ; Allowed 1KiB, 2KiB, 4KiB, 8KiB. + cmp [ebx + EXT2_SB_STRUC.log_block_size], 3 + ja .fail + + cmp [ebx + EXT2_SB_STRUC.magic], EXT2_SUPER_MAGIC + jne .fail + + cmp [ebx + EXT2_SB_STRUC.state], EXT2_VALID_FS + jne .fail + + ; Can't have no inodes per group. + cmp [ebx + EXT2_SB_STRUC.inodes_per_group], 0 + je .fail + + ; If incompatible features required, unusable superblock. + mov eax, [ebx + EXT2_SB_STRUC.feature_incompat] + test eax, not EXT4_FEATURE_INCOMPAT_SUPP + jz .setup + + .fail: + ; Not a (valid/usable) EXT2 superblock. + pop ebx + xor eax, eax + ret + + .setup: + movi eax, sizeof.EXTFS + call malloc + test eax, eax + jz ext2_create_partition.fail + + ; Store the first sector field. + mov ecx, dword[ebp + PARTITION.FirstSector] + mov dword[eax + EXTFS.FirstSector], ecx + mov ecx, dword [ebp + PARTITION.FirstSector+4] + mov dword [eax + EXTFS.FirstSector+4], ecx + + ; The length field. + mov ecx, dword[ebp + PARTITION.Length] + mov dword[eax + EXTFS.Length], ecx + mov ecx, dword[ebp + PARTITION.Length+4] + mov dword[eax + EXTFS.Length+4], ecx + + ; The disk field. + mov ecx, [ebp + PARTITION.Disk] + mov [eax + EXTFS.Disk], ecx + + mov [eax + EXTFS.FSUserFunctions], ext2_user_functions + + push ebp esi edi + + mov ebp, eax + lea ecx, [eax + EXTFS.lock] + call mutex_init + + ; Copy superblock from buffer to reserved memory. + mov esi, ebx + lea edi, [ebp + EXTFS.superblock] + mov ecx, 512/4 + rep movsd + + ; Get total groups. + mov eax, [ebx + EXT2_SB_STRUC.blocks_count] + sub eax, [ebx + EXT2_SB_STRUC.first_data_block] + dec eax + xor edx, edx + div [ebx + EXT2_SB_STRUC.blocks_per_group] + inc eax + mov [ebp + EXTFS.groups_count], eax + + ; Get log(block_size), such that 1,2,3,4 equ 1KiB,2KiB,4KiB,8KiB. + mov ecx, [ebx + EXT2_SB_STRUC.log_block_size] + inc ecx + mov [ebp + EXTFS.log_block_size], ecx + + ; 512-byte blocks in ext2 blocks. + mov eax, 1 + shl eax, cl + mov [ebp + EXTFS.count_block_in_block], eax + + ; Get block_size/4 (we'll find square later). + shl eax, 7 + mov [ebp + EXTFS.count_pointer_in_block], eax + mov edx, eax + + ; Get block size. + shl eax, 2 + mov [ebp + EXTFS.block_size], eax + + ; Save block size for 2 kernel_alloc calls. + push eax eax + + mov eax, edx + mul edx + mov [ebp + EXTFS.count_pointer_in_block_square], eax + + ; Have temporary block storage for get_inode procedure, and one for global procedure. + KERNEL_ALLOC [ebp + EXTFS.ext2_save_block], .error + KERNEL_ALLOC [ebp + EXTFS.ext2_temp_block], .error + + mov [ebp + EXTFS.partition_flags], 0x00000000 + mov eax, [ebx + EXT2_SB_STRUC.feature_ro_compat] + and eax, not EXT2_FEATURE_RO_COMPAT_SUPP + jnz .read_only + + mov eax, [ebx + EXT2_SB_STRUC.feature_incompat] + and eax, EXT4_FEATURE_INCOMPAT_W_NOT_SUPP + jz @F + + .read_only: + ; Mark as read-only. + or [ebp + EXTFS.partition_flags], EXT2_RO + @@: + mov ecx, [ebx + EXT2_SB_STRUC.blocks_per_group] + mov [ebp + EXTFS.blocks_per_group], ecx + + movzx ecx, word[ebx + EXT2_SB_STRUC.inode_size] + mov [ebp + EXTFS.inode_size], ecx + + ; Allocate for three inodes (loop would be overkill). + push ecx ecx ecx + + KERNEL_ALLOC [ebp + EXTFS.ext2_save_inode], .error + KERNEL_ALLOC [ebp + EXTFS.ext2_temp_inode], .error + KERNEL_ALLOC [ebp + EXTFS.root_inode], .error + + ; Read root inode. + mov ebx, eax + mov eax, EXT2_ROOT_INO + call ext2_inode_read + + test eax, eax + jnz .error + + mov eax, ebp ; Return pointer to EXTFS. + pop edi esi ebp ebx + ret + + ; Error in setting up. + .error: + ; Free save block. + KERNEL_FREE [ebp + EXTFS.ext2_save_block], .fail + + ; Temporary block. + KERNEL_FREE [ebp + EXTFS.ext2_temp_block], .fail + + ; All inodes. + KERNEL_FREE [ebp + EXTFS.ext2_save_inode], .fail + KERNEL_FREE [ebp + EXTFS.ext2_temp_inode], .fail + KERNEL_FREE [ebp + EXTFS.root_inode], .fail + + mov eax, ebp + call free + + jmp .fail +endp + +; FUNCTIONS PROVIDED BY SYSCALLS. + +;--------------------------------------------------------------------- +; Frees up all ext2 structures. +; Input: eax = pointer to EXTFS. +;--------------------------------------------------------------------- +proc ext2_free + push ebp + + xchg ebp, eax + stdcall kernel_free, [ebp+EXTFS.ext2_save_block] + stdcall kernel_free, [ebp+EXTFS.ext2_temp_block] + stdcall kernel_free, [ebp+EXTFS.ext2_save_inode] + stdcall kernel_free, [ebp+EXTFS.ext2_temp_inode] + stdcall kernel_free, [ebp+EXTFS.root_inode] + + xchg ebp, eax + call free + + pop ebp + ret +endp + +;--------------------------------------------------------------------- +; Read disk folder. +; Input: ebp = pointer to EXTFS structure. +; esi + [esp + 4] = file name. +; ebx = pointer to parameters from sysfunc 70. +; Output: ebx = blocks read (or 0xFFFFFFFF, folder not found) +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_ReadFolder: + call ext2_lock + cmp byte [esi], 0 + jz .root_folder + + push ebx + stdcall ext2_inode_find, [esp + 4 + 4] ; Get inode. + pop ebx + + mov esi, [ebp + EXTFS.ext2_save_inode] + test eax, eax + jnz .error_ret + + ; If not a directory, then return with error. + test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR + jz .error_not_found + jmp @F + + .root_folder: + mov esi, [ebp + EXTFS.root_inode] + test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR + jz .error_root + + ; Copy the inode. + mov edi, [ebp + EXTFS.ext2_save_inode] + mov ecx, [ebp + EXTFS.inode_size] + shr ecx, 2 + + push edi + rep movsd + pop esi + + @@: + cmp [esi + EXT2_INODE_STRUC.i_size], 0 ; Folder is empty. + je .error_empty_dir + + mov edx, [ebx + 16] + push edx ; Result address [edi + 28]. + push 0 ; End of the current block in folder [edi + 24] + push dword[ebx + 12] ; Blocks to read [edi + 20] + push dword[ebx + 4] ; The first wanted file [edi + 16] + push dword[ebx + 8] ; Flags [edi + 12] + push 0 ; Read files [edi + 8] + push 0 ; Files in folder [edi + 4] + push 0 ; Number of blocks read in dir (and current block index) [edi] + + ; Fill header with zeroes. + mov edi, edx + mov ecx, 32/4 + rep stosd + + mov edi, esp ; edi = pointer to local variables. + add edx, 32 ; edx = mem to return. + + xor ecx, ecx ; Get number of first block. + call ext2_get_inode_block + test eax, eax + jnz .error_get_block + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read ; Read the block. + test eax, eax + jnz .error_get_block + + mov eax, ebx ; esi: current directory record + add eax, [ebp + EXTFS.block_size] + + mov [edi + 24], eax + + mov ecx, [edi + 16] ; ecx = first wanted (flags ommited) + + .find_wanted_start: + jecxz .find_wanted_end + + .find_wanted_cycle: + cmp [ebx + EXT2_DIR_STRUC.inode], 0 ; Don't count unused inode in total files. + jz @F + + inc dword [edi + 4] ; EXT2 files in folder. + dec ecx + @@: + movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] + + cmp eax, 12 ; Minimum record length. + jb .error_bad_len + test eax, 0x3 ; Record length must be divisible by four. + jnz .error_bad_len + + sub [esi + EXT2_INODE_STRUC.i_size], eax ; Subtract "processed record" length directly from inode. + add ebx, eax ; Go to next record. + cmp ebx, [edi + 24] ; If not reached the next block, continue. + jb .find_wanted_start + + push .find_wanted_start + .end_block: ; Get the next block. + cmp [esi + EXT2_INODE_STRUC.i_size], 0 + jle .end_dir + + inc dword [edi] ; Number of blocks read. + + ; Read the next block. + push ecx + mov ecx, [edi] + call ext2_get_inode_block + test eax, eax + jnz .error_get_block + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .error_get_block + pop ecx + + mov eax, ebx + add eax, [ebp + EXTFS.block_size] + mov [edi + 24], eax ; Update the end of the current block variable. + ret + + .wanted_end: + loop .find_wanted_cycle ; Skip files till we reach wanted one. + + ; First requisite file. + .find_wanted_end: + mov ecx, [edi + 20] + .wanted_start: ; Look for first_wanted + count. + jecxz .wanted_end + + cmp [ebx + EXT2_DIR_STRUC.inode], 0 ; if (inode == 0): not used; + jz .empty_rec + + ; Increment "files in dir" and "read files" count. + inc dword [edi + 8] + inc dword [edi + 4] + + push edi ecx + mov edi, edx ; Zero out till the name field. + xor eax, eax + mov ecx, 40 / 4 + rep stosd + pop ecx edi + + push ebx edi edx + mov eax, [ebx + EXT2_DIR_STRUC.inode] ; Get the child inode. + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + test eax, eax + jnz .error_read_subinode + + lea edi, [edx + 8] + + mov eax, [ebx + EXT2_INODE_STRUC.i_ctime] ; Convert time in NTFS format. + xor edx, edx + add eax, 3054539008 ; (369 * 365 + 89) * 24 * 3600 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + mov eax, [ebx + EXT2_INODE_STRUC.i_atime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + mov eax, [ebx + EXT2_INODE_STRUC.i_mtime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + pop edx + test [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR ; If folder, don't report size. + jnz @F + + mov eax, [ebx + EXT2_INODE_STRUC.i_size] ; Low size + stosd + mov eax, [ebx + EXT2_INODE_STRUC.i_dir_acl] ; High size + stosd + + xor dword [edx], FS_FT_DIR ; Mark as file. + @@: + xor dword [edx], FS_FT_DIR ; Mark as directory. + + ; Copy name after converting from UTF-8 to CP866. + push ecx esi + mov esi, [esp + 12] + movzx ecx, [esi + EXT2_DIR_STRUC.name_len] + lea edi, [edx + 40] + lea esi, [esi + EXT2_DIR_STRUC.name] + call utf8_to_cp866 + and byte [edi], 0 + pop esi ecx edi ebx + + cmp byte [edx + 40], '.' ; If it begins with ".", mark it as hidden. + jne @F + or dword [edx], FS_FT_HIDDEN + + @@: + add edx, 40 + 264 ; Go to next record. + dec ecx + .empty_rec: + movzx eax, [ebx + EXT2_DIR_STRUC.rec_len] + + cmp eax, 12 ; Illegal length. + jb .error_bad_len + test eax, 0x3 ; Not a multiple of four. + jnz .error_bad_len + + sub [esi + EXT2_INODE_STRUC.i_size], eax ; Subtract directly from the inode. + add ebx, eax + cmp ebx, [edi + 24] ; Are we at the end of the block? + jb .wanted_start + + push .wanted_start + jmp .end_block + + .end_dir: ; End of the directory. + call ext2_unlock + mov edx, [edi + 28] ; Address of where to return data. + mov ebx, [edi + 8] ; EXT2_read_in_folder + mov ecx, [edi + 4] ; EXT2_files_in_folder + mov dword [edx], 1 ; Version + mov [edx + 4], ebx + mov [edx + 8], ecx + + lea esp, [edi + 32] + + xor eax, eax ; Reserved in current implementation. + lea edi, [edx + 12] + mov ecx, 20 / 4 + rep stosd + ret + + .error_bad_len: + mov eax, ERROR_FS_FAIL + + .error_read_subinode: + .error_get_block: + ; Fix the stack. + lea esp, [edi + 32] + + .error_ret: + or ebx, -1 + push eax + call ext2_unlock + pop eax + + ret + + .error_empty_dir: ; inode of folder without blocks. + .error_root: ; Root has to be a folder. + mov eax, ERROR_FS_FAIL + jmp .error_ret + + .error_not_found: ; Directory not found. + mov eax, ERROR_FILE_NOT_FOUND + jmp .error_ret + +;--------------------------------------------------------------------- +; Read file from the hard disk. +; Input: esi + [esp + 4] = points to file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: ebx = bytes read (0xFFFFFFFF -> file not found) +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_Read: + call ext2_lock + cmp byte [esi], 0 + jnz @F + + .this_is_nofile: + call ext2_unlock + or ebx, -1 + mov eax, ERROR_ACCESS_DENIED + ret + + @@: + push ebx + stdcall ext2_inode_find, [esp + 4 + 4] + pop ebx + + mov esi, [ebp + EXTFS.ext2_save_inode] + test eax, eax + jz @F + + call ext2_unlock + or ebx, -1 + mov eax, ERROR_FILE_NOT_FOUND + ret + + @@: + mov ax, [esi + EXT2_INODE_STRUC.i_mode] + and ax, EXT2_S_IFMT ; Leave the file format in AX. + + ; Check if file. + cmp ax, EXT2_S_IFREG + jne .this_is_nofile + + mov edi, [ebx + 16] + mov ecx, [ebx + 12] + + mov eax, [ebx + 4] + mov edx, [ebx + 8] ; edx:eax = start byte number. + + ; Check if file is big enough for us. + cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx + ja .size_greater + jb .size_less + + cmp [esi + EXT2_INODE_STRUC.i_size], eax + ja .size_greater + + .size_less: + call ext2_unlock + xor ebx, ebx + mov eax, ERROR_END_OF_FILE + ret + + @@: + .size_greater: + add eax, ecx ; Get last byte. + adc edx, 0 + + ; Check if we've to read whole file, or till requested. + cmp [esi + EXT2_INODE_STRUC.i_dir_acl], edx + ja .read_till_requested + jb .read_whole_file + cmp [esi + EXT2_INODE_STRUC.i_size], eax + jae .read_till_requested + + .read_whole_file: + push 1 ; Read till the end of file. + mov ecx, [esi + EXT2_INODE_STRUC.i_size] + sub ecx, [ebx + 4] ; To read = (size - starting byte) + jmp @F + + .read_till_requested: + push 0 ; Read as much as requested. + + @@: + ; ecx = bytes to read. + ; edi = return memory + ; [esi] = starting byte. + + push ecx ; Number of bytes to read. + + ; Get part of the first block. + mov edx, [ebx + 8] + mov eax, [ebx + 4] + div [ebp + EXTFS.block_size] + + push eax ; Save block counter to stack. + + push ecx + mov ecx, eax + call ext2_get_inode_block + test eax, eax + jnz .error_at_first_block + + mov ebx, [ebp + EXTFS.ext2_save_block] + mov eax, ecx + call ext2_block_read + test eax, eax + jnz .error_at_first_block + + pop ecx + ; Get index inside block. + add ebx, edx + + neg edx + add edx, [ebp + EXTFS.block_size] ; Get number of bytes in this block. + + ; If it's smaller than total bytes to read, then only one block. + cmp ecx, edx + jbe .only_one_block + + mov eax, ecx + sub eax, edx + mov ecx, edx + + push esi + mov esi, ebx + rep movsb ; Copy part of 1st block. + pop esi + + ; eax -> bytes to read. + .calc_blocks_count: + mov ebx, edi ; Read the block in ebx. + xor edx, edx + div [ebp + EXTFS.block_size] ; Get number of bytes in last block in edx. + mov edi, eax ; Get number of blocks in edi. + + @@: + ; Test if all blocks are done. + test edi, edi + jz .finish_block + + inc dword [esp] + mov ecx, [esp] + call ext2_get_inode_block + + test eax, eax + jnz .error_at_read_cycle + + mov eax, ecx ; ebx already contains desired values. + call ext2_block_read + + test eax, eax + jnz .error_at_read_cycle + + add ebx, [ebp + EXTFS.block_size] + + dec edi + jmp @B + + ; In edx -- number of bytes in the last block. + .finish_block: + test edx, edx + jz .end_read + + pop ecx ; Pop block counter in ECX. + inc ecx + call ext2_get_inode_block + + test eax, eax + jnz .error_at_finish_block + + mov edi, ebx + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + + test eax, eax + jnz .error_at_finish_block + + mov ecx, edx + mov esi, ebx + rep movsb ; Copy last piece of block. + jmp @F + + .end_read: + pop ecx ; Pop block counter in ECX. + @@: + pop ebx ; Number of bytes read. + call ext2_unlock + pop eax ; If we were asked to read more, say EOF. + test eax, eax + jz @F + + mov eax, ERROR_END_OF_FILE + ret + @@: + xor eax, eax + ret + + .only_one_block: + mov esi, ebx + rep movsb ; Copy last piece of block. + jmp .end_read + + .error_at_first_block: + pop edx + .error_at_read_cycle: + pop ebx + .error_at_finish_block: + pop ecx edx + or ebx, -1 + push eax + call ext2_unlock + pop eax + ret + +;--------------------------------------------------------------------- +; Read file information from block device. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_GetFileInfo: + call ext2_lock + mov edx, [ebx + 16] + cmp byte [esi], 0 + jz .is_root + + push edx + stdcall ext2_inode_find, [esp + 4 + 4] + mov ebx, edx + pop edx + + mov esi, [ebp + EXTFS.ext2_save_inode] + test eax, eax + jz @F + + push eax + call ext2_unlock + pop eax + ret + + .is_root: + xor ebx, ebx ; Clear out first char, since we don't want to set hidden flag on root. + mov esi, [ebp + EXTFS.root_inode] + + @@: + xor eax, eax + mov edi, edx + mov ecx, 40/4 + rep stosd ; Zero fill buffer. + + cmp bl, '.' + jne @F + or dword [edx], FS_FT_HIDDEN + + @@: + test [esi + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR + jnz @F ; If a directory, don't put in file size. + + mov eax, [esi + EXT2_INODE_STRUC.i_size] ; Low file size. + mov ebx, [esi + EXT2_INODE_STRUC.i_dir_acl] ; High file size. + mov dword [edx+32], eax + mov dword [edx+36], ebx + + xor dword [edx], FS_FT_DIR ; Next XOR will clean this, to mark it as a file. + @@: + xor dword [edx], FS_FT_DIR ; Mark as directory. + + lea edi, [edx + 8] + + ; Store all time. + mov eax, [esi + EXT2_INODE_STRUC.i_ctime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + mov eax, [esi + EXT2_INODE_STRUC.i_atime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + mov eax, [esi + EXT2_INODE_STRUC.i_mtime] + xor edx, edx + add eax, 3054539008 + adc edx, 2 + call ntfs_datetime_to_bdfe.sec + + call ext2_unlock + xor eax, eax + ret + +;--------------------------------------------------------------------- +; Set file information for block device. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_SetFileInfo: + push edx esi edi ebx + call ext2_lock + mov edx, [ebx + 16] + + ; Is this read-only? + test [ebp + EXTFS.partition_flags], EXT2_RO + jnz .fail + + ; Not supported for root. + cmp byte [esi], 0 + je .fail + + .get_inode: + push edx + stdcall ext2_inode_find, [esp + 4 + 20] + pop edx + + test eax, eax + jnz @F + + ; Save inode number. + push esi + mov esi, [ebp + EXTFS.ext2_save_inode] + + ; From the BDFE, we ignore read-only file flags, hidden file flags; + ; We ignore system file flags, file was archived or not. + + ; Also ignored is file creation time. ext2 stores "inode modification" + ; time in the ctime field, which is updated by the respective inode_write + ; procedure, and any writes on it would be overwritten anyway. + + ; Access time. + lea edi, [esi + EXT2_INODE_STRUC.i_atime] + lea esi, [edx + 16] + call bdfe_to_unix_time + + ; Modification time. + add esi, 8 + add edi, 8 + call bdfe_to_unix_time + + mov ebx, [ebp + EXTFS.ext2_save_inode] ; Get address of inode into ebx. + pop eax ; Get inode number in eax. + call ext2_inode_write ; eax contains error code, if any. + test eax, eax + jnz @F + + call ext2_sb_update + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + @@: + push eax + call ext2_unlock + pop eax + + pop ebx edi esi edx + ret + + .fail: + call ext2_sb_update + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_UNSUPPORTED_FS + jmp @B + +;--------------------------------------------------------------------- +; Set file information for block device. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_Delete: + push ebx ecx edx esi edi + call ext2_lock + + add esi, [esp + 20 + 4] + + ; Can't delete root. + cmp byte [esi], 0 + jz .error_access_denied + + push esi + stdcall ext2_inode_find, 0 + mov ebx, esi + pop esi + + test eax, eax + jnz .error_access_denied + + mov edx, [ebp + EXTFS.ext2_save_inode] + movzx edx, [edx + EXT2_INODE_STRUC.i_mode] + and edx, EXT2_S_IFMT ; Get the mask. + cmp edx, EXT2_S_IFDIR + jne @F ; If not a directory, we don't need to check if it's empty. + + call ext2_dir_empty ; 0 means directory is empty. + + test eax, eax + jnz .error_access_denied + + @@: + ; Find parent. + call ext2_inode_find_parent + test eax, eax + jnz .error_access_denied + mov eax, esi + + ; Save file/dir & parent inode. + push ebx eax + + cmp edx, EXT2_S_IFDIR + jne @F + + ; Unlink '.' + mov eax, [esp + 4] + call ext2_inode_unlink + cmp eax, 0xFFFFFFFF + je .error_stack8 + + ; Unlink '..' + mov eax, [esp + 4] + mov ebx, [esp] + call ext2_inode_unlink + cmp eax, 0xFFFFFFFF + je .error_stack8 + + @@: + pop eax + mov ebx, [esp] + ; Unlink the inode. + call ext2_inode_unlink + cmp eax, 0xFFFFFFFF + je .error_stack4 + + ; If hardlinks aren't zero, shouldn't completely free. + test eax, eax + jz @F + + add esp, 4 + jmp .disk_sync + + @@: + ; Read the inode. + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_save_inode] + call ext2_inode_read + test eax, eax + jnz .error_stack4 + + ; Free inode data. + mov esi, [ebp + EXTFS.ext2_save_inode] + xor ecx, ecx + + @@: + push ecx + call ext2_get_inode_block + test eax, eax + jnz .error_stack8 + mov eax, ecx + pop ecx + + ; If 0, we're done. + test eax, eax + jz @F + + call ext2_block_free + test eax, eax + jnz .error_stack4 + + inc ecx + jmp @B + + @@: + ; Clear the inode, and add deletion time. + mov edi, [ebp + EXTFS.ext2_save_inode] + xor eax, eax + mov ecx, [ebp + EXTFS.inode_size] + rep stosb + + mov edi, [ebp + EXTFS.ext2_save_inode] + add edi, EXT2_INODE_STRUC.i_dtime + call current_unix_time + + ; Write the inode. + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_save_inode] + call ext2_inode_write + test eax, eax + jnz .error_stack4 + + ; Check if directory. + cmp edx, EXT2_S_IFDIR + jne @F + + ; If it is, decrement used_dirs_count. + + ; Get block group. + mov eax, [esp] + dec eax + xor edx, edx + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + + push eax + call ext2_bg_read_desc + test eax, eax + jz .error_stack8 + + dec [eax + EXT2_BLOCK_GROUP_DESC.used_dirs_count] + + pop eax + call ext2_bg_write_desc + + @@: + pop eax + call ext2_inode_free + test eax, eax + jnz .error_access_denied + + .disk_sync: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + .return: + push eax + call ext2_unlock + pop eax + + pop edi esi edx ecx ebx + ret + + .error_stack8: + add esp, 4 + .error_stack4: + add esp, 4 + .error_access_denied: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_ACCESS_DENIED + jmp .return + +;--------------------------------------------------------------------- +; Set file information for block device. +; Input: esi + [esp + 4] = file name. +; ebx = pointer to paramteres from sysfunc 70. +; ebp = pointer to EXTFS structure. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_CreateFolder: + push ebx ecx edx esi edi + call ext2_lock + + add esi, [esp + 20 + 4] + + ; Can't create root, but for CreateFile already existing directory is success. + cmp byte [esi], 0 + jz .success + + push esi + stdcall ext2_inode_find, 0 + pop esi + + ; If the directory is there, we've succeeded. + test eax, eax + jz .success + + ; Find parent. + call ext2_inode_find_parent + test eax, eax + jnz .error + + ; Inode ID for preference. + mov eax, esi + call ext2_inode_alloc + test eax, eax + jnz .error_full + + ; Save allocated inode in EDX; filename is in EDI; parent ID in ESI. + mov edx, ebx + + push edi + + xor al, al + mov edi, [ebp + EXTFS.ext2_temp_inode] + mov ecx, [ebp + EXTFS.inode_size] + rep stosb + + mov edi, [ebp + EXTFS.ext2_temp_inode] + add edi, EXT2_INODE_STRUC.i_atime + call current_unix_time + + add edi, 8 + call current_unix_time + + pop edi + + mov ebx, [ebp + EXTFS.ext2_temp_inode] + mov [ebx + EXT2_INODE_STRUC.i_mode], EXT2_S_IFDIR + mov eax, edx + call ext2_inode_write + test eax, eax + jnz .error + + ; Link to self. + push edx esi + + mov eax, edx + mov ebx, eax + mov dl, EXT2_FT_DIR + mov esi, self_link + call ext2_inode_link + + pop esi edx + + test eax, eax + jnz .error + + ; Link to parent. + push edx esi + + mov eax, ebx + mov ebx, esi + mov dl, EXT2_FT_DIR + mov esi, parent_link + call ext2_inode_link + + pop esi edx + + test eax, eax + jnz .error + + ; Link parent to child. + mov eax, esi + mov ebx, edx + mov esi, edi + mov dl, EXT2_FT_DIR + call ext2_inode_link + test eax, eax + jnz .error + + ; Get block group descriptor for allocated inode's block. + mov eax, ebx + dec eax + xor edx, edx + + ; EAX = block group. + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + mov edx, eax + + call ext2_bg_read_desc + test eax, eax + jz .error + + inc [eax + EXT2_BLOCK_GROUP_DESC.used_dirs_count] + mov eax, edx + call ext2_bg_write_desc + test eax, eax + jnz .error + + .success: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + .return: + push eax + call ext2_unlock + pop eax + + pop edi esi edx ecx ebx + ret + + .error: + call ext2_sb_update + + ; Sync the disk. + mov esi, [ebp + PARTITION.Disk] + call disk_sync ; eax contains error code, if any. + + mov eax, ERROR_ACCESS_DENIED + jmp .return + + .error_full: + mov eax, ERROR_DISK_FULL + jmp .return + +self_link: db ".", 0 +parent_link: db "..", 0 + +ext2_Rewrite: +ext2_Write: +ext2_SetFileEnd: + xor ebx, ebx + mov eax, ERROR_UNSUPPORTED_FS + ret diff --git a/kernel/trunk/fs/ext2/ext2.inc b/kernel/trunk/fs/ext2/ext2.inc new file mode 100644 index 0000000000..36df347808 --- /dev/null +++ b/kernel/trunk/fs/ext2/ext2.inc @@ -0,0 +1,648 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 structures, and macros. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Recommended move to some kernel-wide bitmap handling code (with a bit of abstraction, of course). + +;--------------------------------------------------------------------- +; Clears a bit. +; Input: eax = index into bitmap. +; [EXTFS.ext2_save_block] = address of bitmap. +; ebp = address of EXTFS. +; Output: Bit cleared. +; eax = non-zero, if already cleared. +;--------------------------------------------------------------------- +bitmap_clear_bit: + push ebx ecx edx + + xor edx, edx + mov ecx, 8 + div ecx + + add eax, [ebp + EXTFS.ext2_save_block] + + ; Get the mask. + mov ebx, 1 + mov ecx, edx + shl ebx, cl + + test [eax], ebx + jz .cleared + + not ebx + and [eax], ebx + + xor eax, eax + .return: + pop edx ecx ebx + ret + + ; Already cleared. + .cleared: + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Finds free bit in the bitmap. +; Input: ecx = number of bits in the bitmap. +; [EXTFS.ext2_save_block] = address of bitmap. +; ebp = address of EXTFS. +; Output: eax = index of free bit in the bitmap; marked set. +; 0xFFFFFFFF if no free bit found. +;--------------------------------------------------------------------- +ext2_find_free_bit: +bitmap_find_free_bit: + push esi ebx ecx edx + mov esi, [ebp + EXTFS.ext2_save_block] + + ; Get total DWORDS in eax; total bits in last dword, if any, in edx. + xor edx, edx + mov eax, ecx + mov ecx, 32 + div ecx + + mov ecx, eax + xor eax, eax + push edx + + test ecx, ecx + jz .last_bits + + ; Check in the DWORDS. + .dwords: + mov ebx, [esi] + not ebx + + bsf edx, ebx + + ; If 0, then the original value would be 0xFFFFFFFF, hence no free bits. + jz @F + + ; We found the value. Let's return with it. + add esp, 4 + add eax, edx + jmp .return + + @@: + add esi, 4 + add eax, 32 + loop .dwords + + .last_bits: + ; Check in the last few bits. + pop ecx + test ecx, ecx + jz @F + + mov ebx, [esi] + not ebx + bsf ebx, edx + + ; If 0, no free bits. + jz @F + + ; If free bit is greater than the last known bit, then error. + cmp edx, ecx + jg @F + + add eax, edx + jmp .return + + @@: + ; Didn't find any free bits. + xor eax, eax + not eax + jmp @F + + .return: + mov ecx, edx + mov edx, 1 + shl edx, cl + or [esi], edx + + @@: + pop edx ecx ebx esi + ret + +; Recommended move to some kernel-wide string handling code. +;--------------------------------------------------------------------- +; Find the length of a string. +; Input: esi = source. +; Output: length in ecx +;--------------------------------------------------------------------- +strlen: + push eax esi + xor ecx, ecx + + @@: + lodsb + test al, al + jz .ret + + inc ecx + jmp @B + + .ret: + pop esi eax + ret + +;--------------------------------------------------------------------- +; Convert UTF-8 string to ASCII-string (codepage 866) +; Input: esi = source. +; edi = buffer. +; ecx = length of source. +; Output: destroys eax, esi, edi +;--------------------------------------------------------------------- +utf8_to_cp866: + ; Check for zero-length string. + jecxz .return + + .start: + lodsw + cmp al, 0x80 + jb .ascii + + xchg al, ah ; Big-endian. + cmp ax, 0xd080 + jz .yo1 + + cmp ax, 0xd191 + jz .yo2 + + cmp ax, 0xd090 + jb .unk + + cmp ax, 0xd180 + jb .rus1 + + cmp ax, 0xd190 + jb .rus2 + + .unk: + mov al, '_' + jmp .doit + + .yo1: + mov al, 0xf0 ; Ё capital. + jmp .doit + + .yo2: + mov al, 0xf1 ; ё small. + jmp .doit + + .rus1: + sub ax, 0xd090 - 0x80 + jmp .doit + + .rus2: + sub ax, 0xd18f - 0xEF + + .doit: + stosb + sub ecx, 2 + ja .start + ret + + .ascii: + stosb + dec esi + dec ecx + jnz .start + + .return: + ret + +; Recommended move to some kernel-wide time handling code. + +; Total cumulative seconds till each month. +cumulative_seconds_in_month: + .january: dd 0 * (60 * 60 * 24) + .february: dd 31 * (60 * 60 * 24) + .march: dd 59 * (60 * 60 * 24) + .april: dd 90 * (60 * 60 * 24) + .may: dd 120 * (60 * 60 * 24) + .june: dd 151 * (60 * 60 * 24) + .july: dd 181 * (60 * 60 * 24) + .august: dd 212 * (60 * 60 * 24) + .september: dd 243 * (60 * 60 * 24) + .october: dd 273 * (60 * 60 * 24) + .november: dd 304 * (60 * 60 * 24) + .december: dd 334 * (60 * 60 * 24) + +current_bdfe_time: + dd 0 +current_bdfe_date: + dd 0 + +;--------------------------------------------------------------------- +; Stores current unix time. +; Input: edi = buffer to output Unix time. +;--------------------------------------------------------------------- +current_unix_time: + push eax esi + mov esi, current_bdfe_time + + ; Just a small observation: + ; The CMOS is a pretty bad source to get time from. One shouldn't rely on it, + ; since it messes up the time by tiny bits. Of course, this is all technical, + ; but one can look it up on the osdev wiki. What is better is to get the time + ; from CMOS during boot, then update system time using a more accurate timer. + ; I'll probably add that after the Summer of Code, so TODO! TODO! TODO!. + + ; Get time from CMOS. + ; Seconds. + mov al, 0x00 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 0], al + + ; Minute. + mov al, 0x02 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 1], al + + ; Hour. + mov al, 0x04 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 2], al + + ; Get date. + + ; Day. + mov al, 0x7 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 4], al + + ; Month. + mov al, 0x8 + out 0x70, al + in al, 0x71 + call bcd2bin + mov [esi + 5], al + + ; Year. + mov al, 0x9 + out 0x70, al + in al, 0x71 + call bcd2bin + add ax, 2000 ; CMOS only returns last two digits. + ; Note that everywhere in KolibriOS this is used. + ; This is hacky, since the RTC can be incorrectly set + ; to something before 2000. + mov [esi + 6], ax + + call bdfe_to_unix_time + pop esi eax + ret + +;--------------------------------------------------------------------- +; Convert time+date from BDFE to Unix time. +; Input: esi = pointer to BDFE time+date. +; edi = buffer to output Unix time. +;--------------------------------------------------------------------- +bdfe_to_unix_time: + push eax ebx ecx edx + mov dword[edi], 0x00000000 + + ; The minimum representable time is 1901-12-13. + cmp word[esi + 6], 1901 + jb .ret + jg .max + + cmp byte[esi + 5], 12 + jb .ret + + cmp byte[esi + 4], 13 + jbe .ret + jg .convert + + ; Check if it is more than the maximum representable time. + .max: + ; The maximum representable time is 2038-01-19. + cmp word[esi + 6], 2038 + jg .ret + jb .convert + + cmp byte[esi + 5], 1 + jg .ret + + cmp byte[esi + 4], 19 + jge .ret + + ; Convert the time. + .convert: + ; Get if current year is leap year in ECX. + xor ecx, ecx + mov ebx, 4 + xor edx, edx + + cmp word[esi + 6], 1970 + jb .negative + + movzx eax, word[esi + 6] ; Year. + cmp byte[esi + 5], 3 ; If the month is less than March, than that year doesn't matter. + jge @F + + test eax, 3 + ; Not a leap year. + jnz @F + + inc ecx + @@: + ; Number of leap years between two years = ((end date - 1)/4) - (1970/4) + dec eax + div ebx + sub eax, 1970/4 + + ; EAX is the number of leap years. + add eax, ecx + mov ecx, (60 * 60 * 24) ; Seconds in a day. + mul ecx + + ; Account for leap years, i.e., one day extra for each. + add [edi], eax + + ; Get total days in EAX. + movzx eax, byte[esi + 4] + dec eax + mul ecx + + ; Account for days. + add [edi], eax + + ; Account for month. + movzx eax, byte[esi + 5] + dec eax + mov eax, [cumulative_seconds_in_month + (eax * 4)] + add [edi], eax + + ; Account for year. + movzx eax, word[esi + 6] + sub eax, 1970 + mov ecx, (60 * 60 * 24) * 365 ; Seconds in a year. + mul ecx + add [edi], eax + + ; Seconds. + movzx eax, byte[esi + 0] + add [edi], eax + + ; Minutes. + movzx eax, byte[esi + 1] + mov ecx, 60 + mul ecx + add [edi], eax + + ; Hours. + movzx eax, byte[esi + 2] + mov ecx, (60 * 60) + mul ecx + add [edi], eax + + ; The time wanted is before the epoch; handle it here. + .negative: + ; TODO. + + .ret: + pop edx ecx ebx eax + ret + +; Recommended move to some kernel-wide alloc handling code. +macro KERNEL_ALLOC store, label +{ + call kernel_alloc + mov store, eax + test eax, eax + jz label +} + +macro KERNEL_FREE data, label +{ + cmp data, 0 + jz label + push data + call kernel_free +} + +struct EXTFS PARTITION + lock MUTEX + partition_flags dd ? + log_block_size dd ? + block_size dd ? + count_block_in_block dd ? + blocks_per_group dd ? + global_desc_table dd ? + root_inode dd ? ; Pointer to root inode in memory. + inode_size dd ? + count_pointer_in_block dd ? ; (block_size / 4) + count_pointer_in_block_square dd ? ; (block_size / 4)**2 + ext2_save_block dd ? ; Block for 1 global procedure. + ext2_temp_block dd ? ; Block for small procedures. + ext2_save_inode dd ? ; inode for global procedures. + ext2_temp_inode dd ? ; inode for small procedures. + groups_count dd ? + superblock rd 1024/4 +ends + +; EXT2 revisions. +EXT2_GOOD_OLD_REV = 0 + +; For fs_type. +FS_TYPE_UNDEFINED = 0 +FS_TYPE_EXT = 2 + +; Some set inodes. +EXT2_BAD_INO = 1 +EXT2_ROOT_INO = 2 +EXT2_ACL_IDX_INO = 3 +EXT2_ACL_DATA_INO = 4 +EXT2_BOOT_LOADER_INO = 5 +EXT2_UNDEL_DIR_INO = 6 + +; EXT2_SUPER_MAGIC. +EXT2_SUPER_MAGIC = 0xEF53 +EXT2_VALID_FS = 1 + +; Flags defining i_mode values. +EXT2_S_IFMT = 0xF000 ; Mask for file type. + +EXT2_S_IFREG = 0x8000 ; Regular file. +EXT2_S_IFDIR = 0x4000 ; Directory. + +; File type defining values in directory entry. +EXT2_FT_REG_FILE = 1 ; Regular file. +EXT2_FT_DIR = 2 ; Directory. + +; Flags used by KolibriOS. +FS_FT_HIDDEN = 2 +FS_FT_DIR = 0x10 ; Directory. + +; ext2 partition flags. +EXT2_RO = 0x01 + +FS_FT_ASCII = 0 ; Name in ASCII. +FS_FT_UNICODE = 1 ; Name in Unicode. + +EXT2_FEATURE_INCOMPAT_FILETYPE = 0x0002 ; Have file type in directory entry. +EXT4_FEATURE_INCOMPAT_EXTENTS = 0x0040 ; Extents. +EXT4_FEATURE_INCOMPAT_FLEX_BG = 0x0200 ; Flexible block groups. + +EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER = 0x0001 ; Sparse Superblock +EXT2_FEATURE_RO_COMPAT_LARGE_FILE = 0x0002 ; Large file support (64-bit file size) + +; Implemented ext[2,3,4] features. +EXT4_FEATURE_INCOMPAT_SUPP = EXT2_FEATURE_INCOMPAT_FILETYPE \ + or EXT4_FEATURE_INCOMPAT_EXTENTS \ + or EXT4_FEATURE_INCOMPAT_FLEX_BG + +; Implemented features which otherwise require "read-only" mount. +EXT2_FEATURE_RO_COMPAT_SUPP = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER \ + or EXT2_FEATURE_RO_COMPAT_LARGE_FILE + +; ext4 features not support for write. +EXT4_FEATURE_INCOMPAT_W_NOT_SUPP = EXT4_FEATURE_INCOMPAT_EXTENTS \ + or EXT4_FEATURE_INCOMPAT_FLEX_BG + +; Flags specified in i_flags. +EXT2_EXTENTS_FL = 0x00080000 ; Extents. + +struct EXT2_INODE_STRUC + i_mode dw ? + i_uid dw ? + i_size dd ? + i_atime dd ? + i_ctime dd ? + i_mtime dd ? + i_dtime dd ? + i_gid dw ? + i_links_count dw ? + i_blocks dd ? + i_flags dd ? + i_osd1 dd ? + i_block rd 15 + i_generation dd ? + i_file_acl dd ? + i_dir_acl dd ? + i_faddr dd ? + i_osd2 dd ? ; 12 bytes. +ends + +struct EXT2_DIR_STRUC + inode dd ? + rec_len dw ? + name_len db ? + file_type db ? + name db ? ; 255 (max) bytes. +ends + +struct EXT2_BLOCK_GROUP_DESC + block_bitmap dd ? ; +0 + inode_bitmap dd ? ; +4 + inode_table dd ? ; +8 + free_blocks_count dw ? ; +12 + free_inodes_count dw ? ; +14 + used_dirs_count dw ? ; +16 + pad dw ? ; +18 + reserved rb 12 ; +20 +ends + +struct EXT2_SB_STRUC + inodes_count dd ? ; +0 + blocks_count dd ? ; +4 + r_block_count dd ? ; +8 + free_block_count dd ? ; +12 + free_inodes_count dd ? ; +16 + first_data_block dd ? ; +20 + log_block_size dd ? ; +24 + log_frag_size dd ? ; +28 + blocks_per_group dd ? ; +32 + frags_per_group dd ? ; +36 + inodes_per_group dd ? ; +40 + mtime dd ? ; +44 + wtime dd ? ; +48 + mnt_count dw ? ; +52 + max_mnt_count dw ? ; +54 + magic dw ? ; +56 + state dw ? ; +58 + errors dw ? ; +60 + minor_rev_level dw ? ; +62 + lastcheck dd ? ; +64 + check_intervals dd ? ; +68 + creator_os dd ? ; +72 + rev_level dd ? ; +76 + def_resuid dw ? ; +80 + def_resgid dw ? ; +82 + first_ino dd ? ; +84 + inode_size dw ? ; +88 + block_group_nr dw ? ; +90 + feature_compat dd ? ; +92 + feature_incompat dd ? ; +96 + feature_ro_compat dd ? ; +100 + uuid rb 16 ; +104 + volume_name rb 16 ; +120 + last_mounted rb 64 ; +136 + algo_bitmap dd ? ; +200 + prealloc_blocks db ? ; +204 + preallock_dir_blocks db ? ; +205 + reserved_gdt_blocks dw ? ; +206 + journal_uuid rb 16 ; +208 + journal_inum dd ? ; +224 + journal_dev dd ? ; +228 + last_orphan dd ? ; +232 + hash_seed rd 4 ; +236 + def_hash_version db ? ; +252 + reserved rb 3 ; +253 (reserved) + default_mount_options dd ? ; +256 + first_meta_bg dd ? ; +260 + mkfs_time dd ? ; +264 + jnl_blocks rd 17 ; +268 + blocks_count_hi dd ? ; +336 + r_blocks_count_hi dd ? ; +340 + free_blocks_count_hi dd ? ; +344 + min_extra_isize dw ? ; +348 + want_extra_isize dw ? ; +350 + flags dd ? ; +352 + raid_stride dw ? ; +356 + mmp_interval dw ? ; +358 + mmp_block dq ? ; +360 + raid_stripe_width dd ? ; +368 + log_groups_per_flex db ? ; +372 +ends + +; Header block extents. +struct EXT4_EXTENT_HEADER + eh_magic dw ? ; Magic value of 0xF30A, for ext4. + eh_entries dw ? ; Number of blocks covered by the extent. + eh_max dw ? ; Capacity of entries. + eh_depth dw ? ; Tree depth (if 0, extents in the array are not extent indexes) + eh_generation dd ? ; ??? +ends + +; Extent. +struct EXT4_EXTENT + ee_block dd ? ; First logical block extent covers. + ee_len dw ? ; Number of blocks covered by extent. + ee_start_hi dw ? ; Upper 16 bits of 48-bit address (unused in KOS) + ee_start_lo dd ? ; Lower 32 bits of 48-bit address. +ends + +; Index on-disk structure; pointer to block of extents/indexes. +struct EXT4_EXTENT_IDX + ei_block dd ? ; Covers logical blocks from here. + ei_leaf_lo dd ? ; Lower 32-bits of pointer to the physical block of the next level. + ei_leaf_hi dw ? ; Higher 16-bits (unused in KOS). + ei_unused dw ? ; Reserved. +ends \ No newline at end of file diff --git a/kernel/trunk/fs/ext2/inode.asm b/kernel/trunk/fs/ext2/inode.asm new file mode 100644 index 0000000000..c9d4c5a464 --- /dev/null +++ b/kernel/trunk/fs/ext2/inode.asm @@ -0,0 +1,1175 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains ext2 inode handling code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; KSOC_EXT2_WRITE_END_TODO: clean this up. +;--------------------------------------------------------------------- +; Receives block number from extent-based inode. +; Input: ecx = number of block in inode +; esi = address of extent header +; ebp = pointer to EXTFS +; Output: ecx = address of next block, if successful +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext4_block_recursive_search: + cmp word [esi + EXT4_EXTENT_HEADER.eh_magic], 0xF30A ;EXT4_EXT_MAGIC + jne .fail + + movzx ebx, [esi + EXT4_EXTENT_HEADER.eh_entries] + add esi, sizeof.EXT4_EXTENT_HEADER + cmp word [esi - sizeof.EXT4_EXTENT_HEADER + EXT4_EXTENT_HEADER.eh_depth], 0 + je .leaf_block ;листовой ли это блок? + + ;не листовой блок, а индексный ; eax - ext4_extent_idx + test ebx, ebx + jz .fail ;пустой индексный блок -> ошибка + + ;цикл по индексам экстентов + @@: + cmp ebx, 1 ;у индексов не хранится длина, + je .end_search_index ;поэтому, если остался последний - то это нужный + + cmp ecx, [esi + EXT4_EXTENT_IDX.ei_block] + jb .fail + + cmp ecx, [esi + sizeof.EXT4_EXTENT_IDX + EXT4_EXTENT_IDX.ei_block] ;блок слeдующего индекса + jb .end_search_index ;следующий дальше - значит текущий, то что нам нужен + + add esi, sizeof.EXT4_EXTENT_IDX + dec ebx + jmp @B + + .end_search_index: + ;ebp указывает на нужный extent_idx, считываем следующий блок + mov ebx, [ebp + EXTFS.ext2_temp_block] + mov eax, [esi + EXT4_EXTENT_IDX.ei_leaf_lo] + call ext2_block_read + test eax, eax + jnz .fail + mov esi, ebx + jmp ext4_block_recursive_search ;рекурсивно прыгаем в начало + + .leaf_block: ;листовой блок esi - ext4_extent + ;цикл по экстентам + @@: + test ebx, ebx + jz .fail ;ни один узел не подошел - ошибка + + mov edx, [esi + EXT4_EXTENT.ee_block] + cmp ecx, edx + jb .fail ;если меньше, значит он был в предыдущих блоках -> ошибка + + movzx edi, [esi + EXT4_EXTENT.ee_len] + add edx, edi + cmp ecx, edx + jb .end_search_extent ;нашли нужный блок + + add esi, sizeof.EXT4_EXTENT + dec ebx + jmp @B + + .end_search_extent: + mov edx, [esi + EXT4_EXTENT.ee_start_lo] + sub ecx, [esi + EXT4_EXTENT.ee_block] ;разница в ext4 блоках + add ecx, edx + xor eax, eax + ret + + .fail: + mov eax, ERROR_FS_FAIL + ret + +;--------------------------------------------------------------------- +; Sets block ID for indirect-addressing inode. +; Input: ecx = index of block in inode +; edi = block ID to set to +; esi = address of inode +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_set_inode_block: + push ebx ecx edx + + ; 0 to 11: direct blocks. + cmp ecx, 12 + jb .direct_block + + ; Indirect blocks + sub ecx, 12 + cmp ecx, [ebp + EXTFS.count_pointer_in_block] + jb .indirect_block + + ; Double indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block] + cmp ecx, [ebp + EXTFS.count_pointer_in_block_square] + jb .double_indirect_block + + ; Triple indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block_square] + + ; Get triply-indirect block in temp_block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4] + test eax, eax + jnz @F + + ; TODO: fix to have correct preference. + mov eax, EXT2_ROOT_INO + call ext2_block_calloc + test eax, eax + jnz .fail_alloc + + mov [esi + EXT2_INODE_STRUC.i_block + 14*4], ebx + mov eax, ebx + + @@: + push eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + ; Get index in triply-indirect block. + xor edx, edx + mov eax, ecx + div [ebp + EXTFS.count_pointer_in_block_square] + + ; eax: index in triply-indirect block, edx: index in doubly-indirect block. + lea ecx, [ebx + eax*4] + mov eax, [ebx + eax*4] + test eax, eax + jnz @F + + mov eax, EXT2_ROOT_INO + call ext2_block_calloc + test eax, eax + jnz .fail_alloc_4 + + mov [ecx], ebx + + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .fail_alloc_4 + + mov eax, [ecx] + @@: + mov [esp], eax + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + mov eax, edx + jmp @F + + .double_indirect_block: + ; Get doubly-indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4] + test eax, eax + jnz .double_indirect_present + + ; TODO: fix to have correct preference. + mov eax, EXT2_ROOT_INO + call ext2_block_calloc + test eax, eax + jnz .fail_alloc + + mov [esi + EXT2_INODE_STRUC.i_block + 13*4], ebx + mov eax, ebx + + .double_indirect_present: + ; Save block we're at. + push eax + + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + mov eax, ecx + @@: + xor edx, edx + div [ebp + EXTFS.count_pointer_in_block] + + ; eax: index in doubly-indirect block, edx: index in indirect block. + lea ecx, [ebx + edx*4] + push ecx + + lea ecx, [ebx + eax*4] + cmp dword[ecx], 0 + jne @F + + ; TODO: fix to have correct preference. + mov eax, EXT2_ROOT_INO + call ext2_block_calloc + test eax, eax + jnz .fail_alloc_8 + + mov [ecx], ebx + + mov eax, [esp + 4] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .fail_alloc_8 + + @@: + mov eax, [ecx] + push eax + call ext2_block_read + test eax, eax + jnz .fail_alloc_12 + + pop eax + pop ecx + mov [ecx], edi + call ext2_block_write + + add esp, 4 + jmp .return + + .indirect_block: + ; Get index of indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4] + test eax, eax + jnz @F + + ; TODO: fix to have correct preference. + mov eax, EXT2_ROOT_INO + call ext2_block_calloc + test eax, eax + jnz .fail_alloc + + mov [esi + EXT2_INODE_STRUC.i_block + 12*4], ebx + mov eax, ebx + + @@: + push eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_alloc_4 + + ; Get the block ID. + mov [ebx + ecx*4], edi + pop eax + call ext2_block_write + jmp .return + + .direct_block: + mov [esi + EXT2_INODE_STRUC.i_block + ecx*4], edi + xor eax, eax + + .return: + pop edx ecx ebx + ret + + .fail_alloc: + xor eax, eax + not eax + jmp .return + + .fail_alloc_12: + add esp, 4 + .fail_alloc_8: + add esp, 4 + .fail_alloc_4: + add esp, 4 + jmp .fail_alloc + +;--------------------------------------------------------------------- +; Receives block ID from indirect-addressing inode. +; Input: ecx = index of block in inode +; esi = address of inode +; ebp = pointer to EXTFS +; Output: ecx = block ID, if successful +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_get_inode_block: + ; If inode is extent-based, use ext4_block_recursive_search. + test [esi + EXT2_INODE_STRUC.i_flags], EXT2_EXTENTS_FL + jz @F + + pushad + + ; Get extent header in EBP. + add esi, EXT2_INODE_STRUC.i_block + call ext4_block_recursive_search + mov PUSHAD_ECX, ecx + mov PUSHAD_EAX, eax + + popad + ret + + @@: + ; 0 to 11: direct blocks. + cmp ecx, 12 + jb .get_direct_block + + ; Indirect blocks + sub ecx, 12 + cmp ecx, [ebp + EXTFS.count_pointer_in_block] + jb .get_indirect_block + + ; Double indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block] + cmp ecx, [ebp + EXTFS.count_pointer_in_block_square] + jb .get_double_indirect_block + + ; Triple indirect blocks. + sub ecx, [ebp + EXTFS.count_pointer_in_block_square] + push edx ebx + + ; Get triply-indirect block in temp_block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 14*4] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail + + ; Get index in triply-indirect block. + xor edx, edx + mov eax, ecx + div [ebp + EXTFS.count_pointer_in_block_square] + + ; eax: index in triply-indirect block, edx: index in doubly-indirect block. + mov eax, [ebx + eax*4] + call ext2_block_read + test eax, eax + jnz .fail + + mov eax, edx + jmp @F + + .get_double_indirect_block: + push edx ebx + + ; Get doubly-indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 13*4] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail + + mov eax, ecx + @@: + xor edx, edx + div [ebp + EXTFS.count_pointer_in_block] + + ; eax: index in doubly-indirect block, edx: index in indirect block. + mov eax, [ebx + eax*4] + + call ext2_block_read + test eax, eax + jnz .fail + + mov ecx, [ebx + edx*4] + .fail: + pop ebx edx + + ret + + .get_indirect_block: + push ebx + + ; Get index of indirect block. + mov eax, [esi + EXT2_INODE_STRUC.i_block + 12*4] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz @F + + ; Get the block ID. + mov ecx, [ebx + ecx*4] + @@: + pop ebx + + ret + + .get_direct_block: + mov ecx, [esi + EXT2_INODE_STRUC.i_block + ecx*4] + xor eax, eax + + ret + +;--------------------------------------------------------------------- +; Get block containing inode. +; Input: eax = inode number. +; ebp = pointer to EXTFS. +; Output: ebx = block (hard disk) containing inode. +; edx = index inside block. +; eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_read_block_of_inode: + pushad + + dec eax + xor edx, edx + + ; EAX = block group. + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + + push edx ; Index in group. + + mov edx, 32 + mul edx ; Get index of descriptor in global_desc_table. + + ; eax: inode group offset relative to global descriptor table start + ; Find the block this block descriptor is in. + div [ebp + EXTFS.block_size] + add eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + inc eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .return + + add ebx, edx ; edx: local index of descriptor inside block + mov eax, [ebx + EXT2_BLOCK_GROUP_DESC.inode_table] ; Block number of inode table - in ext2 terms. + mov ecx, [ebp + EXTFS.log_block_size] + shl eax, cl + + ; eax: points to inode table on HDD. + mov esi, eax + + ; Add local address of inode. + pop eax + mov ecx, [ebp + EXTFS.inode_size] + mul ecx ; (index * inode_size) + + mov ebp, 512 + div ebp ; Divide by hard disk block size. + + add eax, esi ; Found block to read. + mov ebx, eax ; Get it inside ebx. + + xor eax, eax + .return: + mov PUSHAD_EAX, eax + mov PUSHAD_EBX, ebx + mov PUSHAD_EDX, edx + + popad + ret + +;--------------------------------------------------------------------- +; Sets content of inode by number. +; Input: eax = inode number. +; ebx = address from where to write inode content. +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_write: + push edx edi esi ecx ebx + mov esi, ebx + + ; Ext2 actually stores time of modification of inode in ctime. + lea edi, [ebx + EXT2_INODE_STRUC.i_ctime] + call current_unix_time + + ; Get block where inode is situated. + call ext2_read_block_of_inode + test eax, eax + jnz .error + + mov eax, ebx ; Get block into EAX. + mov ebx, [ebp + EXTFS.ext2_temp_block] + + mov ecx, eax ; Save block. + call fs_read32_sys + test eax, eax + jz @F + + .error: + mov eax, ERROR_DEVICE + jmp .return + + @@: + mov eax, ecx + mov ecx, [ebp + EXTFS.inode_size] + mov edi, edx ; The index into the block. + add edi, ebx + rep movsb + + ; Write the block. + call fs_write32_sys + + .return: + pop ebx ecx esi edi edx + ret + +;--------------------------------------------------------------------- +; Get content of inode by number. +; Input: eax = inode number. +; ebx = address where to store inode content. +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +;--------------------------------------------------------------------- +ext2_inode_read: + push edx edi esi ecx ebx + mov edi, ebx + + ; Get block where inode is situated. + call ext2_read_block_of_inode + test eax, eax + jnz .error + + mov eax, ebx ; Get block into EAX. + mov ebx, [ebp + EXTFS.ext2_temp_block] + call fs_read32_sys + test eax, eax + jz @F + + .error: + mov eax, ERROR_DEVICE + jmp .return + + @@: + mov ecx, [ebp + EXTFS.inode_size] + mov esi, edx ; The index into the inode. + add esi, ebx + rep movsb + + xor eax, eax + .return: + pop ebx ecx esi edi edx + ret + +;--------------------------------------------------------------------- +; Seek inode from the path. +; Input: esi + [esp + 4] = name. +; ebp = pointer to EXTFS. +; Output: eax = error code (0 implies no error) +; esi = inode number. +; dl = first byte of file/folder name. +; [ext2_data.ext2_save_inode] stores the inode. +;--------------------------------------------------------------------- +ext2_inode_find: + mov edx, [ebp + EXTFS.root_inode] + + ; Check for empty root. + cmp [edx + EXT2_INODE_STRUC.i_blocks], 0 + je .error_empty_root + + ; Check for root. + cmp byte[esi], 0 + jne .next_path_part + + push edi ecx + mov esi, [ebp + EXTFS.root_inode] + mov edi, [ebp + EXTFS.ext2_save_inode] + mov ecx, [ebp + EXTFS.inode_size] + rep movsb + pop ecx edi + + xor eax, eax + xor dl, dl + mov esi, EXT2_ROOT_INO + ret 4 + + .next_path_part: + push [edx + EXT2_INODE_STRUC.i_blocks] + xor ecx, ecx + + .folder_block_cycle: + push ecx + xchg esi, edx + call ext2_get_inode_block + xchg esi, edx + test eax, eax + jnz .error_get_inode_block + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_save_block] ; Get directory records from directory. + call ext2_block_read + test eax, eax + jnz .error_get_block + + push esi + push edx + call ext2_block_find_parent + pop edx + pop edi ecx + + cmp edi, esi ; Did something match? + je .next_folder_block ; No, move to next block. + + cmp byte [esi], 0 ; Reached the "end" of path successfully. + jnz @F + cmp dword[esp + 8], 0 + je .get_inode_ret + mov esi, [esp + 8] + mov dword[esp + 8], 0 + + @@: + mov eax, [ebx + EXT2_DIR_STRUC.inode] + mov ebx, [ebp + EXTFS.ext2_save_inode] + call ext2_inode_read + test eax, eax + jnz .error_get_inode + + movzx eax, [ebx + EXT2_INODE_STRUC.i_mode] + and eax, EXT2_S_IFMT ; Get the mask. + cmp eax, EXT2_S_IFDIR + jne .not_found ; Matched till part, but directory entry we got doesn't point to folder. + + pop ecx ; Stack top contains number of blocks. + mov edx, ebx + jmp .next_path_part + + .next_folder_block: + ; Next block in current folder. + pop eax ; Get blocks counter. + sub eax, [ebp + EXTFS.count_block_in_block] + jle .not_found + + push eax + inc ecx + jmp .folder_block_cycle + + .not_found: + mov eax, ERROR_FILE_NOT_FOUND + ret 4 + + .get_inode_ret: + pop ecx ; Stack top contains number of blocks. + + mov dl, [ebx + EXT2_DIR_STRUC.name] ; First character of file-name. + mov eax, [ebx + EXT2_DIR_STRUC.inode] + mov ebx, [ebp + EXTFS.ext2_save_inode] + mov esi, eax + + ; If we can't get the inode, eax contains the error. + call ext2_inode_read + ret 4 + + .error_get_inode_block: + .error_get_block: + pop ecx + .error_get_inode: + pop ebx + .error_empty_root: + mov eax, ERROR_FS_FAIL + ret 4 + +;--------------------------------------------------------------------- +; Seeks parent inode from path. +; Input: esi = path. +; ebp = pointer to EXTFS. +; Output: eax = error code. +; esi = inode. +; edi = pointer to file name. +;--------------------------------------------------------------------- +ext2_inode_find_parent: + push esi + xor edi, edi + + .loop: + cmp byte[esi], '/' + jne @F + + mov edi, esi + inc esi + jmp .loop + + @@: + inc esi + cmp byte[esi - 1], 0 + jne .loop + + ; If it was just a filename (without any additional directories), + ; use the last byte as "parent path". + cmp edi, 0 + jne @F + + pop edi + dec esi + jmp .get_inode + + ; It had some additional directories, so handle it that way. + @@: + mov byte[edi], 0 + inc edi + pop esi + + .get_inode: + push ebx edx + stdcall ext2_inode_find, 0 + pop edx ebx + + .return: + ret + +;--------------------------------------------------------------------- +; Link an inode. +; Input: eax = inode on which to link. +; ebx = inode to link. +; dl = file type. +; esi = name. +; ebp = pointer to EXTFS. +; Output: eax = error code. +;--------------------------------------------------------------------- +ext2_inode_link: + push eax + push esi edi ebx ecx edx + + ; Get string length, and then directory entry structure size. + call strlen + add ecx, 8 + + push esi ebx ecx + + xor ecx, ecx + mov esi, [ebp + EXTFS.ext2_temp_inode] + mov ebx, esi + + call ext2_inode_read + test eax, eax + jnz .error_inode_read + + ; Get the maximum addressible i_block index by (i_blocks/(2 << s_log_block_size)). + ; Note that i_blocks contains number of reserved 512B blocks, which is why we've to + ; find out the ext2 blocks. + mov eax, 2 + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + shl eax, cl + mov ecx, eax + + mov eax, [esi + EXT2_INODE_STRUC.i_blocks] + xor edx, edx + + div ecx + + ; EAX is the maximum index inside i_block we can go. + push eax + push dword 0 + + ; ECX contains the "block inside i_block" index. + xor ecx, ecx + @@: + call ext2_get_inode_block + test eax, eax + jnz .error_get_inode_block + test ecx, ecx + jz .alloc_block ; We've got no block here, so allocate one. + + push ecx ; Save block number. + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .error_block_read + + ; Try to find free space in current block. + mov ecx, [esp + 8] + call ext2_block_find_fspace + test eax, eax + jz .found + + cmp eax, 0x00000001 + jne .next_iter + + ; This block wasn't linking to the next block, so fix that, and use the next one. + ; Write the block. + pop eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .error_get_inode_block + + inc dword [esp] + mov ecx, [esp] + call ext2_get_inode_block + test eax, eax + jnz .error_get_inode_block + + test ecx, ecx + jz .alloc_block + + ; If there was a block there, prepare it for our use! + push ecx + jmp .prepare_block + + .next_iter: + add esp, 4 + + inc dword [esp] + mov ecx, [esp] + cmp ecx, [esp + 4] + jbe @B + + .alloc_block: + mov eax, [esp + 12] ; Get inode ID of what we're linking. + call ext2_block_calloc + test eax, eax + jnz .error_get_inode_block + + mov ecx, [esp] ; Get the index of it inside the inode. + mov edi, ebx ; And what to set to. + call ext2_set_inode_block + test eax, eax + jnz .error_get_inode_block + + ; Update i_size. + mov eax, [ebp + EXTFS.block_size] + add [esi + EXT2_INODE_STRUC.i_size], eax + + ; Update i_blocks. + mov ecx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.log_block_size] + mov eax, 2 + shl eax, cl + add [esi + EXT2_INODE_STRUC.i_blocks], eax + + ; Write the inode. + mov eax, [esp + 40] + mov ebx, esi + call ext2_inode_write + test eax, eax + jnz .error_get_inode_block + + push edi ; Save the block we just allocated. + + ; If we've allocated/using-old-block outside of loop, prepare it. + .prepare_block: + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .error_block_read + + mov edi, ebx + mov eax, [ebp + EXTFS.block_size] + mov [edi + EXT2_DIR_STRUC.rec_len], ax + + .found: + pop edx + add esp, 8 + pop ecx ebx esi + + push ebx + mov [edi], ebx ; Save inode. + + mov eax, [esp + 4] ; Get EDX off the stack -- contains the file_type. + cmp [ebp + EXTFS.superblock + EXT2_SB_STRUC.rev_level], EXT2_GOOD_OLD_REV + je .name + + ; Set the file-type. + mov [edi + EXT2_DIR_STRUC.file_type], al + + .name: + ; Save name. + sub ecx, 8 + mov [edi + EXT2_DIR_STRUC.name_len], cl + add edi, 8 + rep movsb + + ; Write block. + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .error_block_write + + mov eax, [esp] + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + test eax, eax + jnz .error_block_write + + pop eax + inc [ebx + EXT2_INODE_STRUC.i_links_count] + call ext2_inode_write + test eax, eax + jnz .error + + xor eax, eax + .ret: + pop edx ecx ebx edi esi + add esp, 4 + ret + + .error_block_read: + add esp, 4 + .error_get_inode_block: + add esp, 8 + .error_inode_read: + add esp, 8 + .error_block_write: + add esp, 4 + .error: + xor eax, eax + not eax + jmp .ret + +;--------------------------------------------------------------------- +; Unlink an inode. +; Input: eax = inode from which to unlink. +; ebx = inode to unlink. +; ebp = pointer to EXTFS. +; Output: eax = number of links to inode, after unlinking (0xFFFFFFFF implies error) +;--------------------------------------------------------------------- +ext2_inode_unlink: + push ebx ecx edx esi edi + + push ebx + mov ebx, [ebp + EXTFS.ext2_temp_inode] + call ext2_inode_read + + test eax, eax + jnz .fail_get_inode + + ; The index into the inode block data. + push dword 0 + mov esi, [ebp + EXTFS.ext2_temp_inode] + + .loop: + mov ecx, [esp] + call ext2_get_inode_block + + test eax, eax + jnz .fail_loop + test ecx, ecx + jz .fail_loop + + mov eax, ecx + mov edi, eax + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .fail_loop + + ; edi -> block. + .first_dir_entry: + mov eax, [esp + 4] + cmp [ebx], eax + jne @F + + mov dword[ebx], 0 ; inode. + mov word[ebx + 6], 0 ; name_len + file_type. + jmp .write_block + + @@: + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + push edx + + mov edx, ebx + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, ecx + + .dir_entry: + cmp [ebx], eax + jne @F + + mov cx, [ebx + EXT2_DIR_STRUC.rec_len] + add [edx + EXT2_DIR_STRUC.rec_len], cx + add esp, 4 + jmp .write_block + + @@: + mov edx, ebx + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + + ; If it's a zero length entry, error. + test ecx, ecx + jz .fail_inode + + add ebx, ecx + + cmp ebx, [esp] + jb .dir_entry + + add esp, 4 + inc dword[esp] + jmp .loop + + .write_block: + mov eax, edi + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_write + test eax, eax + jnz .fail_loop + + add esp, 4 + mov ebx, [ebp + EXTFS.ext2_temp_inode] + mov eax, [esp] + call ext2_inode_read + test eax, eax + jnz .fail_get_inode + + dec word[ebx + EXT2_INODE_STRUC.i_links_count] + movzx eax, word[ebx + EXT2_INODE_STRUC.i_links_count] + push eax + + mov eax, [esp + 4] + call ext2_inode_write + test eax, eax + jnz .fail_loop + + pop eax + add esp, 4 + .return: + pop edi esi edx ecx ebx + ret + + .fail_inode: + add esp, 4 + + .fail_loop: + add esp, 4 + + .fail_get_inode: + add esp, 4 + + .fail: + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Checks if a directory is empty. +; Input: ebx = inode to check. +; ebp = pointer to EXTFS. +; [EXTFS.ext2_save_inode] = points to saved inode. +; Output: eax = 0 signifies empty directory. +;--------------------------------------------------------------------- +ext2_dir_empty: + push ebx ecx edx + + ; The index into the inode block data. + push dword 0 + mov esi, [ebp + EXTFS.ext2_save_inode] + + .loop: + mov ecx, [esp] + call ext2_get_inode_block + + ; Treat a failure as not-empty. + test eax, eax + jnz .not_empty + test ecx, ecx + jz .empty + + mov eax, ecx + mov ebx, [ebp + EXTFS.ext2_temp_block] + call ext2_block_read + test eax, eax + jnz .not_empty + + mov edx, ebx + add edx, [ebp + EXTFS.block_size] + + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, ecx + + .dir_entry: + ; Process entry. + cmp byte[ebx + EXT2_DIR_STRUC.name_len], 1 + jne @F + + cmp byte[ebx + EXT2_DIR_STRUC.name], '.' + jne .not_empty + + @@: + cmp byte[ebx + EXT2_DIR_STRUC.name_len], 2 + jne .not_empty + + cmp word[ebx + EXT2_DIR_STRUC.name], '..' + jne .not_empty + + @@: + movzx ecx, [ebx + EXT2_DIR_STRUC.rec_len] + add ebx, ecx + + cmp ebx, edx + jb .dir_entry + + inc dword[esp] + jmp .loop + + .empty: + xor eax, eax + .return: + add esp, 4 + pop edx ecx ebx + ret + + .not_empty: + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Gets the block group's inode bitmap. +; Input: eax = block group. +; Output: eax = if zero, error; else, points to block group descriptor. +; ebx = inode bitmap's block (hard disk). +;--------------------------------------------------------------------- +ext2_bg_read_inode_bitmap: + push ecx + + call ext2_bg_read_desc + test eax, eax + jz .fail + + mov ebx, [eax + EXT2_BLOCK_GROUP_DESC.inode_bitmap] ; Block number of inode bitmap - in ext2 terms. + + .return: + pop ecx + ret + + .fail: + xor eax, eax + jmp .return + +;--------------------------------------------------------------------- +; Allocates a inode. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; Output: Inode marked as set in inode group. +; eax = error code. +; ebx = inode ID. +;--------------------------------------------------------------------- +ext2_inode_alloc: + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_count] + push EXT2_BLOCK_GROUP_DESC.free_inodes_count + push [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + + lea ebx, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_inodes_count] + push ebx + + push ext2_bg_read_inode_bitmap + + call ext2_resource_alloc + + ; Inode table starts with 1. + inc ebx + + ret + +;--------------------------------------------------------------------- +; Frees a inode. +; Input: eax = inode ID. +; ebp = pointer to EXTFS. +; Output: inode marked as free in block group. +; eax = error code. +;--------------------------------------------------------------------- +ext2_inode_free: + push edi ecx + + ; Inode table starts with 1. + dec eax + + mov edi, ext2_bg_read_inode_bitmap + xor ecx, ecx + inc cl + call ext2_resource_free + + pop ecx edi + ret \ No newline at end of file diff --git a/kernel/trunk/fs/ext2/resource.asm b/kernel/trunk/fs/ext2/resource.asm new file mode 100644 index 0000000000..c0b38b96dc --- /dev/null +++ b/kernel/trunk/fs/ext2/resource.asm @@ -0,0 +1,223 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Contains common resource allocation + freeing code. ;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2013. All rights reserved. ;; +;; Distributed under the terms of the new BSD license. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;--------------------------------------------------------------------- +; Frees a resource (block/inode). +; Input: eax = resource ID. +; edi = function pointer of ext2_bg_*_bitmap form, to +; get bitmap of resource. +; ecx = 0, block; 1, inode. +; ebp = pointer to EXTFS. +; Output: Block marked as free in block group. +; eax = error code. +;--------------------------------------------------------------------- +ext2_resource_free: + push ebx edx esi + + ; Get block group. + sub eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.first_data_block] + xor edx, edx + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.blocks_per_group] + push eax edx + + call edi + test eax, eax + jz .fail + mov esi, eax + + ; Read the bitmap. + mov eax, ebx + mov edx, eax + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .fail + + pop eax + ; Mark bit free. + call bitmap_clear_bit + test eax, eax + jz @F + + ; No need to save anything. + xor eax, eax + + add esp, 4 + jmp .return + + @@: + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_write + test eax, eax + jnz .fail + + ; Read the descriptor. + mov eax, [esp] + call ext2_bg_read_desc + test eax, eax + jz .fail_bg_desc_read + + lea eax, [eax + EXT2_BLOCK_GROUP_DESC.free_blocks_count] + shl ecx, 1 + add eax, ecx + inc word[eax] + + lea eax, [ebp + EXTFS.superblock + EXT2_SB_STRUC.free_block_count] + shl ecx, 1 + add eax, ecx + inc dword[eax] + + pop eax + call ext2_bg_write_desc + + .return: + pop esi edx ebx + ret + + .fail: + add esp, 4 + .fail_bg_desc_read: + add esp, 4 + xor eax, eax + not eax + jmp .return + +;--------------------------------------------------------------------- +; Allocates a resource. +; Input: eax = inode ID for "preference". +; ebp = pointer to EXTFS. +; [esp + 4], func pointer to ext2_bg_*_bitmap +; [esp + 8], pointer to free_*_count in SB. +; [esp + 12], *_per_group +; [esp + 16], offset to free_*_count in bg descriptor. +; [esp + 20], *_count +; Output: Resource marked as set in block group. +; eax = error code. +; ebx = resource ID. +;--------------------------------------------------------------------- +ext2_resource_alloc: + ; Block allocation is a pretty serious area, since bad allocation + ; can lead to defragmentation. Thus, the best way to allocate that + ; comes to mind is to allocate around an inode as much as possible. + ; On the other hand, this isn't about a single inode/file/directory, + ; and focusing just around the preferred inode would lead to + ; congestion. Thus, after much though, the chosen allocation algorithm + ; is to search forward, then backward. + push ecx edx esi edi + + cmp dword[esp + 16 + 8], 0 + jnz @F + + ; No free blocks. + xor eax, eax + not eax + pop edi esi edx ecx + ret 20 + + @@: + ; Calculate which block group the preferred inode belongs to. + dec eax + xor edx, edx + + ; EAX = block group. + div [ebp + EXTFS.superblock + EXT2_SB_STRUC.inodes_per_group] + push eax + push eax + + mov edi, .forward + + .test_block_group: + call dword[esp + 16 + 8 + 4] + test eax, eax + jz .fail + mov esi, eax + + mov eax, ebx + mov edx, eax + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_read + test eax, eax + jnz .fail + + mov ecx, [esp + 16 + 8 + 12] + call ext2_find_free_bit + cmp eax, 0xFFFFFFFF + jne @F + + mov eax, edi + jmp eax + + @@: + mov ecx, eax + + mov eax, edx + mov ebx, [ebp + EXTFS.ext2_save_block] + call ext2_block_write + test eax, eax + jnz .fail + + ; ecx: the index of the matched entry. + ; [esp]: block group where we found. + ; [esp + 4]: starting block group. + ; esi: block group descriptor. + mov eax, [esp] ; Index of block group in which we found. + mul dword[esp + 16 + 8 + 12] + add eax, ecx + mov ebx, eax + + mov eax, [esp + 16 + 8 + 8] + dec dword[eax] + + mov eax, esi + add eax, [esp + 16 + 8 + 16] + dec word[eax] + + pop eax + call ext2_bg_write_desc + + add esp, 4 + jmp .return + + ; Continue forward. + .forward: + inc dword[esp] + mov eax, [esp] + mul dword[esp + 16 + 8 + 12] + cmp eax, [esp + 16 + 8 + 20] + jbe @F + + ; We need to go backward. + mov eax, [esp + 4] + mov [esp], eax + mov edi, .backward + jmp .backward + + @@: + mov eax, [esp] + jmp .test_block_group + + ; Continue backward. + .backward: + cmp dword[esp], 0 + je .fail + + dec dword[esp] + mov eax, [esp] + jmp .test_block_group + + .return: + pop edi esi edx ecx + ret 20 + + .fail: + add esp, 8 + xor eax, eax + not eax + jmp .return \ No newline at end of file diff --git a/kernel/trunk/kernel32.inc b/kernel/trunk/kernel32.inc index 1f0d135fc0..821243649a 100644 --- a/kernel/trunk/kernel32.inc +++ b/kernel/trunk/kernel32.inc @@ -191,7 +191,7 @@ include "fs/fat12.inc" ; read / write for fat12 filesystem include "blkdev/rd.inc" ; ramdisk read /write include "fs/fs_lfn.inc" ; syscall, version 2 include "fs/iso9660.inc" ; read for iso9660 filesystem CD -include "fs/ext2.inc" ; read / write for ext2 filesystem +include "fs/ext2/ext2.asm" ; read / write for ext2 filesystem include "fs/xfs.asm" ; read / write for xfs filesystem ; sound