Index: trunk/kernel/fs/vfs.c
===================================================================
--- trunk/kernel/fs/vfs.c	(revision 457)
+++ trunk/kernel/fs/vfs.c	(revision 459)
@@ -247,11 +247,8 @@
 }  // end vfs_inode_create()  
 
-/////////////////////////////////////////////
-void vfs_inode_destroy( vfs_inode_t * inode )
-{
-    if( inode->refcount )
-    {
-        assert( false , __FUNCTION__ , "inode refcount non zero\n" );
-    }       
+////////////////////////////////////////////////
+error_t vfs_inode_destroy( vfs_inode_t * inode )
+{
+    assert( (inode->refcount == 0), __FUNCTION__ , "inode refcount non zero\n" );
 
     // release memory allocated for mapper
@@ -264,4 +261,6 @@
 	kmem_free( &req );
 
+    return 0;
+
 }  // end vfs_inode_destroy()
 
@@ -315,5 +314,5 @@
     return error;
 
-} // end vfs_load_inode()
+} // end vfs_inode_load()
 
 ////////////////////////////////////////////
@@ -432,10 +431,11 @@
     vfs_dentry_t   * dentry;     // dentry descriptor (to be allocated)
 	kmem_req_t       req;        // request to kernel memory allocator
+    error_t          error;
 
 #if DEBUG_VFS_DENTRY_CREATE
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_DENTRY_CREATE < cycle )
-printk("\n[DBG] %s : thread %x enter for <%s> / parent_inode %x / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD , name , parent , cycle );
+printk("\n[DBG] %s : thread %x in process %x enter for <%s> / parent_inode %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD->trdid, CURRENT_THREAD->process->pid, name, parent, cycle );
 #endif
 
@@ -444,8 +444,8 @@
     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
+    else 
     {
         ctx = NULL;
-        assert( false , __FUNCTION__ , "undefined file system type\n" );
+        return EINVAL;
     }
 
@@ -453,12 +453,5 @@
     uint32_t length = strlen( name );
 
-    if( length >= CONFIG_VFS_MAX_NAME_LENGTH )
-    {
-
-#if DEBUG_SYSCALLS_ERROR
-printk("\n[ERROR] in %s : name <name> too long\n", __FUNCTION__ , name );
-#endif
-        return EINVAL;
-    }
+    if( length >= CONFIG_VFS_MAX_NAME_LENGTH ) return EINVAL;
 
     // allocate memory for dentry descriptor
@@ -468,12 +461,5 @@
 	dentry     = (vfs_dentry_t *)kmem_alloc( &req );
 
-    if( dentry == NULL )
-    {
-
-#if DEBUG_SYSCALLS_ERROR
-printk("\n[ERROR] in %s : cannot allocate dentry\n", __FUNCTION__ );
-#endif
-        return ENOMEM;
-    }
+    if( dentry == NULL ) return ENOMEM;
 
     // initialize dentry descriptor
@@ -491,7 +477,9 @@
 
     // register dentry in hash table rooted in parent inode
-    xhtab_insert( XPTR( local_cxy , &parent->children ),
-                  name, 
-                  XPTR( local_cxy , &dentry->list ) );
+    error = xhtab_insert( XPTR( local_cxy , &parent->children ),
+                          name, 
+                          XPTR( local_cxy , &dentry->list ) );
+
+    if( error ) return EINVAL;
 
 #if( DEBUG_VFS_DENTRY_CREATE & 1 )
@@ -507,6 +495,6 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_DENTRY_CREATE < cycle )
-printk("\n[DBG] %s : thread %x exit for <%s> / dentry %x / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD , name , dentry , cycle );
+printk("\n[DBG] %s : thread %x in process %x exit for <%s> / dentry %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD->trdid, CURRENT_THREAD->process->pid, name, dentry, cycle );
 #endif
 
@@ -515,16 +503,28 @@
 }  // end vfs_dentry_create()
 
-////////////////////////////////////////////////
-void vfs_dentry_destroy( vfs_dentry_t * dentry )
-{
-    if( dentry->refcount )
-    {
-        assert( false , __FUNCTION__ , "dentry refcount non zero\n" );
-    }       
-
+///////////////////////////////////////////////////
+error_t vfs_dentry_destroy( vfs_dentry_t * dentry )
+{
+    error_t error;
+
+    assert( (dentry->refcount == 0) , __FUNCTION__ , "dentry refcount non zero\n" );
+
+    // get pointer on parent inode
+    vfs_inode_t * parent = dentry->parent;
+
+    // remove this dentry from parent inode htab
+    error = xhtab_remove( XPTR( local_cxy , &parent->children ),
+                          dentry->name,
+                          XPTR( local_cxy , &dentry->list ) ); 
+
+    if( error ) return EINVAL;     
+
+    // release memory allocated to dentry
 	kmem_req_t req;
 	req.ptr   = dentry;
 	req.type  = KMEM_VFS_DENTRY;
 	kmem_free( &req );
+
+    return 0;
 }
 
@@ -564,4 +564,12 @@
 
     *file_xp = XPTR( local_cxy , file );
+
+#if DEBUG_VFS_OPEN
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_VFS_OPEN < cycle )
+printk("\n[DBG] %s : thread %x in process %x created file %x in cluster %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD->trdid, CURRENT_THREAD->process->pid, file, local_cxy, cycle );
+#endif
+
     return 0;
 
@@ -581,4 +589,11 @@
 	kmem_free( &req );
 
+#if DEBUG_VFS_CLOSE
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_VFS_CLOSE < cycle )
+printk("\n[DBG] %s : thread %x in process %x deleted file %x in cluster %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD->trdid, CURRENT_THREAD->process->pid, file, local_cxy, cycle );
+#endif
+
 }  // end vfs_file_destroy() 
 
@@ -589,5 +604,5 @@
     // get file cluster and local pointer
     cxy_t        file_cxy = GET_CXY( file_xp );
-    vfs_file_t * file_ptr = (vfs_file_t *)GET_PTR( file_xp ); 
+    vfs_file_t * file_ptr = GET_PTR( file_xp ); 
 
     // atomically increment count
@@ -600,5 +615,5 @@
     // get file cluster and local pointer
     cxy_t        file_cxy = GET_CXY( file_xp );
-    vfs_file_t * file_ptr = (vfs_file_t *)GET_PTR( file_xp ); 
+    vfs_file_t * file_ptr = GET_PTR( file_xp ); 
 
     // atomically decrement count
@@ -630,6 +645,6 @@
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_OPEN < cycle )
-printk("\n[DBG] %s :  thread %x enter for <%s> / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, path, cycle );
+printk("\n[DBG] %s :  thread %x in process %x enter for <%s> / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD->trdid, CURRENT_THREAD->process->pid, path, cycle );
 #endif
 
@@ -677,6 +692,7 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_OPEN < cycle )
-printk("\n[DBG] %s : thread %x exit for <%s> / file %x in cluster %x / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, path, GET_PTR(file_xp), GET_CXY(file_xp), cycle );
+printk("\n[DBG] %s :  thread %x in process %x exit for <%s> / fdid %d / cluster %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD->trdid, CURRENT_THREAD->process->pid, path,
+file_id, GET_CXY( file_xp ), cycle );
 #endif
 
@@ -868,4 +884,16 @@
                    uint32_t file_id )
 {
+    cluster_t  * cluster;          // local pointer on local cluster
+    cxy_t        file_cxy;         // cluster containing the file descriptor.
+    vfs_file_t * file_ptr;         // local ponter on file descriptor
+    cxy_t        owner_cxy;        // process owner cluster
+    lpid_t       lpid;             // process local index
+    xptr_t       root_xp;          // root of list of process copies
+    xptr_t       lock_xp;          // lock protecting the list of copies
+    xptr_t       iter_xp;          // iterator on list of process copies
+    xptr_t       process_xp;       // extended pointer on one process copy
+    cxy_t        process_cxy;      // process copy cluster
+    process_t  * process_ptr;      // process copy local pointer
+
     assert( (file_xp != XPTR_NULL) , __FUNCTION__ , "file_xp == XPTR_NULL" );
 
@@ -875,42 +903,70 @@
     process_t * process = this->process;
 
-    // get cluster and local pointer on remote file descriptor
-    cxy_t        file_cxy = GET_CXY( file_xp );
-    vfs_file_t * file_ptr = (vfs_file_t *)GET_PTR( file_xp );
+#if DEBUG_VFS_CLOSE
+uint32_t cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_VFS_CLOSE < cycle )
+printk("\n[DBG] %s : thread %x in process %x enter / fdid %d / cycle %d\n",
+__FUNCTION__, this->trdid, process->pid, file_id, cycle );
+#endif
 
     // get local pointer on local cluster manager
-    cluster_t * cluster = LOCAL_CLUSTER;
+    cluster = LOCAL_CLUSTER;
 
     // get owner process cluster and lpid
-    cxy_t   owner_cxy  = CXY_FROM_PID( process->pid );
-    lpid_t  lpid       = LPID_FROM_PID( process->pid );
+    owner_cxy  = CXY_FROM_PID( process->pid );
+    lpid       = LPID_FROM_PID( process->pid );
 
     // get extended pointers on copies root and lock
-    xptr_t root_xp = XPTR( owner_cxy , &cluster->pmgr.copies_root[lpid] );
-    xptr_t lock_xp = XPTR( owner_cxy , &cluster->pmgr.copies_lock[lpid] );
-
-    // take the lock protecting the copies
+    root_xp = XPTR( owner_cxy , &cluster->pmgr.copies_root[lpid] );
+    lock_xp = XPTR( owner_cxy , &cluster->pmgr.copies_lock[lpid] );
+
+    // 1) loop on the process descriptor copies to reset all fd_array[file_id] entries
+
+    // take the lock protecting the list of copies
     remote_spinlock_lock( lock_xp );
 
-    // 1) loop on the process descriptor copies to cancel all fd_array[file_id] entries
-    xptr_t  iter_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 = (process_t *)GET_PTR( process_xp );
-
-        xptr_t lock_xp  = XPTR( process_cxy , &process_ptr->fd_array.lock );
-        xptr_t entry_xp = XPTR( process_cxy , &process_ptr->fd_array.array[file_id] );
-
-        // lock is required for atomic write of a 64 bits word
-        remote_rwlock_wr_lock( lock_xp );
+        process_xp  = XLIST_ELEMENT( iter_xp , process_t , copies_list );
+        process_cxy = GET_CXY( process_xp );
+        process_ptr = GET_PTR( process_xp );
+
+#if (DEBUG_VFS_CLOSE & 1 )
+if( DEBUG_VFS_CLOSE < cycle )
+printk("\n[DBG] %s : reset fd_array[%d] for process %x in cluster %x\n",
+__FUNCTION__, file_id, process_ptr, process_cxy );
+#endif
+
+// fd_array lock is required for atomic write of a 64 bits word
+// xptr_t fd_array_lock_xp = XPTR( process_cxy , &process_ptr->fd_array.lock );
+
+        xptr_t entry_xp         = XPTR( process_cxy , &process_ptr->fd_array.array[file_id] );
+
+// remote_rwlock_wr_lock( fd_array_lock_xp );
+
         hal_remote_swd( entry_xp , XPTR_NULL );
-        remote_rwlock_wr_unlock( lock_xp );
+        
+// remote_rwlock_wr_unlock( fd_array_lock_xp );
+
+        vfs_file_count_down( file_xp );
 
         hal_fence();
     }    
 
+    // release the lock protecting the list of copies
+    remote_spinlock_unlock( lock_xp );
+
+#if (DEBUG_VFS_CLOSE & 1) 
+if( DEBUG_VFS_CLOSE < cycle )
+printk("\n[DBG] %s : thread %x in process %x reset all fd-array copies\n",
+__FUNCTION__, this->trdid, process->pid );
+#endif
+
     // 2) release memory allocated to file descriptor in remote cluster
+
+    // get cluster and local pointer on remote file descriptor
+    file_cxy = GET_CXY( file_xp );
+    file_ptr = GET_PTR( file_xp );
+
     if( file_cxy == local_cxy )             // file cluster is local
     {
@@ -921,4 +977,11 @@
         rpc_vfs_file_destroy_client( file_cxy , file_ptr );
     }
+
+#if DEBUG_VFS_CLOSE
+cycle = (uint32_t)hal_get_cycles();
+if( DEBUG_VFS_CLOSE < cycle )
+printk("\n[DBG] %s : thread %x in process %x exit / fdid %d closed / cycle %d\n",
+__FUNCTION__, this->trdid, process->pid, file_id, cycle );
+#endif
 
     return 0;
@@ -1351,5 +1414,4 @@
     cxy_t              child_cxy;    // cluster for child inode
     vfs_inode_t      * child_ptr;    // local pointer on child inode  
-    vfs_inode_type_t   child_type;   // child inode type
     vfs_fs_type_t      fs_type;      // File system type
     vfs_ctx_t        * ctx_ptr;      // local pointer on FS context
@@ -1358,4 +1420,7 @@
     bool_t             last;         // true when the name is the last in path
     bool_t             found;        // true when a child has been found
+    bool_t             dir;          // searched inode is a directory
+    bool_t             create;       // searched inode must be created if not found
+    bool_t             excl;         // searched inode must not exist
     thread_t         * this;         // pointer on calling thread descriptor
     process_t        * process;      // pointer on calling process descriptor
@@ -1368,8 +1433,13 @@
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_LOOKUP < cycle )
-printk("\n[DBG] %s : thread %x enter for <%s> / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, pathname, cycle );
-#endif
-
+printk("\n[DBG] %s : thread %x in process %x enter for <%s> / cycle %d\n",
+__FUNCTION__, this->trdid, process->pid, pathname, cycle );
+#endif
+
+    // compute lookup flags
+    dir    = mode & VFS_LOOKUP_DIR;
+    create = mode & VFS_LOOKUP_CREATE;
+    excl   = mode & VFS_LOOKUP_EXCL;
+    
     // get extended pointer on first inode to search
     if( pathname[0] == '/' ) parent_xp = process->vfs_root_xp;
@@ -1386,5 +1456,5 @@
 
     // sequencially loop on nodes in pathname
-    // load from device if one node not found in inode tree
+    // load from device if one node in path not found in inode tree
     // exit loop when last name found (i.e. last == true)
     do
@@ -1395,5 +1465,6 @@
 #if (DEBUG_VFS_LOOKUP & 1)
 if( DEBUG_VFS_LOOKUP < cycle )
-printk("\n[DBG] %s : look for <%s> / last = %d\n", __FUNCTION__ , name , last );
+printk("\n[DBG] %s : look for <%s> / last = %d\n",
+__FUNCTION__ , name , last );
 #endif
 
@@ -1403,27 +1474,30 @@
                                &child_xp );
 
