/*
 * vfs.c - Virtual File System implementation.
 *
 * Author  Mohamed Lamine Karaoui (2014,2015)
 *         Alain Greiner          (2016,2017,2018,2019,2020)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <kernel_config.h>
#include <hal_kernel_types.h>
#include <hal_atomic.h>
#include <hal_special.h>
#include <printk.h>
#include <list.h>
#include <xlist.h>
#include <slist.h>
#include <xhtab.h>
#include <string.h>
#include <errno.h>
#include <kmem.h>
#include <mapper.h>
#include <thread.h>
#include <chdev.h>
#include <pipe.h>
#include <process.h>
#include <cluster.h>
#include <vfs.h>
#include <fatfs.h>
#include <ramfs.h>
#include <devfs.h>
#include <syscalls.h>

//////////////////////////////////////////////////////////////////////////////////////////
//           Extern variables         
//////////////////////////////////////////////////////////////////////////////////////////

extern vfs_ctx_t          fs_context[FS_TYPES_NR];    // allocated in kernel_init.c
extern chdev_directory_t  chdev_dir;                  // allocated in kernel_init.c  
extern char *             lock_type_str[];            // allocated in kernel_init.c
  
///////////////////////////////////////////////////////////////////////////////////////////
//           VFS Context related functions
//////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////
void vfs_ctx_init( cxy_t           cxy,
                   vfs_fs_type_t   fs_type,
	               uint32_t        total_clusters,
	               uint32_t        cluster_size,
	               xptr_t          vfs_root_xp,
                   void          * extend )
{
    // get pointer on relevant VFS context (same in all clusters) 
    vfs_ctx_t * vfs_ctx_ptr = &fs_context[fs_type];

    // initialise VFS context fields 
    hal_remote_s32( XPTR( cxy , &vfs_ctx_ptr->type           ) , fs_type );
    hal_remote_s32( XPTR( cxy , &vfs_ctx_ptr->total_clusters ) , total_clusters );
    hal_remote_s32( XPTR( cxy , &vfs_ctx_ptr->cluster_size   ) , cluster_size );
    hal_remote_s64( XPTR( cxy , &vfs_ctx_ptr->vfs_root_xp    ) , vfs_root_xp );
    hal_remote_spt( XPTR( cxy , &vfs_ctx_ptr->extend         ) , extend );

    // initialize VFS context lock
    remote_busylock_init( XPTR( cxy , &vfs_ctx_ptr->lock ) , LOCK_VFS_CTX );

    // initialize inum allocator
    bitmap_remote_init( XPTR( cxy , &vfs_ctx_ptr->bitmap ),
                        BITMAP_SIZE(CONFIG_VFS_MAX_INODES) ); 

} // end vfs_ctx_init()

///////////////////////////////////////////////
error_t vfs_ctx_inum_alloc( xptr_t      ctx_xp,
                            uint32_t  * inum )
{
    // get context cluster and local pointer
    cxy_t       ctx_cxy = GET_CXY( ctx_xp );
    vfs_ctx_t * ctx_ptr = GET_PTR( ctx_xp );

    // build extended pointer on lock protecting the inum allocator
    xptr_t lock_xp = XPTR( ctx_cxy , &ctx_ptr->lock );

    // build extended pointer on inum bitmap
    xptr_t bitmap_xp = XPTR( ctx_cxy , &ctx_ptr->bitmap );

    // get lock on inum allocator
    remote_busylock_acquire( lock_xp );

    // get lid from local inum allocator
    uint32_t lid = bitmap_remote_ffc( bitmap_xp , CONFIG_VFS_MAX_INODES );

    if( lid == 0xFFFFFFFF )   // no more free slot => error
    {
        // release lock
        remote_busylock_release( lock_xp );

        // return error
        return -1;
    }
    else              // found => return inum
    {
        // set slot allocated
        bitmap_remote_set( bitmap_xp , lid );

        // release lock
        remote_busylock_release( lock_xp );

        // return inum
        *inum = (((uint32_t)local_cxy) << 16) | (lid & 0xFFFF);
        return 0;
    }
}  // end vfs_ctx_inum_alloc()

/////////////////////////////////////////////
void vfs_ctx_inum_release( xptr_t     ctx_xp,
                           uint32_t   inum )
{
    // get context cluster and local pointer
    cxy_t       ctx_cxy = GET_CXY( ctx_xp );
    vfs_ctx_t * ctx_ptr = GET_PTR( ctx_xp );

    // build extended pointer on inum bitmap
    xptr_t bitmap_xp = XPTR( ctx_cxy , &ctx_ptr->bitmap );

    // build extended pointer on lock
    xptr_t lock_xp = XPTR( ctx_cxy , &ctx_ptr->lock );

    // get lock 
    remote_busylock_acquire( lock_xp );

    bitmap_remote_clear( bitmap_xp , inum & 0xFFFF ); 

    // release lock
    remote_busylock_release( lock_xp );

}  // end vfs_ctx_inum_release()

//////////////////////////////////////////////////////////////////////////////////////////
//           VFS inode descriptor related functions
//////////////////////////////////////////////////////////////////////////////////////////

const char * vfs_inode_type_str( vfs_file_type_t type )
{
    switch ( type ) 
    {
        case FILE_TYPE_REG: return "FILE";
        case FILE_TYPE_DIR:  return "DIR ";
        case FILE_TYPE_FIFO: return "FIFO";
        case FILE_TYPE_PIPE: return "PIPE";
        case FILE_TYPE_SOCK: return "SOCK";
        case FILE_TYPE_DEV:  return "DEV ";
        case FILE_TYPE_BLK:  return "BLK ";
        case FILE_TYPE_SYML: return "SYML";
        default:              return "undefined";
    }
}

////////////////////////////////////////////////
error_t vfs_inode_create( cxy_t             cxy,
                          vfs_fs_type_t     fs_type,
                          uint32_t          attr,
                          uint32_t          rights,
                          uid_t             uid,
                          gid_t             gid,
                          xptr_t          * inode_xp )
{
    xptr_t             mapper_xp;    // extended pointer on associated mapper
    mapper_t         * mapper_ptr;   // local pointer on associated mapper
    vfs_inode_t      * inode_ptr;    // local pointer on allocated inode
    uint32_t           inum;         // inode identifier (to be allocated)
    vfs_ctx_t        * ctx;          // file system context 
	kmem_req_t         req;          // request to kernel memory allocator
    error_t            error;

#if DEBUG_VFS_INODE_CREATE || DEBUG_VFS_ERROR
uint32_t       cycle      = (uint32_t)hal_get_cycles();
thread_t *     this       = CURRENT_THREAD;
#endif

    // check fs type and get pointer on context
    if     ( fs_type == FS_TYPE_FATFS ) ctx = &fs_context[FS_TYPE_FATFS];
    else if( fs_type == FS_TYPE_RAMFS ) ctx = &fs_context[FS_TYPE_RAMFS];
    else if( fs_type == FS_TYPE_DEVFS ) ctx = &fs_context[FS_TYPE_DEVFS];
    else
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] / illegal FS type\n",
__FUNCTION__ , this->process->pid , this->trdid );
#endif
        return -1;
    }

// check inode descriptor contained in one page
assert( __FUNCTION__, (sizeof(vfs_inode_t) <= CONFIG_PPM_PAGE_SIZE),
"inode descriptor must fit in one page" );

    // allocate inum
    error = vfs_ctx_inum_alloc( XPTR( cxy , ctx ) , &inum );

    if( error )
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate inum\n",
__FUNCTION__ , this->process->pid , this->trdid );
#endif
        return -1;
    }

    // allocate memory for mapper in cluster cxy
    mapper_xp = mapper_create( cxy , fs_type );

    if( mapper_xp == XPTR_NULL )
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate mapper\n",
__FUNCTION__ , this->process->pid , this->trdid );
#endif
        vfs_ctx_inum_release( XPTR( cxy , ctx ) , inum );
        return -1;
    }

    mapper_ptr = GET_PTR( mapper_xp );

    // allocate one page for VFS inode descriptor
    // because the embedded "children" xhtab footprint
	req.type   = KMEM_PPM;
	req.order  = 0;
    req.flags  = AF_KERNEL | AF_ZERO;
	inode_ptr  = kmem_remote_alloc( cxy , &req );

    if( inode_ptr == NULL )
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate inode\n",
__FUNCTION__ , this->process->pid , this->trdidi );
#endif
        vfs_ctx_inum_release( XPTR( cxy , ctx ) , inum );
        mapper_destroy( mapper_xp );
        return -1;
    }

    // initialise inode field in mapper
    hal_remote_spt( XPTR( cxy , &mapper_ptr->inode ) , inode_ptr );
 
    // initialize inode descriptor
    hal_remote_s32( XPTR( cxy , &inode_ptr->type   ) , FILE_TYPE_REG );  // default value
    hal_remote_s32( XPTR( cxy , &inode_ptr->inum   ) , inum );  
    hal_remote_s32( XPTR( cxy , &inode_ptr->attr   ) , attr );  
    hal_remote_s32( XPTR( cxy , &inode_ptr->rights ) , rights );  
    hal_remote_s32( XPTR( cxy , &inode_ptr->links  ) , 0 );
    hal_remote_s32( XPTR( cxy , &inode_ptr->uid    ) , uid );  
    hal_remote_s32( XPTR( cxy , &inode_ptr->gid    ) , gid );  
    hal_remote_spt( XPTR( cxy , &inode_ptr->ctx    ) , ctx );  
    hal_remote_spt( XPTR( cxy , &inode_ptr->mapper ) , mapper_ptr );  
    hal_remote_spt( XPTR( cxy , &inode_ptr->extend ) , NULL );  

    // initialize chidren dentries xhtab
    xhtab_init( XPTR( cxy , &inode_ptr->children ) , XHTAB_DENTRY_TYPE );

    // initialize parents dentries xlist
    xlist_root_init( XPTR( cxy , &inode_ptr->parents ) );
 
    // initialize lock protecting size 
    remote_rwlock_init( XPTR( cxy , &inode_ptr->size_lock ), LOCK_VFS_SIZE );

    // initialise lock protecting inode tree traversal
    remote_rwlock_init( XPTR( cxy , &inode_ptr->main_lock ), LOCK_VFS_MAIN );

    // return extended pointer on inode
    *inode_xp = XPTR( cxy , inode_ptr );

#if DEBUG_VFS_INODE_CREATE
if( DEBUG_VFS_INODE_CREATE < cycle )
printk("\n[%s] thread[%x,%x] created inode (%x,%x) / ctx %x / fs_type %d / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cxy, inode_ptr, ctx, ctx->type, cycle );
#endif
 
    return 0;

}  // end vfs_inode_create()  

//////////////////////////////////////////
void vfs_inode_destroy( xptr_t  inode_xp )
{
    // get cluster and local pointer
    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

    // get local pointer on mapper
    mapper_t    * mapper_ptr = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ));

    // release memory allocated for mapper
    mapper_destroy( XPTR( inode_cxy , mapper_ptr ) );

    // release memory allocated for inode descriptor
	kmem_req_t req;
	req.type  = KMEM_PPM;
	req.ptr   = inode_ptr;
	kmem_remote_free( inode_cxy , &req );

}  // end vfs_inode_destroy()

//////////////////////////////////////////////
uint32_t vfs_inode_get_size( xptr_t inode_xp )
{
    // get inode cluster and local pointer
    cxy_t         cxy = GET_CXY( inode_xp );
    vfs_inode_t * ptr = GET_PTR( inode_xp );

    // build extended pointers on lock & size 
    xptr_t   lock_xp = XPTR( cxy , &ptr->size_lock );
    xptr_t   size_xp = XPTR( cxy , &ptr->size );

    // take lock in read mode 
    remote_rwlock_rd_acquire( lock_xp );

    // get size
    uint32_t size = hal_remote_l32( size_xp );

    // release lock from read mode
    remote_rwlock_rd_release( lock_xp );

    return size;
}

///////////////////////////////////////////////
void vfs_inode_update_size( xptr_t    inode_xp,
                            uint32_t  size )
{
    // get inode cluster and local pointer
    cxy_t         cxy = GET_CXY( inode_xp );
    vfs_inode_t * ptr = GET_PTR( inode_xp );

    // build extended pointers on lock & size 
    xptr_t   lock_xp = XPTR( cxy , &ptr->size_lock );
    xptr_t   size_xp = XPTR( cxy , &ptr->size );

    // take lock in write mode 
    remote_rwlock_wr_acquire( lock_xp );

    // get current size
    uint32_t current_size = hal_remote_l32( size_xp );

    // set size if required
    if( current_size < size ) hal_remote_s32( size_xp , size );

    // release lock from write mode
    remote_rwlock_wr_release( lock_xp );
}

////////////////////////////////////////
void vfs_inode_unlock( xptr_t inode_xp )
{
    // get inode cluster and local pointer
    cxy_t         cxy = GET_CXY( inode_xp );
    vfs_inode_t * ptr = GET_PTR( inode_xp );

    // release the main lock
    remote_busylock_release( XPTR( cxy , &ptr->main_lock ) );
}

//////////////////////////////////////
void vfs_inode_lock( xptr_t inode_xp )
{
    // get inode cluster and local pointer
    cxy_t         cxy = GET_CXY( inode_xp );
    vfs_inode_t * ptr = GET_PTR( inode_xp );

    // get the main lock
    remote_busylock_acquire( XPTR( cxy , &ptr->main_lock ) );
}

///////////////////////////////////////////
void vfs_inode_get_name( xptr_t   inode_xp,
                         char   * name )
{
    cxy_t          inode_cxy;          // inode cluster identifier 
    vfs_inode_t  * inode_ptr;          // local pointer on inode
    xptr_t         parents_root_xp;    // extended pointer on inode parents root
    
    // get inode cluster and local pointer
    inode_ptr = GET_PTR( inode_xp );
    inode_cxy = GET_CXY( inode_xp );

    // build extended pointer on parents dentries root
    parents_root_xp  = XPTR( inode_cxy , &inode_ptr->parents );

    // check VFS root 
    if( xlist_is_empty( parents_root_xp ) )  // inode is the VFS root
    {
        strcpy( name , "/" );
    }
    else                                     // not the VFS root
    {
        xptr_t         dentry_xp;
        cxy_t          dentry_cxy;
        vfs_dentry_t * dentry_ptr;

        // get first name in list of parents
        dentry_xp  = XLIST_FIRST( parents_root_xp , vfs_dentry_t , parents );
        dentry_cxy = GET_CXY( dentry_xp );
        dentry_ptr = GET_PTR( dentry_xp );

        hal_remote_strcpy( XPTR( local_cxy  , name ) , 
                           XPTR( dentry_cxy , dentry_ptr->name ) );
    }

}  // end vfs_inode_get_name()

////////////////////////////////////////////////////
error_t vfs_inode_load_all_pages( xptr_t  inode_xp )
{
    uint32_t   page_id;
    xptr_t     page_xp;


    // get inode cluster and local pointer
    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

    // get pointer on mapper and size
    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
    uint32_t   size   = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size ) );

#if DEBUG_VFS_INODE_LOAD_ALL
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_VFS_INODE_LOAD_ALL < cycle )
printk("\n[%s] thread[%x,%x] enter for <%s> in cluster %x / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, name, inode_cxy, cycle );
#endif

    // compute number of pages
    uint32_t npages = size >> CONFIG_PPM_PAGE_SHIFT;
    if( (size & CONFIG_PPM_PAGE_MASK) || (size == 0) ) npages++;

    // loop on pages 
    for( page_id = 0 ; page_id < npages ; page_id ++ )
    {
        // If the mage is missing, this function allocates the missing page,
        // and load the page from IOC device into mapper
        page_xp = mapper_get_page( XPTR( inode_cxy , mapper ), page_id );

        if( page_xp == XPTR_NULL ) return -1;
    }

#if DEBUG_VFS_INODE_LOAD_ALL
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_INODE_LOAD_ALL < cycle )
printk("\n[%s] thread[%x,%x] exit for <%s> in cluster %x\n",
__FUNCTION__, this->process->pid, this->trdid, name, inode_cxy );
#endif

    return 0;

}  // end vfs_inode_load_all_pages()

/////////////////////////////////////////
void vfs_inode_display( xptr_t inode_xp )
{
    assert( __FUNCTION__, (inode_xp != XPTR_NULL), "inode pointer is NULL");

    char  name[CONFIG_VFS_MAX_NAME_LENGTH];

    vfs_inode_get_name( inode_xp , name );

    // get inode cluster and local pointer
    cxy_t         inode_cxy = GET_CXY( inode_xp );
    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );

    vfs_file_type_t type    = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );
    uint32_t         attr    = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->attr ) );
    uint32_t         size    = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size ) );
    uint32_t         parents = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->links ) );
    mapper_t       * mapper  = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
    void           * extend  = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->extend ) );

    printk("\n**** inode <%s>\n"
           " - type    = %s\n"
           " - attr    = %x\n"
           " - size    = %d\n"
           " - parents = %d\n"
           " - cxy     = %x\n"
           " - inode   = %x\n"
           " - mapper  = %x\n"
           " - extend  = %x\n",
           name,
           vfs_inode_type_str( type ),
           attr,
           size,
           parents,
           inode_cxy,
           inode_ptr,
           mapper,
           extend );

}  // end vfs_inode_display()


////////////////////////////////////////////////////////////////////////////////////////////
//          VFS dentry descriptor related functions
//////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////
error_t vfs_dentry_create( cxy_t           cxy,
                           vfs_fs_type_t   fs_type,
                           char          * name,
                           xptr_t        * dentry_xp )
{
	kmem_req_t       req;            // request to kernel memory allocator
    vfs_ctx_t      * ctx = NULL;     // context descriptor
    vfs_dentry_t   * dentry_ptr;     // dentry descriptor (to be allocated)

#if DEBUG_VFS_DENTRY_CREATE || DEBUG_VFS_ERROR
thread_t * this  = CURRENT_THREAD;
uint32_t   cycle = (uint32_t)hal_get_cycles();
#endif

#if DEBUG_VFS_DENTRY_CREATE
if( DEBUG_VFS_DENTRY_CREATE < cycle )
printk("\n[%s] thread[%x,%x] enters for <%s> /  fs_type %x / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, name, fs_type, cycle );
#endif

    // get pointer on context
    if     ( fs_type == FS_TYPE_FATFS ) ctx = &fs_context[FS_TYPE_FATFS];
    else if( fs_type == FS_TYPE_RAMFS ) ctx = &fs_context[FS_TYPE_RAMFS];
    else if( fs_type == FS_TYPE_DEVFS ) ctx = &fs_context[FS_TYPE_DEVFS];
    else 
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] / undefined fs_type %d\n",
__FUNCTION__ , this->process->pid, this->trdid, fs_type );
#endif
        return -1;
    }

    // get name length
    uint32_t length = strlen( name );

    if( length >= CONFIG_VFS_MAX_NAME_LENGTH ) return -1;

    // allocate memory for dentry descriptor
	req.type   = KMEM_KCM;
	req.order  = bits_log2( sizeof(vfs_dentry_t) );
    req.flags  = AF_KERNEL | AF_ZERO;
	dentry_ptr = kmem_remote_alloc( cxy , &req );

    if( dentry_ptr == NULL ) 
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate dentry descriptor\n",
__FUNCTION__ , this->process->pid, this->trdid );
#endif
        return -1;
    }

    // initialize dentry descriptor
    hal_remote_spt( XPTR( cxy , &dentry_ptr->ctx    ) , ctx );
    hal_remote_s32( XPTR( cxy , &dentry_ptr->length ) , length );
    hal_remote_spt( XPTR( cxy , &dentry_ptr->extend ) , NULL );

    // register name
    hal_remote_strcpy( XPTR( cxy, dentry_ptr->name ),
                       XPTR( local_cxy, name ) );

    // return extended pointer on dentry 
    *dentry_xp = XPTR( cxy , dentry_ptr );

#if DEBUG_VFS_DENTRY_CREATE
if( DEBUG_VFS_DENTRY_CREATE < cycle )
printk("\n[%s] thread[%x,%x] exit for <%s> / dentry (%x,%x)\n",
__FUNCTION__, this->process->pid, this->trdid, name, cxy, dentry_ptr );
#endif

    return 0;

}  // end vfs_dentry_create()

////////////////////////////////////////////
void vfs_dentry_destroy( xptr_t  dentry_xp )
{
    // get cluster and local pointer
    vfs_dentry_t * dentry_ptr = GET_PTR( dentry_xp );
    cxy_t          dentry_cxy = GET_CXY( dentry_xp );
  
    // release memory allocated to dentry
	kmem_req_t req;
	req.type  = KMEM_KCM;
	req.ptr   = dentry_ptr;
	kmem_remote_free( dentry_cxy , &req );

}  // end vfs_dentry_destroy()


//////////////////////////////////////////////////////////////////////////////////////////
//       VFS file descriptor related functions
//////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////
error_t vfs_file_create( xptr_t     inode_xp,
                         uint32_t   attr,
                         xptr_t   * file_xp )
{
    vfs_file_t   * file_ptr;
	kmem_req_t     req;
    uint32_t       type;
    mapper_t     * mapper;
    pipe_t       * pipe;
    vfs_ctx_t    * ctx;

    // get inode cluster and local pointer
    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

#if DEBUG_VFS_FILE_CREATE
thread_t * this = CURRENT_THREAD;
uint32_t cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_FILE_CREATE < cycle )
printk("\n[%s] thread[%x,%x] enter for inode (%x,%x) / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, inode_cxy, inode_ptr, cycle );
#endif

    // allocate memory for new file descriptor
	req.type  = KMEM_KCM;
	req.order = bits_log2( sizeof(vfs_file_t) );
    req.flags = AF_KERNEL | AF_ZERO;
	file_ptr  = kmem_remote_alloc( inode_cxy , &req );

    if( file_ptr == NULL ) return -1;

    // get type, ctx, mapper, and buffer from inode descriptor
    type   = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );
    ctx    = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->ctx ) );
    mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
    pipe   = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->pipe ) );

    // initializes new file descriptor
    hal_remote_s32( XPTR( inode_cxy , &file_ptr->type     ) , type );
    hal_remote_s32( XPTR( inode_cxy , &file_ptr->attr     ) , attr );
    hal_remote_spt( XPTR( inode_cxy , &file_ptr->ctx      ) , ctx );
    hal_remote_s32( XPTR( inode_cxy , &file_ptr->offset   ) , 0 );
    hal_remote_spt( XPTR( inode_cxy , &file_ptr->inode    ) , inode_ptr );
    hal_remote_spt( XPTR( inode_cxy , &file_ptr->mapper   ) , mapper );
    hal_remote_spt( XPTR( inode_cxy , &file_ptr->pipe     ) , pipe );

    remote_rwlock_init( XPTR( inode_cxy , &file_ptr->lock ), LOCK_VFS_FILE );

    *file_xp = XPTR( inode_cxy , file_ptr );

#if DEBUG_VFS_FILE_CREATE
if( (DEBUG_VFS_FILE_CREATE < cycle) && (type == FILE_TYPE_REG))
{
    char name[CONFIG_VFS_MAX_NAME_LENGTH];
    vfs_file_get_name( XPTR( inode_cxy , file_ptr ) , name );
    printk("\n[%s] thread[%x,%x] created file (%x,%x) / %s (%s)\n",
    __FUNCTION__, this->process->pid, this->trdid,
    inode_cxy, file_ptr, vfs_inode_type_str(type), name );
}
if( (DEBUG_VFS_FILE_CREATE < cycle) && (type != FILE_TYPE_REG))
{
    printk("\n[%s] thread[%x,%x] created file (%x,%x) / %s\n",
    __FUNCTION__, this->process->pid, this->trdid,
    inode_cxy, file_ptr, vfs_inode_type_str(type) );
}
#endif

    return 0;

}  // end vfs_file_create()

////////////////////////////////////////
void vfs_file_destroy( xptr_t  file_xp )
{
    // get file cluster and local pointer
    vfs_file_t * file_ptr = GET_PTR( file_xp );
    cxy_t        file_cxy = GET_CXY( file_xp );

#if DEBUG_VFS_FILE_DESTROY
char name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_file_get_name( file_xp , name );
thread_t * this = CURRENT_THREAD;
uint32_t cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_FILE_DESTROY < cycle )
printk("\n[%s] thread[%x,%x] enter / file_ptr %x / file_cxy %x / <%s> / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, file_ptr, file_cxy, name, cycle );
#endif

    // release file descriptor
	kmem_req_t req;
	req.type  = KMEM_KCM;
	req.ptr   = file_ptr;
	kmem_remote_free( file_cxy , &req );

#if DEBUG_VFS_FILE_DESTROY
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_FILE_DESTROY < cycle )
printk("\n[%s] thread[%x,%x] exit for <%s> / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, file_ptr, file_cxy, name, cycle );
#endif

}  // end vfs_file_destroy() 

///////////////////////////////////////
void vfs_file_get_name( xptr_t file_xp,
                        char * name )
{
    // get cluster and local pointer on remote file
    vfs_file_t * file_ptr = GET_PTR( file_xp );
    cxy_t        file_cxy = GET_CXY( file_xp );

    // get pointers on remote inode
    vfs_inode_t * inode_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->inode ) ); 
    xptr_t        inode_xp  = XPTR( file_cxy , inode_ptr );

    // call the relevant function
    vfs_inode_get_name( inode_xp , name );
}


//////////////////////////////////////////////////////////////////////////////////////////
//           "syscalls" API related functions
//////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////
error_t vfs_open( xptr_t      root_xp,
		          char      * path,
                  xptr_t      process_xp,
		          uint32_t    flags,
                  uint32_t    mode, 
		          xptr_t    * new_file_xp,
                  uint32_t  * new_file_id )
{
    error_t        error;
    xptr_t         inode_xp;       // extended pointer on target inode
    cxy_t          inode_cxy;      // inode cluster identifier       
    vfs_inode_t  * inode_ptr;      // inode local pointer
    uint32_t       file_attr;      // file descriptor attributes
    uint32_t       lookup_mode;    // lookup working mode        
    xptr_t         file_xp;        // extended pointer on created file descriptor
    uint32_t       file_id;        // created file descriptor index in reference fd_array
    xptr_t         vfs_root_xp;    // extended pointer on VFS root inode
    vfs_inode_t  * vfs_root_ptr;   // local pointer on VFS root inode
    cxy_t          vfs_root_cxy;   // VFS root inode cluster identifier
    xptr_t         lock_xp;        // extended pointer on Inode Tree lock 

    if( mode != 0 )
    {
        printk("\n[ERROR] in %s : the mode parameter is not supported yet\n" );
        return -1;
    }

    thread_t  * this    = CURRENT_THREAD;
    process_t * process = this->process;

    // compute lookup working mode
    lookup_mode = VFS_LOOKUP_OPEN;
    if( (flags & O_DIR    )      )  lookup_mode |= VFS_LOOKUP_DIR;
    if( (flags & O_CREAT  )      )  lookup_mode |= VFS_LOOKUP_CREATE;
    if( (flags & O_EXCL   )      )  lookup_mode |= VFS_LOOKUP_EXCL;
 
#if DEBUG_VFS_OPEN || DEBUG_VFS_ERROR
uint32_t cycle = (uint32_t)hal_get_cycles();
#endif

#if DEBUG_VFS_OPEN
if( DEBUG_VFS_OPEN < cycle )
printk("\n[%s] thread[%x,%x] enter for <%s> / root_inode (%x,%x) / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, path, GET_CXY(root_xp), GET_PTR(root_xp), cycle );
#endif

    // compute attributes for the created file
    file_attr = 0;
    if( (flags & O_RDONLY ) == 0 )  file_attr |= FD_ATTR_WRITE_ENABLE;
    if( (flags & O_WRONLY ) == 0 )  file_attr |= FD_ATTR_READ_ENABLE;
    if( (flags & O_SYNC   )      )  file_attr |= FD_ATTR_SYNC;
    if( (flags & O_APPEND )      )  file_attr |= FD_ATTR_APPEND;
    if( (flags & O_CLOEXEC)      )  file_attr |= FD_ATTR_CLOSE_EXEC;

    // build extended pointer on lock protecting Inode Tree
    vfs_root_xp  = process->vfs_root_xp;
    vfs_root_ptr = GET_PTR( vfs_root_xp );
    vfs_root_cxy = GET_CXY( vfs_root_xp );
    lock_xp      = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock );

    // take lock protecting Inode Tree in read mode
    remote_rwlock_rd_acquire( lock_xp );

    // get extended pointer on target inode
    error = vfs_lookup( root_xp,
                        path,
                        lookup_mode,
                        &inode_xp,
                        NULL );

    // release lock protecting Inode Tree
    remote_rwlock_rd_release( lock_xp );

    if( error )
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot get inode <%s>\n",
__FUNCTION__ , process->pid, this->trdid , path );
#endif
        return -1;
    }

    // get target inode cluster and local pointer
    inode_cxy = GET_CXY( inode_xp );
    inode_ptr = GET_PTR( inode_xp );
    
#if (DEBUG_VFS_OPEN & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_OPEN < cycle )
printk("\n[%s] thread[%x,%x] found inode(%x,%x) for <%s>\n",
__FUNCTION__, process->pid, this->trdid, inode_cxy, inode_ptr, path );
#endif

    // create a new file descriptor in cluster containing inode
    error = vfs_file_create( inode_xp , file_attr , &file_xp );

    if( error )  return error;

#if (DEBUG_VFS_OPEN & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_OPEN < cycle )
printk("\n[%s] thread[%x,%x] created file descriptor (%x,%x) for <%s>\n",
__FUNCTION__, process->pid, this->trdid, GET_CXY(file_xp), GET_PTR(file_xp), path );
#endif

    // allocate and register a new file descriptor index in reference process
    error = process_fd_register( process_xp , file_xp , &file_id );

    if( error ) return error;

    // get new file descriptor cluster and local pointer
    cxy_t        file_cxy = GET_CXY( file_xp );
    vfs_file_t * file_ptr = GET_PTR( file_xp );

    //get file type
    uint32_t file_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ));

    // link the client thead to the pipe for a FIFO type
    if( file_type == FILE_TYPE_FIFO )
    {
        // get local pointer on attached pipe
        pipe_t * pipe = hal_remote_lpt( XPTR( file_cxy , &file_ptr->pipe ));

        // build extended pointer on client thread
        xptr_t thread_xp = XPTR( local_cxy , this );

        // update pipe descriptor
        if( flags & O_RDONLY ) hal_remote_s64( XPTR(file_cxy,&pipe->reader_xp) , thread_xp );
        if( flags & O_WRONLY ) hal_remote_s64( XPTR(file_cxy,&pipe->writer_xp) , thread_xp );
    }

#if DEBUG_VFS_OPEN
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_OPEN < cycle )
printk("\n[%s] thread[%x,%x] exit for <%s> / fdid %d / file(%x,%x) / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, path, file_id,
GET_CXY( file_xp ), GET_PTR( file_xp ), cycle );
#endif

    // success
    *new_file_xp = file_xp;
    *new_file_id = file_id;
    return 0;

}  // end vfs_open()

///////////////////////////////////////////
uint32_t vfs_user_move( bool_t   to_buffer,
                        xptr_t   file_xp,
                        void   * buffer,
                        uint32_t count )
{
    cxy_t              file_cxy;       // remote file descriptor cluster 
    vfs_file_t       * file_ptr;       // remote file descriptor local pointer
    mapper_t         * mapper;         // local pointer on file mapper
    vfs_inode_t      * inode;          // local pointer on file inode
    vfs_file_type_t   type;           // inode type
    uint32_t           offset;         // current offset in file
    uint32_t           size;           // current file size
    uint32_t           nbytes;         // number of bytes actually transfered
    error_t            error;

// check argument
assert( __FUNCTION__, (file_xp != XPTR_NULL), "file_xp == XPTR_NULL" );

    // get cluster and local pointer on remote file descriptor
    file_cxy  = GET_CXY( file_xp );
    file_ptr  = GET_PTR( file_xp );

    // get various infos from remote file descriptor 
    type   = hal_remote_l32( XPTR( file_cxy , &file_ptr->type   ) );
    offset = hal_remote_l32( XPTR( file_cxy , &file_ptr->offset ) );
    mapper = hal_remote_lpt( XPTR( file_cxy , &file_ptr->mapper ) );
    inode  = hal_remote_lpt( XPTR( file_cxy , &file_ptr->inode  ) );
    size   = hal_remote_l32( XPTR( file_cxy , &inode->size      ) );
   
// check inode type
assert( __FUNCTION__, (type == FILE_TYPE_REG), "bad inode type" );

#if DEBUG_VFS_USER_MOVE || DEBUG_VFS_ERROR
uint32_t      cycle      = (uint32_t)hal_get_cycles();
thread_t    * this       = CURRENT_THREAD;
#endif

#if DEBUG_VFS_USER_MOVE
char          name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_inode_get_name( XPTR( file_cxy , inode ) , name );
if( cycle > DEBUG_VFS_USER_MOVE )
{
    if( to_buffer ) 
    printk("\n[%s] thread[%x,%x] enter / %d bytes / map(%s) -> buf(%x) / offset %d / cycle %d\n",
    __FUNCTION__ , this->process->pid, this->trdid, count, name, buffer, offset, cycle );
    else            
    printk("\n[%s] thread[%x,%x] enter / %d bytes / buf(%x) -> map(%s) / offset %d / cycle %d\n",
    __FUNCTION__ , this->process->pid, this->trdid, count, buffer, name, offset, cycle );
}
#endif

    if( to_buffer ) // => compute the number of bytes to move and make the move
    {
        // compute number of bytes to move
        if     ( size <= offset )         nbytes = 0; 
        else if( size < offset + count )  nbytes = size - offset;
        else                              nbytes = count;

        // move data from mapper to buffer when required
        if( nbytes > 0 )
        {      
            error = mapper_move_user( XPTR( file_cxy , mapper ),
                                      to_buffer,
                                      offset,
                                      buffer,
                                      nbytes );
        }
        else
        {
            error = 0;
        }
    }
    else // to mapper => make the move and update the file size if required
    {
        nbytes = count;

        // move data from buffer to mapper
        error = mapper_move_user( XPTR( file_cxy , mapper ),
                                  to_buffer,
                                  offset,
                                  buffer,
                                  count );

        // update file size in inode if required
        if( offset + count > size ) 
        {
            vfs_inode_update_size( XPTR( file_cxy , inode ) , offset + count );
        }
    }
        
    if( error ) 
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s thread[%x,%x] cannot move data",
__FUNCTION__, this->process->pid, this->trdid );
#endif
        return -1;
    }

    // update file offset in file descriptor
    hal_remote_atomic_add( XPTR( file_cxy , &file_ptr->offset ) , nbytes );

#if DEBUG_VFS_USER_MOVE
cycle = (uint32_t)hal_get_cycles();
if( cycle > DEBUG_VFS_USER_MOVE )
{
    if( to_buffer ) 
    printk("\n[%s] thread[%x,%x] exit / %d bytes moved from mapper to buffer\n",
    __FUNCTION__ , this->process->pid, nbytes );
    else            
    printk("\n[%s] thread[%x,%x] exit / %d bytes moved from buffer to mapper\n",
    __FUNCTION__ , this->process->pid, nbytes );
}
#endif

    return nbytes;

}  // end vfs_user_move()

////////////////////////////////////////////
error_t vfs_kernel_move( bool_t   to_buffer,
                         xptr_t   file_xp,
                         xptr_t   buffer_xp,
                         uint32_t size )
{
    cxy_t              file_cxy;     // remote file descriptor cluster 
    vfs_file_t       * file_ptr;     // remote file descriptor local pointer
    vfs_file_type_t   inode_type;   // remote file type
    uint32_t           file_offset;  // current offset in file
    mapper_t         * mapper_ptr;   // remote mapper local pointer
    xptr_t             mapper_xp;    // remote mapper extended pointer
    error_t            error;

// check argument
assert( __FUNCTION__, (file_xp != XPTR_NULL) , "file_xp == XPTR_NULL" );

    // get cluster and local pointer on remote file descriptor
    file_cxy  = GET_CXY( file_xp );
    file_ptr  = GET_PTR( file_xp );

    // get inode type from remote file descriptor
    inode_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type   ) );

// check inode type
assert( __FUNCTION__, (inode_type == FILE_TYPE_REG), "bad file type" );

    // get mapper pointers and file offset from file descriptor
    file_offset = hal_remote_l32( XPTR( file_cxy , &file_ptr->offset ) );
    mapper_ptr  = hal_remote_lpt( XPTR( file_cxy , &file_ptr->mapper ) );
    mapper_xp   = XPTR( file_cxy , mapper_ptr );

    // move data between mapper and buffer
    error = mapper_move_kernel( mapper_xp,
                                to_buffer,
                                file_offset,
                                buffer_xp,
                                size );
    if( error ) 
    {
        printk("\n[ERROR] in %s : cannot move data", __FUNCTION__ );
        return -1;
    }

#if DEBUG_VFS_KERNEL_MOVE
char          name[CONFIG_VFS_MAX_NAME_LENGTH];
uint32_t      cycle      = (uint32_t)hal_get_cycles();
thread_t    * this       = CURRENT_THREAD;
cxy_t         buffer_cxy = GET_CXY( buffer_xp );
void        * buffer_ptr = GET_PTR( buffer_xp );
vfs_inode_t * inode      = hal_remote_lpt( XPTR( file_cxy , &file_ptr->inode ) );
vfs_inode_get_name( XPTR( file_cxy , inode ) , name );
if( cycle > DEBUG_VFS_KERNEL_MOVE )
{
    if( to_buffer ) 
    printk("\n[%s] thread[%x,%x] moves %d bytes from <%s> mapper to buffer(%x,%x) / cycle %d\n",
    __FUNCTION__ , this->process->pid, this->trdid, size, name, buffer_cxy, buffer_ptr );
    else            
    printk("\n[%s] thread[%x,%x] moves %d bytes from buffer(%x,%x) to <%s> mapper / cycle %d\n",
    __FUNCTION__ , this->process->pid, this->trdid, size, buffer_cxy, buffer_ptr, name );
}
#endif

    return 0;

}  // end vfs_kernel_move()

//////////////////////////////////////
error_t vfs_lseek( xptr_t     file_xp,
                   uint32_t   offset,
                   uint32_t   whence, 
                   uint32_t * new_offset )
{
    xptr_t         offset_xp;
    xptr_t         lock_xp;
    xptr_t         size_xp;
    cxy_t          file_cxy;
    vfs_file_t  *  file_ptr;
    vfs_inode_t *  inode_ptr;
    uint32_t       new;

// check arguments
assert( __FUNCTION__, (file_xp != XPTR_NULL) , "file_xp == XPTR_NULL" );
assert( __FUNCTION__, (new_offset != NULL )  , "new_offset == NULL" );

    // get cluster and local pointer on remote file descriptor
    file_cxy = GET_CXY( file_xp );
    file_ptr = GET_PTR( file_xp );

    // get local pointer on remote inode
    inode_ptr = (vfs_inode_t *)hal_remote_lpt( XPTR( file_cxy , &file_ptr->inode ) );

    // build extended pointers on lock, offset and size
    offset_xp = XPTR( file_cxy , &file_ptr->offset );
    lock_xp   = XPTR( file_cxy , &file_ptr->lock );
    size_xp   = XPTR( file_cxy , &inode_ptr->size );

    // take file descriptor lock
    remote_rwlock_wr_acquire( lock_xp );

    if      ( whence == SEEK_CUR )   // new = current + offset
    {
        new = hal_remote_l32( offset_xp ) + offset;
    }
    else if ( whence == SEEK_SET )   // new = offset
    {
        new = offset;
    }
    else if ( whence == SEEK_END )   // new = size + offset
    { 
        new = hal_remote_l32( size_xp ) + offset;
    }
    else
    {
        printk("\n[ERROR] in %s : illegal whence value\n", __FUNCTION__ );
        remote_rwlock_wr_release( lock_xp );
        return -1;
    }

#if DEBUG_VFS_LSEEK
uint32_t   cycle = (uint32_t)hal_get_cycles();
thread_t * this  = CURRENT_THREAD;
char       name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_inode_get_name( XPTR( file_cxy , inode_ptr ) , name );
if( cycle > DEBUG_VFS_LSEEK )
printk("\n[%s] thread[%x,%x] for <%s> / new offset %d / cycle %d\n",
__FUNCTION__ , this->process->pid, this->trdid, name, new, cycle );
#endif

    // set new offset
    hal_remote_s32( offset_xp , new );

    // release file descriptor lock
    remote_rwlock_wr_release( lock_xp );

    // success
    *new_offset = new;
    return 0;

}  // vfs_lseek()

////////////////////////////////////
error_t vfs_close( xptr_t   file_xp,
                   uint32_t file_id )
{
    cxy_t         file_cxy;         // cluster containing the file descriptor.
    vfs_file_t  * file_ptr;         // local pointer on file descriptor
    cxy_t         owner_cxy;        // process owner cluster
    pid_t         pid;              // process identifier
    lpid_t        lpid;             // process local index
    xptr_t        root_xp;          // root of xlist (processes , or dentries)
    xptr_t        lock_xp;          // lock protecting the xlist
    xptr_t        iter_xp;          // iterator on xlist
    mapper_t    * mapper_ptr;       // local pointer on associated mapper
    vfs_inode_t * inode_ptr;        // local pointer on associated inode
    uint32_t      size;             // current file size (from inode descriptor)
    error_t       error;

    char          name[CONFIG_VFS_MAX_NAME_LENGTH];  // file name

// check argument
assert( __FUNCTION__, (file_xp != XPTR_NULL) , "file_xp is XPTR_NULL" );

    thread_t  * this    = CURRENT_THREAD;
    process_t * process = this->process;
    cluster_t * cluster = LOCAL_CLUSTER;

    // get file name
    vfs_file_get_name( file_xp , name );
    
#if DEBUG_VFS_CLOSE
uint32_t cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_CLOSE < cycle )
printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, name, cycle );
#endif

    // get cluster and local pointer on remote file descriptor
    file_cxy = GET_CXY( file_xp );
    file_ptr = GET_PTR( file_xp );

    //////// 1) update all dirty pages from mapper to device

    // get local pointer on mapper associated to file
    mapper_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->mapper ) );

    // copy all dirty pages from mapper to device
    error = mapper_sync( XPTR( file_cxy , mapper_ptr ) );

    if( error )
    {
        printk("\n[ERROR] in %s : cannot synchronise dirty pages for <%s>\n",
        __FUNCTION__, name ); 
        return -1;
    }

#if DEBUG_VFS_CLOSE 
if( DEBUG_VFS_CLOSE < cycle )
printk("\n[%s] thread[%x,%x] synchronised mapper of <%s> to device\n",
__FUNCTION__, process->pid, this->trdid, name );
#endif

    //////// 2) update file size in all parent directory mapper(s) and update device

    // get local pointer on remote inode
    inode_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->inode ) );

    // get file size from remote inode
    size = hal_remote_l32( XPTR( file_cxy , &inode_ptr->size ) );

    // get root of list of parents dentry
    root_xp = XPTR( file_cxy , &inode_ptr->parents );

    // loop on all parents
    XLIST_FOREACH( root_xp , iter_xp )
    {
        // get pointers on parent directory dentry
        xptr_t         parent_dentry_xp  = XLIST_ELEMENT( iter_xp , vfs_dentry_t , parents );
        cxy_t          parent_cxy        = GET_CXY( parent_dentry_xp );
        vfs_dentry_t * parent_dentry_ptr = GET_PTR( parent_dentry_xp );

        // get local pointer on parent directory inode
        vfs_inode_t * parent_inode_ptr = hal_remote_lpt( XPTR( parent_cxy, 
                                                         &parent_dentry_ptr->parent ) );

        // get local pointer on parent directory mapper
        mapper_t * parent_mapper_ptr = hal_remote_lpt( XPTR( parent_cxy,
                                                       &parent_inode_ptr->mapper ) );
 
        // update dentry size in parent directory mapper
        error = vfs_fs_update_dentry( XPTR( parent_cxy , parent_inode_ptr ),
                                      parent_dentry_ptr );

        if( error )
        {
            printk("\n[ERROR] in %s : cannot update size in parent\n",
            __FUNCTION__ ); 
            return -1;
        }

#if DEBUG_VFS_CLOSE 
char parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_inode_get_name( XPTR( parent_cxy , parent_inode_ptr ) , parent_name );
if( DEBUG_VFS_CLOSE < cycle )
printk("\n[%s] thread[%x,%x] updated <%s> in <%s> / size = %d bytes\n",
__FUNCTION__, process->pid, this->trdid, name, parent_name, size );
#endif

        // copy all dirty pages from parent mapper to device
        error = mapper_sync( XPTR( parent_cxy , parent_mapper_ptr ) );

        if( error )
        {
            printk("\n[ERROR] in %s : cannot synchronise parent mapper to device\n",
            __FUNCTION__ ); 
            return -1;
        }

#if DEBUG_VFS_CLOSE 
if( DEBUG_VFS_CLOSE < cycle )
printk("\n[%s] thread[%x,%x] synchonized mapper of parent <%s> to device\n",
__FUNCTION__, process->pid, this->trdid, parent_name );
#endif

    }

    //////// 3) loop on the process copies to reset all fd_array[file_id] entries

    // get owner process cluster and lpid
    pid        = process->pid;
    owner_cxy  = CXY_FROM_PID( pid );
    lpid       = LPID_FROM_PID( pid );

    // get extended pointers on copies root and lock
    root_xp = XPTR( owner_cxy , &cluster->pmgr.copies_root[lpid] );
    lock_xp = XPTR( owner_cxy , &cluster->pmgr.copies_lock[lpid] );

    // take the lock protecting the list of copies
    remote_queuelock_acquire( lock_xp );

    XLIST_FOREACH( root_xp , iter_xp )
    {
        xptr_t      process_xp  = XLIST_ELEMENT( iter_xp , process_t , copies_list );
        cxy_t       process_cxy = GET_CXY( process_xp );
        process_t * process_ptr = GET_PTR( process_xp );

        xptr_t entry_xp = XPTR( process_cxy , &process_ptr->fd_array.array[file_id] );
        hal_remote_s64( entry_xp , XPTR_NULL );
        hal_fence();
    }    

    // release the lock protecting the list of copies
    remote_queuelock_release( lock_xp );

#if DEBUG_VFS_CLOSE 
if( DEBUG_VFS_CLOSE < cycle )
printk("\n[%s] thread[%x,%x] reset all fd-array copies for <%s>\n",
__FUNCTION__, process->pid, this->trdid, name );
#endif

    //////// 4) release memory allocated to file descriptor in remote cluster

    vfs_file_destroy( file_xp );

#if DEBUG_VFS_CLOSE
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_CLOSE < cycle )
printk("\n[%s] thread[%x,%x] exit / closed <%s> in process %x / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, name, process->pid, cycle );
#endif

    return 0;

}  // end vfs_close()

////////////////////////////////////
error_t vfs_mkdir( xptr_t   root_xp,
                   char   * path,
                   uint32_t rights )
{
    error_t        error;
    xptr_t         vfs_root_xp;        // extended pointer on VFS root inode
    vfs_inode_t  * vfs_root_ptr;       // local pointer on VFS root inode
    cxy_t          vfs_root_cxy;       // VFS root inode cluster identifier
    xptr_t         lock_xp;            // extended pointer on lock protecting Inode Tree
    xptr_t         inode_xp;           // extended pointer on new directory inode
    vfs_inode_t  * inode_ptr;          // local pointer on new directory inode
    cxy_t          inode_cxy;          // new directory inode cluster identifier
    xptr_t         dentry_xp;          // extended pointer on new dentry
    vfs_dentry_t * dentry_ptr;         // new dentry local pointer
    xptr_t         parent_xp;          // extended pointer on parent inode
    vfs_inode_t  * parent_ptr;         // local pointer on parent inode  
    cxy_t          parent_cxy;         // parent inode cluster identifier
    vfs_ctx_t    * parent_ctx_ptr;     // local pointer on parent inode context
    uint32_t       parent_fs_type;     // parent inode file system type

    xptr_t         parents_root_xp;    // extended pointer on parents field in inode (root)
    xptr_t         parents_entry_xp;   // extended pointer on parents field in dentry
    xptr_t         children_xhtab_xp;  // extended pointer on children field in inode (root)
    xptr_t         children_entry_xp;  // extended pointer on children field in dentry

    char           last_name[CONFIG_VFS_MAX_NAME_LENGTH];

    thread_t  * this    = CURRENT_THREAD;
    process_t * process = this->process;

#if DEBUG_VFS_MKDIR
char root_name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_inode_get_name( root_xp , root_name );
uint32_t   cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_MKDIR < cycle )
printk("\n[%s] thread[%x,%x] enter / root <%s> / path <%s> / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, root_name, path, cycle );
#endif

    // build extended pointer on lock protecting Inode-Tree (in VFS root inode)
    vfs_root_xp  = process->vfs_root_xp;
    vfs_root_ptr = GET_PTR( vfs_root_xp );
    vfs_root_cxy = GET_CXY( vfs_root_xp );
    lock_xp      = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock );

    // take the lock protecting Inode Tree in write mode
    remote_rwlock_wr_acquire( lock_xp );

    // 1. get pointers on parent inode
    error = vfs_lookup( root_xp,
                        path,
                        VFS_LOOKUP_DIR | VFS_LOOKUP_PARENT,
                        &parent_xp,
                        last_name );
    if( error )
    {
        remote_rwlock_wr_release( lock_xp );
        printk("\n[ERROR] in %s : cannot get parent inode for <%s>\n",
        __FUNCTION__, path );
        return -1;
    }

    // get parent inode cluster and local pointer
    parent_cxy = GET_CXY( parent_xp );
    parent_ptr = GET_PTR( parent_xp );

#if( DEBUG_VFS_MKDIR & 1 )
if( DEBUG_VFS_MKDIR < cycle )
printk("\n[%s] thread[%x,%x] get parent inode (%x,%x) for <%s>\n",
__FUNCTION__, process->pid, this->trdid, parent_cxy, parent_ptr, path );
#endif

    // get parent inode context, and FS type
    parent_ctx_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) );
    parent_fs_type = hal_remote_l32( XPTR( parent_cxy , &parent_ctx_ptr->type ) );

    // 2. create one new dentry in parent cluster
    error = vfs_dentry_create( parent_cxy,
                               parent_fs_type,
                               last_name,
                               &dentry_xp );
    if( error )
    {
        remote_rwlock_wr_release( lock_xp );
        printk("\n[ERROR] in %s : cannot create new dentry in cluster %x for <%s>\n",
        __FUNCTION__, parent_cxy, path );
        return -1;
    }

    // get local pointer on dentry 
    dentry_ptr = GET_PTR( dentry_xp );

#if( DEBUG_VFS_MKDIR & 1 )
if( DEBUG_VFS_MKDIR < cycle )
printk("\n[%s] thread[%x,%x] created new dentry (%x,%x) for <%s>\n",
__FUNCTION__, process->pid, this->trdid, parent_cxy, dentry_ptr, path );
#endif

    // 3. create new directory inode
    // TODO : define attr / uid / gid
    uint32_t attr = 0;
    uint32_t uid  = 0;
    uint32_t gid  = 0;

    // select a target cluster for new inode
    inode_cxy = cluster_random_select();
    
    // create inode
    error = vfs_inode_create( inode_cxy,
                              parent_fs_type,
                              attr,
                              rights,
                              uid,
                              gid,
                              &inode_xp );
    if( error )
    {
        remote_rwlock_wr_release( lock_xp );
        printk("\n[ERROR] in %s : cannot create new inode in cluster %x for <%s>\n",
         __FUNCTION__ , inode_cxy , path );
        vfs_dentry_destroy( dentry_xp );
        return -1;
    }

    // get new inode local pointer
    inode_ptr = GET_PTR( inode_xp );

    // update inode "type" field
    hal_remote_s32( XPTR( inode_cxy , &inode_ptr->type ) , FILE_TYPE_DIR ); 
    
#if(DEBUG_VFS_MKDIR & 1)
if( DEBUG_VFS_MKDIR < cycle )
printk("\n[%s] thread[%x,%x] created new inode (%x,%x) for <%s>\n",
__FUNCTION__ , process->pid, this->trdid, inode_cxy, inode_ptr, path );
#endif

    // 4. register dentry in new inode list of parents
    parents_root_xp  = XPTR( inode_cxy  , &inode_ptr->parents );
    parents_entry_xp = XPTR( parent_cxy , &dentry_ptr->parents );
    xlist_add_first( parents_root_xp , parents_entry_xp );
    hal_remote_atomic_add( XPTR( inode_cxy , &inode_ptr->links ) , 1 );

    // 5. register dentry in parent inode 
    children_xhtab_xp = XPTR( parent_cxy , &parent_ptr->children );
    children_entry_xp = XPTR( parent_cxy , &dentry_ptr->children );
    xhtab_insert( children_xhtab_xp , last_name , children_entry_xp );

    // 6. update "parent" and "child_xp" fields in dentry
    hal_remote_s64( XPTR( parent_cxy , &dentry_ptr->child_xp ) , inode_xp );
    hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->parent ) , parent_ptr );

#if(DEBUG_VFS_MKDIR & 1)
if( DEBUG_VFS_MKDIR < cycle )
printk("\n[%s] thread[%x,%x] updated Inode Tree for <%s>\n",
__FUNCTION__, process->pid, this->trdid, path );
#endif

    // 7. create the two special dentries <.> and <..> in new directory
    // both the new directory mapper, and the Inode Tree are updated
    error = vfs_add_special_dentries( inode_xp,
                                      parent_xp );

    if( error )
    {
        remote_rwlock_wr_release( lock_xp );
        printk("\n[ERROR] in %s : cannot create new inode in cluster %x for <%s>\n",
        __FUNCTION__ , inode_cxy , path );
        vfs_dentry_destroy( dentry_xp );
        return -1;
    }

    // release the lock protecting Inode Tree
    remote_rwlock_wr_release( lock_xp );

    // 8. update parent directory mapper 
    //    and synchronize the parent directory on IOC device
    error = vfs_fs_add_dentry( parent_xp,
                               dentry_ptr );
    if( error )
    {
        printk("\n[ERROR] in %s : cannot update parent directory for <%s>\n",
        __FUNCTION__, path );
        return -1;
    }

#if(DEBUG_VFS_MKDIR & 1)
if( DEBUG_VFS_MKDIR < cycle )
printk("\n[%s] thread[%x,%x] updated parent dir (mapper and IOC) for <%s>\n",
__FUNCTION__, process->pid, this->trdid, path );
#endif

    return 0;

}  // end vfs_mkdir()

///////////////////////////////////////
error_t vfs_link( xptr_t   old_root_xp,
                  char   * old_path,
                  xptr_t   new_root_xp,
                  char   * new_path )
{
    error_t        error;
    xptr_t         vfs_root_xp;        // extended pointer on VFS root inode
    vfs_inode_t  * vfs_root_ptr;       // local pointer on VFS root inode
    cxy_t          vfs_root_cxy;       // VFS root inode cluster identifier
    xptr_t         lock_xp;            // extended pointer on lock protecting Inode Tree
    xptr_t         inode_xp;           // extended pointer on target inode
    vfs_inode_t  * inode_ptr;          // local pointer on target inode
    cxy_t          inode_cxy;          // target inode cluster identifier
    uint32_t       inode_type;         // target inode type
    vfs_ctx_t    * inode_ctx_ptr;      // local pointer on target inode context
    uint32_t       inode_fs_type;      // target inode file system type
    xptr_t         dentry_xp;          // extended pointer on new dentry
    vfs_dentry_t * dentry_ptr;         // target dentry local pointer
    xptr_t         new_parent_xp;      // extended pointer on new parent inode
    vfs_inode_t  * new_parent_ptr;     // local pointer on new parent inode  
    cxy_t          new_parent_cxy;     // new parent inode cluster identifier

    xptr_t         parents_root_xp;    // extended pointer on parents field in inode (root)
    xptr_t         parents_entry_xp;   // extended pointer on parents field in dentry
    xptr_t         children_xhtab_xp;  // extended pointer on children field in inode (root)
    xptr_t         children_entry_xp;  // extended pointer on children field in dentry

    char           new_name[CONFIG_VFS_MAX_NAME_LENGTH];

    thread_t  * this    = CURRENT_THREAD;
    process_t * process = this->process;

#if DEBUG_VFS_LINK
char old_root_name[CONFIG_VFS_MAX_NAME_LENGTH];
char new_root_name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_inode_get_name( old_root_xp , old_root_name );
vfs_inode_get_name( new_root_xp , new_root_name );
uint32_t   cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_LINK < cycle )
printk("\n[%s] thread[%x,%x] enter / old_root <%s> / old_path <%s> / "
"new_root <%s> / new_path <%s> / cycle %d\n",
__FUNCTION__, process->pid, this->trdid,
old_root_name, old_path, new_root_name, new_path, cycle );
#endif

    // build extended pointer on lock protecting Inode Tree (in VFS root inode)
    vfs_root_xp  = process->vfs_root_xp;
    vfs_root_ptr = GET_PTR( vfs_root_xp );
    vfs_root_cxy = GET_CXY( vfs_root_xp );
    lock_xp      = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock );

    // take the lock protecting Inode Tree in write mode
    remote_rwlock_wr_acquire( lock_xp );

    // get extended pointer on target inode
    error = vfs_lookup( old_root_xp,
                        old_path,
                        0,
                        &inode_xp,
                        NULL );
    if( error )
    {
        remote_rwlock_wr_release( lock_xp );
        printk("\n[ERROR] in %s : cannot get target inode for <%s>\n",
        __FUNCTION__, old_path );
        return -1;
    }

#if( DEBUG_VFS_LINK & 1 )
if( DEBUG_VFS_LINK < cycle )
printk("\n[%s] thread[%x,%x] get child inode (%x,%x) for <%s>\n",
__FUNCTION__, process->pid, this->trdid,
GET_CXY(inode_xp), GET_PTR(inode_xp), old_path, cycle );
#endif

    // get extended pointer on parent inode in new path
    error = vfs_lookup( new_root_xp,
                        new_path,
                        VFS_LOOKUP_PARENT,
                        &new_parent_xp,
                        new_name );
    if( error )
    {
        remote_rwlock_wr_release( lock_xp );
        printk("\n[ERROR] in %s : cannot get parent inode for <%s>\n",
        __FUNCTION__, new_path );
        return -1;
    }

#if( DEBUG_VFS_LINK & 1 )
if( DEBUG_VFS_LINK < cycle )
printk("\n[%s] thread[%x,%x] get parent inode (%x,%x) for <%s>\n",
__FUNCTION__, process->pid, this->trdid,
GET_CXY(new_parent_xp), GET_PTR(new_parent_xp), new_path );
#endif

    // get target inode cluster and local pointer
    inode_cxy = GET_CXY( inode_xp );
    inode_ptr = GET_PTR( inode_xp );

    // get target inode type, context, and FS type
    inode_type        = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );
    inode_ctx_ptr     = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->ctx ) );
    inode_fs_type     = hal_remote_l32( XPTR( inode_cxy , &inode_ctx_ptr->type ) );

    // get new parent inode cluster an local pointer
    new_parent_ptr = GET_PTR( new_parent_xp );
    new_parent_cxy = GET_CXY( new_parent_xp );

    ///////////////////////////////////////////////////////////////////////
    if( (inode_type == FILE_TYPE_REG) || (inode_type == FILE_TYPE_DIR) )
    {
        // 1. create one new dentry
        error = vfs_dentry_create( new_parent_cxy,
                                   inode_fs_type,
                                   new_name,
                                   &dentry_xp );
        if( error )
        {
            remote_rwlock_wr_release( lock_xp );
            printk("\n[ERROR] in %s : cannot create new dentry for <%s>\n",
            __FUNCTION__, new_path );
            return -1;
        }

        // get local pointer on dentry 
        dentry_ptr = GET_PTR( dentry_xp );

        // 2. register dentry in target inode 
        parents_root_xp  = XPTR( inode_cxy      , &inode_ptr->parents );
        parents_entry_xp = XPTR( new_parent_cxy , &dentry_ptr->parents );
        xlist_add_first( parents_root_xp , parents_entry_xp );
        hal_remote_atomic_add( XPTR( inode_cxy , &inode_ptr->links ) , 1 );

        // 3. register dentry in parent inode 
        children_xhtab_xp = XPTR( new_parent_cxy , &new_parent_ptr->children );
        children_entry_xp = XPTR( new_parent_cxy , &dentry_ptr->children );
        xhtab_insert( children_xhtab_xp , new_name , children_entry_xp );

        // 4. update "parent" and "child_xp" fields in dentry
        hal_remote_s64( XPTR( new_parent_cxy , &dentry_ptr->child_xp ) , inode_xp );
        hal_remote_spt( XPTR( new_parent_cxy , &dentry_ptr->parent ) , new_parent_ptr );

#if(DEBUG_VFS_LINK & 1)
if( DEBUG_VFS_LINK < cycle )
printk("\n[%s] thread[%x,%x] updated Inode Tree / old <%s> / new <%s>\n",
__FUNCTION__, process->pid, this->trdid, old_path, new_path );
vfs_display( new_parent_xp ); 
#endif

        // release the lock protecting Inode Tree
        remote_rwlock_wr_release( lock_xp );

        // 5. update new parent directory mapper in Inode Tree
        //    and synchronize the parent directory on IOC device
        if (new_parent_cxy == local_cxy)
        error = vfs_fs_add_dentry( new_parent_xp,
                                   dentry_ptr );
        if( error )
        {
            printk("\n[ERROR] in %s : cannot update new parent directory for <%s>\n",
            __FUNCTION__, new_path );
            return -1;
        }

#if(DEBUG_VFS_LINK & 1)
if( DEBUG_VFS_LINK < cycle )
printk("\n[%s] thread[%x,%x] updated new parent dir (mapper and IOC) / old <%s> / new <%s>\n",
__FUNCTION__, process->pid, this->trdid, old_path, new_path );
#endif
        return 0;
    }
    else
    {
        // release the lock protecting Inode Tree
        remote_rwlock_wr_release( lock_xp );

        printk("\n[ERROR] in %s : unsupported inode type %s\n",
        __FUNCTION__ , vfs_inode_type_str( inode_type ) );
        return -1;
    }

}  // end vfs_link()

/////////////////////////////////////
error_t vfs_unlink( xptr_t   root_xp,
                    char   * path )
{
    error_t           error;
    xptr_t            vfs_root_xp;        // extended pointer on VFS root inode
    vfs_inode_t     * vfs_root_ptr;       // local_pointer on VFS root inode
    cxy_t             vfs_root_cxy;       // VFS root inode cluster identifier
    xptr_t            lock_xp;            // extended pointer on lock protecting Inode Tree
    xptr_t            parent_xp;          // extended pointer on target inode
    cxy_t             parent_cxy;         // target inode cluster identifier       
    vfs_inode_t     * parent_ptr;         // target inode local pointer
    xptr_t            inode_xp;           // extended pointer on target inode
    cxy_t             inode_cxy;          // target inode cluster identifier       
    vfs_inode_t     * inode_ptr;          // target inode local pointer
    uint32_t          inode_links;        // target inode links count
    vfs_file_type_t  inode_type;         // target inode type
    uint32_t          inode_children;     // target inode number of children
    xptr_t            dentry_xp;          // extended pointer on dentry to unlink
    vfs_dentry_t    * dentry_ptr;         // local pointer on dentry to unlink
    vfs_ctx_t       * ctx_ptr;            // local pointer on FS context
    vfs_fs_type_t     fs_type;            // File system type

    char              child_name[CONFIG_VFS_MAX_NAME_LENGTH];   // name of link to remove
    char              parent_name[CONFIG_VFS_MAX_NAME_LENGTH];  // name of parent directory

    thread_t  * this    = CURRENT_THREAD;
    process_t * process = this->process;

#if DEBUG_VFS_UNLINK
uint32_t   cycle = (uint32_t)hal_get_cycles();
char root_name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_inode_get_name( root_xp , root_name );
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] : enter for root <%s> / path <%s> / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, root_name, path, cycle );
#endif

    // build extended pointer on lock protecting Inode Tree (in VFS root inode)
    vfs_root_xp  = process->vfs_root_xp;
    vfs_root_ptr = GET_PTR( vfs_root_xp );
    vfs_root_cxy = GET_CXY( vfs_root_xp );
    lock_xp      = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock );

    // take the lock protecting Inode Tree
    remote_rwlock_wr_acquire( lock_xp );

    // get extended pointer on parent inode
    error = vfs_lookup( root_xp,
                        path,
                        VFS_LOOKUP_PARENT,
                        &parent_xp,
                        child_name );
    if( error ) 
    {
        remote_rwlock_wr_release( lock_xp );
        printk("\n[ERROR] in %s : cannot get parent inode for <%s> in <%s>\n",
        __FUNCTION__, child_name, path );
        return -1;
    }
    
    // get parent inode name, cluster and local pointer
    vfs_inode_get_name( parent_xp , parent_name );
    parent_cxy = GET_CXY( parent_xp );
    parent_ptr = GET_PTR( parent_xp );
 
#if( DEBUG_VFS_UNLINK & 1 )
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] : parent inode <%s> is (%x,%x)\n",
__FUNCTION__, process->pid, this->trdid, parent_name, parent_cxy, parent_ptr );
#endif

    // build extended pointer on parent inode "children" xhtab
    xptr_t children_xp = XPTR( parent_cxy , &parent_ptr->children );

    // try to get extended pointer on dentry from Inode Tree
    dentry_xp = xhtab_lookup( children_xp , child_name );
    
    // when dentry not found in Inode Tree, try to get it from mapper

    if( dentry_xp == XPTR_NULL )           // miss target dentry in Inode Tree
    {

#if( DEBUG_VFS_UNLINK & 1 )
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] : inode <%s> not found => scan parent mapper <%s>\n",
__FUNCTION__, process->pid, this->trdid, child_name , parent_name );
#endif
        // get parent inode FS type
        ctx_ptr    = hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) );
        fs_type    = hal_remote_l32( XPTR( parent_cxy , &ctx_ptr->type ) );

        // select a cluster for new inode
        inode_cxy = cluster_random_select();

        // speculatively insert a new child dentry/inode couple in inode tree
        error = vfs_add_child_in_parent( inode_cxy,
                                         fs_type, 
                                         parent_xp, 
                                         child_name, 
                                         &dentry_xp,
                                         &inode_xp );
        if( error )
        {
            printk("\n[ERROR] in %s : cannot create inode <%s> in Inode Tree\n",
            __FUNCTION__ , child_name );
            return -1;
        }

        // get local pointers on new dentry and new inode descriptors
        inode_ptr  = GET_PTR( inode_xp );
        dentry_ptr = GET_PTR( dentry_xp );

        // scan parent mapper to find the missing dentry, and complete 
        // initialisation of new dentry and new inode descriptors In Inode Tree
        error = vfs_fs_new_dentry_from_mapper( parent_xp,
                                               dentry_ptr );
        if ( error ) 
        {
            printk("\n[ERROR] in %s : cannot get entry <%s> in parent <%s> mapper\n",
            __FUNCTION__ , child_name, parent_name );
            return -1;
        }

#if (DEBUG_VFS_UNLINK & 1)
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] : created missing inode & dentry <%s> in cluster %x\n",
__FUNCTION__, process->pid, this->trdid, child_name, inode_cxy );
#endif

    }
    else                                  // found target dentry in Inode Tree
    {
        dentry_ptr = GET_PTR( dentry_xp );
       
        // get pointer on target inode from dentry
        inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
        inode_cxy = GET_CXY( inode_xp );
        inode_ptr = GET_PTR( inode_xp );
    }

    // At this point the Inode Tree contains the target dentry and child inode
    // we can safely remove this dentry from both the parent mapper, and the Inode Tree.

#if( DEBUG_VFS_UNLINK & 1 )
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] : dentry (%x,%x) / inode (%x,%x)\n",
__FUNCTION__, process->pid, this->trdid, parent_cxy, dentry_ptr, inode_cxy, inode_ptr );
#endif

    // get target inode "type" and "links"
    inode_type   = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );
    inode_links  = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->links ) );

    ///////////////////////////////////////////////////////////////////////
    if( (inode_type == FILE_TYPE_REG) || (inode_type == FILE_TYPE_DIR) )  
    {

#if( DEBUG_VFS_UNLINK & 1 )
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] : unlink inode <%s> / type %s / %d links\n",
__FUNCTION__, process->pid, this->trdid, child_name, 
vfs_inode_type_str(inode_type), inode_links );
#endif
        // 1. Release space allocated to inode on IOC device 
        //    and synchronize the FAT on IOC device if last link.
        if( inode_links == 1 ) 
        {
            // build extended pointer on target inode "children" number
            xptr_t inode_children_xp = XPTR( inode_cxy , &inode_ptr->children.items );

            // get target inode number of children 
            inode_children = hal_remote_l32( inode_children_xp );

            // check no children
            if( inode_children != 0 )
            {
                remote_rwlock_wr_release( lock_xp );
                printk("\n[ERROR] in %s : cannot remove <%s> inode that has children\n",
                __FUNCTION__, path );
                return -1;
            }

            // release space on IOC device
            error = vfs_fs_release_inode( inode_xp ); 

            if( error )
            {
                remote_rwlock_wr_release( lock_xp );
                printk("\n[ERROR] in %s : cannot update FAT mapper to remove <%s> inode\n",
                __FUNCTION__ , path );
                return -1;
            }

#if(DEBUG_VFS_UNLINK & 1)
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] removed <%s> inode from FAT (mapper and IOC device)\n",
__FUNCTION__, process->pid, this->trdid, path );
#endif
        }

        // 2. update parent directory mapper
        //    and synchronize the parent directory on IOC device
        error = vfs_fs_remove_dentry( parent_xp,
                                      dentry_ptr );
        if( error )
        {
            remote_rwlock_wr_release( lock_xp );
            printk("\n[ERROR] in %s : cannot update dentry on device for <%s>\n",
            __FUNCTION__ , path );
            return -1;
        }

#if(DEBUG_VFS_UNLINK & 1)
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] removed <%s> inode from parent dir (mapper and IOC device)\n",
__FUNCTION__, process->pid, this->trdid, path );
#endif
        // 3. remove dentry from Inode Tree (and associated child inode when last link)
        vfs_remove_child_from_parent( dentry_xp );

        // release the lock protecting Inode Tree
        remote_rwlock_wr_release( lock_xp );

#if DEBUG_VFS_UNLINK
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] exit / removed <%s> inode from Inode Tree / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, path, cycle );
#endif
        return 0;
    }
    ////////////////////////////////////////
    else if( inode_type == FILE_TYPE_FIFO )
    {
        // 1. destroy the associated pipe
        pipe_t * pipe    = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->pipe )); 
        pipe_destroy( XPTR( inode_cxy , pipe ) );

#if(DEBUG_VFS_UNLINK & 1)
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] released <%s> pipe\n",
__FUNCTION__, process->pid, this->trdid, path );
#endif

        // 2. remove dentry from Inode Tree (and associated child inode when last link)
        vfs_remove_child_from_parent( dentry_xp );

        // release the lock protecting Inode Tree
        remote_rwlock_wr_release( lock_xp );

#if DEBUG_VFS_UNLINK 
if( DEBUG_VFS_UNLINK < cycle )
printk("\n[%s] thread[%x,%x] exit / removed <%s> inode from Inode Tree / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, path, cycle );
#endif
        return 0;
    }
    else
    {
        remote_rwlock_wr_release( lock_xp );
        printk("\n[ERROR] in %s : unsupported inode type %s\n",
        __FUNCTION__ , vfs_inode_type_str( inode_type ) );
        return -1;
    }

}  // end vfs_unlink() 

////////////////////////////////////////////////
error_t vfs_stat( xptr_t         root_inode_xp,
                  char         * path,
                  struct stat  * st )
{
    error_t       error;
    xptr_t        inode_xp;           // extended pointer on target inode
    vfs_inode_t * inode_ptr;          // local pointer on target inode
    cxy_t         inode_cxy;          // target inode cluster identifier
    xptr_t        vfs_root_xp;        // extended pointer on VFS root inode
    vfs_inode_t * vfs_root_ptr;       // local_pointer on VFS root inode
    cxy_t         vfs_root_cxy;       // VFS root inode cluster identifier
    xptr_t        lock_xp;            // extended pointer on lock protecting Inode Tree

    thread_t  * this    = CURRENT_THREAD;
    process_t * process = this->process;

    // build extended pointer on lock protecting Inode Tree (in VFS root inode)
    vfs_root_xp  = process->vfs_root_xp;
    vfs_root_ptr = GET_PTR( vfs_root_xp );
    vfs_root_cxy = GET_CXY( vfs_root_xp );
    lock_xp      = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock );

    // get the lock protecting Inode Tree in read mode
    remote_rwlock_rd_acquire( lock_xp );

    // get extended pointer on target inode
    error = vfs_lookup( root_inode_xp,
                        path,
                        0,
                        &inode_xp,
                        NULL );

    // release the lock protecting Inode Tree
    remote_rwlock_rd_release( lock_xp );

    if( error )
    {
        printk("\n[ERROR] in %s : cannot found inode <%s>\n",
        __FUNCTION__ , path );
        return -1;
    }

    // get cluster and local pointer on inode descriptor
    inode_ptr = GET_PTR( inode_xp );
    inode_cxy = GET_CXY( inode_xp );

    // get relevant infos from inode descriptor
    uint32_t inum   = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->inum   ) );
    uint32_t size   = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size   ) );
    uint32_t uid    = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->uid    ) );
    uint32_t gid    = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->gid    ) );
    uint32_t type   = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type   ) );
    uint32_t rights = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->rights ) );

    // set stat structure fields 
    st->st_ino  = inum;
    st->st_gid  = gid;
    st->st_uid  = uid;
    st->st_size = size;
    st->st_mode = (type << 16) | rights;

#if DEBUG_VFS_STAT
uint32_t cycle  = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_STAT < cycle )
printk("\n[%s] thread[%x,%x] set stat %x for inode %x in cluster %x / cycle %d\n"
       " %s / inum %d / size %d\n",
__FUNCTION__, process->pid, this->trdid, st, inode_ptr, inode_cxy, cycle,
vfs_inode_type_str( type ), inum, size );
#endif

    return 0;

}  // end vfs_stat()

////////////////////////////////////
error_t vfs_chdir( xptr_t   root_xp,
                   char   * path )
{
    error_t           error;
    xptr_t            inode_xp;           // extended pointer on target inode
    cxy_t             inode_cxy;          // target inode cluster identifier       
    vfs_inode_t     * inode_ptr;          // target inode local pointer
    vfs_file_type_t  inode_type;         // target inode type
    xptr_t            vfs_root_xp;        // extended pointer on VFS root inode
    vfs_inode_t     * vfs_root_ptr;       // local_pointer on VFS root inode
    cxy_t             vfs_root_cxy;       // VFS root inode cluster identifier
    xptr_t            main_lock_xp;       // extended pointer on lock protecting Inode Tree
    xptr_t            ref_xp;             // extended pointer on reference process
    process_t       * ref_ptr;            // local pointer on reference process
    cxy_t             ref_cxy;            // reference process cluster
    xptr_t            cwd_lock_xp;        // extended pointer on lock protecting CWD change
    xptr_t            cwd_xp_xp;          // extended pointer on cwd_xp in reference process

    thread_t  * this    = CURRENT_THREAD;
    process_t * process = this->process;

#if DEBUG_VFS_CHDIR
uint32_t cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_CHDIR < cycle )
printk("\n[%s] thread[%x,%x] enter for path <%s> / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, path, cycle );
#endif

    // build extended pointer on lock protecting Inode Tree (in VFS root inode)
    vfs_root_xp  = process->vfs_root_xp;
    vfs_root_ptr = GET_PTR( vfs_root_xp );
    vfs_root_cxy = GET_CXY( vfs_root_xp );
    main_lock_xp = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock );

    // take lock protecting Inode Tree in read mode
    remote_rwlock_rd_acquire( main_lock_xp );

    // get extended pointer on target inode
    error = vfs_lookup( root_xp,
                        path,
                        VFS_LOOKUP_DIR,
                        &inode_xp,
                        NULL );

    // release lock protecting Inode Tree in read mode
    remote_rwlock_rd_release( main_lock_xp );

    if( error ) 
    {
        printk("\n[ERROR] in %s : <%s> not found\n",
        __FUNCTION__, path );
        return -1;
    }

    // get inode type from remote file
    inode_cxy = GET_CXY( inode_xp );
    inode_ptr = GET_PTR( inode_xp );
    inode_type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );

    if( inode_type != FILE_TYPE_DIR )
    {
        printk("\n[ERROR] in %s : <%s> is not a directory\n",
        __FUNCTION__, path );
        return -1;
    }

    // build extended pointer on cwd_lock and cwd_xp 
    ref_xp       = process->ref_xp;
    ref_ptr      = GET_PTR( ref_xp );
    ref_cxy      = GET_CXY( ref_xp );
    cwd_lock_xp  = XPTR( ref_cxy , &ref_ptr->cwd_lock );
    cwd_xp_xp    = XPTR( ref_cxy , &ref_ptr->cwd_xp );

    // take lock protecting CWD changes
    remote_busylock_acquire( cwd_lock_xp );

    // update cwd_xp field in reference process descriptor
    hal_remote_s64( cwd_xp_xp , inode_xp );

    // release lock protecting CWD changes
    remote_busylock_release( cwd_lock_xp );

#if DEBUG_VFS_CHDIR
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_CHDIR < cycle )
printk("\n[%s] thread[%x,%x] exit : inode (%x,%x) / &cwd_xp (%x,%x) / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, inode_cxy, inode_ptr, 
GET_CXY(cwd_xp_xp), GET_PTR(cwd_xp_xp), cycle );
#endif

    return 0;

}  // end vfs_chdir()

///////////////////////////////////
error_t vfs_chmod( xptr_t   cwd_xp,
                   char   * path,
                   uint32_t rights )
{
    error_t           error;
    xptr_t            inode_xp;     // extended pointer on target inode
    cxy_t             inode_cxy;    // inode cluster identifier       
    vfs_inode_t     * inode_ptr;    // inode local pointer

// check lookup working mode
assert( __FUNCTION__, (rights == 0), "access rights non implemented yet" );
 
    // get extended pointer on target inode
    error = vfs_lookup( cwd_xp,
                        path,
                        0,
                        &inode_xp,
                        NULL );

    if( error ) return error;

    // get inode cluster and local pointer
    inode_cxy = GET_CXY( inode_xp );
    inode_ptr = GET_PTR( inode_xp );
    
    // get inode type from remote inode
    // inode_type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );

    // TODO finalize implementation

assert( __FUNCTION__, false , "not implemented" );

    // set inode rights in remote inode
    hal_remote_s32( XPTR( inode_cxy , &inode_ptr->rights ) , rights );

    return 0;

}   // end vfs_chmod()

/////////////////////////////////////
error_t vfs_mkfifo( xptr_t   root_xp,
                    char   * path,
                    uint32_t mode )
{
    error_t       error;
    xptr_t        parent_inode_xp;         // extended pointer on parent inode in path
    xptr_t        fifo_dentry_xp;          // extended pointer on created dentry
    xptr_t        fifo_inode_xp;           // extended pointer on created inode
    cxy_t         fifo_cxy;                // selected cluster for fifo inode

    char          fifo_name[CONFIG_VFS_MAX_NAME_LENGTH];

    thread_t    * this    = CURRENT_THREAD;
    process_t   * process = this->process;

    // build extended pointer on lock protecting Inode Tree
    xptr_t        vfs_root_xp  = process->vfs_root_xp;
    vfs_inode_t * vfs_root_ptr = GET_PTR( vfs_root_xp );
    cxy_t         vfs_root_cxy = GET_CXY( vfs_root_xp );
    xptr_t        vfs_lock_xp  = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock );

    // take the lock protecting the Inode-Tree in write mode
    remote_rwlock_wr_acquire( vfs_lock_xp );

    // 1. get extended pointer on parent inode and fifo name from vfs
    error = vfs_lookup( root_xp,
                        path,
                        VFS_LOOKUP_PARENT,
                        &parent_inode_xp,
                        fifo_name );
    if( error )
    {
        printk("\n[ERROR] in %s : cannot get parent inode for <%s> path\n",
        __FUNCTION__ , path );
        return -1;
    }

    // get parent inode cluster and local pointer
    cxy_t         parent_cxy = GET_CXY( parent_inode_xp );
    vfs_inode_t * parent_ptr = GET_PTR( parent_inode_xp );

    // 2. select a cluster for new fifo inode 
    fifo_cxy = cluster_random_select();

    // get parent inode FS type
    vfs_ctx_t    * ctx_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) );
    vfs_fs_type_t  fs_type = hal_remote_l32( XPTR( parent_cxy , &ctx_ptr->type ) );

    // 3. insert a new inode and dentry in Inode-Tree
    error = vfs_add_child_in_parent( fifo_cxy,
                                     fs_type, 
                                     parent_inode_xp,
                                     fifo_name,           
                                     &fifo_dentry_xp,   
                                     &fifo_inode_xp );
   
    // get fifo inode local pointer
    vfs_inode_t * fifo_inode_ptr = GET_PTR( fifo_inode_xp );

    if( error )
    {
        printk("\n[ERROR] in %s : cannot create fifo inode for <%s> path\n",
        __FUNCTION__ , path );
        return -1;
    }

    // 4. allocate memory for a pipe descriptor, for the
    //    remote_buf_t descriptor, and for the associated data buffer
    pipe_t * pipe = pipe_create( fifo_cxy,
                                 CONFIG_PIPE_BUF_SIZE ); 
    if( pipe == NULL )
    {
        printk("\n[ERROR] in %s : cannot create pipe for <%s> path\n",
        __FUNCTION__ , path );
        return -1;
    }

    // 5. update fifo_inode "type", "pipe", and "rights" fields
    hal_remote_s32( XPTR( fifo_cxy , &fifo_inode_ptr->type ) , FILE_TYPE_FIFO ); 
    hal_remote_s32( XPTR( fifo_cxy , &fifo_inode_ptr->rights ) , mode ); 
    hal_remote_spt( XPTR( fifo_cxy , &fifo_inode_ptr->pipe ) , pipe   );

    // release the lock protecting the Inode-Tree from write mode
    remote_rwlock_wr_release( vfs_lock_xp );

    return 0;

}   // end vfs_mkfifo()



//////////////////////////////////////////////////////////////////////////////////////////
//       Distributed Inode Tree access related functions
//////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
// This recursive function is called by the vfs_display() function.
// that is supposed to take the TXT0 lock.
//////////////////////////////////////////////////////////////////////////
static void vfs_recursive_display( xptr_t   inode_xp,
                                   xptr_t   name_xp,
                                   uint32_t indent )
{
    cxy_t              inode_cxy;
    vfs_inode_t      * inode_ptr;
    vfs_file_type_t   inode_type;
    uint32_t           inode_size;
    uint32_t           inode_attr;
    void             * inode_extd;

    xptr_t             children_xp;    // extended pointer on children xhtab

    xptr_t             child_dentry_xp;
    cxy_t              child_dentry_cxy;
    vfs_dentry_t     * child_dentry_ptr;
    xptr_t             child_inode_xp;
    xptr_t             child_dentry_name_xp;
    mapper_t         * mapper_ptr;

    char               name[CONFIG_VFS_MAX_NAME_LENGTH];

    char *             indent_str[] = { "",                                  // level 0
                                        "  ",                                // level 1
                                        "    ",                              // level 2
                                        "      ",                            // level 3
                                        "        ",                          // level 4
                                        "          ",                        // level 5
                                        "            ",                      // level 6
                                        "              ",                    // level 7
                                        "                ",                  // level 8
                                        "                  ",                // level 9
                                        "                    ",              // level 10
                                        "                      ",            // level 11
                                        "                        ",          // level 12
                                        "                          ",        // level 13
                                        "                            ",      // level 14
                                        "                              " };  // level 15

assert( __FUNCTION__, (inode_xp != XPTR_NULL) , "inode_xp cannot be NULL" );
assert( __FUNCTION__, (name_xp  != XPTR_NULL) , "name_xp cannot be NULL" );
assert( __FUNCTION__, (indent < 16)           , "depth cannot be larger than 15" );
    
    // get current inode cluster and local pointer
    inode_cxy = GET_CXY( inode_xp );
    inode_ptr = GET_PTR( inode_xp );

    // get inode type, size, attr, mapper, and inum
    inode_type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type   ) );
    inode_size = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size   ) );
    inode_attr = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->attr   ) );
    inode_extd = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->extend ) );
    mapper_ptr = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );

    // make a local copy of node name
    hal_remote_strcpy( XPTR( local_cxy , name ) , name_xp );

    // compute dirty
    // inode_dirty = ((inode_attr & INODE_ATTR_DIRTY) != 0); unused [AG] dec 2019

    // display inode
    nolock_printk("%s<%s> : %s / extd %x / %d bytes / cxy %x / inode %x / mapper %x\n",
    indent_str[indent], name, vfs_inode_type_str( inode_type ), (uint32_t)inode_extd,
    inode_size, inode_cxy, inode_ptr, mapper_ptr );

    // scan directory entries when current inode is a directory
    // don't scan the the "." and ".." directories to break loops 
    if( (inode_type == FILE_TYPE_DIR) && 
        (strcmp( name , "." ) != 0)    &&
        (strcmp( name , ".." ) != 0) )
    {
        // get extended pointer on directory entries xhtab
        children_xp =  XPTR( inode_cxy , &inode_ptr->children );

        // get xhtab lock
        xhtab_lock( children_xp );

        // get first dentry from xhtab
        child_dentry_xp = xhtab_get_first( children_xp );

        while( child_dentry_xp != XPTR_NULL )
        {
            // get dentry cluster and local pointer
            child_dentry_cxy = GET_CXY( child_dentry_xp );
            child_dentry_ptr = GET_PTR( child_dentry_xp );

            // get extended pointer on child inode
            child_inode_xp = hal_remote_l64( XPTR( child_dentry_cxy,
                                                   &child_dentry_ptr->child_xp ) );

            // get extended pointer on dentry name
            child_dentry_name_xp = XPTR( child_dentry_cxy , &child_dentry_ptr->name );

            // recursive call on inode display
            vfs_recursive_display( child_inode_xp,
                                   child_dentry_name_xp,
                                   indent+1 );

            // get next dentry
            child_dentry_xp = xhtab_get_next( children_xp );
        }

        // release xhtab lock
        xhtab_unlock( children_xp );
    }
}  // end vfs_recursive_display()

///////////////////////////////////
void vfs_display( xptr_t inode_xp )
{
    xptr_t         name_xp;
    xptr_t         dentry_xp; 
    cxy_t          dentry_cxy;
    vfs_dentry_t * dentry_ptr;
    xptr_t         parents_root_xp;   // root of parent dentries xlist

    // get target inode cluster and local pointer 
    cxy_t         inode_cxy = GET_CXY( inode_xp );
    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );

    // build extended pointer on parents dentries root
    parents_root_xp = XPTR( inode_cxy , &inode_ptr->parents );

    // check VFS root     
    if( xlist_is_empty( parents_root_xp ) )  // inode is the VFS root
    {
        // build extended pointer on root name
        name_xp = XPTR( local_cxy , "/" );
    }
    else
    {
        // get first parent dentry cluster and pointers
        dentry_xp  = XLIST_FIRST( parents_root_xp , vfs_dentry_t , parents );
        dentry_cxy = GET_CXY( dentry_xp );
        dentry_ptr = GET_PTR( dentry_xp );

        // get extended pointer on dentry name
        name_xp = XPTR( dentry_cxy , &dentry_ptr->name );
    }

    // get pointers on TXT0 chdev
    xptr_t    txt0_xp  = chdev_dir.txt_tx[0];
    cxy_t     txt0_cxy = GET_CXY( txt0_xp );
    chdev_t * txt0_ptr = GET_PTR( txt0_xp );

    // get extended pointer on remote TXT0 chdev lock
    xptr_t  lock_xp = XPTR( txt0_cxy , &txt0_ptr->wait_lock );

    // get TXT0 lock in busy waiting mode
    remote_busylock_acquire( lock_xp );

    // print header
    nolock_printk("\n***** current VFS state\n\n");

    // call recursive function
    vfs_recursive_display( inode_xp , name_xp , 0 );

    // release lock
    remote_busylock_release( lock_xp );

}  // end vfs_display()

/*
//////////////////////////////////////////////////////////////////////////////////////////
// This static function is used by the vfs_lookup() function.
// It takes an extended pointer on a remote inode (parent directory inode),
// and check access_rights violation for the calling thread.
// It can be used by any thread running in any cluster.
//////////////////////////////////////////////////////////////////////////////////////////
// @ inode_xp    : extended pointer on inode.
// @ client_uid  : client thread user ID
// @ client_gid  : client thread group ID
// @ return true if access rights are violated.
//////////////////////////////////////////////////////////////////////////////////////////
static bool_t vfs_access_denied( xptr_t   inode_xp,
                          uint32_t client_uid,
                          uint32_t client_gid )
{
    // get found inode cluster and local pointer
    cxy_t         inode_cxy = GET_CXY( inode_xp );
    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );

    // get inode access mode, UID, and GID
    // TODO uint32_t  mode = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->mode ) );
    uid_t     uid  = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->uid  ) );
    gid_t     gid  = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->gid  ) );

    // TODO : me must use mode
    if( (uid == client_uid) || (gid == client_gid) ) return false;
    else                                             return true;
}
*/

