;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;                                                              ;;
;; System service for filesystem call                           ;;
;; (C) 2004 Ville Turjanmaa, License: GPL                       ;;
;; 29.04.2006 Elimination of hangup after the                   ;;
;;            expiration hd_wait_timeout (for LBA) -  Mario79   ;;
;; 15.01.2005 get file size/attr/date,                          ;;
;;            file_append (only for hd) - ATV                   ;;
;; 23.11.2004 test if hd/partition is set - ATV                 ;;
;; 18.11.2004 get_disk_info and more error codes - ATV          ;;
;; 08.11.2004 expand_pathz and rename (only for hd) - ATV       ;;
;; 20.10.2004 Makedir/Removedir (only for hd) - ATV             ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$


iglobal
dir0:        db  'HARDDISK   '
             db  'RAMDISK    '
             db  'FLOPPYDISK '
             db  0

dir1:        db  'FIRST      '
             db  'SECOND     '
             db  'THIRD      '
             db  'FOURTH     '
             db  0

not_select_IDE db 0

hd_address_table:  dd  0x1f0,0x00,0x1f0,0x10
                   dd  0x170,0x00,0x170,0x10
endg

file_system:

; IN:
;
; eax = 0  ; read file          /RamDisk/First  6
; eax = 8  ; lba read
; eax = 15 ; get_disk_info
;
; OUT:
;
; eax = 0  : read ok
; eax = 1  : no hd base and/or partition defined
; eax = 2  : function is unsupported for this FS
; eax = 3  : unknown FS
; eax = 4  : partition not defined at hd
; eax = 5  : file not found
; eax = 6  : end of file
; eax = 7  : memory pointer not in application area
; eax = 8  : disk full
; eax = 9  : fat table corrupted
; eax = 10 : access denied
; eax = 11 : disk error
;
; ebx = size

; \begin{diamond}[18.03.2006]
; for subfunction 16 (start application) error codes must be negative
;    because positive values are valid PIDs
; so possible return values are:
; eax > 0 : process created, eax=PID

; -0x10 <= eax < 0 : -eax is filesystem error code:
; eax = -1  = 0xFFFFFFFF : no hd base and/or partition defined
; eax = -3  = 0xFFFFFFFD : unknown FS
; eax = -5  = 0xFFFFFFFB : file not found
; eax = -6  = 0xFFFFFFFA : unexpected end of file (probably not executable file)
; eax = -9  = 0xFFFFFFF7 : fat table corrupted
; eax = -10 = 0xFFFFFFF6 : access denied

; -0x20 <= eax < -0x10: eax is process creation error code:
; eax = -0x20 = 0xFFFFFFE0 : too many processes
; eax = -0x1F = 0xFFFFFFE1 : not Menuet/Kolibri executable
; eax = -0x1E = 0xFFFFFFE2 : no memory

; ebx is not changed

; \end{diamond}[18.03.2006]

    ; Extract parameters
 ;   add    eax, std_application_base_address    ; abs start of info block

    cmp   dword [eax+0],15      ; GET_DISK_INFO
    je    fs_info

    cmp   dword [CURRENT_TASK],1      ; no memory checks for kernel requests
    jz    no_checks_for_kernel
    mov   edx,eax
    cmp   dword [eax+0],1
    jnz   .usual_check
    mov   ebx,[eax+12]
 ;   add   ebx,std_application_base_address
    mov   ecx,[eax+8]
    call  check_region
    test  eax,eax
    jnz   area_in_app_mem

.error_output:
    mov   esi,buffer_failed
    call  sys_msg_board_str
;    mov   eax,7
    mov   dword [esp+36],7
    ret
iglobal
  buffer_failed db 'K : Buffer check failed',13,10,0
endg
.usual_check:
    cmp   dword [eax+0],0
    mov   ecx,512
    jnz   .small_size
    mov   ecx,[eax+8]
    shl   ecx,9
.small_size:
    mov   ebx,[eax+12]
 ;   add   ebx,std_application_base_address
    call  check_region
    test  eax,eax
    jz    .error_output
  area_in_app_mem:
    mov   eax,edx
  no_checks_for_kernel:

  fs_read:

    mov   ebx,[eax+20]          ; program wants root directory ?
    test  bl,bl
    je    fs_getroot
    test  bh,bh
    jne   fs_noroot
  fs_getroot:
; \begin{diamond}[18.03.2006]
; root - only read is allowed
; other operations return "access denied", eax=10
; (execute operation returns eax=-10)
    cmp    dword [eax], 0
    jz    .read_root
    mov    dword [esp+36], 10
    ret
.read_root:
; \end{diamond}[18.03.2006]
    mov   esi,dir0
    mov   edi,[eax+12]
 ;   add   edi,std_application_base_address
    mov   ecx,11
    push  ecx
;    cld    ; already is
    rep   movsb
    mov   al,0x10
    stosb
    add   edi,32-11-1
    pop   ecx
    rep   movsb
    stosb
    and   dword [esp+36],0      ; ok read
    mov   dword [esp+24],32*2   ; size of root
    ret

  fs_info:                      ;start of code - Mihasik
    push  eax
    cmp   [eax+21],byte 'h'
    je    fs_info_h
    cmp   [eax+21],byte 'H'
    je    fs_info_h
    cmp   [eax+21],byte 'r'
    je    fs_info_r
    cmp   [eax+21],byte 'R'
    je    fs_info_r
    mov   eax,3                 ;if unknown disk
    xor   ebx,ebx
    xor   ecx,ecx
    xor   edx,edx
    jmp   fs_info1
  fs_info_r:
    call  ramdisk_free_space    ;if ramdisk
    mov   ecx,edi               ;free space in ecx
    shr   ecx,9                 ;free clusters
    mov   ebx,2847              ;total clusters
    mov   edx,512               ;cluster size
    xor   eax,eax               ;always 0
    jmp   fs_info1
  fs_info_h:                    ;if harddisk
    call  get_hd_info
  fs_info1:
    pop   edi
    mov   [esp+36],eax
    mov   [esp+24],ebx           ; total clusters on disk
    mov   [esp+32],ecx           ; free clusters on disk
    mov   [edi],edx              ; cluster size in bytes
    ret                          ;end of code - Mihasik

  fs_noroot:

    push  dword [eax+0]         ; read/write/delete/.../makedir/rename/lba/run
    push  dword [eax+4]         ; 512 block number to read
    push  dword [eax+8]         ; bytes to write/append or 512 blocks to read
    mov   ebx,[eax+12]
 ;   add   ebx,std_application_base_address
    push  ebx                   ; abs start of return/save area

    lea   esi,[eax+20]          ; abs start of dir + filename
    mov   edi,[eax+16]
 ;   add   edi,std_application_base_address    ; abs start of work area

    call  expand_pathz

    push  edi                   ; dir start
    push  ebx                   ; name of file start

    mov   eax,[edi+1]
    cmp   eax,'RD  '
    je    fs_yesramdisk
    cmp   eax,'RAMD'
    jne   fs_noramdisk

  fs_yesramdisk:

    cmp   byte [edi+1+11],0
    je    fs_give_dir1

    mov   eax,[edi+1+12]
    cmp   eax,'1   '
    je    fs_yesramdisk_first
    cmp   eax,'FIRS'
    jne   fs_noramdisk

  fs_yesramdisk_first:

    cmp   dword [esp+20],8      ; LBA read ramdisk
    jne   fs_no_LBA_read_ramdisk

    mov   eax,[esp+16]          ; LBA block to read
    mov   ecx,[esp+8]           ; abs pointer to return area

    call  LBA_read_ramdisk
    jmp   file_system_return


  fs_no_LBA_read_ramdisk:

    cmp   dword [esp+20],0      ; READ
    jne   fs_noramdisk_read

    mov   eax,[esp+4]           ; fname
    add   eax,2*12+1
    mov   ebx,[esp+16]          ; block start
    inc   ebx
    mov   ecx,[esp+12]          ; block count
    mov   edx,[esp+8]           ; return
    mov   esi,[esp+0]
    sub   esi,eax
    add   esi,12+1              ; file name length
    call  fileread

    jmp   file_system_return


  fs_noramdisk_read:
  fs_noramdisk:

  ;********************************************************************
    mov   eax,[edi+1]
    cmp   eax,'FD  '
    je    fs_yesflpdisk
    cmp   eax,'FLOP'
    jne   fs_noflpdisk

  fs_yesflpdisk:
    call   reserve_flp

    cmp   byte [edi+1+11],0
    je    fs_give_dir1

    mov   eax,[edi+1+12]
    cmp   eax,'1   '
    je    fs_yesflpdisk_first
    cmp   eax,'FIRS'
    je    fs_yesflpdisk_first
    cmp   eax,'2   '
    je    fs_yesflpdisk_second
    cmp   eax,'SECO'
    jne   fs_noflpdisk
    jmp   fs_yesflpdisk_second

  fs_yesflpdisk_first:
    mov   [flp_number],1
    jmp   fs_yesflpdisk_start
  fs_yesflpdisk_second:
    mov   [flp_number],2
  fs_yesflpdisk_start:
    cmp   dword [esp+20],0      ; READ
    jne   fs_noflpdisk_read

    mov   eax,[esp+4]           ; fname
    add   eax,2*12+1
    mov   ebx,[esp+16]          ; block start
    inc   ebx
    mov   ecx,[esp+12]          ; block count
    mov   edx,[esp+8]           ; return
    mov   esi,[esp+0]
    sub   esi,eax
    add   esi,12+1              ; file name length
    call  floppy_fileread

    jmp   file_system_return


  fs_noflpdisk_read:
  fs_noflpdisk:
  ;*****************************************************************

    mov   eax,[edi+1]
    cmp   eax,'HD0 '
    je    fs_yesharddisk_IDE0
    cmp   eax,'HD1 '
    je    fs_yesharddisk_IDE1
    cmp   eax,'HD2 '
    je    fs_yesharddisk_IDE2
    cmp   eax,'HD3 '
    je    fs_yesharddisk_IDE3
    jmp   old_path_harddisk