-        // if a child inode is not found in the inode tree:
-        // - we create the missing inode/dentry couple in the inode tree,
-        // - we scan the parent mapper to complete the child inode (type and extension),
-        // - we return an error if child not found on device.
-        // - if the missing child is a directory, we load the child mapper from device
-
-        // for the last name, the behaviour depends on the "mode" argument:
-
-        if (found == false ) // child node not found in inode tree 
+        if (found == false )  // child not found in inode tree
         {
 
 #if (DEBUG_VFS_LOOKUP & 1)
 if( DEBUG_VFS_LOOKUP < cycle )
-printk("\n[DBG] %s : miss <%s> => load it\n", __FUNCTION__ , name );
-#endif
+printk("\n[DBG] %s : miss <%s> node => try to create it\n",
+__FUNCTION__ , name );
+#endif
+            // if a child node is not found in the inode tree,
+            // we introduce a new (dentry/inode) in inode tree,
+            // and try to find it by scanning the parent directory mapper.
+            // . if it is found in parent mapper:
+            //   - if the child is a directory, the child mapper is loaded from device
+            //   - if the child is not a directory, the search is completed
+            // . if it is not found in the parent mapper:
+            //   - if ( not last or not create ) an error is reported
+            //   - if (last and create and dir) a new directory is created
+            //   - if (last and create and not dir) a new file is created
 
             // release lock on parent inode
             vfs_inode_unlock( parent_xp );
-
+ 
             // get parent inode FS type
             parent_cxy = GET_CXY( parent_xp );
-            parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp );
-            ctx_ptr    = (vfs_ctx_t *)hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) );
+            parent_ptr = GET_PTR( parent_xp );
+            ctx_ptr    = (vfs_ctx_t *)hal_remote_lpt( XPTR( parent_cxy,&parent_ptr->ctx ) );
             fs_type    = hal_remote_lw( XPTR( parent_cxy , &ctx_ptr->type ) );
 
