;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;; FAT functions for KolibriOS ;; ;; ;; ;; Copyright 2002 Paolo Minazzi, paolo.minazzi@inwind.it ;; ;; ;; ;; See file COPYING for details ;; ;; 06.2015 fs_read64 - pathoswithin ;; ;; 04.02.2007 LFN create folder - diamond ;; ;; 08.10.2006 LFN delete file/folder - diamond ;; ;; 20.08.2006 LFN set file size (truncate/extend) - diamond ;; ;; 17.08.2006 LFN write/append to file - diamond ;; ;; 23.06.2006 LFN start application - diamond ;; ;; 15.06.2006 LFN get/set file/folder info - diamond ;; ;; 27.05.2006 LFN create/rewrite file - diamond ;; ;; 04.05.2006 LFN read folder - diamond ;; ;; 29.04.2006 Elimination of hangup after the ;; ;; expiration hd_wait_timeout - Mario79 ;; ;; 23.04.2006 LFN read file - diamond ;; ;; 28.01.2006 find all Fat16/32 partition in all input point ;; ;; to MBR, see file part_set.inc - Mario79 ;; ;; 15.01.2005 get file size/attr/date, file_append - ATV ;; ;; 04.12.2004 skip volume label, file delete bug fixed - ATV ;; ;; 29.11.2004 get_free_FAT changed, append dir bug fixed - ATV ;; ;; 23.11.2004 don't allow overwrite dir with file - ATV ;; ;; 18.11.2004 get_disk_info and more error codes - ATV ;; ;; 17.11.2004 set_FAT/get_FAT and disk cache rewritten - ATV ;; ;; 10.11.2004 removedir clear whole directory structure - ATV ;; ;; 08.11.2004 rename - ATV ;; ;; 30.10.2004 file_read return also dirsize in bytes - ATV ;; ;; 20.10.2004 Makedir/Removedir - ATV ;; ;; 14.10.2004 Partition chain/Fat16 - ATV (thanks drh3xx) ;; ;; 06.09.2004 Fix free space - Mario79 ;; ;; 24.05.2004 Write back buffer for File_write - VT ;; ;; 20.05.2004 File_read function to work with syscall 58 - VT ;; ;; 30.03.2004 Error parameters at function return - VT ;; ;; 29.06.2002 Improved fat32 verification - VT ;; ;; 20.05.2002 Hd status check - VT ;; ;; 01.05.2002 Bugfix in device write - VT ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; $Revision$ cache_max equ 1919 ; max. is 1919*512+0x610000=0x6ffe00 PUSHAD_EAX equ [esp+28] PUSHAD_ECX equ [esp+24] PUSHAD_EDX equ [esp+20] PUSHAD_EBX equ [esp+16] PUSHAD_EBP equ [esp+8] PUSHAD_ESI equ [esp+4] PUSHAD_EDI equ [esp+0] ; Internal data for every FAT partition. struct FAT PARTITION fs_type db ? fat16_root db 0 ; flag for fat16 rootdir fat_change db 0 ; 1=fat has changed 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 0x1f3a NUMBER_OF_FATS dd 0x2 SECTORS_PER_CLUSTER dd 0x8 BYTES_PER_SECTOR dd 0x200 ; Note: if BPS <> 512 need lots of changes ROOT_CLUSTER dd 2 ; first rootdir cluster FAT_START dd 0 ; start of fat table ROOT_START dd 0 ; start of rootdir (only fat16) ROOT_SECTORS dd 0 ; count of rootdir sectors (only fat16) DATA_START dd 0 ; start of data area (=first cluster 2) LAST_CLUSTER dd 0 ; last availabe cluster ADR_FSINFO dd 0 ; used only by fat32 fatRESERVED dd 0x0FFFFFF6 fatBAD dd 0x0FFFFFF7 fatEND dd 0x0FFFFFF8 fatMASK dd 0x0FFFFFFF fatStartScan dd 2 cluster_tmp dd 0 ; used by analyze_directory ; and analyze_directory_to_write longname_sec1 dd 0 ; used by analyze_directory to save 2 previous longname_sec2 dd 0 ; directory sectors for delete long filename fat_in_cache dd -1 ; For FAT16/FAT32, this points to 512-byte buffer for the current sector of FAT. ; For FAT12, the entire FAT structure is read ; and unpacked from 12bit per cluster to word per cluster. ; Note: work with unpacked copy of FAT12 means ; additional memory and additional code for packing/unpacking. ; I'm not sure that the economy justifies the cost, but anyway, ; there is how work was done before my edits, and I'm just keeping the principle. fat_cache_ptr dd ? fat12_unpacked_ptr dd ? buffer rb 512 fsinfo_buffer rb 512 ends uglobal align 4 partition_count dd 0 ; partitions found by set_FAT32_variables hd_error dd 0 hd_setup dd 0 hd_wait_timeout dd 0 cache_search_start dd 0 ; used by find_empty_slot endg uglobal align 4 Sector512: ; label for dev_hdcd.inc buffer: rb 512 endg iglobal align 4 fat_user_functions: dd fat_free dd (fat_user_functions_end - fat_user_functions - 4) / 4 dd fat_Read dd fat_ReadFolder dd fat_Rewrite dd fat_Write dd fat_SetFileEnd dd fat_GetFileInfo dd fat_SetFileInfo dd 0 dd fat_Delete dd fat_CreateFolder fat_user_functions_end: endg ; these labels are located before the main function to make ; most of jumps to these be short fat_create_partition.free_return0: mov eax, ebp call free pop ebp fat_create_partition.return0: xor eax, eax ret fat_create_partition: ; sector size must be 512 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 @@: ; total sector count must not exceed partition size cmp dword [ebp+FAT.Length+4], 0 jnz @f cmp eax, dword [ebp+FAT.Length] ja .free_return0 @@: mov dword [ebp+FAT.Length], eax and dword [ebp+FAT.Length+4], 0 sub eax, [ebp+FAT.DATA_START] ; eax = count of data sectors jc .free_return0 xor edx, edx div [ebp+FAT.SECTORS_PER_CLUSTER] inc eax mov [ebp+FAT.LAST_CLUSTER], eax dec eax ; cluster count jz .free_return0 mov [ebp+FAT.fatStartScan], 2 ; limits by Microsoft Hardware White Paper v1.03 cmp eax, 4085 ; 0xff5 jb .fat12 cmp eax, 65525 ; 0xfff5 jb .fat16 .fat32: mov eax, [ebx+0x2c] ; rootdir cluster mov [ebp+FAT.ROOT_CLUSTER], eax movzx eax, word [ebx+0x30] mov [ebp+FAT.ADR_FSINFO], eax push ebx add ebx, 512 call fs_read32_sys test eax, eax jnz @f mov eax, [ebx+0x1ec] cmp eax, -1 jz @f mov [ebp+FAT.fatStartScan], eax @@: pop ebx mov [ebp+FAT.fatRESERVED], 0x0FFFFFF6 mov [ebp+FAT.fatBAD], 0x0FFFFFF7 mov [ebp+FAT.fatEND], 0x0FFFFFF8 mov [ebp+FAT.fatMASK], 0x0FFFFFFF mov al, 32 .fat_not_12_finalize: mov [ebp+FAT.fs_type], al ; For FAT16 and FAT32, allocate 512 bytes for FAT cache. mov eax, 512 call malloc test eax, eax jz .free_return0 mov [ebp+FAT.fat_cache_ptr], eax mov eax, ebp pop ebp ret .fat16: and [ebp+FAT.ROOT_CLUSTER], 0 mov [ebp+FAT.fatRESERVED], 0x0000FFF6 mov [ebp+FAT.fatBAD], 0x0000FFF7 mov [ebp+FAT.fatEND], 0x0000FFF8 mov [ebp+FAT.fatMASK], 0x0000FFFF mov al, 16 jmp .fat_not_12_finalize .fat12: and [ebp+FAT.ROOT_CLUSTER], 0 mov [ebp+FAT.fatRESERVED], 0xFF6 mov [ebp+FAT.fatBAD], 0xFF7 mov [ebp+FAT.fatEND], 0xFFF mov [ebp+FAT.fatMASK], 0xFFF mov al, 12 mov [ebp+FAT.fs_type], al ; For FAT12, allocate&read data for entire table: ; calculate A = ALIGN_UP(NUM_CLUSTERS, 8), ; calculatefatchain/restorefatchain will process A items, ; allocate ALIGN_UP(A*3/2, 512) bytes for FAT table plus A*2 bytes for unpacked data. mov eax, [ebp+FAT.LAST_CLUSTER] and eax, not 7 add eax, 8 mov edx, eax lea eax, [eax*3] add eax, 512*2-1 shr eax, 10 shl eax, 9 lea eax, [eax+edx*2] call malloc test eax, eax jz .free_return0 ; Read ALIGN_UP(NUM_CLUSTERS*3/2, 512) bytes. ; Note that this can be less than allocated, this is ok, ; overallocation simplifies calculatefatchain/restorefatchain. push ebx mov [ebp+FAT.fat_cache_ptr], eax mov edx, [ebp+FAT.LAST_CLUSTER] lea edx, [(edx+1)*3 + 512*2-1] shr edx, 10 xchg eax, ebx xor eax, eax .read_fat: push eax add eax, [ebp+FAT.FAT_START] call fs_read32_sys test eax, eax pop eax jz @f dbgstr 'Failed to read FAT table' mov eax, [ebp+FAT.fat_cache_ptr] call free pop ebx jmp .free_return0 @@: add ebx, 512 inc eax cmp eax, edx jb .read_fat mov [ebp+FAT.fat12_unpacked_ptr], ebx call calculatefatchain pop ebx mov eax, ebp pop ebp ret fat_free: push eax mov eax, [eax+FAT.fat_cache_ptr] call free pop eax jmp free calculatefatchain: pushad mov esi, [ebp+FAT.fat_cache_ptr] mov edi, [ebp+FAT.fat12_unpacked_ptr] mov edx, [ebp+FAT.LAST_CLUSTER] and edx, not 7 lea edx, [edi+(edx+8)*2] push edx fcnew: mov eax, dword [esi] mov ebx, dword [esi+4] mov ecx, dword [esi+8] mov edx, ecx shr edx, 4;8 ok shr dx, 4;7 ok xor ch, ch shld ecx, ebx, 20;6 ok shr cx, 4;5 ok shld ebx, eax, 12 and ebx, 0x0fffffff;4 ok shr bx, 4;3 ok shl eax, 4 and eax, 0x0fffffff;2 ok shr ax, 4;1 ok mov dword [edi], eax mov dword [edi+4], ebx mov dword [edi+8], ecx mov dword [edi+12], edx add edi, 16 add esi, 12 cmp edi, [esp] jnz fcnew pop eax popad ret restorefatchain: ; restore fat chain pushad mov esi, [ebp+FAT.fat12_unpacked_ptr] mov edi, [ebp+FAT.fat_cache_ptr] mov edx, [ebp+FAT.LAST_CLUSTER] and edx, not 7 lea edx, [esi+(edx+8)*2] fcnew2: mov eax, dword [esi] mov ebx, dword [esi+4] shl ax, 4 shl eax, 4 shl bx, 4 shr ebx, 4 shrd eax, ebx, 8 shr ebx, 8 mov dword [edi], eax mov word [edi+4], bx add edi, 6 add esi, 8 cmp esi, edx jb fcnew2 mov esi, [ebp+FAT.NUMBER_OF_FATS] mov edx, [ebp+FAT.LAST_CLUSTER] lea edx, [(edx+1)*3 + 512*2-1] shr edx, 10 push [ebp+FAT.FAT_START] .write_fats: xor eax, eax mov ebx, [ebp+FAT.fat_cache_ptr] .loop1: push eax add eax, [esp+4] call fs_write32_sys test eax, eax pop eax jnz .fail add ebx, 512 inc eax cmp eax, edx jb .loop1 pop eax add eax, [ebp+FAT.SECTORS_PER_FAT] push eax dec esi jnz .write_fats pop eax popad ret .fail: dbgstr 'Failed to save FAT' popad ret iglobal label fat_legal_chars byte ; 0 = not allowed ; 1 = allowed only in long names ; 3 = allowed times 32 db 0 ; ! " # $ % & ' ( ) * + , - . / db 1,3,0,3,3,3,3,3,3,3,0,1,1,3,3,0 ; 0 1 2 3 4 5 6 7 8 9 : ; < = > ? db 3,3,3,3,3,3,3,3,3,3,0,1,0,1,0,0 ; @ A B C D E F G H I J K L M N O db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 ; P Q R S T U V W X Y Z [ \ ] ^ _ db 3,3,3,3,3,3,3,3,3,3,3,1,0,1,3,3 ; ` a b c d e f g h i j k l m n o db 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 ; p q r s t u v w x y z { | } ~ db 3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0 endg fat_name_is_legal: ; in: esi->(long) name ; out: CF set <=> legal ; destroys eax push esi xor eax, eax @@: lodsb test al, al jz .done cmp al, 80h jae .big test [fat_legal_chars+eax], 1 jnz @b .err: pop esi clc ret .big: ; 0x80-0xAF, 0xE0-0xEF cmp al, 0xB0 jb @b cmp al, 0xE0 jb .err cmp al, 0xF0 jb @b jmp .err .done: sub esi, [esp] cmp esi, 257 pop esi ret fat_next_short_name: ; in: edi->8+3 name ; out: name corrected ; CF=1 <=> error pushad mov ecx, 8 mov al, '~' std push edi add edi, 7 repnz scasb pop edi cld jz .tilde ; tilde is not found, insert "~1" at end add edi, 6 cmp word [edi], ' ' jnz .insert_tilde @@: dec edi cmp byte [edi], ' ' jz @b inc edi .insert_tilde: mov word [edi], '~1' popad clc ret .tilde: push edi add edi, 7 xor ecx, ecx @@: ; after tilde may be only digits and trailing spaces cmp byte [edi], '~' jz .break cmp byte [edi], ' ' jz .space cmp byte [edi], '9' jnz .found dec edi jmp @b .space: dec edi inc ecx jmp @b .found: inc byte [edi] add dword [esp], 8 jmp .zerorest .break: jecxz .noplace inc edi mov al, '1' @@: xchg al, [edi] inc edi cmp al, ' ' mov al, '0' jnz @b .succ: pop edi popad clc ret .noplace: dec edi cmp edi, [esp] jz .err add dword [esp], 8 mov word [edi], '~1' inc edi inc edi @@: mov byte [edi], '0' .zerorest: inc edi cmp edi, [esp] jb @b pop edi popad ;clc ; automatically ret .err: pop edi popad stc ret fat_gen_short_name: ; in: esi->long name ; edi->buffer (8+3=11 chars) ; out: buffer filled pushad mov eax, ' ' push edi stosd stosd stosd pop edi xor eax, eax movi ebx, 8 lea ecx, [edi+8] .loop: lodsb test al, al jz .done call char_toupper cmp al, ' ' jz .space cmp al, 80h ja .big test [fat_legal_chars+eax], 2 jnz .symbol .inv_symbol: mov al, '_' or bh, 1 .symbol: cmp al, '.' jz .dot .normal_symbol: dec bl jns .store mov bl, 0 .space: or bh, 1 jmp .loop .store: stosb jmp .loop .big: cmp al, 0xB0 jb .normal_symbol cmp al, 0xE0 jb .inv_symbol cmp al, 0xF0 jb .normal_symbol jmp .inv_symbol .dot: test bh, 2 jz .firstdot pop ebx add ebx, edi sub ebx, ecx push ebx cmp ebx, ecx jb @f pop ebx push ecx @@: cmp edi, ecx jbe .skip @@: dec edi mov al, [edi] dec ebx mov [ebx], al mov byte [edi], ' ' cmp edi, ecx ja @b .skip: mov bh, 3 jmp @f .firstdot: cmp bl, 8 jz .space push edi or bh, 2 @@: mov edi, ecx mov bl, 3 jmp .loop .done: test bh, 2 jz @f pop edi @@: lea edi, [ecx-8] test bh, 1 jz @f call fat_next_short_name @@: popad ret fat12_free_space: ;--------------------------------------------- ; ; returns free space in edi ; rewr.by Mihasik ;--------------------------------------------- push eax ebx ecx mov edi, [ebp+FAT.fat12_unpacked_ptr];start of FAT xor ax, ax;Free cluster=0x0000 in FAT xor ebx, ebx;counter mov ecx, [ebp+FAT.LAST_CLUSTER] inc ecx cld rdfs1: repne scasw jnz rdfs2 ;if last cluster not 0 inc ebx test ecx, ecx jnz rdfs1 rdfs2: shl ebx, 9;free clusters*512 mov edi, ebx pop ecx ebx eax ret set_FAT: ;-------------------------------- ; input : EAX = cluster ; EDX = value to save ; EBP = pointer to FAT structure ; output : EDX = old value ;-------------------------------- ; out: CF set <=> error push eax ebx esi cmp eax, 2 jb sfc_error cmp eax, [ebp+FAT.LAST_CLUSTER] ja sfc_error cmp [ebp+FAT.fs_type], 12 je set_FAT12 cmp [ebp+FAT.fs_type], 16 je sfc_1 add eax, eax sfc_1: add eax, eax mov esi, 511 and esi, eax ; esi = position in fat sector shr eax, 9 ; eax = fat sector add eax, [ebp+FAT.FAT_START] mov ebx, [ebp+FAT.fat_cache_ptr] cmp eax, [ebp+FAT.fat_in_cache]; is fat sector already in memory? je sfc_in_cache ; yes cmp [ebp+FAT.fat_change], 0; is fat changed? je sfc_no_change ; no call write_fat_sector; yes. write it into disk jc sfc_error sfc_no_change: mov [ebp+FAT.fat_in_cache], eax; save fat sector call fs_read32_sys test eax, eax jne sfc_error sfc_in_cache: cmp [ebp+FAT.fs_type], 16 jne sfc_test32 sfc_set16: xchg [ebx+esi], dx ; save new value and get old value jmp sfc_write sfc_test32: mov eax, [ebp+FAT.fatMASK] sfc_set32: and edx, eax xor eax, -1 ; mask for high bits and eax, [ebx+esi] ; get high 4 bits or eax, edx mov edx, [ebx+esi] ; get old value mov [ebx+esi], eax ; save new value sfc_write: mov [ebp+FAT.fat_change], 1; fat has changed sfc_nonzero: and edx, [ebp+FAT.fatMASK] sfc_return: pop esi ebx eax ret sfc_error: stc jmp sfc_return set_FAT12: test edx, 0xF000 jnz sfc_error mov ebx, [ebp+FAT.fat12_unpacked_ptr] xchg [ebx+eax*2], dx mov [ebp+FAT.fat_change], 1 pop esi ebx eax clc ret get_FAT: ;-------------------------------- ; input : EAX = cluster ; EBP = pointer to FAT structure ; output : EAX = next cluster ;-------------------------------- ; out: CF set <=> error push ebx esi cmp [ebp+FAT.fs_type], 12 je get_FAT12 cmp [ebp+FAT.fs_type], 16 je gfc_1 add eax, eax gfc_1: add eax, eax mov esi, 511 and esi, eax ; esi = position in fat sector shr eax, 9 ; eax = fat sector add eax, [ebp+FAT.FAT_START] mov ebx, [ebp+FAT.fat_cache_ptr] cmp eax, [ebp+FAT.fat_in_cache]; is fat sector already in memory? je gfc_in_cache cmp [ebp+FAT.fat_change], 0; is fat changed? je gfc_no_change ; no call write_fat_sector; yes. write it into disk jc hd_error_01 gfc_no_change: mov [ebp+FAT.fat_in_cache], eax call fs_read32_sys test eax, eax jne hd_error_01 gfc_in_cache: mov eax, [ebx+esi] and eax, [ebp+FAT.fatMASK] gfc_return: pop esi ebx ret hd_error_01: stc jmp gfc_return get_FAT12: mov ebx, [ebp+FAT.fat12_unpacked_ptr] movzx eax, word [ebx+eax*2] pop esi ebx clc ret get_free_FAT: ;----------------------------------------------------------- ; output : if CARRY=0 EAX = # first cluster found free ; if CARRY=1 disk full ; Note : for more speed need to use fat_cache directly ;----------------------------------------------------------- push ecx mov ecx, [ebp+FAT.LAST_CLUSTER]; counter for full disk mov eax, [ebp+FAT.fatStartScan] cmp [ebp+FAT.fs_type], 12 jz get_free_FAT12 dec ecx cmp eax, 2 jb gff_reset gff_test: cmp eax, [ebp+FAT.LAST_CLUSTER]; if above last cluster start at cluster 2 jbe gff_in_range gff_reset: mov eax, 2 gff_in_range: push eax call get_FAT ; get cluster state jc gff_not_found_1 test eax, eax ; is it free? pop eax je gff_found ; yes inc eax ; next cluster dec ecx ; is all checked? jnz gff_test ; no gff_not_found: pop ecx ; yes. disk is full stc ret gff_not_found_1: pop eax jmp gff_not_found gff_found: lea ecx, [eax+1] mov [ebp+FAT.fatStartScan], ecx pop ecx clc ret get_free_FAT12: push edx edi mov edi, [ebp+FAT.fat12_unpacked_ptr] cmp eax, 2 jb .reset cmp eax, ecx jbe @f .reset: mov eax, 2 @@: mov edx, eax lea edi, [edi+eax*2] sub ecx, eax inc ecx xor eax, eax repnz scasw jz .found cmp edx, 2 jz .notfound mov edi, [ebp+FAT.fat12_unpacked_ptr] lea ecx, [edx-2] repnz scasw jnz .notfound .found: sub edi, [ebp+FAT.fat12_unpacked_ptr] shr edi, 1 mov [ebp+FAT.fatStartScan], edi lea eax, [edi-1] pop edi edx ecx ret .notfound: pop edi edx ecx stc ret write_fat_sector: ;----------------------------------------------------------- ; write changed fat to disk ;----------------------------------------------------------- push eax ebx ecx mov [ebp+FAT.fat_change], 0 mov eax, [ebp+FAT.fat_in_cache] cmp eax, -1 jz write_fat_not_used mov ebx, [ebp+FAT.fat_cache_ptr] mov ecx, [ebp+FAT.NUMBER_OF_FATS] write_next_fat: push eax call fs_write32_sys test eax, eax pop eax jnz write_fat_not_used add eax, [ebp+FAT.SECTORS_PER_FAT] dec ecx jnz write_next_fat write_fat_not_used: pop ecx ebx eax ret bcd2bin: ;---------------------------------- ; input : AL=BCD number (eg. 0x11) ; output : AH=0 ; AL=decimal number (eg. 11) ;---------------------------------- xor ah, ah shl ax, 4 shr al, 4 aad ret get_date_for_file: ;----------------------------------------------------- ; Get date from CMOS and pack day,month,year in AX ; DATE bits 0..4 : day of month 0..31 ; 5..8 : month of year 1..12 ; 9..15 : count of years from 1980 ;----------------------------------------------------- mov al, 0x7 ;day out 0x70, al in al, 0x71 call bcd2bin ror eax, 5 mov al, 0x8 ;month out 0x70, al in al, 0x71 call bcd2bin ror eax, 4 mov al, 0x9 ;year out 0x70, al in al, 0x71 call bcd2bin add ax, 20 ;because CMOS return only the two last ;digit (eg. 2000 -> 00 , 2001 -> 01) and we rol eax, 9 ;need the difference with 1980 (eg. 2001-1980) ret get_time_for_file: ;----------------------------------------------------- ; Get time from CMOS and pack hour,minute,second in AX ; TIME bits 0..4 : second (the low bit is lost) ; 5..10 : minute 0..59 ; 11..15 : hour 0..23 ;----------------------------------------------------- mov al, 0x0 ;second out 0x70, al in al, 0x71 call bcd2bin ror eax, 6 mov al, 0x2 ;minute out 0x70, al in al, 0x71 call bcd2bin ror eax, 6 mov al, 0x4 ;hour out 0x70, al in al, 0x71 call bcd2bin rol eax, 11 ret set_current_time_for_entry: ;----------------------------------------------------- ; Set current time/date for file entry ; input : ebx = file entry pointer ;----------------------------------------------------- push eax call get_time_for_file; update files date/time mov [ebx+22], ax call get_date_for_file mov [ebx+24], ax pop eax ret add_disk_free_space: ;----------------------------------------------------- ; input : ecx = cluster count ; Note : negative = remove clusters from free space ; positive = add clusters to free space ;----------------------------------------------------- test ecx, ecx ; no change je add_dfs_no cmp [ebp+FAT.fs_type], 32 ; free disk space only used by fat32 jne add_dfs_no push eax ebx mov eax, [ebp+FAT.ADR_FSINFO] lea ebx, [ebp+FAT.fsinfo_buffer] call fs_read32_sys test eax, eax jnz add_not_fs cmp dword [ebx+0x1fc], 0xaa550000; check sector id jne add_not_fs add [ebx+0x1e8], ecx push [ebp+FAT.fatStartScan] pop dword [ebx+0x1ec] mov eax, [ebp+FAT.ADR_FSINFO] call fs_write32_sys ; jc add_not_fs add_not_fs: pop ebx eax add_dfs_no: ret clear_cluster_chain: ;----------------------------------------------------- ; input : eax = first cluster ;----------------------------------------------------- push eax ecx edx xor ecx, ecx ; cluster count clean_new_chain: cmp eax, [ebp+FAT.LAST_CLUSTER]; end of file ja delete_OK cmp eax, 2 ; unfinished fat chain or zero length file jb delete_OK cmp eax, [ebp+FAT.ROOT_CLUSTER]; don't remove root cluster jz delete_OK xor edx, edx call set_FAT ; clear fat entry jc access_denied_01 inc ecx ; update cluster count mov eax, edx ; old cluster jmp clean_new_chain delete_OK: call add_disk_free_space; add clusters to free disk space clc access_denied_01: pop edx ecx eax ret if 0 get_hd_info: ;----------------------------------------------------------- ; output : eax = 0 - ok ; 3 - unknown FS ; 10 - access denied ; edx = cluster size in bytes ; ebx = total clusters on disk ; ecx = free clusters on disk ;----------------------------------------------------------- cmp [ebp+FAT.fs_type], 16 jz info_fat_ok cmp [ebp+FAT.fs_type], 32 jz info_fat_ok xor edx, edx xor ebx, ebx xor ecx, ecx mov eax, ERROR_UNKNOWN_FS ret info_fat_ok: ; call reserve_hd1 xor ecx, ecx ; count of free clusters mov eax, 2 mov ebx, [ebp+FAT.LAST_CLUSTER] info_cluster: push eax call get_FAT ; get cluster info jc info_access_denied test eax, eax ; is it free? jnz info_used ; no inc ecx info_used: pop eax inc eax cmp eax, ebx ; is above last cluster? jbe info_cluster ; no. test next cluster dec ebx ; cluster count imul edx, [ebp+FAT.SECTORS_PER_CLUSTER], 512; cluster size in bytes xor eax, eax ret info_access_denied: add esp, 4 xor edx, edx xor ebx, ebx xor ecx, ecx mov eax, ERROR_ACCESS_DENIED ret end if update_disk: cmp [ebp+FAT.fat_change], 0 ; is fat changed? je upd_no_change cmp [ebp+FAT.fs_type], 12 jz .fat12 ;----------------------------------------------------------- ; write changed fat and cache to disk ;----------------------------------------------------------- call write_fat_sector jc update_disk_acces_denied jmp upd_no_change .fat12: call restorefatchain mov [ebp+FAT.fat_change], 0 upd_no_change: push esi mov esi, [ebp+PARTITION.Disk] call disk_sync pop esi update_disk_acces_denied: ret fat_lock: lea ecx, [ebp+FAT.Lock] jmp mutex_lock fat_unlock: lea ecx, [ebp+FAT.Lock] jmp mutex_unlock ; \begin{diamond} uni2ansi_str: ; convert UNICODE zero-terminated string to ASCII-string (codepage 866) ; in: esi->source, edi->buffer (may be esi=edi) ; destroys: eax,esi,edi lodsw test ax, ax jz .done cmp ax, 0x80 jb .ascii cmp ax, 0x401 jz .yo1 cmp ax, 0x451 jz .yo2 cmp ax, 0x410 jb .unk cmp ax, 0x440 jb .rus1 cmp ax, 0x450 jb .rus2 .unk: mov al, '_' jmp .doit .yo1: mov al, 0xF0 ; 'Ё' jmp .doit .yo2: mov al, 0xF1 ; 'ё' jmp .doit .rus1: ; 0x410-0x43F -> 0x80-0xAF add al, 0x70 jmp .doit .rus2: ; 0x440-0x44F -> 0xE0-0xEF add al, 0xA0 .ascii: .doit: stosb jmp uni2ansi_str .done: mov byte [edi], 0 ret ansi2uni_char: ; convert ANSI character in al to UNICODE character in ax, using cp866 encoding mov ah, 0 ; 0x00-0x7F - trivial map cmp al, 0x80 jb .ret ; 0x80-0xAF -> 0x410-0x43F cmp al, 0xB0 jae @f add ax, 0x410-0x80 .ret: ret @@: ; 0xE0-0xEF -> 0x440-0x44F cmp al, 0xE0 jb .unk cmp al, 0xF0 jae @f add ax, 0x440-0xE0 ret ; 0xF0 -> 0x401 ; 0xF1 -> 0x451 @@: cmp al, 0xF0 ; 'Ё' jz .yo1 cmp al, 0xF1 ; 'ё' jz .yo2 .unk: mov al, '_' ; ah=0 ret .yo1: mov ax, 0x401 ret .yo2: mov ax, 0x451 ret char_toupper: ; convert character to uppercase, using cp866 encoding ; in: al=symbol ; out: al=converted symbol cmp al, 'a' jb .ret cmp al, 'z' jbe .az cmp al, 0xF1 ; 'ё' jz .yo1 cmp al, 0xA0 ; 'а' jb .ret cmp al, 0xE0 ; 'р' jb .rus1 cmp al, 0xEF ; 'я' ja .ret ; 0xE0-0xEF -> 0x90-0x9F sub al, 0xE0-0x90 .ret: ret .rus1: ; 0xA0-0xAF -> 0x80-0x8F .az: and al, not 0x20 ret .yo1: ; 0xF1 -> 0xF0 dec ax ret fat_get_name: ; in: edi->FAT entry ; out: CF=1 - no valid entry ; else CF=0 and ebp->ASCIIZ-name ; (maximum length of filename is 255 (wide) symbols without trailing 0, ; but implementation requires buffer 261 words) ; destroys eax cmp byte [edi], 0 jz .no cmp byte [edi], 0xE5 jnz @f .no: stc ret @@: cmp byte [edi+11], 0xF jz .longname test byte [edi+11], 8 jnz .no push ecx push edi ebp test byte [ebp-4], 1 jnz .unicode_short mov eax, [edi] mov ecx, [edi+4] mov [ebp], eax mov [ebp+4], ecx mov ecx, 8 @@: cmp byte [ebp+ecx-1], ' ' loope @b mov eax, [edi+8] cmp al, ' ' je .done shl eax, 8 mov al, '.' lea ebp, [ebp+ecx+1] mov [ebp], eax mov ecx, 3 @@: rol eax, 8 cmp al, ' ' jne .done loop @b dec ebp .done: and byte [ebp+ecx+1], 0 ; CF=0 pop ebp edi ecx ret .unicode_short: mov ecx, 8 push ecx @@: mov al, [edi] inc edi call ansi2uni_char mov [ebp], ax inc ebp inc ebp loop @b pop ecx @@: cmp word [ebp-2], ' ' jnz @f dec ebp dec ebp loop @b @@: mov word [ebp], '.' inc ebp inc ebp mov ecx, 3 push ecx @@: mov al, [edi] inc edi call ansi2uni_char mov [ebp], ax inc ebp inc ebp loop @b pop ecx @@: cmp word [ebp-2], ' ' jnz @f dec ebp dec ebp loop @b dec ebp dec ebp @@: and word [ebp], 0 ; CF=0 pop ebp edi ecx ret .longname: ; LFN mov al, byte [edi] and eax, 0x3F dec eax cmp al, 20 jae .no ; ignore invalid entries mov word [ebp+260*2], 0 ; force null-terminating for orphans imul eax, 13*2 add ebp, eax test byte [edi], 0x40 jz @f mov word [ebp+13*2], 0 @@: push eax ; now copy name from edi to ebp ... mov eax, [edi+1] mov [ebp], eax ; symbols 1,2 mov eax, [edi+5] mov [ebp+4], eax ; 3,4 mov eax, [edi+9] mov [ebp+8], ax ; 5 mov eax, [edi+14] mov [ebp+10], eax ; 6,7 mov eax, [edi+18] mov [ebp+14], eax ; 8,9 mov eax, [edi+22] mov [ebp+18], eax ; 10,11 mov eax, [edi+28] mov [ebp+22], eax ; 12,13 ; ... done pop eax sub ebp, eax test eax, eax jz @f ; if this is not first entry, more processing required stc ret @@: ; if this is first entry: test byte [ebp-4], 1 jnz .ret ; buffer at ebp contains UNICODE name, convert it to ANSI push esi edi mov esi, ebp mov edi, ebp call uni2ansi_str pop edi esi .ret: clc ret fat_compare_name: ; compares ASCIIZ-names, case-insensitive (cp866 encoding) ; in: esi->name, ebp->name ; out: if names match: ZF=1 and esi->next component of name ; else: ZF=0, esi is not changed ; destroys eax push ebp esi .loop: mov al, [ebp] inc ebp call char_toupper push eax lodsb call char_toupper cmp al, [esp] jnz .done pop eax test al, al jnz .loop dec esi pop eax pop ebp xor eax, eax ; set ZF flag ret .done: cmp al, '/' jnz @f cmp byte [esp], 0 jnz @f mov [esp+4], esi @@: pop eax pop esi ebp ret fat_find_lfn: ; in: esi->name ; [esp+4] = next ; [esp+8] = first ; [esp+C]... - possibly parameters for first and next ; out: CF=1 - file not found, eax=error code ; else CF=0, esi->next name component, edi->direntry pusha lea eax, [esp+0Ch+20h] call dword [eax-4] jc .reterr sub esp, 262*2 ; reserve place for LFN push 0 ; for fat_get_name: read ASCII name .l1: lea ebp, [esp+4] call fat_get_name jc .l2 call fat_compare_name jz .found .l2: mov ebp, [esp+8+262*2+4] lea eax, [esp+0Ch+20h+262*2+4] call dword [eax-8] jnc .l1 add esp, 262*2+4 .reterr: mov [esp+28], eax stc popa ret .found: add esp, 262*2+4 mov ebp, [esp+8] ; if this is LFN entry, advance to true entry cmp byte [edi+11], 0xF jnz @f lea eax, [esp+0Ch+20h] call dword [eax-8] jc .reterr @@: add esp, 8 ; CF=0 push esi push edi popa ret fat_time_to_bdfe: ; in: eax=FAT time ; out: eax=BDFE time push ecx edx mov ecx, eax mov edx, eax shr eax, 11 shl eax, 16 ; hours and edx, 0x1F add edx, edx mov al, dl ; seconds shr ecx, 5 and ecx, 0x3F mov ah, cl ; minutes pop edx ecx ret fat_date_to_bdfe: push ecx edx mov ecx, eax mov edx, eax shr eax, 9 add ax, 1980 shl eax, 16 ; year and edx, 0x1F mov al, dl ; day shr ecx, 5 and ecx, 0xF mov ah, cl ; month pop edx ecx ret bdfe_to_fat_time: push edx mov edx, eax shr eax, 16 and dh, 0x3F shl eax, 6 or al, dh shr dl, 1 and dl, 0x1F shl eax, 5 or al, dl pop edx ret bdfe_to_fat_date: push edx mov edx, eax shr eax, 16 sub ax, 1980 and dh, 0xF shl eax, 4 or al, dh and dl, 0x1F shl eax, 5 or al, dl pop edx ret fat_entry_to_bdfe: ; convert FAT entry at edi to BDFE (block of data of folder entry) at esi, advance esi ; destroys eax mov eax, [ebp-4] mov [esi+4], eax ; ASCII/UNICODE name fat_entry_to_bdfe2: movzx eax, byte [edi+11] mov [esi], eax ; attributes movzx eax, word [edi+14] call fat_time_to_bdfe mov [esi+8], eax ; creation time movzx eax, word [edi+16] call fat_date_to_bdfe mov [esi+12], eax ; creation date and dword [esi+16], 0 ; last access time is not supported on FAT movzx eax, word [edi+18] call fat_date_to_bdfe mov [esi+20], eax ; last access date movzx eax, word [edi+22] call fat_time_to_bdfe mov [esi+24], eax ; last write time movzx eax, word [edi+24] call fat_date_to_bdfe mov [esi+28], eax ; last write date mov eax, [edi+28] mov [esi+32], eax ; file size (low dword) xor eax, eax mov [esi+36], eax ; file size (high dword) test ebp, ebp jz .ret push ecx edi lea edi, [esi+40] mov esi, ebp test byte [esi-4], 1 jz .ansi mov ecx, 260/2 rep movsd mov [edi-2], ax @@: mov esi, edi pop edi ecx .ret: ret .ansi: mov ecx, 264/4 rep movsd mov [edi-1], al jmp @b bdfe_to_fat_entry: ; convert BDFE at edx to FAT entry at edi ; destroys eax ; attributes byte test byte [edi+11], 8 ; volume label? jnz @f mov al, [edx] and al, 0x27 and byte [edi+11], 0x10 or byte [edi+11], al @@: mov eax, [edx+8] call bdfe_to_fat_time mov [edi+14], ax ; creation time mov eax, [edx+12] call bdfe_to_fat_date mov [edi+16], ax ; creation date mov eax, [edx+20] call bdfe_to_fat_date mov [edi+18], ax ; last access date mov eax, [edx+24] call bdfe_to_fat_time mov [edi+22], ax ; last write time mov eax, [edx+28] call bdfe_to_fat_date mov [edi+24], ax ; last write date ret hd_find_lfn: ; in: ebp -> FAT structure ; in: esi+[esp+4] -> name ; out: CF=1 - file not found, eax=error code ; else CF=0 and edi->direntry, eax=sector ; destroys eax push esi edi push 0 push 0 push fat1x_root_first push fat1x_root_next mov eax, [ebp+FAT.ROOT_CLUSTER] cmp [ebp+FAT.fs_type], 32 jz .fat32 .loop: and [ebp+FAT.longname_sec1], 0 and [ebp+FAT.longname_sec2], 0 call fat_find_lfn jc .notfound cmp byte [esi], 0 jz .found .continue: test byte [edi+11], 10h jz .notfound and dword [esp+12], 0 mov eax, [edi+20-2] mov ax, [edi+26] ; cluster .fat32: mov [esp+8], eax mov dword [esp+4], fat_notroot_first mov dword [esp], fat_notroot_next jmp .loop .notfound: add esp, 16 pop edi esi stc ret 4 .found: lea eax, [esp+4+24] cmp dword [eax], 0 jz @f mov esi, [eax] and dword [eax], 0 jmp .continue @@: lea eax, [esp+8] cmp dword [eax], 0 jz .root call fat_get_sector jmp .cmn .root: mov eax, [eax+4] add eax, [ebp+FAT.ROOT_START] .cmn: add esp, 20 ; CF=0 pop esi ret 4 ;---------------------------------------------------------------- ; fat_Read - FAT implementation of reading a file ; in: ebp = pointer to FAT structure ; in: esi+[esp+4] = name ; in: ebx = pointer to parameters from sysfunc 70 ; out: eax, ebx = return values for sysfunc 70 ;---------------------------------------------------------------- fat_Read: call fat_lock push edi cmp byte [esi], 0 jnz @f .noaccess: pop edi call fat_unlock or ebx, -1 mov eax, ERROR_ACCESS_DENIED ret @@: stdcall hd_find_lfn, [esp+8] jnc .found pop edi push eax call fat_unlock pop eax or ebx, -1 ret .found: test byte [edi+11], 0x10 ; do not allow read directories jnz .noaccess cmp dword [ebx+8], 0 jz @f xor ebx, ebx call fat_unlock mov eax, ERROR_END_OF_FILE pop edi ret @@: mov edx, [ebx+4] ; file offset mov ecx, [ebx+12] ; size mov ebx, [ebx+16] ; buffer push ebx push 0 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 @@: 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 edi 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 - FAT implementation of reading a folder ; in: ebp = pointer to FAT structure ; in: esi+[esp+4] = name ; in: ebx = pointer to parameters from sysfunc 70 ; out: eax, ebx = return values for sysfunc 70 ;---------------------------------------------------------------- fat_ReadFolder: call fat_lock mov eax, [ebp+FAT.ROOT_CLUSTER] push edi cmp byte [esi], 0 jz .doit stdcall hd_find_lfn, [esp+4+4] jnc .found pop edi push eax call fat_unlock pop eax or ebx, -1 ret .found: test byte [edi+11], 0x10 ; do not allow read files jnz .found_dir pop edi call fat_unlock or ebx, -1 mov eax, ERROR_ACCESS_DENIED ret .found_dir: mov eax, [edi+20-2] mov ax, [edi+26] ; eax=cluster .doit: push esi sub esp, 262*2 ; reserve space for LFN push dword [ebx+8] ; for fat_get_name: read ANSI/UNICODE name mov edx, [ebx+16] ; pointer to buffer ; init header push eax mov edi, edx mov ecx, 32/4 xor eax, eax rep stosd pop eax mov byte [edx], 1 ; version mov esi, edi ; esi points to BDFE mov ecx, [ebx+12] ; number of blocks to read mov ebx, [ebx+4] ; index of the first block .new_cluster: mov [ebp+FAT.cluster_tmp], eax test eax, eax jnz @f cmp [ebp+FAT.fs_type], 32 jz .notfound mov eax, [ebp+FAT.ROOT_START] push [ebp+FAT.ROOT_SECTORS] push ebx jmp .new_sector @@: dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] push [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] push ebx .new_sector: lea ebx, [ebp+FAT.buffer] mov edi, ebx push eax call fs_read32_sys test eax, eax pop eax jnz .notfound2 add ebx, 512 push eax .l1: push ebp lea ebp, [esp+20] call fat_get_name pop ebp jc .l2 cmp byte [edi+11], 0xF jnz .do_bdfe add edi, 0x20 cmp edi, ebx jb .do_bdfe pop eax inc eax dec dword [esp+4] jnz @f mov eax, [ebp+FAT.cluster_tmp] test eax, eax jz .done call get_FAT jc .notfound2 cmp eax, 2 jb .done cmp eax, [ebp+FAT.fatRESERVED] jae .done push eax mov eax, [ebp+FAT.SECTORS_PER_CLUSTER] mov [esp+8], eax pop eax mov [ebp+FAT.cluster_tmp], eax dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] @@: lea ebx, [ebp+FAT.buffer] mov edi, ebx push eax call fs_read32_sys test eax, eax pop eax jnz .notfound2 add ebx, 512 push eax .do_bdfe: inc dword [edx+8] ; new file found dec dword [esp+4] jns .l2 dec ecx js .l2 inc dword [edx+4] ; new file block copied push ebp lea ebp, [esp+20] call fat_entry_to_bdfe pop ebp .l2: add edi, 0x20 cmp edi, ebx jb .l1 pop eax inc eax dec dword [esp+4] jnz .new_sector mov eax, [ebp+FAT.cluster_tmp] test eax, eax jz .done call get_FAT jc .notfound2 cmp eax, 2 jb .done cmp eax, [ebp+FAT.fatRESERVED] jae .done push eax mov eax, [ebp+FAT.SECTORS_PER_CLUSTER] mov [esp+8], eax pop eax pop ebx add esp, 4 jmp .new_cluster .notfound2: add esp, 8 .notfound: add esp, 262*2+4 pop esi edi mov ebx, [edx+4] call fat_unlock mov eax, ERROR_DEVICE ret .done: add esp, 262*2+4+8 mov ebx, [edx+4] xor eax, eax dec ecx js @f mov al, ERROR_END_OF_FILE @@: push eax call fat_unlock pop eax pop esi edi ret fat1x_root_next: push ecx lea ecx, [ebp+FAT.buffer+0x200-0x20] cmp edi, ecx jae fat1x_root_next_sector pop ecx add edi, 0x20 ret ; CF=0 fat1x_root_next_sector: ; read next sector push [ebp+FAT.longname_sec2] pop [ebp+FAT.longname_sec1] mov ecx, [eax+4] push ecx add ecx, [ebp+FAT.ROOT_START] mov [ebp+FAT.longname_sec2], ecx pop ecx inc ecx mov [eax+4], ecx cmp ecx, [ebp+FAT.ROOT_SECTORS] pop ecx jb fat1x_root_first mov eax, ERROR_FILE_NOT_FOUND stc ret fat1x_root_first: mov eax, [eax+4] add eax, [ebp+FAT.ROOT_START] push ebx lea edi, [ebp+FAT.buffer] mov ebx, edi call fs_read32_sys pop ebx test eax, eax jnz .readerr ret ; CF=0 .readerr: mov eax, ERROR_DEVICE stc ret .notfound: mov eax, ERROR_FILE_NOT_FOUND stc ret fat1x_root_begin_write: push edi eax call fat1x_root_first pop eax edi ret fat1x_root_end_write: pusha mov eax, [eax+4] add eax, [ebp+FAT.ROOT_START] lea ebx, [ebp+FAT.buffer] call fs_write32_sys popa ret fat1x_root_next_write: push ecx lea ecx, [ebp+FAT.buffer+0x200] cmp edi, ecx jae @f pop ecx ret @@: call fat1x_root_end_write jmp fat1x_root_next_sector fat1x_root_extend_dir: stc ret fat_notroot_next: push ecx lea ecx, [ebp+FAT.buffer+0x200-0x20] cmp edi, ecx jae fat_notroot_next_sector pop ecx add edi, 0x20 ret ; CF=0 fat_notroot_next_sector: push [ebp+FAT.longname_sec2] pop [ebp+FAT.longname_sec1] push eax call fat_get_sector mov [ebp+FAT.longname_sec2], eax pop eax mov ecx, [eax+4] inc ecx cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER] jae fat_notroot_next_cluster mov [eax+4], ecx jmp @f fat_notroot_next_cluster: push eax mov eax, [eax] call get_FAT mov ecx, eax pop eax jc fat_notroot_first.deverr cmp ecx, 2 jb fat_notroot_next_err cmp ecx, [ebp+FAT.fatRESERVED] jae fat_notroot_next_err mov [eax], ecx and dword [eax+4], 0 @@: pop ecx fat_notroot_first: call fat_get_sector push ebx lea edi, [ebp+FAT.buffer] mov ebx, edi call fs_read32_sys pop ebx test eax, eax jz .ret ; CF=0 push ecx .deverr: pop ecx mov eax, ERROR_DEVICE stc .ret: ret fat_notroot_next_err: pop ecx mov eax, ERROR_FILE_NOT_FOUND stc ret fat_notroot_begin_write: push eax edi call fat_notroot_first pop edi eax ret fat_notroot_end_write: call fat_get_sector push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_sys pop ebx ret fat_notroot_next_write: push ecx lea ecx, [ebp+FAT.buffer+0x200] cmp edi, ecx jae @f pop ecx ret @@: push eax call fat_notroot_end_write pop eax jmp fat_notroot_next_sector fat_notroot_extend_dir: push eax call get_free_FAT jnc .found pop eax ret ; CF=1 .found: push edx mov edx, [ebp+FAT.fatEND] call set_FAT jc .writeerr mov edx, eax mov eax, [esp+4] mov eax, [eax] push edx call set_FAT pop edx jnc @f .writeerr: pop edx pop eax stc ret @@: push ecx or ecx, -1 call add_disk_free_space ; zero new cluster mov ecx, 512/4 lea edi, [ebp+FAT.buffer] push edi xor eax, eax rep stosd pop edi pop ecx mov eax, [esp+4] mov [eax], edx and dword [eax+4], 0 pop edx mov eax, [eax] dec eax dec eax push ebx ecx mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] imul eax, ecx add eax, [ebp+FAT.DATA_START] mov ebx, edi @@: push eax call fs_write32_sys pop eax inc eax loop @b pop ecx ebx eax clc ret fat_get_sector: push ecx mov ecx, [eax] dec ecx dec ecx imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER] add ecx, [ebp+FAT.DATA_START] add ecx, [eax+4] mov eax, ecx pop ecx ret fshrad: call fat_unlock mov eax, ERROR_ACCESS_DENIED xor ebx, ebx ret ;---------------------------------------------------------------- ; fat_CreateFolder - FAT implementation of creating a folder ; in: ebp = pointer to FAT structure ; in: esi+[esp+4] = name ; in: ebx = pointer to parameters from sysfunc 70 ; out: eax, ebx = return values for sysfunc 70 ;---------------------------------------------------------------- fat_CreateFolder: push 1 jmp fat_Rewrite.common ;---------------------------------------------------------------- ; fat_Rewrite - FAT implementation of creating a new file ; in: ebp = pointer to FAT structure ; in: esi+[esp+4] = name ; in: ebx = pointer to parameters from sysfunc 70 ; out: eax, ebx = return values for sysfunc 70 ;---------------------------------------------------------------- fat_Rewrite: push 0 .common: call fat_lock pop eax cmp byte [esi], 0 jz fshrad mov ecx, [ebx+12] mov edx, [ebx+16] pushad xor edi, edi mov edx, [esp+4+20h] push esi test edx, edx jz @f mov esi, edx @@: lodsb test al, al jz @f cmp al, '/' jnz @b lea edi, [esi-1] jmp @b @@: pop esi test edi, edi jnz .noroot test edx, edx jnz .hasebp mov edx, [ebp+FAT.ROOT_CLUSTER] cmp [ebp+FAT.fs_type], 32 jz .pushnotroot xor edx, edx push edx push fat1x_root_extend_dir push fat1x_root_end_write push fat1x_root_next_write push fat1x_root_begin_write push edx push edx push fat1x_root_first push fat1x_root_next jmp .common1 .hasebp: mov eax, ERROR_ACCESS_DENIED cmp byte [edx], 0 jz .ret1 stdcall hd_find_lfn, 0 mov esi, [esp+4+20h] jc .ret1 jmp .common0 .noroot: mov eax, ERROR_ACCESS_DENIED cmp byte [edi+1], 0 jz .ret1 ; check existence mov byte [edi], 0 push edi stdcall hd_find_lfn, [esp+4+24h] pop esi mov byte [esi], '/' jnc @f .notfound0: mov eax, ERROR_FILE_NOT_FOUND .ret1: mov [esp+28], eax call fat_unlock popad xor ebx, ebx ret @@: inc esi .common0: test byte [edi+11], 0x10 ; must be directory mov eax, ERROR_ACCESS_DENIED jz .ret1 mov edx, [edi+20-2] mov dx, [edi+26] ; ebp=cluster mov eax, ERROR_FAT_TABLE cmp edx, 2 jb .ret1 .pushnotroot: push edx push fat_notroot_extend_dir push fat_notroot_end_write push fat_notroot_next_write push fat_notroot_begin_write push 0 push edx push fat_notroot_first push fat_notroot_next .common1: call fat_find_lfn jc .notfound ; found test byte [edi+11], 10h jz .exists_file ; found directory; if we are creating directory, return OK, ; if we are creating file, say "access denied" add esp, 36 call fat_unlock popad test al, al mov eax, ERROR_ACCESS_DENIED jz @f mov al, 0 @@: xor ebx, ebx ret .exists_file: ; found file; if we are creating directory, return "access denied", ; if we are creating file, delete existing file and continue cmp byte [esp+36+28], 0 jz @f add esp, 36 call fat_unlock popad mov eax, ERROR_ACCESS_DENIED xor ebx, ebx ret @@: ; delete FAT chain push edi xor eax, eax mov dword [edi+28], eax ; zero size xor ecx, ecx mov eax, [edi+20-2] mov ax, [edi+26] mov word [edi+20], cx mov word [edi+26], cx test eax, eax jz .done1 @@: cmp eax, [ebp+FAT.fatRESERVED] jae .done1 push edx xor edx, edx call set_FAT mov eax, edx pop edx jc .done1 inc ecx jmp @b .done1: pop edi call get_time_for_file mov [edi+22], ax call get_date_for_file mov [edi+24], ax mov [edi+18], ax or byte [edi+11], 20h ; set 'archive' attribute jmp .doit .notfound: ; file is not found; generate short name call fat_name_is_legal jc @f add esp, 36 call fat_unlock popad mov eax, ERROR_FILE_NOT_FOUND xor ebx, ebx ret @@: sub esp, 12 mov edi, esp call fat_gen_short_name .test_short_name_loop: push esi edi ecx mov esi, edi lea eax, [esp+12+12+8] mov edx, [eax+24] mov [eax], edx and dword [eax+4], 0 call dword [eax-4] jc .found .test_short_name_entry: cmp byte [edi+11], 0xF jz .test_short_name_cont mov ecx, 11 push esi edi repz cmpsb pop edi esi jz .short_name_found .test_short_name_cont: lea eax, [esp+12+12+8] call dword [eax-8] jnc .test_short_name_entry jmp .found .short_name_found: pop ecx edi esi call fat_next_short_name jnc .test_short_name_loop .disk_full: add esp, 12+36 call fat_unlock popa mov eax, ERROR_DISK_FULL xor ebx, ebx ret .found: pop ecx edi esi ; now find space in directory ; we need to save LFN <=> LFN is not equal to short name <=> generated name contains '~' mov al, '~' push ecx edi mov ecx, 8 repnz scasb movi eax, 1 ; 1 entry jnz .notilde ; we need ceil(strlen(esi)/13) additional entries = floor((strlen(esi)+12+13)/13) total xor eax, eax @@: cmp byte [esi], 0 jz @f inc esi inc eax jmp @b @@: sub esi, eax add eax, 12+13 mov ecx, 13 push edx cdq div ecx pop edx .notilde: push -1 push -1 push -1 ; find successive entries in directory xor ecx, ecx push eax lea eax, [esp+16+8+12+8] mov edx, [eax+24] mov [eax], edx and dword [eax+4], 0 call dword [eax-4] pop eax jnc .scan_dir .fsfrfe3: add esp, 12+8+12+36 call fat_unlock popad mov eax, ERROR_DEVICE xor ebx, ebx ret .scan_dir: cmp byte [edi], 0 jz .free cmp byte [edi], 0xE5 jz .free xor ecx, ecx .scan_cont: push eax lea eax, [esp+16+8+12+8] call dword [eax-8] mov edx, eax pop eax jnc .scan_dir cmp edx, ERROR_DEVICE jz .fsfrfe3 push eax lea eax, [esp+16+8+12+8] call dword [eax+20] ; extend directory pop eax jnc .scan_dir add esp, 12+8+12+36 call fat_unlock popad mov eax, ERROR_DISK_FULL xor ebx, ebx ret .free: test ecx, ecx jnz @f mov [esp], edi mov ecx, [esp+12+8+12+8] mov [esp+4], ecx mov ecx, [esp+12+8+12+12] mov [esp+8], ecx xor ecx, ecx @@: inc ecx cmp ecx, eax jb .scan_cont ; found! push esi ecx ; If creating a directory, allocate one data cluster now and fail immediately ; if this is impossible. This prevents from creating an invalid directory entry ; on a full disk. ; yup, the argument is quite non-intuitive... but what should I do if ; the entire function uses such arguments? BTW, it refers to al from pushad, ; which in turn is filled with 0 in fat_Rewrite and 1 in fat_CreateFolder. cmp byte [esp+8+12+8+12+36+28], 0 jz .no.preallocate.folder.data call get_free_FAT jnc @f add esp, 8+12+8 jmp .disk_full @@: mov [esp+8+12+8+12+36+20], eax ; store the cluster somewhere .no.preallocate.folder.data: ; calculate name checksum mov esi, [esp+8+12] mov ecx, 11 xor eax, eax @@: ror al, 1 add al, [esi] inc esi loop @b pop ecx esi pop edi pop dword [esp+8+12+12] pop dword [esp+8+12+12] ; edi points to first entry in free chunk dec ecx jz .nolfn push esi push eax lea eax, [esp+8+8+12+8] call dword [eax+8] ; begin write mov al, 40h .writelfn: or al, cl mov esi, [esp+4] push ecx dec ecx imul ecx, 13 add esi, ecx stosb mov cl, 5 call fat_read_symbols mov ax, 0xF stosw mov al, [esp+4] stosb mov cl, 6 call fat_read_symbols xor eax, eax stosw mov cl, 2 call fat_read_symbols pop ecx lea eax, [esp+8+8+12+8] call dword [eax+12] ; next write xor eax, eax loop .writelfn pop eax pop esi ; lea eax, [esp+8+12+8] ; call dword [eax+16] ; end write .nolfn: xchg esi, [esp] mov ecx, 11 rep movsb mov word [edi], 20h ; attributes sub edi, 11 pop esi ecx add esp, 12 mov byte [edi+13], 0 ; tenths of a second at file creation time call get_time_for_file mov [edi+14], ax ; creation time mov [edi+22], ax ; last write time call get_date_for_file mov [edi+16], ax ; creation date mov [edi+24], ax ; last write date mov [edi+18], ax ; last access date xor ecx, ecx mov word [edi+20], cx ; high word of cluster mov word [edi+26], cx ; low word of cluster - to be filled mov dword [edi+28], ecx ; file size - to be filled cmp byte [esp+36+28], cl jz .doit ; create directory mov byte [edi+11], 10h ; attributes: folder mov esi, edi lea eax, [esp+8] call dword [eax+16] ; flush directory mov eax, [esp+36+20] ; extract saved cluster mov [esp+36+20], edi ; this is needed for calculating arg of add_disk_free_space! push ecx mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] shl ecx, 9 push ecx push edi jmp .doit2 .doit: mov esi, [esp+36+20] lea eax, [esp+8] call dword [eax+16] ; flush directory push ecx mov ecx, [esp+4+36+24] push ecx push edi test ecx, ecx jz .done call get_free_FAT jc .diskfull .doit2: push eax mov [edi+26], ax shr eax, 16 mov [edi+20], ax lea eax, [esp+16+8] call dword [eax+16] ; flush directory pop eax push edx mov edx, [ebp+FAT.fatEND] call set_FAT pop edx .write_cluster: push eax dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] push [ebp+FAT.SECTORS_PER_CLUSTER] ; write data .write_sector: cmp byte [esp+20+36+28], 0 jnz .writedir mov ecx, 512 cmp dword [esp+12], ecx jb .writeshort ; we can write directly from given buffer mov ebx, esi add esi, ecx jmp .writecommon .writeshort: mov ecx, [esp+12] push ecx lea edi, [ebp+FAT.buffer] mov ebx, edi rep movsb .writedircont: lea ecx, [ebp+FAT.buffer+0x200] sub ecx, edi push eax xor eax, eax rep stosb pop eax pop ecx .writecommon: push eax call fs_write32_app test eax, eax pop eax jnz .writeerr inc eax sub dword [esp+12], ecx jz .writedone dec dword [esp] jnz .write_sector pop eax ; allocate new cluster pop eax mov ecx, eax call get_free_FAT jc .diskfull push edx mov edx, [ebp+FAT.fatEND] call set_FAT xchg eax, ecx mov edx, ecx call set_FAT pop edx xchg eax, ecx jmp .write_cluster .diskfull: mov eax, ERROR_DISK_FULL jmp .ret .writeerr: pop eax eax sub esi, ecx mov eax, ERROR_DEVICE jmp .ret .writedone: pop eax eax .done: xor eax, eax .ret: pop edi ecx sub esi, [esp+4+36+20] mov [esp+4+36+28], eax mov [esp+4+36+16], esi lea eax, [esp+12] call dword [eax+8] mov [edi+28], esi call dword [eax+16] mov [esp+36+16], ebx lea eax, [esi+511] shr eax, 9 mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] lea eax, [eax+ecx-1] xor edx, edx div ecx pop ecx sub ecx, eax call add_disk_free_space add esp, 36 call update_disk call fat_unlock popad ret .writedir: push 512 lea edi, [ebp+FAT.buffer] mov ebx, edi mov ecx, [ebp+FAT.SECTORS_PER_CLUSTER] shl ecx, 9 cmp ecx, [esp+16] jnz .writedircont dec dword [esp+20] push esi mov ecx, 32/4 rep movsd pop esi mov dword [edi-32], '. ' mov dword [edi-32+4], ' ' mov dword [edi-32+8], ' ' mov byte [edi-32+11], 10h push esi mov ecx, 32/4 rep movsd pop esi mov dword [edi-32], '.. ' mov dword [edi-32+4], ' ' mov dword [edi-32+8], ' ' mov byte [edi-32+11], 10h mov ecx, [esp+20+36] cmp ecx, [ebp+FAT.ROOT_CLUSTER] jnz @f xor ecx, ecx @@: mov word [edi-32+26], cx shr ecx, 16 mov [edi-32+20], cx jmp .writedircont fat_read_symbol: or ax, -1 test esi, esi jz .retFFFF lodsb test al, al jnz ansi2uni_char xor eax, eax xor esi, esi .retFFFF: ret fat_read_symbols: call fat_read_symbol stosw loop fat_read_symbols ret fat_Write.access_denied: push ERROR_ACCESS_DENIED fat_Write.ret0: pop eax xor ebx, ebx ret fat_Write.ret11: push ERROR_DEVICE jmp fat_Write.ret0 ;---------------------------------------------------------------- ; fat_Write - FAT implementation of writing to file ; in: ebp = pointer to FAT structure ; in: esi+[esp+4] = name ; in: ebx = pointer to parameters from sysfunc 70 ; out: eax, ebx = return values for sysfunc 70 ;---------------------------------------------------------------- fat_Write: cmp byte [esi], 0 jz .access_denied call fat_lock push edi stdcall hd_find_lfn, [esp+4+4] jnc .found pop edi push eax call fat_unlock jmp .ret0 .found: ; FAT does not support files larger than 4GB cmp dword [ebx+8], 0 jz @f .eof: pop edi push ERROR_END_OF_FILE call fat_unlock jmp .ret0 @@: mov ecx, [ebx+12] mov edx, [ebx+16] mov ebx, [ebx+4] ; now edi points to direntry, ebx=start byte to write, ; ecx=number of bytes to write, edx=data pointer ; extend file if needed add ecx, ebx jc .eof ; FAT does not support files larger than 4GB push edx push eax ; save directory sector push 0 ; return value=0 call get_time_for_file mov [edi+22], ax ; last write time call get_date_for_file mov [edi+24], ax ; last write date mov [edi+18], ax ; last access date push dword [edi+28] ; save current file size cmp ecx, [edi+28] jbe .length_ok cmp ecx, ebx jz .length_ok call hd_extend_file jnc .length_ok mov [esp+4], eax ; hd_extend_file can return three error codes: FAT table error, device error or disk full. ; First two cases are fatal errors, in third case we may write some data cmp al, ERROR_DISK_FULL jz .disk_full call fat_unlock pop eax pop eax pop ecx pop edx pop edi xor ebx, ebx ret .disk_full: ; correct number of bytes to write mov ecx, [edi+28] cmp ecx, ebx ja .length_ok push 0 .ret: pop eax sub edx, [esp+12] mov ebx, edx ; ebx=number of written bytes call update_disk test eax, eax jz @f mov byte [esp+4], ERROR_DEVICE @@: call fat_unlock pop eax pop eax pop ecx pop edx pop edi ret .length_ok: mov esi, [edi+28] mov eax, [edi+20-2] mov ax, [edi+26] mov edi, eax ; edi=current cluster push 0 ; current sector in cluster ; save directory mov eax, [esp+12] push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_sys pop ebx test eax, eax jz @f .device_err: mov byte [esp+8], ERROR_DEVICE jmp .ret .fat_err: mov byte [esp+8], ERROR_FAT_TABLE jmp .ret @@: ; now ebx=start pos, ecx=end pos, both lie inside file sub ecx, ebx jz .ret .write_loop: ; skip unmodified sectors cmp dword [esp+4], 0x200 jb .modify sub ebx, 0x200 jae .skip add ebx, 0x200 .modify: ; get length of data in current sector push ecx sub ebx, 0x200 jb .hasdata neg ebx xor ecx, ecx jmp @f .hasdata: neg ebx cmp ecx, ebx jbe @f mov ecx, ebx @@: ; get current sector number mov eax, edi dec eax dec eax imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] add eax, [esp+4] ; load sector if needed cmp dword [esp+8], 0 ; we don't need to read uninitialized data jz .noread cmp ecx, 0x200 ; we don't need to read sector if it is fully rewritten jz .noread cmp ecx, esi ; (same for the last sector) jz .noread push eax ebx lea ebx, [ebp+FAT.buffer] call fs_read32_app test eax, eax pop ebx eax jz @f .device_err2: pop ecx jmp .device_err @@: .noread: ; zero uninitialized data if file was extended (because hd_extend_file does not this) push eax ecx edi xor eax, eax mov ecx, 0x200 sub ecx, [esp+8+12] jbe @f lea edi, [ebp+FAT.buffer] add edi, [esp+8+12] rep stosb @@: ; zero uninitialized data in the last sector mov ecx, 0x200 sub ecx, esi jbe @f lea edi, [ebp+FAT.buffer+esi] rep stosb @@: pop edi ecx ; copy new data mov eax, edx neg ebx jecxz @f lea ebx, [ebp+FAT.buffer+0x200+ebx] call memmove xor ebx, ebx @@: pop eax ; save sector push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_app pop ebx test eax, eax jnz .device_err2 add edx, ecx sub [esp], ecx pop ecx jz .ret .skip: ; next sector pop eax inc eax push eax cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER] jb @f and dword [esp], 0 mov eax, edi call get_FAT mov edi, eax jc .device_err cmp edi, 2 jb .fat_err cmp edi, [ebp+FAT.fatRESERVED] jae .fat_err @@: sub esi, 0x200 jae @f xor esi, esi @@: sub dword [esp+4], 0x200 jae @f and dword [esp+4], 0 @@: jmp .write_loop hd_extend_file.zero_size: xor eax, eax jmp hd_extend_file.start_extend ; extends file on hd to given size (new data area is undefined) ; in: edi->direntry, ecx=new size ; out: CF=0 => OK, eax=0 ; CF=1 => error, eax=code (ERROR_FAT_TABLE or ERROR_DISK_FULL or ERROR_DEVICE) hd_extend_file: push esi mov esi, [ebp+FAT.SECTORS_PER_CLUSTER] imul esi, [ebp+FAT.BYTES_PER_SECTOR] push ecx ; find the last cluster of file mov eax, [edi+20-2] mov ax, [edi+26] mov ecx, [edi+28] jecxz .zero_size .last_loop: sub ecx, esi jbe .last_found call get_FAT jnc @f .device_err: pop ecx .device_err2: pop esi push ERROR_DEVICE .ret_err: pop eax stc ret @@: cmp eax, 2 jb .fat_err cmp eax, [ebp+FAT.fatRESERVED] jb .last_loop .fat_err: pop ecx esi push ERROR_FAT_TABLE jmp .ret_err .last_found: push eax call get_FAT jnc @f pop eax jmp .device_err @@: cmp eax, [ebp+FAT.fatRESERVED] pop eax jb .fat_err ; set length to full number of clusters sub [edi+28], ecx .start_extend: pop ecx ; now do extend push edx mov edx, 2 ; start scan from cluster 2 .extend_loop: cmp [edi+28], ecx jae .extend_done ; add new cluster push eax call get_free_FAT jc .disk_full mov edx, [ebp+FAT.fatEND] call set_FAT mov edx, eax pop eax test eax, eax jz .first_cluster push edx call set_FAT pop edx jmp @f .first_cluster: ror edx, 16 mov [edi+20], dx ror edx, 16 mov [edi+26], dx @@: push ecx mov ecx, -1 call add_disk_free_space pop ecx mov eax, edx add [edi+28], esi jmp .extend_loop .extend_done: mov [edi+28], ecx pop edx esi xor eax, eax ; CF=0 ret .device_err3: pop edx jmp .device_err2 .disk_full: pop eax edx esi movi eax, ERROR_DISK_FULL stc ret fat_update_datetime: call get_time_for_file mov [edi+22], ax ; last write time call get_date_for_file mov [edi+24], ax ; last write date mov [edi+18], ax ; last access date ret ;---------------------------------------------------------------- ; fat_SetFileEnd - FAT implementation of setting end-of-file ; in: ebp = pointer to FAT structure ; in: esi+[esp+4] = name ; in: ebx = pointer to parameters from sysfunc 70 ; out: eax, ebx = return values for sysfunc 70 ;---------------------------------------------------------------- fat_SetFileEnd: call fat_lock push edi cmp byte [esi], 0 jnz @f .access_denied: push ERROR_ACCESS_DENIED .ret: call fat_unlock pop eax pop edi ret @@: stdcall hd_find_lfn, [esp+4+4] jnc @f .reteax: push eax jmp .ret @@: ; must not be directory test byte [edi+11], 10h jnz .access_denied ; file size must not exceed 4 Gb cmp dword [ebx+8], 0 jz @f push ERROR_END_OF_FILE jmp .ret @@: push eax ; save directory sector ; set file modification date/time to current call fat_update_datetime mov eax, [ebx+4] cmp eax, [edi+28] jb .truncate ja .expand pop eax lea ebx, [ebp+FAT.buffer] call fs_write32_sys test eax, eax jz @f push ERROR_DEVICE jmp .ret @@: push 0 jmp .ret .expand: push ebx ebp ecx push dword [edi+28] ; save old size mov ecx, eax call hd_extend_file push eax ; return code jnc .expand_ok cmp al, ERROR_DISK_FULL jz .disk_full .pop_ret: call update_disk pop eax ecx ecx ebp ebx ecx jmp .reteax .expand_ok: .disk_full: ; save directory mov eax, [edi+28] xchg eax, [esp+20] lea ebx, [ebp+FAT.buffer] call fs_write32_sys test eax, eax mov eax, [edi+20-2] mov ax, [edi+26] mov edi, eax jz @f .pop_ret11: mov byte [esp], ERROR_DEVICE jmp .pop_ret @@: test edi, edi jz .pop_ret ; now zero new data push 0 ; edi=current cluster, [esp]=sector in cluster ; [esp+24]=new size, [esp+8]=old size, [esp+4]=return code .zero_loop: cmp edi, 2 jb .error_fat cmp edi, [ebp+FAT.fatRESERVED] jae .error_fat sub dword [esp+8], 0x200 jae .next_cluster lea eax, [edi-2] imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] add eax, [esp] cmp dword [esp+8], -0x200 jz .noread push eax lea ebx, [ebp+FAT.buffer] call fs_read32_app test eax, eax pop eax jnz .err_next .noread: mov ecx, [esp+8] neg ecx push edi lea edi, [ebp+FAT.buffer+0x200] add edi, [esp+12] push eax xor eax, eax mov [esp+16], eax rep stosb pop eax pop edi call fs_write32_app test eax, eax jz .next_cluster .err_next: mov byte [esp+4], ERROR_DEVICE .next_cluster: pop eax sub dword [esp+20], 0x200 jbe .pop_ret inc eax push eax cmp eax, [ebp+FAT.SECTORS_PER_CLUSTER] jb .zero_loop and dword [esp], 0 mov eax, edi call get_FAT mov edi, eax jnc .zero_loop pop eax jmp .pop_ret11 .truncate: mov [edi+28], eax push ecx mov ecx, [edi+20-2] mov cx, [edi+26] push eax test eax, eax jz .zero_size ; find new last cluster @@: cmp ecx, 2 jb .error_fat2 cmp ecx, [ebp+FAT.fatRESERVED] jae .error_fat2 mov eax, [ebp+FAT.SECTORS_PER_CLUSTER] shl eax, 9 sub [esp], eax jbe @f mov eax, ecx call get_FAT mov ecx, eax jnc @b .device_err3: pop eax ecx eax edi call update_disk call fat_unlock movi eax, ERROR_DEVICE ret @@: ; we will zero data at the end of last sector - remember it push ecx ; terminate FAT chain push edx mov eax, ecx mov edx, [ebp+FAT.fatEND] call set_FAT mov eax, edx pop edx jnc @f .device_err4: pop ecx jmp .device_err3 .zero_size: and word [edi+20], 0 and word [edi+26], 0 push 0 mov eax, ecx @@: ; delete FAT chain call clear_cluster_chain jc .device_err4 ; save directory mov eax, [esp+12] push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_sys pop ebx test eax, eax jnz .device_err4 ; zero last sector, ignore errors pop ecx pop eax dec ecx imul ecx, [ebp+FAT.SECTORS_PER_CLUSTER] add ecx, [ebp+FAT.DATA_START] push eax sar eax, 9 add ecx, eax pop eax and eax, 0x1FF jz .truncate_done push ebx eax mov eax, ecx lea ebx, [ebp+FAT.buffer] call fs_read32_app pop eax lea edi, [ebp+FAT.buffer+eax] push ecx mov ecx, 0x200 sub ecx, eax xor eax, eax rep stosb pop eax call fs_write32_app pop ebx .truncate_done: pop ecx eax edi call update_disk call fat_unlock xor eax, eax ret .error_fat: pop eax mov byte [esp], ERROR_FAT_TABLE jmp .pop_ret .error_fat2: pop eax ecx eax edi call update_disk call fat_unlock movi eax, ERROR_FAT_TABLE ret ;---------------------------------------------------------------- ; fat_GetFileInfo - FAT implementation of getting file info ; in: ebp = pointer to FAT structure ; in: esi+[esp+4] = name ; in: ebx = pointer to parameters from sysfunc 70 ; out: eax, ebx = return values for sysfunc 70 ;---------------------------------------------------------------- fat_GetFileInfo: cmp byte [esi], 0 jnz @f mov eax, 2 ret @@: push edi call fat_lock stdcall hd_find_lfn, [esp+4+4] jc .error push ebp xor ebp, ebp mov esi, [ebx+16] mov dword [esi+4], ebp call fat_entry_to_bdfe2 pop ebp call fat_unlock xor eax, eax pop edi ret .error: push eax call fat_unlock pop eax pop edi ret ;---------------------------------------------------------------- ; fat_SetFileInfo - FAT implementation of setting file info ; in: ebp = pointer to FAT structure ; in: esi+[esp+4] = name ; in: ebx = pointer to parameters from sysfunc 70 ; out: eax, ebx = return values for sysfunc 70 ;---------------------------------------------------------------- fat_SetFileInfo: cmp byte [esi], 0 jnz @f mov eax, 2 ret @@: push edi call fat_lock stdcall hd_find_lfn, [esp+4+4] jc .error push eax mov edx, [ebx+16] call bdfe_to_fat_entry pop eax lea ebx, [ebp+FAT.buffer] call fs_write32_sys call update_disk call fat_unlock pop edi xor eax, eax ret .error: push eax call fat_unlock pop eax pop edi ret ;---------------------------------------------------------------- ; fat_Delete - FAT implementation of deleting a file/folder ; in: ebp = pointer to FAT structure ; in: esi+[esp+4] = name ; in: ebx = pointer to parameters from sysfunc 70 ; out: eax, ebx = return values for sysfunc 70 ;---------------------------------------------------------------- fat_Delete: call fat_lock cmp byte [esi], 0 jnz @f ; cannot delete root! .access_denied: push ERROR_ACCESS_DENIED .pop_ret: call fat_unlock pop eax xor ebx, ebx ret @@: and [ebp+FAT.longname_sec1], 0 and [ebp+FAT.longname_sec2], 0 push edi stdcall hd_find_lfn, [esp+4+4] jnc .found pop edi push ERROR_FILE_NOT_FOUND jmp .pop_ret .found: cmp dword [edi], '. ' jz .access_denied2 cmp dword [edi], '.. ' jz .access_denied2 test byte [edi+11], 10h jz .dodel ; we can delete only empty folders! pushad mov esi, [edi+20-2] mov si, [edi+26] xor ecx, ecx lea eax, [esi-2] imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] lea ebx, [ebp+FAT.buffer] call fs_read32_sys test eax, eax jnz .err1 lea eax, [ebx+0x200] add ebx, 2*0x20 .checkempty: cmp byte [ebx], 0 jz .empty cmp byte [ebx], 0xE5 jnz .notempty add ebx, 0x20 cmp ebx, eax jb .checkempty inc ecx cmp ecx, [ebp+FAT.SECTORS_PER_CLUSTER] jb @f mov eax, esi call get_FAT jc .err1 cmp eax, 2 jb .error_fat cmp eax, [ebp+FAT.fatRESERVED] jae .empty mov esi, eax xor ecx, ecx @@: lea eax, [esi-2] imul eax, [ebp+FAT.SECTORS_PER_CLUSTER] add eax, [ebp+FAT.DATA_START] add eax, ecx lea ebx, [ebp+FAT.buffer] call fs_read32_sys test eax, eax lea eax, [ebx+0x200] jz .checkempty .err1: popad .err2: pop edi call fat_unlock movi eax, ERROR_DEVICE ret .error_fat: popad pop edi call fat_unlock movi eax, ERROR_FAT_TABLE ret .notempty: popad .access_denied2: pop edi call fat_unlock movi eax, ERROR_ACCESS_DENIED ret .empty: popad push eax ebx lea ebx, [ebp+FAT.buffer] call fs_read32_sys test eax, eax pop ebx eax jnz .err2 .dodel: push eax mov eax, [edi+20-2] mov ax, [edi+26] xchg eax, [esp] ; delete folder entry mov byte [edi], 0xE5 ; delete LFN (if present) .lfndel: lea edx, [ebp+FAT.buffer] cmp edi, edx ja @f cmp [ebp+FAT.longname_sec2], 0 jz .lfndone push [ebp+FAT.longname_sec2] push [ebp+FAT.longname_sec1] pop [ebp+FAT.longname_sec2] and [ebp+FAT.longname_sec1], 0 push ebx mov ebx, edx call fs_write32_sys mov eax, [esp+4] call fs_read32_sys pop ebx pop eax lea edi, [ebp+FAT.buffer+0x200] @@: sub edi, 0x20 cmp byte [edi], 0xE5 jz .lfndone cmp byte [edi+11], 0xF jnz .lfndone mov byte [edi], 0xE5 jmp .lfndel .lfndone: push ebx lea ebx, [ebp+FAT.buffer] call fs_write32_sys pop ebx ; delete FAT chain pop eax call clear_cluster_chain call update_disk call fat_unlock pop edi xor eax, eax ret ; \end{diamond}