fs_yesharddisk_IDE0:
     call  reserve_hd1
     mov  [hdbase],0x1f0
     mov  [hdid],0x0
     mov  [hdpos],1
     jmp  fs_yesharddisk_partition
fs_yesharddisk_IDE1:
     call  reserve_hd1
     mov  [hdbase],0x1f0
     mov  [hdid],0x10
     mov  [hdpos],2
     jmp  fs_yesharddisk_partition
fs_yesharddisk_IDE2:
     call  reserve_hd1
     mov  [hdbase],0x170
     mov  [hdid],0x0
     mov  [hdpos],3
     jmp  fs_yesharddisk_partition
fs_yesharddisk_IDE3:
     call  reserve_hd1
     mov  [hdbase],0x170
     mov  [hdid],0x10
     mov  [hdpos],4
fs_yesharddisk_partition:
        call    reserve_hd_channel
;    call  choice_necessity_partition
;    jmp   fs_yesharddisk_all
    jmp   fs_for_new_semantic

choice_necessity_partition:
    mov   eax,[edi+1+12]
    call  StringToNumber
        mov   [fat32part],eax
choice_necessity_partition_1:
    mov   ecx,[hdpos]
    xor   eax,eax
    mov   [hd_entries], eax    ; entries in hd cache
    mov   edx,DRIVE_DATA+2
    cmp   ecx,0x80
    jb    search_partition_array
    mov   ecx,4
 search_partition_array:
    mov   bl,[edx]
    movzx ebx,bl
    add   eax,ebx
    inc   edx
    loop  search_partition_array
    mov   ecx,[hdpos]
    mov   edx,BiosDiskPartitions
    sub   ecx,0x80
    jb    .s
    je    .f
 @@:
    mov   ebx,[edx]
    add   edx,4
    add   eax,ebx
    loop  @b
    jmp   .f
 .s:
    sub   eax,ebx
 .f:
    add   eax,[fat32part]
    dec   eax
    xor   edx,edx
    imul  eax,100
    add   eax,DRIVE_DATA+0xa
    mov   [transfer_adress],eax
    call  partition_data_transfer_1
    ret

 old_path_harddisk:
    mov   eax,[edi+1]
    cmp   eax,'HD  '
    je    fs_yesharddisk
    cmp   eax,'HARD'
    jne   fs_noharddisk

  fs_yesharddisk:
    cmp   dword [esp+20],8      ; LBA read
    jne   fs_no_LBA_read
    mov   eax,[esp+16]          ; LBA block to read
    lea   ebx,[edi+1+12]        ; pointer to FIRST/SECOND/THIRD/FOURTH
    mov   ecx,[esp+8]           ; abs pointer to return area
    call  LBA_read
    jmp   file_system_return

  fs_no_LBA_read:

    cmp   byte [edi+1+11],0     ; directory read
    je    fs_give_dir1
    call  reserve_hd1
 fs_for_new_semantic:
    call  choice_necessity_partition

  fs_yesharddisk_all:
    mov   eax,1
    mov    ebx, [esp+24+24]
    cmp   [hdpos],0             ; is hd base set?
    jz    hd_err_return
    cmp   [fat32part],0         ; is partition set?
    jnz   @f