@@ -1431,20 +1505,29 @@
             child_cxy = vfs_cluster_random_select();
  
-            // insert a new child dentry/inode in parent inode
+            // insert a new child dentry/inode in inode tree
             error = vfs_add_child_in_parent( child_cxy,
-                                             INODE_TYPE_DIR,
+                                             0,           // type will be updated later
                                              fs_type, 
                                              parent_xp, 
                                              name, 
-                                             NULL,     // fs_type_specific inode extend
+                                             NULL,        // fs_type_specific inode extend
                                              &child_xp );
             if( error )
             {
-                printk("\n[ERROR] in %s : thread %x cannot allocate inode for path <%s>\n",
-                __FUNCTION__ , this , pathname );
+                printk("\n[ERROR] in %s : cannot create node %s for path <%s>\n",
+                __FUNCTION__ , name, pathname );
                 return ENOMEM;
             }
 
-            // scan parent mapper to complete the missing inode
+            // get child inode cluster and local pointer
+            child_cxy = GET_CXY( child_xp );
+            child_ptr = GET_PTR( child_xp );
+
+#if (DEBUG_VFS_LOOKUP & 1)
+if( DEBUG_VFS_LOOKUP < cycle )
+printk("\n[DBG] %s : missing <%s> inode speculatively created / cxy %x / ptr %x\n",
+__FUNCTION__ , name , child_cxy, child_ptr );
+#endif
+             // scan parent mapper to complete the missing inode
             if( parent_cxy == local_cxy )
             {
@@ -1462,49 +1545,71 @@
             }
 