//////////////////////////////////////////////////////////////////////////////////////////
// This static function is used by the vfs_lookup() function.
// It takes an extended pointer on a remote parent directory inode, a directory 
// entry name, and and scan the XHTAB associated to the parent inode to find the
// searched dentry. It does NOT modify the inode tree in case of miss.
// It can be used by any thread running in any cluster.
//////////////////////////////////////////////////////////////////////////////////////////
// @ parent_xp   : extended pointer on parent inode in remote cluster.
// @ name        : dentry name
// @ child_xp    : [out] buffer for extended pointer on child inode.
// @ return true if success / return false if not found.
//////////////////////////////////////////////////////////////////////////////////////////
static bool_t vfs_get_child( xptr_t   parent_xp,
                             char   * name,
                             xptr_t * child_xp )
{
    xptr_t         xhtab_xp;    // extended pointer on hash table for children dentries
    xptr_t         dentry_xp;   // extended pointer on children dentry
    cxy_t          dentry_cxy;
    vfs_dentry_t * dentry_ptr;

    // get parent inode cluster and local pointer
    cxy_t         parent_cxy = GET_CXY( parent_xp );
    vfs_inode_t * parent_ptr = GET_PTR( parent_xp );

    // get extended pointer on hash table of children directory entries
    xhtab_xp = XPTR( parent_cxy , &parent_ptr->children );

    // get pointers on matching dentry
    dentry_xp  = xhtab_lookup( xhtab_xp , name );
    dentry_cxy = GET_CXY( dentry_xp );
    dentry_ptr = GET_PTR( dentry_xp );

    if( dentry_xp == XPTR_NULL ) 
    {
        return false;
    }
    else
    {
        *child_xp = (xptr_t)hal_remote_l64( XPTR( dentry_cxy , &dentry_ptr->child_xp ) );
        return true;
    }

}  // end vfs_get_child()

