;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                                 ;;
;; Copyright (C) KolibriOS team 2004-2011. All rights reserved.    ;;
;; Distributed under terms of the GNU General Public License       ;;
;;                                                                 ;;
;;  FAT32.INC                                                      ;;
;;                                                                 ;;
;;  FAT16/32 functions for KolibriOS                               ;;
;;                                                                 ;;
;;  Copyright 2002 Paolo Minazzi, paolo.minazzi@inwind.it          ;;
;;                                                                 ;;
;;  See file COPYING for details                                   ;;
;;  04.02.2007 LFN create folder - diamond                         ;;
;;  08.10.2006 LFN delete file/folder - diamond                    ;;
;;  20.08.2006 LFN set file size (truncate/extend) - diamond       ;;
;;  17.08.2006 LFN write/append to file - diamond                  ;;
;;  23.06.2006 LFN start application - diamond                     ;;
;;  15.06.2006 LFN get/set file/folder info - diamond              ;;
;;  27.05.2006 LFN create/rewrite file - diamond                   ;;
;;  04.05.2006 LFN read folder - diamond                           ;;
;;  29.04.2006 Elimination of hangup after the                     ;;
;;             expiration hd_wait_timeout -  Mario79               ;;
;;  23.04.2006 LFN read file - diamond                             ;;
;;  28.01.2006 find all Fat16/32 partition in all input point      ;;
;;             to MBR, see file part_set.inc - Mario79             ;;
;;  15.01.2005 get file size/attr/date, file_append - ATV          ;;
;;  04.12.2004 skip volume label, file delete bug fixed - ATV      ;;
;;  29.11.2004 get_free_FAT changed, append dir bug fixed - ATV    ;;
;;  23.11.2004 don't allow overwrite dir with file - ATV           ;;
;;  18.11.2004 get_disk_info and more error codes - ATV            ;;
;;  17.11.2004 set_FAT/get_FAT and disk cache rewritten - ATV      ;;
;;  10.11.2004 removedir clear whole directory structure - ATV     ;;
;;  08.11.2004 rename - ATV                                        ;;
;;  30.10.2004 file_read return also dirsize in bytes - ATV        ;;
;;  20.10.2004 Makedir/Removedir - ATV                             ;;
;;  14.10.2004 Partition chain/Fat16 - ATV (thanks drh3xx)         ;;
;;  06.9.2004  Fix free space by Mario79 added - MH                ;;
;;  24.5.2004  Write back buffer for File_write -VT                ;;
;;  20.5.2004  File_read function to work with syscall 58 - VT     ;;
;;  30.3.2004  Error parameters at function return - VT            ;;
;;  01.5.2002  Bugfix in device write - VT                         ;;
;;  20.5.2002  Hd status check - VT                                ;;
;;  29.6.2002  Improved fat32 verification - VT                    ;;
;;                                                                 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$


cache_max equ 1919      ; max. is 1919*512+0x610000=0x6ffe00

ERROR_SUCCESS        = 0
ERROR_DISK_BASE      = 1
ERROR_UNSUPPORTED_FS = 2
ERROR_UNKNOWN_FS     = 3
ERROR_PARTITION      = 4
ERROR_FILE_NOT_FOUND = 5
ERROR_END_OF_FILE    = 6
ERROR_MEMORY_POINTER = 7
ERROR_DISK_FULL      = 8
ERROR_FAT_TABLE      = 9
ERROR_ACCESS_DENIED  = 10
ERROR_DEVICE         = 11

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]

uglobal
align 4
partition_count      dd 0       ; partitions found by set_FAT32_variables
longname_sec1        dd 0       ; used by analyze_directory to save 2 previous
longname_sec2        dd 0       ; directory sectors for delete long filename

hd_error             dd 0       ; set by wait_for_sector_buffer
hd_setup             dd 0
hd_wait_timeout      dd 0

cluster_tmp          dd 0       ; used by analyze_directory
                                ; and analyze_directory_to_write

file_size            dd 0       ; used by file_read

cache_search_start   dd 0       ; used by find_empty_slot
endg

iglobal
fat_in_cache         dd -1
endg

uglobal
align 4
fat_cache:
                     times 512 db 0
 Sector512:                      ; label for dev_hdcd.inc
  buffer:
                       times 512 db 0
  fsinfo_buffer:
                       times 512 db 0
endg

uglobal
  fat16_root           db 0       ; flag for fat16 rootdir
  fat_change           db 0       ; 1=fat has changed
endg

reserve_hd1:

        cli
        cmp     [hd1_status], 0
        je      reserve_ok1

        sti
        call    change_task
        jmp     reserve_hd1

  reserve_ok1:

        push    eax
        mov     eax, [CURRENT_TASK]
        shl     eax, 5
        mov     eax, [eax+CURRENT_TASK+TASKDATA.pid]
        mov     [hd1_status], eax
        pop     eax
        sti
        ret
;********************************************

uglobal
hd_in_cache db ?
endg

reserve_hd_channel:
; BIOS disk accesses are protected with common mutex hd1_status
; This must be modified when hd1_status will not be valid!
        cmp     [hdpos], 0x80
        jae     .ret
        cmp     [hdbase], 0x1F0
        jne     .IDE_Channel_2
.IDE_Channel_1:
        cli
        cmp     [IDE_Channel_1], 0
        je      .reserve_ok_1
        sti
        call    change_task
        jmp     .IDE_Channel_1
.IDE_Channel_2:
        cli
        cmp     [IDE_Channel_2], 0
        je      .reserve_ok_2
        sti
        call    change_task
        jmp     .IDE_Channel_2
.reserve_ok_1:
        mov     [IDE_Channel_1], 1
        push    eax
        mov     al, 1
        jmp     @f
.reserve_ok_2:
        mov     [IDE_Channel_2], 1
        push    eax
        mov     al, 3
@@:
        cmp     [hdid], 1
        sbb     al, -1
        cmp     al, [hd_in_cache]
        jz      @f
        mov     [hd_in_cache], al
        call    clear_hd_cache
@@:
        pop     eax
        sti
.ret:
        ret

free_hd_channel:
; see comment at reserve_hd_channel
        cmp     [hdpos], 0x80
        jae     .ret
        cmp     [hdbase], 0x1F0
        jne     .IDE_Channel_2
.IDE_Channel_1:
        mov     [IDE_Channel_1], 0
.ret:
        ret
.IDE_Channel_2:
        mov     [IDE_Channel_2], 0
        ret
;********************************************
problem_partition db 0  ; used for partitions search

include  'part_set.inc'