-            if ( error )
+            if ( error )   // child not found in parent mapper
             {
-                printk("\n[ERROR] in %s : thread %x / <%s> node not found in <%s>\n",
-                __FUNCTION__ , this , name , pathname );
-                return ENOENT;
-            }
-
-            // get child inode type
-            child_ptr  = (vfs_inode_t *)GET_PTR( child_xp );
-            child_type = hal_remote_lw( XPTR( child_cxy , &child_ptr->type ) );
-
-            // load child mapper from device if it is a directory
-            if( child_type == INODE_TYPE_DIR )
-            {
-                if( child_cxy == local_cxy )
+                if( last && create && dir )  // new directory  => update inode type
                 {
-                    error = vfs_mapper_load_all( child_ptr );
+                     hal_remote_sw( XPTR( child_cxy, &child_ptr->type ), INODE_TYPE_DIR );
+
+#if (DEBUG_VFS_LOOKUP & 1)
+if( DEBUG_VFS_LOOKUP < cycle )
+printk("\n[DBG] %s : created node <%s> in path %s / type DIR\n",
+__FUNCTION__ , name, pathname );
+#endif
                 }
-                else
+                else if ( last && create )   // new file => update inode type
                 {
-                    rpc_vfs_mapper_load_all_client( child_cxy,
-                                                    child_ptr,
-                                                    &error );
+                     hal_remote_sw( XPTR( child_cxy, &child_ptr->type ), INODE_TYPE_FILE );
+
+#if (DEBUG_VFS_LOOKUP & 1)
+if( DEBUG_VFS_LOOKUP < cycle )
+printk("\n[DBG] %s : created node <%s> in path %s / type FILE\n",
+__FUNCTION__ , name, pathname );
+#endif
                 }
-
-                if ( error )
-                {
-                    printk("\n[ERROR] in %s : thread %x / cannot access device for <%s>\n",
-                    __FUNCTION__ , this , name );
-                    return EIO;
+                else                         // not last or not create => remove created node 
+                {                       
+                     printk("\n[ERROR] in %s : <%s> node not found in parent for <%s>\n",
+                     __FUNCTION__ , name , pathname );
+                     vfs_remove_child_from_parent( child_xp );
+                     return ENOENT;
                 }
             }
-
-            // TODO handle lookup mode here [AG]
+            else          // child found in parent
+            {
+                // load child mapper from device if child is a directory (prefetch)
+                if( hal_remote_lw( XPTR( child_cxy , &child_ptr->type ) ) == INODE_TYPE_DIR ) 
+                {
+                    if( child_cxy == local_cxy )
+                    {
+                        error = vfs_mapper_load_all( child_ptr );
+                    }
+                    else
+                    {
+                        rpc_vfs_mapper_load_all_client( child_cxy,
+                                                        child_ptr,
+                                                        &error );
+                    }
+                    if ( error )
+                    {
+                        printk("\n[ERROR] in %s : cannot load <%s> from device\n",
+                        __FUNCTION__ , name );
+                        vfs_remove_child_from_parent( child_xp );
+                        return EIO;
+                    }
+
+#if (DEBUG_VFS_LOOKUP & 1)
+if( DEBUG_VFS_LOOKUP < cycle )
+printk("\n[DBG] %s : load mapper from device for node <%s> in path %s\n",
+__FUNCTION__ , name, pathname );
+#endif
+                }
+            }
 
             // take lock on parent inode
             vfs_inode_lock( parent_xp );
-
-#if (DEBUG_VFS_LOOKUP & 1)
-if( DEBUG_VFS_LOOKUP < cycle )
-printk("\n[DBG] %s : created node <%s>\n", __FUNCTION__ , name );
-#endif
-
         }