//////////////////////////////////////////////////////////////////////////////////////////
// This static function is used by the vfs_lookup() function.
// It takes the <current> pointer on a buffer containing a complete pathname, and return
// in the <name> buffer, allocated by the caller, a single name in the path.
// It return also in the <next> pointer the next character to analyse in the path.
// Finally it returns a <last> boolean, that is true when the returned <name> is the
// last name in the path. The names are supposed to be separated by one or several '/'
// characters, that are not written in  the <name> buffer.
// 
// WARNING: the leading characters '/' in the path are skiped before analysis.
//          The path "/" identifies the VFS root, and is therefore anaysed as an empty
//          string. This empty string is dignaled by the (-1) return value.  
//////////////////////////////////////////////////////////////////////////////////////////
// @ current   : pointer on first character to analyse in buffer containing the path.
// @ name      : [out] pointer on buffer allocated by the caller for the returned name.
// @ next      : [out] pointer on next character to analyse in buffer containing the path.
// @ last      : [out] true if the returned name is the last (NUL character found).
// @ return 0 if success / return -1 if string empty (first chracter is NUL).
//////////////////////////////////////////////////////////////////////////////////////////
static error_t vfs_get_name_from_path( char     * current,
                                       char     * name,
                                       char    ** next,
                                       bool_t   * last )
{
    char * ptr = current;

    // skip leading '/' characters
    while( *ptr == '/' ) ptr++;

    // signal empty string
    if( *ptr == 0 )
    {
        *last = true;
        return -1;
    }

    // copy all characters in name until NUL or '/'
    while( (*ptr != 0) && (*ptr !='/') )  *(name++) = *(ptr++);

    // set NUL terminating character in name buffer
    *(name++) = 0;

    // return last an next
    if( *ptr == 0 )             // last found character is NUL => last name in path
    {
        *last = true;
    }
    else                        // last found character is '/' => skip it 
    {
        *last = false;
        *next = ptr + 1;
    }

    return 0;

}  // end vfs_get name_from_path()
   