set_FAT:
;--------------------------------
; input  : EAX = cluster
;          EDX = value to save
; output : EDX = old value
;--------------------------------
        push    eax ebx esi

        cmp     eax, 2
        jb      sfc_error
        cmp     eax, [LAST_CLUSTER]
        ja      sfc_error
        cmp     [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, [FAT_START]
        mov     ebx, fat_cache

        cmp     eax, [fat_in_cache]; is fat sector already in memory?
        je      sfc_in_cache    ; yes

        cmp     [fat_change], 0 ; is fat changed?
        je      sfc_no_change   ; no
        call    write_fat_sector; yes. write it into disk
        cmp     [hd_error], 0
        jne     sfc_error

  sfc_no_change:
        mov     [fat_in_cache], eax; save fat sector
        call    hd_read
        cmp     [hd_error], 0
        jne     sfc_error


  sfc_in_cache:
        cmp     [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, [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     [fat_change], 1 ; fat has changed

  sfc_nonzero:
        and     edx, [fatMASK]

  sfc_error:
        pop     esi ebx eax
        ret


get_FAT:
;--------------------------------
; input  : EAX = cluster
; output : EAX = next cluster
;--------------------------------
        push    ebx esi

        cmp     [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, [FAT_START]
        mov     ebx, fat_cache

        cmp     eax, [fat_in_cache]; is fat sector already in memory?
        je      gfc_in_cache

        cmp     [fat_change], 0 ; is fat changed?
        je      gfc_no_change   ; no
        call    write_fat_sector; yes. write it into disk
        cmp     [hd_error], 0
        jne     hd_error_01

  gfc_no_change:
        mov     [fat_in_cache], eax
        call    hd_read
        cmp     [hd_error], 0
        jne     hd_error_01

  gfc_in_cache:
        mov     eax, [ebx+esi]
        and     eax, [fatMASK]
 hd_error_01:
        pop     esi ebx
        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, [LAST_CLUSTER]; counter for full disk
        sub     ecx, 2
        mov     eax, [fatStartScan]
        cmp     eax, 2
        jb      gff_reset

  gff_test:
        cmp     eax, [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
        cmp     [hd_error], 0
        jne     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?
        jns     gff_test        ; no

  gff_not_found_1:
        add     esp, 4
  gff_not_found:
        pop     ecx             ; yes. disk is full
        stc
        ret

  gff_found:
        lea     ecx, [eax+1]
        mov     [fatStartScan], ecx
        pop     ecx
        clc
        ret


write_fat_sector:
;-----------------------------------------------------------
; write changed fat to disk
;-----------------------------------------------------------
        push    eax ebx ecx

        mov     [fat_change], 0
        mov     eax, [fat_in_cache]
        cmp     eax, -1
        jz      write_fat_not_used
        mov     ebx, fat_cache
        mov     ecx, [NUMBER_OF_FATS]

  write_next_fat:
        call    hd_write
        cmp     [hd_error], 0
        jne     write_fat_not_used

        add     eax, [SECTORS_PER_FAT]
        dec     ecx
        jnz     write_next_fat

  write_fat_not_used:
        pop     ecx ebx eax
        ret


analyze_directory:
;-----------------------------------------------------------
; input  : EAX = first cluster of the directory
;          EBX = pointer to filename
; output : IF CARRY=0 EAX = sector where th file is found
;                     EBX = pointer in buffer
;                     [buffer .. buffer+511]
;                     ECX,EDX,ESI,EDI not changed
;          IF CARRY=1 filename not found
; Note   : if cluster=0 it's changed to read rootdir
;          save 2 previous directory sectors in longname_sec
;-----------------------------------------------------------
        push    ecx edx esi edi ebx; ebx = [esp+0]
        mov     [longname_sec1], 0
        mov     [longname_sec2], 0

  adr_new_cluster:
        mov     [cluster_tmp], eax
        mov     [fat16_root], 0
        cmp     eax, [LAST_CLUSTER]
        ja      adr_not_found   ; too big cluster number, something is wrong
        cmp     eax, 2
        jnb     adr_data_cluster

        mov     eax, [ROOT_CLUSTER]; if cluster < 2 then read rootdir
        cmp     [fs_type], 16
        jne     adr_data_cluster
        mov     eax, [ROOT_START]
        mov     edx, [ROOT_SECTORS]
        mov     [fat16_root], 1 ; flag for fat16 rootdir
        jmp     adr_new_sector

  adr_data_cluster:
        sub     eax, 2
        mov     edx, [SECTORS_PER_CLUSTER]
        imul    eax, edx
        add     eax, [DATA_START]

  adr_new_sector:
        mov     ebx, buffer
        call    hd_read
        cmp     [hd_error], 0
        jne     adr_not_found

        mov     ecx, 512/32     ; count of dir entrys per sector = 16

  adr_analyze:
        mov     edi, [ebx+11]   ; file attribute
        and     edi, 0xf
        cmp     edi, 0xf
        je      adr_long_filename
        test    edi, 0x8        ; skip over volume label
        jne     adr_long_filename; Note: label can be same name as file/dir

        mov     esi, [esp+0]    ; filename need to be uppercase
        mov     edi, ebx
        push    ecx
        mov     ecx, 11
        cld
        rep cmpsb               ; compare 8+3 filename
        pop     ecx
        je      adr_found

  adr_long_filename:
        add     ebx, 32         ; position of next dir entry
        dec     ecx
        jnz     adr_analyze

        mov     ecx, [longname_sec1]; save 2 previous directory sectors
        mov     [longname_sec1], eax; for delete long filename
        mov     [longname_sec2], ecx
        inc     eax             ; next sector
        dec     edx
        jne     adr_new_sector
        cmp     [fat16_root], 1 ; end of fat16 rootdir
        je      adr_not_found

  adr_next_cluster:
        mov     eax, [cluster_tmp]
        call    get_FAT         ; get next cluster
        cmp     [hd_error], 0
        jne     adr_not_found

        cmp     eax, 2          ; incorrect fat chain?
        jb      adr_not_found   ; yes
        cmp     eax, [fatRESERVED]; is it end of directory?
        jb      adr_new_cluster ; no. analyse it

  adr_not_found:
        pop     edi edi esi edx ecx; first edi will remove ebx
        stc                     ; file not found
        ret

  adr_found:
        pop     edi edi esi edx ecx; first edi will remove ebx
        clc                     ; file found
        ret


get_data_cluster:
;-----------------------------------------------------------
; input  : EAX = cluster
;          EBX = pointer to buffer
;          EDX = # blocks to read in buffer
;          ESI = # blocks to skip over
; output : if CARRY=0 ok EBX/EDX/ESI updated
;          if CARRY=1 cluster out of range
; Note   : if cluster=0 it's changed to read rootdir
;-----------------------------------------------------------
        push    eax ecx

        mov     [fat16_root], 0
        cmp     eax, [LAST_CLUSTER]
        ja      gdc_error       ; too big cluster number, something is wrong
        cmp     eax, 2
        jnb     gdc_cluster

        mov     eax, [ROOT_CLUSTER]; if cluster < 2 then read rootdir
        cmp     [fs_type], 16
        jne     gdc_cluster
        mov     eax, [ROOT_START]
        mov     ecx, [ROOT_SECTORS]; Note: not cluster size
        mov     [fat16_root], 1 ; flag for fat16 rootdir
        jmp     gdc_read

  gdc_cluster:
        sub     eax, 2
        mov     ecx, [SECTORS_PER_CLUSTER]
        imul    eax, ecx
        add     eax, [DATA_START]

  gdc_read:
        test    esi, esi        ; first wanted block
        je      gdcl1           ; yes, skip count is 0
        dec     esi
        jmp     gdcl2

  gdcl1:
        call    hd_read
        cmp     [hd_error], 0
        jne     gdc_error

        add     ebx, 512        ; update pointer
        dec     edx

  gdcl2:
        test    edx, edx        ; is all read?
        je      out_of_read

        inc     eax             ; next sector
        dec     ecx
        jnz     gdc_read

  out_of_read:
        pop     ecx eax
        clc
        ret

  gdc_error:
        pop     ecx eax
        stc
        ret


get_cluster_of_a_path:
;---------------------------------------------------------
; input  : EBX = pointer to a path string
;          (example: the path "/files/data/document" become
;                             "files......data.......document...0"
;          '.' = space char
;          '0' = char(0) (ASCII=0) !!! )
; output : if (CARRY=1) -> ERROR in the PATH
;          if (CARRY=0) -> EAX=cluster
;---------------------------------------------------------
        push    ebx edx

        mov     eax, [ROOT_CLUSTER]
        mov     edx, ebx

search_end_of_path:
        cmp     byte [edx], 0
        je      found_end_of_path

        inc     edx; '/'
        mov     ebx, edx
        call    analyze_directory
        jc      directory_not_found

        mov     eax, [ebx+20-2] ; read the HIGH 16bit cluster field
        mov     ax, [ebx+26]    ; read the LOW 16bit cluster field
        and     eax, [fatMASK]
        add     edx, 11         ; 8+3 (name+extension)
        jmp     search_end_of_path

found_end_of_path:
        pop     edx ebx
        clc                     ; no errors
        ret

directory_not_found:
        pop     edx ebx
        stc                     ; errors occour
        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     [fs_type], 32  ; free disk space only used by fat32
        jne     add_dfs_no

        push    eax ebx
        mov     eax, [ADR_FSINFO]
        mov     ebx, fsinfo_buffer
        call    hd_read
        cmp     [hd_error], 0
        jne     add_not_fs

        cmp     dword [ebx+0x1fc], 0xaa550000; check sector id
        jne     add_not_fs

        add     [ebx+0x1e8], ecx
        push    [fatStartScan]
        pop     dword [ebx+0x1ec]
        call    hd_write
;    cmp   [hd_error],0
;    jne   add_not_fs

  add_not_fs:
        pop     ebx eax

  add_dfs_no:
        ret


file_read:
;--------------------------------------------------------------------------
;   INPUT :  user-register register-in-this  meaning         symbol-in-this
;
;            EAX           EDI               system call to write   /
;            EBX           EAX   (PAR0)      pointer to file-name   PAR0
;            EDX           ECX   (PAR1)      pointer to buffer      PAR1
;            ECX           EBX   (PAR2)   vt file blocks to read    PAR2
;            ESI           EDX   (PAR3)      pointer to path        PAR3
;            EDI           ESI            vt first 512 block to read
;                          EDI               if 0 - read root
;
; output : eax = 0 - ok
;                3 - unknown FS
;                5 - file not found
;                6 - end of file
;                9 - fat table corrupted
;               10 - access denied
;          ebx = size of file/directory
;--------------------------------------------------------------------------
        cmp     [fs_type], 16
        jz      fat_ok_for_reading
        cmp     [fs_type], 32
        jz      fat_ok_for_reading
        xor     ebx, ebx
        mov     eax, ERROR_UNKNOWN_FS
        mov     [hd1_status], ebx
        ret

  fat_ok_for_reading:
;    call  reserve_hd1

        pushad

        mov     ebx, edx
        call    get_cluster_of_a_path
        jc      file_to_read_not_found

        test    edi, edi        ; read rootdir
        jne     no_read_root

        xor     eax, eax
        call    get_dir_size    ; return rootdir size
        cmp     [hd_error], 0
        jne     file_access_denied

        mov     [file_size], eax
        mov     eax, [ROOT_CLUSTER]
        jmp     file_read_start

  no_read_root:
        mov     ebx, PUSHAD_EAX ; file name
        call    analyze_directory
        jc      file_to_read_not_found

        mov     eax, [ebx+28]   ; file size
        test    byte [ebx+11], 0x10; is it directory?
        jz      read_set_size   ; no

        mov     eax, [ebx+20-2] ; FAT entry
        mov     ax, [ebx+26]
        and     eax, [fatMASK]
        call    get_dir_size
        cmp     [hd_error], 0
        jne     file_access_denied

  read_set_size:
        mov     [file_size], eax

        mov     eax, [ebx+20-2] ; FAT entry
        mov     ax, [ebx+26]
        and     eax, [fatMASK]

  file_read_start:
        mov     ebx, PUSHAD_ECX ; pointer to buffer
        mov     edx, PUSHAD_EBX ; file blocks to read
        mov     esi, PUSHAD_ESI ; first 512 block to read

  file_read_new_cluster:
        call    get_data_cluster
        jc      file_read_eof   ; end of file or cluster out of range

        test    edx, edx        ; is all read?
        je      file_read_OK    ; yes

        call    get_FAT         ; get next cluster
        cmp     [hd_error], 0
        jne     file_access_denied

        cmp     eax, [fatRESERVED]; end of file
        jnb     file_read_eof
        cmp     eax, 2          ; incorrect fat chain
        jnb     file_read_new_cluster

        popad
        mov     [hd1_status], 0
        mov     ebx, [file_size]
        mov     eax, ERROR_FAT_TABLE
        ret

  file_read_eof:
        cmp     [hd_error], 0
        jne     file_access_denied
        popad
        mov     [hd1_status], 0
        mov     ebx, [file_size]
        mov     eax, ERROR_END_OF_FILE
        ret

  file_read_OK:
        popad
        mov     [hd1_status], 0
        mov     ebx, [file_size]
        xor     eax, eax
        ret

  file_to_read_not_found:
        cmp     [hd_error], 0
        jne     file_access_denied
        popad
        mov     [hd1_status], 0
        xor     ebx, ebx
        mov     eax, ERROR_FILE_NOT_FOUND
        ret

  file_access_denied:
        popad
        mov     [hd1_status], 0
        xor     ebx, ebx
        mov     eax, ERROR_ACCESS_DENIED
        ret

get_dir_size:
;-----------------------------------------------------
; input  : eax = first cluster (0=rootdir)
; output : eax = directory size in bytes
;-----------------------------------------------------
        push    edx
        xor     edx, edx        ; count of directory clusters
        test    eax, eax
        jnz     dir_size_next

        mov     eax, [ROOT_SECTORS]
        shl     eax, 9          ; fat16 rootdir size in bytes
        cmp     [fs_type], 16
        je      dir_size_ret
        mov     eax, [ROOT_CLUSTER]

  dir_size_next:
        cmp     eax, 2          ; incorrect fat chain
        jb      dir_size_end
        cmp     eax, [fatRESERVED]; end of directory
        ja      dir_size_end
        call    get_FAT         ; get next cluster
        cmp     [hd_error], 0
        jne     dir_size_ret

        inc     edx
        jmp     dir_size_next

  dir_size_end:
        imul    eax, [SECTORS_PER_CLUSTER], 512; cluster size in bytes
        imul    eax, edx

  dir_size_ret:
        pop     edx
        ret


clear_cluster_chain:
;-----------------------------------------------------
; input  : eax = first cluster
;-----------------------------------------------------
        push    eax ecx edx
        xor     ecx, ecx        ; cluster count

  clean_new_chain:
        cmp     eax, [LAST_CLUSTER]; end of file
        ja      delete_OK
        cmp     eax, 2          ; unfinished fat chain or zero length file
        jb      delete_OK
        cmp     eax, [ROOT_CLUSTER]; don't remove root cluster
        jz      delete_OK

        xor     edx, edx
        call    set_FAT         ; clear fat entry
        cmp     [hd_error], 0
        jne     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
  access_denied_01:
        pop     edx ecx eax
        ret


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     [fs_type], 16
        jz      info_fat_ok
        cmp     [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, [LAST_CLUSTER]

  info_cluster:
        push    eax
        call    get_FAT         ; get cluster info
        cmp     [hd_error], 0
        jne     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, [SECTORS_PER_CLUSTER], 512; cluster size in bytes
        mov     [hd1_status], 0
        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

update_disk:
;-----------------------------------------------------------
; write changed fat and cache to disk
;-----------------------------------------------------------
        cmp     [fat_change], 0 ; is fat changed?
        je      upd_no_change

        call    write_fat_sector
        cmp     [hd_error], 0
        jne     update_disk_acces_denied

  upd_no_change:

        call    write_cache
  update_disk_acces_denied:
        ret


; \begin{diamond}
hd_find_lfn:
; in: esi+ebp -> name
; out: CF=1 - file not found
;      else CF=0 and edi->direntry, eax=sector
; destroys eax
        push    esi edi
        push    0
        push    0
        push    fat16_root_first
        push    fat16_root_next
        mov     eax, [ROOT_CLUSTER]
        cmp     [fs_type], 32
        jz      .fat32
.loop:
        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
.found:
        test    ebp, ebp
        jz      @f
        mov     esi, ebp
        xor     ebp, ebp
        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, [ROOT_START]
.cmn:
        add     esp, 20         ; CF=0
        pop     esi
        ret

;----------------------------------------------------------------
;
;  fs_HdRead - LFN variant for reading hard disk
;
;  esi  points to filename
;  ebx  pointer to 64-bit number = first wanted byte, 0+
;       may be ebx=0 - start from first byte
;  ecx  number of bytes to read, 0+
;  edx  mem location to return data
;
;  ret ebx = bytes read or 0xffffffff file not found
;      eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
fs_HdRead:
        cmp     [fs_type], 16
        jz      @f
        cmp     [fs_type], 32
        jz      @f
        cmp     [fs_type], 1
        jz      ntfs_HdRead
        cmp     [fs_type], 2
        jz      ext2_HdRead
        or      ebx, -1
        mov     eax, ERROR_UNKNOWN_FS
        ret
@@:
        push    edi
        cmp     byte [esi], 0
        jnz     @f
.noaccess:
        pop     edi
.noaccess_2:
        or      ebx, -1
        mov     eax, ERROR_ACCESS_DENIED
        ret

@@:
        call    hd_find_lfn
        jnc     .found
        pop     edi
        cmp     [hd_error], 0
        jne     .noaccess_2
        or      ebx, -1
        mov     eax, ERROR_FILE_NOT_FOUND
        ret

.found:
        test    byte [edi+11], 0x10; do not allow read directories
        jnz     .noaccess
        test    ebx, ebx
        jz      .l1
        cmp     dword [ebx+4], 0
        jz      @f
        xor     ebx, ebx
.reteof:
        mov     eax, 6
        pop     edi
        ret
@@:
        mov     ebx, [ebx]
.l1:
        push    ecx edx
        push    0
        mov     eax, [edi+28]
        sub     eax, ebx
        jb      .eof
        cmp     eax, ecx
        jae     @f
        mov     ecx, eax
        mov     byte [esp], 6
@@:
        mov     eax, [edi+20-2]
        mov     ax, [edi+26]
; now eax=cluster, ebx=position, ecx=count, edx=buffer for data
.new_cluster:
        jecxz   .new_sector
        test    eax, eax
        jz      .eof
        cmp     eax, [fatRESERVED]
        jae     .eof
        mov     [cluster_tmp], eax
        dec     eax
        dec     eax
        mov     edi, [SECTORS_PER_CLUSTER]
        imul    eax, edi
        add     eax, [DATA_START]
.new_sector:
        test    ecx, ecx
        jz      .done
        sub     ebx, 512
        jae     .skip
        add     ebx, 512
        jnz     .force_buf
        cmp     ecx, 512
        jb      .force_buf
; we may read directly to given buffer
        push    ebx
        mov     ebx, edx
        call    hd_read
        pop     ebx
        cmp     [hd_error], 0
        jne     .noaccess_1
        add     edx, 512
        sub     ecx, 512
        jmp     .skip
.force_buf:
; we must read sector to temporary buffer and then copy it to destination
        push    eax ebx
        mov     ebx, buffer
        call    hd_read
        mov     eax, ebx
        pop     ebx
        cmp     [hd_error], 0
        jne     .noaccess_3
        add     eax, ebx
        push    ecx
        add     ecx, ebx
        cmp     ecx, 512
        jbe     @f
        mov     ecx, 512
@@:
        sub     ecx, ebx
        mov     ebx, edx
        call    memmove
        add     edx, ecx
        sub     [esp], ecx
        pop     ecx
        pop     eax
        xor     ebx, ebx
.skip:
        inc     eax
        dec     edi
        jnz     .new_sector
        mov     eax, [cluster_tmp]
        call    get_FAT
        cmp     [hd_error], 0
        jne     .noaccess_1

        jmp     .new_cluster
.noaccess_3:
        pop     eax
.noaccess_1:
        pop     eax
        push    11
.done:
        mov     ebx, edx
        pop     eax edx ecx edi
        sub     ebx, edx
        ret
.eof:
        mov     ebx, edx
        pop     eax edx ecx
        sub     ebx, edx
        jmp     .reteof

;----------------------------------------------------------------
;
;  fs_HdReadFolder - LFN variant for reading hard disk folder
;
;  esi  points to filename
;  ebx  pointer to structure 32-bit number = first wanted block, 0+
;                          & flags (bitfields)
; flags: bit 0: 0=ANSI names, 1=UNICODE names
;  ecx  number of blocks to read, 0+
;  edx  mem location to return data
;
;  ret ebx = blocks read or 0xffffffff folder not found
;      eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
fs_HdReadFolder:
        cmp     [fs_type], 1
        jz      ntfs_HdReadFolder
        cmp     [fs_type], 2
        jz      ext2_HdReadFolder
        cmp     [fs_type], 16
        jz      @f
        cmp     [fs_type], 32
        jz      @f
        push    ERROR_UNSUPPORTED_FS
        pop     eax
        or      ebx, -1
        ret
@@:
        mov     eax, [ROOT_CLUSTER]
        push    edi
        cmp     byte [esi], 0
        jz      .doit
        call    hd_find_lfn
        jnc     .found
        pop     edi
        or      ebx, -1
        mov     eax, ERROR_FILE_NOT_FOUND
        ret
.found:
        test    byte [edi+11], 0x10     ; do not allow read files
        jnz     .found_dir
        pop     edi
        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 ecx
        push    ebp
        sub     esp, 262*2      ; reserve space for LFN
        mov     ebp, esp
        push    dword [ebx+4]   ; for fat_get_name: read ANSI/UNICODE name
        mov     ebx, [ebx]
; init header
        push    eax ecx
        mov     edi, edx
        mov     ecx, 32/4
        xor     eax, eax
        rep stosd
        pop     ecx eax
        mov     byte [edx], 1   ; version
        mov     esi, edi        ; esi points to BDFE
.new_cluster:
        mov     [cluster_tmp], eax
        test    eax, eax
        jnz     @f
        cmp     [fs_type], 32
        jz      .notfound
        mov     eax, [ROOT_START]
        push    [ROOT_SECTORS]
        push    ebx
        jmp     .new_sector
@@:
        dec     eax
        dec     eax
        imul    eax, [SECTORS_PER_CLUSTER]
        push    [SECTORS_PER_CLUSTER]
        add     eax, [DATA_START]
        push    ebx
.new_sector:
        mov     ebx, buffer
        mov     edi, ebx
        call    hd_read
        cmp     [hd_error], 0
        jnz     .notfound2
        add     ebx, 512
        push    eax
.l1:
        call    fat_get_name
        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, [cluster_tmp]
        test    eax, eax
        jz      .done
        call    get_FAT
        cmp     [hd_error], 0
        jnz     .notfound2
        cmp     eax, 2
        jb      .done
        cmp     eax, [fatRESERVED]
        jae     .done
        push    eax
        mov     eax, [SECTORS_PER_CLUSTER]
        mov     [esp+8], eax
        pop     eax
        mov     [cluster_tmp], eax
        dec     eax
        dec     eax
        imul    eax, [SECTORS_PER_CLUSTER]
        add     eax, [DATA_START]
@@:
        mov     ebx, buffer
        mov     edi, ebx
        call    hd_read
        cmp     [hd_error], 0
        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
        call    fat_entry_to_bdfe
.l2:
        add     edi, 0x20
        cmp     edi, ebx
        jb      .l1
        pop     eax
        inc     eax
        dec     dword [esp+4]
        jnz     .new_sector
        mov     eax, [cluster_tmp]
        test    eax, eax
        jz      .done
        call    get_FAT
        cmp     [hd_error], 0
        jnz     .notfound2
        cmp     eax, 2
        jb      .done
        cmp     eax, [fatRESERVED]
        jae     .done
        push    eax
        mov     eax, [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     ebp ecx esi edi
        mov     eax, ERROR_FILE_NOT_FOUND
        or      ebx, -1
        ret
.done:
        add     esp, 262*2+4+8
        pop     ebp
        mov     ebx, [edx+4]
        xor     eax, eax
        dec     ecx
        js      @f
        mov     al, ERROR_END_OF_FILE
@@:
        pop     ecx esi edi
        ret

fat16_root_next:
        cmp     edi, buffer+0x200-0x20
        jae     fat16_root_next_sector
        add     edi, 0x20
        ret     ; CF=0
fat16_root_next_sector:
; read next sector
        push    [longname_sec2]
        pop     [longname_sec1]
        push    ecx
        mov     ecx, [eax+4]
        push    ecx
        add     ecx, [ROOT_START]
        mov     [longname_sec2], ecx
        pop     ecx
        inc     ecx
        mov     [eax+4], ecx
        cmp     ecx, [ROOT_SECTORS]
        pop     ecx
        jae     fat16_root_first.readerr
fat16_root_first:
        mov     eax, [eax+4]
        add     eax, [ROOT_START]
        push    ebx
        mov     edi, buffer
        mov     ebx, edi
        call    hd_read
        pop     ebx
        cmp     [hd_error], 0
        jnz     .readerr
        ret     ; CF=0
.readerr:
        stc
        ret
fat16_root_begin_write:
        push    edi eax
        call    fat16_root_first
        pop     eax edi
        ret
fat16_root_end_write:
        pusha
        mov     eax, [eax+4]
        add     eax, [ROOT_START]
        mov     ebx, buffer
        call    hd_write
        popa
        ret
fat16_root_next_write:
        cmp     edi, buffer+0x200
        jae     @f
        ret
@@:
        call    fat16_root_end_write
        jmp     fat16_root_next_sector
fat16_root_extend_dir:
        stc
        ret

fat_notroot_next:
        cmp     edi, buffer+0x200-0x20
        jae     fat_notroot_next_sector
        add     edi, 0x20
        ret     ; CF=0
fat_notroot_next_sector:
        push    [longname_sec2]
        pop     [longname_sec1]
        push    eax
        call    fat_get_sector
        mov     [longname_sec2], eax
        pop     eax
        push    ecx
        mov     ecx, [eax+4]
        inc     ecx
        cmp     ecx, [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
        cmp     [hd_error], 0
        jnz     fat_notroot_next_err
        cmp     ecx, [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
        mov     edi, buffer
        mov     ebx, edi
        call    hd_read
        pop     ebx
        cmp     [hd_error], 0
        jnz     @f
        ret     ; CF=0
fat_notroot_next_err:
        pop     ecx
@@:
        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
        mov     ebx, buffer
        call    hd_write
        pop     ebx
        ret
fat_notroot_next_write:
        cmp     edi, buffer+0x200
        jae     @f
        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, [fatEND]
        call    set_FAT
        mov     edx, eax
        mov     eax, [esp+4]
        mov     eax, [eax]
        push    edx
        call    set_FAT
        pop     edx
        cmp     [hd_error], 0
        jz      @f
        pop     edx
        pop     eax
        stc
        ret
@@:
        push    ecx
        or      ecx, -1
        call    add_disk_free_space
; zero new cluster
        mov     ecx, 512/4
        mov     edi, 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, [SECTORS_PER_CLUSTER]
        imul    eax, ecx
        add     eax, [DATA_START]
        mov     ebx, edi
@@:
        call    hd_write
        inc     eax
        loop    @b
        pop     ecx ebx eax
        clc
        ret

fat_get_sector:
        push    ecx
        mov     ecx, [eax]
        dec     ecx
        dec     ecx
        imul    ecx, [SECTORS_PER_CLUSTER]
        add     ecx, [DATA_START]
        add     ecx, [eax+4]
        mov     eax, ecx
        pop     ecx
        ret

;----------------------------------------------------------------
;
;  fs_HdRewrite - LFN variant for writing hard disk
;
;  esi  points to filename
;  ebx  ignored (reserved)
;  ecx  number of bytes to write, 0+
;  edx  mem location to data
;
;  ret ebx = number of written bytes
;      eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
fshrad:
        mov     eax, ERROR_ACCESS_DENIED
        xor     ebx, ebx
        ret
fshrfs:
        mov     eax, ERROR_UNKNOWN_FS
        xor     ebx, ebx
        ret

fs_HdCreateFolder:
        mov     al, 1
        jmp     fs_HdRewrite.common

fs_HdRewrite:
        xor     eax, eax
.common:
        cmp     [fs_type], 1
        jz      ntfs_HdRewrite
        cmp     [fs_type], 2
        jz      ext2_HdRewrite
        cmp     [fs_type], 16
        jz      @f
        cmp     [fs_type], 32
        jnz     fshrfs
@@:
        cmp     byte [esi], 0
        jz      fshrad
        pushad
        xor     edi, edi
        push    esi
        test    ebp, ebp
        jz      @f
        mov     esi, ebp
@@:
        lodsb
        test    al, al
        jz      @f
        cmp     al, '/'
        jnz     @b
        lea     edi, [esi-1]
        jmp     @b
@@:
        pop     esi
        test    edi, edi
        jnz     .noroot
        test    ebp, ebp
        jnz     .hasebp
        mov     ebp, [ROOT_CLUSTER]
        cmp     [fs_type], 32
        jz      .pushnotroot
        push    fat16_root_extend_dir
        push    fat16_root_end_write
        push    fat16_root_next_write
        push    fat16_root_begin_write
        xor     ebp, ebp
        push    ebp
        push    ebp
        push    fat16_root_first
        push    fat16_root_next
        jmp     .common1
.hasebp:
        mov     eax, ERROR_ACCESS_DENIED
        cmp     byte [ebp], 0
        jz      .ret1
        push    ebp
        xor     ebp, ebp
        call    hd_find_lfn
        pop     esi
        jc      .notfound0
        jmp     .common0
.noroot:
        mov     eax, ERROR_ACCESS_DENIED
        cmp     byte [edi+1], 0
        jz      .ret1
; check existence
        mov     byte [edi], 0
        push    edi
        call    hd_find_lfn
        pop     esi
        mov     byte [esi], '/'
        jnc     @f
.notfound0:
        mov     eax, ERROR_FILE_NOT_FOUND
.ret1:
        mov     [esp+28], eax
        popad
        xor     ebx, ebx
        ret
@@:
        inc     esi
.common0:
        test    byte [edi+11], 0x10     ; must be directory
        mov     eax, ERROR_ACCESS_DENIED
        jz      .ret1
        mov     ebp, [edi+20-2]
        mov     bp, [edi+26]            ; ebp=cluster
        mov     eax, ERROR_FAT_TABLE
        cmp     ebp, 2
        jb      .ret1
.pushnotroot:
        push    fat_notroot_extend_dir
        push    fat_notroot_end_write
        push    fat_notroot_next_write
        push    fat_notroot_begin_write
        push    0
        push    ebp
        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, 32
        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+32+28], 0
        jz      @f
        add     esp, 32
        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, [fatRESERVED]
        jae     .done1
        push    edx
        xor     edx, edx
        call    set_FAT
        mov     eax, edx
        pop     edx
        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, 32
        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     [eax], ebp
        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+32
        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
        push    1
        pop     eax     ; 1 entry
        jnz     .notilde
; we need ceil(strlen(esi)/13) additional entries = floor((strlen(esi)+12+13)/13) total
        xor     eax, eax
@@:
        cmp     byte [esi], 0
        jz      @f
        inc     esi
        inc     eax
        jmp     @b
@@:
        sub     esi, eax
        add     eax, 12+13
        mov     ecx, 13
        push    edx
        cdq
        div     ecx
        pop     edx
.notilde:
        push    -1
        push    -1
        push    -1
; find <eax> successive entries in directory
        xor     ecx, ecx
        push    eax
        lea     eax, [esp+16+8+12+8]
        mov     [eax], ebp
        and     dword [eax+4], 0
        call    dword [eax-4]
        pop     eax
        jnc     .scan_dir
.fsfrfe3:
        add     esp, 12+8+12+32
        popad
        mov     eax, 11
        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]
        pop     eax
        jnc     .scan_dir
        cmp     [hd_error], 0
        jnz     .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+32
        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!
; calculate name checksum
        push    esi ecx
        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    fs_RamdiskRewrite.read_symbols
        mov     ax, 0xF
        stosw
        mov     al, [esp+4]
        stosb
        mov     cl, 6
        call    fs_RamdiskRewrite.read_symbols
        xor     eax, eax
        stosw
        mov     cl, 2
        call    fs_RamdiskRewrite.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+32+28], cl
        jz      .doit
; create directory
        mov     byte [edi+11], 10h      ; attributes: folder
        mov     edx, edi
        lea     eax, [esp+8]
        call    dword [eax+16]  ; flush directory
        push    ecx
        mov     ecx, [SECTORS_PER_CLUSTER]
        shl     ecx, 9
        jmp     .doit2
.doit:
        lea     eax, [esp+8]
        call    dword [eax+16]  ; flush directory
        push    ecx
        mov     ecx, [esp+4+32+24]
.doit2:
        push    ecx
        push    edi
        mov     esi, edx
        test    ecx, ecx
        jz      .done
        call    get_free_FAT
        jc      .diskfull
        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, [fatEND]
        call    set_FAT
        pop     edx
.write_cluster:
        push    eax
        dec     eax
        dec     eax
        mov     ebp, [SECTORS_PER_CLUSTER]
        imul    eax, ebp
        add     eax, [DATA_START]
; write data
.write_sector:
        cmp     byte [esp+16+32+28], 0
        jnz     .writedir
        mov     ecx, 512
        cmp     dword [esp+8], ecx
        jb      .writeshort
; we can write directly from given buffer
        mov     ebx, esi
        add     esi, ecx
        jmp     .writecommon
.writeshort:
        mov     ecx, [esp+8]
        push    ecx
        mov     edi, buffer
        mov     ebx, edi
        rep movsb
.writedircont:
        mov     ecx, buffer+0x200
        sub     ecx, edi
        push    eax
        xor     eax, eax
        rep stosb
        pop     eax
        pop     ecx
.writecommon:
        call    hd_write
        cmp     [hd_error], 0
        jnz     .writeerr
        inc     eax
        sub     dword [esp+8], ecx
        jz      .writedone
        dec     ebp
        jnz     .write_sector
; allocate new cluster
        pop     eax
        mov     ecx, eax
        call    get_free_FAT
        jc      .diskfull
        push    edx
        mov     edx, [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
        sub     esi, ecx
        mov     eax, 11
        jmp     .ret
.writedone:
        pop     eax
.done:
        xor     eax, eax
.ret:
        pop     edi ecx
        mov     ebx, esi
        sub     ebx, edx
        pop     ebp
        mov     [esp+32+28], eax
        lea     eax, [esp+8]
        call    dword [eax+8]
        mov     [edi+28], ebx
        call    dword [eax+16]
        mov     [esp+32+16], ebx
        lea     eax, [ebx+511]
        shr     eax, 9
        mov     ecx, [SECTORS_PER_CLUSTER]
        lea     eax, [eax+ecx-1]
        xor     edx, edx
        div     ecx
        mov     ecx, ebp
        sub     ecx, eax
        call    add_disk_free_space
        add     esp, 32
        call    update_disk
        popad
        ret
.writedir:
        push    512
        mov     edi, buffer
        mov     ebx, edi
        mov     ecx, [SECTORS_PER_CLUSTER]
        shl     ecx, 9
        cmp     ecx, [esp+12]
        jnz     .writedircont
        dec     dword [esp+16]
        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+8]
        cmp     ecx, [ROOT_CLUSTER]
        jnz     @f
        xor     ecx, ecx
@@:
        mov     word [edi-32+26], cx
        shr     ecx, 16
        mov     [edi-32+20], cx
        jmp     .writedircont

;----------------------------------------------------------------
;
;  fs_HdWrite - LFN variant for writing to hard disk
;
;  esi  points to filename
;  ebx  pointer to 64-bit number = first wanted byte, 0+
;       may be ebx=0 - start from first byte
;  ecx  number of bytes to write, 0+
;  edx  mem location to data
;
;  ret ebx = bytes written (maybe 0)
;      eax = 0 ok write or other = errormsg
;
;--------------------------------------------------------------
fs_HdWrite.access_denied:
        push    ERROR_ACCESS_DENIED
fs_HdWrite.ret0:
        pop     eax
        xor     ebx, ebx
        ret

fs_HdWrite.ret11:
        push    11
        jmp     fs_HdWrite.ret0

fs_HdWrite:
        cmp     [fs_type], 1
        jz      ntfs_HdWrite
        cmp     [fs_type], 2
        jz      ext2_HdWrite
        cmp     [fs_type], 16
        jz      @f
        cmp     [fs_type], 32
        jz      @f
        push    ERROR_UNKNOWN_FS
        jmp     .ret0
@@:
        cmp     byte [esi], 0
        jz      .access_denied
        pushad
        call    hd_find_lfn
        pushfd
        cmp     [hd_error], 0
        jz      @f
        popfd
        popad
        push    11
        jmp     .ret0
@@:
        popfd
        jnc     .found
        popad
        push    ERROR_FILE_NOT_FOUND
        jmp     .ret0
.found:
; FAT does not support files larger than 4GB
        test    ebx, ebx
        jz      .l1
        cmp     dword [ebx+4], 0
        jz      @f
.eof:
        popad
        push    ERROR_END_OF_FILE
        jmp     .ret0
@@:
        mov     ebx, [ebx]
.l1:
; 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    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
        pop     eax
        pop     eax
        mov     [esp+4+28], eax
        pop     eax
        popad
        xor     ebx, ebx
        ret
.disk_full:
; correct number of bytes to write
        mov     ecx, [edi+28]
        cmp     ecx, ebx
        ja      .length_ok
.ret:
        call    update_disk
        cmp     [hd_error], 0
        jz      @f
        mov     byte [esp+4], 11
@@:
        pop     eax
        pop     eax
        mov     [esp+4+28], eax ; eax=return value
        pop     eax
        sub     edx, [esp+20]
        mov     [esp+16], edx   ; ebx=number of written bytes
        popad
        ret
.length_ok:
        mov     esi, [edi+28]
        mov     eax, [edi+20-2]
        mov     ax, [edi+26]
        mov     edi, eax        ; edi=current cluster
        xor     ebp, ebp        ; ebp=current sector in cluster
; save directory
        mov     eax, [esp+8]
        push    ebx
        mov     ebx, buffer
        call    hd_write
        pop     ebx
        cmp     [hd_error], 0
        jz      @f
.device_err:
        mov     byte [esp+4], 11
        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], 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, [SECTORS_PER_CLUSTER]
        add     eax, [DATA_START]
        add     eax, ebp
; load sector if needed
        cmp     dword [esp+4], 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    ebx
        mov     ebx, buffer
        call    hd_read
        pop     ebx
        cmp     [hd_error], 0
        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+4+12]
        jbe     @f
        mov     edi, buffer
        add     edi, [esp+4+12]
        rep stosb
@@:
; zero uninitialized data in the last sector
        mov     ecx, 0x200
        sub     ecx, esi
        jbe     @f
        mov     edi, buffer
        add     edi, esi
        rep stosb
@@:
        pop     edi ecx
; copy new data
        mov     eax, edx
        neg     ebx
        jecxz   @f
        add     ebx, buffer+0x200
        call    memmove
        xor     ebx, ebx
@@:
        pop     eax
; save sector
        push    ebx
        mov     ebx, buffer
        call    hd_write
        pop     ebx
        cmp     [hd_error], 0
        jnz     .device_err2
        add     edx, ecx
        sub     [esp], ecx
        pop     ecx
        jz      .ret
.skip:
; next sector
        inc     ebp
        cmp     ebp, [SECTORS_PER_CLUSTER]
        jb      @f
        xor     ebp, ebp
        mov     eax, edi
        call    get_FAT
        mov     edi, eax
        cmp     [hd_error], 0
        jnz     .device_err
@@:
        sub     esi, 0x200
        jae     @f
        xor     esi, esi
@@:
        sub     dword [esp], 0x200
        jae     @f
        and     dword [esp], 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 11)
hd_extend_file:
        push    ebp
        mov     ebp, [SECTORS_PER_CLUSTER]
        imul    ebp, [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, ebp
        jbe     .last_found
        call    get_FAT
        cmp     [hd_error], 0
        jz      @f
.device_err:
        pop     ecx
.device_err2:
        pop     ebp
        push    11
.ret_err:
        pop     eax
        stc
        ret
@@:
        cmp     eax, 2
        jb      .fat_err
        cmp     eax, [fatRESERVED]
        jb      .last_loop
.fat_err:
        pop     ecx ebp
        push    ERROR_FAT_TABLE
        jmp     .ret_err
.last_found:
        push    eax
        call    get_FAT
        cmp     [hd_error], 0
        jz      @f
        pop     eax
        jmp     .device_err
@@:
        cmp     eax, [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, [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
        cmp     [hd_error], 0
        jnz     .device_err3
        add     [edi+28], ebp
        jmp     .extend_loop
.extend_done:
        mov     [edi+28], ecx
        pop     edx ebp
        xor     eax, eax        ; CF=0
        ret
.device_err3:
        pop     edx
        jmp     .device_err2
.disk_full:
        pop     eax edx ebp
        push    ERROR_DISK_FULL
        pop     eax
        cmp     [hd_error], 0
        jz      @f
        mov     al, 11
@@:
        stc
        ret

;----------------------------------------------------------------
;
;  fs_HdSetFileEnd - set end of file on hard disk
;
;  esi  points to filename
;  ebx  points to 64-bit number = new file size
;  ecx  ignored (reserved)
;  edx  ignored (reserved)
;
;  ret eax = 0 ok or other = errormsg
;
;--------------------------------------------------------------
fs_HdSetFileEnd:
        cmp     [fs_type], 1
        jz      ntfs_HdSetFileEnd
        cmp     [fs_type], 2
        jz      ext2_HdSetFileEnd
        cmp     [fs_type], 16
        jz      @f
        cmp     [fs_type], 32
        jz      @f
        push    ERROR_UNKNOWN_FS
.ret:
        pop     eax
        ret
@@:
        cmp     byte [esi], 0
        jnz     @f
.access_denied:
        push    ERROR_ACCESS_DENIED
        jmp     .ret
@@:
        push    edi
        call    hd_find_lfn
        pushfd
        cmp     [hd_error], 0
        jz      @f
        popfd
        push    11
        jmp     .ret
@@:
        popfd
        jnc     @f
        pop     edi
        push    ERROR_FILE_NOT_FOUND
        jmp     .ret
@@:
; must not be directory
        test    byte [edi+11], 10h
        jz      @f
        pop     edi
        jmp     .access_denied
@@:
; file size must not exceed 4 Gb
        cmp     dword [ebx+4], 0
        jz      @f
        pop     edi
        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]
        cmp     eax, [edi+28]
        jb      .truncate
        ja      .expand
        pop     eax
        mov     ebx, buffer
        call    hd_write
        pop     edi
        xor     eax, eax
        cmp     [hd_error], 0
        jz      @f
        mov     al, 11
@@:
        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 ebp ebx ecx edi edi
        ret
.expand_ok:
.disk_full:
; save directory
        mov     eax, [edi+28]
        xchg    eax, [esp+20]
        mov     ebx, buffer
        call    hd_write
        mov     eax, [edi+20-2]
        mov     ax, [edi+26]
        mov     edi, eax
        cmp     [hd_error], 0
        jz      @f
.pop_ret11:
        mov     byte [esp], 11
        jmp     .pop_ret
@@:
; now zero new data
        xor     ebp, ebp
; edi=current cluster, ebp=sector in cluster
; [esp+20]=new size, [esp+4]=old size, [esp]=return code
.zero_loop:
        sub     dword [esp+4], 0x200
        jae     .next_cluster
        lea     eax, [edi-2]
        imul    eax, [SECTORS_PER_CLUSTER]
        add     eax, [DATA_START]
        add     eax, ebp
        cmp     dword [esp+4], -0x200
        jz      .noread
        mov     ebx, buffer
        call    hd_read
        cmp     [hd_error], 0
        jnz     .err_next
.noread:
        mov     ecx, [esp+4]
        neg     ecx
        push    edi
        mov     edi, buffer+0x200
        add     edi, [esp+8]
        push    eax
        xor     eax, eax
        mov     [esp+12], eax
        rep stosb
        pop     eax
        pop     edi
        call    hd_write
        cmp     [hd_error], 0
        jz      .next_cluster
.err_next:
        mov     byte [esp], 11
.next_cluster:
        sub     dword [esp+20], 0x200
        jbe     .pop_ret
        inc     ebp
        cmp     ebp, [SECTORS_PER_CLUSTER]
        jb      .zero_loop
        xor     ebp, ebp
        mov     eax, edi
        call    get_FAT
        mov     edi, eax
        cmp     [hd_error], 0
        jnz     .pop_ret11
        jmp     .zero_loop
.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
@@:
        mov     eax, [SECTORS_PER_CLUSTER]
        shl     eax, 9
        sub     [esp], eax
        jbe     @f
        mov     eax, ecx
        call    get_FAT
        mov     ecx, eax
        cmp     [hd_error], 0
        jz      @b
.device_err3:
        pop     eax ecx eax edi
        push    11
        pop     eax
        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, [fatEND]
        call    set_FAT
        mov     eax, edx
        pop     edx
        cmp     [hd_error], 0
        jz      @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
        cmp     [hd_error], 0
        jnz     .device_err4
; save directory
        mov     eax, [esp+12]
        push    ebx
        mov     ebx, buffer
        call    hd_write
        pop     ebx
        cmp     [hd_error], 0
        jnz     .device_err4
; zero last sector, ignore errors
        pop     ecx
        pop     eax
        dec     ecx
        imul    ecx, [SECTORS_PER_CLUSTER]
        add     ecx, [DATA_START]
        push    eax
        sar     eax, 9
        add     ecx, eax
        pop     eax
        and     eax, 0x1FF
        jz      .truncate_done
        push    ebx eax
        mov     eax, ecx
        mov     ebx, buffer
        call    hd_read
        pop     eax
        lea     edi, [buffer+eax]
        push    ecx
        mov     ecx, 0x200
        sub     ecx, eax
        xor     eax, eax
        rep stosb
        pop     eax
        call    hd_write
        pop     ebx
.truncate_done:
        pop     ecx eax edi
        call    update_disk
        xor     eax, eax
        cmp     [hd_error], 0
        jz      @f
        mov     al, 11
@@:
        ret

fs_HdGetFileInfo:
        cmp     [fs_type], 1
        jz      ntfs_HdGetFileInfo
        cmp     [fs_type], 2
        jz      ext2_HdGetFileInfo
        cmp     [fs_type], 16
        jz      @f
        cmp     [fs_type], 32
        jz      @f
        mov     eax, ERROR_UNKNOWN_FS
        ret
@@:
        cmp     byte [esi], 0
        jnz     @f
        mov     eax, 2
        ret
@@:
        push    edi
        call    hd_find_lfn
        pushfd
        cmp     [hd_error], 0
        jz      @f
        popfd
        pop     edi
        mov     eax, 11
        ret
@@:
        popfd
        jmp     fs_GetFileInfo_finish

fs_HdSetFileInfo:
        cmp     [fs_type], 1
        jz      ntfs_HdSetFileInfo
        cmp     [fs_type], 2
        jz      ext2_HdSetFileInfo
        cmp     [fs_type], 16
        jz      @f
        cmp     [fs_type], 32
        jz      @f
        mov     eax, ERROR_UNKNOWN_FS
        ret
@@:
        cmp     byte [esi], 0
        jnz     @f
        mov     eax, 2
        ret
@@:
        push    edi
        call    hd_find_lfn
        pushfd
        cmp     [hd_error], 0
        jz      @f
        popfd
        pop     edi
        mov     eax, 11
        ret
@@:
        popfd
        jnc     @f
        pop     edi
        mov     eax, ERROR_FILE_NOT_FOUND
        ret
@@:
        push    eax
        call    bdfe_to_fat_entry
        pop     eax
        mov     ebx, buffer
        call    hd_write
        call    update_disk
        pop     edi
        xor     eax, eax
        ret

;----------------------------------------------------------------
;
;  fs_HdDelete - delete file or empty folder from hard disk
;
;  esi  points to filename
;
;  ret  eax = 0 ok or other = errormsg
;
;--------------------------------------------------------------
fs_HdDelete:
        cmp     [fs_type], 1
        jz      ntfs_HdDelete
        cmp     [fs_type], 2
        jz      ext2_HdDelete
        cmp     [fs_type], 16
        jz      @f
        cmp     [fs_type], 32
        jz      @f
        push    ERROR_UNKNOWN_FS
.pop_ret:
        pop     eax
        ret
@@:
        cmp     byte [esi], 0
        jnz     @f
; cannot delete root!
.access_denied:
        push    ERROR_ACCESS_DENIED
        jmp     .pop_ret
@@:
        and     [longname_sec1], 0
        and     [longname_sec2], 0
        push    edi
        call    hd_find_lfn
        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     ebp, [edi+20-2]
        mov     bp, [edi+26]
        xor     ecx, ecx
        lea     eax, [ebp-2]
        imul    eax, [SECTORS_PER_CLUSTER]
        add     eax, [DATA_START]
        mov     ebx, buffer
        call    hd_read
        cmp     [hd_error], 0
        jnz     .err1
        add     ebx, 2*0x20
.checkempty:
        cmp     byte [ebx], 0
        jz      .empty
        cmp     byte [ebx], 0xE5
        jnz     .notempty
        add     ebx, 0x20
        cmp     ebx, buffer+0x200
        jb      .checkempty
        inc     ecx
        cmp     ecx, [SECTORS_PER_CLUSTER]
        jb      @f
        mov     eax, ebp
        call    get_FAT
        cmp     [hd_error], 0
        jnz     .err1
        mov     ebp, eax
        xor     ecx, ecx
@@:
        lea     eax, [ebp-2]
        imul    eax, [SECTORS_PER_CLUSTER]
        add     eax, [DATA_START]
        add     eax, ecx
        mov     ebx, buffer
        call    hd_read
        cmp     [hd_error], 0
        jz      .checkempty
.err1:
        popad
.err2:
        pop     edi
        push    11
        pop     eax
        ret
.notempty:
        popad
.access_denied2:
        pop     edi
        push    ERROR_ACCESS_DENIED
        pop     eax
        ret
.empty:
        popad
        push    ebx
        mov     ebx, buffer
        call    hd_read
        pop     ebx
        cmp     [hd_error], 0
        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:
        cmp     edi, buffer
        ja      @f
        cmp     [longname_sec2], 0
        jz      .lfndone
        push    [longname_sec2]
        push    [longname_sec1]
        pop     [longname_sec2]
        and     [longname_sec1], 0
        push    ebx
        mov     ebx, buffer
        call    hd_write
        mov     eax, [esp+4]
        call    hd_read
        pop     ebx
        pop     eax
        mov     edi, 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
        mov     ebx, buffer
        call    hd_write
        pop     ebx
; delete FAT chain
        pop     eax
        call    clear_cluster_chain
        call    update_disk
        pop     edi
        xor     eax, eax
        cmp     [hd_error], 0
        jz      @f
        mov     al, 11
@@:
        ret

; \end{diamond}