-
+        else   // child found in inode tree
+        {
+        
 #if (DEBUG_VFS_LOOKUP & 1)
 if( DEBUG_VFS_LOOKUP < cycle )
@@ -1512,4 +1617,15 @@
 __FUNCTION__ , name , GET_PTR(child_xp) , GET_CXY(child_xp) );
 #endif
+            child_ptr  = GET_PTR( child_xp );
+            child_cxy  = GET_CXY( child_xp );
+            parent_cxy = GET_CXY( parent_xp );
+            parent_ptr = GET_PTR( parent_xp );
+
+            if( last && (mode & VFS_LOOKUP_CREATE) && (mode & VFS_LOOKUP_EXCL) )
+            {
+                printk("\n[ERROR] in %s : node already exist <%s>\n", __FUNCTION__, name );
+                return EINVAL;
+            }
+        }
 
         // TODO check access rights here [AG]
@@ -1540,10 +1656,13 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_LOOKUP < cycle )
-printk("\n[DBG] %s : thread %x exit for <%s> / inode %x in cluster %x / cycle %d\n",
-__FUNCTION__, CURRENT_THREAD, pathname, GET_PTR(child_xp), GET_CXY(child_xp), cycle );
+printk("\n[DBG] %s : thread %x in process %x exit for <%s>\n" 
+"     parent %x in cluster %x / child %x in cluster %x / cycle %d\n",
+__FUNCTION__ , this->trdid, process->pid, pathname, 
+parent_ptr, parent_cxy, child_ptr, child_cxy, cycle );
 #endif
 
     // return searched pointer