///////////////////////////////////////////////
error_t vfs_lookup( xptr_t             root_xp,
                    char             * pathname,
                    uint32_t           lookup_mode,
					xptr_t           * inode_xp,
					char             * last_name )
{
    char               name[CONFIG_VFS_MAX_NAME_LENGTH];   // one name in path 

    xptr_t             parent_xp;    // extended pointer on parent inode
    cxy_t              parent_cxy;   // cluster for parent inode
    vfs_inode_t      * parent_ptr;   // local pointer on parent inode  
    xptr_t             dentry_xp;    // extended pointer on dentry       
    vfs_dentry_t     * dentry_ptr;   // local pointer on dentry
    xptr_t             child_xp;     // extended pointer on child inode
    cxy_t              child_cxy;    // cluster for child inode
    vfs_inode_t      * child_ptr;    // local pointer on child inode
    vfs_fs_type_t      fs_type;      // File system type
    vfs_ctx_t        * ctx_ptr;      // local pointer on FS context
    char             * current;      // current pointer on path
    char             * next;         // next value for current pointer   
    bool_t             last;         // true when the name is the last in path
    bool_t             found;        // true when a child has been found
    bool_t             create;       // searched inode must be created if not found
    bool_t             excl;         // searched inode must not exist
    bool_t             parent;       // searched inode is the parent
    thread_t         * this;         // pointer on calling thread descriptor
    process_t        * process;      // pointer on calling process descriptor
    error_t            error;

    this    = CURRENT_THREAD;
    process = this->process;

// check pathname / root_xp consistency
assert( __FUNCTION__, ((pathname[0] != '/') || (root_xp == process->vfs_root_xp)), 
"root inode must be VFS root for path <%s>", pathname );

#if DEBUG_VFS_LOOKUP || DEBUG_VFS_ERROR
uint32_t cycle = (uint32_t)hal_get_cycles();
#endif

#if DEBUG_VFS_LOOKUP
char     root_name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_inode_get_name( root_xp , root_name );
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] enter / root <%s> / path <%s> / mode %x / cycle %d\n",
__FUNCTION__, process->pid, this->trdid, root_name, pathname, lookup_mode, cycle );
#endif

