Index: trunk/kernel/fs/fatfs.c
===================================================================
--- trunk/kernel/fs/fatfs.c	(revision 568)
+++ trunk/kernel/fs/fatfs.c	(revision 602)
@@ -2,5 +2,5 @@
  * fatfs.c - FATFS file system API implementation.
  *
- * Author    Alain Greiner (2016,2017)
+ * Author    Alain Greiner (2016,2017,2018)
  *
  * Copyright (c) UPMC Sorbonne Universites
@@ -42,8 +42,8 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 
-extern vfs_ctx_t          fs_context[FS_TYPES_NR];   // allocated in vfs.c file
-
-//////////////////////////////////////////////////////////////////////////////////////////
-//              FATFS specific and static functions 
+extern vfs_ctx_t     fs_context[FS_TYPES_NR];   // allocated in kernel_init.c file
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//              FATFS specific static functions 
 //////////////////////////////////////////////////////////////////////////////////////////
 
@@ -89,9 +89,9 @@
     uint32_t res  = 0;
 
-    if ( little_endian)
+    if ( little_endian )
     {
         for( n = size ; n > 0 ; n-- ) res = (res<<8) | buffer[offset+n-1];
     }
-    else
+    else // big_endian
     {
         for( n = 0 ; n < size ; n++ ) res = (res<<8) | buffer[offset+n];
@@ -100,4 +100,33 @@
 
 }  // end fatfs_get_record()
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This function writes one, two, or four bytes from a 32 bits integer to a memory buffer,
+// taking into account endianness.
+//////////////////////////////////////////////////////////////////////////////////////////
+// @ offset        : first byte of record in buffer.
+// @ size          : record length in bytes (1/2/4).
+// @ buffer        : pointer on buffer base.
+// @ little endian : the most significant byte has the highest address when true.
+// @ return the integer value in a 32 bits word.
+//////////////////////////////////////////////////////////////////////////////////////////
+static void fatfs_set_record( uint32_t    offset,
+                              uint32_t    size,
+                              uint8_t   * buffer,
+                              uint32_t    little_endian,
+                              uint32_t    value )
+{
+    uint32_t n;
+
+    if ( little_endian )
+    {
+        for( n = size ; n > 0 ; n-- ) buffer[offset+n-1] = (uint8_t)(value>>((n-1)<<3));
+    }
+    else // big_endian
+    {
+        for( n = 0 ; n < size ; n++ ) buffer[offset+n] = (uint8_t)(value>>((size-1-n)<<3));
+    }
+
+}  // end fatfs_set_record()
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -136,5 +165,6 @@
 
     name[j] = '\0';
-}
+
+}  // fatfs_get_name_from_short()
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -213,5 +243,337 @@
     name[name_offset] = 0;
 
-} // end get_name_from_long()
+} // end fatfs_get_name_from_long()
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This static function analyse the <name> input argument, and returns in other
+// output arguments various informations required to store the name in FATFS directory.
+// The <name> length cannot be larger than 31 characters :
+// - Short name (less than 13 characters) require 1 LFN entry.
+// - Medium names (from 14 to 26 characters require 2 LFN entries.
+// - Large names (up to 31 characters) require 3 LFN entries.
+//////////////////////////////////////////////////////////////////////////////////////////
+// @ name     : [in]  complete directory entry name.
+// @ length   : [out] total number of characters in name.
+// @ nb_lfn   : [out] number of LFN entries required to store the name.
+// @ sfn      : [out] a legal SFN name extracted from name / upper case and 8-3 format.
+// @ checksum : [out] checksum to be stored in SFN.
+// @ returns 0 on success / returns 1 if the name length is larger than 31 characters.
+//////////////////////////////////////////////////////////////////////////////////////////
+static error_t fatfs_name_format( const char  * name,
+                                  uint32_t    * length,
+                                  uint32_t    * nb_lfn,
+                                  char        * sfn,
+                                  uint8_t     * checksum )
+{
+    // compute name length
+    uint32_t name_length = strlen( name );
+    *length = name_length;
+
+    uint32_t suffix_length = 0;
+    uint32_t prefix_length = 0;
+    uint32_t dot_found     = 0;
+    uint32_t i;
+
+    // compute prefix and suffix length
+    // only the last '.' is taken into account
+    for ( i=0 ; i<name_length ; i++ )
+    {
+        if (name[i] == '.' )
+        {
+            if ( dot_found ) 
+            {
+                prefix_length += suffix_length + 1;
+                suffix_length =  0;
+            }
+            else
+            {
+                dot_found = 1;
+            }
+        }
+        else
+        { 
+            if ( dot_found)  suffix_length++;
+            else             prefix_length++;
+        }
+    } 
+
+    // build SFN prefix (8bits)
+    if (prefix_length <= 8)
+    {
+        for( i=0 ; i<8 ; i++)
+        {
+            if ( i<prefix_length ) sfn[i] = to_upper( name[i] );
+            else                   sfn[i] = 0x20;
+        }
+    }
+    else
+    {
+        for( i=0 ; i<6 ; i++)
+        {
+            sfn[i] = to_upper( name[i] );
+        }
+        sfn[6] = 0x7E;
+        sfn[7] = 0x31;
+    }
+
+    // build SFN suffix (3 bits)
+    if ( suffix_length == 0 )
+    {
+        sfn[8]  = 0x20;
+        sfn[9]  = 0x20;
+        sfn[10] = 0x20;
+    }
+    else if ( suffix_length == 1 )
+    {
+        sfn[8]  = to_upper( name[name_length-1] );
+        sfn[9]  = 0x20;
+        sfn[10] = 0x20;
+    }
+    else if ( suffix_length == 2 )
+    {
+        sfn[8]  = to_upper( name[name_length-2] );
+        sfn[9]  = to_upper( name[name_length-1] );
+        sfn[10] = 0x20;
+    }
+    else
+    {
+        sfn[8]  = to_upper( name[name_length-suffix_length] );
+        sfn[9]  = to_upper( name[name_length-suffix_length+1] );
+        sfn[10] = to_upper( name[name_length-suffix_length+2] );
+    }
+
+    // compute 8 bits checksum
+    uint8_t sum = 0;
+    for ( i=0 ; i<11 ; i++ )
+    {
+        sum = (((sum & 0x01)<<7) | ((sum & 0xFE)>>1)) + sfn[i];
+    }
+    *checksum = sum;
+
+    // set nb_lfn and length values
+    if      ( name_length <= 13 )
+    {
+        *nb_lfn  = 1;
+        return 0;
+    }
+    else if ( name_length <= 26 )
+    {
+        *nb_lfn  = 2;
+        return 0;
+    }
+    else if ( name_length <= 31 )
+    {
+        *nb_lfn  = 3;
+        return 0;
+    }
+    else
+    {
+        return 1;
+    }
+}   // end fatfs_name_format()  
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This static function - atomically - decrements "free_clusters", and updates 
+// the "free_cluster_hint" shared variables in the FATFS context in the FAT cluster.
+// It scan the FAT to find the first free slot larger than the <cluster> argument,
+// and set "free_cluster_hint" <= (free - 1).
+//
+// WARNING : The free_lock protecting exclusive access to these variables
+//           must be taken by the calling function. 
+//////////////////////////////////////////////////////////////////////////////////////////
+// @ cluster     : recently allocated cluster index in FAT.
+//////////////////////////////////////////////////////////////////////////////////////////
+static error_t fatfs_free_clusters_decrement( uint32_t  cluster )
+{
+    fatfs_ctx_t * loc_ctx;      // local pointer on local FATFS context
+    fatfs_ctx_t * fat_ctx;      // local pointer on FATFS in cluster containing FAT mapper
+    cxy_t         mapper_cxy;   // cluster identifier for cluster containing FAT mapper
+    xptr_t        mapper_xp;    // extended pointer on FAT mapper
+    xptr_t        hint_xp;      // extended pointer on "free_cluster_hint" shared variable
+    xptr_t        numb_xp;      // extended pointer on "free_clusters" shared variable 
+    uint32_t      numb;         // "free_clusters" variable current value
+    uint32_t      page_id;      // page index in FAT mapper
+    uint32_t      slot_id;      // slot index in one page of FAT (1024 slots per page)
+    uint32_t      page_max;     // max number of pages in FAT mapper
+    xptr_t        page_xp;      // extended pointer on current page in FAT mapper
+    xptr_t        slot_xp;      // extended pointer on current slot in FAT mapper
+
+#if DEBUG_FATFS_FREE_CLUSTERS
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() )
+printk("\n[%s] thread[%x,%x] enter for allocated cluster %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cluster , cycle );
+#endif
+
+    // get local pointer on local FATFS context
+    loc_ctx = fs_context[FS_TYPE_FATFS].extend;
+
+    // get cluster containing FAT mapper
+    mapper_xp  = loc_ctx->fat_mapper_xp;
+    mapper_cxy = GET_CXY( mapper_xp ); 
+
+    // get local pointer on FATFS context in FAT cluster
+    fat_ctx = hal_remote_lpt( XPTR( mapper_cxy , &fs_context[FS_TYPE_FATFS].extend ) );
+
+    // build extended pointers on free_clusters, and free_cluster_hint
+    hint_xp = XPTR( mapper_cxy , &fat_ctx->free_cluster_hint );
+    numb_xp = XPTR( mapper_cxy , &fat_ctx->free_clusters );
+
+    // update "free_clusters"
+    numb = hal_remote_l32( numb_xp );
+    hal_remote_s32( numb_xp , numb - 1 );
+
+    // scan FAT mapper to find the first free slot > cluster
+    // and update "free_cluster_hint" as (free - 1)
+    page_id  = (cluster + 1) >> 10;
+    slot_id  = (cluster + 1) & 0x3FF;
+    page_max = (loc_ctx->fat_sectors_count >> 3);
+
+    // scan FAT mapper / loop on pages
+    while ( page_id < page_max )            
+    {
+        // get current page from mapper
+        page_xp = mapper_remote_get_page( mapper_xp , page_id );
+
+        if( page_xp == XPTR_NULL )
+        {
+            printk("\n[ERROR] in %s : cannot access FAT mapper\n", __FUNCTION__ );
+            return -1;
+        }
+
+        // scan FAT mapper / loop on slots
+        while ( slot_id < 1024 )
+        {
+            // get extended pointer on current slot
+            slot_xp = ppm_page2base( page_xp ) + (slot_id << 2);
+
+            // test FAT slot value 
+            if ( hal_remote_l32( slot_xp ) == FREE_CLUSTER )
+            {
+                // update "free_cluster_hint" <= (free - 1)
+                hal_remote_s32( hint_xp , (page_id << 10) + slot_id - 1 );
+
+#if DEBUG_FATFS_FREE_CLUSTERS 
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() )
+printk("\n[%s] thread[%x,%x] exit / hint %x / free_clusters %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid,
+hal_remote_l32(hint_xp), hal_remote_l32(numb_xp), cycle );
+#endif
+                return 0;
+            }
+
+            // increment  slot_id
+            slot_id++;
+
+        }  // end loop on slots
+
+        // update loop variables
+        page_id++;
+        slot_id = 0;
+
+    }  // end loop on pages
+
+    // return error if no free cluster found 
+    printk("\n[ERROR] in %s : No free cluster found\n", __FUNCTION__ );
+    return -1;
+    
+}  // end fatfs_free_clusters_decrement()
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This static function atomically increments <free_clusters>, and updates 
+// the <free_cluster_hint> shared variables in the FATFS context in the FAT cluster.
+// If the released cluster index is smaller than the current (hint + 1) value, 
+// it set "free_cluster_hint" <= cluster - 1.
+//
+// WARNING : The free_lock protecting exclusive access to these variables
+//           must be taken by the calling function. 
+//////////////////////////////////////////////////////////////////////////////////////////
+// @ cluster     : recently released cluster index in FAT.
+//////////////////////////////////////////////////////////////////////////////////////////
+static void fatfs_free_clusters_increment( uint32_t  cluster )
+{
+    fatfs_ctx_t * loc_ctx;      // local pointer on local FATFS context
+    fatfs_ctx_t * fat_ctx;      // local pointer on FATFS in cluster containing FAT mapper
+    cxy_t         fat_cxy;      // cluster identifier for cluster containing FAT mapper
+    xptr_t        hint_xp;      // extended pointer on "free_cluster_hint" shared variable
+    xptr_t        numb_xp;      // extended pointer on "free_clusters" shared variable 
+    uint32_t      hint;         // "free_cluster_hint" variable current value
+    uint32_t      numb;         // "free_clusters" variable current value
+
+    // get local pointer on local FATFS context
+    loc_ctx = fs_context[FS_TYPE_FATFS].extend;
+
+    // get cluster containing FAT mapper
+    fat_cxy = GET_CXY( loc_ctx->fat_mapper_xp );
+
+    // get local pointer on FATFS context in FAT cluster
+    fat_ctx = hal_remote_lpt( XPTR( fat_cxy , &fs_context[FS_TYPE_FATFS].extend ) );
+
+    // build extended pointers free_lock, free_clusters, and free_cluster_hint
+    hint_xp = XPTR( fat_cxy , &fat_ctx->free_cluster_hint );
+    numb_xp = XPTR( fat_cxy , &fat_ctx->free_clusters );
+
+    // get current value of free_cluster_hint and free_clusters
+    hint = hal_remote_l32( hint_xp );
+    numb = hal_remote_l32( numb_xp );
+
+    // update free_cluster_hint if required
+    if ( cluster < (hint + 1) ) hal_remote_s32( hint_xp , (cluster - 1) );
+
+    // update free_clusters
+    hal_remote_s32( numb_xp , numb + 1 );
+
+#if DEBUG_FATFS_FREE_CLUSTERS 
+thread_t * this = CURRENT_THREAD;
+if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() )
+printk("\n[%s] thread[%x,%x] updates free cluster info : hint %x / number %x\n",
+__FUNCTION__, this->process->pid, this->trdid,
+hal_remote_l32( hint_xp ), hal_remote_l32( numb_xp ) );
+#endif
+
+}  // end fatfs_free_clusters_increment()
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This recursive function is called by the generic function fatfs_release_all_clusters()
+// It release all clusters allocated to a given inode in the FAT mapper.
+// The removal is done in reverse order of the linked list (from last to first).
+// It does NOT update the FS on the IOC device.
+//////////////////////////////////////////////////////////////////////////////////////////
+// @ fat_mapper_xp : extended pointer on FAT mapper.
+// @ cluster       : cluster index in FAT.
+// @ return 0 if success / return -1 if error (cannot access FAT)
+//////////////////////////////////////////////////////////////////////////////////////////
+static error_t fatfs_recursive_release( xptr_t    fat_mapper_xp,
+                                        uint32_t  cluster )
+{
+    uint32_t next;
+
+    // get next cluster from FAT mapper
+    if ( mapper_remote_get_32( fat_mapper_xp , cluster , &next ) ) return -1;
+
+#if (DEBUG_FATFS_RELEASE_INODE & 1)
+thread_t * this = CURRENT_THREAD;
+if ( DEBUG_FATFS_RELEASE_INODE < (uint32_t)hal_get_cycles() )
+printk("\n[%s] thread[%x,%x] access FAT for cluster %x / next %x\n",
+__FUNCTION__, this->process->pid, this->trdid, cluster, next );
+#endif
+
+    if ( next < END_OF_CHAIN_CLUSTER_MIN )  // non terminal case
+    {
+        // call fatfs_recursive_release() on next cluster
+        if ( fatfs_recursive_release( fat_mapper_xp , next ) ) return -1;
+    }       
+
+    // update current cluster in FAT mapper
+    if ( mapper_remote_set_32( fat_mapper_xp, cluster , FREE_CLUSTER ) ) return -1;
+
+    // Update free_cluster_hint and free_clusters in FAT context
+    fatfs_free_clusters_increment( cluster );
+
+    return 0;
+
+}  // end fatfs_recursive_release()
 
 
@@ -220,18 +582,21 @@
 //////////////////////////////////////////////////////////////////////////////////////////
 