-    *inode_xp = child_xp;
+    if( mode & VFS_LOOKUP_PARENT ) *inode_xp = parent_xp;
+    else                           *inode_xp = child_xp;
 
     return 0;
@@ -1638,11 +1757,12 @@
     // get parent inode cluster and local pointer
     parent_cxy = GET_CXY( parent_xp );
-    parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp );
+    parent_ptr = GET_PTR( parent_xp );
 
 #if DEBUG_VFS_ADD_CHILD
 uint32_t cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_ADD_CHILD < cycle )
-printk("\n[DBG] %s : thread %x enter for <%s> / child_cxy = %x / parent_cxy = %x\n",
-__FUNCTION__ , CURRENT_THREAD , name , child_cxy , parent_cxy );
+printk("\n[DBG] %s : thread %x enter for <%s> / child_cxy = %x / parent_cxy = %x / cycle %d\n",
+__FUNCTION__, CURRENT_THREAD->trdid, CURRENT_THREAD->process->pid, name,
+child_cxy, parent_cxy, (uint32_t)hal_get_cycles() );
 #endif
 
@@ -1736,7 +1856,7 @@
                __FUNCTION__ , child_cxy );
  
-        vfs_dentry_t * dentry = (vfs_dentry_t *)GET_PTR( dentry_xp );
+        vfs_dentry_t * dentry = GET_PTR( dentry_xp );
         if( parent_cxy == local_cxy ) vfs_dentry_destroy( dentry );