#if ( DEBUG_VFS_LOOKUP & 1 )
if( DEBUG_VFS_LOOKUP < cycle )
vfs_display( root_xp );
#endif

    // compute lookup flags
    create = (lookup_mode & VFS_LOOKUP_CREATE) == VFS_LOOKUP_CREATE;
    excl   = (lookup_mode & VFS_LOOKUP_EXCL)   == VFS_LOOKUP_EXCL;
    parent = (lookup_mode & VFS_LOOKUP_PARENT) == VFS_LOOKUP_PARENT;

    // initialise loop variables
    parent_xp = root_xp;
    current   = pathname;
    next      = NULL;
    last      = false;
    child_xp  = XPTR_NULL;
    child_cxy = 0;
    child_ptr = NULL;

    // loop on nodes in pathname
    // load from device if one node in path not found in Inode Tree
    // exit loop when last name found (i.e. last == true)
    while( 1 )
    {
        // get parent inode cluster and local pointer
        parent_cxy = GET_CXY( parent_xp );
        parent_ptr = GET_PTR( parent_xp );

        // get one "name" from path, and "last" flag
        error = vfs_get_name_from_path( current , name , &next , &last );

        // handle VFS root case
        if ( error )
        {

#if DEBUG_VFS_LOOKUP
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] exit / parent inode(%x,%x) / <%s> / cycle %d\n",
__FUNCTION__ , process->pid, this->trdid, parent_cxy, parent_ptr, pathname, cycle );
#endif
            *inode_xp = process->vfs_root_xp;
            break;
        }