hd_err_return:
    call  free_hd_channel
    and   [hd1_status], 0
    jmp   file_system_return
@@:

    cmp   dword [esp+20],0      ; READ
    jne   fs_noharddisk_read

    mov   eax,[esp+0]           ; /fname
    lea   edi,[eax+12]
    mov   byte [eax],0          ; path to asciiz
    inc   eax                   ; filename start

    mov   ebx,[esp+12]          ; count to read
    mov   ecx,[esp+8]           ; buffer
    mov   edx,[esp+4]
    add   edx,12*2              ; dir start
    sub   edi,edx               ; path length
    mov   esi,[esp+16]          ; blocks to read

    call  file_read

    mov   edi,[esp+0]
    mov   byte [edi],'/'

    call  free_hd_channel
    and   [hd1_status], 0
    jmp   file_system_return

  fs_noharddisk_read:

    call  free_hd_channel
    and   [hd1_status], 0

  fs_noharddisk:
; \begin{diamond}[18.03.2006]
    mov    eax, 5        ; file not found
; � ����� ����, ���������� ������ ��� ������?
    mov    ebx, [esp+24+24]    ; do not change ebx in application
; \end{diamond}[18.03.2006]

  file_system_return:

    add   esp,24

    mov   [esp+36],eax
    mov   [esp+24],ebx
    ret


  fs_give_dir1:

; \begin{diamond}[18.03.2006]
; /RD,/FD,/HD - only read is allowed
; other operations return "access denied", eax=10
; (execute operation returns eax=-10)
    cmp    dword [esp+20], 0
    jz    .read
    add    esp, 20
    pop    ecx
    mov    dword [esp+36], 10
    ret
.read:
; \end{diamond}[18.03.2006]
    mov   al,0x10
    mov   ebx,1
    mov   edi,[esp+8]
    mov   esi,dir1
  fs_d1_new:
    mov   ecx,11
;    cld
    rep   movsb
    stosb
    add   edi,32-11-1
    dec   ebx
    jne   fs_d1_new

    add   esp,24

    and   dword [esp+36],0      ; ok read
    mov   dword [esp+24],32*1   ; dir/data size
    ret



LBA_read_ramdisk:

    cmp   [lba_read_enabled],1
    je    lbarrl1

    xor   ebx,ebx
    mov   eax,2
    ret

  lbarrl1:

    cmp   eax,18*2*80
    jb    lbarrl2
    xor   ebx,ebx
    mov   eax,3
    ret

  lbarrl2:

    pushad

    call  restorefatchain

    mov   edi,ecx
    mov   esi,eax

    shl   esi,9
    add   esi,RAMDISK
    mov   ecx,512/4
;    cld
    rep   movsd

    popad

    xor   ebx,ebx
    xor   eax,eax
    ret

LBA_read:

; IN:
;
; eax = LBA block to read
; ebx = pointer to FIRST/SECOND/THIRD/FOURTH
; ecx = abs pointer to return area

    cmp   [lba_read_enabled],1
    je    lbarl1
    mov   eax,2
    ret

  lbarl1:

    call  reserve_hd1

    push  eax
    push  ecx

    mov   edi,hd_address_table
    mov   esi,dir1
    mov   eax,[ebx]
    mov   edx,'1   '
    mov   ecx,4
  blar0:
    cmp   eax,[esi]
    je    blar2
    cmp   eax,edx
    je    blar2
    inc   edx
    add   edi,8
    add   esi,11
    dec   ecx
    jnz   blar0

    mov   eax,1
    mov   ebx,1
    jmp   LBA_read_ret

  blar2:
    mov   eax,[edi+0]
    mov   ebx,[edi+4]

    mov  [hdbase],eax
    mov  [hdid],ebx

    call  wait_for_hd_idle
    cmp   [hd_error],0
    jne   hd_lba_error

    ; eax = hd port
    ; ebx = set for primary (0x00) or slave (0x10)

    cli

    mov   edx,eax
    inc   edx
    xor   eax,eax
    out   dx,al
    inc   edx
    inc   eax
    out   dx,al
    inc   edx
    mov   eax,[esp+4]
    out   dx,al
    shr   eax,8
    inc   edx
    out   dx,al
    shr   eax,8
    inc   edx
    out   dx,al
    shr   eax,8
    inc   edx
    and   al,1+2+4+8
    add   al,bl
    add   al,128+64+32
    out   dx,al

    inc   edx
    mov   al,20h
    out   dx,al

    sti

    call  wait_for_sector_buffer
    cmp   [hd_error],0
    jne   hd_lba_error

    cli

    mov   edi,[esp+0]
    mov   ecx,256
    sub   edx,7
    cld
    rep   insw

    sti

    xor   eax,eax
    xor   ebx,ebx

  LBA_read_ret:
    mov [hd_error],0
    mov   [hd1_status],0
    add   esp,2*4

    ret


expand_pathz:
; IN:
;   esi = asciiz path & file
;   edi = buffer for path & file name
; OUT:
;   edi = directory & file : / 11 + / 11 + / 11 - zero terminated
;   ebx = /file name - zero terminated
;   esi = pointer after source

    push  eax
    push  ecx
    push  edi ;[esp+0]

  pathz_start:
    mov   byte [edi],'/'
    inc   edi
    mov   al,32
    mov   ecx,11
    cld
    rep   stosb                 ; clear filename area
    sub   edi,11
    mov   ebx,edi               ; start of dir/file name

  pathz_new_char:
    mov   al,[esi]
    inc   esi
    cmp   al,0
    je    pathz_end

    cmp   al,'/'
    jne   pathz_not_path
    cmp   edi,ebx               ; skip first '/'
    jz    pathz_new_char
    lea   edi,[ebx+11]          ; start of next directory
    jmp   pathz_start

  pathz_not_path:
    cmp   al,'.'
    jne   pathz_not_ext
    lea   edi,[ebx+8]           ; start of extension
    jmp   pathz_new_char

  pathz_not_ext:
    cmp   al,'a'
    jb    pathz_not_low
    cmp   al,'z'
    ja    pathz_not_low
    sub   al,0x20               ; char to uppercase

  pathz_not_low:
    mov   [edi],al
    inc   edi
    mov   eax,[esp+0]           ; start_of_dest_path
    add   eax,512               ; keep maximum path under 512 bytes
    cmp   edi,eax
    jb    pathz_new_char

  pathz_end:
    cmp   ebx,edi               ; if path end with '/'
    jnz   pathz_put_zero        ; go back 1 level
    sub   ebx,12

  pathz_put_zero:
    mov   byte [ebx+11],0
    dec   ebx                   ; include '/' char into file name
    pop   edi
    pop   ecx
    pop   eax
    ret

;*******************************************
;* string to number
;* input eax - 4 byte string
;* output eax - number
;*******************************************
StringToNumber:
;    ������� ���������� ����� � �������� ���
;    ����:
;        EDI - ����� ������ � ������. ����� ����� ������� ����� 0Dh
;    �����:
;        CF - ��������� ������:
;            0 - ������ ���;
;            1 - ������
;        ���� CF=0, �� AX - �����.

    push    bx
    push    cx
    push    dx
    push    edi
    mov   [partition_string],eax
    mov    edi,partition_string
    xor    cx,cx
i1:
    mov    al,[edi]
    cmp    al,32  ;13
    je    i_exit
;    cmp    al,'0'
;    jb    err
;    cmp    al,'9'
;    ja    err
    sub    al,48
    shl    cx,1
    jc    error
    mov    bx,cx
    shl    cx,1
    jc    error
    shl    cx,1
    jc    error
    add    cx,bx
    jc    error
    cbw
    add    cx,ax
    jc    error
i3:
    inc    edi
    jmp    i1
i_exit:
    mov    ax,cx
    clc
i4:
    movzx  eax,ax
    pop    edi
    pop    dx
    pop    cx
    pop    bx
    ret

error:
    stc
    jmp    i4

partition_string: dd 0
                  db 32