;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2016. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License. ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; $Revision$ ; FAT external functions ; in: ; ebx -> parameter structure of sysfunc 70 ; ebp -> FAT structure ; esi -> path string in UTF-8 ; out: ; eax, ebx = return values for sysfunc 70 iglobal align 4 fat_user_functions: dd fat_free dd (fat_user_functions_end - fat_user_functions - 4) / 4 dd fat_Read dd fat_ReadFolder dd fat_CreateFile dd fat_Write dd fat_SetFileEnd dd fat_GetFileInfo dd fat_SetFileInfo dd 0 dd fat_Delete dd fat_CreateFolder dd fat_Rename fat_user_functions_end: endg cache_max equ 1919 ; max. is 1919*512+0x610000=0x6ffe00 PUSHAD_EAX equ [esp+28] PUSHAD_ECX equ [esp+24] PUSHAD_EDX equ [esp+20] PUSHAD_EBX equ [esp+16] PUSHAD_EBP equ [esp+8] PUSHAD_ESI equ [esp+4] PUSHAD_EDI equ [esp+0] ; Internal data for every FAT partition. struct FAT PARTITION fs_type db ? fat_change db ? ; 1=fat has changed createOption db ? rb 1 Lock MUTEX ; currently operations with one partition ; can not be executed in parallel since the legacy code is not ready SECTORS_PER_FAT dd ? NUMBER_OF_FATS dd ? SECTORS_PER_CLUSTER dd ? BYTES_PER_SECTOR dd ? ; Note: if BPS <> 512 need lots of changes ROOT_CLUSTER dd ? ; first rootdir cluster FAT_START dd ? ; start of fat table ROOT_START dd ? ; start of rootdir (only fat16) ROOT_SECTORS dd ? ; count of rootdir sectors (only fat16) DATA_START dd ? ; start of data area (=first cluster 2) LAST_CLUSTER dd ? ; last availabe cluster ADR_FSINFO dd ? ; used only by fat32 fatRESERVED dd ? fatBAD dd ? fatEND dd ? fatMASK dd ? fatStartScan dd ? cluster_tmp dd ? ; used by analyze_directory and analyze_directory_to_write longname_sec1 dd ? ; used by analyze_directory to save 2 previous longname_sec2 dd ? ; directory sectors for delete long filename fat_in_cache dd ? ; For FAT16/FAT32, this points to 512-byte buffer for the current sector of FAT. ; For FAT12, the entire FAT structure is read ; and unpacked from 12bit per cluster to word per cluster. ; Note: work with unpacked copy of FAT12 means ; additional memory and additional code for packing/unpacking. ; I'm not sure that the economy justifies the cost, but anyway, ; there is how work was done before my edits, and I'm just keeping the principle. fat_cache_ptr dd ? fat12_unpacked_ptr dd ? volumeLabel rb 12 buffer rb 512 fsinfo_buffer rb 512 ends uglobal align 4 partition_count dd ? ; partitions found by set_FAT32_variables hd_error dd ? hd_setup dd ? hd_wait_timeout dd ? cache_search_start dd ? ; used by find_empty_slot Sector512: ; label for dev_hdcd.inc buffer: rb 512 endg ; these labels are located before the main function to make ; most of jumps to these be short fat_create_partition.free_return0: mov eax, ebp call free pop ebp fat_create_partition.return0: xor eax, eax ret fat_create_partition: cmp dword [esi+DISK.MediaInfo.SectorSize], 512 jnz .return0 ; bootsector must have been successfully read cmp dword [esp+4], 0 jnz .return0 ; bootsector signature must be correct cmp word [ebx+0x1fe], 0xaa55 jnz .return0 ; sectors per cluster must be nonzero cmp byte [ebx+0xd], 0 jz .return0 ; bytes per sector must be 0x200 cmp word [ebx+0xb], 0x200 jnz .return0 ; number of fats must be nonzero cmp byte [ebx+0x10], 0 jz .return0 ; The only reason to be invalid partition now is FAT12. Since the test for ; FAT size requires knowledge of some calculated values, which are also used ; in the normal operation, let's hope for the best and allocate data now; if ; it will prove wrong, just deallocate it. movi eax, sizeof.FAT call malloc test eax, eax jz .return0 mov ecx, dword [ebp+PARTITION.FirstSector] mov dword [eax+FAT.FirstSector], ecx mov ecx, dword [ebp+PARTITION.FirstSector+4] mov dword [eax+FAT.FirstSector+4], ecx mov ecx, dword [ebp+PARTITION.Length] mov dword [eax+FAT.Length], ecx mov ecx, dword [ebp+PARTITION.Length+4] mov dword [eax+FAT.Length+4], ecx mov ecx, [ebp+PARTITION.Disk] mov [eax+FAT.Disk], ecx mov [eax+FAT.FSUserFunctions], fat_user_functions or [eax+FAT.fat_in_cache], -1 mov [eax+FAT.fat_change], 0 push ebp mov ebp, eax lea ecx, [ebp+FAT.Lock] call mutex_init movzx eax, word [ebx+0xe] ; sectors reserved mov [ebp+FAT.FAT_START], eax movzx eax, byte [ebx+0xd] ; sectors per cluster mov [ebp+FAT.SECTORS_PER_CLUSTER], eax movzx ecx, word [ebx+0xb] ; bytes per sector mov [ebp+FAT.BYTES_PER_SECTOR], ecx movzx eax, word [ebx+0x11] ; count of rootdir entries (=0 fat32) shl eax, 5 ; mul 32 dec ecx add eax, ecx ; round up if not equal count inc ecx ; bytes per sector xor edx, edx div ecx mov [ebp+FAT.ROOT_SECTORS], eax ; count of rootdir sectors movzx eax, word [ebx+0x16] ; sectors per fat <65536 test eax, eax jnz @f mov eax, [ebx+0x24] ; sectors per fat @@: mov [ebp+FAT.SECTORS_PER_FAT], eax movzx eax, byte [ebx+0x10] ; number of fats mov [ebp+FAT.NUMBER_OF_FATS], eax mul [ebp+FAT.SECTORS_PER_FAT] test edx, edx jnz .free_return0 add eax, [ebp+FAT.FAT_START] jc .free_return0 mov [ebp+FAT.ROOT_START], eax ; rootdir = fat_start + fat_size * fat_count add eax, [ebp+FAT.ROOT_SECTORS] ; rootdir sectors should be 0 on fat32 jc .free_return0 mov [ebp+FAT.DATA_START], eax ; data area = rootdir + rootdir_size movzx eax, word [ebx+0x13] ; total sector count <65536 test eax, eax jnz @f mov eax, [ebx+0x20] ; total sector count @@: cmp dword [ebp+FAT.Length+4], 0 jnz @f cmp eax, dword [ebp+FAT.Length] ja .free_return0 @@: mov dword [ebp+FAT.Length], eax and dword [ebp+FAT.Length+4], 0 sub eax, [ebp+FAT.DATA_START] ; eax = count of data sectors jc .free_return0 xor edx, edx div [ebp+FAT.SECTORS_PER_CLUSTER] inc eax mov [ebp+FAT.LAST_CLUSTER], eax dec eax ; cluster count jz .free_return0 mov [ebp+FAT.fatStartScan], 2 cmp eax, 0xfff5 jb .fat16 .fat32: pusha lea esi, [ebx+71] lea edi, [ebp+FAT.volumeLabel] movsd movsd movsd popa mov eax, [ebx+0x2c] ; rootdir cluster mov [ebp+FAT.ROOT_CLUSTER], eax movzx eax, word [ebx+0x30] mov [ebp+FAT.ADR_FSINFO], eax push ebx add ebx, 512 call fs_read32_sys test eax, eax jnz @f mov eax, [ebx+0x1ec] cmp eax, -1 jz @f mov [ebp+FAT.fatStartScan], eax @@: pop ebx mov [ebp+FAT.fatRESERVED], 0x0FFFFFF6 mov [ebp+FAT.fatBAD], 0x0FFFFFF7 mov [ebp+FAT.fatEND], 0x0FFFFFF8 mov [ebp+FAT.fatMASK], 0x0FFFFFFF mov al, 32 .fat_not_12_finalize: mov [ebp+FAT.fs_type], al ; For FAT16 and FAT32, allocate 512 bytes for FAT cache. mov eax, 512 call malloc test eax, eax jz .free_return0 mov [ebp+FAT.fat_cache_ptr], eax mov eax, ebp pop ebp ret .fat16: pusha lea esi, [ebx+43] lea edi, [ebp+FAT.volumeLabel] movsd movsd movsd popa cmp eax, 0xff5 jb .fat12 and [ebp+FAT.ROOT_CLUSTER], 0 mov [ebp+FAT.fatRESERVED], 0x0000FFF6 mov [ebp+FAT.fatBAD], 0x0000FFF7 mov [ebp+FAT.fatEND], 0x0000FFF8 mov [ebp+FAT.fatMASK], 0x0000FFFF mov al, 16 jmp .fat_not_12_finalize .fat12: and [ebp+FAT.ROOT_CLUSTER], 0 mov [ebp+FAT.fatRESERVED], 0xFF6 mov [ebp+FAT.fatBAD], 0xFF7 mov [ebp+FAT.fatEND], 0xFFF mov [ebp+FAT.fatMASK], 0xFFF mov al, 12 mov [ebp+FAT.fs_type], al ; For FAT12, allocate&read data for entire table: ; calculate A = ALIGN_UP(NUM_CLUSTERS, 8), ; allocate ALIGN_UP(A*3/2, 512) bytes for FAT table plus A*2 bytes for unpacked data. mov eax, [ebp+FAT.LAST_CLUSTER] and eax, not 7 add eax, 8 mov edx, eax lea eax, [eax*3] add eax, 512*2-1 shr eax, 10 shl eax, 9 lea eax, [eax+edx*2] call malloc test eax, eax jz .free_return0 ; Read ALIGN_UP(NUM_CLUSTERS*3/2, 512) bytes. push ebx mov [ebp+FAT.fat_cache_ptr], eax mov edx, [ebp+FAT.LAST_CLUSTER] lea edx, [(edx+1)*3 + 512*2-1] shr edx, 10 xchg eax, ebx xor eax, eax .read_fat: push eax add eax, [ebp+FAT.FAT_START] call fs_read32_sys test eax, eax pop eax jz @f mov eax, [ebp+FAT.fat_cache_ptr] call free pop ebx jmp .free_return0 @@: add ebx, 512 inc eax cmp eax, edx jb .read_fat mov [ebp+FAT.fat12_unpacked_ptr], ebx pushad mov esi, [ebp+FAT.fat_cache_ptr] mov edi, [ebp+FAT.fat12_unpacked_ptr] mov edx, [ebp+FAT.LAST_CLUSTER] and edx, not 7 lea edx, [edi+(edx+8)*2] push edx @@: mov eax, dword [esi] mov ebx, dword [esi+4] mov ecx, dword [esi+8] mov edx, ecx shr edx, 4 shr dx, 4 xor ch, ch shld ecx, ebx, 20 shr cx, 4 shld ebx, eax, 12 and ebx, 0x0fffffff shr bx, 4 shl eax, 4 and eax, 0x0fffffff shr ax, 4 mov dword [edi], eax mov dword [edi+4], ebx mov dword [edi+8], ecx mov dword [edi+12], edx add edi, 16 add esi, 12 cmp edi, [esp] jnz @b pop eax popad mov eax, ebp pop ebx ebp ret fat_free: push eax mov eax, [eax+FAT.fat_cache_ptr] call free pop eax jmp free iglobal label fat_legal_chars byte ; 0 = not allowed ; 1 = allowed only in long names ; 3 = allowed times 32 db 0 ; ! " # $ % & ' ( ) * + , - . / db 1,3,0,3,3,3,3,3,3,3,0,1,1,3,3,0 ; 0 1 2 3 4 5 6 7 8 9 : ; < = > ? db 3,3,3,3,3,3,3,3,3,3,0,1,0,1,0,0 ; @ A B C D E F G H I J K L M N O db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 ; P Q R S T U V W X Y Z [ \ ] ^ _ db 3,3,3,3,3,3,3,3,3,3,3,1,0,1,3,3 ; ` a b c d e f g h i j k l m n o db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 ; p q r s t u v w x y z { | } ~ db 3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0 endg fat_name_is_legal: ; in: esi -> UTF-8 name ; out: CF=1 -> legal push esi xor eax, eax @@: lodsb test al, al js @b test [fat_legal_chars+eax], 1 jnz @b test al, al jnz @f stc @@: pop esi ret fat_next_short_name: ; in: edi->8+3 name ; out: name corrected, CF=1 -> error pushad mov ecx, 8 mov al, '~' std push edi add edi, 7 repnz scasb pop edi cld jz .tilde ; tilde is not found, insert "~1" at end add edi, 6 cmp word [edi], ' ' jnz .insert_tilde @@: dec edi cmp byte [edi], ' ' jz @b inc edi .insert_tilde: mov word [edi], '~1' popad clc ret .tilde: push edi add edi, 7 xor ecx, ecx @@: ; after tilde may be only digits and trailing spaces cmp byte [edi], '~' jz .break cmp byte [edi], ' ' jz .space cmp byte [edi], '9' jnz .found dec edi jmp @b .space: dec edi inc ecx jmp @b .found: inc byte [edi] add dword [esp], 8 jmp .zerorest .break: jecxz .noplace inc edi mov al, '1' @@: xchg al, [edi] inc edi cmp al, ' ' mov al, '0' jnz @b .succ: pop edi popad clc ret .noplace: dec edi cmp edi, [esp] jz .err add dword [esp], 8 mov word [edi], '~1' inc edi inc edi @@: mov byte [edi], '0' .zerorest: inc edi cmp edi, [esp] jb @b pop edi popad ret .err: pop edi popad stc ret fat_gen_short_name: ; in: ; esi -> UTF-8 name ; edi -> buffer (8+3=11 chars) pushad mov eax, ' ' push edi stosd stosd stosd pop edi xor eax, eax movi ebx, 8 lea ecx, [edi+8] .loop: lodsb test al, al js .space jz .done test [fat_legal_chars+eax], 2 jz .space cmp al, '.' jz .dot dec bl jns .store inc bl .space: or bh, 1 jmp .loop .store: call cp866toUpper stosb jmp .loop .dot: test bh, 2 jz .firstdot pop ebx add ebx, edi sub ebx, ecx push ebx cmp ebx, ecx jb @f pop ebx push ecx @@: cmp edi, ecx jbe .skip @@: dec edi mov al, [edi] dec ebx mov [ebx], al mov byte [edi], ' ' cmp edi, ecx ja @b .skip: mov bh, 3 jmp @f .firstdot: cmp bl, 8 jz .space push edi or bh, 2 @@: mov edi, ecx mov bl, 3 jmp .loop .done: test bh, 2 jz @f pop edi @@: lea edi, [ecx-8] test bh, 1 jz @f call fat_next_short_name @@: popad ret set_FAT: ; in: eax = cluster, edx = value to save ; out: edx = old value, CF=1 -> error push eax ebx esi cmp eax, 2 jc .ret cmp [ebp+FAT.LAST_CLUSTER], eax jc .ret cmp [ebp+FAT.fs_type], 12 je .FAT12 cmp [ebp+FAT.fs_type], 16 je @f add eax, eax @@: add eax, eax mov esi, 511 and esi, eax shr eax, 9 add eax, [ebp+FAT.FAT_START] mov ebx, [ebp+FAT.fat_cache_ptr] cmp eax, [ebp+FAT.fat_in_cache] je .inCache cmp [ebp+FAT.fat_change], 0 je @f call write_fat_sector @@: mov [ebp+FAT.fat_in_cache], eax call fs_read32_sys test eax, eax jne .error .inCache: cmp [ebp+FAT.fs_type], 16 jne .test32 xchg [ebx+esi], dx ; save new value and get old value jmp .write .test32: mov eax, [ebp+FAT.fatMASK] and edx, eax xor eax, -1 ; mask for high bits and eax, [ebx+esi] ; get high 4 bits or eax, edx mov edx, [ebx+esi] ; get old value mov [ebx+esi], eax ; save new value .write: mov [ebp+FAT.fat_change], 1 and edx, [ebp+FAT.fatMASK] .ret: pop esi ebx eax ret .error: stc jmp .ret .FAT12: test edx, 0xF000 jnz .error mov ebx, [ebp+FAT.fat12_unpacked_ptr] xchg [ebx+eax*2], dx mov [ebp+FAT.fat_change], 1 jmp .ret get_FAT: ; in: eax = cluster ; out: eax = next cluster, CF=1 -> error push ebx esi cmp [ebp+FAT.fs_type], 12 je .FAT12 cmp [ebp+FAT.fs_type], 16 je @f add eax, eax @@: add eax, eax mov esi, 511 and esi, eax shr eax, 9 add eax, [ebp+FAT.FAT_START] mov ebx, [ebp+FAT.fat_cache_ptr] cmp eax, [ebp+FAT.fat_in_cache] je .inCache cmp [ebp+FAT.fat_change], 0 je @f call write_fat_sector @@: mov [ebp+FAT.fat_in_cache], eax call fs_read32_sys test eax, eax jnz .error .inCache: mov eax, [ebx+esi] and eax, [ebp+FAT.fatMASK] .ret: pop esi ebx ret .error: stc jmp .ret .FAT12: mov ebx, [ebp+FAT.fat12_unpacked_ptr] movzx eax, word [ebx+eax*2] jmp .ret get_free_FAT: ; out: eax = number of first free cluster, CF=1 -> disk full push ecx mov ecx, [ebp+FAT.LAST_CLUSTER] mov eax, [ebp+FAT.fatStartScan] cmp [ebp+FAT.fs_type], 12 jz get_free_FAT12 dec ecx cmp eax, 2 jb .reset .test: cmp eax, [ebp+FAT.LAST_CLUSTER] jbe .inRange .reset: mov eax, 2 .inRange: push eax call get_FAT jc @f test eax, eax pop eax je .found inc eax dec ecx jnz .test .notFound: pop ecx stc ret @@: pop eax jmp .notFound .found: lea ecx, [eax+1] mov [ebp+FAT.fatStartScan], ecx pop ecx clc ret get_free_FAT12: push edx edi mov edi, [ebp+FAT.fat12_unpacked_ptr] cmp eax, 2 jb .reset cmp eax, ecx jbe @f .reset: mov eax, 2 @@: mov edx, eax lea edi, [edi+eax*2] sub ecx, eax inc ecx xor eax, eax repnz scasw jz .found cmp edx, 2 jz .notfound mov edi, [ebp+FAT.fat12_unpacked_ptr] lea ecx, [edx-2] repnz scasw jnz .notfound .found: sub edi, [ebp+FAT.fat12_unpacked_ptr] shr edi, 1 mov [ebp+FAT.fatStartScan], edi lea eax, [edi-1] @@: pop edi edx ecx ret .notfound: stc jmp @b write_fat_sector: push eax ebx ecx mov [ebp+FAT.fat_change], 0 mov eax, [ebp+FAT.fat_in_cache] cmp eax, -1 jz @f mov ebx, [ebp+FAT.fat_cache_ptr] mov ecx, [ebp+FAT.NUMBER_OF_FATS] .write_next_fat: push eax call fs_write32_sys pop eax add eax, [ebp+FAT.SECTORS_PER_FAT] dec ecx jnz .write_next_fat @@: pop ecx ebx eax ret get_date_for_file: ; out in ax: ; bits 0-4 = day ; bits 5-8 = month ; bits 9-15 = count of years from 1980 mov al, 7 call fsReadCMOS ror eax, 5 mov al, 8 call fsReadCMOS ror eax, 4 mov al, 9 call fsReadCMOS add ax, 20 rol eax, 9 ret get_time_for_file: ; out in ax: ; bits 0-4 = second (the low bit is lost) ; bits 5-10 = minute ; bits 11-15 = hour mov al, 0 call fsReadCMOS ror eax, 6 mov al, 2 call fsReadCMOS ror eax, 6 mov al, 4 call fsReadCMOS rol eax, 11 ret add_disk_free_space: ; in: ecx = cluster count (signed) test ecx, ecx je .ret cmp [ebp+FAT.fs_type], 32 jne .ret push eax ebx mov eax, [ebp+FAT.ADR_FSINFO] lea ebx, [ebp+FAT.fsinfo_buffer] call fs_read32_sys test eax, eax jnz @f cmp dword [ebx+0x1fc], 0xaa550000 ; check sector id jne @f add [ebx+0x1e8], ecx push [ebp+FAT.fatStartScan] pop dword [ebx+0x1ec] mov eax, [ebp+FAT.ADR_FSINFO] call fs_write32_sys @@: pop ebx eax .ret: ret clear_cluster_chain: ; in: eax = first cluster push eax ecx edx xor ecx, ecx ; cluster count @@: cmp eax, [ebp+FAT.LAST_CLUSTER] ja @f cmp eax, 2 jb @f cmp eax, [ebp+FAT.ROOT_CLUSTER] jz @f xor edx, edx call set_FAT jc .ret inc ecx mov eax, edx jmp @b @@: call add_disk_free_space clc .ret: pop edx ecx eax ret update_disk: cmp [ebp+FAT.fat_change], 0 jz .noChange cmp [ebp+FAT.fs_type], 12 jz .fat12 call write_fat_sector .noChange: mov esi, [ebp+PARTITION.Disk] call disk_sync ret .fat12: mov esi, [ebp+FAT.fat12_unpacked_ptr] mov edi, [ebp+FAT.fat_cache_ptr] mov edx, [ebp+FAT.LAST_CLUSTER] and edx, not 7 lea edx, [esi+(edx+8)*2] @@: mov eax, dword [esi] mov ebx, dword [esi+4] shl ax, 4 shl eax, 4 shl bx, 4 shr ebx, 4 shrd eax, ebx, 8 shr ebx, 8 mov dword [edi], eax mov word [edi+4], bx add edi, 6 add esi, 8 cmp esi, edx jb @b mov esi, [ebp+FAT.NUMBER_OF_FATS] mov edx, [ebp+FAT.LAST_CLUSTER] lea edx, [(edx+1)*3 + 512*2-1] shr edx, 10 push [ebp+FAT.FAT_START] .write_fats: xor eax, eax mov ebx, [ebp+FAT.fat_cache_ptr] .loop1: push eax add eax, [esp+4] call fs_write32_sys test eax, eax pop eax jnz @f add ebx, 512 inc eax cmp eax, edx jb .loop1 pop eax add eax, [ebp+FAT.SECTORS_PER_FAT] push eax dec esi jnz .write_fats @@: pop eax mov [ebp+FAT.fat_change], 0 jmp .noChange fat_lock: lea ecx, [ebp+FAT.Lock] jmp mutex_lock fat_unlock: lea ecx, [ebp+FAT.Lock] jmp mutex_unlock fat_get_name: ; in: edi -> FAT entry, esi -> buffer for UTF-16 name ; out: CF=1 -> no valid entry cmp byte [edi], 0 jz .no cmp byte [edi], 0xE5 jz .no cmp byte [edi+11], 0xF jz .longname push edi xchg esi, edi test byte [esi+11], 8 jnz .label pushd ecx 8 pop ecx xor eax, eax @@: lodsb stosw loop @b mov cl, 8 @@: cmp word [edi-2], ' ' jnz @f sub edi, 2 loop @b @@: mov word [edi], '.' add edi, 2 mov cl, 3 @@: lodsb stosw loop @b mov cl, 3 @@: cmp word [edi-2], ' ' jnz @f sub edi, 2 loop @b sub edi, 2 @@: and word [edi], 0 ; CF=0 pop ecx edi ret .label: lea edi, [ebp+FAT.volumeLabel] movsd movsd movsd pop edi .no: stc ret .longname: mov al, byte [edi] and eax, 0x3F dec eax cmp al, 20 jae .no ; ignore invalid entries mov word [esi+260*2], 0 ; force null-terminating for orphans imul eax, 13*2 test byte [edi], 0x40 jz @f mov word [esi+eax+13*2], 0 @@: ; copy name (13 chars in UTF-16) push edi inc edi add esi, eax xchg esi, edi movsd movsd movsw add esi, 3 movsd movsd movsd add esi, 2 movsd pop edi test eax, eax jnz .no ; if this is not first entry, more processing required ret fat_find_lfn: ; in: ; esi -> path in UTF-8 ; parameters in the stack ; out: ; esi -> next name in the path ; edi -> direntry ; CF=1 -> file not found, eax = error code lea eax, [esp+12] call dword [eax-4] jc .reterr sub esp, 262*2 ; reserve place for LFN .l1: push esi lea esi, [esp+4] call fat_get_name pop esi jc .no push edi esi lea edi, [esp+8] @@: call utf8to16 call utf16toUpper mov edx, eax mov ax, [edi] call utf16toUpper cmp ax, dx jnz .done add edi, 2 test ax, ax jnz @b dec esi pop eax edi .found: add esp, 262*2 ; if this is LFN entry, advance to true entry cmp byte [edi+11], 0xF jnz @f lea eax, [esp+12] call dword[eax-8] jc .reterr @@: xor eax, eax ret .done: cmp dx, '/' jnz @f test ax, ax jnz @f mov [esp], esi @@: pop esi edi jz .found .no: lea eax, [esp+262*2+12] call dword[eax-8] jnc .l1 add esp, 262*2 .reterr: stc ret fat_time_to_bdfe: ; in: eax=FAT time ; out: eax=BDFE time push ecx edx mov ecx, eax mov edx, eax shr eax, 11 shl eax, 16 ; hours and edx, 0x1F add edx, edx mov al, dl ; seconds shr ecx, 5 and ecx, 0x3F mov ah, cl ; minutes pop edx ecx ret fat_date_to_bdfe: push ecx edx mov ecx, eax mov edx, eax shr eax, 9 add ax, 1980 shl eax, 16 ; year and edx, 0x1F mov al, dl ; day shr ecx, 5 and ecx, 0xF mov ah, cl ; month pop edx ecx ret bdfe_to_fat_time: push edx mov edx, eax shr eax, 16 and dh, 0x3F shl eax, 6 or al, dh shr dl, 1 and dl, 0x1F shl eax, 5 or al, dl pop edx ret bdfe_to_fat_date: push edx mov edx, eax shr eax, 16 sub ax, 1980 and dh, 0xF shl eax, 4 or al, dh and dl, 0x1F shl eax, 5 or al, dl pop edx ret fat_entry_to_bdfe: ; convert FAT entry at edi to BDFE (block of data of folder entry) at esi, advance esi mov eax, [ebp-4] mov [esi+4], eax ; cp866/UNICODE name fat_entry_to_bdfe2: movzx eax, byte [edi+11] mov [esi], eax ; attributes movzx eax, word [edi+14] call fat_time_to_bdfe mov [esi+8], eax ; creation time movzx eax, word [edi+16] call fat_date_to_bdfe mov [esi+12], eax ; creation date and dword [esi+16], 0 ; last access time is not supported on FAT movzx eax, word [edi+18] call fat_date_to_bdfe mov [esi+20], eax ; last access date movzx eax, word [edi+22] call fat_time_to_bdfe mov [esi+24], eax ; last write time movzx eax, word [edi+24] call fat_date_to_bdfe mov [esi+28], eax ; last write date mov eax, [edi+28] mov [esi+32], eax ; file size (low dword) xor eax, eax mov [esi+36], eax ; file size (high dword) test ebp, ebp jz .ret add esi, 40 push edi esi mov edi, esi mov esi, ebp cmp byte [ebp-4], 2 jz .utf16 cmp byte [ebp-4], 3 jz .utf8 @@: lodsw call uni2ansi_char stosb test al, al jnz @b pop esi edi add esi, 264 .ret: ret .utf8: push ecx mov ecx, 519 call UTF16to8_string pop ecx jmp @f .utf16: lodsw stosw test eax, eax jnz .utf16 @@: pop esi edi add esi, 520 ret bdfe_to_fat_entry: ; convert BDFE at edx to FAT entry at edi ; destroys eax ; attributes byte test byte [edi+11], 8 ; volume label? jnz @f mov al, [edx] and al, 0x27 and byte [edi+11], 0x10 or byte [edi+11], al @@: mov eax, [edx+8] call bdfe_to_fat_time mov [edi+14], ax ; creation time mov eax, [edx+12] call bdfe_to_fat_date mov [edi+16], ax ; creation date mov eax, [edx+20] call bdfe_to_fat_date mov [edi+18], ax ; last access date mov eax, [edx+24] call bdfe_to_fat_time mov [edi+22], ax ; last write time mov eax, [edx+28] call bdfe_to_fat_date mov [edi+24], ax ; last write date ret hd_find_lfn: ; in: esi -> path string in UTF-8 ; out: CF=1 - file not found, eax=error code ; else CF=0 and edi->direntry, eax=sector push esi edi push 0 push 0 push fat1x_root_first push fat1x_root_next mov eax, [ebp+FAT.ROOT_CLUSTER] cmp [ebp+FAT.fs_type], 32 jz .fat32 .loop: and [ebp+FAT.longname_sec1], 0 and [ebp+FAT.longname_sec2], 0 call fat_find_lfn jc .notfound cmp byte [esi], 0 jz .found test byte [edi+11], 10h jz .notfound and dword [esp+12], 0 mov eax, [edi+20-2] mov ax, [edi+26] ; cluster .fat32: mov [esp+8], eax mov dword [esp+4], fat_notroot_first mov dword [esp], fat_notroot_next jmp .loop .notfound: add esp, 16 pop edi esi stc ret .found: lea eax, [esp+8] cmp dword [eax], 0 jz .root call fat_get_sector jmp .cmn .root: mov eax, [eax+4] add eax, [ebp+FAT.ROOT_START] .cmn: add esp, 20 ; CF=0 pop esi ret ;---------------------------------------------------------------- fat_Read: call fat_lock call hd_find_lfn jc .notFound test byte [edi+11], 0x10 ; do not allow read directories jnz .noaccess cmp dword [ebx+8], 0 jnz .endOfFile mov edx, [ebx+4] ; file offset mov ecx, [ebx+12] ; size mov ebx, [ebx+16] ; buffer push ebx push 0 test ecx, ecx jz .done mov eax, [edi+28] sub eax, edx jb .fileEnd cmp eax, ecx jae @f mov ecx, eax mov byte [esp], 6 @@: mov eax, [edi+20-2] mov ax, [edi+26] ; now eax=cluster, ebx=buffer for data, ecx=count, edx=position mov edi, [ebp+FAT.SECTORS_PER_CLUSTER] shl edi, 9 @@: cmp eax, 2 jb .fileEnd cmp eax, [ebp+FAT.fatRESERVED] jae .fileEnd sub edx, edi jc @f call get_FAT jc .noaccess2 jmp @b .notFound: push eax jmp .ret .noaccess: push ERROR_ACCESS_DENIED jmp .ret .endOfFile: push ERROR_END_OF_FILE .ret: call fat_unlock pop eax xor ebx, ebx ret @@: mov esi, eax dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] add edx, edi jz .alignedCluster mov edi, edx shr edi, 9 add eax, edi and edx, 511 cmp ecx, 512 jc .sectorPiece test edx, edx jz .alignedSector .sectorPiece: push eax ebx lea ebx, [ebp+FAT.buffer] call fs_read32_app test eax, eax mov eax, ebx pop ebx jne .noaccess3 add eax, edx push ecx add ecx, edx cmp ecx, 512 jbe @f mov ecx, 512 @@: sub ecx, edx call memmove sub [esp], ecx add ebx, ecx pop ecx eax xor edx, edx inc edi inc eax test ecx, ecx jz .done .alignedSector: shl edi, 9 add ecx, edi mov edi, [ebp+FAT.SECTORS_PER_CLUSTER] shl edi, 9 .alignedCluster: cmp ecx, 512 jc .sectorPiece mov edx, eax mov eax, esi @@: sub ecx, edi jbe .readEnd call get_FAT jc .noaccess4 cmp eax, 2 jb .fileEnd2 cmp eax, [ebp+FAT.fatRESERVED] jae .fileEnd2 inc esi cmp eax, esi jz @b .fragmentEnd: xchg eax, esi dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] push ecx mov ecx, eax mov eax, esi dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] push eax .readFragment: sub ecx, edx mov eax, edx xor edx, edx call fs_read64_app shl ecx, 9 add ebx, ecx test eax, eax pop eax jnz .noaccess3 pop ecx xor edx, edx jecxz .done jmp .alignedCluster .readEnd: add ecx, edi mov edi, ecx and ecx, 511 shr edi, 9 dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] add eax, edi push ecx push eax mov ecx, eax jmp .readFragment .noaccess3: pop eax .noaccess2: mov byte [esp], ERROR_DEVICE .done: call fat_unlock pop eax edx sub ebx, edx ret .fileEnd: mov byte [esp], ERROR_END_OF_FILE jmp .done .noaccess4: mov byte [esp], ERROR_DEVICE jmp @f .fileEnd2: mov byte [esp], ERROR_END_OF_FILE @@: inc esi xor ecx, ecx jmp .fragmentEnd ;---------------------------------------------------------------- fat_ReadFolder: call fat_lock mov eax, [ebp+FAT.ROOT_CLUSTER] cmp byte [esi], 0 jz .doit call hd_find_lfn jc .error test byte [edi+11], 0x10 ; do not allow read files jz .accessDenied mov eax, [edi+20-2] mov ax, [edi+26] ; eax=cluster .doit: sub esp, 262*2 ; reserve space for LFN push dword [ebx+8] ; cp866/UNICODE name mov edx, [ebx+16] ; pointer to buffer ; init header push eax mov edi, edx mov ecx, 32/4 xor eax, eax rep stosd pop eax mov byte [edx], 1 ; version mov esi, edi ; esi points to BDFE mov ecx, [ebx+12] ; number of blocks to read mov ebx, [ebx+4] ; index of the first block .new_cluster: mov [ebp+FAT.cluster_tmp], eax test eax, eax jnz @f cmp [ebp+FAT.fs_type], 32 jz .notfound mov eax, [ebp+FAT.ROOT_START] push [ebp+FAT.ROOT_SECTORS] push ebx jmp .new_sector @@: dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] push [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] push ebx .new_sector: lea ebx, [ebp+FAT.buffer] mov edi, ebx push eax call fs_read32_sys test eax, eax pop eax jnz .notfound2 add ebx, 512 push eax .l1: push esi lea esi, [esp+20] call fat_get_name pop esi jc .l2 cmp byte [edi+11], 0xF jnz .do_bdfe add edi, 0x20 cmp edi, ebx jb .do_bdfe pop eax inc eax dec dword [esp+4] jnz @f mov eax, [ebp+FAT.cluster_tmp] test eax, eax jz .done call get_FAT jc .notfound2 cmp eax, 2 jb .done cmp eax, [ebp+FAT.fatRESERVED] jae .done push eax mov eax, [ebp+FAT.SECTORS_PER_CLUSTER] mov [esp+8], eax pop eax mov [ebp+FAT.cluster_tmp], eax dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] @@: lea ebx, [ebp+FAT.buffer] mov edi, ebx push eax call fs_read32_sys test eax, eax pop eax jnz .notfound2 add ebx, 512 push eax .do_bdfe: inc dword [edx+8] ; new file found dec dword [esp+4] jns .l2 dec ecx js .l2 inc dword [edx+4] ; new file block copied push ebp lea ebp, [esp+20] call fat_entry_to_bdfe pop ebp .l2: add edi, 0x20 cmp edi, ebx jb .l1 pop eax inc eax dec dword [esp+4] jnz .new_sector mov eax, [ebp+FAT.cluster_tmp] test eax, eax jz .done call get_FAT jc .notfound2 cmp eax, 2 jb .done cmp eax, [ebp+FAT.fatRESERVED] jae .done push eax mov eax, [ebp+FAT.SECTORS_PER_CLUSTER] mov [esp+8], eax pop eax pop ebx add esp, 4 jmp .new_cluster .notfound2: add esp, 8 .notfound: add esp, 262*2+4 push ERROR_DEVICE jmp @f .done: add esp, 262*2+12 pushd 0 dec ecx js @f mov byte [esp], ERROR_END_OF_FILE @@: mov ebx, [edx+4] .ret: call fat_unlock pop eax ret .error: push eax xor ebx, ebx jmp .ret .accessDenied: push ERROR_ACCESS_DENIED xor ebx, ebx jmp .ret fat1x_root_next: push ecx lea ecx, [ebp+FAT.buffer+0x200-0x20] cmp edi, ecx jae fat1x_root_next_sector add edi, 0x20 @@: pop ecx ret fat1x_root_next_write: push ecx lea ecx, [ebp+FAT.buffer+0x200] cmp edi, ecx jc @b call fat1x_root_end_write fat1x_root_next_sector: push [ebp+FAT.longname_sec2] pop [ebp+FAT.longname_sec1] mov ecx, [eax+4] push ecx add ecx, [ebp+FAT.ROOT_START] mov [ebp+FAT.longname_sec2], ecx pop ecx inc ecx mov [eax+4], ecx cmp ecx, [ebp+FAT.ROOT_SECTORS] jnc fat_notroot_next_err pop ecx fat1x_root_first: mov eax, [eax+4] add eax, [ebp+FAT.ROOT_START] push ebx lea edi, [ebp+FAT.buffer] mov ebx, edi call fs_read32_sys pop ebx test eax, eax jz @f movi eax, ERROR_DEVICE stc @@: ret fat1x_root_begin_write: push edi eax call fat1x_root_first pop eax edi ret fat1x_root_end_write: pusha mov eax, [eax+4] add eax, [ebp+FAT.ROOT_START] lea ebx, [ebp+FAT.buffer] call fs_write32_sys popa ret fat_notroot_next: push ecx lea ecx, [ebp+FAT.buffer+0x200-0x20] cmp edi, ecx jae fat_notroot_next_sector add edi, 0x20 @@: pop ecx ret fat_notroot_next_write: push ecx lea ecx, [ebp+FAT.buffer+0x200] cmp edi, ecx jc @b push eax call fat_notroot_end_write pop eax fat_notroot_next_sector: push [ebp+FAT.longname_sec2] pop [ebp+FAT.longname_sec1] push eax call fat_get_sector mov [ebp+FAT.longname_sec2], eax pop eax mov ecx, [eax+4] inc ecx cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER] jae fat_notroot_next_cluster mov [eax+4], ecx jmp @f fat_notroot_next_err: pop ecx movi eax, ERROR_FILE_NOT_FOUND fat1x_root_extend_dir: stc ret fat_notroot_next_cluster: push eax mov eax, [eax] call get_FAT mov ecx, eax pop eax jc fat_notroot_first.deverr cmp ecx, 2 jb fat_notroot_next_err cmp ecx, [ebp+FAT.fatRESERVED] jae fat_notroot_next_err mov [eax], ecx and dword [eax+4], 0 @@: pop ecx fat_notroot_first: call fat_get_sector push ebx lea edi, [ebp+FAT.buffer] mov ebx, edi call fs_read32_sys pop ebx test eax, eax jz .ret ; CF=0 push ecx .deverr: pop ecx mov eax, ERROR_DEVICE stc .ret: ret fat_notroot_begin_write: push eax edi call fat_notroot_first pop edi eax ret fat_notroot_end_write: call fat_get_sector push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_sys pop ebx ret fat_notroot_extend_dir.writeerr: pop edx @@: pop eax ret fat_notroot_extend_dir: push eax call get_free_FAT jc @b push edx mov edx, [ebp+FAT.fatEND] call set_FAT jc .writeerr mov edx, eax mov eax, [esp+4] mov eax, [eax] push edx call set_FAT pop edx jc .writeerr push ecx or ecx, -1 call add_disk_free_space mov ecx, 512/4 lea edi, [ebp+FAT.buffer] push edi xor eax, eax rep stosd pop edi pop ecx mov eax, [esp+4] mov [eax], edx and dword [eax+4], 0 pop edx mov eax, [eax] dec eax dec eax push ebx ecx mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] imul eax, ecx add eax, [ebp+FAT.DATA_START] mov ebx, edi @@: push eax call fs_write32_sys pop eax inc eax loop @b pop ecx ebx eax clc ret fat_get_sector: push ecx mov ecx, [eax] dec ecx dec ecx imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER] add ecx, [ebp+FAT.DATA_START] add ecx, [eax+4] mov eax, ecx pop ecx ret ;---------------------------------------------------------------- fat_CreateFolder: mov [ebp+FAT.createOption], 0 jmp @f fat_CreateFile: mov [ebp+FAT.createOption], 1 @@: call fat_lock mov ecx, [ebx+12] mov edx, [ebx+16] .rename: pushad xor edi, edi push esi @@: lodsb test al, al jz @f cmp al, '/' jnz @b lea edi, [esi-1] jmp @b @@: pop esi test edi, edi jnz .noroot mov edx, [ebp+FAT.ROOT_CLUSTER] cmp [ebp+FAT.fs_type], 32 jz .pushnotroot xor edx, edx push edx push fat1x_root_extend_dir push fat1x_root_end_write push fat1x_root_next_write push fat1x_root_begin_write push edx push edx push fat1x_root_first push fat1x_root_next jmp .common1 .retNotFound: movi eax, ERROR_FILE_NOT_FOUND jmp .ret1 .noAccess: movi eax, ERROR_ACCESS_DENIED .ret1: mov [esp+28], eax call fat_unlock popad xor ebx, ebx ret .full: movi eax, ERROR_DISK_FULL jmp .ret1 .noroot: cmp byte [edi+1], 0 jz .noAccess ; check existence mov byte [edi], 0 push edi call hd_find_lfn pop esi mov byte [esi], '/' jc .retNotFound inc esi test byte [edi+11], 0x10 jz .noAccess ; file mov edx, [edi+20-2] mov dx, [edi+26] movi eax, ERROR_FS_FAIL cmp edx, 2 jb .ret1 .pushnotroot: push edx push fat_notroot_extend_dir push fat_notroot_end_write push fat_notroot_next_write push fat_notroot_begin_write push 0 push edx push fat_notroot_first push fat_notroot_next .common1: call fat_find_lfn jc .notfound test byte [edi+11], 10h jz .exists_file ; found directory add esp, 36 call fat_unlock popad xor eax, eax cmp [ebp+FAT.createOption], 0 jz @f mov al, ERROR_ACCESS_DENIED @@: xor ebx, ebx ret .exists_file: cmp [ebp+FAT.createOption], 1 jz @f add esp, 36 jmp .noAccess @@: ; delete FAT chain push edi xor eax, eax mov dword [edi+28], eax ; zero size xor ecx, ecx mov eax, [edi+20-2] mov ax, [edi+26] mov word [edi+20], cx mov word [edi+26], cx test eax, eax jz .done1 @@: cmp eax, [ebp+FAT.fatRESERVED] jae .done1 xor edx, edx call set_FAT mov eax, edx jc .done1 inc ecx jmp @b .short_name_found: pop ecx edi esi call fat_next_short_name jnc .test_short_name_loop .disk_full: add esp, 12+36 jmp .full .notfound: ; generate short name call fat_name_is_legal jc @f add esp, 36 jmp .retNotFound @@: sub esp, 12 mov edi, esp call fat_gen_short_name .test_short_name_loop: push esi edi ecx mov esi, edi lea eax, [esp+12+12+8] mov edx, [eax+24] mov [eax], edx and dword [eax+4], 0 call dword [eax-4] jc .found .test_short_name_entry: cmp byte [edi+11], 0xF jz .test_short_name_cont mov ecx, 11 push esi edi repz cmpsb pop edi esi jz .short_name_found .test_short_name_cont: lea eax, [esp+12+12+8] call dword [eax-8] jnc .test_short_name_entry .found: pop ecx edi esi ; now find space in directory ; we need to save LFN <=> LFN is not equal to short name <=> generated name contains '~' mov al, '~' push ecx edi mov ecx, 8 repnz scasb movi eax, 1 ; 1 entry jnz .notilde ; we need ceil(strlen(esi)/13) additional entries = floor((strlen(esi)+12+13)/13) total xor ecx, ecx push esi @@: call utf8to16 inc ecx test ax, ax jnz @b pop esi mov eax, ecx add eax, 12+13-1 mov ecx, 13 cdq div ecx .notilde: push -1 push -1 push -1 ; find successive entries in directory xor ecx, ecx push eax lea eax, [esp+16+8+12+8] mov edx, [eax+24] mov [eax], edx and dword [eax+4], 0 call dword [eax-4] pop eax jnc .scan_dir .fsfrfe3: add esp, 12+8+12+36 movi eax, ERROR_DEVICE jmp .ret1 .scan_dir: cmp byte [edi], 0 jz .free cmp byte [edi], 0xE5 jz .free xor ecx, ecx .scan_cont: push eax lea eax, [esp+16+8+12+8] call dword [eax-8] mov edx, eax pop eax jnc .scan_dir cmp edx, ERROR_DEVICE jz .fsfrfe3 push eax lea eax, [esp+16+8+12+8] call dword [eax+20] ; extend directory pop eax jnc .scan_dir add esp, 12+8+12+36 jmp .full .free: test ecx, ecx jnz @f mov [esp], edi mov ecx, [esp+12+8+12+8] mov [esp+4], ecx mov ecx, [esp+12+8+12+12] mov [esp+8], ecx xor ecx, ecx @@: inc ecx cmp ecx, eax jb .scan_cont ; found! push esi ecx ; If creating a directory, allocate one data cluster or fail immediately if this is impossible. ; This prevents from creating an invalid directory entry on a full disk. cmp [ebp+FAT.createOption], 0 jnz .notFolder call get_free_FAT jnc @f add esp, 8+12+8 jmp .disk_full @@: mov [esp+8+12+8+12+36+20], eax ; store the cluster somewhere .notFolder: ; calculate name checksum mov esi, [esp+8+12] mov ecx, 11 xor eax, eax @@: ror al, 1 add al, [esi] inc esi loop @b pop ecx esi edi pop dword [esp+8+12+12] pop dword [esp+8+12+12] ; edi points to first entry in free chunk dec ecx jz .nolfn push esi eax lea eax, [esp+8+8+12+8] call dword [eax+8] ; begin write mov al, 40h .writelfn: or al, cl stosb mov esi, [esp+4] push ecx dec ecx jz @f imul ecx, 13 .scroll: call utf8to16 loop .scroll @@: mov cl, 5 call fat_read_symbols mov ax, 0xF stosw mov al, [esp+4] stosb mov cl, 6 call fat_read_symbols xor eax, eax stosw mov cl, 2 call fat_read_symbols pop ecx lea eax, [esp+8+8+12+8] call dword [eax+12] ; next write xor eax, eax loop .writelfn pop eax esi .nolfn: pop esi add esp, 16 mov ecx, 11 rep movsb cmp [ebp+FAT.createOption], 2 jz .copy mov word [edi], 20h ; attributes sub edi, 11 mov byte [edi+13], 0 ; tenths of a second at file creation time call get_time_for_file mov [edi+14], ax ; creation time mov [edi+22], ax ; last write time call get_date_for_file mov [edi+16], ax ; creation date mov [edi+24], ax ; last write date mov [edi+18], ax ; last access date mov word [edi+20], cx ; high word of cluster mov word [edi+26], cx ; low word of cluster - to be filled mov dword [edi+28], ecx ; file size - to be filled cmp [ebp+FAT.createOption], 0 jnz .doit ; create directory mov byte [edi+11], 10h ; attributes: folder mov esi, edi mov eax, [esp+36+20] ; extract saved cluster mov [esp+36+20], edi ; this is needed for calculating arg of add_disk_free_space! push ecx mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] shl ecx, 9 push ecx push edi jmp .doit2 .copy: lea esi, [esp+72+11] mov cl, 21 rep movsb sub edi, 32 jmp .doit .done1: pop edi call get_time_for_file mov [edi+22], ax call get_date_for_file mov [edi+24], ax mov [edi+18], ax or byte [edi+11], 20h ; set 'archive' attribute .doit: mov esi, [esp+36+20] lea eax, [esp+8] call dword [eax+16] ; flush directory push ecx mov ecx, [esp+4+36+24] xor eax, eax test ecx, ecx jz .done push ecx edi call get_free_FAT jc .diskfull .doit2: push eax mov [edi+26], ax shr eax, 16 mov [edi+20], ax lea eax, [esp+16+8] call dword [eax+16] ; flush directory pop eax mov edx, [ebp+FAT.fatEND] call set_FAT .write_cluster: push eax dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] push [ebp+FAT.SECTORS_PER_CLUSTER] .write_sector: cmp [ebp+FAT.createOption], 0 jz .writedir mov ecx, 512 cmp dword [esp+12], ecx jb .writeshort ; we can write directly from given buffer mov ebx, esi add esi, ecx jmp .writecommon .writedir: push 512 lea edi, [ebp+FAT.buffer] mov ebx, edi mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] shl ecx, 9 cmp ecx, [esp+16] jnz .writedircont dec dword [esp+20] push esi mov ecx, 32/4 rep movsd pop esi mov dword [edi-32], '. ' mov dword [edi-32+4], ' ' mov dword [edi-32+8], ' ' mov byte [edi-32+11], 10h push esi mov ecx, 32/4 rep movsd pop esi mov dword [edi-32], '.. ' mov dword [edi-32+4], ' ' mov dword [edi-32+8], ' ' mov byte [edi-32+11], 10h mov ecx, [esp+20+36] cmp ecx, [ebp+FAT.ROOT_CLUSTER] jnz @f xor ecx, ecx @@: mov word [edi-32+26], cx shr ecx, 16 mov [edi-32+20], cx jmp .writedircont .writeshort: mov ecx, [esp+12] push ecx lea edi, [ebp+FAT.buffer] mov ebx, edi rep movsb .writedircont: lea ecx, [ebp+FAT.buffer+0x200] sub ecx, edi push eax xor eax, eax rep stosb pop eax pop ecx .writecommon: push eax call fs_write32_app test eax, eax pop eax jnz .writeerr inc eax sub dword [esp+12], ecx jz .writedone dec dword [esp] jnz .write_sector pop eax ; allocate new cluster pop eax mov ecx, eax call get_free_FAT jc .diskfull mov edx, [ebp+FAT.fatEND] call set_FAT xchg eax, ecx mov edx, ecx call set_FAT xchg eax, ecx jmp .write_cluster .diskfull: mov eax, ERROR_DISK_FULL jmp .ret .writeerr: pop eax eax sub esi, ecx mov eax, ERROR_DEVICE jmp .ret .writedone: pop eax eax xor eax, eax .ret: pop edi ecx inc ecx .done: sub esi, [esp+4+36+20] mov [esp+4+36+28], eax mov [esp+4+36+16], esi jecxz @f lea eax, [esp+12] call dword [eax+8] mov [edi+28], esi call dword [eax+16] @@: lea eax, [esi+511] shr eax, 9 mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] lea eax, [eax+ecx-1] xor edx, edx div ecx pop ecx sub ecx, eax call add_disk_free_space add esp, 36 cmp [ebp+FAT.createOption], 2 jz @f call update_disk call fat_unlock @@: popad ret @@: or eax, -1 rep stosw ret fat_read_symbols: test esi, esi jz @b call utf8to16 stosw test ax, ax jnz @f xor esi, esi @@: loop fat_read_symbols ret ;---------------------------------------------------------------- fat_Write: call fat_lock call hd_find_lfn jc .error cmp dword [ebx+8], 0 jnz .eof ; FAT does not support files larger than 4GB mov ecx, [ebx+12] mov edx, [ebx+16] mov ebx, [ebx+4] ; now edi points to direntry, ebx=start byte to write, ; ecx=number of bytes to write, edx=data pointer ; extend file if needed add ecx, ebx jc .eof ; FAT does not support files larger than 4GB push edx push eax ; save directory sector push 0 ; return value=0 call get_time_for_file mov [edi+22], ax ; last write time call get_date_for_file mov [edi+24], ax ; last write date mov [edi+18], ax ; last access date push dword [edi+28] ; save current file size cmp ecx, [edi+28] jbe .length_ok cmp ecx, ebx jz .length_ok call hd_extend_file jnc .length_ok mov [esp+4], eax ; hd_extend_file can return three error codes: FAT table error, device error or disk full. ; First two cases are fatal errors, in third case we may write some data cmp al, ERROR_DISK_FULL jnz @f ; correct number of bytes to write mov ecx, [edi+28] cmp ecx, ebx ja .length_ok push 0 .ret: pop eax eax eax ecx ecx sub edx, ecx push eax edx call update_disk pop ebx @@: call fat_unlock pop eax ret .error: push eax jmp @b .eof: push ERROR_END_OF_FILE xor ebx, ebx jmp @b .device_err2: pop ecx .device_err: mov byte [esp+8], ERROR_DEVICE jmp .ret .fat_err: mov byte [esp+8], ERROR_FS_FAIL jmp .ret .length_ok: mov esi, [edi+28] mov eax, [edi+20-2] mov ax, [edi+26] mov edi, eax ; edi=current cluster push 0 ; current sector in cluster ; save directory mov eax, [esp+12] push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_sys pop ebx test eax, eax jnz .device_err ; now ebx=start pos, ecx=end pos, both lie inside file sub ecx, ebx jz .ret .write_loop: ; skip unmodified sectors cmp dword [esp+4], 0x200 jb .modify sub ebx, 0x200 jae .skip add ebx, 0x200 .modify: ; get length of data in current sector push ecx sub ebx, 0x200 jb .hasdata neg ebx xor ecx, ecx jmp @f .hasdata: neg ebx cmp ecx, ebx jbe @f mov ecx, ebx @@: ; get current sector number mov eax, edi dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] add eax, [esp+4] ; load sector if needed cmp dword [esp+8], 0 ; we don't need to read uninitialized data jz .noread cmp ecx, 0x200 ; we don't need to read sector if it is fully rewritten jz .noread cmp ecx, esi ; (same for the last sector) jz .noread push eax ebx lea ebx, [ebp+FAT.buffer] call fs_read32_app test eax, eax pop ebx eax jnz .device_err2 .noread: ; zero uninitialized data if file was extended (because hd_extend_file does not this) push eax ecx edi xor eax, eax mov ecx, 0x200 sub ecx, [esp+8+12] jbe @f lea edi, [ebp+FAT.buffer] add edi, [esp+8+12] rep stosb @@: ; zero uninitialized data in the last sector mov ecx, 0x200 sub ecx, esi jbe @f lea edi, [ebp+FAT.buffer+esi] rep stosb @@: pop edi ecx ; copy new data mov eax, edx neg ebx jecxz @f lea ebx, [ebp+FAT.buffer+0x200+ebx] call memmove xor ebx, ebx @@: pop eax ; save sector push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_app pop ebx test eax, eax jnz .device_err2 add edx, ecx sub [esp], ecx pop ecx jz .ret .skip: ; next sector pop eax inc eax push eax cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER] jb @f and dword [esp], 0 mov eax, edi call get_FAT mov edi, eax jc .device_err cmp edi, 2 jb .fat_err cmp edi, [ebp+FAT.fatRESERVED] jae .fat_err @@: sub esi, 0x200 jae @f xor esi, esi @@: sub dword [esp+4], 0x200 jae @f and dword [esp+4], 0 @@: jmp .write_loop hd_extend_file.zero_size: xor eax, eax jmp hd_extend_file.start_extend ; extends file on hd to given size (new data area is undefined) ; in: edi->direntry, ecx=new size ; out: CF=0 => OK, eax=0 ; CF=1 => error, eax=code (ERROR_FS_FAIL or ERROR_DISK_FULL or ERROR_DEVICE) hd_extend_file: push esi mov esi, [ebp+FAT.SECTORS_PER_CLUSTER] imul esi, [ebp+FAT.BYTES_PER_SECTOR] push ecx ; find the last cluster of file mov eax, [edi+20-2] mov ax, [edi+26] mov ecx, [edi+28] jecxz .zero_size .last_loop: sub ecx, esi jbe .last_found call get_FAT jnc @f .device_err: pop ecx .device_err2: pop esi push ERROR_DEVICE .ret_err: pop eax stc ret @@: cmp eax, 2 jb .fat_err cmp eax, [ebp+FAT.fatRESERVED] jb .last_loop .fat_err: pop ecx esi push ERROR_FS_FAIL jmp .ret_err .last_found: push eax call get_FAT jnc @f pop eax jmp .device_err @@: cmp eax, [ebp+FAT.fatRESERVED] pop eax jb .fat_err ; set length to full number of clusters sub [edi+28], ecx .start_extend: pop ecx ; now do extend push edx mov edx, 2 ; start scan from cluster 2 .extend_loop: cmp [edi+28], ecx jae .extend_done ; add new cluster push eax call get_free_FAT jc .disk_full mov edx, [ebp+FAT.fatEND] call set_FAT mov edx, eax pop eax test eax, eax jz .first_cluster push edx call set_FAT pop edx jmp @f .first_cluster: ror edx, 16 mov [edi+20], dx ror edx, 16 mov [edi+26], dx @@: push ecx mov ecx, -1 call add_disk_free_space pop ecx mov eax, edx add [edi+28], esi jmp .extend_loop .extend_done: mov [edi+28], ecx pop edx esi xor eax, eax ; CF=0 ret .device_err3: pop edx jmp .device_err2 .disk_full: pop eax edx esi movi eax, ERROR_DISK_FULL stc ret ;---------------------------------------------------------------- fat_SetFileEnd: call fat_lock call hd_find_lfn jc .reteax ; must not be directory test byte [edi+11], 10h jnz .access_denied ; file size must not exceed 4 Gb cmp dword [ebx+8], 0 jnz .endOfFile push eax ; save directory sector ; set file modification date/time to current call get_time_for_file mov [edi+22], ax ; last write call get_date_for_file mov [edi+24], ax ; last write mov [edi+18], ax ; last access mov eax, [ebx+4] cmp eax, [edi+28] jb .truncate ja .expand pop eax lea ebx, [ebp+FAT.buffer] call fs_write32_sys test eax, eax jnz .errorDevice push 0 jmp .ret .access_denied: push ERROR_ACCESS_DENIED jmp .ret .endOfFile: push ERROR_END_OF_FILE jmp .ret .errorDevice: push ERROR_DEVICE jmp .ret .expand: push ebx ebp ecx push dword [edi+28] ; save old size mov ecx, eax call hd_extend_file push eax ; return code jnc .expand_ok cmp al, ERROR_DISK_FULL jnz .pop_ret .expand_ok: ; save directory mov eax, [edi+28] xchg eax, [esp+20] lea ebx, [ebp+FAT.buffer] call fs_write32_sys test eax, eax jnz .pop_ret11 mov eax, [esp+20] sub eax, [esp+4] cmp eax, 1000001h jnc .pop_ret mov eax, [edi+20-2] mov ax, [edi+26] mov edi, eax test edi, edi jz .pop_ret ; now zero new data push 0 ; edi=current cluster, [esp]=sector in cluster ; [esp+24]=new size, [esp+8]=old size, [esp+4]=return code .zero_loop: cmp edi, 2 jb .error_fat cmp edi, [ebp+FAT.fatRESERVED] jae .error_fat sub dword [esp+8], 0x200 jae .next_cluster lea eax, [edi-2] imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] add eax, [esp] cmp dword [esp+8], -0x200 jz .noread push eax lea ebx, [ebp+FAT.buffer] call fs_read32_app test eax, eax pop eax jnz .err_next .noread: mov ecx, [esp+8] neg ecx push edi lea edi, [ebp+FAT.buffer+0x200] add edi, [esp+12] push eax xor eax, eax mov [esp+16], eax rep stosb pop eax pop edi call fs_write32_app test eax, eax jz .next_cluster .err_next: mov byte [esp+4], ERROR_DEVICE .next_cluster: pop eax sub dword [esp+20], 0x200 jbe .pop_ret inc eax push eax cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER] jb .zero_loop and dword [esp], 0 mov eax, edi call get_FAT mov edi, eax jnc .zero_loop pop eax .pop_ret11: mov byte [esp], ERROR_DEVICE .pop_ret: call update_disk pop eax ecx ecx ebp ebx ecx .reteax: push eax .ret: call fat_unlock pop eax ret .error_fat: pop eax mov byte [esp], ERROR_FS_FAIL jmp .pop_ret .error_fat2: pop eax ecx eax call update_disk push ERROR_FS_FAIL jmp .ret .truncate: mov [edi+28], eax push ecx mov ecx, [edi+20-2] mov cx, [edi+26] push eax test eax, eax jz .zero_size @@: ; find new last cluster cmp ecx, 2 jb .error_fat2 cmp ecx, [ebp+FAT.fatRESERVED] jae .error_fat2 mov eax, [ebp+FAT.SECTORS_PER_CLUSTER] shl eax, 9 sub [esp], eax jbe @f mov eax, ecx call get_FAT mov ecx, eax jnc @b .device_err3: pop eax ecx eax call update_disk push ERROR_DEVICE jmp .ret @@: ; we will zero data at the end of last sector - remember it push ecx ; terminate FAT chain push edx mov eax, ecx mov edx, [ebp+FAT.fatEND] call set_FAT mov eax, edx pop edx jnc @f .device_err4: pop ecx jmp .device_err3 .zero_size: and word [edi+20], 0 and word [edi+26], 0 push 0 mov eax, ecx @@: ; delete FAT chain call clear_cluster_chain jc .device_err4 ; save directory mov eax, [esp+12] push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_sys pop ebx test eax, eax jnz .device_err4 ; zero last sector, ignore errors pop ecx pop eax dec ecx imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER] add ecx, [ebp+FAT.DATA_START] push eax sar eax, 9 add ecx, eax pop eax and eax, 0x1FF jz .truncate_done push ebx eax mov eax, ecx lea ebx, [ebp+FAT.buffer] call fs_read32_app pop eax lea edi, [ebp+FAT.buffer+eax] push ecx mov ecx, 0x200 sub ecx, eax xor eax, eax rep stosb pop eax call fs_write32_app pop ebx .truncate_done: pop ecx eax call update_disk call fat_unlock xor eax, eax ret ;---------------------------------------------------------------- fat_GetFileInfo: cmp byte [esi], 0 jz .volume call fat_lock call hd_find_lfn jc @f push ebp xor ebp, ebp mov esi, [ebx+16] mov dword [esi+4], ebp call fat_entry_to_bdfe2 pop ebp xor eax, eax @@: push eax call fat_unlock pop eax @@: ret .volume: mov eax, dword[ebp+FAT.Length] mov edx, dword[ebp+FAT.Length+4] mov edi, [ebx+16] shld edx, eax, 9 shl eax, 9 mov [edi+36], edx mov [edi+32], eax mov eax, [ebx+8] mov byte [edi], 8 mov [edi+4], eax test eax, eax jz @b lea esi, [ebp+FAT.volumeLabel] mov ecx, 11 @@: mov byte [esi+ecx], 0 dec ecx jz @f cmp byte [esi+ecx], ' ' jz @b @@: mov cl, 12 add edi, 40 cmp eax, 2 jz @f rep movsb xor eax, eax ret @@: lodsb stosw loop @b ret ;---------------------------------------------------------------- fat_SetFileInfo: call fat_lock call hd_find_lfn jc @f push eax mov edx, [ebx+16] call bdfe_to_fat_entry pop eax lea ebx, [ebp+FAT.buffer] call fs_write32_sys call update_disk xor eax, eax @@: push eax call fat_unlock pop eax ret ;---------------------------------------------------------------- fat_Delete: call fat_lock and [ebp+FAT.longname_sec1], 0 and [ebp+FAT.longname_sec2], 0 call hd_find_lfn jc .notFound cmp dword [edi], '. ' jz .access_denied2 cmp dword [edi], '.. ' jz .access_denied2 test byte [edi+11], 10h jz .dodel ; we can delete only empty folders! pushad mov esi, [edi+20-2] mov si, [edi+26] xor ecx, ecx lea eax, [esi-2] imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] lea ebx, [ebp+FAT.buffer] call fs_read32_sys test eax, eax jnz .err1 lea eax, [ebx+0x200] add ebx, 2*0x20 .checkempty: cmp byte [ebx], 0 jz .empty cmp byte [ebx], 0xE5 jnz .notempty add ebx, 0x20 cmp ebx, eax jb .checkempty inc ecx cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER] jb @f mov eax, esi call get_FAT jc .err1 cmp eax, 2 jb .error_fat cmp eax, [ebp+FAT.fatRESERVED] jae .empty mov esi, eax xor ecx, ecx @@: lea eax, [esi-2] imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] add eax, ecx lea ebx, [ebp+FAT.buffer] call fs_read32_sys test eax, eax lea eax, [ebx+0x200] jz .checkempty .err1: popad .err2: push ERROR_DEVICE .ret: call fat_unlock pop eax ret .notFound: push ERROR_FILE_NOT_FOUND jmp .ret .error_fat: popad push ERROR_FS_FAIL jmp .ret .notempty: popad .access_denied2: push ERROR_ACCESS_DENIED jmp .ret .empty: popad push eax ebx lea ebx, [ebp+FAT.buffer] call fs_read32_sys test eax, eax pop ebx eax jnz .err2 .dodel: push eax mov eax, [edi+20-2] mov ax, [edi+26] xchg eax, [esp] ; delete folder entry mov byte [edi], 0xE5 ; delete LFN (if present) .lfndel: lea edx, [ebp+FAT.buffer] cmp edi, edx ja @f cmp [ebp+FAT.longname_sec2], 0 jz .lfndone push [ebp+FAT.longname_sec2] push [ebp+FAT.longname_sec1] pop [ebp+FAT.longname_sec2] and [ebp+FAT.longname_sec1], 0 push ebx mov ebx, edx call fs_write32_sys mov eax, [esp+4] call fs_read32_sys pop ebx pop eax lea edi, [ebp+FAT.buffer+0x200] @@: sub edi, 0x20 cmp byte [edi], 0xE5 jz .lfndone cmp byte [edi+11], 0xF jnz .lfndone mov byte [edi], 0xE5 jmp .lfndel .lfndone: push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_sys pop ebx ; delete FAT chain pop eax call clear_cluster_chain call update_disk call fat_unlock xor eax, eax ret ;---------------------------------------------------------------- fat_Rename: ; in: edi -> new path string in UTF-8 push esi edi call fat_lock call hd_find_lfn pop ebx jc .error sub esp, 32 mov esi, edi mov edi, esp mov ecx, 8 rep movsd mov [ebp+FAT.createOption], 2 mov esi, ebx call fat_CreateFile.rename add esp, 32 pop esi test eax, eax jnz .ret push eax mov [ebp+FAT.longname_sec1], eax mov [ebp+FAT.longname_sec2], eax call hd_find_lfn jc .error mov byte [edi], 0xE5 jmp fat_Delete.lfndel .error: push eax call fat_unlock pop eax ebx .ret: ret