#if (DEBUG_VFS_LOOKUP & 1)
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] search <%s> in <%s> / last = %d\n",
__FUNCTION__, process->pid, this->trdid, name, pathname, last );
#endif

        // search the child dentry matching name in parent inode XHTAB
        found = vfs_get_child( parent_xp,
                               name,
                               &child_xp );

        if( found == false )                          // child not found in Inode Tree 
        {
            // when a inode is not found in the Inode Tree:
            // - if (last and parent) the Inode Tree is not modified
            // - else we speculatively introduce a new (dentry/inode) in inode tree,
            //        and scan the parent directory mapper to initialise it.
            //     . if it is not found in the parent mapper:
            //         - if(last and create), a brand new file or directory is created
            //         - else, an error is reported
            //     . if it is found in parent mapper:
            //         - if( last and excl ), an error is reported
            //         - else the new child (inode & dentry) is initialised in Inode Tree
            //         - if the child is a directory, the child mapper is loaded from device

            if( last && parent )   //  does nothing
            {

#if (DEBUG_VFS_LOOKUP & 1)
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] child not found but only parent requested in <%s>\n",
__FUNCTION__, process->pid, this->trdid, pathname );
#endif
            }
            else                                    // try to get it from parent mapper
            {

#if (DEBUG_VFS_LOOKUP & 1)
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] miss <%s> inode in Inode Tree => build from parent mapper\n",
__FUNCTION__, process->pid, this->trdid, name );
#endif
                // get parent inode FS type
                ctx_ptr    = hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) );
                fs_type    = hal_remote_l32( XPTR( parent_cxy , &ctx_ptr->type ) );

                // select a cluster for new inode
                child_cxy = cluster_random_select();

                // insert a new child dentry/inode couple in inode tree
                error = vfs_add_child_in_parent( child_cxy,
                                                 fs_type, 
                                                 parent_xp, 
                                                 name, 
                                                 &dentry_xp,
                                                 &child_xp );
                if( error )
                {

#if DEBUG_VFS_ERROR  
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot create inode <%s> in path <%s>\n",
__FUNCTION__ , process->pid, this->trdid, name, pathname );
#endif
                    return -1;
                }

                // get child inode and dentry local pointers
                child_ptr  = GET_PTR( child_xp );
                dentry_ptr = GET_PTR( dentry_xp );

