source: soft/giet_vm/giet_fat32/fat32.c @ 742

Last change on this file since 742 was 730, checked in by alain, 9 years ago

Improve seek overflow checking.

  • Property svn:executable set to *
File size: 168.5 KB
RevLine 
[258]1//////////////////////////////////////////////////////////////////////////////////
[587]2// Date     : 01/06/2015
3// Authors  : Alain Greiner
[258]4// Copyright (c) UPMC-LIP6
5//////////////////////////////////////////////////////////////////////////////////
[569]6// The fat32.h and fat32.c files define a library of access functions
[587]7// to a FAT32 disk on a block device. It is intended to be used by both
8// the boot code and the kernel code.
[258]9//////////////////////////////////////////////////////////////////////////////////
10// Implementation notes:
11// 1. the "lba" (Logical Block Address) is the physical sector index on
12//    the block device. The physical sector size is supposed to be 512 bytes.
13// 2. the "cluster" variable is actually a cluster index. A cluster contains
[587]14//    8 sectors (4K bytes) and the cluster index is a 32 bits word.
15// 3. Each file or directory referenced by the software is represented
16//    by an "inode". The set of "inodes" is organised as a tree, that is
17//    a sub-tree of the complete file system existing on the block device.
18// 4. A given file can be referenced by several software tasks, and each task
19//    will use a private handler, called a "file descriptor", allocated by the OS
20//    when the task open the file, that is organised as an indexed array.
21// 5. This FAT32 library implements (N+1) caches : one private "File_ Cache"
22//    for each referenced file or directory, and a specific "Fat_Cache" for
23//    the FAT itself. Each cache contain a variable number of clusters that are
24//    dynamically allocated when they are accessed, and organised as a 64-Tree.
[258]25//////////////////////////////////////////////////////////////////////////////////
[587]26// General Debug Policy:
27// The global variable GIET_DEBUG_FAT is defined in the giet_config.h file.
28// The debug is activated if (proctime > GIET_DEBUG_FAT) && (GIET_DEBUG_FAT != 0)
29// The GIET_DEBUG_FAT bit 0 defines the level of debug:
30//    if   (GIET_DEBUG_FAT & 0x1)    => detailed debug
31//    else                           => external functions only
32//////////////////////////////////////////////////////////////////////////////////
[258]33
34#include <giet_config.h>
[530]35#include <hard_config.h>
[258]36#include <fat32.h>
[530]37#include <utils.h>
38#include <vmem.h>
[587]39#include <kernel_malloc.h>
[709]40#include <ctx_handler.h>
[530]41#include <bdv_driver.h>
42#include <hba_driver.h>
43#include <sdc_driver.h>
44#include <rdk_driver.h>
45#include <mmc_driver.h>
[458]46#include <tty0.h>
[258]47
48//////////////////////////////////////////////////////////////////////////////////
[587]49//               Global variables
[258]50//////////////////////////////////////////////////////////////////////////////////
51
[587]52// Fat-Descriptor
53__attribute__((section(".kdata")))
54fat_desc_t _fat __attribute__((aligned(64))); 
[258]55
[587]56// buffer used by boot code as a simple cache when scanning FAT
57__attribute__((section(".kdata")))
58unsigned char  _fat_buffer_fat[4096] __attribute__((aligned(64)));
[530]59
[587]60// buffer used by boot code as a simple cache when scanning a directory in DATA region
61__attribute__((section(".kdata")))
62unsigned char  _fat_buffer_data[4096] __attribute__((aligned(64)));
[530]63
[587]64// lba of cluster in fat_buffer_fat
65__attribute__((section(".kdata")))
66unsigned int   _fat_buffer_fat_lba;
67
68// lba of cluster in fat_buffer_data
69__attribute__((section(".kdata")))
70unsigned int   _fat_buffer_data_lba;
71
72//////////////////////////////////////////////////////////////////////////////////
73//////////////////////////////////////////////////////////////////////////////////
74//                  Static functions declaration
75//////////////////////////////////////////////////////////////////////////////////
76//////////////////////////////////////////////////////////////////////////////////
77
78
79///////////////////////////////////////////////////////////////////////////////////
80// This debug function displays the content of a 512 bytes buffer "buf",
81// with an identifier defined by the "string" and "block_id" arguments.
82///////////////////////////////////////////////////////////////////////////////////
83
84#if GIET_DEBUG_FAT
85static void _display_one_block( unsigned char* buf,
86                                char*          string,
87                                unsigned int   block_id );
[530]88#endif
89
[587]90//////////////////////////////////////////////////////////////////////////////////
91// This debug function displays the FAT descriptor.
92//////////////////////////////////////////////////////////////////////////////////
[530]93
[587]94#if GIET_DEBUG_FAT
95static void _display_fat_descriptor();
[551]96#endif
[530]97
[587]98/////////////////////////////////////////////////////////////////////////////////
99// The following function transfers one or several blocks between the device
100// and a memory buffer identified by a virtual address.
101// It computes the memory buffer physical address, and calls the proper
102// IOC driver depending on the subtype (BDV / HBA / SDC / SPI / RDK).
103// The use_irq argument allows to activate the descheduling mode,
104// if it supported by the IOC driver subtype
[674]105// It returns 0 on success.
106// It returns -1 on error.
[587]107/////////////////////////////////////////////////////////////////////////////////
[530]108
[587]109static int _fat_ioc_access( unsigned int use_irq,
110                            unsigned int to_mem,
111                            unsigned int lba,
112                            unsigned int buf_vaddr,
113                            unsigned int count );
[530]114
[258]115//////////////////////////////////////////////////////////////////////////////////
[587]116// The following function returns in the "desc" argument a pointer on a buffer
117// descriptor contained in a File_Cache, or in the Fat_Cache.
118// The searched buffer is idenfified by the "inode" and "cluster_id" arguments.
119// If the "inode" pointer is not NULL, the searched cache is a File-Cache.
120// If the "inode" pointer is NULL, the searched cache is the Fat-Cache,
121// The "cluster_id" argument is the buffer index in the file (or in the FAT).
122// In case of miss, it allocate a 4 Kbytes buffer and a cluster descriptor
123// from the local kernel heap, and calls the _fat_ioc_access() function to load
124// the missing cluster from the block device.
[674]125// It returns 0 on success.
126// It returns 1 on error.
[258]127//////////////////////////////////////////////////////////////////////////////////
[587]128
129static unsigned int _get_buffer_from_cache( fat_inode_t*        inode,
130                                            unsigned int        cluster_id,
131                                            fat_cache_desc_t**  desc );
132
133////////////////////////////////////////////////////////////////////////////////
134// This function extract a (partial) name from a LFN directory entry.
135////////////////////////////////////////////////////////////////////////////////
136
137static void _get_name_from_long( unsigned char* buffer, 
138                                 char*          name );
139
140////////////////////////////////////////////////////////////////////////////////
141// The following function extract a name from a NORMAL directory entry.
142////////////////////////////////////////////////////////////////////////////////
143
144static void _get_name_from_short( unsigned char* buffer,
145                                  char*          name );
146
147//////////////////////////////////////////////////////////////////////////////////
148// This function returns the number of levels of a File-Cache (or Fat-Cache)
149// from the size of the file (or FAT).
150//////////////////////////////////////////////////////////////////////////////////
151
152static inline unsigned int _get_levels_from_size( unsigned int size );
153
154///////////////////////////////////////////////////////////////////////////////////
155// The following function analyses the "pathname" argument, from the character
156// defined by the "nb_read" argument.
157// It copies the found name in the "name" buffer (without '/'),
158// and updates the "nb_read" argument.
[674]159// It returns 0 on success.
[587]160// It returns 1 if one name length > NAME_MAX_SIZE characters
161///////////////////////////////////////////////////////////////////////////////////
162
163static unsigned int _get_name_from_path( char*          pathname,
164                                         char*          name,
165                                         unsigned int*  nb_read );
166
167////////////////////////////////////////////////////////////////////////////////
168// The following function scan the "pathname" argument, and copies in the
169// "name" buffer the last name in path (leaf name).
[674]170// It returns 0 on success.
[587]171// It returns 1 if one name length > NAME_MAX_SIZE characters
172////////////////////////////////////////////////////////////////////////////////
173static unsigned int _get_last_name( char*   pathname,
174                                    char*   name );
175
176//////////////////////////////////////////////////////////////////////////////////
[674]177// The following function accesses the Fat-Cache and returns in the "value"
[587]178// argument the content of the FAT slot identified by the "cluster" argument.
179// It loads the missing cluster from block device into cache in case of miss.
[674]180// It returns 0 on success.
181// It returns 1 on error.
[587]182//////////////////////////////////////////////////////////////////////////////////
183
184static unsigned int _get_fat_entry( unsigned int  cluster,
185                                    unsigned int* value );
186
187//////////////////////////////////////////////////////////////////////////////////
188// The following function writes a new "value" in the Fat-Cache, in the slot
189// identified by the "cluster" argument. 
190// It loads the missing cluster from block device into cache in case of miss.
[674]191// It returns 0 on success,
192// It returns 1 on error.
[587]193//////////////////////////////////////////////////////////////////////////////////
194
195static unsigned int _set_fat_entry( unsigned int  cluster,
196                                    unsigned int  value );
197
198//////////////////////////////////////////////////////////////////////////////////
199// The following function introduces the inode identified by the "child" argument,
200// as a new child of the "parent" inode in the Inode-Tree.
201// All checking are supposed to be done by the caller.
202// Nor the File-Cache, neither the block device are modified.
203//////////////////////////////////////////////////////////////////////////////////
204
205static void _add_inode_in_tree( fat_inode_t*  child,
206                                fat_inode_t*  parent );
207
208//////////////////////////////////////////////////////////////////////////////////
209// The following function removes one inode identified by the "inode" argument
210// from the Inode-Tree. All checking are supposed to be done by the caller.
211// Nor the File-Cache, neither the block device are modified.
212//////////////////////////////////////////////////////////////////////////////////
213
214static void _remove_inode_from_tree( fat_inode_t* inode );
215
216//////////////////////////////////////////////////////////////////////////////////
217// This recursive function scan one File-Cache (or Fat-Cache) from root to leaves,
218// to writes all dirty clusters to block device, and reset the dirty bits.
219// The cache is identified by the "root" an "levels" arguments.
220// The "string" argument is only used for debug : inode name or Fat.
[674]221// It returns 0 on success.
222// It returns 1 on error.
[587]223//////////////////////////////////////////////////////////////////////////////////
224
225static unsigned int _update_device_from_cache( unsigned int      levels,
226                                               fat_cache_node_t* root,
227                                               char*             string );
228
229//////////////////////////////////////////////////////////////////////////////////
[674]230// The following function accesses directly the FS_INFO block on the block device,
[587]231// to update the "first_free_cluster" and "free_clusters_number" values,
232// using only the Fat-Descriptor single block buffer.
[674]233// It return 0 on success.
234// It return 1 on error.
[587]235//////////////////////////////////////////////////////////////////////////////////
236
237static unsigned int _update_fs_info();
238
239//////////////////////////////////////////////////////////////////////////////
240// The following function read a data field (from one to four bytes)
241// from an unsigned char[] buffer, taking endianness into account.
242// The analysed field is defined by the "offset" and "size" arguments.
243//////////////////////////////////////////////////////////////////////////////
244
245static unsigned int _read_entry( unsigned int    offset,
246                                 unsigned int    size,
247                                 unsigned char*  buffer,
248                                 unsigned int    little_indian );
249
250//////////////////////////////////////////////////////////////////////////////////
251// The following function returns the lba of first sector in DATA region
252// from the cluster index. The cluster index must be larger than 2.
253//////////////////////////////////////////////////////////////////////////////////
254
255static unsigned int _cluster_to_lba( unsigned int cluster );
256
257//////////////////////////////////////////////////////////////////////////////////
258// The following function returns in the "nb_entries" argument the number of files
[620]259// (or directories) contained in a directory identified by the "inode " pointer.
[674]260// It returns 0 on success.
261// It returns 1 on error.
[587]262//////////////////////////////////////////////////////////////////////////////////
263
264static unsigned int _get_nb_entries( fat_inode_t*   inode,
265                                     unsigned int*  nb_entries );
266
267//////////////////////////////////////////////////////////////////////////////////
268// The following function search in the directory identified by the "parent"
269// inode pointer a child (file or directory) identified by its "name".
270// It returns in the "inode" argument the searched child inode pointer.
[674]271// If the searched name is not found in the Inode-Tree, the function accesses
[587]272// the "file_cache" associated to the parent directory.
[674]273// If the child exists on block device, the Inode-Tree is updated, and
[587]274// a success code is returned.
275// If the file/dir does not exist on block device, a error code is returned.
276// It returns 0 if inode found.
277// It returns 1 if inode not found.
[674]278// It returns 2 on error in cache access.
[587]279//////////////////////////////////////////////////////////////////////////////////
280
281static unsigned int _get_child_from_parent( fat_inode_t*   parent,
282                                            char*          name,
283                                            fat_inode_t**  inode ); 
284
285/////////////////////////////////////////////////////////////////////////////////
286// For a file (or a directory) identified by the "pathname" argument, the
287// following function returns in the "inode" argument the inode pointer
288// associated to the searched file (or directory), with code (0).
289// If the searched file (or directory) is not found, but the parent directory
290// is found, it returns in the "inode" argument the pointer on the parent inode,
291// with code (1).  Finally, code (2) and code (3) are error codes.
292// Both the Inode-Tree and the involved Cache-Files are updated from the block
293// device in case of miss on one inode during the search in path.
294// Neither the Fat-Cache, nor the block device are updated.
295// It returns 0 if searched inode found
296// It returns 1 if searched inode not found but parent directory found
297// It returns 2 if searched inode not found and parent directory not found
298// It returns 3 if one name too long
299/////////////////////////////////////////////////////////////////////////////////
300
301static unsigned int _get_inode_from_path( char*          pathname,
302                                          fat_inode_t**  inode );
303
304//////////////////////////////////////////////////////////////////////////////////
[638]305// The following function checks if node "a" is an ancestor of inode "b".
306// It returns 0 on failure.
307// It returns 1 otherwise.
308//////////////////////////////////////////////////////////////////////////////////
309
310static unsigned int _is_ancestor( fat_inode_t* a,
311                                  fat_inode_t* b);
312
313//////////////////////////////////////////////////////////////////////////////////
[587]314// This function computes the length and the number of LFN entries required
315// to store a node name in the "length" and "nb_lfn" arguments.
316// Short name (less than 13 characters) require 1 LFN entry.
317// Medium names (from 14 to 26 characters require 2 LFN entries.
318// Large names (up to 31 characters) require 3 LFN entries.
[674]319// It returns 0 on success.
[587]320// It returns 1 if length larger than 31 characters.
321//////////////////////////////////////////////////////////////////////////////////
322
323static unsigned int _check_name_length( char* name,
324                                        unsigned int* length,
325                                        unsigned int* nb_lfn );
326
327//////////////////////////////////////////////////////////////////////////////////
328// For a node identified by the "inode" argument, this function updates the
329// "size" and "cluster" values in the entry of the parent directory File-Cache.
330// It set the dirty bit in the modified buffer of the parent directory File-Cache.
331//////////////////////////////////////////////////////////////////////////////////
332
333static unsigned int _update_dir_entry( fat_inode_t*  inode );
334
335//////////////////////////////////////////////////////////////////////////////////
336// The following function add new "child" in Cache-File of "parent" directory.
[674]337// It accesses the File_Cache associated to the parent directory, and scan the
[587]338// clusters allocated to this directory to find the NO_MORE entry.
339// This entry will be the first modified entry in the directory.
340// Regarding the name storage, it uses LFN entries for all names.
341// Therefore, it writes 1, 2, or 3 LFN entries (depending on the child name
342// actual length, it writes one NORMAL entry, and writes the new NOMORE entry.
343// It updates the dentry field in the child inode.
344// It set the dirty bit for all modified File-Cache buffers.
345// The block device is not modified by this function.
346//////////////////////////////////////////////////////////////////////////////////
347
348static unsigned int _add_dir_entry( fat_inode_t* child,
349                                    fat_inode_t* parent );
350
351//////////////////////////////////////////////////////////////////////////////////
352// The following function invalidates all dir_entries associated to the "inode"
353// argument from its parent directory.
354// It set the dirty bit for all modified buffers in parent directory Cache-File.
355// The inode itself is not modified by this function.
356// The block device is not modified by this function.
357//////////////////////////////////////////////////////////////////////////////////
358
359static unsigned int _remove_dir_entry( fat_inode_t*  inode );
360
361//////////////////////////////////////////////////////////////////////////////////
362// The following function add the special entries "." and ".." in the File-Cache
363// of the directory identified by the "child" argument.
364// The parent directory is defined by the "parent" argument.
365// The child directory File-Cache is supposed to be empty.
366// We use two NORMAL entries for these "." and ".." entries.
367// The block device is not modified by this function.
368//////////////////////////////////////////////////////////////////////////////////
369
370static void _add_special_directories( fat_inode_t* child,
371                                      fat_inode_t* parent );
372
373//////////////////////////////////////////////////////////////////////////////////
374// The following function releases all clusters allocated to a file or directory,
375// from the cluster index defined by the "cluster" argument, until the end
376// of the FAT linked list.
377// It calls _get_fat_entry() and _set_fat_entry() functions to scan the FAT,
378// and to update the clusters chaining.
379// The FAT region on block device is updated.
[674]380// It returns 0 on success.
381// It returns 1 on error.
[587]382//////////////////////////////////////////////////////////////////////////////////
383
384static unsigned int _clusters_release( unsigned int cluster );
385
386//////////////////////////////////////////////////////////////////////////////////
387// This function allocate "nb_clusters_more" new clusters to a file (or directory)
388// identified by the "inode" pointer. It allocates also the associated buffers
389// and buffer descriptors in the Cache-File.
390// It calls _get_fat_entry() and _set_fat_entry() functions to update the
391// clusters chaining in the Cache-Fat. The FAT region on block device is updated.
[674]392// It returns 0 on success.
393// It returns 1 on error.
[587]394//////////////////////////////////////////////////////////////////////////////////
395
396static unsigned int _clusters_allocate( fat_inode_t* inode, 
397                                        unsigned int nb_clusters_current,
398                                        unsigned int nb_clusters_more );
399
400//////////////////////////////////////////////////////////////////////////////////
[651]401// This recursive function scans one File-Cache (or Fat-Cache) from root to
402// leaves. All memory allocated for 4KB buffers, and buffer descriptors (in
403// leaves) is released, along with the 64-Tree structure (root node is kept).
404// The cache is identified by the "root" and "levels" arguments.
[587]405// It should not contain any dirty clusters.
406//////////////////////////////////////////////////////////////////////////////////
407
[651]408static void _release_cache_memory( fat_cache_node_t*  root,
409                                   unsigned int       levels );
[587]410
411//////////////////////////////////////////////////////////////////////////////////
[639]412// The following function allocates and initializes a new Fat-Cache node.
413// Its first child can be specified (used when adding a cache level).
414// The Fat-Cache is initialized as empty: all children set to NULL.
415// It returns a pointer to a new Fat-Cache node.
416//////////////////////////////////////////////////////////////////////////////////
417
418static fat_cache_node_t* _allocate_one_cache_node( fat_cache_node_t* first_child );
419
420//////////////////////////////////////////////////////////////////////////////////
[587]421// The following function allocates and initializes a new inode,
422// using the values defined by the arguments.
423// If the "cache_allocate" argument is true ans empty cache is allocated.
424// It returns a pointer on the new inode.
425//////////////////////////////////////////////////////////////////////////////////
426
427static fat_inode_t* _allocate_one_inode( char*        name,
428                                         unsigned int is_dir,
429                                         unsigned int cluster,
430                                         unsigned int size,
431                                         unsigned int count,
432                                         unsigned int dentry,
433                                         unsigned int cache_allocate );
434
435//////////////////////////////////////////////////////////////////////////////////
436// The following function allocates one 4 Kbytes buffer and associated cluster
437// descriptor for the file (or directory) identified by the "inode" argument,
438// and updates the Cache_File slot identified by the "cluster_id" argument.
439// The File-Cache slot must be empty.
440// It updates the cluster descriptor, using the "cluster" argument, that is
441// the cluster index in FAT.  The cluster descriptor dirty field is set.
442// It traverse the 64-tree Cache-file from top to bottom to find the last level.
443//////////////////////////////////////////////////////////////////////////////////
444
445static void _allocate_one_buffer( fat_inode_t*    inode,
446                                  unsigned int    cluster_id,
447                                  unsigned int    cluster );
448
449//////////////////////////////////////////////////////////////////////////////////
450// The following function allocates one free cluster from the FAT "heap" of free
451// clusters, and returns the cluster index in the "cluster" argument.
452// It updates the FAT slot, and the two FAT global variables: first_free_cluster,
453// and free_clusters_number.
[674]454// It returns 0 on success.
455// It returns 1 on error.
[587]456//////////////////////////////////////////////////////////////////////////////////
457
458static unsigned int _allocate_one_cluster( unsigned int*  cluster );
459
460/////////////////////////////////////////////////////////////////////////////
461// This function remove from the file system a file or a directory
462// identified by the "inode" argument.
463// The remove condition must be checked by the caller.
464// The relevant lock(s) must have been taken by te caller.
[674]465// It returns 0 on success.
466// It returns 1 on error.
[587]467/////////////////////////////////////////////////////////////////////////////
468
469static unsigned int _remove_node_from_fs( fat_inode_t* inode );
470
471/////////////////////////////////////////////////////////////////////////////
472// This function return the cluster index and the size for a file
473// identified by the "pathname" argument, scanning directly the block
474// device DATA region.
475// It is intended to be called only by the _fat_load_no_cache() function,
476// it does not use the dynamically allocated File Caches, but uses only
477// the 4 Kbytes _fat_buffer_data.
[674]478// It returns 0 on success.
479// It returns 1 on error.
[587]480/////////////////////////////////////////////////////////////////////////////
481
482static unsigned int _file_info_no_cache( char*          pathname,
483                                         unsigned int*  file_cluster,
484                                         unsigned int*  file_size );
485
486/////////////////////////////////////////////////////////////////////////////
487// This function scan directly the FAT region on the block device,
488// and returns in the "next" argument the value stored in the fat slot
489// identified by the "cluster" argument.
490// It is intended to be called only by the _fat_load_no_cache() function,
491// as it does not use the dynamically allocated Fat-Cache, but uses only
492// the 4 Kbytes _fat_buffer_fat.
[674]493// It returns 0 on success.
494// It returns 1 on error.
[587]495/////////////////////////////////////////////////////////////////////////////
496
497static unsigned int _next_cluster_no_cache( unsigned int   cluster,
498                                            unsigned int*  next );
499
500
501//////////////////////////////////////////////////////////////////////////////////
502// The following functions return the length or the size of a FAT field,
503// identified by an (offset,length) mnemonic defined in the fat32.h file.
504//////////////////////////////////////////////////////////////////////////////////
505
506static inline int get_length( int offset , int length ) { return length; }
507
508static inline int get_offset( int offset , int length ) { return offset; }
509
510
511
512
513
514//////////////////////////////////////////////////////////////////////////////////
515//////////////////////////////////////////////////////////////////////////////////
516//                  Static functions definition
517//////////////////////////////////////////////////////////////////////////////////
518//////////////////////////////////////////////////////////////////////////////////
519
520#if GIET_DEBUG_FAT
521///////////////////////////////////////////////////
522static void _display_one_block( unsigned char* buf,
523                                char*          string,
524                                unsigned int   block_id )
[258]525{
526    unsigned int line;
527    unsigned int word;
528
[587]529    _printf("\n***  <%s>  block %x  ***********************************\n",
530            string , block_id );
[258]531
[417]532    for ( line = 0 ; line < 16 ; line++ )
[258]533    {
[417]534        // display line index
[587]535        _printf("%x : ", line );
[258]536
[417]537        // display 8*4 bytes hexa
[258]538        for ( word=0 ; word<8 ; word++ )
539        {
540            unsigned int byte  = (line<<5) + (word<<2);
[587]541            unsigned int hexa  = (buf[byte  ]<<24) |
542                                 (buf[byte+1]<<16) |
543                                 (buf[byte+2]<< 8) |
544                                 (buf[byte+3]      );
545            _printf(" %X |", hexa );
[258]546        }
[587]547        _printf("\n");
[258]548    }
[587]549    _printf("*******************************************************************\n"); 
550} // end _display_one_block() 
[530]551#endif
[291]552
[587]553
554
[530]555#if GIET_DEBUG_FAT
[587]556/////////////////////////////////////
557static void _display_fat_descriptor()
[530]558{
[587]559    _printf("\n###############  FAT DESCRIPTOR  ################################" 
[674]560            "\nFAT initialized                  %x"
[587]561            "\nBlock Size  (bytes)              %x"
562            "\nCluster Size  (bytes)            %x"
[530]563            "\nFAT region first lba             %x"
[587]564            "\nFAT region size (blocks)         %x"
565            "\nDATA region first lba            %x"
566            "\nDATA region size (blocks)        %x"
[530]567            "\nNumber of free clusters          %x"
[587]568            "\nFirst free cluster index         %x" 
569            "\nFat_cache_levels                 %d" 
[530]570            "\n#################################################################\n",
[674]571            _fat.initialized,
[530]572            _fat.sector_size,
[587]573            _fat.cluster_size,
[530]574            _fat.fat_lba,
[587]575            _fat.fat_sectors,
[530]576            _fat.data_lba,
[587]577            _fat.data_sectors,
578            _fat.free_clusters_number,
579            _fat.first_free_cluster,
580            _fat.fat_cache_levels );
581
582} // end _display_fat_descriptor()
[530]583#endif
584
[587]585
586
[709]587#if 0
[587]588////////////////////////////////////////////////////////
589static void _display_clusters_list( fat_inode_t* inode )
[258]590{
[587]591    _printf("\n**************** clusters for <%s> ***********************\n", inode->name );
592    unsigned int next;
593    unsigned int n       = 0;
594    unsigned int current = inode->cluster;
595    while( (current < END_OF_CHAIN_CLUSTER_MIN) && (n < 1024) )
596    {
597        _get_fat_entry( current , &next );
598        _printf(" > %X", current );
599        n++;
600        if ( (n & 0x7) == 0 ) _printf("\n");
601        current = next;
602    }
603    _printf("\n");
604}  // end _display_clusters_list()
605#endif
[258]606
[587]607
608
609/////////////////////////////////////////////////////////////////////////////////
610static int _fat_ioc_access( unsigned int use_irq,       // descheduling if non zero
611                            unsigned int to_mem,        // read / write
612                            unsigned int lba,           // first sector on device
613                            unsigned int buf_vaddr,     // memory buffer vaddr
614                            unsigned int count )        // number of sectors
[291]615{
[587]616    // compute memory buffer physical address
617    unsigned int       flags;         // for _v2p_translate
618    unsigned long long buf_paddr;     // buffer physical address
[291]619
[587]620    if ( ((_get_mmu_mode() & 0x4) == 0 ) || USE_IOC_RDK )  // identity
621    { 
622        buf_paddr = (unsigned long long)buf_vaddr;
623    }
624    else                                // V2P translation required
[291]625    {
[587]626        buf_paddr = _v2p_translate( buf_vaddr , &flags );
[291]627    }
[587]628
629#if (GIET_DEBUG_FAT & 1)
630if ( _get_proctime() > GIET_DEBUG_FAT )
[664]631_printf("\n[DEBUG FAT] _fat_ioc_access(): enters at cycle %d\n"
[587]632        "  to_mem = %d / vaddr = %x / paddr = %l / sectors = %d / lba = %x\n",
633        _get_proctime(), to_mem, buf_vaddr, buf_paddr, count, lba );
634#endif
635
636
637#if GIET_NO_HARD_CC     // L1 cache inval (virtual addresses)
638    if ( to_mem ) _dcache_buf_invalidate( buf_vaddr, count<<9 );
639#endif
640
641
642#if   ( USE_IOC_BDV )   // call the proper driver
643    return( _bdv_access( use_irq , to_mem , lba , buf_paddr , count ) ); 
644#elif ( USE_IOC_HBA )
645    return( _hba_access( use_irq , to_mem , lba , buf_paddr , count ) );
646#elif ( USE_IOC_SDC )
647    return( _sdc_access( use_irq , to_mem , lba , buf_paddr , count ) );
648#elif ( USE_IOC_SPI )
649    return( _spi_access( use_irq , to_mem , lba , buf_paddr , count ) );
650#elif ( USE_IOC_RDK )
651    return( _rdk_access( use_irq , to_mem , lba , buf_paddr , count ) );
652#else
[664]653    _printf("\n[FAT ERROR] _fat_ioc_access(): no IOC driver\n");
[587]654    _exit();
655#endif
656
657}  // end _fat_ioc_access()
658
659
660
661
662/////////////////////////////////////////////////////////////////////
663static inline unsigned int _get_levels_from_size( unsigned int size )
664{ 
665    if      ( size <= (1<<18) ) return 1;     // 64 clusters == 256 Kbytes
666    else if ( size <= (1<<24) ) return 2;     // 64 * 64 clusters => 16 Mbytes
667    else if ( size <= (1<<30) ) return 3;     // 64 * 64 * 64 cluster => 1 Gbytes
668    else                        return 4;     // 64 * 64 * 64 * 64 clusters
[291]669}
670
[587]671
672
673////////////////////////////////////////////////////////
674static unsigned int _read_entry( unsigned int    offset,
675                                 unsigned int    size,
676                                 unsigned char*  buffer,
677                                 unsigned int    little_endian )
[258]678{
[587]679    unsigned int n;
[258]680    unsigned int res  = 0;
681
[587]682    if ( little_endian)
[258]683    {
[587]684        for( n = size ; n > 0 ; n-- ) res = (res<<8) | buffer[offset+n-1];
[258]685    }
686    else
687    {
[587]688        for( n = 0 ; n < size ; n++ ) res = (res<<8) | buffer[offset+n];
[258]689    }
690    return res;
691
[587]692}  // end _read_entry
[258]693
694
695
[587]696//////////////////////////////////////////////////////////////////
697static inline unsigned int _cluster_to_lba( unsigned int cluster )       
[258]698{
[587]699    if ( cluster < 2 )
700    { 
[664]701        _printf("\n[FAT ERROR] _cluster_to_lba(): cluster smaller than 2\n");
[587]702        _exit();
[530]703    }
[258]704
[587]705   return  ((cluster - 2) << 3) + _fat.data_lba;
[258]706}
707
[587]708
[258]709//////////////////////////////////////////////////////
[596]710static inline unsigned char _to_lower(unsigned char c)
711{
712   if (c >= 'A' && c <= 'Z') return (c | 0x20);
713   else                      return c;
714}
715
716
717//////////////////////////////////////////////////////
[587]718static inline unsigned char _to_upper(unsigned char c)
[258]719{
720   if (c >= 'a' && c <= 'z') return (c & ~(0x20));
721   else                      return c;
722}
723
724
725
[587]726///////////////////////////////////////////////////////////////////////////
727static unsigned int _get_name_from_path( char*          pathname,  // input
728                                         char*          name,      // output
729                                         unsigned int*  nb_read )  // input & output   
[291]730{
[587]731    // skip leading "/" character
732    if ( pathname[*nb_read] == '/' ) *nb_read = *nb_read + 1;
[291]733
[587]734    // initialises current indexes
735    unsigned int i = *nb_read;
736    unsigned int j = 0;
[291]737   
[587]738    while ( (pathname[i] != '/') && (pathname[i] != 0) )
[291]739    {
[587]740        name[j++] = pathname[i++];   
741        if ( j > NAME_MAX_SIZE ) return 1;
[291]742    }
[587]743
744    // set end of string
[291]745    name[j] = 0;
746
[587]747    // skip trailing "/" character
[291]748    if ( pathname[i] == '/' ) *nb_read += j+1;
749    else                      *nb_read += j;
750
[587]751    return 0;
[291]752}
753
[587]754
755
756////////////////////////////////////////////////////////////////////
757static unsigned int _get_last_name( char*   pathname,       // input
758                                    char*   name )          // output
759{
760    unsigned int nb_read = 0;     
761    while ( pathname[nb_read] != 0 )
762    {
763        if ( _get_name_from_path( pathname, name, &nb_read ) ) return 1;
764    }
765
766    return 0;
767}   // end _get_last_name()
768
769
770
[258]771////////////////////////////////////////////////////////////////////////////////
[587]772static void _get_name_from_short( unsigned char* buffer,  // input:  SFN dir_entry
773                                  char*          name )   // output: name
[258]774{
[596]775    unsigned int i;
776    unsigned int j = 0;
[258]777
[596]778    // get name
779    for ( i = 0; i < 8 && buffer[i] != ' '; i++ )
[258]780    {
[596]781        name[j] = _to_lower( buffer[i] );
782        j++;
[258]783    }
[596]784
785    // get extension
786    for ( i = 8; i < 8 + 3 && buffer[i] != ' '; i++ )
787    {
788        // we entered the loop so there is an extension. add the dot
789        if ( i == 8 )
790        {
791            name[j] = '.';
792            j++;
793        }
794
795        name[j] = _to_lower( buffer[i] );
796        j++;
797    }
798
799    name[j] = '\0';
[258]800}
[291]801
[258]802///////////////////////////////////////////////////////////////////////////////
[587]803static void _get_name_from_long( unsigned char*  buffer, // input : LFN dir_entry
804                                 char*           name )  // output : name
[258]805{
[587]806    unsigned int   name_offset         = 0;
807    unsigned int   buffer_offset       = get_length(LDIR_ORD);
[258]808    unsigned int   l_name_1            = get_length(LDIR_NAME_1);
809    unsigned int   l_name_2            = get_length(LDIR_NAME_2);
810    unsigned int   l_name_3            = get_length(LDIR_NAME_3);
811    unsigned int   l_attr              = get_length(LDIR_ATTR);
812    unsigned int   l_type              = get_length(LDIR_TYPE);
813    unsigned int   l_chksum            = get_length(LDIR_CHKSUM);
814    unsigned int   l_rsvd              = get_length(LDIR_RSVD);
815
816    unsigned int j            = 0;
817    unsigned int eof          = 0;
818
[587]819    while ( (buffer_offset != DIR_ENTRY_SIZE)  && (!eof) )
[258]820    {
821        while (j != l_name_1 && !eof )
822        {
[587]823            if ( (buffer[buffer_offset] == 0x00) || 
824                 (buffer[buffer_offset] == 0xFF) )
[258]825            {
826                eof = 1;
827                continue;
828            }
[587]829            name[name_offset] = buffer[buffer_offset];
830            buffer_offset += 2;
[258]831            j += 2;
[587]832            name_offset++;
[258]833        }
834
[587]835        buffer_offset += (l_attr + l_type + l_chksum);
[258]836        j = 0;
837
838        while (j != l_name_2 && !eof )
839        {
[587]840            if ( (buffer[buffer_offset] == 0x00) || 
841                 (buffer[buffer_offset] == 0xFF) )
[258]842            {
843                eof = 1;
844                continue;
845            }
[587]846            name[name_offset] = buffer[buffer_offset];
847            buffer_offset += 2;
[258]848            j += 2;
[587]849            name_offset++;
[258]850        }
851
[587]852        buffer_offset += l_rsvd;
[258]853        j = 0;
854
855        while (j != l_name_3 && !eof )
856        {
[587]857            if ( (buffer[buffer_offset] == 0x00) || 
858                 (buffer[buffer_offset] == 0xFF) )
[258]859            {
860                eof = 1;
861                continue;
862            }
[587]863            name[name_offset] = buffer[buffer_offset];
864            buffer_offset += 2;
[258]865            j += 2;
[587]866            name_offset++;
[258]867        }
868    }
[587]869    name[name_offset] = 0;
[258]870} // end get_name_from_long()
871
[587]872
873
[639]874////////////////////////////////////////////////////////////
875static fat_cache_node_t* _allocate_one_cache_node( fat_cache_node_t* first_child )
876{
877    fat_cache_node_t* cnode;
878    unsigned int i;
[587]879
[639]880    cnode = _malloc( sizeof(fat_cache_node_t) );
881
882    cnode->children[0] = first_child;
883    for ( i = 1 ; i < 64 ; i++ )
884        cnode->children[i] = NULL;
885
886    return cnode;
887}   // end _allocate_one_cache_node()
888
889
890
[587]891////////////////////////////////////////////////////////////
892static fat_inode_t* _allocate_one_inode( char*        name,
893                                         unsigned int is_dir,
894                                         unsigned int cluster,
895                                         unsigned int size, 
896                                         unsigned int count,
897                                         unsigned int dentry,
898                                         unsigned int cache_allocate )
[291]899{
[587]900    fat_inode_t* new_inode  = _malloc( sizeof(fat_inode_t) );
[291]901
[587]902    new_inode->parent   = NULL;                 // set by _add_inode_in_tree()
903    new_inode->next     = NULL;                 // set by _add_inode_in_tree()
904    new_inode->child    = NULL;                 // set by _add_inode_in_tree()
905    new_inode->cluster  = cluster;
906    new_inode->size     = size; 
907    new_inode->cache    = NULL;
908    new_inode->levels   = 0;
909    new_inode->count    = count;
910    new_inode->is_dir   = (is_dir != 0);
911    new_inode->dentry   = dentry;             
[291]912
[587]913    _strcpy( new_inode->name , name ); 
914
915    if ( cache_allocate )
[291]916    {
[639]917        new_inode->cache    = _allocate_one_cache_node( NULL );
[587]918        new_inode->levels   = _get_levels_from_size( size );
[291]919    }
920
[587]921    return new_inode;
922}   // end _allocate_one_inode()
[291]923
[587]924
925
926
927////////////////////////////////////////////////////
928static void _add_inode_in_tree( fat_inode_t*  child,
929                                fat_inode_t*  parent )
930{
931    child->parent = parent;
932    child->next   = parent->child;
933    parent->child = child;
934}   // end _add_inode-in_tree()
935
936
937
938
939//////////////////////////////////////////////////////////
940static void _remove_inode_from_tree( fat_inode_t*  inode )
941{
942    fat_inode_t*  current;
943    fat_inode_t*  prev = inode->parent->child;
944
945    if ( inode == prev )  // removed inode is first in its linked list
[291]946    {
[587]947        inode->parent->child = inode->next;
[291]948    }
[587]949    else                  // removed inode is not the first
[291]950    {
[587]951        for( current = prev->next ; current ; current = current->next )
[291]952        {
[587]953            if ( current == inode )
[291]954            {
[587]955                prev->next = current->next;
[291]956            }
[587]957            prev = current;
958        }   
959    }   
960}  // end _delete_one_inode()
[291]961
962
[587]963
964
965//////////////////////////////////////////////////////////////////////
966static unsigned int _get_buffer_from_cache( fat_inode_t*        inode,
967                                            unsigned int        cluster_id,
968                                            fat_cache_desc_t**  desc )
969{
970    // get cache pointer and levels
971    fat_cache_node_t*   node;         // pointer on a 64-tree node
972    unsigned int        level;        // cache level
973
974    if ( inode == NULL )   // searched cache is the Fat-Cache
975    {
976        node   = _fat.fat_cache_root;
977        level  = _fat.fat_cache_levels;
978
979#if (GIET_DEBUG_FAT & 1)
980if ( _get_proctime() > GIET_DEBUG_FAT )
[664]981_printf("\n[DEBUG FAT] _get_buffer_from_cache(): enters in FAT-Cache"
[587]982        " for cluster_id = %d\n", cluster_id );
983#endif
984    }
985    else                   // searched cache is a File-Cache
986    {
[665]987        // add cache levels if needed
988        while ( _get_levels_from_size( (cluster_id + 1) * 4096 ) > inode->levels )
989        {
990#if (GIET_DEBUG_FAT & 1)
991if ( _get_proctime() > GIET_DEBUG_FAT )
992_printf("\n[DEBUG FAT] _get_buffer_from_cache(): adding a File-Cache level\n" );
993#endif
994
995            inode->cache = _allocate_one_cache_node( inode->cache );
996            inode->levels++;
997        }
998
[587]999        node   = inode->cache;
1000        level  = inode->levels;
1001
1002#if (GIET_DEBUG_FAT & 1)
1003if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1004_printf("\n[DEBUG FAT] _get_buffer_from_cache(): enters in File-Cache <%s>"
[587]1005        " for cluster_id = %d\n", inode->name , cluster_id );
1006#endif
1007    }
1008
1009    // search the 64-tree cache from top to bottom
1010    while ( level )
1011    {
1012        // compute child index at each level
1013        unsigned int index = (cluster_id >> (6*(level-1))) & 0x3F;
1014
1015        if ( level == 1 )        // last level => children are cluster descriptors
[291]1016        {
[587]1017            fat_cache_desc_t* pdesc = (fat_cache_desc_t*)node->children[index];
[291]1018
[587]1019            if ( pdesc == NULL )      // miss
[291]1020            {
[587]1021                // get missing cluster index lba
1022                unsigned int lba;
1023                unsigned int next;
1024                unsigned int current = inode->cluster;
1025                unsigned int count   = cluster_id;
1026
1027                if ( inode == NULL )      // searched cache is the Fat-Cache
[291]1028                {
[587]1029
1030#if (GIET_DEBUG_FAT & 1)
1031if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1032_printf("\n[DEBUG FAT] _get_buffer_from_cache(): miss in FAT-Cache for cluster_id %d\n",
[587]1033        cluster_id );
1034#endif
1035                    lba = _fat.fat_lba + (cluster_id << 3);
[291]1036                }
[587]1037                else                      // searched cache is a File-Cache
1038                {
1039
1040#if (GIET_DEBUG_FAT & 1)
1041if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1042_printf("\n[DEBUG FAT] _get_buffer_from_cache(): miss in File-Cache <%s> "
[587]1043        "for cluster_id %d\n", inode->name, cluster_id );
1044#endif
1045                    while ( count )
1046                    { 
1047                        if ( _get_fat_entry( current , &next ) ) return 1;
1048                        current = next;
1049                        count--;
1050                    }
1051                    lba = _cluster_to_lba( current );
1052                }
1053
[653]1054                // allocate 4K buffer
1055                void* buf = _malloc( 4096 );
1056
[587]1057                // load one cluster (8 blocks) from block device
1058                if ( _fat_ioc_access( 1,         // descheduling
1059                                      1,         // to memory
1060                                      lba,
1061                                      (unsigned int)buf,
1062                                      8 ) )
1063                {
[653]1064                    _free( buf );
[664]1065                    _printf("\n[FAT ERROR] _get_buffer_from_cache()"
1066                            ": cannot access block device for lba = %x\n", lba );
[587]1067                    return 1;
1068                }
1069
[653]1070                // allocate buffer descriptor
1071                pdesc          = _malloc( sizeof(fat_cache_desc_t) );
[587]1072                pdesc->lba     = lba;
1073                pdesc->buffer  = buf;
1074                pdesc->dirty   = 0;
[653]1075                node->children[index] = pdesc;
[587]1076
1077#if (GIET_DEBUG_FAT & 1)
1078if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1079_printf("\n[DEBUG FAT] _get_buffer_from_cache(): buffer loaded from device"
[587]1080        " at vaddr = %x\n", (unsigned int)buf );
1081#endif
[291]1082            }
[587]1083
1084            // return pdesc pointer
1085            *desc = pdesc;
1086
1087            // prepare next iteration
1088            level--;
1089        }
1090        else                      // not last level => children are 64-tree nodes
1091        {
1092            fat_cache_node_t* child = (fat_cache_node_t*)node->children[index];
1093            if ( child == NULL )  // miss
[291]1094            {
[587]1095                // allocate a cache node if miss
[650]1096                child = _allocate_one_cache_node( NULL );
[587]1097                node->children[index] = child;   
[291]1098            }
[587]1099
1100            // prepare next iteration
1101            node = child;
1102            level--;
[291]1103        }
[587]1104    } // end while
[291]1105
[530]1106    return 0;
[587]1107}  // end _get_buffer_from_cache()
[530]1108
1109
[291]1110
1111
[587]1112/////////////////////////////////////
1113static unsigned int _update_fs_info()
1114{
1115    // update buffer if miss
1116    if ( _fat.fs_info_lba != _fat.block_buffer_lba )
[291]1117    {
[587]1118        if ( _fat_ioc_access( 1,                 // descheduling
[530]1119                              1,                 // read
[587]1120                              _fat.fs_info_lba, 
1121                              (unsigned int)_fat.block_buffer, 
1122                              1 ) )              // one block
[291]1123        {
[664]1124            _printf("\n[FAT_ERROR] _update_fs_info(): cannot read block\n");
[291]1125            return 1;
1126        }
[587]1127        _fat.block_buffer_lba = _fat.fs_info_lba;
[291]1128    }
1129
[587]1130    // update FAT buffer
1131    unsigned int* ptr;
1132    ptr  = (unsigned int*)(_fat.block_buffer + get_offset(FS_FREE_CLUSTERS) );
1133    *ptr = _fat.free_clusters_number;
1134
1135    ptr  = (unsigned int*)(_fat.block_buffer + get_offset(FS_FREE_CLUSTER_HINT) );
1136    *ptr = _fat.first_free_cluster;
[530]1137   
1138    // write bloc to FAT
[587]1139    if ( _fat_ioc_access( 1,                // descheduling
[530]1140                          0,                // write
[587]1141                          _fat.fs_info_lba,
1142                          (unsigned int)_fat.block_buffer, 
1143                          1 ) )             // one block
[530]1144    {
[664]1145        _printf("\n[FAT_ERROR] _update_fs_info(): cannot write block\n");
[530]1146        return 1;
1147    }
1148
[587]1149#if (GIET_DEBUG_FAT & 1)
1150if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1151_printf("\n[DEBUG FAT] _update_fs_info(): nb_free = %x / first_free = %x\n",
[587]1152        _fat.free_clusters_number , _fat.first_free_cluster );
1153#endif
1154
[530]1155    return 0;
1156}  // end _update_fs_info()
1157
[587]1158
1159
1160/////////////////////////////////////////////////////////////////
1161static inline unsigned int _get_fat_entry( unsigned int  cluster,
1162                                           unsigned int* value )
[291]1163{
[587]1164    // compute cluster_id & entry_id in FAT
1165    // a FAT cluster is an array of 1024 unsigned int entries
1166    unsigned int       cluster_id = cluster >> 10;       
1167    unsigned int       entry_id   = cluster & 0x3FF;
[291]1168
[587]1169    // get pointer on the relevant cluster descriptor in FAT cache
1170    fat_cache_desc_t*  pdesc;
1171    unsigned int*      buffer;
1172    if ( _get_buffer_from_cache( NULL,               // Fat-Cache
1173                                 cluster_id,
1174                                 &pdesc ) ) return 1;
1175
1176    // get value from FAT slot
1177    buffer = (unsigned int*)pdesc->buffer;
1178    *value = buffer[entry_id];
1179
1180    return 0;
1181}  // end _get_fat_entry()
1182
1183
1184
1185////////////////////////////////////////////////////////////////
1186static inline unsigned int _set_fat_entry( unsigned int cluster, 
1187                                           unsigned int value  )
1188{
1189    // compute cluster_id & entry_id in FAT
1190    // a FAT cluster is an array of 1024 unsigned int entries
1191    unsigned int cluster_id = cluster >> 10;
1192    unsigned int entry_id   = cluster & 0x3FF;
1193
1194    // get pointer on the relevant cluster descriptor in FAT cache
1195    fat_cache_desc_t*  pdesc;
1196    unsigned int*      buffer; 
1197    if ( _get_buffer_from_cache( NULL,               // Fat-Cache
1198                                 cluster_id,
1199                                 &pdesc ) )  return 1;           
1200
1201    // set value into FAT slot
1202    buffer           = (unsigned int*)pdesc->buffer;
1203    buffer[entry_id] = value;
1204    pdesc->dirty     = 1;
1205
1206    return 0;
1207} // end _set_fat_entry()
1208
1209
1210
1211//////////////////////////////////////////////////////
1212static void _allocate_one_buffer( fat_inode_t*  inode,
1213                                  unsigned int  cluster_id,
1214                                  unsigned int  cluster )
1215{
[639]1216    // add cache levels if needed
1217    while ( _get_levels_from_size( (cluster_id + 1) * 4096 ) > inode->levels )
1218    {
1219#if (GIET_DEBUG_FAT & 1)
1220if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1221_printf("\n[DEBUG FAT] _allocate_one_buffer(): adding a cache level\n" );
[639]1222#endif
1223
1224        inode->cache = _allocate_one_cache_node( inode->cache );
1225        inode->levels++;
1226    }
1227
[587]1228    // search the 64-tree cache from top to bottom
1229    fat_cache_node_t*  node   = inode->cache;
[653]1230    unsigned int       level;
[587]1231
[653]1232    for ( level = inode->levels; level != 0; level-- )
[587]1233    {
1234        // compute child index
1235        unsigned int index = (cluster_id >> (6*(level-1))) & 0x3F;
1236
1237        if ( level == 1 )        // last level => children are cluster descriptors
1238        {
1239            fat_cache_desc_t* pdesc = (fat_cache_desc_t*)node->children[index];
1240            if ( pdesc != NULL )      // slot not empty!!!
1241            {
[664]1242                _printf("\n[FAT ERROR] allocate_one buffer(): slot not empty "
[587]1243                        "in File-Cache <%s> / cluster_id = %d\n", inode->name , cluster_id );
1244                _exit();
1245            }
1246
1247#if (GIET_DEBUG_FAT & 1)
[569]1248if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1249_printf("\n[DEBUG FAT] _allocate_one_buffer(): buffer allocated to <%s> for cluster_id %d\n",
[587]1250        inode->name, cluster_id );
[291]1251#endif
1252
[653]1253            // allocate buffer descriptor
1254            pdesc = _malloc( sizeof(fat_cache_desc_t) );
[587]1255            pdesc->lba     = _cluster_to_lba( cluster );
[653]1256            pdesc->buffer  = _malloc( 4096 );
[587]1257            pdesc->dirty   = 1;
[653]1258            node->children[index] = pdesc;
[587]1259        }
1260        else                      // not last level => children are 64-tree nodes
[291]1261        {
[587]1262            fat_cache_node_t* child = (fat_cache_node_t*)node->children[index];
1263            if ( child == NULL )  // miss
1264            {
1265                // allocate a cache node if miss
[650]1266                child = _allocate_one_cache_node( NULL );
[587]1267                node->children[index] = child;   
1268            }
1269
1270            // prepare next iteration
1271            node  = child;
[291]1272        }
[653]1273    } // end for
[587]1274} // end _allocate_one_buffer
[530]1275
1276
[587]1277
1278
1279///////////////////////////////////////////////////////////////////
1280static unsigned int _allocate_one_cluster( unsigned int*  cluster ) 
1281{
1282    unsigned int nb_free = _fat.free_clusters_number;
1283    unsigned int free    = _fat.first_free_cluster;
1284
1285    // scan FAT to get next free cluster index
1286    unsigned int current = free;
1287    unsigned int found   = 0;
1288    unsigned int max     = (_fat.data_sectors >> 3);
1289    unsigned int value;
1290    do
[530]1291    {
[587]1292        // increment current
1293        current++;
1294
1295        // get FAT entry indexed by current
1296        if ( _get_fat_entry( current , &value ) ) return 1;
1297        // test if free
1298        if ( value == FREE_CLUSTER ) found = 1;
1299    }
1300    while ( (current < max) && (found == 0) );
1301       
1302    // check found 
1303    if ( found == 0 )
1304    {
[664]1305        _printf("\n[FAT_ERROR] _allocate_one_cluster(): unconsistent FAT state");
[530]1306        return 1;
1307    }
1308
[587]1309    // update allocated FAT slot
1310    if ( _set_fat_entry( free , END_OF_CHAIN_CLUSTER_MAX ) ) return 1;
1311
1312    // update FAT descriptor global variables
1313    _fat.free_clusters_number = nb_free - 1;
1314    _fat.first_free_cluster   = current;
1315
1316#if (GIET_DEBUG_FAT & 1)
1317if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1318_printf("\n[DEBUG FAT] _allocate_one_cluster(): cluster = %x / first_free = %x\n",
[587]1319        free , current );
1320#endif
1321
1322    // returns free cluster index
1323    *cluster = free;
[530]1324    return 0;
[291]1325
[587]1326}  // end _allocate_one_cluster()
1327
1328
1329
1330
1331//////////////////////////////////////////////////////////////////////////
1332static unsigned int _update_device_from_cache( unsigned int        levels,
1333                                               fat_cache_node_t*   root,
1334                                               char*               string )
[291]1335{
[587]1336    unsigned int index;
1337    unsigned int ret = 0;
[291]1338
[587]1339    if ( levels == 1 )  // last level => children are cluster descriptors
[291]1340    {
[587]1341        for( index = 0 ; index < 64 ; index++ )
1342        { 
1343            fat_cache_desc_t* pdesc = root->children[index];
1344            if ( pdesc != NULL )
1345            { 
1346                // update cluster on device if dirty
1347                if ( pdesc->dirty )
1348                {
1349                    if ( _fat_ioc_access( 1,           // descheduling
1350                                          0,           // to block device
1351                                          pdesc->lba,
1352                                          (unsigned int)pdesc->buffer,
1353                                          8 ) )
1354                    {
[664]1355                        _printf("\n[FAT_ERROR] _update_device from_cache(): "
[587]1356                                " cannot access lba = %x\n", pdesc->lba );
1357                        ret = 1;
1358                    }
1359                    else
1360                    {
1361                        pdesc->dirty = 0;
1362
1363#if (GIET_DEBUG_FAT & 1)
1364if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1365_printf("\n[DEBUG FAT] _update_device_from_cache(): cluster_id = %d for <%s>\n",
[587]1366        index , string );
1367#endif
1368
1369                    }
1370                }
1371            }
1372        }
[291]1373    }
[587]1374    else               // not the last level = recursive call on each children
[291]1375    {
[587]1376        for( index = 0 ; index < 64 ; index++ )
1377        { 
1378            fat_cache_node_t* pnode = root->children[index];
1379            if ( pnode != NULL )
1380            {
1381                if ( _update_device_from_cache( levels - 1,
1382                                                root->children[index],
1383                                                string ) ) ret = 1;
1384            }   
1385        }
[291]1386    }
[587]1387    return ret;
1388}  // end _update_device_from_cache()
[291]1389
1390
[587]1391
1392///////////////////////////////////////////////////////////////////
[651]1393static void _release_cache_memory( fat_cache_node_t*  root,
1394                                   unsigned int       levels )
[587]1395{
[651]1396    unsigned int i;
[587]1397
1398    if ( levels == 1 )  // last level => children are cluster descriptors
[503]1399    {
[651]1400        for( i = 0 ; i < 64 ; i++ )
[587]1401        { 
[651]1402            fat_cache_desc_t* pdesc = root->children[i];
[587]1403
1404            if ( pdesc != NULL )
1405            { 
1406                // check dirty
1407                if ( pdesc->dirty )
[664]1408                    _printf("\n[FAT ERROR] _release_cache_memory(): dirty cluster\n");
[651]1409
1410                _free( pdesc->buffer );
1411                _free( pdesc );
1412                root->children[i] = NULL;
[587]1413            }
1414        }
[503]1415    }
[587]1416    else               // not the last level = recursive call on each children
1417    {
[651]1418        for( i = 0 ; i < 64 ; i++ )
[587]1419        { 
[651]1420            fat_cache_node_t* cnode = root->children[i];
[291]1421
[651]1422            if ( cnode != NULL )
1423            {
1424                _release_cache_memory( cnode, levels - 1 );
1425                _free( cnode );
1426                root->children[i] = NULL;
1427            }
[587]1428        }
1429    }
1430}  // end _release_cache_memory()
1431
1432
1433
1434
1435
1436/////////////////////////////////////////////////////////////
1437static unsigned int _clusters_allocate( fat_inode_t* inode, 
1438                                        unsigned int nb_current_clusters,
1439                                        unsigned int nb_required_clusters )
1440{
1441    // Check if FAT contains enough free clusters
1442    if ( nb_required_clusters > _fat.free_clusters_number )
[291]1443    {
[664]1444        _printf("\n[FAT ERROR] _clusters_allocate(): required_clusters = %d"
[587]1445                " / free_clusters = %d\n", nb_required_clusters , _fat.free_clusters_number );
1446        return 1;
1447    }
[291]1448
[587]1449#if (GIET_DEBUG_FAT & 1)
[569]1450if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1451_printf("\n[DEBUG FAT] _clusters_allocate(): enters for <%s> / nb_current_clusters = %d "
[587]1452        "/ nb_required_clusters = %d\n", 
1453        inode->name , nb_current_clusters , nb_required_clusters );
[291]1454#endif
[587]1455 
1456    // compute last allocated cluster index when (nb_current_clusters > 0)
1457    unsigned int current = inode->cluster;
1458    unsigned int next;
1459    unsigned int last;
1460    if ( nb_current_clusters )   // clusters allocated => search last
1461    {   
1462        while ( current < END_OF_CHAIN_CLUSTER_MIN )
[291]1463        {
[587]1464            // get next cluster
1465            if ( _get_fat_entry( current , &next ) )  return 1;
1466            last    = current;
1467            current = next;
[291]1468        }
[587]1469    } 
[291]1470
[587]1471    // Loop on the new clusters to be allocated
1472    // if (nb_current_clusters == 0) the first new cluster index must
1473    //                               be written in inode->cluster field
1474    // if (nb_current_clusters >  0) the first new cluster index must
1475    //                               be written in FAT[last]
1476    unsigned int      cluster_id;
1477    unsigned int      new;
1478    for ( cluster_id = nb_current_clusters ; 
1479          cluster_id < (nb_current_clusters + nb_required_clusters) ; 
1480          cluster_id ++ )
1481    {
1482        // allocate one cluster on block device
1483        if ( _allocate_one_cluster( &new ) ) return 1;
[291]1484
[587]1485        // allocate one 4K buffer to File-Cache
1486        _allocate_one_buffer( inode,
1487                              cluster_id,
1488                              new );
1489
1490        if ( cluster_id == 0 )  // update inode
[291]1491        {
[587]1492            inode->cluster = new;
[291]1493        }
[587]1494        else                    // update FAT
[291]1495        {
[587]1496            if ( _set_fat_entry( last , new ) ) return 1;
[291]1497        }
1498
[587]1499#if (GIET_DEBUG_FAT & 1)
1500if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1501_printf("\n[DEBUG FAT] _clusters_allocate(): done for cluster_id = %d / cluster = %x\n",
[587]1502        cluster_id , new );
1503#endif
[291]1504
[587]1505        // update loop variables
1506        last = new;
1507
1508    } // end for loop
1509
1510    // update FAT : last slot should contain END_OF_CHAIN_CLUSTER_MAX
1511    if ( _set_fat_entry( last , END_OF_CHAIN_CLUSTER_MAX ) )  return 1;
1512
1513    // update the FAT on block device
1514    if ( _update_device_from_cache( _fat.fat_cache_levels,
1515                                    _fat.fat_cache_root,
1516                                    "FAT" ) )              return 1;
1517    return 0;
1518}  // end _clusters_allocate()
1519
1520
1521
1522//////////////////////////////////////////////////////////////
1523static unsigned int _clusters_release( unsigned int cluster )
1524{
1525    // scan the FAT
1526    unsigned int current = cluster;
1527    unsigned int next;
1528    do
1529    { 
1530        // get next_cluster index
1531        if ( _get_fat_entry( current , &next ) )  return 1;
1532
1533        // release current_cluster
1534        if ( _set_fat_entry( current , FREE_CLUSTER ) )   return 1;
1535
1536        // update first_free_cluster and free_clusters_number in FAT descriptor
1537        _fat.free_clusters_number = _fat.free_clusters_number + 1;
1538        if ( _fat.first_free_cluster > current ) _fat.first_free_cluster = current;
1539
1540        // update loop variable
1541        current = next;
[291]1542    }
[587]1543    while ( next < END_OF_CHAIN_CLUSTER_MIN );
[291]1544
[587]1545    // update the FAT on block device
1546    if ( _update_device_from_cache( _fat.fat_cache_levels,
1547                                    _fat.fat_cache_root,
1548                                    "FAT" ) )                return 1;
[291]1549    return 0;
[587]1550}  // end _clusters_release()
[291]1551
[587]1552
1553
1554///////////////////////////////////////////////////////////
1555static void _add_special_directories( fat_inode_t*  child, 
1556                                      fat_inode_t*  parent )
[258]1557{
[587]1558    // get first File-Cache buffer for child
1559    fat_cache_desc_t*   pdesc  = (fat_cache_desc_t*)child->cache->children[0];
1560    unsigned char*      entry;
[258]1561
[587]1562    unsigned int i;
1563    unsigned int cluster;
1564    unsigned int size;
[417]1565
[587]1566    // set "." entry (32 bytes)
1567    cluster = child->cluster;
1568    size    = child->size;
1569    entry   = pdesc->buffer;
1570   
1571    for ( i = 0 ; i < 32 ; i++ )
1572    {
1573        if      (i == 0 )     entry[i] = 0x2E;          // SFN
1574        else if (i <  11)     entry[i] = 0x20;          // SFN
1575        else if (i == 11)     entry[i] = 0x10;          // ATTR == dir
1576        else if (i == 20)     entry[i] = cluster>>16;   // cluster.B2
1577        else if (i == 21)     entry[i] = cluster>>24;   // cluster.B3
1578        else if (i == 26)     entry[i] = cluster>>0;    // cluster.B0
1579        else if (i == 27)     entry[i] = cluster>>8;    // cluster.B1
1580        else if (i == 28)     entry[i] = size>>0;       // size.B0
1581        else if (i == 29)     entry[i] = size>>8;       // size.B1
1582        else if (i == 30)     entry[i] = size>>16;      // size.B2
1583        else if (i == 31)     entry[i] = size>>24;      // size.B3
1584        else                  entry[i] = 0x00;
1585    }
[258]1586
[587]1587    // set ".." entry (32 bytes)
1588    cluster = parent->cluster;
1589    size    = parent->size;
1590    entry   = pdesc->buffer + 32;
[258]1591
[587]1592    for ( i = 0 ; i < 32 ; i++ )
[258]1593    {
[587]1594        if      (i <  2 )     entry[i] = 0x2E;          // SFN
1595        else if (i <  11)     entry[i] = 0x20;          // SFN
1596        else if (i == 11)     entry[i] = 0x10;          // ATTR == dir
1597        else if (i == 20)     entry[i] = cluster>>16;   // cluster.B2
1598        else if (i == 21)     entry[i] = cluster>>24;   // cluster.B3
1599        else if (i == 26)     entry[i] = cluster>>0;    // cluster.B0
1600        else if (i == 27)     entry[i] = cluster>>8;    // cluster.B1
1601        else if (i == 28)     entry[i] = size>>0;       // size.B0
1602        else if (i == 29)     entry[i] = size>>8;       // size.B1
1603        else if (i == 30)     entry[i] = size>>16;      // size.B2
1604        else if (i == 31)     entry[i] = size>>24;      // size.B3
1605        else                  entry[i] = 0x00;
[258]1606    }
[587]1607}  // end _add_special_directories
[258]1608
[358]1609
[587]1610
1611////////////////////////////////////////////////////////////
[638]1612static unsigned int _is_ancestor( fat_inode_t* a,
1613                                  fat_inode_t* b )
1614{
1615    while ( b )
1616    {
1617        if ( a == b )
1618            return 1;
1619
1620        b = b->parent;
1621    }
1622
1623    return 0;
1624} // _is_ancestor()
1625
1626
1627
1628////////////////////////////////////////////////////////////
[587]1629static unsigned int _check_name_length( char* name,
1630                                        unsigned int* length,
1631                                        unsigned int* nb_lfn )
1632{
1633    unsigned int len = _strlen( name );
1634    if      ( len <= 13 )
[258]1635    {
[587]1636        *length  = len;
1637        *nb_lfn  = 1;
1638        return 0;
1639    }
1640    else if ( len <= 26 )
1641    {
1642        *length  = len;
1643        *nb_lfn  = 2;
1644        return 0;
1645    }
1646    else if ( len <= 31 )
1647    {
1648        *length  = len;
1649        *nb_lfn  = 3;
1650        return 0;
1651    }
1652    else
1653    {
1654        return 1;
1655    }
1656}  // _check_name_length()
1657
1658
1659
1660
1661///////////////////////////////////////////////////////////
1662static unsigned int _get_nb_entries( fat_inode_t*   inode,
1663                                     unsigned int*  nb_entries )
1664{
1665    // scan directory until "end of directory" with two embedded loops:
1666    // - scan the clusters allocated to this directory
1667    // - scan the entries to find NO_MORE_ENTRY
1668    fat_cache_desc_t*  pdesc;                      // pointer on buffer descriptor
1669    unsigned char*     buffer;                     // 4 Kbytes buffer (one cluster)
1670    unsigned int       ord;                        // ORD field in directory entry
1671    unsigned int       attr;                       // ATTR field in directory entry
1672    unsigned int       cluster_id = 0;             // cluster index in directory
1673    unsigned int       offset     = 0;             // position in scanned buffer
1674    unsigned int       found      = 0;             // NO_MORE_ENTRY found
1675    unsigned int       count      = 0;             // number of valid NORMAL entries
1676
1677    // loop on clusters allocated to directory
1678    while ( found == 0 )
1679    {
1680        // get one 4 Kytes buffer from File_Cache 
1681        if ( _get_buffer_from_cache( inode,
1682                                     cluster_id,
1683                                     &pdesc ) )   return 1;
1684        buffer = pdesc->buffer;
1685       
1686        // loop on directory entries in buffer
1687        while ( (offset < 4096) && (found == 0) )
[258]1688        {
[587]1689            attr = _read_entry( DIR_ATTR , buffer + offset , 0 );   
1690            ord  = _read_entry( LDIR_ORD , buffer + offset , 0 );
1691
1692            if ( ord == NO_MORE_ENTRY )
[258]1693            {
[587]1694                found = 1;
1695            } 
1696            else if ( ord == FREE_ENTRY )             // free entry => skip
1697            {
1698                offset = offset + 32;
[258]1699            }
[587]1700            else if ( attr == ATTR_LONG_NAME_MASK )   // LFN entry => skip
[258]1701            {
[587]1702                offset = offset + 32;
1703            }
1704            else                                      // NORMAL entry
1705            {
[620]1706                offset = offset + 32;
[587]1707                count++;
1708            }
1709        }  // end loop on directory entries
1710
1711        cluster_id++;
1712        offset = 0;
1713
1714    }  // end loop on clusters
1715
1716    // return nb_entries
1717    *nb_entries = count;
1718   
1719    return 0;
1720}  // end dir_not_empty()
1721
1722
1723
1724///////////////////////////////////////////////////////////
1725static unsigned int _add_dir_entry( fat_inode_t*   child,
1726                                    fat_inode_t*   parent )
1727{
1728    // get child attributes
1729    unsigned int      is_dir  = child->is_dir;     
1730    unsigned int      size    = child->size;
1731    unsigned int      cluster = child->cluster;
1732
1733    // compute number of required entries to store child->name
1734    // - Short name (less than 13 characters) require 3 entries:
1735    //   one LFN entry / one NORMAL entry / one NO_MORE_ENTRY entry.
1736    // - Longer names (up to 31 characters) can require 4 or 5 entries:
1737    //   2 or 3 LFN entries / one NORMAL entry / one NO_MORE entry.
1738    unsigned int length;
1739    unsigned int nb_lfn;
1740    if ( _check_name_length( child->name, 
1741                             &length,
1742                             &nb_lfn ) )  return 1;
1743
1744#if (GIET_DEBUG_FAT & 1)
1745if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1746_printf("\n[DEBUG FAT] _add_dir_entry(): try to add <%s> in <%s> / nb_lfn = %d\n", 
[587]1747        child->name , parent->name, nb_lfn );
1748#endif
1749
1750    // Find end of directory : two embedded loops:
1751    // - scan the clusters allocated to this directory
1752    // - scan the entries to find NO_MORE_ENTRY
1753    fat_cache_desc_t*  pdesc;                      // pointer on buffer descriptor
1754    unsigned char*     buffer;                     // 4 Kbytes buffer (one cluster)
1755    unsigned int       cluster_id = 0;             // cluster index in directory
1756    unsigned int       offset     = 0;             // position in scanned buffer
1757    unsigned int       found      = 0;             // NO_MORE_ENTRY found
1758
1759    // loop on clusters allocated to directory
1760    while ( found == 0 )
1761    {
1762        // get one 4 Kytes buffer from File_Cache 
1763        if ( _get_buffer_from_cache( parent,
1764                                     cluster_id,
1765                                     &pdesc ) )   return 1;
1766
1767        buffer = pdesc->buffer;
1768       
1769        // loop on directory entries in buffer
1770        while ( (offset < 4096) && (found == 0) )
1771        {
1772            if ( _read_entry( LDIR_ORD , buffer + offset , 0 ) == NO_MORE_ENTRY )
[258]1773            {
[587]1774                found        = 1;
1775                pdesc->dirty = 1;
1776            } 
1777            else
1778            {
1779                offset = offset + 32;
[258]1780            }
[587]1781        }  // end loop on entries
1782        if ( found == 0 )
1783        {
1784            cluster_id++;
1785            offset = 0;
[258]1786        }
[587]1787    }  // end loop on clusters
[258]1788
[587]1789#if (GIET_DEBUG_FAT & 1)
1790if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1791_printf("\n[DEBUG FAT] _add_dir_entry(): get NO_MORE directory entry : "
[587]1792        " buffer = %x / offset = %x / cluster_id = %d\n",
1793        (unsigned int)buffer , offset , cluster_id );
1794#endif
[258]1795
[587]1796    // enter FSM :
1797    // The new child requires to write 3, 4, or 5 directory entries.
1798    // To actually register the new child, we use a 5 steps FSM
1799    // (one state per entry to be written), that is traversed as:
1800    //    LFN3 -> LFN2 -> LFN1 -> NORMAL -> NOMORE
1801    // The buffer and first directory entry to be  written are identified
1802    // by the variables : buffer / cluster_id / offset
[417]1803
[587]1804    unsigned char* name  = (unsigned char*)child->name;
1805
1806    unsigned int step;          // FSM state
1807
1808    if      ( nb_lfn == 1 ) step = 3;
1809    else if ( nb_lfn == 2 ) step = 4;
1810    else if ( nb_lfn == 3 ) step = 5;
1811   
1812    unsigned int   i;           // byte index in 32 bytes directory
1813    unsigned int   c;           // character index in name
1814    unsigned char* entry;       // buffer + offset;
1815
1816    while ( step )   
1817    {
1818        // get another buffer if required
1819        if ( offset >= 4096 )  // new buffer required
[417]1820        {
[665]1821            if ( _get_buffer_from_cache( parent,
1822                                         cluster_id + 1,
1823                                         &pdesc ) )      return 1;
1824            buffer       = pdesc->buffer;
1825            pdesc->dirty = 1;
1826            offset       = 0;
[587]1827        }
[417]1828
[587]1829        // compute directory entry address
1830        entry = buffer + offset;
[417]1831
[587]1832#if (GIET_DEBUG_FAT & 1)
1833if ( _get_proctime() > GIET_DEBUG_FAT )
[664]1834_printf("\n[DEBUG FAT] _add_dir_entry(): FSM step = %d /"
[587]1835        " offset = %x / nb_lfn = %d\n", step, offset, nb_lfn );
1836#endif
1837
1838        // write one 32 bytes directory entry per iteration
1839        switch ( step )
1840        {
1841            case 5:   // write LFN3 entry
1842            {
1843                c = 26;
1844                // scan the 32 bytes in dir_entry
1845                for ( i = 0 ; i < 32 ; i++ )
[258]1846                {
[587]1847                    if (i == 0)
1848                    {
1849                        if ( nb_lfn == 3) entry[i] = 0x43;
1850                        else              entry[i] = 0x03;
1851                    }
1852                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
1853                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
1854                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
1855                              ( c < length ) )
1856                    {
1857                                          entry[i] = name[c];
1858                                          c++;
1859                    }
1860                    else if (i == 11)     entry[i] = 0x0F;
1861                    else if (i == 12)     entry[i] = 0xCA;
1862                    else                  entry[i] = 0x00;
[258]1863                }
[587]1864                step--;
1865                break;
1866            }
1867            case 4:   // write LFN2 entry 
1868            {
1869                c = 13;
1870                // scan the 32 bytes in dir_entry
1871                for ( i = 0 ; i < 32 ; i++ )
[258]1872                {
[587]1873                    if (i == 0)
1874                    {
1875                        if ( nb_lfn == 2) entry[i] = 0x42;
1876                        else              entry[i] = 0x02;
1877                    }
1878                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
1879                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
1880                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
1881                              ( c < length ) )
1882                    {
1883                                          entry[i] = name[c];
1884                                          c++;
1885                    }
1886                    else if (i == 11)     entry[i] = 0x0F;
1887                    else if (i == 12)     entry[i] = 0xCA;
1888                    else                  entry[i] = 0x00;
[258]1889                }
[587]1890                step--;
1891                break;
[258]1892            }
[587]1893            case 3:   // Write LFN1 entry   
[258]1894            {
[587]1895                c = 0;
1896                // scan the 32 bytes in dir_entry
1897                for ( i = 0 ; i < 32 ; i++ )
1898                {
1899                    if (i == 0)
1900                    {
1901                        if ( nb_lfn == 1) entry[i] = 0x41;
1902                        else              entry[i] = 0x01;
1903                    }
1904                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
1905                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
1906                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
1907                              ( c < length ) )
1908                    {
1909                                          entry[i] = name[c];
1910                                          c++;
1911                    }
1912                    else if (i == 11)     entry[i] = 0x0F;
1913                    else if (i == 12)     entry[i] = 0xCA;
1914                    else                  entry[i] = 0x00;
1915                }
1916                step--;
1917                break;
[258]1918            }
[587]1919            case 2:   // write NORMAL entry     
[258]1920            {
[587]1921                c = 0;
1922                // scan the 32 bytes in dir_entry
1923                for ( i = 0 ; i < 32 ; i++ )
[258]1924                {
[587]1925                    if      ( (i < 8) && (c < length) )             // SFN
[417]1926                    {
[587]1927                                          entry[i] = _to_upper( name[c] );
1928                                          c++;
[417]1929                    }
[587]1930                    else if (i <  11)     entry[i] = 0x20;          // EXT
1931                    else if (i == 11)                               // ATTR
1932                    {
1933                        if (is_dir)       entry[i] = 0x10;
1934                        else              entry[i] = 0x20;
1935                    }
1936                    else if (i == 20)     entry[i] = cluster>>16;   // cluster.B2
1937                    else if (i == 21)     entry[i] = cluster>>24;   // cluster.B3
1938                    else if (i == 26)     entry[i] = cluster>>0;    // cluster.B0
1939                    else if (i == 27)     entry[i] = cluster>>8;    // cluster.B1
1940                    else if (i == 28)     entry[i] = size>>0;       // size.B0
1941                    else if (i == 29)     entry[i] = size>>8;       // size.B1
1942                    else if (i == 30)     entry[i] = size>>16;      // size.B2
1943                    else if (i == 31)     entry[i] = size>>24;      // size.B3
1944                    else                  entry[i] = 0x00;
[258]1945                }
[587]1946
1947                // update the dentry field in child inode
1948                child->dentry = ((cluster_id<<12) + offset)>>5;
1949
1950                step--;
1951                break;
[417]1952            }
[587]1953            case 1:   // write NOMORE entry 
[417]1954            {
[587]1955                step--;
1956                entry [0] = 0x00;
1957                break;
1958            }
1959        } // end switch step
1960        offset += 32;
1961    } // exit while => exit FSM   
1962
1963#if (GIET_DEBUG_FAT & 1)
1964if ( _get_proctime() > GIET_DEBUG_FAT )
1965{
[664]1966    _printf("\n[DEBUG FAT] _add_dir_entry(): <%s> successfully added in <%s>\n",
[587]1967            child->name , parent->name );
1968}
1969#endif
1970
1971    return 0;       
1972} // end _add_dir_entry
1973
1974
1975
1976////////////////////////////////////////////////////////////
1977static unsigned int _remove_dir_entry( fat_inode_t*  inode )
1978{
1979    // compute number of LFN entries
1980    unsigned int length;
1981    unsigned int nb_lfn;
1982    if ( _check_name_length( inode->name, 
1983                             &length,
1984                             &nb_lfn ) )  return 1;
1985
1986    // get cluster_id and offset in parent directory cache
1987    unsigned int  dentry     = inode->dentry;
1988    unsigned int  cluster_id = dentry >> 7;
1989    unsigned int  offset     = (dentry & 0x7F)<<5;
1990
1991    // get buffer from parent directory cache
1992    unsigned char*     buffer;
1993    fat_cache_desc_t*  pdesc;
1994
1995    if ( _get_buffer_from_cache( inode->parent,
1996                                 cluster_id,
1997                                 &pdesc ) ) return 1;
1998    buffer       = pdesc->buffer;
1999    pdesc->dirty = 1;
2000
2001    // invalidate NORMAL entry in directory cache
2002    buffer[offset] = 0xE5;
2003
2004    // invalidate LFN entries
2005    while ( nb_lfn )
2006    {
[616]2007        if (offset == 0)  // we must load buffer for (cluster_id - 1)
[587]2008        {
[636]2009            if ( cluster_id == 0 )
2010                break;
2011
[587]2012            if ( _get_buffer_from_cache( inode->parent,
2013                                         cluster_id - 1,
2014                                         &pdesc ) )   return 1;
2015            buffer       = pdesc->buffer;
2016            pdesc->dirty = 1;
[616]2017            offset       = 4096;
[587]2018        }
2019
[616]2020        offset = offset - 32;
2021
2022        // check for LFN entry
2023        if ( _read_entry( DIR_ATTR , buffer + offset , 0 ) != ATTR_LONG_NAME_MASK )
2024            break;
2025
[587]2026        // invalidate LFN entry
2027        buffer[offset] = 0xE5;
2028
2029        nb_lfn--;
2030    }     
2031         
2032    return 0;
2033}  // end _remove_dir_entry
2034
2035
2036
2037
2038////////////////////////////////////////////////////////////
2039static unsigned int _update_dir_entry( fat_inode_t*  inode )
2040{ 
2041    // get Cache-File buffer containing the parent directory entry
2042    // 128 directories entries in one 4 Kbytes buffer
2043    fat_cache_desc_t*  pdesc;
2044    unsigned char*     buffer;   
2045    unsigned int       cluster_id = inode->dentry>>7;
[626]2046    unsigned int       offset     = (inode->dentry & 0x7F)<<5;
[587]2047
2048    if ( _get_buffer_from_cache( inode->parent,
2049                                 cluster_id,
2050                                 &pdesc ) )    return 1;
2051    buffer       = pdesc->buffer;
2052    pdesc->dirty = 1;
2053
2054    // update size field
2055    buffer[offset + 28] = inode->size>>0;       // size.B0
2056    buffer[offset + 29] = inode->size>>8;       // size.B1
2057    buffer[offset + 30] = inode->size>>16;      // size.B2
2058    buffer[offset + 31] = inode->size>>24;      // size.B3
2059
2060    // update cluster field
2061    buffer[offset + 26] = inode->cluster>>0;    // cluster.B0
2062    buffer[offset + 27] = inode->cluster>>8;    // cluster.B1
2063    buffer[offset + 20] = inode->cluster>>16;   // cluster.B2
2064    buffer[offset + 21] = inode->cluster>>24;   // cluster.B3
2065   
2066    return 0;
2067} // end _update_dir_entry()
2068
2069
2070
2071
2072//////////////////////////////////////////////////////////////////
2073static unsigned int _get_child_from_parent( fat_inode_t*   parent,
2074                                            char*          name, 
2075                                            fat_inode_t**  inode )
2076{
2077    fat_inode_t*   current;
2078
2079#if (GIET_DEBUG_FAT & 1)
2080if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2081_printf("\n[DEBUG FAT] _get_child_from_parent(): search <%s> in directory <%s>\n",
[587]2082        name , parent->name );
2083#endif
2084   
2085    // scan inodes in the parent directory
2086    for ( current = parent->child ; current ; current = current->next )
2087    {
[619]2088        if ( _strcmp( name , current->name ) == 0 )
[587]2089        {
2090
2091#if (GIET_DEBUG_FAT & 1)
2092if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2093_printf("\n[DEBUG FAT] _get_child_from_parent(): found inode <%s> in directory <%s>\n", 
[587]2094        name , parent->name );
2095#endif
2096            *inode = current;
2097            return 0;           // name found
2098        }
2099    }
2100
2101    // not found in Inode-Tree => access the parent directory
2102    // file_cache.  Two embedded loops:
2103    // - scan the clusters allocated to this directory
2104    // - scan the directory entries in each 4 Kbytes buffer
2105
2106    unsigned char*    buffer;           // pointer on one cache buffer
2107    char              cname[32];        // buffer for one full entry name
2108    char              lfn1[16];         // buffer for one partial name
2109    char              lfn2[16];         // buffer for one partial name
2110    char              lfn3[16];         // buffer for one partial name
2111    unsigned int      size;             // searched file/dir size (bytes)
2112    unsigned int      cluster;          // searched file/dir cluster index
2113    unsigned int      is_dir;           // searched file/dir type
2114    unsigned int      attr;             // directory entry ATTR field
2115    unsigned int      ord;              // directory entry ORD field
[597]2116    unsigned int      lfn = 0;          // LFN entries number
[587]2117    unsigned int      dentry;           // directory entry index
2118    unsigned int      offset     = 0;   // byte offset in buffer
2119    unsigned int      cluster_id = 0;   // cluster index in directory
2120    int               found      = 0;   // not found (0) / name found (1) / end of dir (-1)
2121
2122#if (GIET_DEBUG_FAT & 1)
2123if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2124_printf("\n[DEBUG FAT] _get_child_from_parent(): does not found inode <%s>"
[587]2125        " in directory <%s> => search in cache\n", name , parent->name );
2126#endif
2127
2128    // scan the clusters allocated to parent directory
2129    while ( found == 0 )
2130    {
2131        // get one 4 Kytes buffer from parent File_Cache 
2132        fat_cache_desc_t*  pdesc;
2133        if ( _get_buffer_from_cache( parent,
2134                                     cluster_id,
2135                                     &pdesc ) )    return 2;
2136        buffer = pdesc->buffer;
2137
2138        // scan this buffer until end of directory, end of buffer, or name found
2139        while( (offset < 4096) && (found == 0) )
2140        {
2141
2142#if (GIET_DEBUG_FAT & 1)
2143if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2144_printf("\n[DEBUG FAT] _get_child_from_parent(): scan buffer %d for <%s>\n",
[587]2145        cluster_id , name );
2146#endif
2147            attr = _read_entry( DIR_ATTR , buffer + offset , 0 );   
2148            ord  = _read_entry( LDIR_ORD , buffer + offset , 0 );
2149
2150            if (ord == NO_MORE_ENTRY)                 // no more entry in directory => break
2151            {
2152                found = -1;
2153            }
2154            else if ( ord == FREE_ENTRY )             // free entry => skip
2155            {
2156                offset = offset + 32;
2157            }
2158            else if ( attr == ATTR_LONG_NAME_MASK )   // LFN entry => get partial name
2159            {
2160                unsigned int seq = ord & 0x3;
2161                lfn = (seq > lfn) ? seq : lfn;   
2162                if      ( seq == 1 ) _get_name_from_long( buffer + offset, lfn1 );
2163                else if ( seq == 2 ) _get_name_from_long( buffer + offset, lfn2 );
2164                else if ( seq == 3 ) _get_name_from_long( buffer + offset, lfn3 );
2165                offset = offset + 32;
2166            }
2167            else                                 // NORMAL entry
2168            {
2169                // build the extracted name
2170                if      ( lfn == 0 )
[258]2171                {
[587]2172                    _get_name_from_short( buffer + offset , cname );
[258]2173                }
[587]2174                else if ( lfn == 1 )
[417]2175                {
[587]2176                    _strcpy( cname      , lfn1 );
2177                }   
2178                else if ( lfn == 2 ) 
2179                {
2180                    _strcpy( cname      , lfn1 );
2181                    _strcpy( cname + 13 , lfn2 );
[417]2182                }
[587]2183                else if ( lfn == 3 ) 
2184                {
2185                    _strcpy( cname      , lfn1 );
2186                    _strcpy( cname + 13 , lfn2 );
2187                    _strcpy( cname + 26 , lfn3 );
2188                }
2189                   
2190                // test if extracted name == searched name
[619]2191                if ( _strcmp( name , cname ) == 0 )
[587]2192                {
2193                    cluster = (_read_entry( DIR_FST_CLUS_HI , buffer + offset , 1 ) << 16) |
2194                              (_read_entry( DIR_FST_CLUS_LO , buffer + offset , 1 )      ) ;
2195                    dentry  = ((cluster_id<<12) + offset)>>5;
2196                    is_dir  = ((attr & ATTR_DIRECTORY) == ATTR_DIRECTORY);
2197                    size    = _read_entry( DIR_FILE_SIZE , buffer + offset , 1 );
2198                    found   = 1;
2199                }
2200                offset = offset + 32;
2201                lfn    = 0;
[258]2202            }
[587]2203        }  // end loop on directory entries
2204        cluster_id++;
2205        offset = 0;
2206    }  // end loop on buffers
2207
2208    if ( found == -1 )  // found end of directory in parent directory
2209    {
2210
2211#if (GIET_DEBUG_FAT & 1)
2212if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2213_printf("\n[DEBUG FAT] _get_child_from_parent(): found end of directory in <%s>\n",
[587]2214        parent->name );
2215#endif
2216        *inode = NULL;
2217        return 1;
2218    }
2219    else               // found searched name in parent directory
2220    {
2221        // allocate a new inode and an empty Cache-File
2222        *inode = _allocate_one_inode( name,
2223                                      is_dir,
2224                                      cluster,
2225                                      size,
2226                                      0,             // count
2227                                      dentry,
2228                                      1 );           // cache_allocate
2229
2230        // introduce it in Inode-Tree
2231        _add_inode_in_tree( *inode , parent );
2232
2233#if (GIET_DEBUG_FAT & 1)
2234if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2235_printf("\n[DEBUG FAT] _get_child_from_parent(): found <%s> on device\n", name );
[587]2236#endif
2237        return 0;
2238    }
2239}  // end _get_child_from_parent()
2240
2241
2242
2243
2244//////////////////////////////////////////////////////////////////
2245static unsigned int _get_inode_from_path( char*          pathname,
2246                                          fat_inode_t**  inode )
2247{
2248    char                 name[32];         // buffer for one name in pathname
2249    unsigned int         nb_read;              // number of characters written in name[]
2250    fat_inode_t*         parent;           // parent inode
2251    fat_inode_t*         child;            // child inode
2252    unsigned int         last;             // while exit condition
2253    unsigned int         code;             // return value
2254
2255#if (GIET_DEBUG_FAT & 1)
2256if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2257_printf("\n[DEBUG FAT] _get_inode_from_path(): enters for path <%s>\n", pathname );
[587]2258#endif
2259
2260    // handle root directory case
[619]2261    if ( _strcmp( pathname , "/" ) == 0 )
[587]2262    {
2263
2264#if (GIET_DEBUG_FAT & 1)
2265if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2266_printf("\n[DEBUG FAT] _get_inode_from_path(): found root inode for <%s>\n", 
[587]2267        pathname );
2268#endif
2269        *inode  = _fat.inode_tree_root;
2270        return 0;
2271    }
2272
2273    // If the pathname is not "/", we traverse the inode tree from the root.
2274    // We use _get_name_from_path() to scan pathname and extract inode names.
2275    // We use _get_child_from_parent() to scan each directory in the path.
2276
2277    last       = 0;
2278    nb_read    = 0;                      // number of characters analysed in path
2279    parent     = _fat.inode_tree_root;   // Inode-Tree root
2280   
[617]2281    while ( !last )
[587]2282    {
2283        // get searched file/dir name
2284        if ( _get_name_from_path( pathname, name, &nb_read ) )
2285        {
2286            return 3;   // error : name too long
[258]2287        }
2288
[587]2289        // compute last iteration condition
2290        last = (pathname[nb_read] == 0);
[258]2291
[587]2292#if (GIET_DEBUG_FAT & 1)
[569]2293if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2294_printf("\n[DEBUG FAT] _get_inode_from_path(): got name <%s>\n", name );
[417]2295#endif
2296
[619]2297        if ( _strcmp( name, ".." ) == 0)
[587]2298        {
[617]2299            // found special name "..", try to go up
2300            code = 0;
2301            if ( parent->parent )
2302                child = parent->parent;
2303            else
2304                child = parent;
2305        }
[619]2306        else if ( _strcmp( name, "." ) == 0 )
[617]2307        {
2308            // found special name ".", stay on the same level
2309            code = 0;
2310            child = parent;
2311        }
2312        else
2313        {
2314            // get child inode from parent directory
2315            code = _get_child_from_parent( parent,
2316                                           name,
2317                                           &child );
[417]2318
[617]2319            // we need to find the child inode for all non terminal names
2320            if ( (code == 2) || ((code == 1 ) && !last) )
2321            {
2322
2323    #if (GIET_DEBUG_FAT & 1)
2324    if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2325    _printf("\n[DEBUG FAT] _get_inode_from_path(): neither parent, nor child found for <%s>\n",
[617]2326            pathname );
2327    #endif
2328                return 2;  // error : parent inode not found
2329            }
[587]2330        }
[617]2331
[587]2332        // update parent if not the last iteration
[617]2333        if ( !last )
2334            parent = child;
[587]2335    } // end while
2336
2337    // returns inode pointer
2338    if (code == 0 )
2339    {
2340
2341#if (GIET_DEBUG_FAT & 1)
2342if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2343_printf("\n[DEBUG FAT] _get_inode_from_path(): found inode for <%s>\n", 
[587]2344        pathname );
2345#endif
2346        *inode  = child;
2347    }
2348    else
2349    {
2350
2351#if (GIET_DEBUG_FAT & 1)
2352if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2353_printf("\n[DEBUG FAT] _get_inode_from_path(): found only parent inode for <%s>\n",
[587]2354        pathname );
2355#endif
2356        *inode  = parent;
2357    }
2358
2359    return code;                 // can be 0 (found) or 1 (not found)
2360
2361}  // end _get_inode_from_path()
2362
2363
2364
2365
2366//////////////////////////////////////////////////////////////
2367static unsigned int _remove_node_from_fs( fat_inode_t* inode )
[258]2368{
[637]2369    // check for root node
2370    if ( !inode->parent ) return 1;
2371
[587]2372    // remove entry in parent directory
2373    if ( _remove_dir_entry( inode ) ) return 1;
2374
2375    // update parent directory on device
2376    if ( _update_device_from_cache( inode->parent->levels,
2377                                    inode->parent->cache,
2378                                    inode->parent->name ) ) return 1;
2379
2380    // release clusters allocated to file/dir in DATA region
2381    if ( _clusters_release( inode->cluster ) ) return 1;
2382
2383    // release File-Cache
[651]2384    _release_cache_memory( inode->cache, inode->levels );
2385    _free ( inode->cache );
[587]2386
2387    // remove inode from Inode-Tree
2388    _remove_inode_from_tree( inode );
2389
2390    // release inode
2391    _free ( inode );
2392
[258]2393    return 0;
[587]2394}  // end _remove_node_from_fs()
[258]2395
2396
[587]2397//////////////////////////////////////////////////////////////////
2398static unsigned int _next_cluster_no_cache( unsigned int   cluster,
2399                                            unsigned int*  next )
2400{
2401    // compute cluster_id and slot_id
2402    // each cluster contains 1024 slots (4 bytes per slot)
2403    unsigned int cluster_id  = cluster >> 10;
2404    unsigned int slot_id     = cluster & 0x3FF;
[258]2405
[587]2406    // compute lba of cluster identified by cluster_id
2407    unsigned int lba = _fat.fat_lba + (cluster_id << 3);
[258]2408
[587]2409    // get cluster containing the adressed FAT slot in FAT buffer
2410    if ( _fat_buffer_fat_lba != lba )
2411    {
2412        if ( _fat_ioc_access( 0,         // no descheduling
2413                              1,         // read
2414                              lba,
2415                              (unsigned int)_fat_buffer_fat,
2416                              8 ) )
2417        {
[664]2418            _printf("\n[FAT ERROR] _next_cluster_no_cache(): "
[587]2419                    "cannot load lba = %x into fat_buffer\n", lba );
2420            return 1;
2421        }
2422
2423        _fat_buffer_fat_lba = lba;
2424    }
2425
2426    // return next cluster index
2427    unsigned int* buf = (unsigned int*)_fat_buffer_fat;
2428    *next = buf[slot_id];
2429    return 0;
2430   
2431}  // end _next_cluster_no_cache()
2432
2433
2434
2435
2436/////////////////////////////////////////////////////////////////
2437static unsigned int _file_info_no_cache( char*          pathname,
2438                                         unsigned int*  file_cluster,
2439                                         unsigned int*  file_size )
[258]2440{
[587]2441   
2442#if (GIET_DEBUG_FAT & 1)
2443if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2444_printf("\n[DEBUG FAT] _file_info_no_cache(): enters for path <%s>\n", pathname );
[587]2445#endif
[258]2446
[587]2447    char            name[32];             // buffer for one name in the analysed pathname
2448    char            lfn1[16];             // buffer for a partial name in LFN entry
2449    char            lfn2[16];             // buffer for a partial name in LFN entry
2450    char            lfn3[16];             // buffer for a partial name in LFN entry
2451    char            cname[32];            // buffer for a full name in a directory entry
2452    unsigned int    nb_read;              // number of characters analysed in path
2453    unsigned int    parent_cluster;       // cluster index for the parent directory
[597]2454    unsigned int    child_cluster = 0;    // cluster index for the searched file/dir
2455    unsigned int    child_size = 0;       // size of the searched file/dir
[587]2456    unsigned int    child_is_dir;         // type of the searched file/dir
2457    unsigned int    offset;               // offset in a 4 Kbytes buffer
2458    unsigned int    ord;                  // ORD field in a directory entry
2459    unsigned int    attr;                 // ATTR field in a directory entry
[597]2460    unsigned int    lfn = 0;              // number of lfn entries
[587]2461    unsigned char*  buf;                  // pointer on a 4 Kbytes buffer
2462    unsigned int    found;                // name found in current directory entry
2463
2464    // Three embedded loops:
2465    // - scan pathname to extract file/dir names,
2466    // - for each name, scan the clusters of the parent directory
2467    // - for each cluster, scan the 4 Kbytes buffer to find the file/dir name
2468    // The starting point is the root directory (cluster 2)
2469
2470    nb_read        = 0;
2471    parent_cluster = 2; 
2472
2473    // scan pathname 
2474    while ( pathname[nb_read] != 0 )   
2475    {
2476        // get searched file/dir name
2477        if ( _get_name_from_path( pathname, name, &nb_read ) ) return 1;
2478
2479#if (GIET_DEBUG_FAT & 1)
2480if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2481_printf("\n[DEBUG FAT] _file_info_no_cache(): search name <%s>"
[587]2482        " in cluster %x\n", name , parent_cluster );
2483#endif
2484        found  = 0;
2485
2486        // scan clusters containing the parent directory
2487        while ( found == 0 ) 
2488        {
2489            // compute lba
2490            unsigned int lba = _cluster_to_lba( parent_cluster );
2491
2492            // load one cluster of the parent directory into data_buffer
2493            if ( _fat_buffer_data_lba != lba )
2494            {
2495                if ( _fat_ioc_access( 0,         // no descheduling
2496                                      1,         // read
2497                                      lba,
2498                                      (unsigned int)_fat_buffer_data,
2499                                      8 ) )
2500                {
[664]2501                    _printf("\n[FAT ERROR] _file_info_no_cache(): "
[587]2502                            "cannot load lba = %x into data_buffer\n", lba );
2503                    return 1;
2504                }
2505
2506                _fat_buffer_data_lba = lba;
2507            }
2508
2509            offset = 0;
2510
2511            // scan this 4 Kbytes buffer
2512            while ( (offset < 4096) && (found == 0) )
2513            {
2514                buf  = _fat_buffer_data + offset;
2515                attr = _read_entry( DIR_ATTR , buf , 0 );   
2516                ord  = _read_entry( LDIR_ORD , buf , 0 );
2517
2518                if (ord == NO_MORE_ENTRY)               // no more entry => break
2519                {
2520                    found = 2;
2521                }
2522                else if ( ord == FREE_ENTRY )           // free entry => skip
2523                {
2524                    offset = offset + 32;
2525                }
2526                else if ( attr == ATTR_LONG_NAME_MASK ) // LFN entry => get partial name
2527                {
2528                    unsigned int seq = ord & 0x3;
2529                    lfn = (seq > lfn) ? seq : lfn;   
2530                    if      ( seq == 1 ) _get_name_from_long( buf, lfn1 );
2531                    else if ( seq == 2 ) _get_name_from_long( buf, lfn2 );
2532                    else if ( seq == 3 ) _get_name_from_long( buf, lfn3 );
2533                    offset = offset + 32;
2534                }
2535                else                                    // NORMAL entry
2536                {
2537                    // build the full mame for current directory entry
2538                    if      ( lfn == 0 )
2539                    {
2540                        _get_name_from_short( buf , cname );
2541                    }
2542                    else if ( lfn == 1 )
2543                    {
2544                        _strcpy( cname      , lfn1 );
2545                    }   
2546                    else if ( lfn == 2 ) 
2547                    {
2548                        _strcpy( cname      , lfn1 );
2549                        _strcpy( cname + 13 , lfn2 );
2550                    }
2551                    else if ( lfn == 3 ) 
2552                    {
2553                        _strcpy( cname      , lfn1 );
2554                        _strcpy( cname + 13 , lfn2 );
2555                        _strcpy( cname + 26 , lfn3 );
2556                    }
2557                   
2558                    // test if extracted name == searched name
[619]2559                    if ( _strcmp( name , cname ) == 0 )
[587]2560                    {
2561                        child_cluster = (_read_entry( DIR_FST_CLUS_HI , buf , 1 ) << 16) |
2562                                        (_read_entry( DIR_FST_CLUS_LO , buf , 1 )      ) ;
2563                        child_is_dir  = ((attr & ATTR_DIRECTORY) == ATTR_DIRECTORY);
2564                        child_size    = _read_entry( DIR_FILE_SIZE , buf , 1 );
2565                        found         = 1;
2566                    }
2567                    offset = offset + 32;
2568                    lfn = 0;
2569                }
2570            }  // en loop on directory entries
2571           
2572            // compute next cluster index
2573            unsigned int next;
2574            if ( _next_cluster_no_cache ( parent_cluster , &next ) ) return 1;
2575            parent_cluster = next;
2576        } // end loop on clusters
2577
2578        if ( found == 2 )  // found end of directory => error
2579        { 
[664]2580            _printf("\n[FAT ERROR] _file_info_no_cache(): <%s> not found\n",
[587]2581                    name );
2582            return 1;
2583        }
2584 
2585        // check type
2586        if ( ((pathname[nb_read] == 0) && (child_is_dir != 0)) ||
2587             ((pathname[nb_read] != 0) && (child_is_dir == 0)) )
2588        {
[664]2589            _printf("\n[FAT ERROR] _file_info_no_cache(): illegal type for <%s>\n", name );
[587]2590            return 1;
2591        }
2592
2593        // update parent_cluster for next name
2594        parent_cluster = child_cluster;
2595
2596    }  // end loop on names
2597
2598#if (GIET_DEBUG_FAT & 1)
2599if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2600_printf("\n[DEBUG FAT] _file_info_no_cache(): success for <%s> / "
[587]2601        "file_size = %x / file_cluster = %x\n", pathname, child_size, child_cluster );
2602#endif
2603
2604    // return file cluster and size
2605    *file_size    = child_size;
2606    *file_cluster = child_cluster;
2607    return 0;
2608
2609}  // end _file_info_no_cache()
2610
2611
2612
2613/////////////////////////////////////////////////////////////////////////////
2614/////////////////////////////////////////////////////////////////////////////
2615//             Extern functions                                               
2616/////////////////////////////////////////////////////////////////////////////
2617/////////////////////////////////////////////////////////////////////////////
2618
2619
2620/////////////////////////////////////////////////////////////////////////////
2621// This function initializes the FAT structures.
[674]2622// - The Fat-Descriptor is always initialized.
[587]2623// - The dynamically allocated structures (the Inode-Tre, the Fat_Cache,
2624//   and the File-Cache for the root directory) are only allocated
[674]2625//   and initialized if the "kernel_mode" argument is set.
[587]2626/////////////////////////////////////////////////////////////////////////////
2627// Implementation note:
2628// This function is called twice, by the boot-loader, and by the kernel_init.
2629// It does not use dynamic memory allocation from the distributed heap.
2630// It use informations found in the boot sector and FS-INFO sector.
2631// that are loaded in the Fat-Descriptor temporary block_buffer.
2632/////////////////////////////////////////////////////////////////////////////
[664]2633// Returns GIET_FAT32_OK on success.
2634// Returns a negative value on error:
2635//   GIET_FAT32_IO_ERROR,
[674]2636//   GIET_FAT32_INVALID_BOOT_SECTOR
[587]2637/////////////////////////////////////////////////////////////////////////////
2638int _fat_init( unsigned int kernel_mode ) 
2639{
2640
[258]2641#if GIET_DEBUG_FAT
[569]2642if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2643_printf("\n[DEBUG FAT] _fat_init(): enters at cycle %d\n", _get_proctime() );
[258]2644#endif
[458]2645
2646    // FAT initialisation should be done only once
[674]2647    if ( _fat.initialized == FAT_INITIALIZED )
[458]2648    {
[664]2649        _printf("\n[FAT WARNING] _fat_init(): FAT already initialized\n");
2650        return GIET_FAT32_OK;
[458]2651    }
2652
[587]2653    // load Boot sector (VBR) into FAT buffer
2654    if ( _fat_ioc_access( 0,                                  // no descheduling
2655                          1,                                  // read
2656                          0,                                  // block index
2657                          (unsigned int)_fat.block_buffer,
2658                          1 ) )                               // one block
[258]2659    {
[664]2660        _printf("\n[FAT ERROR] _fat_init(): cannot load VBR\n");
2661        return GIET_FAT32_IO_ERROR;
[258]2662    }
2663
[587]2664    _fat.block_buffer_lba = 0;
2665   
2666#if GIET_DEBUG_FAT
[569]2667if ( _get_proctime() > GIET_DEBUG_FAT )
[587]2668{
[664]2669    _printf("\n[DEBUG FAT] _fat_init(): Boot sector loaded\n");
[587]2670    _display_one_block( _fat.block_buffer, "block device", _fat.block_buffer_lba );
2671}
[258]2672#endif
2673
2674    // checking various FAT32 assuptions from boot sector
[587]2675    if( _read_entry( BPB_BYTSPERSEC, _fat.block_buffer, 1 ) != 512 )
[258]2676    {
[664]2677        _printf("\n[FAT ERROR] _fat_init(): The sector size must be 512 bytes\n");
2678        return GIET_FAT32_INVALID_BOOT_SECTOR;
[258]2679    }
[587]2680    if( _read_entry( BPB_SECPERCLUS, _fat.block_buffer, 1 ) != 8 )
[258]2681    {
[664]2682        _printf("\n[FAT ERROR] _fat_init(): The cluster size must be 8 blocks\n");
2683        return GIET_FAT32_INVALID_BOOT_SECTOR;
[587]2684    }
2685    if( _read_entry( BPB_NUMFATS, _fat.block_buffer, 1 ) != 1 )
2686    {
[664]2687        _printf("\n[FAT ERROR] _fat_init(): The number of FAT copies in FAT region must be 1\n");
2688        return GIET_FAT32_INVALID_BOOT_SECTOR;
[258]2689    }
[587]2690    if( (_read_entry( BPB_FAT32_FATSZ32, _fat.block_buffer, 1 ) & 0xF) != 0 )
[258]2691    {
[664]2692        _printf("\n[FAT ERROR] _fat_init(): The FAT region must be multiple of 16 sectors\n");
2693        return GIET_FAT32_INVALID_BOOT_SECTOR;
[258]2694    }
[587]2695    if( _read_entry( BPB_FAT32_ROOTCLUS, _fat.block_buffer, 1 ) != 2 )
[258]2696    {
[664]2697        _printf("\n[FAT ERROR] _fat_init(): The root directory must be at cluster 2\n");
2698        return GIET_FAT32_INVALID_BOOT_SECTOR;
[258]2699    }
2700
[587]2701    // initialise Fat-Descriptor from VBR
2702    _fat.sector_size         = 512;
2703    _fat.cluster_size        = 4096;
2704    _fat.fat_sectors         = _read_entry( BPB_FAT32_FATSZ32 , _fat.block_buffer , 1 );
2705    _fat.fat_lba             = _read_entry( BPB_RSVDSECCNT , _fat.block_buffer , 1 );
2706    _fat.data_sectors        = _fat.fat_sectors << 10;
[530]2707    _fat.data_lba            = _fat.fat_lba + _fat.fat_sectors;
[587]2708    _fat.fs_info_lba         = _read_entry( BPB_FAT32_FSINFO , _fat.block_buffer , 1 );
2709    _fat_buffer_fat_lba      = 0xFFFFFFFF;
2710    _fat_buffer_data_lba     = 0xFFFFFFFF;
[674]2711    _fat.initialized         = FAT_INITIALIZED;
[258]2712
[587]2713    // load FS_INFO sector into FAT buffer
2714    if ( _fat_ioc_access( 0,                                // no descheduling
2715                          1,                                // read
2716                          _fat.fs_info_lba,                 // lba
2717                          (unsigned int)_fat.block_buffer,
2718                          1 ) )                             // one block
2719    { 
[664]2720        _printf("\n[FAT ERROR] _fat_init(): cannot load FS_INFO Sector\n"); 
2721        return GIET_FAT32_IO_ERROR;
[587]2722    }
[458]2723
[587]2724    _fat.block_buffer_lba = _fat.fs_info_lba;
[258]2725
[291]2726#if GIET_DEBUG_FAT
[569]2727if ( _get_proctime() > GIET_DEBUG_FAT )
[587]2728{
[664]2729    _printf("\n[DEBUG FAT] _fat_init(): FS-INFO sector loaded\n");
[587]2730    _display_one_block( _fat.block_buffer, "block device", _fat.block_buffer_lba );
2731}
[291]2732#endif
2733
[587]2734    // initialise Fat-Descriptor from FS_INFO
2735    _fat.free_clusters_number   = _read_entry( FS_FREE_CLUSTERS    , _fat.block_buffer, 1);
2736    _fat.first_free_cluster     = _read_entry( FS_FREE_CLUSTER_HINT, _fat.block_buffer, 1);
[291]2737
[587]2738    // This is done only when the _fat_init() is called in kernel mode
[291]2739
[587]2740    if ( kernel_mode )
2741    {
[652]2742        unsigned int i;
[587]2743
[652]2744        // create Inode-Tree root
2745        _fat.inode_tree_root = _allocate_one_inode("/", // dir name
2746                                                   1,   // directory
2747                                                   2,   // cluster id
2748                                                   0,   // no size
2749                                                   0,   // no children
2750                                                   0,   // no dentry
2751                                                   1);  // allocate cache
[587]2752
[652]2753        // initialize lock
[587]2754        _spin_lock_init( &_fat.fat_lock );
2755
[652]2756        // initialize File Descriptor Array
2757        for( i = 0 ; i < GIET_OPEN_FILES_MAX ; i++ ) _fat.fd[i].allocated = 0;
[587]2758
[652]2759        // initialize fat_cache root
[650]2760        _fat.fat_cache_root   = _allocate_one_cache_node( NULL );
[587]2761        _fat.fat_cache_levels = _get_levels_from_size( _fat.fat_sectors << 9 );
2762    }  // end if kernel_mode
2763
[291]2764#if GIET_DEBUG_FAT
[569]2765if ( _get_proctime() > GIET_DEBUG_FAT )
[587]2766_display_fat_descriptor();
[291]2767#endif
2768
[664]2769    return GIET_FAT32_OK;
[258]2770}  // end _fat_init()
2771
[587]2772
2773
2774
[258]2775///////////////////////////////////////////////////////////////////////////////
[587]2776// This function implements the giet_fat_open() system call.
2777// The semantic is similar to the UNIX open() function, but only the O_CREATE
2778// and O_RDONLY flags are supported. The UNIX access rights are not supported.
2779// If the file does not exist in the specified directory, it is created.
2780// If the specified directory does not exist, an error is returned.
2781// It allocates a file descriptor to the calling task, for the file identified
2782// by "pathname". If several tasks try to open the same file, each task 
2783// obtains a private file descriptor.
2784// A node name (file or directory) cannot be larger than 31 characters.
[258]2785///////////////////////////////////////////////////////////////////////////////
[664]2786// Returns a file descriptor index on success.
2787// Returns a negative value on error:
2788//   GIET_FAT32_NOT_INITIALIZED,
2789//   GIET_FAT32_FILE_NOT_FOUND,
2790//   GIET_FAT32_NAME_TOO_LONG,
2791//   GIET_FAT32_IO_ERROR,
2792//   GIET_FAT32_TOO_MANY_OPEN_FILES
[258]2793///////////////////////////////////////////////////////////////////////////////
[587]2794int _fat_open( char*        pathname,     // absolute path from root
2795               unsigned int flags )       // O_CREATE and O_RDONLY
[258]2796{
[587]2797    unsigned int         fd_id;            // index in File-Descriptor-Array
2798    unsigned int         code;             // error code
2799    fat_inode_t*         inode;            // anonymous inode pointer
2800    fat_inode_t*         child;            // pointer on searched file inode
2801    fat_inode_t*         parent;           // pointer on parent directory inode
[258]2802   
[587]2803    // get flags
2804    unsigned int create    = ((flags & O_CREATE) != 0);
[654]2805    unsigned int read_only = ((flags & O_RDONLY) != 0);
2806    unsigned int truncate  = ((flags & O_TRUNC)  != 0);
[587]2807
[258]2808#if GIET_DEBUG_FAT
[295]2809unsigned int procid  = _get_procid();
[429]2810unsigned int x       = procid >> (Y_WIDTH + P_WIDTH);
2811unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
2812unsigned int p       = procid & ((1<<P_WIDTH)-1);
[569]2813if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2814_printf("\n[DEBUG FAT] _fat_open(): P[%d,%d,%d] enters for path <%s> / "
[587]2815        " create = %d / read_only = %d\n",
2816        x, y, p, pathname , create , read_only );
[258]2817#endif
2818
[674]2819    // checking FAT initialized
2820    if( _fat.initialized != FAT_INITIALIZED )
[458]2821    {
[664]2822        _printf("\n[FAT ERROR] _fat_open(): FAT not initialized\n");
2823        return GIET_FAT32_NOT_INITIALIZED;
[458]2824    }
[587]2825
[709]2826    // takes the FAT lock and register it in thread context
2827    static_scheduler_t*  psched = _get_sched();
2828    unsigned int         ltid   = _get_thread_ltid();
[530]2829    _spin_lock_acquire( &_fat.fat_lock );
[709]2830    _atomic_or( &psched->context[ltid].slot[CTX_LOCKS_ID] , LOCKS_MASK_FAT ); 
[295]2831
[587]2832    // get inode pointer
2833    code = _get_inode_from_path( pathname , &inode );
2834
2835    if ( code == 2 ) 
[258]2836    {
[587]2837        _spin_lock_release( &_fat.fat_lock );
[709]2838        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
2839
[664]2840        _printf("\n[FAT ERROR] _fat_open(): path to parent not found"
[587]2841                " for file <%s>\n", pathname );
[664]2842        return GIET_FAT32_FILE_NOT_FOUND;
[587]2843    }
2844    else if ( code == 3 ) 
2845    {
2846        _spin_lock_release( &_fat.fat_lock );
[709]2847        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
2848
[664]2849        _printf("\n[FAT ERROR] _fat_open(): one name in path too long"
[587]2850                " for file <%s>\n", pathname );
[664]2851        return GIET_FAT32_NAME_TOO_LONG;
[587]2852    }
2853    else if ( (code == 1) && (create == 0) )   
2854    {
2855        _spin_lock_release( &_fat.fat_lock );
[709]2856        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
2857
[664]2858        _printf("\n[FAT ERROR] _fat_open(): file not found"
[587]2859                " for file <%s>\n", pathname );
[664]2860        return GIET_FAT32_FILE_NOT_FOUND;
[587]2861    }
2862    else if ( (code == 1) && (create != 0) )   // file name not found => create
2863    {
2864        // set parent inode pointer
2865        parent = inode;
[258]2866
2867#if GIET_DEBUG_FAT
[569]2868if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2869_printf("\n[DEBUG FAT] _fat_open(): P[%d,%d,%d] create a new file <%s>\n",
[587]2870        x , y , p , pathname );
[258]2871#endif
[291]2872
[587]2873        // get new file name / error check already done by _get_inode_from_path()
2874        char name[32];       
2875        _get_last_name( pathname , name );
2876
2877        // allocate a new inode and an empty Cache-File
2878        child = _allocate_one_inode( name,
2879                                     0,                         // not a directory
2880                                     END_OF_CHAIN_CLUSTER_MAX,  // no cluster allocated
2881                                     0,                         // size : new file is empty
2882                                     0,                         // count incremented later
2883                                     0,                         // dentry set by add_dir_entry
2884                                     1 );                       // cache_allocate
2885
2886        // introduce inode into Inode-Tree
2887        _add_inode_in_tree( child , parent );
2888
2889        // add an entry in the parent directory Cache_file
2890        if ( _add_dir_entry( child , parent ) )
[258]2891        {
[587]2892            _spin_lock_release( &_fat.fat_lock );
[709]2893            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
2894
[664]2895            _printf("\n[FAT ERROR] _fat_open(): cannot update parent directory"
[587]2896                    " for file <%s>\n" , pathname );
[664]2897            return GIET_FAT32_IO_ERROR;
[587]2898        } 
2899
2900        // update DATA region on block device for parent directory
2901        if ( _update_device_from_cache( parent->levels,
2902                                        parent->cache,
2903                                        parent->name ) )
2904        {
2905            _spin_lock_release( &_fat.fat_lock );
[709]2906            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
2907
[664]2908            _printf("\n[FAT ERROR] _fat_open(): cannot update DATA region "
[587]2909                    " for parent of file <%s>\n", pathname );
[664]2910            return GIET_FAT32_IO_ERROR;
[258]2911        }
2912
[587]2913        // update FAT region on block device
2914        if ( _update_device_from_cache( _fat.fat_cache_levels,
2915                                        _fat.fat_cache_root,
2916                                        "FAT" ) )
[258]2917        {
[587]2918            _spin_lock_release( &_fat.fat_lock );
[709]2919            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
2920
[664]2921            _printf("\n[FAT ERROR] _fat_open(): cannot update FAT region"
[587]2922                    " for file <%s>\n", pathname );
[664]2923            return GIET_FAT32_IO_ERROR;
[258]2924        }
[587]2925
2926        // update FS_INFO sector
2927        if ( _update_fs_info() )
[258]2928        {
[530]2929            _spin_lock_release( &_fat.fat_lock );
[709]2930            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
2931
[664]2932            _printf("\n[FAT ERROR] _fat_open(): cannot update FS-INFO"
[587]2933                    " for file <%s>\n", pathname );
[664]2934            return GIET_FAT32_IO_ERROR;
[258]2935        }
[654]2936
2937        // no need to truncate a new file
2938        truncate = 0;
[258]2939    }
[587]2940    else // code == 0
2941    {
2942        // set searched file inode pointer
2943        child = inode;
[258]2944
[417]2945#if GIET_DEBUG_FAT
[569]2946if ( _get_proctime() > GIET_DEBUG_FAT )
[664]2947_printf("\n[DEBUG FAT] _fat_open(): P[%d,%d,%d] found file <%s> on device : inode = %x\n",
[587]2948        x , y , p , pathname , child );
[417]2949#endif
[587]2950    }
[258]2951
[587]2952    // Search an empty slot in file descriptors array
2953    fd_id = 0;
2954    while ( (_fat.fd[fd_id].allocated) != 0 && (fd_id < GIET_OPEN_FILES_MAX) )
[258]2955    {
[587]2956        fd_id++;
2957    }
2958
2959    // set file descriptor if an empty slot has been found
[654]2960    if ( fd_id >= GIET_OPEN_FILES_MAX )
[587]2961    {
[654]2962        _spin_lock_release( &_fat.fat_lock );
[709]2963        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
2964
[664]2965        _printf("\n[FAT ERROR] _fat_open(): File-Descriptors-Array full\n");
2966        return GIET_FAT32_TOO_MANY_OPEN_FILES;
[654]2967    }
[587]2968
[654]2969    // update file descriptor
2970    _fat.fd[fd_id].allocated  = 1;
2971    _fat.fd[fd_id].seek       = 0;
2972    _fat.fd[fd_id].read_only  = read_only;
2973    _fat.fd[fd_id].inode      = child;
[587]2974
[654]2975    // increment the refcount
2976    child->count = child->count + 1;
[587]2977
[654]2978    // truncate the file if requested
2979    if ( truncate && !read_only && !child->is_dir )
2980    {
2981        // empty file
2982        child->size = 0;
2983        child->levels = _get_levels_from_size( child->size );
2984
2985        // release File-Cache (keep root node)
2986        _release_cache_memory( child->cache, child->levels );
2987
2988        // release clusters allocated to file/dir in DATA region
2989        if ( _clusters_release( child->cluster ) )
2990        {
2991            _spin_lock_release( &_fat.fat_lock );
[709]2992            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
2993
[664]2994            _printf("\n[FAT ERROR] _fat_open(): can't truncate file\n");
2995            return GIET_FAT32_IO_ERROR;
[654]2996        }
2997
2998        // update parent directory entry (size and cluster index)
2999        if ( _update_dir_entry( child ) )
3000        {
3001            _spin_lock_release( &_fat.fat_lock );
[709]3002            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3003
[664]3004            _printf("\n[FAT ERROR] _fat_open(): can't truncate file\n");
3005            return GIET_FAT32_IO_ERROR;
[654]3006        }
3007    }
3008
3009    // releases the lock
3010    _spin_lock_release( &_fat.fat_lock );
[709]3011    _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
[654]3012
[709]3013
[587]3014#if GIET_DEBUG_FAT
3015if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3016_printf("\n[DEBUG FAT] _fat_open(): P[%d,%d,%d] got fd = %d for <%s> / "
[587]3017        "read_only = %d\n",
3018        x , y , p , fd_id , pathname , read_only );
3019#endif
[654]3020    return fd_id;
[587]3021} // end _fat_open()
3022
3023
3024
3025
3026/////////////////////////////////////////////////////////////////////////////////
3027// This function implements the "giet_fat_close()" system call.
3028// It decrements the inode reference count, and release the fd_id entry
3029// in the file descriptors array.
3030// If the reference count is zero, it writes all dirty clusters on block device,
[651]3031// and releases the memory allocated to the File_Cache.
[587]3032/////////////////////////////////////////////////////////////////////////////////
[664]3033// Returns GIET_FAT32_OK on success.
3034// Returns a negative value on error:
3035//   GIET_FAT32_NOT_INITIALIZED,
3036//   GIET_FAT32_INVALID_FD,
3037//   GIET_FAT32_NOT_OPEN,
3038//   GIET_FAT32_IO_ERROR
[587]3039/////////////////////////////////////////////////////////////////////////////////
3040int _fat_close( unsigned int fd_id )
3041{
[674]3042    // checking FAT initialized
3043    if( _fat.initialized != FAT_INITIALIZED )
[587]3044    {
[664]3045        _printf("\n[FAT ERROR] _fat_close(): FAT not initialized\n");
3046        return GIET_FAT32_NOT_INITIALIZED;
[587]3047    }
3048
3049    if( (fd_id >= GIET_OPEN_FILES_MAX) )
3050    {
[664]3051        _printf("\n[FAT ERROR] _fat_close(): illegal file descriptor index\n");
3052        return GIET_FAT32_INVALID_FD;
[587]3053    } 
3054
[709]3055    // takes the FAT lock and register it in thread context
3056    static_scheduler_t*  psched = _get_sched();
3057    unsigned int         ltid   = _get_thread_ltid();
[587]3058    _spin_lock_acquire( &_fat.fat_lock );
[709]3059    _atomic_or( &psched->context[ltid].slot[CTX_LOCKS_ID] , LOCKS_MASK_FAT ); 
[587]3060
[625]3061    if( _fat.fd[fd_id].allocated == 0 )
3062    {
3063        _spin_lock_release( &_fat.fat_lock );
[709]3064        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3065
[664]3066        _printf("\n[FAT ERROR] _fat_close(): file not open\n");
3067        return GIET_FAT32_NOT_OPEN;
[625]3068    }
3069
[587]3070    // get the inode pointer
3071    fat_inode_t*  inode = _fat.fd[fd_id].inode;
3072
3073    // decrement reference count
3074    inode->count = inode->count - 1;
3075   
3076#if GIET_DEBUG_FAT
3077if ( _get_proctime() > GIET_DEBUG_FAT )
3078_printf("\n[FAT DEBUG] _fat_close() for file <%s> : refcount = %d\n",
3079        inode->name , inode->count );
3080#endif
3081
3082    // update block device and release File-Cache if no more references
3083    if ( inode->count == 0 )
3084    {
3085        // update all dirty clusters for closed file
3086        if ( _update_device_from_cache( inode->levels, 
3087                                        inode->cache,
3088                                        inode->name ) ) 
[258]3089        {
[587]3090            _spin_lock_release( &_fat.fat_lock );
[709]3091            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3092
[664]3093            _printf("\n[FAT ERROR] _fat_close(): cannot write dirty clusters "
[587]3094                    "for file <%s>\n", inode->name );
[664]3095            return GIET_FAT32_IO_ERROR;
[258]3096        }
3097
[587]3098#if GIET_DEBUG_FAT
3099if ( _get_proctime() > GIET_DEBUG_FAT )
3100_printf("\n[FAT DEBUG] _fat_close() update device for file <%s>\n", inode->name );
3101#endif
3102
3103        // update directory dirty clusters for parent directory
[624]3104        if ( inode->parent &&
3105             _update_device_from_cache( inode->parent->levels,
[587]3106                                        inode->parent->cache,
[624]3107                                        inode->parent->name ) )
[258]3108        {
[587]3109            _spin_lock_release( &_fat.fat_lock );
[709]3110            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3111
[664]3112            _printf("\n[FAT ERROR] _fat_close(): cannot write dirty clusters "
[587]3113                    "for directory <%s>\n", inode->parent->name );
[664]3114            return GIET_FAT32_IO_ERROR;
[587]3115        }
[258]3116
3117#if GIET_DEBUG_FAT
[569]3118if ( _get_proctime() > GIET_DEBUG_FAT )
[587]3119_printf("\n[FAT DEBUG] _fat_close() update device for parent directory <%s>\n",
3120        inode->parent->name );
[258]3121#endif
3122
[651]3123        // release memory allocated to File-Cache (keep cache root node)
3124        _release_cache_memory( inode->cache, inode->levels );
[587]3125
3126#if GIET_DEBUG_FAT
3127if ( _get_proctime() > GIET_DEBUG_FAT )
3128_printf("\n[FAT DEBUG] _fat_close() release memory for File-Cache <%s>\n",
3129        inode->name );
3130#endif
3131
[258]3132    }
[587]3133
3134    // release fd_id entry in file descriptor array
3135    _fat.fd[fd_id].allocated = 0;
3136
3137    // release lock
3138    _spin_lock_release( &_fat.fat_lock );
[709]3139    _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
[587]3140
[664]3141    return GIET_FAT32_OK;
[587]3142} // end fat_close()
3143
3144
3145
3146
3147/////////////////////////////////////////////////////////////////////////////////
3148// This function implements the giet_fat_file_info() system call.
[623]3149// It returns the size, the current offset and the directory info for a file
3150// identified by the "fd_id" argument.
[587]3151/////////////////////////////////////////////////////////////////////////////////
[664]3152// Returns GIET_FAT32_OK on success.
3153// Returns a negative value on error:
3154//   GIET_FAT32_NOT_INITIALIZED,
3155//   GIET_FAT32_INVALID_FD,
3156//   GIET_FAT32_NOT_OPEN
[587]3157/////////////////////////////////////////////////////////////////////////////////
[658]3158int _fat_file_info( unsigned int     fd_id,
3159                    fat_file_info_t* info )
[587]3160{
[674]3161    if ( _fat.initialized != FAT_INITIALIZED )
[258]3162    {
[664]3163        _printf("\n[FAT ERROR] _fat_file_info(): FAT not initialized\n");
3164        return GIET_FAT32_NOT_INITIALIZED;
[258]3165    }
3166
[623]3167    if ( fd_id >= GIET_OPEN_FILES_MAX )
[587]3168    {
[664]3169        _printf("\n[FAT ERROR] _fat_file_info(): illegal file descriptor index\n");
3170        return GIET_FAT32_INVALID_FD;
[587]3171    } 
[258]3172
[587]3173    if ( _fat.fd[fd_id].allocated == 0 )
3174    {
[664]3175        _printf("\n[FAT ERROR] _fat_file_info(): file not open\n");
3176        return GIET_FAT32_NOT_OPEN;
[258]3177    }
[587]3178
[623]3179    info->size   = _fat.fd[fd_id].inode->size;
3180    info->offset = _fat.fd[fd_id].seek;
3181    info->is_dir = _fat.fd[fd_id].inode->is_dir;
3182
[664]3183    return GIET_FAT32_OK;
[587]3184} // end _fat_file_info()
3185
3186
3187
3188
3189/////////////////////////////////////////////////////////////////////////////////
[709]3190// The following function implements the "giet_fat_read()" system call,
3191// but can also be used by the kernel in physical addressing mode.
3192// It transfers <count> bytes from the File_Cache associated to the file
3193// identified by <fd_id>, to the destination buffer defined by <vaddr>.
3194// It uses the current file offset defined in the file descriptor.
3195// If the <extend> 16 MSB bits are non zero, it uses physical addressing:
3196// the physical  address is computed as extend[15:0] | vaddr[31:0]
[587]3197// In case of miss in the File_Cache, it loads all involved clusters into cache.
3198/////////////////////////////////////////////////////////////////////////////////
[674]3199// Returns the number of bytes actually transfered on success.
[664]3200// Returns a negative value on error:
3201//   GIET_FAT32_NOT_INITIALIZED,
3202//   GIET_FAT32_INVALID_FD,
3203//   GIET_FAT32_NOT_OPEN,
3204//   GIET_FAT32_IO_ERROR
[587]3205/////////////////////////////////////////////////////////////////////////////////
[707]3206int _fat_read( unsigned int fd_id,          // file descriptor index
[709]3207               unsigned int vaddr,          // destination buffer vaddr
[707]3208               unsigned int count,          // number of bytes to read
[709]3209               unsigned int extend )        // physical address extension
[587]3210{
[709]3211
3212#if GIET_DEBUG_FAT
3213unsigned int procid  = _get_procid();
3214unsigned int x       = procid >> (Y_WIDTH + P_WIDTH);
3215unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
3216unsigned int p       = procid & ((1<<P_WIDTH)-1);
3217if ( _get_proctime() > GIET_DEBUG_FAT )
3218_printf("\n[DEBUG FAT] _fat_read(): P[%d,%d,%d] enters at cycle %d\n"
3219        "  fd = %d / vaddr = %x / bytes = %x / extend = %x\n",
3220        x , y , p , _get_proctime(),
3221        fd_id , vaddr , count , extend );
3222#endif
3223
[674]3224    // checking FAT initialized
3225    if( _fat.initialized != FAT_INITIALIZED )
[258]3226    {
[709]3227        _printf("\n[FAT ERROR] _fat_read(): FAT not initialized\n");
[664]3228        return GIET_FAT32_NOT_INITIALIZED;
[258]3229    }
[587]3230
3231    // check fd_id overflow
3232    if ( fd_id >= GIET_OPEN_FILES_MAX )
[258]3233    {
[664]3234        _printf("\n[FAT ERROR] _fat_read(): illegal file descriptor\n");
3235        return GIET_FAT32_INVALID_FD;
[258]3236    }
[358]3237
[587]3238    // check file is open
3239    if ( _fat.fd[fd_id].allocated == 0 )
3240    {
[664]3241        _printf("\n[FAT ERROR] _fat_read(): file not open\n");
3242        return GIET_FAT32_NOT_OPEN;
[587]3243    }
[358]3244
[709]3245    // takes the FAT lock and register it in thread context
3246    static_scheduler_t*  psched = _get_sched();
3247    unsigned int         ltid   = _get_thread_ltid();
[587]3248    _spin_lock_acquire( &_fat.fat_lock );
[709]3249    _atomic_or( &psched->context[ltid].slot[CTX_LOCKS_ID] , LOCKS_MASK_FAT ); 
[587]3250           
3251    // get file inode pointer and offset
3252    fat_inode_t* inode  = _fat.fd[fd_id].inode;
3253    unsigned int seek   = _fat.fd[fd_id].seek;
3254
[730]3255    // check seek versus file size
3256    if ( (seek >= inode->size) && !inode->is_dir )
[258]3257    {
[587]3258        _spin_lock_release( &_fat.fat_lock );
[709]3259        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3260
[730]3261        _printf("\n[FAT ERROR] _fat_read(): seek larger than file size"
3262                " / seek = %x / file_size = %x\n",
3263                seek , inode->size );
3264        return GIET_FAT32_IO_ERROR;
[258]3265    }
3266
[730]3267    // check and ajust count argument
3268    if ( count > (inode->size - seek) ) count = inode->size - seek;
3269
[587]3270    // compute first_cluster_id and first_byte_to_move
3271    unsigned int first_cluster_id   = seek >> 12;
3272    unsigned int first_byte_to_move = seek & 0xFFF;   
[258]3273
[587]3274    // compute last_cluster and last_byte_to_move
3275    unsigned int last_cluster_id   = (seek + count - 1) >> 12;   
3276    unsigned int last_byte_to_move = (seek + count - 1) & 0xFFF;
[258]3277
3278#if GIET_DEBUG_FAT
[569]3279if ( _get_proctime() > GIET_DEBUG_FAT )
[709]3280_printf("\n[DEBUG FAT] _fat_read(): P[%d,%d,%d] search file <%s> with seek = %x\n "
3281        " first_cluster_id = %x / first_byte_to_move = %x"
[587]3282        " / last_cluster_id = %x / last_byte_to_move = %x\n",
[709]3283        x , y , p , inode->name , seek ,
[587]3284        first_cluster_id , first_byte_to_move , last_cluster_id , last_byte_to_move );
[258]3285#endif
3286
[587]3287    // loop on all cluster covering the requested transfer
3288    unsigned int cluster_id;
3289    unsigned int done = 0;
3290    for ( cluster_id = first_cluster_id ; cluster_id <= last_cluster_id ; cluster_id++ )
[258]3291    {
[587]3292        // get pointer on the cluster_id buffer in cache
3293        unsigned char*     cbuf;
3294        fat_cache_desc_t*  pdesc;
3295        if ( _get_buffer_from_cache( inode, 
3296                                     cluster_id,
3297                                     &pdesc ) )
3298        {
3299            _spin_lock_release( &_fat.fat_lock );
[709]3300            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3301
[664]3302            _printf("\n[FAT ERROR] _fat_read(): cannot load file <%s>\n",
[587]3303                    inode->name );
[664]3304            return GIET_FAT32_IO_ERROR;
[587]3305        }
3306        cbuf = pdesc->buffer;
3307
3308#if GIET_DEBUG_FAT
3309if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3310_printf("\n[DEBUG FAT] _fat_read(): P[%d,%d,%d] moves cluster_id %d from Cache-File <%s>\n",
[587]3311        x , y , p , cluster_id, inode->name );
3312#endif
3313
3314        // compute memcpy arguments
3315        unsigned char*  source;
3316        unsigned int    nbytes;
[707]3317
[587]3318        if ( (cluster_id == first_cluster_id) && (cluster_id == last_cluster_id) )
3319        {
3320            source = cbuf + first_byte_to_move; 
3321            nbytes = last_byte_to_move - first_byte_to_move + 1;
3322        }
3323        else if ( cluster_id == first_cluster_id )
3324        {
3325            source = cbuf + first_byte_to_move; 
3326            nbytes = 4096 - first_byte_to_move;
3327        }
3328        else if ( cluster_id == last_cluster_id )
3329        {
3330            source = cbuf; 
3331            nbytes = last_byte_to_move + 1;
3332        }
3333        else  // not first / not last
3334        {
3335            source = cbuf; 
3336            nbytes = 4096;
3337        }
3338
[709]3339        // move data
3340        if ( (extend & 0xFFFF0000) == 0 )         // no physical addressing
[707]3341        {
[709]3342            char* dest = (char*)(vaddr + done);
3343            memcpy( dest , source , nbytes );
[707]3344        }
[709]3345        else                                      // physical addressing required
[707]3346        {
3347            unsigned int flags;
[709]3348            paddr_t pdest    = (((paddr_t)(extend & 0x0000FFFF))<<32) + vaddr + done;
[707]3349            paddr_t psource  = _v2p_translate( (unsigned int)source, &flags );
[709]3350            _physical_memcpy( pdest , psource , nbytes );
[707]3351        }
3352
[587]3353        done = done + nbytes;
[258]3354    }
3355
[587]3356#if GIET_DEBUG_FAT
3357if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3358_printf("\n[DEBUG FAT] _fat_read(): P[%d,%d,%d] loaded file <%s> from Cache-File\n",
[587]3359        x , y , p , inode->name );
3360#endif
[258]3361
[587]3362    // update seek
3363    _fat.fd[fd_id].seek += done;
[258]3364
[587]3365    // release lock
3366    _spin_lock_release( &_fat.fat_lock );
[709]3367    _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
[587]3368
3369    return done;
3370} // end _fat_read()
3371
3372
3373
3374
3375/////////////////////////////////////////////////////////////////////////////////
[709]3376// The following function implements the "giet_fat_write()" system call,
3377// but can also be used by the kernel in physical addressing mode.
3378// It transfers <count> bytes to the File_Cache associated to the file
3379// identified by <fd_id>, from the source buffer defined by <vaddr>.
3380// It uses the current file offset defined in the file descriptor.
3381// If the <extend> 16 MSB bits are non zero, it uses physical addressing:
3382// the physical  address is computed as extend[15:0] | vaddr[31:0]
[587]3383// It increases the file size and allocate new clusters if (count + offset)
3384// is larger than the current file size. Then it loads and updates all
3385// involved clusters in the cache.
3386/////////////////////////////////////////////////////////////////////////////////
[664]3387// Returns number of bytes actually written on success.
3388// Returns a negative value on error:
3389//   GIET_FAT32_NOT_INITIALIZED,
3390//   GIET_FAT32_INVALID_FD,
3391//   GIET_FAT32_NOT_OPEN,
3392//   GIET_FAT32_READ_ONLY,
3393//   GIET_FAT32_NO_FREE_SPACE,
3394//   GIET_FAT32_IO_ERROR
[587]3395/////////////////////////////////////////////////////////////////////////////////
3396int _fat_write( unsigned int fd_id,    // file descriptor index
[709]3397                unsigned int vaddr,    // source buffer vaddr
3398                unsigned int count,    // number of bytes to write
3399                unsigned int extend )  // physical address extension
[587]3400{
[674]3401    // checking FAT initialized
3402    if( _fat.initialized != FAT_INITIALIZED )
[258]3403    {
[674]3404        _printf("\n[FAT ERROR] _fat_write(): FAT not initialized\n");
[664]3405        return GIET_FAT32_NOT_INITIALIZED;
[587]3406    }
[258]3407
[709]3408    // takes the FAT lock and register it in thread context
3409    static_scheduler_t*  psched = _get_sched();
3410    unsigned int         ltid   = _get_thread_ltid();
[587]3411    _spin_lock_acquire( &_fat.fat_lock );
[709]3412    _atomic_or( &psched->context[ltid].slot[CTX_LOCKS_ID] , LOCKS_MASK_FAT ); 
3413
[587]3414           
3415    // check fd_id overflow
3416    if ( fd_id >= GIET_OPEN_FILES_MAX )
3417    {
3418        _spin_lock_release( &_fat.fat_lock );
[709]3419        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3420
[664]3421        _printf("\n[FAT ERROR] _fat_write(): illegal file descriptor\n");
3422        return GIET_FAT32_INVALID_FD;
[587]3423    }
3424
3425    // check file open
3426    if ( _fat.fd[fd_id].allocated == 0 )
3427    {
3428        _spin_lock_release( &_fat.fat_lock );
[709]3429        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3430
[664]3431        _printf("\n[FAT ERROR] _fat_write(): file not open\n" );
3432        return GIET_FAT32_NOT_OPEN;
[587]3433    }
3434
3435    // check file writable
3436    if ( _fat.fd[fd_id].read_only )
3437    {
3438        _spin_lock_release( &_fat.fat_lock );
[709]3439        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3440
[664]3441        _printf("\n[FAT ERROR] _fat_write(): file <%s> is read-only\n",
[587]3442                _fat.fd[fd_id].inode->name );
[664]3443        return GIET_FAT32_READ_ONLY;
[587]3444    }
3445
3446    // get file inode pointer and seek
3447    fat_inode_t* inode  = _fat.fd[fd_id].inode;
3448    unsigned int seek   = _fat.fd[fd_id].seek;
3449
[258]3450#if GIET_DEBUG_FAT
[587]3451unsigned int procid  = _get_procid();
3452unsigned int x       = procid >> (Y_WIDTH + P_WIDTH);
3453unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
3454unsigned int p       = procid & ((1<<P_WIDTH)-1);
[569]3455if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3456_printf("\n[DEBUG FAT] _fat_write(): P[%d,%d,%d] enters for file <%s> "
[587]3457        " / bytes = %x / seek = %x\n",
3458        x , y , p , inode->name , count , seek );
[258]3459#endif
3460
[587]3461    // chek if file size must be incremented
3462    // and allocate new clusters from FAT if required
3463    unsigned int old_size = inode->size;
3464    unsigned int new_size = seek + count;
3465    if ( new_size > old_size )
3466    {
3467        // update size in inode
3468        inode->size = new_size;
3469 
3470        // compute current and required numbers of clusters
3471        unsigned old_clusters = old_size >> 12;
3472        if ( old_size & 0xFFF ) old_clusters++;
3473
3474        unsigned new_clusters = new_size >> 12;
3475        if ( new_size & 0xFFF ) new_clusters++;
3476
3477        // allocate new clusters from FAT if required
3478        if ( new_clusters > old_clusters )
[258]3479        {
[587]3480
3481#if GIET_DEBUG_FAT
3482if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3483_printf("\n[DEBUG FAT] _fat_write(): P[%d,%d,%d] allocates new clusters for file <%s>"
[587]3484        " / current = %d / required = %d\n",
3485        x , y , p , inode->name , old_clusters , new_clusters );
3486#endif
3487            // allocate missing clusters
3488            if ( _clusters_allocate( inode,
3489                                     old_clusters,
3490                                     new_clusters - old_clusters ) )
3491            {
3492                _spin_lock_release( &_fat.fat_lock );
[709]3493                _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3494
[664]3495                _printf("\n[FAT ERROR] _fat_write(): no free clusters"
[587]3496                        " for file <%s>\n", _fat.fd[fd_id].inode->name );
[664]3497                return GIET_FAT32_NO_FREE_SPACE;
[587]3498            }
[258]3499        }
3500         
[654]3501        // update parent directory entry (size and cluster index)
[587]3502        if ( _update_dir_entry( inode ) )
3503        {
3504            _spin_lock_release( &_fat.fat_lock );
[709]3505            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3506
[664]3507            _printf("\n[FAT ERROR] _fat_write(): cannot update parent directory entry"
[587]3508                    " for file <%s>\n", _fat.fd[fd_id].inode->name );
[664]3509            return GIET_FAT32_IO_ERROR;
[587]3510        }
3511           
3512
3513#if GIET_DEBUG_FAT
3514if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3515_printf("\n[DEBUG FAT] _fat_write(): P[%d,%d,%d] updates size for file <%s> / size = %x\n",
[587]3516        x , y , p , inode->name , (new_size - old_size) );
3517#endif
3518
[258]3519    }
3520
[587]3521    // compute first_cluster_id and first_byte_to_move
3522    unsigned int first_cluster_id   = seek >> 12;
3523    unsigned int first_byte_to_move = seek & 0xFFF;   
[258]3524
[587]3525    // compute last_cluster and last_byte_to_move
3526    unsigned int last_cluster_id   = (seek + count - 1) >> 12;   
3527    unsigned int last_byte_to_move = (seek + count - 1) & 0xFFF;
3528
3529#if GIET_DEBUG_FAT
3530if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3531_printf("\n[DEBUG FAT] _fat_write(): P[%d,%d,%d] starts loop on clusters for file <%s>\n"
[587]3532        "  first_cluster_id = %d / first_byte_to_move = %x"
3533        " / last_cluster_id = %d / last_byte_to_move = %x\n",
3534        x , y , p , inode->name ,
3535        first_cluster_id , first_byte_to_move , last_cluster_id , last_byte_to_move );
3536#endif
3537
3538    // loop on all clusters covering the requested transfer
3539    unsigned int cluster_id;
3540    unsigned int done = 0;
3541    for ( cluster_id = first_cluster_id ; cluster_id <= last_cluster_id ; cluster_id++ )
3542    {
3543        // get pointer on one 4K buffer in File-Cache
3544        unsigned char*     cbuf;
3545        fat_cache_desc_t*  pdesc;
3546        if ( _get_buffer_from_cache( inode,   
3547                                     cluster_id, 
3548                                     &pdesc ) )   
3549        {
3550            _spin_lock_release( &_fat.fat_lock );
[709]3551            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3552
[664]3553            _printf("\n[FAT ERROR] _fat_write(): cannot load file <%s>\n",
[587]3554                    inode->name );
[664]3555            return GIET_FAT32_IO_ERROR;
[587]3556        }
3557       
3558        cbuf         = pdesc->buffer;
3559        pdesc->dirty = 1;
3560   
3561#if GIET_DEBUG_FAT
3562if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3563_printf("\n[DEBUG FAT] _fat_write(): P[%d,%d,%d] move cluster_id %d to Cache-file <%s>\n",
[587]3564        x , y , p , cluster_id, inode->name );
3565#endif
3566
3567        // compute memcpy arguments
3568        unsigned char* dest;
3569        unsigned int   nbytes;
3570        if ( (cluster_id == first_cluster_id) && (cluster_id == last_cluster_id) )
3571        {
3572            dest   = cbuf + first_byte_to_move; 
3573            nbytes = last_byte_to_move - first_byte_to_move + 1;
3574        }
3575        else if ( cluster_id == first_cluster_id )
3576        {
3577            dest   = cbuf + first_byte_to_move; 
3578            nbytes = 4096 - first_byte_to_move;
3579        }
3580        else if ( cluster_id == last_cluster_id )
3581        {
3582            dest   = cbuf; 
3583            nbytes = last_byte_to_move + 1;
3584        }
3585        else
3586        {
3587            dest   = cbuf; 
3588            nbytes = 4096;
3589        }
3590
[709]3591        // move data
3592        if ( (extend & 0xFFFF0000) == 0 )     // no physical addressing
3593        {
3594            char* source = (char*)(vaddr + done);
3595            memcpy( dest , source , nbytes ); 
3596        }
3597        else                                  // physical addressing required
3598        {
3599            unsigned int flags;
3600            paddr_t      psource = (((paddr_t)(extend & 0x0000FFFF))<<32) + vaddr + done;
3601            paddr_t      pdest   = _v2p_translate( (unsigned int)dest , &flags );
3602            _physical_memcpy( pdest , psource , nbytes );
3603        }
3604
[587]3605        done = done + nbytes;
3606
3607    } // end for clusters
3608
3609    // update seek
3610    _fat.fd[fd_id].seek += done;
3611
3612#if GIET_DEBUG_FAT
3613if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3614_printf("\n[DEBUG FAT] _fat_write(): P[%d,%d,%d] store file <%s> into Cache-File\n",
[587]3615        x , y , p , inode->name );
3616#endif
3617
3618    // release lock
3619    _spin_lock_release( &_fat.fat_lock );
[709]3620    _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
[587]3621
3622    return done;
3623} // end _fat_write()
3624
3625
3626
3627/////////////////////////////////////////////////////////////////////////////////
3628// The following function implements the "giet_fat_lseek()" system call.
3629// It repositions the seek in the file descriptor "fd_id", according to
3630// the "seek" and "whence" arguments.
3631// It has the same semantic as the UNIX lseek() function.
3632// Accepted values for whence are SEEK_SET and SEEK_CUR.
3633/////////////////////////////////////////////////////////////////////////////////
[664]3634// Returns new seek value (in bytes) on success.
3635// Returns a negative value on error:
3636//   GIET_FAT32_NOT_INITIALIZED,
3637//   GIET_FAT32_INVALID_FD,
3638//   GIET_FAT32_NOT_OPEN,
3639//   GIET_FAT32_INVALID_ARG
[587]3640/////////////////////////////////////////////////////////////////////////////////
3641int _fat_lseek( unsigned int fd_id,
3642                unsigned int seek,
3643                unsigned int whence )
[258]3644{
[674]3645    // checking FAT initialized
3646    if( _fat.initialized != FAT_INITIALIZED )
[587]3647    {
[664]3648        _printf("\n[FAT ERROR] _fat_lseek(): FAT not initialized\n");
3649        return GIET_FAT32_NOT_INITIALIZED;
[587]3650    }
[259]3651
[587]3652    // check fd_id overflow
3653    if ( fd_id >= GIET_OPEN_FILES_MAX )
3654    {
[664]3655        _printf("\n[FAT ERROR] _fat_lseek(): illegal file descriptor\n");
3656        return GIET_FAT32_INVALID_FD;
[587]3657    }
[259]3658
[709]3659    // takes the FAT lock and register it in thread context
3660    static_scheduler_t*  psched = _get_sched();
3661    unsigned int         ltid   = _get_thread_ltid();
[587]3662    _spin_lock_acquire( &_fat.fat_lock );
[709]3663    _atomic_or( &psched->context[ltid].slot[CTX_LOCKS_ID] , LOCKS_MASK_FAT ); 
[259]3664
[709]3665
[587]3666    // check file open
3667    if ( _fat.fd[fd_id].allocated == 0 )
3668    {
3669        _spin_lock_release( &_fat.fat_lock );
[709]3670        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3671
[664]3672        _printf("\n[FAT ERROR] _fat_lseek(): file not open\n");
3673        return GIET_FAT32_NOT_OPEN;
[587]3674    }
[259]3675
[587]3676    unsigned int  new_seek;
[291]3677
[587]3678    // compute new seek
3679    if      ( whence == SEEK_CUR ) new_seek = _fat.fd[fd_id].seek + seek;
3680    else if ( whence == SEEK_SET ) new_seek = seek;
3681    else
3682    {
3683        _spin_lock_release( &_fat.fat_lock );
[709]3684        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3685
[664]3686        _printf("\n[FAT ERROR] _fat_lseek(): illegal whence value\n");
3687        return GIET_FAT32_INVALID_ARG;
[587]3688    }
[291]3689
[587]3690    // update file descriptor offset
3691    _fat.fd[fd_id].seek = new_seek;
[291]3692
[259]3693#if GIET_DEBUG_FAT
[295]3694unsigned int procid  = _get_procid();
[429]3695unsigned int x       = procid >> (Y_WIDTH + P_WIDTH);
3696unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
3697unsigned int p       = procid & ((1<<P_WIDTH)-1);
[569]3698if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3699_printf("\n[DEBUG FAT] _fat_lseek(): P[%d,%d,%d] set seek = %x for file <%s>\n",
[587]3700        x , y , p , new_seek , _fat.fd[fd_id].inode->name );
[259]3701#endif
3702
[587]3703    // release lock
3704    _spin_lock_release( &_fat.fat_lock );
[709]3705    _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
[587]3706
3707    return new_seek;
3708}  // end _fat_lseek()
3709
3710
3711
3712/////////////////////////////////////////////////////////////////////////////////
3713// The following function implements the giet_fat_remove() system call.
3714// It deletes the file/directory identified by the "pathname" argument from
3715// the file system, if the remove condition is fulfilled (directory empty,
3716// or file not referenced).
3717// All clusters allocated in the block device DATA region are released.
3718// The FAT region is updated on the block device.
3719// The Inode-Tree is updated.
3720// The associated File_Cache is released.
3721// The Fat_Cache is updated.
3722/////////////////////////////////////////////////////////////////////////////////
[664]3723// Returns GIET_FAT32_OK on success.
3724// Returns a negative value on error:
3725//   GIET_FAT32_NOT_INITIALIZED,
3726//   GIET_FAT32_FILE_NOT_FOUND,
3727//   GIET_FAT32_NAME_TOO_LONG,
3728//   GIET_FAT32_IS_DIRECTORY,
3729//   GIET_FAT32_NOT_A_DIRECTORY,
3730//   GIET_FAT32_IS_OPEN,
3731//   GIET_FAT32_IO_ERROR,
3732//   GIET_FAT32_DIRECTORY_NOT_EMPTY
[587]3733/////////////////////////////////////////////////////////////////////////////////
3734int _fat_remove( char*        pathname,
3735                 unsigned int should_be_dir )
3736{
3737    fat_inode_t*  inode;            // searched file inode pointer
3738
3739#if GIET_DEBUG_FAT
3740unsigned int procid  = _get_procid();
3741unsigned int x       = procid >> (Y_WIDTH + P_WIDTH);
3742unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
3743unsigned int p       = procid & ((1<<P_WIDTH)-1);
3744if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3745_printf("\n[DEBUG FAT] _fat_remove(): P[%d,%d,%d] enters for path <%s>\n",
[587]3746        x, y, p, pathname );
3747#endif
3748
[674]3749    // checking FAT initialized
3750    if( _fat.initialized != FAT_INITIALIZED )
[587]3751    {
[664]3752        _printf("\n[FAT ERROR] _fat_remove(): FAT not initialized\n");
3753        return GIET_FAT32_NOT_INITIALIZED;
[259]3754    }
[587]3755
[709]3756    // takes the FAT lock and register it in thread context
3757    static_scheduler_t*  psched = _get_sched();
3758    unsigned int         ltid   = _get_thread_ltid();
[587]3759    _spin_lock_acquire( &_fat.fat_lock );
[709]3760    _atomic_or( &psched->context[ltid].slot[CTX_LOCKS_ID] , LOCKS_MASK_FAT ); 
[587]3761
[709]3762
[587]3763    // get searched file inode
3764    unsigned int code = _get_inode_from_path( pathname , &inode );
3765
3766#if GIET_DEBUG_FAT
3767if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3768_printf("\n[DEBUG FAT] _fat_remove(): P[%d,%d,%d] found inode %x for <%s> / code = %d\n",
[587]3769        x , y , p , (unsigned int)inode , pathname , code );
3770#endif
3771
3772    if ( (code == 1) || (code == 2) )
[259]3773    {
[587]3774        _spin_lock_release( &_fat.fat_lock );
[709]3775        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3776
[664]3777        _printf("\n[FAT ERROR] _fat_remove(): file <%s> not found\n", 
[587]3778                pathname );
[664]3779        return GIET_FAT32_FILE_NOT_FOUND;
[259]3780    }
[587]3781    else if ( code == 3 )
[259]3782    {
[587]3783        _spin_lock_release( &_fat.fat_lock );
[709]3784        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3785
[664]3786        _printf("\n[FAT ERROR] _fat_remove(): name too long in <%s>\n",
[587]3787                pathname );
[664]3788        return GIET_FAT32_NAME_TOO_LONG;
[259]3789    }
3790
[587]3791    // check inode type
3792    if ( (inode->is_dir != 0) && (should_be_dir == 0) ) 
[291]3793    {
[587]3794        _spin_lock_release( &_fat.fat_lock );
[709]3795        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3796
[664]3797        _printf("\n[FAT ERROR] _fat_remove(): <%s> is a directory\n",
[587]3798                pathname );
[664]3799        return GIET_FAT32_IS_DIRECTORY;
[587]3800    }
3801    if ( (inode->is_dir == 0) && (should_be_dir != 0) )
3802    {
3803        _spin_lock_release( &_fat.fat_lock );
[709]3804        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3805
[664]3806        _printf("\n[FAT ERROR] _fat_remove(): <%s> is not a directory\n", 
[587]3807                pathname );
[664]3808        return GIET_FAT32_NOT_A_DIRECTORY;
[587]3809    }
3810
3811#if GIET_DEBUG_FAT
3812if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3813_printf("\n[DEBUG FAT] _fat_remove(): P[%d,%d,%d] checked inode type for <%s>\n",
[587]3814        x , y , p , pathname );
3815#endif
3816   
3817    // check references count for a file
3818    if ( (inode->is_dir == 0) && (inode->count != 0) )
3819    {
3820        _spin_lock_release( &_fat.fat_lock );
[709]3821        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3822
[664]3823        _printf("\n[FAT ERROR] _fat_remove(): file <%s> still referenced\n",
[587]3824                pathname );
[664]3825        return GIET_FAT32_IS_OPEN;
[587]3826    }
3827
3828    //  check empty for a directory
3829    if ( inode->is_dir )
3830    {
3831        unsigned int entries;
3832        if ( _get_nb_entries( inode , &entries ) )
[291]3833        {
[587]3834            _spin_lock_release( &_fat.fat_lock );
[709]3835            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3836
[664]3837            _printf("\n[FAT ERROR] _fat_remove(): cannot scan directory <%s>\n", 
[587]3838                    pathname );
[664]3839            return GIET_FAT32_IO_ERROR;
[291]3840        }
[587]3841        else if ( entries > 2 )
3842        {
3843            _spin_lock_release( &_fat.fat_lock );
[709]3844            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3845
[664]3846            _printf("\n[FAT ERROR] _fat_remove(): directory <%s> not empty\n", 
[587]3847                    pathname );
[664]3848            return GIET_FAT32_DIRECTORY_NOT_EMPTY;
[587]3849        }
[291]3850    }
3851
[587]3852#if GIET_DEBUG_FAT
3853if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3854_printf("\n[DEBUG FAT] _fat_remove(): P[%d,%d,%d] checked remove condition OK for <%s>\n",
[587]3855        x , y , p , pathname );
3856#endif
[259]3857   
[587]3858    // remove the file or directory from the file system
3859    if ( _remove_node_from_fs( inode ) )
3860    {
3861        _spin_lock_release( &_fat.fat_lock );
[709]3862        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3863
[664]3864        _printf("\n[FAT ERROR] _fat_remove(): cannot remove <%s> from FS\n",
[587]3865                pathname );
[664]3866        return GIET_FAT32_IO_ERROR;
[587]3867    }
[259]3868
[587]3869    // release lock and return success
3870    _spin_lock_release( &_fat.fat_lock );
[709]3871    _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
[587]3872
[259]3873#if GIET_DEBUG_FAT
[569]3874if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3875_printf("\n[DEBUG FAT] _fat_remove(): P[%d,%d,%d] removed  <%s> from FS\n",
[587]3876        x, y, p, pathname );
[259]3877#endif
[587]3878   
[664]3879    return GIET_FAT32_OK;
[587]3880       
3881}  // end _fat_remove()
[259]3882
[587]3883
3884
3885
3886
3887/////////////////////////////////////////////////////////////////////////////////
3888// This function implements the giet_fat_rename() system call.
3889// It moves an existing file or directory from one node (defined by "old_path"
3890// argument) to another node (defined by "new_path" argument) in the FS tree.
3891// The type (file/directory) and content are not modified.
3892// If the new_path file/dir exist, it is removed from the file system, but only 
3893// if the remove condition is respected (directory empty / file not referenced).
3894// The removed entry is only removed after the new entry is actually created.
3895/////////////////////////////////////////////////////////////////////////////////
[664]3896// Returns GIET_FAT32_OK on success.
3897// Returns a negative value on error:
3898//   GIET_FAT32_NOT_INITIALIZED,
3899//   GIET_FAT32_FILE_NOT_FOUND,
3900//   GIET_FAT32_MOVE_INTO_SUBDIR,
3901//   GIET_FAT32_IO_ERROR,
3902//   GIET_FAT32_DIRECTORY_NOT_EMPTY,
3903//   GIET_FAT32_IS_OPEN
[587]3904/////////////////////////////////////////////////////////////////////////////////
3905int _fat_rename( char*  old_path,
3906                 char*  new_path )
3907{
3908    fat_inode_t*  inode;        // anonymous inode pointer
3909    fat_inode_t*  old;          // inode identified by old_path      => to be deleted
3910    fat_inode_t*  new;          // inode identified by new_path      => to be created
3911    fat_inode_t*  old_parent;   // parent inode  in old_path         => to be modified
3912    fat_inode_t*  new_parent;   // parent inode  in new_path         => to be modified
3913    fat_inode_t*  to_remove;    // previouly identified by new_path  => to be removed
3914    unsigned int  code;
3915
3916#if GIET_DEBUG_FAT
3917unsigned int procid  = _get_procid();
3918unsigned int x       = procid >> (Y_WIDTH + P_WIDTH);
3919unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
3920unsigned int p       = procid & ((1<<P_WIDTH)-1);
3921if ( _get_proctime() > GIET_DEBUG_FAT )
[664]3922_printf("\n[DEBUG FAT] _fat_rename(): P[%d,%d,%d] enters to move <%s> to <%s>\n",
[587]3923        x , y , p , old_path , new_path );
3924#endif
3925
[674]3926    // checking FAT initialized
3927    if( _fat.initialized != FAT_INITIALIZED )
[259]3928    {
[664]3929        _printf("\n[FAT ERROR] _fat_rename(): FAT not initialized\n");
3930        return GIET_FAT32_NOT_INITIALIZED;
[259]3931    }
3932
[709]3933    // takes the FAT lock and register it in thread context
3934    static_scheduler_t*  psched = _get_sched();
3935    unsigned int         ltid   = _get_thread_ltid();
[587]3936    _spin_lock_acquire( &_fat.fat_lock );
[709]3937    _atomic_or( &psched->context[ltid].slot[CTX_LOCKS_ID] , LOCKS_MASK_FAT ); 
[259]3938
[709]3939
[587]3940    // get "old" and "old_parent" inode pointers
3941    if ( _get_inode_from_path( old_path , &inode ) )
3942    {
3943        _spin_lock_release( &_fat.fat_lock );
[709]3944        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3945
[664]3946        _printf("\n[FAT ERROR] _fat_rename(): <%s> not found\n", old_path );
3947        return GIET_FAT32_FILE_NOT_FOUND;
[587]3948    }
3949    else
3950    {
3951        old        = inode;
3952        old_parent = inode->parent;
3953    }
[259]3954
[587]3955    // get "to_removed" and "new_parent" inode pointers
3956    code = _get_inode_from_path( new_path , &inode );
3957
3958    if ( code == 0 )       // new_path inode already exist
[259]3959    {
[621]3960        if ( inode == old )  // the file will replace itself, do nothing
3961        {
3962            _spin_lock_release( &_fat.fat_lock );
[709]3963            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3964
[664]3965            return GIET_FAT32_OK;
[621]3966        }
3967
[587]3968        to_remove        = inode;
[621]3969        new_parent       = inode->parent;
[587]3970    }
3971    else if ( code == 1 )  // to_remove does not exist but parent exist
3972    {
3973        to_remove        = NULL;
3974        new_parent       = inode;
3975    }
3976    else                   // parent directory in new_path not found
3977    {
3978        _spin_lock_release( &_fat.fat_lock );
[709]3979        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3980
[664]3981        _printf("\n[FAT ERROR] _fat_rename(): <%s> not found\n", new_path );
3982        return GIET_FAT32_FILE_NOT_FOUND;
[587]3983    }
[259]3984
[638]3985    // check for move into own subdirectory
3986    if ( _is_ancestor( old, new_parent ) )
3987    {
3988        _spin_lock_release( &_fat.fat_lock );
[709]3989        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
3990
3991        _printf("\n[FAT ERROR] _fat_rename(): can't move %s into  own directory\n", old_path );
[664]3992        return GIET_FAT32_MOVE_INTO_SUBDIR;
[638]3993    }
3994
[259]3995#if GIET_DEBUG_FAT
[569]3996if ( _get_proctime() > GIET_DEBUG_FAT )
[587]3997{
3998if ( to_remove )
[664]3999_printf("\n[DEBUG FAT] _fat_rename(): old_parent = %s / old = %s / new_parent = %s "
[587]4000        "/ to_remove = %s\n",
4001        old_parent->name , old->name , new_parent->name , to_remove->name );
4002else
[664]4003_printf("\n[DEBUG FAT] _fat_rename(): old_parent = %s / old = %s / new_parent = %s "
[587]4004        "/ no remove\n", 
4005        old_parent->name , old->name , new_parent->name );
4006}
[259]4007#endif
4008
[587]4009    // check remove condition for "to_remove" inode
4010    if ( to_remove )
4011    {
4012        if ( to_remove->is_dir )   // it's a directory
[259]4013        {
[587]4014            unsigned int entries;
4015            if ( _get_nb_entries( to_remove , &entries ) )
4016            {
4017                _spin_lock_release( &_fat.fat_lock );
[709]4018                _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4019
[664]4020                _printf("\n[FAT ERROR] _fat_rename(): cannot scan directory <%s>\n", 
[587]4021                        to_remove->name );
[664]4022                return GIET_FAT32_IO_ERROR;
[587]4023            }
4024            else if ( entries > 2 )
4025            {
4026                _spin_lock_release( &_fat.fat_lock );
[709]4027                _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4028
[664]4029                _printf("\n[FAT ERROR] _fat_rename(): directory <%s> not empty\n", 
[587]4030                        to_remove->name );
[664]4031                return GIET_FAT32_DIRECTORY_NOT_EMPTY;
[587]4032            }
[259]4033        }
[587]4034        else                       // it's a file
4035        {
4036            if ( to_remove->count ) 
4037            {
4038                _spin_lock_release( &_fat.fat_lock );
[709]4039                _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4040
[664]4041                _printf("\n[FAT ERROR] _fat_rename(): file <%s> still referenced\n", 
[587]4042                        to_remove->name );
[664]4043                return GIET_FAT32_IS_OPEN;
[587]4044            }
4045        }
[259]4046    }
[291]4047
[587]4048#if GIET_DEBUG_FAT
4049if ( _get_proctime() > GIET_DEBUG_FAT )
[664]4050_printf("\n[FAT DEBUG] _fat_rename(): P[%d,%d,%d] checked remove condition OK\n",
[587]4051        x , y , p );
4052#endif
4053
4054    // get new last name / error checking already done by _get_inode_from_path()
4055    char  new_name[32];
4056    _get_last_name( new_path , new_name );
4057
4058    // allocate "new" inode
4059    new = _allocate_one_inode( new_name,
4060                               old->is_dir,
4061                               old->cluster,
4062                               old->size,
4063                               0,              // count
4064                               0,              // dentry
4065                               0 );            // no cache_allocate
4066 
4067    // give the "old" File-Cache to the "new inode
4068    new->levels = old->levels;
4069    new->cache  = old->cache;
4070
4071    // add "new" to "new_parent" directory File-Cache
4072    if ( _add_dir_entry( new , new_parent ) )
[291]4073    {
[587]4074        _spin_lock_release( &_fat.fat_lock );
[709]4075        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4076
[664]4077        _printf("\n[FAT ERROR] _fat_rename(): cannot add <%s> into <%s>\n",
[587]4078                new->name , new_parent->name );
[664]4079        return GIET_FAT32_IO_ERROR;
[291]4080    }
4081
[587]4082    // add "new" to "new_parent" directory in Inode-Tree
4083    _add_inode_in_tree( new , new_parent );
4084   
4085    // updates "new_parent" directory on device
4086    if ( _update_device_from_cache( new_parent->levels,
4087                                    new_parent->cache,
4088                                    new_parent->name ) )
[291]4089    {
[587]4090        _spin_lock_release( &_fat.fat_lock );
[709]4091        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4092
[664]4093        _printf("\n[FAT ERROR] _fat_rename(): cannot update <%s> on device\n",
[587]4094                    new_parent->name );
[664]4095        return GIET_FAT32_IO_ERROR;
[291]4096    }
[258]4097
[587]4098    // remove "old" from "old_parent" File-Cache
4099    if ( _remove_dir_entry( old ) )
4100    {
4101        _spin_lock_release( &_fat.fat_lock );
[709]4102        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4103
[664]4104        _printf("\n[FAT ERROR] _fat_rename(): cannot remove <%s> from <%s>\n",
[587]4105                old->name , old_parent->name );
[664]4106        return GIET_FAT32_IO_ERROR;
[587]4107    }
4108 
4109    // remove "old" inode from Inode-Tree
4110    _remove_inode_from_tree( old );
4111
[622]4112    // release "old" inode
4113    _free( old );
4114
[587]4115    // updates "old_parent" directory on device
4116    if ( _update_device_from_cache( old_parent->levels,
4117                                    old_parent->cache,
4118                                    old_parent->name ) )
4119    {
4120        _spin_lock_release( &_fat.fat_lock );
[709]4121        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4122
[664]4123        _printf("\n[FAT ERROR] _fat_rename(): cannot update <%s> on device\n",
[587]4124                    old_parent->name );
[664]4125        return GIET_FAT32_IO_ERROR;
[587]4126    }
4127
4128    // remove "to_remove" from File System (if required)
4129    if ( to_remove )
4130    {
4131        if ( _remove_node_from_fs( to_remove ) )
4132        {
4133            _spin_lock_release( &_fat.fat_lock );
[709]4134            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4135
[664]4136            _printf("\n[FAT ERROR] _fat_rename(): cannot remove <%s> from FS\n",
[587]4137                    to_remove->name );
[664]4138            return GIET_FAT32_IO_ERROR;
[587]4139        }
4140    }
4141
4142    // release lock
4143    _spin_lock_release( &_fat.fat_lock );
[709]4144    _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
[587]4145
[664]4146    return GIET_FAT32_OK;
[587]4147}  // end _fat_rename()
4148
4149
4150
4151
[258]4152/////////////////////////////////////////////////////////////////////////////////
[587]4153// The following function implements the giet_fat_mkdir() system call.
4154// It creates in file system the directory specified by the "pathname" argument.
4155// The Inode-Tree is updated.
4156// One cluster is allocated to the new directory.
4157// The associated File-Cache is created.
4158// The FAT region on block device is updated.
4159// The DATA region on block device is updated.
[260]4160/////////////////////////////////////////////////////////////////////////////////
[664]4161// Returns GIET_FAT32_OK on success.
4162// Returns a negative value on error:
4163//   GIET_FAT32_NOT_INITIALIZED,
4164//   GIET_FAT32_FILE_NOT_FOUND,
4165//   GIET_FAT32_NAME_TOO_LONG,
4166//   GIET_FAT32_FILE_EXISTS,
4167//   GIET_FAT32_NO_FREE_SPACE,
4168//   GIET_FAT32_IO_ERROR
[260]4169/////////////////////////////////////////////////////////////////////////////////
[587]4170int _fat_mkdir( char* pathname )
[260]4171{
[587]4172    fat_inode_t*         inode;            // anonymous inode pointer
4173    fat_inode_t*         child;            // searched directory inode pointer
4174    fat_inode_t*         parent;           // parent directory inode pointer
[260]4175
[587]4176#if GIET_DEBUG_FAT
4177unsigned int procid  = _get_procid();
4178unsigned int x       = procid >> (Y_WIDTH + P_WIDTH);
4179unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
4180unsigned int p       = procid & ((1<<P_WIDTH)-1);
4181if ( _get_proctime() > GIET_DEBUG_FAT )
[664]4182_printf("\n[DEBUG FAT] _fat_mkdir(): P[%d,%d,%d] enters for path <%s>\n",
[587]4183        x, y, p, pathname );
4184#endif
4185
[674]4186    // checking FAT initialized
4187    if( _fat.initialized != FAT_INITIALIZED )
[260]4188    {
[664]4189        _printf("\n[FAT ERROR] _fat_mkdir(): FAT not initialized\n");
4190        return GIET_FAT32_NOT_INITIALIZED;
[587]4191    }
[260]4192
[709]4193    // takes the FAT lock and register it in thread context
4194    static_scheduler_t*  psched = _get_sched();
4195    unsigned int         ltid   = _get_thread_ltid();
[587]4196    _spin_lock_acquire( &_fat.fat_lock );
[709]4197    _atomic_or( &psched->context[ltid].slot[CTX_LOCKS_ID] , LOCKS_MASK_FAT ); 
4198
[587]4199    // get inode
4200    unsigned int code = _get_inode_from_path( pathname , &inode );
[260]4201
[587]4202    if ( code == 2 ) 
4203    {
4204        _spin_lock_release( &_fat.fat_lock );
[709]4205        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4206
[664]4207        _printf("\n[FAT ERROR] _fat_mkdir(): path to parent not found"
[587]4208                " for directory <%s>\n", pathname );
[664]4209        return GIET_FAT32_FILE_NOT_FOUND;
[260]4210    }
[587]4211    else if ( code == 3 ) 
[260]4212    {
[587]4213        _spin_lock_release( &_fat.fat_lock );
[709]4214        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4215
[664]4216        _printf("\n[FAT ERROR] _fat_mkdir(): one name in path too long"
[587]4217                " for directory  <%s>\n", pathname );
[664]4218        return GIET_FAT32_NAME_TOO_LONG;
[587]4219    }
4220    else if ( code == 0 )
4221    {
4222        _spin_lock_release( &_fat.fat_lock );
[709]4223        _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4224
[664]4225        _printf("\n[FAT ERROR] _fat_mkdir(): directory <%s> already exist\n",
[587]4226                pathname );
[664]4227        return GIET_FAT32_FILE_EXISTS;
[587]4228    }
4229    else if ( code == 1 )   // directory not found => create
4230    {
4231        parent = inode;
[260]4232
[587]4233#if GIET_DEBUG_FAT
4234if ( _get_proctime() > GIET_DEBUG_FAT )
[664]4235_printf("\n[DEBUG FAT] _fat_mkdir(): P[%d,%d,%d] create new directory <%s>\n",
[587]4236        x , y , p , pathname );
4237#endif
4238
4239        // get directory name / error check already done by _get_inode_from_path()
4240        char name[32];       
4241        _get_last_name( pathname , name );
4242
4243        // allocate one cluster from FAT for the new directory
4244        unsigned int cluster;
4245        if ( _allocate_one_cluster( &cluster ) )
4246        {
4247            _spin_lock_release( &_fat.fat_lock );
[709]4248            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4249
[664]4250            _printf("\n[FAT ERROR] _fat_mkdir(): no free cluster"
[587]4251                    " for directory <%s>\n" , pathname );
[664]4252            return GIET_FAT32_NO_FREE_SPACE;
[587]4253        }
4254
4255        // allocate a new inode and an empty Cache-File
4256        child = _allocate_one_inode( name,
4257                                     1,           // it's a directory
4258                                     cluster, 
4259                                     0,           // size not defined
4260                                     0,           // count
4261                                     0,           // dentry set by _add_dir_entry()
4262                                     1 );         // cache_allocate
4263
4264        // introduce inode in Inode-Tree
4265        _add_inode_in_tree( child , parent );
4266 
4267        // allocate and initialise one 4 Kbytes buffer and associated descriptor
4268        _allocate_one_buffer( child,
4269                              0,            // cluster_id,
4270                              cluster );
4271
4272        _add_special_directories( child, 
4273                                  parent );
4274
4275        // add an entry in the parent directory Cache_file
4276        if ( _add_dir_entry( child , parent ) )
4277        { 
4278            _spin_lock_release( &_fat.fat_lock );
[709]4279            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4280
[664]4281            _printf("\n[FAT ERROR] _fat_mkdir(): cannot update parent directory"
[587]4282                    " for directory <%s>\n" , pathname );
[664]4283            return GIET_FAT32_IO_ERROR;
[587]4284        } 
4285
4286        // update DATA region on block device for parent directory
4287        if ( _update_device_from_cache( parent->levels,
4288                                        parent->cache,
4289                                        parent->name ) )
4290        {
4291            _spin_lock_release( &_fat.fat_lock );
[709]4292            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4293
[664]4294            _printf("\n[FAT ERROR] _fat_mkdir(): cannot update DATA region "
[587]4295                    " for parent of directory <%s>\n", pathname );
[664]4296            return GIET_FAT32_IO_ERROR;
[587]4297        }
4298
4299        // update FAT region on block device
4300        if ( _update_device_from_cache( _fat.fat_cache_levels,
4301                                        _fat.fat_cache_root,
4302                                        "FAT" ) )
4303        {
4304            _spin_lock_release( &_fat.fat_lock );
[709]4305            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4306
[664]4307            _printf("\n[FAT ERROR] _fat_mkdir(): cannot update FAT region"
[587]4308                    " for directory <%s>\n", pathname );
[664]4309            return GIET_FAT32_IO_ERROR;
[587]4310        }
4311
4312        // update FS_INFO sector
4313        if ( _update_fs_info() )
4314        {
4315            _spin_lock_release( &_fat.fat_lock );
[709]4316            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4317
[664]4318            _printf("\n[FAT ERROR] _fat_mkdir(): cannot update FS-INFO"
[587]4319                    " for directory <%s>\n", pathname );
[664]4320            return GIET_FAT32_IO_ERROR;
[587]4321        }
4322
4323        // update DATA region on block device for the new directory
4324        if ( _update_device_from_cache( child->levels,   
4325                                        child->cache,
4326                                        child->name ) )
4327        {
4328            _spin_lock_release( &_fat.fat_lock );
[709]4329            _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
4330
[664]4331            _printf("\n[FAT ERROR] _fat_mkdir(): cannot update DATA region"
[587]4332                    " for directory <%s>\n", pathname );
[664]4333            return GIET_FAT32_IO_ERROR;
[587]4334        }
4335    }  // end create directory
4336
[606]4337    // release lock
4338    _spin_lock_release( &_fat.fat_lock );
[709]4339    _atomic_and( &psched->context[ltid].slot[CTX_LOCKS_ID] , ~LOCKS_MASK_FAT ); 
[606]4340
[664]4341    return GIET_FAT32_OK;
[587]4342}  // end _fat_mkdir()
4343
4344
4345
4346
[658]4347///////////////////////////////////////////////////////////////////////////////
4348// This function implements the giet_fat_opendir() system call.
4349// The semantic is similar to the UNIX opendir() function.
4350// If the specified directory does not exist, an error is returned.
4351// It allocates a file descriptor to the calling task, for the directory
4352// identified by "pathname". If several tasks try to open the same directory,
4353// each task obtains a private file descriptor.
4354// A node name cannot be larger than 31 characters.
4355///////////////////////////////////////////////////////////////////////////////
[664]4356// Returns a file descriptor for the directory index on success
[658]4357// Returns a negative value on error:
[664]4358//   GIET_FAT32_NOT_INITIALIZED,
4359//   GIET_FAT32_NAME_TOO_LONG,
4360//   GIET_FAT32_FILE_NOT_FOUND,
4361//   GIET_FAT32_TOO_MANY_OPEN_FILES,
4362//   GIET_FAT32_NOT_A_DIRECTORY
[658]4363///////////////////////////////////////////////////////////////////////////////
4364extern int _fat_opendir( char* pathname )
4365{
4366    int fd_id = _fat_open( pathname, O_RDONLY );
[587]4367
[658]4368    if ( fd_id < 0 )
4369        return fd_id;
4370
4371    if ( !_fat.fd[fd_id].inode->is_dir )
4372    {
[664]4373        _printf("\n[FAT ERROR] _fat_opendir(): <%s> is not a directory\n",
[658]4374                pathname );
[664]4375        return GIET_FAT32_NOT_A_DIRECTORY;
[658]4376    }
4377
4378    return fd_id;
4379}
4380
4381
4382
4383
[260]4384/////////////////////////////////////////////////////////////////////////////////
[658]4385// This function implements the "giet_fat_closedir()" system call.
4386// Same behavior as _fat_close(), no check for directory.
4387/////////////////////////////////////////////////////////////////////////////////
[664]4388// Returns GIET_FAT32_OK on success.
4389// Returns a negative value on error:
4390//   GIET_FAT32_NOT_INITIALIZED,
4391//   GIET_FAT32_INVALID_FD,
4392//   GIET_FAT32_NOT_OPEN,
4393//   GIET_FAT32_IO_ERROR
[658]4394/////////////////////////////////////////////////////////////////////////////////
4395extern int _fat_closedir( unsigned int fd_id )
4396{
4397    return _fat_close( fd_id );
4398}
4399
4400
4401
4402
4403/////////////////////////////////////////////////////////////////////////////////
4404// This function implements the "giet_fat_readdir()" system call.
4405// It reads one directory entry from the file descriptor opened by
4406// "giet_fat_opendir()" and writes its info to the "entry" argument.
4407// This includes the cluster, size, is_dir and name info for each entry.
4408/////////////////////////////////////////////////////////////////////////////////
[664]4409// Returns GIET_FAT32_OK on success.
[658]4410// Returns a negative value on error:
[664]4411//   GIET_FAT32_NOT_INITIALIZED,
4412//   GIET_FAT32_INVALID_FD,
4413//   GIET_FAT32_NOT_OPEN,
4414//   GIET_FAT32_NOT_A_DIRECTORY,
4415//   GIET_FAT32_IO_ERROR,
4416//   GIET_FAT32_NO_MORE_ENTRIES
[658]4417/////////////////////////////////////////////////////////////////////////////////
4418extern int _fat_readdir( unsigned int  fd_id,
4419                         fat_dirent_t* entry )
4420{
4421    unsigned int  lfn   = 0;            // lfn entries count
4422    unsigned int  attr;                 // ATTR field value
4423    unsigned int  ord;                  // ORD field value
4424    char          lfn1[16];             // temporary buffer for string in LFN1
4425    char          lfn2[16];             // temporary buffer for string in LFN2
4426    char          lfn3[16];             // temporary buffer for string in LFN3
4427    unsigned char buf[DIR_ENTRY_SIZE];  // raw entry buffer
4428    fat_file_info_t info;
4429
4430    // check for directory
4431    int ret = _fat_file_info( fd_id, &info );
4432    if (ret < 0)
4433    {
4434        return ret;
4435    }
4436    else if ( !info.is_dir )
4437    {
[664]4438        _printf("\n[FAT ERROR] _fat_readdir(): not a directory\n" );
4439        return GIET_FAT32_NOT_A_DIRECTORY;
[658]4440    }
4441
4442    while ( 1 )
4443    {
[707]4444        if ( _fat_read( fd_id, (unsigned int)&buf, sizeof(buf), 0 ) != sizeof(buf) )
[658]4445        {
[664]4446            _printf("\n[FAT ERROR] _fat_readdir(): can't read entry\n" );
4447            return GIET_FAT32_IO_ERROR;
[658]4448        }
4449
4450        attr = _read_entry( DIR_ATTR, buf, 0 );
4451        ord  = _read_entry( LDIR_ORD, buf, 0 );
4452
4453        if (ord == NO_MORE_ENTRY)               // no more entry in directory => stop
4454        {
4455            // seek back to this entry
[709]4456            _atomic_increment( &_fat.fd[fd_id].seek , -DIR_ENTRY_SIZE );
[658]4457
[664]4458            return GIET_FAT32_NO_MORE_ENTRIES;
[658]4459        }
4460        else if ( ord == FREE_ENTRY )           // free entry => skip
4461        {
4462            continue;
4463        }
4464        else if ( attr == ATTR_LONG_NAME_MASK ) // LFN entry => get partial names
4465        {
4466            unsigned int seq = ord & 0x3;
4467            lfn = (seq > lfn) ? seq : lfn;
4468            if      ( seq == 1 ) _get_name_from_long( buf, lfn1 );
4469            else if ( seq == 2 ) _get_name_from_long( buf, lfn2 );
4470            else if ( seq == 3 ) _get_name_from_long( buf, lfn3 );
4471            continue;
4472        }
4473        else                                    // NORMAL entry => stop
4474        {
4475            break;
4476        }
4477    }
4478
4479    // TODO handle is_vid
4480    entry->cluster = (_read_entry( DIR_FST_CLUS_HI, buf, 1 ) << 16) |
4481                     (_read_entry( DIR_FST_CLUS_LO, buf, 1 )      ) ;
4482    entry->size    = (_read_entry( DIR_FILE_SIZE  , buf, 1 )      ) ;
4483    entry->is_dir  = ((attr & ATTR_DIRECTORY) == ATTR_DIRECTORY);
4484
4485    if      ( lfn == 0 )
4486    {
4487        _get_name_from_short( buf, entry->name );
4488    }
4489    else if ( lfn == 1 )
4490    {
4491        _strcpy( entry->name     , lfn1 );
4492    }
4493    else if ( lfn == 2 )
4494    {
4495        _strcpy( entry->name     , lfn1 );
4496        _strcpy( entry->name + 13, lfn2 );
4497    }
4498    else if ( lfn == 3 )
4499    {
4500        _strcpy( entry->name     , lfn1 );
4501        _strcpy( entry->name + 13, lfn2 );
4502        _strcpy( entry->name + 26, lfn3 );
4503    }
4504
[664]4505    return GIET_FAT32_OK;
[658]4506}
4507
4508
4509
4510
[587]4511///////////////////////////////////////////////////////////////////////////////
[674]4512// This function loads a file identified by the "pathname" argument into the
[587]4513// memory buffer defined by the "buffer_vbase" and "buffer_size" arguments.
4514// It is intended to be called by the boot-loader, as it does not use the
4515// dynamically allocated FAT structures (Inode-Tree, Fat_Cache or File-Cache,
4516// File-Descriptor-Array).
4517// It uses only the 512 bytes buffer defined in the FAT descriptor.
4518///////////////////////////////////////////////////////////////////////////////
[664]4519// Returns GIET_FAT32_OK on success.
4520// Returns negative value on error:
4521//   GIET_FAT32_NOT_INITIALIZED
4522//   GIET_FAT32_FILE_NOT_FOUND
4523//   GIET_FAT32_BUFFER_TOO_SMALL
4524//   GIET_FAT32_IO_ERROR
[587]4525///////////////////////////////////////////////////////////////////////////////
4526int _fat_load_no_cache( char*        pathname,
4527                        unsigned int buffer_vbase, 
4528                        unsigned int buffer_size ) 
[258]4529{
[674]4530    // checking FAT initialized
4531    if( _fat.initialized != FAT_INITIALIZED )
[587]4532    {
[664]4533        _printf("\n[FAT ERROR] _fat_load_no_cache(): FAT not initialized\n");
4534        return GIET_FAT32_NOT_INITIALIZED;
[587]4535    }
[258]4536
[587]4537    unsigned int  file_size;
4538    unsigned int  cluster;
4539
4540#if GIET_DEBUG_FAT
4541unsigned int procid  = _get_procid();
4542unsigned int x       = procid >> (Y_WIDTH + P_WIDTH);
4543unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1);
4544unsigned int p       = procid & ((1<<P_WIDTH)-1);
4545if ( _get_proctime() > GIET_DEBUG_FAT )
[664]4546_printf("\n[DEBUG FAT] _fat_load_no_cache(): P[%d,%d,%d] enters for file <%s>\n",
[587]4547        x , y , p , pathname );
4548#endif
4549
4550    // get file size, and cluster index in FAT
4551    if ( _file_info_no_cache( pathname,
4552                              &cluster,
4553                              &file_size ) )
4554    {
[664]4555        _printf("\n[FAT ERROR] _fat_load_no_cache(): file <%s> not found\n",
[587]4556        pathname );
[664]4557        return GIET_FAT32_FILE_NOT_FOUND;
[587]4558    }
4559
4560    // check buffer size
4561    if ( file_size > buffer_size )
4562    {
[664]4563        _printf("\n[FAT ERROR] _fat_load_no_cache(): buffer too small : "
[587]4564                "file_size = %x / buffer_size = %x", file_size , buffer_size );
[664]4565        return GIET_FAT32_BUFFER_TOO_SMALL;
[587]4566    }
4567
4568    // compute total number of clusters to read
4569    unsigned int nb_clusters = file_size >> 12;
4570    if ( file_size & 0xFFF ) nb_clusters++;
4571
4572    // initialise buffer address
4573    unsigned int dst = buffer_vbase;
4574
4575    // loop on the clusters containing the file
4576    while ( nb_clusters > 0 )
4577    {
4578        unsigned int lba = _cluster_to_lba( cluster );
4579
4580        if( _fat_ioc_access( 0,         // no descheduling
4581                             1,         // read
4582                             lba, 
4583                             dst, 
4584                             8 ) )      // 8 blocks
4585        {
[664]4586            _printf("\n[FAT ERROR] _fat_load_no_cache(): cannot load lba %x", lba );
4587            return GIET_FAT32_IO_ERROR;
[587]4588        }
4589         
4590
4591        // compute next cluster index
4592        unsigned int next;
4593        if ( _next_cluster_no_cache( cluster , &next ) )
4594        {
[664]4595            _printf("\n[FAT ERROR] _fat_load_no_cache(): cannot get next cluster "
[587]4596                    " for cluster = %x\n", cluster );
[664]4597            return GIET_FAT32_IO_ERROR;
[587]4598        }
4599       
4600        // update variables for next iteration
4601        nb_clusters = nb_clusters - 1;
4602        dst         = dst + 4096;
4603        cluster     = next;
4604    }
4605         
4606#if GIET_DEBUG_FAT
4607if ( _get_proctime() > GIET_DEBUG_FAT )
[664]4608_printf("\n[DEBUG FAT] _fat_load_no_cache(): P[%d,%d,%d] loaded <%s> at vaddr = %x"
[587]4609        " / size = %x\n", x , y , p , pathname , buffer_vbase , file_size );
4610#endif
4611
[664]4612    return GIET_FAT32_OK;
[587]4613}  // end _fat_load_no_cache()
[258]4614
4615
[587]4616
[258]4617// Local Variables:
4618// tab-width: 4
4619// c-basic-offset: 4
4620// c-file-offsets:((innamespace . 0)(inline-open . 0))
4621// indent-tabs-mode: nil
4622// End:
4623// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
4624
Note: See TracBrowser for help on using the repository browser.