-//////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////
 void fatfs_ctx_display( void )
 {
+    // get pointer on local FATFS context
     vfs_ctx_t   * vfs_ctx   = &fs_context[FS_TYPE_FATFS];
     fatfs_ctx_t * fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend;
 
     printk("\n*** FAT context ***\n" 
-           "- fat_sectors      = %d\n"
-           "- sector size      = %d\n"
-           "- cluster size     = %d\n"
-           "- fat_first_lba    = %d\n"
-           "- data_first_lba   = %d\n"
-           "- root_dir_cluster = %d\n"
-           "- mapper_xp        = %l\n",
+           "- fat_sectors       = %d\n"
+           "- sector size       = %d\n"
+           "- cluster size      = %d\n"
+           "- fat_first_lba     = %d\n"
+           "- data_first_lba    = %d\n"
+           "- root_dir_cluster  = %d\n"
+           "- free_clusters     = %d\n"
+           "- free_cluster_hint = %d\n"
+           "- fat_mapper_xp     = %l\n",
            fatfs_ctx->fat_sectors_count,
            fatfs_ctx->bytes_per_sector,
@@ -240,15 +605,57 @@
            fatfs_ctx->cluster_begin_lba,
            fatfs_ctx->root_dir_cluster,
+           fatfs_ctx->free_clusters,
+           fatfs_ctx->free_cluster_hint,
            fatfs_ctx->fat_mapper_xp );
-}
-
-/////////////////////////////////////////////
-error_t fatfs_get_cluster( mapper_t * mapper,
-                           uint32_t   first_cluster_id,
+
+}  // end fatfs_ctx_display()
+
+//////////////////////////////////////////
+void fatfs_display_fat( uint32_t  page_id,
+                        uint32_t  nentries )
+{
+    uint32_t line;
+    uint32_t maxline;
+
+    // copute numner of lines to display
+    maxline = nentries >> 3;
+    if( nentries & 0x7 ) maxline++;
+
+    // get pointer on local FATFS context
+    vfs_ctx_t   * vfs_ctx   = &fs_context[FS_TYPE_FATFS];
+    fatfs_ctx_t * fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend;
+
+    // get extended pointer on FAT mapper
+    xptr_t fat_mapper_xp = fatfs_ctx->fat_mapper_xp;
+
+    // get extended pointer and cluster of FAT mapper requested page 
+    xptr_t     page_xp  = mapper_remote_get_page( fat_mapper_xp , page_id );
+
+    // get extended pointer on requested page base
+    xptr_t     base_xp  = ppm_page2base( page_xp );
+
+    printk("\n***** FAT content / page %d *****\n", page_id );
+    for( line = 0 ; line < maxline ; line++ )
+    {
+        printk("%x : %X | %X | %X | %X | %X | %X | %X | %X\n", (line<<3),
+        hal_remote_l32( base_xp + ((line<<5)      ) ),
+        hal_remote_l32( base_xp + ((line<<5) + 4  ) ),
+        hal_remote_l32( base_xp + ((line<<5) + 8  ) ),
+        hal_remote_l32( base_xp + ((line<<5) + 12 ) ),
+        hal_remote_l32( base_xp + ((line<<5) + 16 ) ),
+        hal_remote_l32( base_xp + ((line<<5) + 20 ) ),
+        hal_remote_l32( base_xp + ((line<<5) + 24 ) ),
+        hal_remote_l32( base_xp + ((line<<5) + 28 ) ) );
+    }
+
+}  // end fatfs_display_fat()
+
+///////////////////////////////////////////////////////
+error_t fatfs_get_cluster( uint32_t   first_cluster_id,
                            uint32_t   searched_page_index,
                            uint32_t * searched_cluster_id )
 {
-    page_t   * current_page_desc;      // pointer on current page descriptor
-    uint32_t * current_page_buffer;    // pointer on current page (array of uint32_t)
+    xptr_t     current_page_xp;        // pointer on current page descriptor
+    uint32_t * buffer;                 // pointer on current page (array of uint32_t)
     uint32_t   current_page_index;     // index of current page in FAT 
     uint32_t   current_page_offset;    // offset of slot in current page
@@ -256,20 +663,25 @@
     uint32_t   next_cluster_id;        // content of current FAT slot
 
-    assert( (searched_page_index > 0) ,
-    "no FAT access required for first page\n");
+assert( (searched_page_index > 0) ,
+"no FAT access required for first page\n");
 
 #if DEBUG_FATFS_GET_CLUSTER
-uint32_t cycle = (uint32_t)hal_get_cycles();
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
 if( DEBUG_FATFS_GET_CLUSTER < cycle )
-printk("\n[DBG] %s : thread %x enter / first_cluster_id %d / searched_index / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, first_cluster_id, searched_page_index, cycle );
-#endif
-
-    // get number of FAT slots per page
-    uint32_t slots_per_page = CONFIG_PPM_PAGE_SIZE >> 2;
-
-    // initialize loop variable
-    current_page_index  = first_cluster_id / slots_per_page;
-    current_page_offset = first_cluster_id % slots_per_page;
+printk("\n[%s] thread[%x,%x] enter / first_cluster_id %d / searched_index / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, first_cluster_id, searched_page_index, cycle );
+#endif
+
+    // get local pointer on local FATFS context
+    fatfs_ctx_t * ctx = fs_context[FS_TYPE_FATFS].extend;
+
+    // get extended pointer and cluster on FAT mapper
+    xptr_t mapper_xp  = ctx->fat_mapper_xp;
+    cxy_t  mapper_cxy = GET_CXY( mapper_xp );
+
+    // initialize loop variable (1024 slots per page)
+    current_page_index  = first_cluster_id >> 10;
+    current_page_offset = first_cluster_id & 0x3FF;
     page_count_in_file  = 0;
     next_cluster_id     = 0xFFFFFFFF;
@@ -279,18 +691,22 @@
     {
         // get pointer on current page descriptor
-        current_page_desc = mapper_get_page( mapper , current_page_index );
-
-        if( current_page_desc == NULL ) return EIO;
+        current_page_xp = mapper_remote_get_page( mapper_xp , current_page_index );
+
+        if( current_page_xp == XPTR_NULL )
+        {
+            // TODO
+            return -1;
+        }
 
         // get pointer on buffer for current page
-        xptr_t base_xp = ppm_page2base( XPTR( local_cxy , current_page_desc ) );
-        current_page_buffer = (uint32_t *)GET_PTR( base_xp );
+        xptr_t base_xp = ppm_page2base( current_page_xp );
+        buffer = (uint32_t *)GET_PTR( base_xp );
 
         // get FAT slot content 
-        next_cluster_id = current_page_buffer[current_page_offset];
+        next_cluster_id = hal_remote_l32( XPTR( mapper_cxy , &buffer[current_page_offset] ) );
 
 #if (DEBUG_FATFS_GET_CLUSTER & 1)
 if( DEBUG_FATFS_GET_CLUSTER < cycle )
-printk("\n[DBG] %s : traverse FAT / current_page_index = %d\n"
+printk("\n[%s] traverse FAT / current_page_index = %d\n"
 "current_page_offset = %d / next_cluster_id = %d\n",
 __FUNCTION__, current_page_index, current_page_offset , next_cluster_id );
@@ -298,16 +714,16 @@
 
         // update loop variables
-        current_page_index  = next_cluster_id / slots_per_page;
-        current_page_offset = next_cluster_id % slots_per_page;
+        current_page_index  = next_cluster_id >> 10;
+        current_page_offset = next_cluster_id & 0x3FF;
         page_count_in_file++;
     }
 
-    if( next_cluster_id == 0xFFFFFFFF ) return EIO;
+    if( next_cluster_id == 0xFFFFFFFF ) return -1;
    
 #if DEBUG_FATFS_GET_CLUSTER
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_FATFS_GET_CLUSTER < cycle )
-printk("\n[DBG] %s : thread %x exit / searched_cluster_id = %d / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, next_cluster_id / cycle );
+printk("\n[%s] thread[%x,%x] exit / searched_cluster_id = %d / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, next_cluster_id / cycle );
 #endif
 
@@ -320,5 +736,5 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////
-// Generic API : the following functions are called by the kernel (VFS)
+// Generic API : the following functions are called by the kernel VFS
 //               and must be defined by all supported file systems.
 ///////////////////////////////////////////////////////////////////////////////////////
@@ -342,13 +758,16 @@
     uint8_t     * buffer;
 
-#if DEBUG_FATFS_INIT
+#if DEBUG_FATFS_CTX_INIT
 uint32_t cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_INIT < cycle )
-printk("\n[DBG] %s : thread %x enter for fatfs_ctx = %x / cycle %d\n",
-__FUNCTION__ , CURRENT_THREAD , fatfs_ctx , cycle );
-#endif
-
-    assert( (fatfs_ctx != NULL) ,
-    "cannot allocate memory for FATFS context\n" );
+if( DEBUG_FATFS_CTX_INIT < cycle )
+printk("\n[%s] thread[%x,%x] enter for fatfs_ctx = %x / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, fatfs_ctx , cycle );
+#endif
+
+// check argument
+assert( (fatfs_ctx != NULL) , "pointer on FATFS context is NULL\n" );
+
+// check only cluster 0 does FATFS init
+assert( (local_cxy == 0) , "only cluster 0 can initialize FATFS\n");
 
     // allocate a 512 bytes buffer to store the boot record
@@ -357,16 +776,21 @@
 	buffer      = (uint8_t *)kmem_alloc( &req );
 
-    assert( (buffer != NULL) ,
-    "cannot allocate memory for 512 bytes buffer\n" );
+    if( buffer == NULL ) 
+    {
+        printk("\n[PANIC] in %s : cannot allocate buffer\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
      
-    // load the boot record from device
-    // using a synchronous access to IOC device  
+    // load the BOOT record from device
     error = dev_ioc_sync_read( buffer , 0 , 1 );
 
-    assert( (error == 0) ,
-    "cannot access boot record\n" );
-
-#if (DEBUG_FATFS_INIT & 0x1)
-if( DEBUG_FATFS_INIT < cycle )
+    if ( error )
+    {
+        printk("\n[PANIC] in %s : cannot access boot record\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
+
+#if (DEBUG_FATFS_CTX_INIT & 0x1)
+if( DEBUG_FATFS_CTX_INIT < cycle )
 {
     uint32_t   line;
@@ -387,36 +811,74 @@
 #endif
 
-    // check sector size from boot record
+    // get sector size from boot record
     uint32_t sector_size = fatfs_get_record( BPB_BYTSPERSEC , buffer , 1 );
-
-    assert( (sector_size == 512) ,
-            "sector size must be 512 bytes\n" );
-
-    // check cluster size from boot record
+    if ( sector_size != 512 )
+    {
+        printk("\n[PANIC] in %s : sector size must be 512 bytes\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
+
+    // get cluster size from boot record
     uint32_t nb_sectors = fatfs_get_record( BPB_SECPERCLUS , buffer , 1 );
-
-    assert( (nb_sectors == 8) ,
-    "cluster size must be 8 sectors\n" );
-
-    // check number of FAT copies from boot record
+    if ( nb_sectors != 8 )
+    {
+        printk("\n[PANIC] in %s : cluster size must be 8 sectors\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
+
+    // get number of FAT copies from boot record
     uint32_t nb_fats = fatfs_get_record( BPB_NUMFATS , buffer , 1 );
-
-    assert( (nb_fats == 1) ,
-    "number of FAT copies must be 1\n" );
-
-    // get & check number of sectors in FAT from boot record
+    if ( nb_fats != 1 )
+    {
+        printk("\n[PANIC] in %s : number of FAT copies must be 1\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
+
+    // get number of sectors in FAT from boot record
     uint32_t fat_sectors = fatfs_get_record( BPB_FAT32_FATSZ32 , buffer , 1 );
-
-    assert( ((fat_sectors & 0xF) == 0) ,
-    "FAT not multiple of 16 sectors\n");
-
-    // get and check root cluster from boot record
+    if ( (fat_sectors & 0xF) != 0 )
+    {
+        printk("\n[PANIC] in %s : FAT size not multiple of 16 sectors\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
+
+    // get root cluster from boot record
     uint32_t root_cluster = fatfs_get_record( BPB_FAT32_ROOTCLUS , buffer , 1 );
-
-    assert( (root_cluster == 2) ,
-    "root cluster index must be  2\n");
+    if ( root_cluster != 2 ) 
+    {
+        printk("\n[PANIC] in %s : root cluster index must be 2\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
 
     // get FAT lba from boot record
     uint32_t fat_lba = fatfs_get_record( BPB_RSVDSECCNT , buffer , 1 );
+
+    // get FS_INFO sector lba from boot record
+    uint32_t fs_info_lba = fatfs_get_record( BPB_FAT32_FSINFO , buffer , 1 );
+
+    // load the FS_INFO record from device
+    error = dev_ioc_sync_read( buffer , fs_info_lba , 1 );
+
+    if ( error )
+    {
+        printk("\n[PANIC] in %s : cannot access FS_INFO record\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
+
+    // get free clusters number from FS_INFO record
+    uint32_t free_clusters = fatfs_get_record( FS_FREE_CLUSTERS , buffer , 1 );
+    if ( free_clusters >= fat_sectors << 7 )
+    {
+        printk("\n[PANIC] in %s : unconsistent free_clusters\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
+
+    // get cluster hint from FS_INFO record
+    uint32_t free_cluster_hint = fatfs_get_record( FS_FREE_CLUSTER_HINT , buffer , 1 );
+    if ( free_cluster_hint >= fat_sectors << 7 )
+    {
+        printk("\n[PANIC] in %s : unconsistent free_cluster_hint\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
 
     // release the 512 bytes buffer
@@ -427,7 +889,9 @@
     // allocate a mapper for the FAT itself
     mapper_t * fat_mapper = mapper_create( FS_TYPE_FATFS );
-
-    assert( (fat_mapper != NULL) ,
-    "no memory for FAT mapper" );
+    if ( fat_mapper == NULL )
+    {
+        printk("\n[PANIC] in %s : no memory for FAT mapper\n", __FUNCTION__ );
+        hal_core_sleep();
+    }
 
     // WARNING : the inode field MUST be NULL for the FAT mapper
@@ -441,13 +905,15 @@
     fatfs_ctx->cluster_begin_lba     = fat_lba + fat_sectors;
     fatfs_ctx->root_dir_cluster      = 2;
-    fatfs_ctx->last_allocated_sector = 0;    // TODO ???
-    fatfs_ctx->last_allocated_index  = 0;    // TODO ???
     fatfs_ctx->fat_mapper_xp         = XPTR( local_cxy , fat_mapper );
-
-#if DEBUG_FATFS_INIT
+    fatfs_ctx->free_clusters         = free_clusters;
+    fatfs_ctx->free_cluster_hint     = free_cluster_hint;
+
+    remote_queuelock_init( XPTR( local_cxy , &fatfs_ctx->free_lock ) , LOCK_FATFS_FREE );
+
+#if DEBUG_FATFS_CTX_INIT
 cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_INIT < cycle )
-printk("\n[DBG] %s : thread %x exit for fatfs_ctx = %x / cycle %d\n",
-__FUNCTION__ , CURRENT_THREAD , fatfs_ctx , cycle );
+if( DEBUG_FATFS_CTX_INIT < cycle )
+printk("\n[%s]  thread[%x,%x] exit for fatfs_ctx = %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, fatfs_ctx, cycle );
 #endif
 
@@ -463,171 +929,456 @@
 }
 
-//////////////////////////////////////////////
-error_t fatfs_mapper_move_page( page_t * page,
-                                bool_t   to_mapper )
+///////////////////////////////////////////////
+error_t fatfs_add_dentry( vfs_inode_t  * inode,
+                          vfs_dentry_t * dentry )
 {
     error_t       error;
-    vfs_inode_t * inode;
-    mapper_t    * mapper;
-    uint32_t      index;       // page index in mapper
-    uint8_t     * buffer;      // page base address in mapper
-    uint32_t      count;       // number of sectors in a page
-    uint32_t      lba;         // block address on device
-    fatfs_ctx_t * fatfs_ctx;   // pointer on local FATFS context
-
-    // get pointer on mapper and page index from page descriptor
-    mapper = page->mapper;
-    index  = page->index;
-
-    // get inode pointer from mapper
-    inode = mapper->inode;
-
-#if DEBUG_FATFS_MOVE
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_MOVE < cycle )
-printk("\n[DBG] %s : thread %x enter / page %d / inode %x / mapper %x / cycle %d\n",
-__FUNCTION__ , CURRENT_THREAD , index , inode , mapper , cycle );
-#endif
-
-    // get page base address
-    xptr_t base_xp = ppm_page2base( XPTR( local_cxy , page ) );
-    buffer = (uint8_t *)GET_PTR( base_xp );
- 
-    // get number of sectors for one page (from FATFS context)
-    fatfs_ctx = (fatfs_ctx_t *)fs_context[FS_TYPE_FATFS].extend;
-    count = fatfs_ctx->sectors_per_cluster;
-
-    // test FAT/normal inode
-    if( inode == NULL )      // it is the FAT mapper
-    {
-        // get lba from page index 
-        lba = fatfs_ctx->fat_begin_lba + (count * index);
- 
-#if (DEBUG_FATFS_MOVE & 0x1)
-if( DEBUG_FATFS_MOVE < cycle )
-printk("\n[DBG] %s : access FAT on device / lba = %d\n", __FUNCTION__ , lba );
-#endif
-
-        // access device
-        if( to_mapper ) error = dev_ioc_sync_read ( buffer , lba , count );
-        else            error = dev_ioc_write( buffer , lba , count );     
-
-        if( error ) return EIO;
-    }
-    else                     // it is a normal inode mapper
-    {
-        uint32_t  searched_cluster_id;
-
-        // get first_cluster_id from inode extension
-        uint32_t  first_cluster_id = (uint32_t)(intptr_t)inode->extend;
-
-        // compute cluster_id
-        if( index == 0 )            // no need to access FAT mapper
+    uint32_t      length;        // dentry name length
+    uint32_t      nb_lfn;        // number or required LFN
+    char          sfn[11];       // buffer for SFN name
+    uint8_t       checksum;      // name checksum 
+    mapper_t    * mapper;        // loal pointer on parent inode mapper
+    xptr_t        mapper_xp;     // extended pointer on parent inode mapper
+    xptr_t        child_xp;      // extended pointer on child inode 
+    cxy_t         child_cxy;     // child inode cluster
+    vfs_inode_t * child_ptr;     // child inode local pointer
+    uint32_t      size;          // child inode size
+    uint32_t      type;          // child inode type
+    uint32_t      cluster;       // child inode cluster index
+
+#if DEBUG_FATFS_ADD_DENTRY
+char       dir_name[CONFIG_VFS_MAX_NAME_LENGTH];
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
+if( DEBUG_FATFS_ADD_DENTRY < cycle )
+printk("\n[%s]  thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
+#endif
+
+// check arguments
+assert( (inode != NULL) , "inode pointer is NULL\n" );
+assert( (dentry != NULL) , "dentry pointer is NULL\n" );
+assert( (inode->type == INODE_TYPE_DIR) , "inode is not a directory\n" );
+assert( (inode->mapper != NULL ) , "mapper pointer is NULL\n" );
+  
+    // get pointers on directory mapper
+    mapper    = inode->mapper;
+    mapper_xp = XPTR( local_cxy , mapper );
+
+    // get extended pointers on remote child inode
+    child_xp  = dentry->child_xp;
+    child_cxy = GET_CXY( child_xp );
+    child_ptr = GET_PTR( child_xp );
+
+    // get relevant infos from child inode
+    type    =                     hal_remote_l32( XPTR( child_cxy , &child_ptr->type ) );
+    size    =                     hal_remote_l32( XPTR( child_cxy , &child_ptr->size ) );
+    cluster = (uint32_t)(intptr_t)hal_remote_lpt( XPTR( child_cxy , &child_ptr->extend ) );
+
+    // analyse dentry name
+    error = fatfs_name_format( dentry->name,
+                               &length,
+                               &nb_lfn,
+                               sfn,
+                               &checksum );
+    if ( error )
+    {
+        printk("\n[ERROR] in %s : dentry name > 31 bytes\n", __FUNCTION__ );
+        return -1;
+    }
+                               
+    // Search end of directory with two embedded loops:
+    // - scan the pages in the mapper
+    // - scan the entries in each page to find NO_MORE_ENTRY
+
+    xptr_t     page_xp;                 // extended pointer on page descriptor
+    xptr_t     base_xp;                 // extended pointer on page base
+    uint8_t  * base;                    // local pointer on page base (array of bytes)
+    uint32_t   page_id = 0;             // page index in mapper
+    uint32_t   offset  = 0;             // position in page
+    uint32_t   found   = 0;             // NO_MORE_ENTRY found
+
+    // loop on pages in mapper
+    while ( found == 0 )
+    {
+        // get extended pointer on page descriptor in mapper 
+        page_xp  = mapper_remote_get_page( mapper_xp , page_id );
+
+        if ( page_xp == XPTR_NULL )
         {
-            searched_cluster_id = first_cluster_id;
+            printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ );
+            return -1;
         }
-        else                        // FAT mapper access required
+        
+        // get pointer on page base
+        base_xp = ppm_page2base( page_xp );
+        base = GET_PTR( base_xp );
+
+        // loop on directory entries in this page
+        while ( (offset < 4096) && (found == 0) )
         {
-            // get cluster and local pointer on FAT mapper
-            xptr_t     fat_mapper_xp  = fatfs_ctx->fat_mapper_xp;
-            cxy_t      fat_mapper_cxy = GET_CXY( fat_mapper_xp );
-            mapper_t * fat_mapper_ptr = (mapper_t *)GET_PTR( fat_mapper_xp );
-
-            // access FAT mapper
-            if( fat_mapper_cxy == local_cxy )    // FAT mapper is local
+            if ( fatfs_get_record( LDIR_ORD, (base + offset), 0 ) == NO_MORE_ENTRY )
             {
-
-#if (DEBUG_FATFS_MOVE & 0x1)
-if( DEBUG_FATFS_MOVE < cycle )
-printk("\n[DBG] %s : access local FAT mapper\n"
-"fat_mapper_cxy = %x / fat_mapper_ptr = %x / first_cluster_id = %d / index = %d\n",
-__FUNCTION__ , fat_mapper_cxy , fat_mapper_ptr , first_cluster_id , index );
-#endif
-                error = fatfs_get_cluster( fat_mapper_ptr,
-                                           first_cluster_id,
-                                           index,
-                                           &searched_cluster_id );
+                found = 1;
+            }  
+            else
+            {
+                offset = offset + 32;
             }
-            else                                 // FAT mapper is remote
+        }  // end loop on entries
+
+        if ( found == 0 )
+        {
+            page_id++;
+            offset = 0;
+        }
+    }  // end loop on pages
+
+    // Modify the directory mapper: depending on the name length,
+    // the new child requires to write (3, 4, or 5) directory entries.
+    // To actually register the new child, we use a 5 steps FSM
+    // (one state per entry to be written), that is traversed as:
+    // LFN3 -> LFN2 -> LFN1 -> NORMAL -> NOMORE 
+    // At most two pages are modified:
+    // - the page containing the NO_MORE_ENTRY is always modified
+    // - the following page can be modified if the name spread on to pages.
+
+    char * name = dentry->name;
+
+    uint32_t step;          // FSM state
+
+    if      ( nb_lfn == 1 ) step = 3;
+    else if ( nb_lfn == 2 ) step = 4;
+    else if ( nb_lfn == 3 ) step = 5;
+    
+    uint8_t  * entry;       // pointer on directory entry to be written
+    uint32_t   i;           // byte index in one 32 bytes directory
+    uint32_t   c;           // character index in name
+
+    while ( step )   
+    {
+        // when the new child is split on two pages,
+        // we need to access a new page in mapper
+        if ( offset >= 4096 )
+        {
+            // copy the modified page to IOC device
+            fatfs_move_page( page_xp , false );   
+
+            // get the next page in FAT mapper
+            page_xp  = mapper_remote_get_page( mapper_xp , page_id + 1 );
+
+            if ( page_xp == XPTR_NULL )
             {
-
-#if (DEBUG_FATFS_MOVE & 0x1)
-if( DEBUG_FATFS_MOVE < cycle )
-printk("\n[DBG] %s : access remote FAT mapper\n"
-"fat_mapper_cxy = %x / fat_mapper_ptr = %x / first_cluster_id = %d / index = %d\n",
-__FUNCTION__ , fat_mapper_cxy , fat_mapper_ptr , first_cluster_id , index );
-#endif
-                rpc_fatfs_get_cluster_client( fat_mapper_cxy,
-                                              fat_mapper_ptr,
-                                              first_cluster_id,
-                                              index,
-                                              &searched_cluster_id,
-                                              &error );
+                printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ );
+                return -1;
             }
-
-            if( error )  return EIO;
+        
+            // get pointer on page base
+            base_xp = ppm_page2base( page_xp );
+            base = GET_PTR( base_xp );
+            
+            // update offset
+            offset = 0;
         }
 
-#if (DEBUG_FATFS_MOVE & 0x1)
-if( DEBUG_FATFS_MOVE < cycle )
-printk("\n[DBG] %s : access device for inode %x / cluster_id %d\n",
-__FUNCTION__ , inode , searched_cluster_id );
-#endif
-
-        // get lba from cluster_id
-        lba = fatfs_lba_from_cluster( fatfs_ctx , searched_cluster_id );
-
-        // access device
-        if( to_mapper ) error = dev_ioc_sync_read ( buffer , lba , count );
-        else            error = dev_ioc_write( buffer , lba , count );     
-
-        if( error ) return EIO;
-    }
-
-#if DEBUG_FATFS_MOVE
+        // compute directory entry address
+        entry = base + offset;
+
+#if (DEBUG_FATFS_ADD_DENTRY & 1)
+if( DEBUG_FATFS_ADD_DENTRY < cycle )
+printk("\n[%s] FSM step = %d / offset = %x / nb_lfn = %d\n",
+__FUNCTION__, step, offset, nb_lfn );
+#endif
+
+        // write 32 bytes (one directory entry) per iteration
+        switch ( step )
+        {
+            case 5:   // write LFN3 entry
+            {
+                c = 26;
+                // scan the 32 bytes in dir_entry
+                for ( i = 0 ; i < 32 ; i++ )
+                {
+                    if (i == 0)
+                    {
+                        if ( nb_lfn == 3) entry[i] = 0x43;
+                        else              entry[i] = 0x03;
+                    }
+                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
+                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
+                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
+                              ( c < length ) )
+                    {
+                                          entry[i] = name[c];
+                                          c++;
+                    }
+                    else if (i == 11)     entry[i] = 0x0F;
+                    else if (i == 13)     entry[i] = checksum;
+                    else                  entry[i] = 0x00;
+                }
+                step--;
+                break;
+            }
+            case 4:   // write LFN2 entry  
+            {
+                c = 13;
+                // scan the 32 bytes in dir_entry
+                for ( i = 0 ; i < 32 ; i++ )
+                {
+                    if (i == 0)
+                    {
+                        if ( nb_lfn == 2) entry[i] = 0x42;
+                        else              entry[i] = 0x02;
+                    }
+                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
+                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
+                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
+                              ( c < length ) )
+                    {
+                                          entry[i] = name[c];
+                                          c++;
+                    }
+                    else if (i == 11)     entry[i] = 0x0F;
+                    else if (i == 13)     entry[i] = checksum;
+                    else                  entry[i] = 0x00;
+                }
+                step--;
+                break;
+            }
+            case 3:   // Write LFN1 entry   
+            {
+                c = 0;
+                // scan the 32 bytes in dir_entry
+                for ( i = 0 ; i < 32 ; i++ )
+                {
+                    if (i == 0)
+                    {
+                        if ( nb_lfn == 1) entry[i] = 0x41;
+                        else              entry[i] = 0x01;
+                    }
+                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
+                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
+                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
+                              ( c < length ) )
+                    {
+                                          entry[i] = name[c];
+                                          c++;
+                    }
+                    else if (i == 11)     entry[i] = 0x0F;
+                    else if (i == 13)     entry[i] = checksum;
+                    else                  entry[i] = 0x00;
+                }
+                step--;
+                break;
+            }
+            case 2:   // write NORMAL entry     
+            {
+                // scan the 32 bytes in dir_entry
+                for ( i = 0 ; i < 32 ; i++ )
+                {
+                    if      ( i < 11 )                              // 8.3 SFN
+                    {
+                                          entry[i] = sfn[i];
+                    }
+                    else if (i == 11)                               // ATTR
+                    {
+                        if (type == INODE_TYPE_DIR)  entry[i] = 0x10;
+                        else                         entry[i] = 0x20;
+                    }
+                    else if (i == 20)     entry[i] = cluster>>16;   // cluster.B2
+                    else if (i == 21)     entry[i] = cluster>>24;   // cluster.B3
+                    else if (i == 26)     entry[i] = cluster>>0;    // cluster.B0
+                    else if (i == 27)     entry[i] = cluster>>8;    // cluster.B1
+                    else if (i == 28)     entry[i] = size>>0;       // size.B0
+                    else if (i == 29)     entry[i] = size>>8;       // size.B1
+                    else if (i == 30)     entry[i] = size>>16;      // size.B2
+                    else if (i == 31)     entry[i] = size>>24;      // size.B3
+                    else                  entry[i] = 0x00;
+                }
+
+                // update the "extend" field in dentry descriptor
+                dentry->extend = (void*)(intptr_t)(((page_id<<12) + offset)>>5);
+
+                step--;
+                break;
+            }
+            case 1:   // write NOMORE entry  
+            {
+                entry [0] = 0x00;
+                step--;
+                break;
+            }
+        } // end switch step
+
+        offset += 32;
+
+    } // exit while     
+
+    // copy the modified page to the IOC device
+    fatfs_move_page( page_xp , false );   
+
+#if DEBUG_FATFS_ADD_DENTRY
 cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_MOVE < cycle )
-printk("\n[DBG] %s : thread %x exit / page %d / inode %x / mapper %x / cycle %d\n",
-__FUNCTION__ , CURRENT_THREAD , index , inode , mapper , cycle );
-#endif
-
-#if (DEBUG_FATFS_MOVE & 0x1)
-if( DEBUG_FATFS_MOVE < cycle )
-{
-    uint32_t * tab = (uint32_t *)buffer;
-    uint32_t line , word;
-    printk("\n***** %s : First 64 words of loaded page\n", __FUNCTION__ );
-    for( line = 0 ; line < 8 ; line++ )
-    {
-        printk("%X : ", line );
-        for( word = 0 ; word < 8 ; word++ ) printk("%X ", tab[(line<<3) + word] );
-        printk("\n");
-    }
-}
+if( DEBUG_FATFS_ADD_DENTRY < cycle )
+printk("\n[%s]  thread[%x,%x] exit / parent %s / child %s / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
 #endif
 
     return 0;
 
-}  // end fatfs_mapper_move_page()
-
-/////////////////////////////////////////////////////
-error_t fatfs_inode_load( vfs_inode_t * parent_inode,
+}  // end fatfs_add_dentry()
+
+//////////////////////////////////////////////////
+error_t fatfs_remove_dentry( vfs_inode_t  * inode,
+                             vfs_dentry_t * dentry )
+{
+    xptr_t     mapper_xp;  // extended pointer on mapper
+    mapper_t * mapper;     // local pointer on mapper
+    xptr_t     page_xp;    // extended pointer on mapper page descriptor
+    xptr_t     base_xp;    // extended pointer on mapper page base
+    uint8_t  * base;       // local pointer on mapper page base
+
+#if DEBUG_FATFS_REMOVE_DENTRY
+char       dir_name[CONFIG_VFS_MAX_NAME_LENGTH];
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
+if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
+printk("\n[%s]  thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
+#endif
+
+// check arguments
+assert( (inode != NULL) , "inode pointer is NULL\n" );
+assert( (dentry != NULL) , "dentry pointer is NULL\n" );
+assert( (inode->type == INODE_TYPE_DIR) , "inode is not a directory\n" );
+assert( (inode->mapper != NULL ) , "mapper pointer is NULL\n" );
+
+    // get pointers on directory mapper
+    mapper    = inode->mapper;
+    mapper_xp = XPTR( local_cxy , mapper );
+
+    // compute number of LFN entries
+    uint32_t nb_lfn;
+    uint32_t name_length = strlen( dentry->name );
+
+    if      ( name_length <= 13 ) nb_lfn  = 1;
+    else if ( name_length <= 26 ) nb_lfn  = 2;
+    else                          nb_lfn  = 3;
+
+    // we must invalidate (2, 3 or 4) 32 bytes entries:
+    // the NORMAL entry (registered in dentry->extend) and all preceding LFN entries
+    // At most two pages are modified:
+    // - the page containing the NORMAL entry is always modified.
+    // - the preceding page is modified when the name spread on two pages. 
+
+    // get 32 bytes directory entry index from dentry->extend 
+    uint32_t  dentry_id  = (uint32_t)(intptr_t)dentry->extend; 
+
+    // get page index and offset in parent directory mapper
+    uint32_t  page_id    = dentry_id >> 7;
+    uint32_t  offset     = (dentry_id & 0x7F)<<5;
+
+    // get extended pointer on page descriptor from parent directory mapper
+    page_xp  = mapper_remote_get_page( mapper_xp , page_id );
+
+    if ( page_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ );
+        return -1;
+    }
+        
+    // get pointers on page base
+    base_xp = ppm_page2base( page_xp );
+    base    = GET_PTR( base_xp );
+
+    // invalidate NORMAL entry in directory cache
+    base[offset] = 0xE5;
+
+    // invalidate LFN entries
+    while ( nb_lfn )
+    {
+        if (offset == 0)  // we must load page (page_id - 1)
+        {
+
+// check page_id 
+assert( (page_id > 0), "page_id and offset cannot be both 0\n" );
+
+            // copy the modified page to the IOC device
+            fatfs_move_page( page_xp , false );   
+
+            // get extended pointer on page descriptor from parent directory mapper
+            page_xp  = mapper_remote_get_page( mapper_xp , page_id );
+
+            if ( page_xp == XPTR_NULL )
+            {
+                printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ );
+                return -1;
+            }
+        
+            // get pointers on page base
+            base_xp = ppm_page2base( page_xp );
+            base    = GET_PTR( base_xp );
+
+            // update offset
+            offset = 4096;
+        }
+
+        offset = offset - 32;
+
+// check for LFN entry
+assert( (fatfs_get_record( DIR_ATTR, base + offset, 0 ) == ATTR_LONG_NAME_MASK ),
+"this directory entry must be a LFN\n");
+
+        // invalidate LFN entry
+        base[offset] = 0xE5;
+
+        nb_lfn--;
+    }     
+
+    // copy the modified page to the IOC device
+    fatfs_move_page( page_xp , false );   
+    
+
+#if DEBUG_FATFS_REMOVE_DENTRY
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
+printk("\n[%s]  thread[%x,%x] exit / parent %s / child %s / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
+#endif
+
+    return 0;
+
+}  // end fatfs_remove_dentry
+
+////////////////////////////////////////////////////////////////
+error_t fatfs_child_init( vfs_inode_t * parent_inode,
                           char        * name,
                           xptr_t        child_inode_xp )
 {
     // Two embedded loops:
-    // - scan the parent mapper pages 
+    // - scan the parent directory mapper pages 
     // - scan the directory entries in each 4 Kbytes page
 
-#if DEBUG_FATFS_LOAD
-uint32_t cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_LOAD < cycle )
-printk("\n[DBG] %s : thread %x enter for child <%s> in parent inode %x / cycle %d\n",
-__FUNCTION__ , CURRENT_THREAD , name , parent_inode , cycle );
-#endif
-
-    mapper_t * mapper = parent_inode->mapper;
-
-    assert( (mapper != NULL) , "parent mapper undefined\n");
+#if DEBUG_FATFS_CHILD_INIT
+char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+vfs_inode_get_name( XPTR( local_cxy , parent_inode ) , parent_name );
+if( DEBUG_FATFS_CHILD_INIT < cycle )
+printk("\n[%s]  thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, name , parent_name , cycle );
+#endif
+
+// check parent_inode and child_inode
+assert( (parent_inode != NULL) , "parent_inode is NULL\n" );
+assert( (child_inode_xp != XPTR_NULL ) , "child_inode is XPTR_NULL\n" );
+
+    mapper_t * mapper    = parent_inode->mapper;
+    xptr_t     mapper_xp = XPTR( local_cxy , mapper );
+
+// check parent mapper
+assert( (mapper != NULL) , "parent mapper is NULL\n");
     
     char       cname[CONFIG_VFS_MAX_NAME_LENGTH];  // name extracter from each directory entry
@@ -636,17 +1387,18 @@
     char       lfn2[16];         // buffer for one partial cname
     char       lfn3[16];         // buffer for one partial cname
-    page_t   * page;             // pointer on current page descriptor
-    uint8_t  * base;             // pointer on current page base
-    uint32_t   offset  = 0;      // byte offset in page
-    uint32_t   index   = 0;      // page index in mapper
+    xptr_t     page_xp;          // extended pointer on page descriptor
+    xptr_t     base_xp;          // extended pointer on page base
+    uint8_t  * base;             // local pointer on page base
     uint32_t   attr;             // directory entry ATTR field
     uint32_t   ord;              // directory entry ORD field
     uint32_t   seq;              // sequence index
-    uint32_t   lfn     = 0;      // LFN entries number
-    uint32_t   size    = 0;      // searched file/dir size (bytes) 
-    uint32_t   cluster = 0;      // searched file/dir cluster index
-    uint32_t   is_dir  = 0;      // searched file/dir type
-    uint32_t   dentry;           // directory entry index 
-    int32_t    found   = 0;      // not found (0) / name found (1) / end of dir (-1)
+    uint32_t   lfn       = 0;    // LFN entries number
+    uint32_t   size      = 0;    // searched file/dir size (bytes) 
+    uint32_t   cluster   = 0;    // searched file/dir cluster index
+    uint32_t   is_dir    = 0;    // searched file/dir type
+    int32_t    found     = 0;    // not found (0) / name found (1) / end of dir (-1)
+    uint32_t   page_id   = 0;    // page index in mapper
+    uint32_t   dentry_id = 0;    // directory entry index 
+    uint32_t   offset    = 0;    // byte offset in page
 
     // scan the parent directory mapper
@@ -654,19 +1406,19 @@
     {
         // get one page
-        page = mapper_get_page( mapper , index );
-
-        assert( (page != NULL) , "bad parent mapper\n");
+        page_xp = mapper_remote_get_page( mapper_xp , page_id );
+
+        if( page_xp == XPTR_NULL) return EIO;
 
         // get page base
-        xptr_t base_xp = ppm_page2base( XPTR( local_cxy , page ) );
-        base = (uint8_t *)GET_PTR( base_xp );
-
-#if (DEBUG_FATFS_LOAD & 0x1)
-if( DEBUG_FATFS_LOAD < cycle )
+        base_xp = ppm_page2base( page_xp );
+        base    = (uint8_t *)GET_PTR( base_xp );
+
+#if (DEBUG_FATFS_CHILD_INIT & 0x1)
+if( DEBUG_FATFS_CHILD_INIT < cycle )
 {
     uint32_t * buf = (uint32_t *)base;
     uint32_t line , word;
-    printk("\n***** %s : First 16 dentries for parent inode %x\n",
-    __FUNCTION__ , parent_inode );
+    printk("\n[%s] First 16 dentries for <%s>\n",
+    __FUNCTION__ , parent_name );
     for( line = 0 ; line < 16 ; line++ )
     {
@@ -728,15 +1480,17 @@
                     cluster = (fatfs_get_record( DIR_FST_CLUS_HI , base + offset , 1 ) << 16) |
                               (fatfs_get_record( DIR_FST_CLUS_LO , base + offset , 1 )      ) ;
-                    dentry  = ((index<<12) + offset)>>5;
-                    is_dir  = ((attr & ATTR_DIRECTORY) == ATTR_DIRECTORY);
-                    size    = fatfs_get_record( DIR_FILE_SIZE , base + offset , 1 );
-                    found   = 1;
+                    dentry_id = ((page_id<<12) + offset)>>5;
+                    is_dir    = ((attr & ATTR_DIRECTORY) == ATTR_DIRECTORY);
+                    size      = fatfs_get_record( DIR_FILE_SIZE , base + offset , 1 );
+                    found     = 1;
                 }
                 offset = offset + 32;
                 lfn    = 0;
             }
-        }  // end loop on directory entries
-        index++;
+        }  // end loop on directory entries in page
+
+        page_id++;
         offset = 0;
+
     }  // end loop on pages
 
@@ -746,34 +1500,627 @@
     {
 
-#if DEBUG_FATFS_LOAD
+#if DEBUG_FATFS_CHILD_INIT
 cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_LOAD < cycle )
-printk("\n[DBG] %s : thread %x exit / child <%s> not found / cycle %d\n",
-__FUNCTION__ , CURRENT_THREAD, name, cycle );
-#endif
-
-        return ENOENT;
-    }
-    else               // found searched child name
-    {
-        // get child inode cluster and local pointer
-        cxy_t         child_cxy = GET_CXY( child_inode_xp );
-        vfs_inode_t * child_ptr = (vfs_inode_t *)GET_PTR( child_inode_xp );
-
-        // update the child inode "type", "size", and "extend" fields
-        vfs_inode_type_t type = (is_dir) ? INODE_TYPE_DIR : INODE_TYPE_FILE;
-
-        hal_remote_s32( XPTR( child_cxy , &child_ptr->type   ) , type );
-        hal_remote_s32( XPTR( child_cxy , &child_ptr->size   ) , size );
-        hal_remote_s32( XPTR( child_cxy , &child_ptr->extend ) , cluster );
-
-#if DEBUG_FATFS_LOAD
+if( DEBUG_FATFS_CHILD_INIT < cycle )
+printk("\n[%s]  thread[%x,%x] exit / child <%s> not found / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, name, cycle );
+#endif
+
+        return -1;
+    }
+
+    // get child inode cluster and local pointer
+    cxy_t          inode_cxy = GET_CXY( child_inode_xp );
+    vfs_inode_t  * inode_ptr = (vfs_inode_t *)GET_PTR( child_inode_xp );
+
+    // get dentry pointers and cluster
+    xptr_t         dentry_xp  = hal_remote_l64( XPTR( inode_cxy , &inode_ptr->parent_xp ) );  
+    vfs_dentry_t * dentry_ptr = GET_PTR( dentry_xp );
+    cxy_t          dentry_cxy = GET_CXY( dentry_xp );
+
+// dentry descriptor must be in same cluster as parent inode
+assert( (dentry_cxy == local_cxy) , "illegal dentry cluster\n" );
+
+    // update the child inode "type", "size", and "extend" fields
+    vfs_inode_type_t type = (is_dir) ? INODE_TYPE_DIR : INODE_TYPE_FILE;
+
+    hal_remote_s32( XPTR( inode_cxy , &inode_ptr->type   ) , type );
+    hal_remote_s32( XPTR( inode_cxy , &inode_ptr->size   ) , size );
+    hal_remote_s32( XPTR( inode_cxy , &inode_ptr->extend ) , cluster );
+
+    // update the dentry "extend" field
+    dentry_ptr->extend = (void *)(intptr_t)dentry_id;
+
+#if DEBUG_FATFS_CHILD_INIT
 cycle = (uint32_t)hal_get_cycles();
-if( DEBUG_FATFS_LOAD < cycle )
-printk("\n[DBG] %s : thread %x exit / child <%s> loaded / cycle %d\n",
-__FUNCTION__ , CURRENT_THREAD, name, cycle );
-#endif
-
-        return 0;
-    }
-}  // end fatfs_inode_load()
+if( DEBUG_FATFS_CHILD_INIT < cycle )
+printk("\n[%s]  thread[%x,%x] exit / child <%s> loaded in <%s> / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, name, parent_name, cycle );
+#endif
+
+    return 0;
+
+}  // end fatfs_child_init()
+
+///////////////////////////////////////////////
+error_t fatfs_sync_inode( vfs_inode_t * inode )
+{
+
+// check inode pointer and cluster index
+assert( (inode != NULL)                  , "inode pointer undefined\n" );
+assert( (inode->mapper != NULL )         , "mapper pointer undefined\n" );
+assert( (inode->type == INODE_TYPE_FILE) , "inode must be a file\n" );     
+
+#if DEBUG_FATFS_SYNC_INODE
+char       name[CONFIG_VFS_MAX_NAME_LENGTH];
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+vfs_inode_get_name( XPTR( local_cxy , inode ) , name );
+if( DEBUG_FATFS_SYNC_INODE < cycle )
+printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
+#endif
+
+    error_t    error;
+    mapper_t * mapper;
+    page_t   * page;
+    uint32_t   page_id;
+
+    // get mapper from inode 
+    mapper = inode->mapper;
+
+    // compute max number of pages in mapper from file size
+    uint32_t size  = inode->size;
+    uint32_t pages = size >> CONFIG_PPM_PAGE_SHIFT;
+    if( size & CONFIG_PPM_PAGE_MASK ) pages++; 
+         
+    // get pointer on mapper radix tree
+    grdxt_t * rt = &mapper->rt;
+
+    // scan all pages
+    for( page_id = 0 ; page_id < pages ; page_id++ )
+    {
+        // get page descriptor from mapper
+        page = grdxt_lookup( rt , page_id );
+
+        // check all existing pages
+        if ( page != NULL )
+        {
+            if ( page->flags & PG_DIRTY )
+            {
+
+#if (DEBUG_FATFS_SYNC_INODE & 1)
+if( DEBUG_FATFS_SYNC_INODE < cycle )
+printk("\n[%s] thread[%x,%x] synchronizes page %d from <%s> mapper to IOC device\n",
+__FUNCTION__, page_id, name );
+#endif
+                // build extended pointer on page descriptor
+                xptr_t page_xp = XPTR( local_cxy , page );
+
+                // move page from mapper to device
+                error = fatfs_move_page( page_xp , false );
+
+                if ( error )  return -1;
+
+                // reset page dirty flag
+                ppm_page_undo_dirty( page_xp );
+            }
+        }
+    }  // end loop on pages
+
+#if DEBUG_FATFS_SYNC_INODE
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_SYNC_INODE < cycle )
+printk("\n[%s] thread[%x,%x] exit for <%s> / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
+#endif
+
+    return 0;
+
+}  // end fatfs_sync_inode()
+
+//////////////////////////////
+error_t fatfs_sync_fat( void )
+{
+
+#if DEBUG_FATFS_SYNC_FAT
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+if( DEBUG_FATFS_SYNC_FAT < cycle )
+printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, cycle );
+#endif
+
+    uint32_t   page_id;
+    error_t    error;
+
+    // get FAT mapper pointers an cluster 
+    fatfs_ctx_t * fatfs_ctx  = fs_context[FS_TYPE_FATFS].extend;
+    xptr_t        mapper_xp  = fatfs_ctx->fat_mapper_xp;
+    cxy_t         mapper_cxy = GET_CXY( mapper_xp );
+    mapper_t    * mapper_ptr = GET_PTR( mapper_xp );
+
+    // compute max number of 4 Kbytes pages in FAT mapper 
+    // TODO : this could be improved (see fatfs.h) [AG]
+    uint32_t   pages = fatfs_ctx->fat_sectors_count >> 3;
+         
+    // get pointers on remote FAT mapper radix tree
+    grdxt_t  * rt_ptr = &mapper_ptr->rt;
+    xptr_t     rt_xp  = XPTR( mapper_cxy , rt_ptr );
+
+    // scan all pages
+    for( page_id = 0 ; page_id < pages ; page_id++ )
+    {
+        // get extended pointer on page descriptor from FAT mapper
+        xptr_t page_xp = grdxt_remote_lookup( rt_xp , page_id );
+
+        // check all existing pages
+        if ( page_xp != XPTR_NULL )
+        {
+            page_t * page_ptr = GET_PTR( page_xp );
+            uint32_t flags    = hal_remote_l32( XPTR( mapper_cxy , &page_ptr->flags ) );
+
+            if ( flags & PG_DIRTY )
+            {
+
+#if (DEBUG_FATFS_SYNC_FAT & 1)
+if( DEBUG_FATFS_SYNC_FAT < cycle )
+printk("\n[%s] thread[%x,%x] synchronizes page %d from FAT mapper to IOC device\n",
+__FUNCTION__, page_id );
+#endif
+                // move page from mapper to device
+                error = fatfs_move_page( page_xp , false );
+
+                if ( error )  return -1;
+
+                // reset page dirty flag
+                ppm_page_undo_dirty( page_xp );
+            }
+        }
+    }  // end loop on pages
+
+#if DEBUG_FATFS_SYNC_FAT
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_SYNC_FAT < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, cycle );
+#endif
+
+    return 0;
+
+}  // end fatfs_sync_fat()
+
+////////////////////////////////////
+error_t fatfs_sync_free_info( void )
+{
+
+#if DEBUG_FATFS_SYNC_FSINFO
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+if( DEBUG_FATFS_SYNC_FSINFO < cycle )
+printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, cycle );
+#endif
+
+    uint8_t     * buffer;   // dynamically allocated aligned 512 bytes buffer
+    kmem_req_t    req;
+    error_t       error;
+
+    // get FS_INFO lba, free_ from FATFS context
+    fatfs_ctx_t * fatfs_ctx  = fs_context[FS_TYPE_FATFS].extend;
+    uint32_t      lba        = fatfs_ctx->fs_info_lba;
+    uint32_t      hint       = fatfs_ctx->free_cluster_hint;
+    uint32_t      number     = fatfs_ctx->free_clusters;
+
+    // allocate buffer to store the FS_INFO sector
+	req.type    = KMEM_512_BYTES;
+    req.flags   = AF_KERNEL | AF_ZERO;
+	buffer      = (uint8_t *)kmem_alloc( &req );
+    if( buffer == NULL ) 
+    {
+        printk("\n[PANIC] in %s : cannot allocate buffer\n", __FUNCTION__ );
+        return ENOMEM;
+    }
+     
+    // load the FS_INFO sector from device to buffer
+    error = dev_ioc_read( buffer , lba , 1 );
+    if ( error )
+    {
+        printk("\n[PANIC] in %s : cannot read FS_INFO record\n", __FUNCTION__ );
+        return EIO;
+    }
+
+    // update buffer 
+    fatfs_set_record( FS_FREE_CLUSTERS     , buffer , 1 , number );
+    fatfs_set_record( FS_FREE_CLUSTER_HINT , buffer , 1 , hint );
+
+    // write modified FS_INFO sector from buffer to device
+    error = dev_ioc_write( buffer , lba , 1 );
+    if ( error )
+    {
+        printk("\n[PANIC] in %s : cannot write FS_INFO record\n", __FUNCTION__ );
+        return EIO;
+    }
+
+    // release the 512 bytes buffer
+    req.type = KMEM_512_BYTES;
+    req.ptr  = buffer;
+    kmem_free( &req );
+
+#if DEBUG_FATFS_SYNC_FSINFO
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_SYNC_FSINFO < cycle )
+printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, cycle );
+#endif
+
+    return 0;
+
+}  // end fatfs_sync_fs_info()
+
+//////////////////////////////////////////////////////////
+error_t fatfs_cluster_alloc( uint32_t * searched_cluster )
+{
+    uint32_t      page_id;        // page index in mapper
+    uint32_t      slot_id;        // slot index in page (1024 slots per page)
+    uint32_t      hint;           // first free cluster index in FAT
+    uint32_t      free_clusters;  // total number of free clusters
+    vfs_ctx_t   * vfs_ctx;        // local pointer on VFS context (same in all clusters)
+    fatfs_ctx_t * loc_fatfs_ctx;  // local pointer on local FATFS context
+    fatfs_ctx_t * fat_fatfs_ctx;  // local pointer on FATFS context in FAT cluster
+    xptr_t        mapper_xp;      // extended pointer on FAT mapper
+    cxy_t         mapper_cxy;     // Fat mapper cluster identifier
+    xptr_t        page_xp;        // extended pointer on current page descriptor in mapper
+    xptr_t        slot_xp;        // extended pointer on FAT slot defined by hint
+    xptr_t        lock_xp;        // extended pointer on lock protecting free clusters info
+    xptr_t        hint_xp;        // extended pointer on free_cluster_hint in FAT cluster
+    xptr_t        numb_xp;        // extended pointer on free_clusters_number in FAT cluster
+
+#if DEBUG_FATFS_CLUSTER_ALLOC
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
+printk("\n[%s] thread[%x,%x] enter / cycle = %d\n",
+__FUNCTION__, this->process->pid, this->trdid, cycle );
+#endif
+
+    // get local pointer on VFS context (same in all clusters)
+    vfs_ctx = &fs_context[FS_TYPE_FATFS];
+
+    // get local pointer on local FATFS context
+    loc_fatfs_ctx = vfs_ctx->extend;
+
+    // get extended pointer and cluster on FAT mapper
+    mapper_xp  = loc_fatfs_ctx->fat_mapper_xp;
+    mapper_cxy = GET_CXY( mapper_xp );
+    
+    // get local pointer on FATFS context in FAT cluster 
+    fat_fatfs_ctx = hal_remote_lpt( XPTR( mapper_cxy , &vfs_ctx->extend ) );
+
+    // build relevant extended pointers in on free clusters info in FAT cluster 
+    lock_xp = XPTR( mapper_cxy , &fat_fatfs_ctx->free_lock );
+    hint_xp = XPTR( mapper_cxy , &fat_fatfs_ctx->free_cluster_hint );
+    numb_xp = XPTR( mapper_cxy , &fat_fatfs_ctx->free_clusters );
+
+    // take the lock protecting free clusters
+    remote_queuelock_acquire( lock_xp );
+
+    // get hint and free_clusters values from FATFS context 
+    hint          = hal_remote_l32( hint_xp );
+    free_clusters = hal_remote_l32( numb_xp );
+        
+    // get page index & slot index for the first free cluster 
+    page_id  = (hint + 1) >> 10;
+    slot_id  = (hint + 1) & 0x3FF;
+
+    // get relevant page from mapper
+    page_xp = mapper_remote_get_page( mapper_xp , page_id );
+
+    if( page_xp == XPTR_NULL )
+    {
+        printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ );
+        return -1;
+    }
+
+    // build extended pointer on free cluster slot 
+    slot_xp = ppm_page2base( page_xp ) + (slot_id<<2);
+         
+#if (DEBUG_FATFS_CLUSTER_ALLOC & 1)
+if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
+printk("\n[%s] thread[%x,%x] get free info / hint %x / free_clusters %x\n",
+__FUNCTION__, this->process->pid, this->trdid, hint, free_clusters );
+#endif
+
+    // check "free_clusters"
+    if ( free_clusters == 0 )
+    {
+        printk("\n[ERROR] in %s : no more free FATFS clusters\n", __FUNCTION__ );
+        remote_queuelock_acquire( lock_xp );
+        return -1;
+    }
+    else if ( free_clusters < CONFIG_VFS_FREE_CLUSTERS_MIN )
+    {
+        printk("\n[WARNING] in %s : only %n free FATFS clusters\n", 
+        __FUNCTION__, CONFIG_VFS_FREE_CLUSTERS_MIN );
+    }
+
+    // check "hint" 
+    if( hal_remote_l32( slot_xp ) != FREE_CLUSTER )
+    { 
+        printk("\n[ERROR] in %s : illegal hint cluster\n", __FUNCTION__ );
+        remote_queuelock_acquire( lock_xp );
+        return -1;
+    }
+
+    // update allocated cluster in FAT mapper
+    hal_remote_s32( slot_xp , END_OF_CHAIN_CLUSTER_MAX );
+
+    // update free cluster info
+    fatfs_free_clusters_decrement( hint + 1 );
+
+    // release free clusters busylock
+    remote_queuelock_release( lock_xp );
+
+#if DEBUG_FATFS_CLUSTER_ALLOC 
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
+printk("\n[%s] thread[%x,%x] exit / cluster %x / cycle %d\n",
+__FUNCTION__, this->process->pid, this->trdid, hint + 1, cycle );
+#endif
+
+    *searched_cluster = hint + 1;
+    return 0;
+
+}  // end fat_cluster_alloc()
+
+//////////////////////////////////////////////
+error_t fatfs_release_inode( xptr_t inode_xp )
+{
+    vfs_ctx_t   * vfs_ctx;        // local pointer on VFS context (same in all clusters).
+    fatfs_ctx_t * loc_fatfs_ctx;  // local pointer on local FATFS context
+    fatfs_ctx_t * fat_fatfs_ctx;  // local pointer on FATFS context in FAT cluster
+    xptr_t        mapper_xp;      // extended pointer on FAT mapper
+    cxy_t         mapper_cxy;     // Fat mapper cluster identifier
+    xptr_t        lock_xp;        // extended pointer on lock protecting free clusters info.
+    xptr_t        first_xp;       // extended pointer on inode extension
+    uint32_t      first_cluster;  // first cluster index for released inode
+    vfs_inode_t * inode_ptr;
+    cxy_t         inode_cxy;
+
+// check inode pointer 
+assert( (inode_xp != XPTR_NULL) , "inode pointer is NULL\n" );
+
+    // get first_cluster from inode extension
+    inode_ptr     = GET_PTR( inode_xp );
+    inode_cxy     = GET_CXY( inode_xp );
+    first_xp      = XPTR( inode_cxy , &inode_ptr->extend );
+    first_cluster = (uint32_t)(intptr_t)hal_remote_lpt( first_xp );
+
+// check first cluster index
+assert( (first_cluster != 0) , "inode extend is NULL\n" );
+
+#if DEBUG_FATFS_RELEASE_INODE
+char       name[CONFIG_VFS_MAX_NAME_LENGTH];
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+vfs_inode_get_name( inode_xp , name );
+if( DEBUG_FATFS_RELEASE_INODE < cycle )
+printk("\n[%s] thread[%x,%x] enter for <%s> / first_cluster %x / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, name, first_cluster, cycle );
+#endif
+
+    // get local pointer on VFS context (same in all clusters)
+    vfs_ctx = &fs_context[FS_TYPE_FATFS];
+
+    // get local pointer on local FATFS context
+    loc_fatfs_ctx = vfs_ctx->extend;
+
+    // get extended pointer and cluster on FAT mapper
+    mapper_xp  = loc_fatfs_ctx->fat_mapper_xp;
+    mapper_cxy = GET_CXY( mapper_xp );
+    
+    // get local pointer on FATFS context in FAT cluster 
+    fat_fatfs_ctx = hal_remote_lpt( XPTR( mapper_cxy , &vfs_ctx->extend ) );
+
+    // get extended pointer on free clusters lock in FAT cluster
+    lock_xp = XPTR( mapper_cxy , &fat_fatfs_ctx->free_lock );
+
+    // take lock protecting free clusters
+    remote_queuelock_acquire( lock_xp );
+
+    // call the recursive function to release all clusters from FAT mapper
+    if ( fatfs_recursive_release( mapper_xp , first_cluster ) )
+    {
+        printk("\n[ERROR] in %s : cannot update FAT mapper\n", __FUNCTION__ );
+        remote_queuelock_release( lock_xp );
+        return -1;
+    }
+
+    // release lock protecting free cluster
+    remote_queuelock_release( lock_xp );
+
+#if (DEBUG_FATFS_RELEASE_INODE & 1)
+if( DEBUG_FATFS_RELEASE_INODE < cycle )
+printk("\n[%s] inode <%s> removed from FAT mapper\n", __FUNCTION__, name );
+#endif
+
+    // update FAT on IOC device (from FAT mapper)
+    if ( fatfs_sync_fat() )
+    {
+        printk("\n[ERROR] in %s : cannot update FAT on device\n", __FUNCTION__ );
+        return -1;
+    }
+
+#if (DEBUG_FATFS_RELEASE_INODE & 1)
+if( DEBUG_FATFS_RELEASE_INODE < cycle )
+printk("\n[%s] inode <%s> removed from FAT on IOC device\n", __FUNCTION__, name );
+#endif
+
+    // update FS-INFO sector on IOC device (from FATFS context)
+    if ( fatfs_sync_free_info() )
+    {
+        printk("\n[ERROR] in %s: cannot update FS_INFO on device\n", __FUNCTION__ );
+        return -1;
+    }
+
+#if DEBUG_FATFS_RELEASE_INODE
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_RELEASE_INODE < cycle )
+printk("\n[%s] thread[%x,%x] removed <%s> inode from FATFS / cycle %d\n",
+__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
+#endif
+
+    return 0;
+
+}  // end fatfs_release_inode()
+
+/////////////////////////////////////////
+error_t fatfs_move_page( xptr_t  page_xp,
+                         bool_t  to_mapper )
+{
+    error_t       error;
+    vfs_inode_t * inode_ptr;
+    mapper_t    * mapper_ptr;      
+    uint32_t      page_id;     // page index in mapper
+
+#if DEBUG_FATFS_MOVE_PAGE
+uint32_t   cycle = (uint32_t)hal_get_cycles();
+thread_t * this  = CURRENT_THREAD;
+char       name[CONFIG_VFS_MAX_NAME_LENGTH];
+#endif
+
+    // get page cluster an local pointer
+    cxy_t    page_cxy = GET_CXY( page_xp );
+    page_t * page_ptr = GET_PTR( page_xp );
+
+    // get mapper pointer and page index from page descriptor
+    mapper_ptr = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) );
+    page_id    = hal_remote_l32( XPTR( page_cxy , &page_ptr->index ) );
+
+    // get pointer on local FATFS context
+    fatfs_ctx_t * fatfs_ctx      = fs_context[FS_TYPE_FATFS].extend;
+
+    // get page base address
+    xptr_t    base_xp = ppm_page2base( page_xp );
+    uint8_t * buffer  = (uint8_t *)GET_PTR( base_xp );
+ 
+    // get inode pointer from mapper
+    inode_ptr  = hal_remote_lpt( XPTR( page_cxy , &mapper_ptr->inode ) );
+
+    ////////////////////////////// it is the FAT mapper
+    if( inode_ptr == NULL )
+    {
+        // get lba from FATFS context and page_id 
+        uint32_t      lba        = fatfs_ctx->fat_begin_lba + (page_id << 3);
+ 
+        // access device
+        if( to_mapper ) error = dev_ioc_sync_read ( buffer , lba , 8 );
+        else            error = dev_ioc_write( buffer , lba , 8 );     
+
+        if( error ) return EIO;
+
+#if (DEBUG_FATFS_MOVE_PAGE & 0x1)
+if( DEBUG_FATFS_MOVE_PAGE < cycle )
+{
+    uint32_t * tab = (uint32_t *)buffer;
+    uint32_t line , word;
+    printk("\n***** %s : First 64 words of page %d in FAT mapper\n",
+    __FUNCTION__ , page_id );
+    for( line = 0 ; line < 8 ; line++ )
+    {
+        printk("%X : ", line );
+        for( word = 0 ; word < 8 ; word++ ) printk("%X ", tab[(line<<3) + word] );
+        printk("\n");
+    }
+}
+#endif
+
+#if DEBUG_FATFS_MOVE_PAGE
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_FATFS_MOVE_PAGE < cycle )
+{
+    if (to_mapper)
+         printk("\n[%s] thread[%x,%x] load page %d of FAT / cycle %d\n",
+         __FUNCTION__, this->process->pid, this->trdid, page_id, cycle );
+    else
+        printk("\n[%s] thread[%x,%x] sync page %d of FAT / cycle %d\n",
+        __FUNCTION__, this->process->pid, this->trdid, page_id, cycle );
+}
+#endif
+
+    }
+    ///////////////////////// it is an inode mapper
+    else                       
+    {
+
+#if DEBUG_FATFS_MOVE_PAGE
+vfs_inode_get_name( XPTR( page_cxy , inode_ptr ) , name );
+#endif
+
+        uint32_t  searched_cluster;
+        uint32_t  first_cluster;
+
+        // get first_cluster from inode extension
+        void * extend = hal_remote_lpt( XPTR( page_cxy , &inode_ptr->extend ) );
+        first_cluster = (uint32_t)(intptr_t)extend;
+
+        // compute searched_cluster
+        if( page_id == 0 )            // no need to access FAT mapper
+        {
+            // searched cluster is first cluster
+            searched_cluster = first_cluster;
+        }
+        else                        // FAT mapper access required
+        {
+            // access FAT mapper to get searched cluster
+            error = fatfs_get_cluster( first_cluster,
+                                       page_id,
+                                       &searched_cluster );
+            if( error )  return EIO;
+        }
+
+        // get lba from searched_cluster
+        uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , searched_cluster );
+
+        // access device
+        if( to_mapper ) error = dev_ioc_sync_read ( buffer , lba , 8 );
+        else            error = dev_ioc_write( buffer , lba , 8 );     
+
+        if( error ) return EIO;
+
+#if (DEBUG_FATFS_MOVE_PAGE & 0x1)
+if( DEBUG_FATFS_MOVE_PAGE < cycle )
+{
+    uint32_t * tab = (uint32_t *)buffer;
+    uint32_t line , word;
+    printk("\n***** %s : First 64 words of page %d in <%s> mapper\n",
+    __FUNCTION__, page_id, name );
+    for( line = 0 ; line < 8 ; line++ )
+    {
+        printk("%X : ", line );
+        for( word = 0 ; word < 8 ; word++ ) printk("%X ", tab[(line<<3) + word] );
+        printk("\n");
+    }
+}
+#endif
+
+#if DEBUG_FATFS_MOVE_PAGE
+cycle = (uint32_t)hal_get_cycles();
+if(DEBUG_FATFS_MOVE_PAGE < cycle)
+{
+    if(to_mapper)
+        printk("\n[%s] thread[%x,%x] load page %d of <%s> inode / cycle %d\n",
+        __FUNCTION__, this->process->pid, this->trdid, page_id, name, cycle );
+    else
+        printk("\n[%s] thread[%x,%x] sync page %d of <%s> inode / cycle %d\n",
+        __FUNCTION__, this->process->pid, this->trdid, page_id, name, cycle );
+}
+#endif
+
+    }
+
+    return 0;
+
+}  // end fatfs_move_page()
+
+