#if (DEBUG_VFS_LOOKUP & 1)
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] created dentry/inode <%s> in cluster %x\n",
__FUNCTION__, process->pid, this->trdid, name, child_cxy );
#endif
                // scan parent mapper to find the missing dentry, and complete 
                // the initialisation of dentry and child inode descriptors
                error = vfs_fs_new_dentry_from_mapper( parent_xp,
                                                       dentry_ptr );

                if ( error )  // an error can be fatal or non-fatal :
                {
                    if ( last && create )  // add a brand new dentry in parent directory
                    {
                        error = vfs_fs_new_dentry_to_mapper( parent_xp,                
                                                             dentry_ptr );
                        if ( error )
                        {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot add dentry <%s> in parent dir\n",
__FUNCTION__, process->pid, this->trdid, name );
#endif
                            vfs_remove_child_from_parent( dentry_xp );
                            return -1;
                        }

#if (DEBUG_VFS_LOOKUP & 1)
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] child <%s> not found in parent mapper => created it\n",
__FUNCTION__, process->pid, this->trdid, name );
#endif
                    }
                    else                   // not last or not create => error
                    {                       

#if DEBUG_VFS_ERROR      
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot found node <%s> in parent for <%s>\n",
__FUNCTION__ , process->pid, this->trdid, name, pathname );
#endif
                        vfs_remove_child_from_parent( dentry_xp );
                        return -1;
                    }
                }
                else          // child has been found in parent mapper
                {
                    // check the excl
                    if( last && create && excl )
                    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] found an existing node <%s>  %\n",
__FUNCTION__ , process->pid, this->trdid, pathname );
#endif
                       return -1;
                    }

#if (DEBUG_VFS_LOOKUP & 1)
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] initialised inode <%s> from parent mapper\n",
__FUNCTION__, process->pid, this->trdid, name );
#endif
                    // load child mapper from device if child is a directory (prefetch)
                    uint32_t type = hal_remote_l32( XPTR( child_cxy , &child_ptr->type ) );
                    if( type == FILE_TYPE_DIR ) 
                    {
                        error = vfs_inode_load_all_pages( child_xp );

                        if ( error )
                        {
#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot load <%s> from device\n",
__FUNCTION__ , process->pid, this->trdid, name );
#endif
                            vfs_remove_child_from_parent( dentry_xp );
                            return -1;
                        }

#if (DEBUG_VFS_LOOKUP & 1)
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] loaded directory mapper for <%s> from IOC\n",
__FUNCTION__ , process->pid, this->trdid, name );
#endif
                    }
                }
            }
        }
        else                                    // child found in Inode Tree
        {
            // get child inode local pointer and cluster
            child_ptr  = GET_PTR( child_xp );
            child_cxy  = GET_CXY( child_xp );
        
#if (DEBUG_VFS_LOOKUP & 1)
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] found <%s> in Inode Tree / inode (%x,%x)\n",
__FUNCTION__, process->pid, this->trdid, name, child_cxy, child_ptr );
#endif
            // check the excl flag
            if( last && create && excl )
            {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] found an existing node <%s>\n",
__FUNCTION__ , process->pid, this->trdid, pathname );
#endif
                return -1;
            }
        }

        // TODO check access rights here [AG]
        // error = vfs_access_denied( child_xp,
        //                            client_uid,
        //                            client_gid );
        // if( error ) 
        // {
        //     #if DEBUG_VFS_ERROR
        //     if( DEBUG_VFS_ERROR < cycle )
        //     printk("\n[ERROR] in %s : thread[%x,%x] / permission denied for <%s>\n",
        //     __FUNCTION__ , process->pid, this->trdid , pathname );
        //     #endif
        //
        //     return -1;
        // }

        // exit when last
        if ( last )           // last inode in path  => return relevant info
        {
            if ( parent )  // return parent inode and child name
            {

#if DEBUG_VFS_LOOKUP
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] exit / parent inode(%x,%x) / <%s> / cycle %d\n",
__FUNCTION__ , process->pid, this->trdid, parent_cxy, parent_ptr, pathname, cycle );
#endif
                *inode_xp = parent_xp;
                strcpy( last_name , name );
                break; 
            }
            else        // return child inode name     
            {

#if DEBUG_VFS_LOOKUP
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_LOOKUP < cycle )
printk("\n[%s] thread[%x,%x] exit / child inode (%x,%x) / <%s> / cycle %d\n",
__FUNCTION__ , process->pid, this->trdid, child_cxy, child_ptr, pathname, cycle );
#endif
                *inode_xp = child_xp;
                break;
            }
        }
        else                     // not the last node in path => update loop variables
        {
            parent_xp = child_xp;
            current   = next;
        }
    }  // end while loop on nodes in pathname

#if ( DEBUG_VFS_LOOKUP & 1 )
if( DEBUG_VFS_LOOKUP < cycle )
vfs_display( root_xp );
#endif

    return 0;

}  // end vfs_lookup()

///////////////////////////////////////////////////
error_t vfs_add_special_dentries( xptr_t  child_xp,
                                  xptr_t  parent_xp )
{
    error_t         error;
    vfs_inode_t   * child_ptr;         // local pointer on child inode directory
    cxy_t           child_cxy;         // child inode directory cluster identifier
    vfs_ctx_t     * ctx_ptr;           // local pointer on child inode FS context
    vfs_fs_type_t   fs_type;           // FS type of child inode
    xptr_t          dentry_xp;         // extended pointer on dentry (used for . and ..)
    vfs_dentry_t  * dentry_ptr;        // local pointer on dentry (used for . and ..) 
    xptr_t          children_xhtab_xp; // extended pointer on inode "children" field
    xptr_t          children_entry_xp; // extended pointer on dentry "children" field

#if DEBUG_VFS_ADD_SPECIAL
uint32_t   cycle = (uint32_t)hal_get_cycles();
thread_t * this  = CURRENT_THREAD;
char child_name[CONFIG_VFS_MAX_NAME_LENGTH];
char parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_inode_get_name( child_xp  , child_name );
vfs_inode_get_name( parent_xp , parent_name );
if( DEBUG_VFS_ADD_SPECIAL < cycle )
printk("\n[%s] thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle );
#endif

    // get child directory cluster and local pointer
    child_cxy  = GET_CXY( child_xp );
    child_ptr  = GET_PTR( child_xp );

    // get child inode FS type
    ctx_ptr    = hal_remote_lpt( XPTR( child_cxy , &child_ptr->ctx ) );
    fs_type    = hal_remote_l32( XPTR( child_cxy , &ctx_ptr->type ) );

    //////////////////////////// create <.> dentry //////////////////////
    error = vfs_dentry_create( child_cxy,
                               fs_type,
                               ".",
                               &dentry_xp );
    if( error )
    {
        printk("\n[ERROR] in %s : cannot create dentry <.> in cluster %x\n",
        __FUNCTION__ , child_cxy );
        return -1;
    }

    // get <.> dentry local pointer
    dentry_ptr = GET_PTR( dentry_xp );

#if(DEBUG_VFS_ADD_SPECIAL & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_ADD_SPECIAL < cycle )
printk("\n[%s] thread[%x,%x] created dentry <.> (%x,%x) / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, child_cxy, dentry_ptr, cycle );
#endif

    // register <.> dentry in child inode xhtab of children
    children_xhtab_xp = XPTR( child_cxy , &child_ptr->children );
    children_entry_xp = XPTR( child_cxy , &dentry_ptr->children );

    error = xhtab_insert( children_xhtab_xp , "." , children_entry_xp );

    if( error )
    {
        printk("\n[ERROR] in %s : cannot register dentry <.> in xhtab\n",
        __FUNCTION__ );
        return -1;
    }
    
    // update "parent" and "child_xp" fields in <.> dentry
    hal_remote_s64( XPTR( child_cxy , &dentry_ptr->child_xp ) , child_xp );
    hal_remote_spt( XPTR( child_cxy , &dentry_ptr->parent ) , child_ptr );

#if(DEBUG_VFS_ADD_SPECIAL & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_ADD_SPECIAL < cycle )
printk("\n[%s] thread[%x,%x] linked dentry <.> to parent and child inodes / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, cycle ); 
#endif

    // introduce <.> dentry into child directory mapper
    // only if the target directory is not the root VFS
    if( child_xp != parent_xp )
    {
        error = vfs_fs_add_dentry( child_xp,
                                   dentry_ptr );
        if( error )
        {
            printk("\n[ERROR] in %s : cannot introduce dentry <..> in mapper %x\n",
            __FUNCTION__ );
            return -1;
        }

#if(DEBUG_VFS_ADD_SPECIAL & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_ADD_SPECIAL < cycle )
printk("\n[%s] thread[%x,%x] registered dentry <.> in child mapper / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, cycle ); 
#endif

    }

    ///////////////////////////// create <..> dentry ///////////////////////
    error = vfs_dentry_create( child_cxy,
                               fs_type,
                               "..",
                               &dentry_xp );
    if( error )
    {
        printk("\n[ERROR] in %s : cannot create dentry <..> in cluster %x\n",
        __FUNCTION__ , child_cxy );
        return -1;
    }

    // get <..> dentry local pointer
    dentry_ptr = GET_PTR( dentry_xp );

#if(DEBUG_VFS_ADD_SPECIAL & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_ADD_SPECIAL < cycle )
printk("\n[%s] thread[%x,%x] created dentry <..> (%x,%x) / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, child_cxy, dentry_ptr, cycle );
#endif

    // register <..> dentry in child_inode xhtab of children
    children_xhtab_xp = XPTR( child_cxy , &child_ptr->children );
    children_entry_xp = XPTR( child_cxy , &dentry_ptr->children );
    error = xhtab_insert( children_xhtab_xp , ".." , children_entry_xp );
    if( error )
    {
        printk("\n[ERROR] in %s : cannot register dentry <..> in xhtab\n",
        __FUNCTION__ );
        return -1;
    }

    // don't register <..> dentry in parent_inode xlist of parents 

    // update "parent" and "child_xp" fields in <..> dentry
    hal_remote_s64( XPTR( child_cxy , &dentry_ptr->child_xp ) , parent_xp );
    hal_remote_spt( XPTR( child_cxy , &dentry_ptr->parent ) , child_ptr );

#if(DEBUG_VFS_ADD_SPECIAL & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_ADD_SPECIAL < cycle )
printk("\n[%s] thread[%x,%x] linked dentry <..> to parent and child inodes / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, cycle ); 
#endif

    // introduce <..> dentry into child directory mapper
    // only if the target directory is not the root VFS
    if( child_xp != parent_xp )
    {
        error = vfs_fs_add_dentry( child_xp,
                                   dentry_ptr );
        if( error )
        {
            printk("\n[ERROR] in %s : cannot introduce dentry <..> in mapper %x\n",
            __FUNCTION__ );
            return -1;
        }

#if(DEBUG_VFS_ADD_SPECIAL & 1)
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_ADD_SPECIAL < cycle )
printk("\n[%s] thread[%x,%x] registered dentry <..> in child mapper / cycle %d\n", 
__FUNCTION__, this->process->pid, this->trdid, cycle ); 
#endif

    }

#if DEBUG_VFS_ADD_SPECIAL
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_ADD_SPECIAL < cycle )
printk("\n[%s] thread[%x,%x] exit for child <%s> in parent <%s> / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle );
#endif

    return 0;

}  // end vfs_add_special_dentries()

//////////////////////////////////////////
error_t vfs_get_path( xptr_t     inode_xp,
                      char     * buffer,
                      char    ** first,
                      uint32_t   max_size )
{
	xptr_t         dentry_xp;        // extended pointer on current dentry
    vfs_dentry_t * dentry_ptr;       // local pointer on current dentry
    cxy_t          dentry_cxy;       // current dentry cluster identifier
    xptr_t         name_xp;          // extended pointer on current dentry name
	uint32_t       length;           // length of current dentry name
	int32_t        index;            // slot index in buffer
    xptr_t         current_xp;       // extended pointer on current inode
    vfs_inode_t  * current_ptr;      // local pointer on current inode
    cxy_t          current_cxy;      // current inode cluster identifier
    xptr_t         vfs_root_xp;      // extended pointer on VFS root inode
    vfs_inode_t  * vfs_root_ptr;     // local pointer on VFS root inode
    cxy_t          vfs_root_cxy;     // VFS root inode cluster identifier
    xptr_t         lock_xp;          // extended pointer on Inode Tree lock 
    xptr_t         parents_root_xp;  // extended pointer on current inode parents root
    bool_t         found;            // condition to exit the while loop

    thread_t  * this    = CURRENT_THREAD;
    process_t * process = this->process;

#if DEBUG_VFS_GET_PATH
uint32_t cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_GET_PATH < cycle )
printk("\n[%s] thread[%x,%x] enter : inode (%x,%x) / cycle %d\n",
__FUNCTION__ , process->pid, this->trdid,
GET_CXY( inode_xp ), GET_PTR( inode_xp ), cycle );
#endif

	// set the NUL character in buffer / initialise buffer index
	buffer[max_size - 1] = 0;
    index    = (int32_t)(max_size - 1);

    // initialize current inode
    current_xp  = inode_xp;

    // build extended pointer on lock protecting Inode Tree
    vfs_root_xp  = process->vfs_root_xp;
    vfs_root_ptr = GET_PTR( vfs_root_xp );
    vfs_root_cxy = GET_CXY( vfs_root_xp );
    lock_xp      = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock );

    // take lock protecting Inode Tree in read mode
    remote_rwlock_rd_acquire( lock_xp );

    // traverse Inode Tree from target inode to VFS root
    // selecting always the first parent dentry
    // the buffer is written in "reverse order" (from target inode to root) 
    // exit the while loop when the VFS root has been found
	do
    {
        // get current inode cluster and local pointer
        current_cxy = GET_CXY( current_xp );
        current_ptr = GET_PTR( current_xp );

        // build extended pointer on parents dentries root
        parents_root_xp = XPTR( current_cxy , &current_ptr->parents );

        // compute exit condition <=> current inode is VFS root    
        found = xlist_is_empty( parents_root_xp );

        if( found )                              // parent is the VFS root
        {
            if( index == (int32_t)(max_size - 1) )
            {
                // update index
                index--;
                 
                // set separator  
		        buffer[index] = '/';

// check buffer overflow
assert( __FUNCTION__, (index >= 0) , "kernel buffer too small" );

            }
        }
        else                                     // not the VFS root
        {
            // get first parent dentry cluster and pointers
            dentry_xp  = XLIST_FIRST( parents_root_xp , vfs_dentry_t , parents );
            dentry_cxy = GET_CXY( dentry_xp );
            dentry_ptr = GET_PTR( dentry_xp );

            // get extended pointer on dentry name and name length
            name_xp = XPTR( dentry_cxy , dentry_ptr->name );
            length  = hal_remote_l32( XPTR( dentry_cxy , &dentry_ptr->length ) );

#if (DEBUG_VFS_GET_PATH & 1)
char debug_name[CONFIG_VFS_MAX_NAME_LENGTH];
hal_remote_strcpy( XPTR( local_cxy , debug_name ) , name_xp );
if( DEBUG_VFS_GET_PATH < cycle )
printk("\n[%s] thread(%x,%s) get current dentry <%s> in cluster %x\n",
__FUNCTION__ , process->pid, this->trdid, debug_name, current_cxy );
#endif
            // update index 
            index -= (length + 1); 

// check buffer overflow
assert( __FUNCTION__, (index >= 0) , "kernel buffer too small" );

            // update pathname
            hal_remote_memcpy( XPTR( local_cxy , &buffer[index + 1] ) ,
                               name_xp , length );

            // set separator  
		    buffer[index] = '/';

            // get extended pointer on parent inode
            current_xp = XPTR( dentry_cxy , 
                               hal_remote_lpt( XPTR( dentry_cxy , &dentry_ptr->parent ) ) );
        }
    }
    while( found == false );

    // release lock protecting Inode Tree in read mode
    remote_rwlock_rd_release( lock_xp );

#if DEBUG_VFS_GET_PATH
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_GET_PATH < cycle )
printk("\n[%s] thread[%x,%x] exit : path <%s> / cycle %d\n",
__FUNCTION__ , process->pid, this->trdid, &buffer[index], cycle );
#endif

    // return pointer on first character in buffer
    *first = &buffer[index];
	return 0;

}  // end vfs_get_path()

      
//////////////////////////////////////////////////////////////
error_t vfs_add_child_in_parent( cxy_t              child_cxy,
                                 vfs_fs_type_t      fs_type,
                                 xptr_t             parent_inode_xp,
                                 char             * name,
                                 xptr_t           * child_dentry_xp,
                                 xptr_t           * child_inode_xp )
{
    error_t        error;
    cxy_t          parent_cxy;          // parent inode cluster identifier
    vfs_inode_t  * parent_inode_ptr;    // parent inode local pointer
    xptr_t         new_dentry_xp;       // extended pointer on created dentry
    vfs_dentry_t * new_dentry_ptr;      // created dentry local pointer
    xptr_t         new_inode_xp;        // extended pointer on created child inode
    vfs_inode_t  * new_inode_ptr;       // local pointer on created child inode

    xptr_t         parents_root_xp;     // extended pointer on child inode  "parents" field
    xptr_t         parents_entry_xp;    // extended pointer on child dentry "parents" field
    xptr_t         children_xhtab_xp;   // extended pointer on parent inode "children" field
    xptr_t         children_entry_xp;   // extended pointer on child dentry "children" field
    
    // get parent inode cluster and pointer
    parent_cxy       = GET_CXY( parent_inode_xp );
    parent_inode_ptr = GET_PTR( parent_inode_xp );

#if DEBUG_VFS_ADD_CHILD || DEBUG_VFS_ERROR
uint32_t cycle = (uint32_t)hal_get_cycles();
thread_t * this = CURRENT_THREAD; 
#endif

#if DEBUG_VFS_ADD_CHILD
char parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
vfs_inode_get_name( parent_inode_xp , parent_name );
if( DEBUG_VFS_ADD_CHILD < cycle )
printk("\n[%s] thread[%x,%x] enter / child <%s> / parent <%s> / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, name,
parent_name, (uint32_t)hal_get_cycles() );
#endif

    // 1. create dentry in parent cluster
    error = vfs_dentry_create( parent_cxy,
                               fs_type,
                               name,
                               &new_dentry_xp );
    if( error )
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot create dentry <%s> in cluster %x\n",
__FUNCTION__ , this->process->pid, this->trdid , name , parent_cxy );
#endif
        return -1;
    }

    // get dentry local pointer
    new_dentry_ptr = GET_PTR( new_dentry_xp );

#if(DEBUG_VFS_ADD_CHILD & 1)
if( DEBUG_VFS_ADD_CHILD < cycle )
printk("\n[%s] thread[%x,%x] created dentry <%s> : (%x,%x)\n",
__FUNCTION__, this->process->pid, this->trdid, name, parent_cxy, new_dentry_ptr );
#endif

    // 2. create child inode in child cluster
    // TODO : define attr / mode / uid / gid
    uint32_t attr = 0;
    uint32_t mode = 0;
    uint32_t uid  = 0;
    uint32_t gid  = 0;
    
    error = vfs_inode_create( child_cxy,
                              fs_type,
                              attr,
                              mode,
                              uid,
                              gid,
                              &new_inode_xp );
    if( error )
    {

#if DEBUG_VFS_ERROR
if( DEBUG_VFS_ERROR < cycle )
printk("\n[ERROR] in %s : thread[%x,%x] cannot create inode in cluster %x\n",
__FUNCTION__ , this->process->pid , this->trdid , child_cxy );
#endif
 
        vfs_dentry_destroy( new_dentry_xp );
        return -1;
    }

    // get new inode local pointer
    new_inode_ptr = GET_PTR( new_inode_xp );
    
#if(DEBUG_VFS_ADD_CHILD & 1)
if( DEBUG_VFS_ADD_CHILD < cycle )
printk("\n[%s] thread[%x,%x] created inode <%s> : (%x,%x)\n",
__FUNCTION__ , this->process->pid, this->trdid, name , child_cxy, new_inode_ptr );
#endif

    // 3. register new_dentry in new_inode xlist of parents 
    parents_root_xp  = XPTR( child_cxy , &new_inode_ptr->parents );
    parents_entry_xp = XPTR( parent_cxy, &new_dentry_ptr->parents );
    xlist_add_first( parents_root_xp , parents_entry_xp );
    hal_remote_atomic_add( XPTR( child_cxy , &new_inode_ptr->links ) , 1 );

#if(DEBUG_VFS_ADD_CHILD & 1)
if( DEBUG_VFS_ADD_CHILD < cycle )
printk("\n[%s] thread[%x,%x] linked dentry(%x,%x) to child inode(%x,%x)\n",
__FUNCTION__, this->process->pid, this->trdid, 
parent_cxy, new_dentry_ptr, child_cxy, new_inode_ptr );
#endif

    // 4. register new_dentry in parent_inode xhtab of children
    children_xhtab_xp = XPTR( parent_cxy , &parent_inode_ptr->children );
    children_entry_xp = XPTR( parent_cxy , &new_dentry_ptr->children );
    xhtab_insert( children_xhtab_xp , name , children_entry_xp );

#if(DEBUG_VFS_ADD_CHILD & 1)
if( DEBUG_VFS_ADD_CHILD < cycle )
printk("\n[%s] thread[%x,%x] linked dentry(%x,%x) to parent inode(%x,%x)\n",
__FUNCTION__, this->process->pid, this->trdid, 
parent_cxy, new_dentry_ptr, parent_cxy, parent_inode_ptr );
#endif

    // 5. update "parent" and "child_xp" fields in new_dentry
    hal_remote_s64( XPTR( parent_cxy , &new_dentry_ptr->child_xp ) , new_inode_xp );
    hal_remote_spt( XPTR( parent_cxy , &new_dentry_ptr->parent ) , parent_inode_ptr );

#if DEBUG_VFS_ADD_CHILD
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_ADD_CHILD < cycle )
printk("\n[%s] thread[%x,%x] exit for <%s> / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, name, cycle );
#endif

    // return extended pointer on dentry & child inode
    *child_dentry_xp = new_dentry_xp;
    *child_inode_xp  = new_inode_xp;
    return 0;

}  // end vfs_add_child_in_parent()