-        else rpc_vfs_dentry_destroy_client( parent_cxy , dentry );
+        else rpc_vfs_dentry_destroy_client( parent_cxy , dentry , &error );
         return ENOMEM;
     }
@@ -1750,6 +1870,6 @@
 cycle = (uint32_t)hal_get_cycles();
 if( DEBUG_VFS_ADD_CHILD < cycle )
-printk("\n[DBG] %s : thread %x exit for <%s>\n",
-__FUNCTION__ , CURRENT_THREAD , name );
+printk("\n[DBG] %s : thread %x in process %x exit for <%s>\n",
+__FUNCTION__, CURRENT_THREAD, CURRENT_THREAD->process->pid, name );
 #endif
 
@@ -1758,5 +1878,58 @@
     return 0;
 
+    // FIXME update the refcount fields in both inode and dentry
+
 }  // end vfs_add_child_in_parent()
+
+///////////////////////////////////////////////////////
+error_t vfs_remove_child_from_parent( xptr_t inode_xp )
+{
+    cxy_t          inode_cxy;
+    vfs_inode_t  * inode_ptr;
+    xptr_t         dentry_xp;
+    cxy_t          dentry_cxy;
+    vfs_dentry_t * dentry_ptr;
+    error_t        error;
+   
+    // get inode cluster and local pointer
+    inode_cxy = GET_CXY( inode_xp );
+    inode_ptr = GET_PTR( inode_xp );
+
+    // get cluster and pointers of associated dentry
+    dentry_xp  = hal_remote_lwd( XPTR( inode_cxy , &inode_ptr->parent_xp ) );
+    dentry_cxy = GET_CXY( dentry_xp ); 
+    dentry_ptr = GET_PTR( dentry_xp );
+
+    // FIXME update the refcount fields in both inode and dentry
+
+    // delete dentry
+    if( dentry_cxy == local_cxy )
+    {
+         error = vfs_dentry_destroy( dentry_ptr );
+    }
+    else
+    {
+         rpc_vfs_dentry_destroy_client( dentry_cxy,
+                                        dentry_ptr,
+                                        &error );
+    }
+    if( error ) return EINVAL;
+
+    // delete inode
+    if( inode_cxy == local_cxy )
+    {
+         vfs_inode_destroy( inode_ptr );
+    }
+    else
+    {
+         rpc_vfs_inode_destroy_client( inode_cxy,
+                                       inode_ptr,
+                                       &error );
+    }
+    if( error ) return EINVAL;
+
+    return 0;
+
+}  // end vfs_remove_child_from_parent()
 
 //////////////////////////////////////////////////////////////////////////////////////////