/////////////////////////////////////////////////////
void vfs_remove_child_from_parent( xptr_t dentry_xp )
{
    cxy_t          parent_cxy;         // parent inode cluster identifier
    cxy_t          child_cxy;          // child inode cluster identifier
    vfs_dentry_t * dentry_ptr;         // local pointer on dentry
    xptr_t         child_inode_xp;     // extended pointer on child inode
    vfs_inode_t  * child_inode_ptr;    // local pointer on child inode
    vfs_inode_t  * parent_inode_ptr;   // local pointer on parent inode
    uint32_t       links;              // number of child inode parents
    error_t        error;

#if DEBUG_VFS_REMOVE_CHILD 
uint32_t   cycle = (uint32_t)hal_get_cycles();
#endif

    char dentry_name[CONFIG_VFS_MAX_NAME_LENGTH];
   
    thread_t * this  = CURRENT_THREAD;

    // get parent cluster and dentry local pointer
    parent_cxy = GET_CXY( dentry_xp );
    dentry_ptr = GET_PTR( dentry_xp );

    // get a local copy of dentry name
    hal_remote_strcpy( XPTR( local_cxy  , dentry_name ),
                       XPTR( parent_cxy , &dentry_ptr->name ) );

    // get parent_inode local pointer
    parent_inode_ptr = hal_remote_lpt( XPTR( parent_cxy , &dentry_ptr->parent ) );
  
    // get child cluster and child_inode pointers 
    child_inode_xp   = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
    child_cxy        = GET_CXY( child_inode_xp ); 
    child_inode_ptr  = GET_PTR( child_inode_xp );

#if DEBUG_VFS_REMOVE_CHILD
if( DEBUG_VFS_REMOVE_CHILD < cycle )
printk("\n[%s] thread[%x,%x] enter for dentry[%x,%x] / inode[%x,%x] / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid,
parent_cxy, dentry_ptr, child_cxy, child_inode_ptr, cycle );
#endif

    // remove dentry from parent_inode
    error = xhtab_remove( XPTR( parent_cxy , &parent_inode_ptr->children ),
                          dentry_name,
                          XPTR( parent_cxy , &dentry_ptr->children ) );

    if( error )
    { 
        printk("\n[WARNING] in %s] thread[%x,%x] cannot remove dentry %s from parent dir\n",
        __FUNCTION__, this->process->pid, this->trdid, dentry_name ); 
    }

#if DEBUG_VFS_REMOVE_CHILD
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_REMOVE_CHILD < cycle )
printk("\n[%s] thread[%x,%x] removed dentry from parent inode / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

    // remove dentry from child_inode
    xlist_unlink( XPTR( parent_cxy , &dentry_ptr->parents ) );

    // get and update number of parents dentries
    links = hal_remote_atomic_add( XPTR( child_cxy , &child_inode_ptr->links ) , -1 );

#if DEBUG_VFS_REMOVE_CHILD
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_REMOVE_CHILD < cycle )
printk("\n[%s] thread[%x,%x] removed dentry from child inode / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

    // delete dentry descriptor
    vfs_dentry_destroy( dentry_xp );

#if DEBUG_VFS_REMOVE_CHILD
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_REMOVE_CHILD < cycle )
printk("\n[%s] thread[%x,%x] deleted dentry descriptor / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

    // delete child_inode descriptor if last link
    if( links == 1 )
    {
        vfs_inode_destroy( child_inode_xp );

#if DEBUG_VFS_REMOVE_CHILD
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_VFS_REMOVE_CHILD < cycle )
printk("\n[%s] thread[%x,%x] deleted child inode descriptor / cycle %d\n",
__FUNCTION__, this->process->pid, this->trdid, cycle );
#endif

    }
}  // end vfs_remove_child_from_parent()




//////////////////////////////////////////////////////////////////////////////////////////
//    API used by VFS to access a specific FS  
//////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////
error_t vfs_fs_add_dentry( xptr_t         inode_xp,
                           vfs_dentry_t * dentry_ptr )
{
    error_t error = 0;

assert( __FUNCTION__, (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
assert( __FUNCTION__, (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );

    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

    // get inode mapper
    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );

assert( __FUNCTION__, (mapper != NULL) , "mapper pointer is NULL");        

    // get FS type
    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_add_dentry( inode_xp , dentry_ptr ); 
    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        error = 0;     // does nothing for RAMFS
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        error = 0;     // does nothing for DEVFS
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;

}  // end vfs_fs_add_dentry()

//////////////////////////////////////////////////////
error_t vfs_fs_remove_dentry( xptr_t         inode_xp,
                              vfs_dentry_t * dentry_ptr )
{
    error_t error = 0;

assert( __FUNCTION__, (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
assert( __FUNCTION__, (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );

    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

    // get inode mapper
    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );

assert( __FUNCTION__, (mapper != NULL) , "mapper pointer is NULL");        

    // get FS type
    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_remove_dentry( inode_xp , dentry_ptr ); 

    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        error = 0;     // does nothing for RAMFS
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        error = 0;     // does nothing for DEVFS
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;

}  // end vfs_fs_remove_dentry()

///////////////////////////////////////////////////////////////
error_t vfs_fs_new_dentry_from_mapper( xptr_t         inode_xp,
                                       vfs_dentry_t * dentry_ptr )
{
    error_t error = 0;

assert( __FUNCTION__, (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
assert( __FUNCTION__, (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );

    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

    // get inode mapper
    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );

assert( __FUNCTION__, (mapper != NULL) , "mapper pointer is NULL");

    // get FS type
    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_new_dentry_from_mapper( inode_xp , dentry_ptr );
    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        assert( __FUNCTION__, false , "should not be called for RAMFS" );
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        assert( __FUNCTION__, false , "should not be called for DEVFS" );
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;

} // end vfs_fs_new_dentry_from_mapper()

///////////////////////////////////////////////////////////////
error_t vfs_fs_new_dentry_to_mapper( xptr_t         inode_xp,
                                     vfs_dentry_t * dentry_ptr )
{
    error_t error = 0;

assert( __FUNCTION__, (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
assert( __FUNCTION__, (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );

    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

    // get inode mapper
    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );

assert( __FUNCTION__, (mapper != NULL) , "mapper pointer is NULL");

    // get FS type
    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_new_dentry_to_mapper( inode_xp , dentry_ptr );
    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        assert( __FUNCTION__, false , "should not be called for RAMFS" );
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        assert( __FUNCTION__, false , "should not be called for DEVFS" );
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;

} // end vfs_fs_new_dentry_to_mapper()

//////////////////////////////////////////////////////
error_t vfs_fs_update_dentry( xptr_t         inode_xp,
                              vfs_dentry_t * dentry_ptr )
{
    error_t error = 0;

assert( __FUNCTION__, (inode_xp   != XPTR_NULL) , "inode_xp argument is NULL" );
assert( __FUNCTION__, (dentry_ptr != NULL     ) , "dentry_ptr argument is NULL" );

    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

    // get inode mapper
    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );

assert( __FUNCTION__, (mapper != NULL) , "mapper pointer is NULL");

    // get FS type
    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_update_dentry( inode_xp , dentry_ptr );
    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        assert( __FUNCTION__, false , "should not be called for RAMFS" );
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        assert( __FUNCTION__, false , "should not be called for DEVFS" );
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;

} // end vfs_fs_update_dentry()

///////////////////////////////////////////////////
error_t vfs_fs_get_user_dir( vfs_inode_t   * inode,
                             struct dirent * array,
                             uint32_t        max_dirent,
                             uint32_t        min_dentry,
                             bool_t          detailed,
                             uint32_t      * entries,
                             bool_t        * done )
{
    error_t error = 0;

// check arguments
assert( __FUNCTION__, (inode != NULL) , "parent pointer is NULL");
assert( __FUNCTION__, (array != NULL) , "child pointer is NULL");
assert( __FUNCTION__, (detailed == false) , "detailed argument not supported\n");

    // check inode type
    if( inode->type != FILE_TYPE_DIR )
    {
        printk("\n[ERROR] in %s : target inode is not a directory\n",
        __FUNCTION__ );
        return -1;
    }

    // get parent inode FS type
    vfs_fs_type_t fs_type = inode->ctx->type;

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_get_user_dir( inode, 
                                    array,
                                    max_dirent,
                                    min_dentry,
                                    detailed,
                                    entries,
                                    done );
    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        assert( __FUNCTION__, false , "should not be called for RAMFS" );
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        error = devfs_get_user_dir( inode,
                                    array,
                                    max_dirent,
                                    min_dentry,
                                    detailed,
                                    entries,
                                    done );
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;

}  // end vfs_fs_get_user_dir()
 
/////////////////////////////////////////////
error_t vfs_fs_sync_inode( xptr_t  inode_xp )
{
    error_t error = 0;

assert( __FUNCTION__, (inode_xp != XPTR_NULL) , "inode_xp argument is NULL");

    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

    // get inode mapper
    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );

assert( __FUNCTION__, (mapper != NULL) , "mapper pointer is NULL");

    // get FS type
    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_sync_inode( inode_xp );
    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        assert( __FUNCTION__, false , "should not be called for RAMFS" );
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        assert( __FUNCTION__, false , "should not be called for DEVFS" );
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;

}  // end vfs_fs_sync_inode()

////////////////////////////////////////////////
error_t vfs_fs_sync_fat( vfs_fs_type_t fs_type )
{
    error_t error = 0;

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_sync_fat();
    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        assert( __FUNCTION__, false , "should not be called for RAMFS" );
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        assert( __FUNCTION__, false , "should not be called for DEVFS" );
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;

}  // end vfs_fs_sync_fat()

////////////////////////////////////////////////
error_t vfs_fs_release_inode( xptr_t  inode_xp )
{
    error_t error = 0;

assert( __FUNCTION__, (inode_xp  != XPTR_NULL) , "inode_xp argument is NULL");

    vfs_inode_t * inode_ptr = GET_PTR( inode_xp );
    cxy_t         inode_cxy = GET_CXY( inode_xp );

    // get local pointer on mapper 
    mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );

assert( __FUNCTION__, (mapper != NULL) , "mapper pointer is NULL");

    // get FS type from mapper
    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->fs_type ) );

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_release_inode( inode_xp ); 
    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        assert( __FUNCTION__, false , "should not be called for RAMFS" );
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        assert( __FUNCTION__, false , "should not be called for DEVFS" );
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;
    
}  // end vfs_fs_release_inode()

//////////////////////////////////////////////////
error_t vfs_fs_move_page( xptr_t          page_xp,
                          ioc_cmd_type_t  cmd_type )
{
    error_t error = 0;

assert( __FUNCTION__, (page_xp != XPTR_NULL) , "page pointer is NULL" );

    page_t * page_ptr = GET_PTR( page_xp );
    cxy_t    page_cxy = GET_CXY( page_xp );

    // get local pointer on  mapper
    mapper_t * mapper = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) );

assert( __FUNCTION__, (mapper != NULL) , "no mapper for page" );

    // get FS type
    vfs_fs_type_t fs_type = hal_remote_l32( XPTR( page_cxy , &mapper->fs_type ) );

    // call relevant FS function
    if( fs_type == FS_TYPE_FATFS )
    {
        error = fatfs_move_page( page_xp , cmd_type ); 
    }
    else if( fs_type == FS_TYPE_RAMFS )
    {
        assert( __FUNCTION__, false , "should not be called for RAMFS\n" );
    }
    else if( fs_type == FS_TYPE_DEVFS )
    {
        assert( __FUNCTION__, false , "should not be called for DEVFS\n" );
    }
    else
    {
        assert( __FUNCTION__, false , "undefined file system type" );
    }

    return error;

}  // end vfs_fs_move_page()


