/* * vfs.c - Virtual File System implementation. * * Author Mohamed Lamine Karaoui (2015) * Alain Greiner (2016) * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////////////////// // Global variables ////////////////////////////////////////////////////////////////////////////////////////// // array of supported FS contexts (indexed by the FS type) vfs_ctx_t fs_context[FS_TYPES_NR]; ////////////////////////////////////////////////////////////////////////////////////////// // Context related functions ////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////// error_t vfs_ctx_inum_alloc( vfs_ctx_t * ctx, uint32_t * inum ) { // get lock on inum allocator spinlock_lock( &ctx->lock ); // get lid from local inum allocator uint32_t lid = bitmap_ffc( ctx->inum , CONFIG_VFS_MAX_INODES ); if( lid == -1 ) // no more free slot => error { // release lock spinlock_unlock( &ctx->lock ); // return error return 1; } else // found => return inum { // set slot allocated bitmap_set( ctx->inum , lid ); // release lock spinlock_unlock( &ctx->lock ); // return inum *inum = (((uint32_t)local_cxy) << 16) | (lid & 0xFFFF); return 0; } } //////////////////////////////////////////// void vfs_ctx_inum_release( vfs_ctx_t * ctx, uint32_t inum ) { bitmap_clear( ctx->inum , inum & 0xFFFF ); } ////////////////////////////////////////////////////////////////////////////////////////// // Inode related functions ////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////// error_t vfs_inode_create( xptr_t dentry_xp, uint32_t type, uint32_t attr, uint32_t mode, uid_t uid, gid_t gid, xptr_t * inode_xp ) { mapper_t * mapper; // associated mapper( to be allocated) vfs_inode_t * inode; // inode descriptor (to be allocated) 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; // check type and get pointer on context if ( type == FS_TYPE_FATFS ) ctx = &fs_context[FS_TYPE_FATFS]; else if( type == FS_TYPE_RAMFS ) ctx = &fs_context[FS_TYPE_RAMFS]; else { ctx = NULL; printk("\n[PANIC] in %s : undefined file system type\n", __FUNCTION__ ); hal_core_sleep(); } // allocate inum error = vfs_ctx_inum_alloc( ctx , &inum ); if( error ) { printk("\n[ERROR] in %s : cannot allocate inum\n", __FUNCTION__ ); return ENOMEM; } // allocate memory for mapper mapper = mapper_create(); if( mapper == NULL ) { printk("\n[ERROR] in %s : cannot allocate mapper\n", __FUNCTION__ ); vfs_ctx_inum_release( ctx , inum ); return ENOMEM; } // allocate memory for inode descriptor req.type = KMEM_VFS_INODE; req.size = sizeof(vfs_inode_t); req.flags = AF_KERNEL | AF_ZERO; inode = (vfs_inode_t *)kmem_alloc( &req ); if( inode == NULL ) { printk("\n[ERROR] in %s : cannot allocate inode descriptor\n", __FUNCTION__ ); vfs_ctx_inum_release( ctx , inum ); mapper_destroy( mapper ); return ENOMEM; } // initialize inode descriptor inode->gc = 0; inode->inum = inum; inode->attr = attr; inode->mode = mode; inode->uid = uid; inode->gid = gid; inode->refcount = 0; inode->parent_xp = dentry_xp; inode->ctx = ctx; inode->mapper = NULL; // initialise threads waiting queue xlist_root_init( XPTR( local_cxy , &inode->wait_root ) ); // initialize dentries hash table, if new inode is a directory if( attr & INODE_ATTR_DIR ) xhtab_init( &inode->children , XHTAB_DENTRY_TYPE ); // initialize inode locks remote_rwlock_init( XPTR( local_cxy , &inode->size_lock ) ); remote_spinlock_init( XPTR( local_cxy , &inode->main_lock ) ); // create FS specific inode if ( ctx->type == FS_TYPE_FATFS ) fatfs_inode_create( inode ); else if( ctx->type == FS_TYPE_RAMFS ) ramfs_inode_create( inode ); // return extended pointer on inode *inode_xp = XPTR( local_cxy , inode ); return 0; } // end vfs_inode_create() ///////////////////////////////////////////// void vfs_inode_destroy( vfs_inode_t * inode ) { if( inode->refcount ) { printk("\n[PANIC] in %s : inode refcount non zero\n", __FUNCTION__ ); hal_core_sleep(); } // release memory allocated for mapper mapper_destroy( inode->mapper ); // release memory allocate for inode descriptor kmem_req_t req; req.ptr = inode; req.type = KMEM_VFS_INODE; kmem_free( &req ); } // end vfs_inode_destroy() //////////////////////////////////////////// void vfs_inode_remote_up( xptr_t inode_xp ) { // get inode cluster and local pointer cxy_t inode_cxy = GET_CXY( inode_xp ); vfs_inode_t * inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp ); hal_remote_atomic_add( XPTR( inode_cxy , &inode_ptr->refcount ) , 1 ); } ////////////////////////////////////////////// void vfs_inode_remote_down( xptr_t inode_xp ) { // get inode cluster and local pointer cxy_t inode_cxy = GET_CXY( inode_xp ); vfs_inode_t * inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp ); hal_remote_atomic_add( XPTR( inode_cxy , &inode_ptr->refcount ) , -1 ); } ////////////////////////////////////////////// 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 = (vfs_inode_t *)GET_PTR( inode_xp ); // get size remote_rwlock_rd_lock( XPTR( cxy , &ptr->size_lock ) ); uint32_t size = hal_remote_lw( XPTR( cxy , &ptr->size ) ); remote_rwlock_rd_unlock( XPTR( cxy , &ptr->size_lock ) ); return size; } ///////////////////////////////////////////////// void vfs_inode_size_set_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 = (vfs_inode_t *)GET_PTR( inode_xp ); // set size remote_rwlock_wr_unlock( XPTR( cxy , &ptr->size_lock ) ); hal_remote_sw( XPTR( cxy , &ptr->size ) , size ); remote_rwlock_wr_unlock( XPTR( cxy , &ptr->size_lock ) ); } /////////////////////////////////////////////// void vfs_inode_remote_unlock( xptr_t inode_xp ) { // get inode cluster and local pointer cxy_t cxy = GET_CXY( inode_xp ); vfs_inode_t * ptr = (vfs_inode_t *)GET_PTR( inode_xp ); // release the main lock remote_spinlock_unlock( XPTR( cxy , &ptr->main_lock ) ); } ///////////////////////////////////////////// void vfs_inode_remote_lock( xptr_t inode_xp ) { // get inode cluster and local pointer cxy_t cxy = GET_CXY( inode_xp ); vfs_inode_t * ptr = (vfs_inode_t *)GET_PTR( inode_xp ); // get the main lock remote_spinlock_lock( XPTR( cxy , &ptr->main_lock ) ); } ////////////////////////////////////////////////////////////////////////////////////////// // Dentry related functions ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// error_t vfs_dentry_create( uint32_t type, char * name, vfs_inode_t * parent, xptr_t * dentry_xp ) { vfs_ctx_t * ctx; // context descriptor vfs_dentry_t * dentry; // dentry descriptor (to be allocated) kmem_req_t req; // request to kernel memory allocator xptr_t xhtab_xp; // extended pointer on xhtab_t embedded in inode xptr_t xlist_xp; // extended pointer on xlist_entry_t in dentry // check type and get pointer on context if ( type == FS_TYPE_FATFS ) ctx = &fs_context[FS_TYPE_FATFS]; else if( type == FS_TYPE_RAMFS ) ctx = &fs_context[FS_TYPE_RAMFS]; else { ctx = NULL; printk("\n[PANIC] in %s : undefined file system type\n", __FUNCTION__ ); hal_core_sleep(); } // get name length uint32_t length = strlen( name ); if( length > (CONFIG_VFS_MAX_NAME_LENGTH - 1) ) { printk("\n[ERROR] in %s : name too long\n", __FUNCTION__ ); return EINVAL; } // allocate memory for dentry descriptor req.type = KMEM_VFS_DENTRY; req.size = sizeof(vfs_dentry_t); req.flags = AF_KERNEL | AF_ZERO; dentry = (vfs_dentry_t *)kmem_alloc( &req ); if( dentry == NULL ) { printk("\n[ERROR] in %s : cannot allocate dentry descriptor\n", __FUNCTION__ ); return ENOMEM; } // initialize dentry descriptor dentry->ctx = ctx; dentry->length = length; dentry->parent = parent; strcpy( dentry->name , name ); // return extended pointer on dentry to caller *dentry_xp = XPTR( local_cxy , dentry ); // register dentry in hash table rooted in parent inode xhtab_xp = XPTR( local_cxy , &parent->children ); xlist_xp = XPTR( local_cxy , &dentry->xlist ); xhtab_register( xhtab_xp , name , xlist_xp ); return 0; } // end vfs_dentry_create() //////////////////////////////////////////////// void vfs_dentry_destroy( vfs_dentry_t * dentry ) { if( dentry->refcount ) { printk("\n[PANIC] in %s : dentry refcount non zero\n", __FUNCTION__ ); hal_core_sleep(); } kmem_req_t req; req.ptr = dentry; req.type = KMEM_VFS_DENTRY; kmem_free( &req ); } ////////////////////////////////////////////////////////////////////////////////////////// // File descriptor related functions ////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// void vfs_file_count_up( xptr_t file_xp ) { // 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 ); // atomically increment count hal_remote_atomic_add( XPTR( file_cxy , &file_ptr->refcount ) , 1 ); } ////////////////////////////////////////// void vfs_file_count_down( xptr_t file_xp ) { // 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 ); // atomically decrement count hal_remote_atomic_add( XPTR( file_cxy , &file_ptr->refcount ) , -1 ); } //////////////////////////////////////////////// error_t vfs_file_create( xptr_t inode_xp, uint32_t type, uint32_t attr, xptr_t * file_xp ) { vfs_file_t * file_ptr; kmem_req_t req; // get inode cluster and local pointer cxy_t inode_cxy = GET_CXY( inode_xp ); vfs_inode_t * inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp ); // check cluster identifier if( inode_cxy != local_cxy ) { printk("\n[PANIC] in %s : local cluster is not the inode owner\n", __FUNCTION__ ); hal_core_sleep(); } // allocate memory for new file descriptor req.type = KMEM_VFS_FILE; req.size = sizeof(vfs_file_t); req.flags = AF_KERNEL | AF_ZERO; file_ptr = (vfs_file_t *)kmem_alloc( &req ); if( file_ptr == NULL ) return ENOMEM; // get inode local pointer // initializes new file descriptor file_ptr->gc = 0; file_ptr->type = type; file_ptr->attr = attr; file_ptr->offset = 0; file_ptr->refcount = 0; file_ptr->inode = inode_ptr; file_ptr->ctx = inode_ptr->ctx; file_ptr->mapper = inode_ptr->mapper; remote_rwlock_init( XPTR( local_cxy , &file_ptr->lock ) ); *file_xp = XPTR( local_cxy , file_ptr ); return 0; } //////////////////////////////////////// void vfs_file_destroy( xptr_t file_xp ) { // 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 ); if( file_cxy != local_cxy ) { printk("\n[PANIC] in %s : file descriptor not in local cluster\n", __FUNCTION__ ); hal_core_sleep(); } if( file_ptr->refcount ) { printk("\n[PANIC] in %s : file refcount non zero\n", __FUNCTION__ ); hal_core_sleep(); } kmem_req_t req; req.ptr = file_ptr; req.type = KMEM_VFS_FILE; kmem_free( &req ); } ////////////////////////////////////////////////////////////////////////////////////////// // File related functions ////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////// error_t vfs_open( xptr_t cwd_xp, char * path, uint32_t flags, xptr_t * file_xp ) { return 0; } /////////////////////////////////// uint32_t vfs_read( xptr_t file_xp, void * buffer, uint32_t size ) { return 0; } //////////////////////////////////// uint32_t vfs_write( xptr_t file_xp, void * buffer, uint32_t size ) { return 0; } ////////////////////////////////////// error_t vfs_lseek( xptr_t file_xp, uint32_t offset, uint32_t whence, uint32_t * new_offset ) { return 0; } ////////////////////////////////////// error_t vfs_close( xptr_t file_xp, uint32_t * refcount ) { return 0; } //////////////////////////////////// error_t vfs_unlink( xptr_t cwd_xp, char * path ) { return 0; } ////////////////////////////////////// error_t vfs_stat( xptr_t file_xp, vfs_stat_t * stat ) { return 0; } ////////////////////////////////////////////////////////////////////////////////////////// // Directory related functions ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // Inode Tree functions ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // 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. ////////////////////////////////////////////////////////////////////////////////////////// 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 = (vfs_inode_t *)GET_PTR( inode_xp ); // get inode access mode, UID, and GID // TODO uint32_t mode = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->mode ) ); uid_t uid = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->uid ) ); gid_t gid = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->gid ) ); // FIXME : 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 inode (parent directory inode), a directory // entry name, and returns an extended pointer on the child inode. // 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 containing children dentries xptr_t dentry_xp; // extended pointer on children dentry // get parent inode cluster and local pointer cxy_t parent_cxy = GET_CXY( parent_xp ); vfs_inode_t * parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp ); // get extended pointer on hash table of children directory entries xhtab_xp = XPTR( parent_cxy , &parent_ptr->children ); // search extended pointer on matching dentry dentry_xp = xhtab_lookup( xhtab_xp , name ); if( dentry_xp == XPTR_NULL ) return false; // get dentry cluster and local pointer cxy_t dentry_cxy = GET_CXY( dentry_xp ); vfs_dentry_t * dentry_ptr = (vfs_dentry_t *)GET_PTR( dentry_xp ); // return child inode *child_xp = (xptr_t)hal_remote_lwd( XPTR( dentry_cxy , &dentry_ptr->parent ) ); return true; } ////////////////////////////////////////////////////////////////////////////////////////// // This static function is used by the vfs_lookup() function. // It takes the pointer on a buffer containing a complete pathname, and return // in the buffer, allocated by the caller, a single name in the path. // It return also in the pointer the next character to analyse in the path. // Finally it returns a boolean, that is true when the returned 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 buffer. ////////////////////////////////////////////////////////////////////////////////////////// // @ 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 EINVAL 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++; // return EINVAL if string empty if( *ptr == 0 ) return EINVAL; // copy all characters in name until NUL or '/' while( (*ptr != 0) && (*ptr !='/') ) *(name++) = *(ptr++); // 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; } //////////////////////////////////////// error_t vfs_lookup( xptr_t cwd_xp, char * pathname, uint32_t client_uid, uint32_t client_gid, xptr_t * inode_xp, xptr_t * ctx_xp ) { 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 parentc inode vfs_inode_t * parent_ptr; // local pointer on parent inode 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 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 thread_t * this; // pointer on calling thread descriptor process_t * process; // pointer on calling process descriptor vfs_ctx_t * ctx; // parent inode context uint32_t type; // file system type of parent inode error_t error; this = CURRENT_THREAD; process = this->process; // get extended pointer on first inode to search if( pathname[0] == '/' ) parent_xp = process->vfs_root_xp; else parent_xp = cwd_xp; // initialise loop variables current = pathname; next = NULL; last = false; child_xp = XPTR_NULL; // take lock on parent inode vfs_inode_remote_lock( parent_xp ); // break : if one intermediate name not found // exit : when last name found (i.e. last == true) do { // get cluster and local pointer for parent inode parent_cxy = GET_CXY( parent_xp ); parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp ); // get parent inode FS type ctx = (vfs_ctx_t *)(intptr_t)hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) ); type = ctx->type; // get one name from path vfs_get_name_from_path( current , name , &next , &last ); // search a child dentry matching name for parent inode found = vfs_get_child( parent_xp, name, &child_xp ); if( found == false ) // child inode not found in inode tree => try to load it { // release lock on parent inode vfs_inode_remote_unlock( parent_xp ); // insert a new dentry/inode in parent inode error = vfs_add_child_in_parent( type , parent_xp , name , &child_xp ); if( error ) { printk("\n[ERROR] in %s : inode %s not found in path %s\n", __FUNCTION__ , name , pathname ); return ENOENT; } // take lock on parent inode vfs_inode_remote_lock( parent_xp ); } // check access rights error = vfs_access_denied( child_xp, client_uid, client_gid ); if( error ) { printk("\n[ERROR] in %s : permission denied for %s\n", __FUNCTION__ , name ); return EACCES; } // take lock on child inode if not last if( last == false ) vfs_inode_remote_lock( child_xp ); // release lock on parent inode vfs_inode_remote_unlock( parent_xp ); // update loop variables parent_xp = child_xp; current = next; } while( last == false ); vfs_dmsg("\n[INFO] in %s : searched inode found for %s\n", __FUNCTION__ , pathname ); // get cluster and local pointer on child inode child_cxy = GET_CXY( child_xp ); child_ptr = (vfs_inode_t *)GET_PTR( child_xp ); // return searched pointers *inode_xp = child_xp; *ctx_xp = (xptr_t)hal_remote_lwd( XPTR( child_cxy , &child_ptr->ctx ) ); return 0; } // end vfs_lookup() //////////////////////////////////////////// error_t vfs_get_path( xptr_t searched_xp, char * buffer, uint32_t max_size ) { xptr_t dentry_xp; // extended pointer on current dentry char * name; // local pointer on current dentry name uint32_t length; // length of current dentry name uint32_t count; // number of characters written in buffer uint32_t index; // slot index in buffer xptr_t inode_xp; // extended pointer on // implementation note: // we use two variables "index" and "count" because the buffer // is actually written in decreasing index order (from leaf to root) // TODO : handle conflict with a concurrent rename // FIXME : handle synchro in the loop ... [AG] // set the NUL character in buffer / initialise buffer index and count buffer[max_size - 1] = 0; count = 1; index = max_size - 2; // initialize current inode inode_xp = searched_xp; // exit when root inode found (i.e. dentry_xp == XPTR_NULL) do { // get inode cluster and local pointer cxy_t inode_cxy = GET_CXY( inode_xp ); vfs_inode_t * inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp ); // get extended pointer on parent dentry dentry_xp = (xptr_t)hal_remote_lwd( XPTR( inode_cxy , inode_ptr->parent_xp ) ); // get dentry cluster and local pointer cxy_t dentry_cxy = GET_CXY( dentry_xp ); vfs_dentry_t * dentry_ptr = (vfs_dentry_t *)GET_PTR( dentry_xp ); // get dentry name length and pointer length = hal_remote_lw( XPTR( dentry_cxy , &dentry_ptr->length ) ); name = (char *)hal_remote_lpt( XPTR( dentry_cxy , &dentry_ptr->name ) ); // update index and count index -= (length + 1); count += (length + 1); // check buffer overflow if( count >= max_size ) { printk("\n[ERROR] in %s : kernel buffer too small\n", __FUNCTION__ ); return EINVAL; } // update pathname hal_remote_memcpy( XPTR( local_cxy , &buffer[index + 1] ) , XPTR( dentry_cxy , name ) , length ); buffer[index] = '/'; // get extended pointer on next inode inode_xp = (xptr_t)hal_remote_lwd( XPTR( dentry_cxy , dentry_ptr->parent ) ); } while( (dentry_xp != XPTR_NULL) ); return 0; } // end vfs_get_path() /////////////////////////////////////////////// error_t vfs_add_child_in_parent( uint32_t type, xptr_t parent_xp, char * name, xptr_t * child_xp ) { xptr_t dentry_xp; // extended pointer on created dentry xptr_t inode_xp; // extended pointer on created inode error_t error; // get parent inode cluster and local pointer cxy_t parent_cxy = GET_CXY( parent_xp ); vfs_inode_t * parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp ); // create dentry if( parent_cxy == local_cxy ) // parent cluster is the local cluster { error = vfs_dentry_create( type, name, parent_ptr, &dentry_xp ); } else // parent cluster is remote { rpc_vfs_dentry_create_client( parent_cxy, type, name, parent_ptr, &dentry_xp, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot create dentry in cluster %x\n", __FUNCTION__ , parent_cxy ); return error; } // select a target cluster for child inode uint32_t x_size = LOCAL_CLUSTER->x_size; uint32_t y_size = LOCAL_CLUSTER->y_size; uint32_t y_width = LOCAL_CLUSTER->y_width; uint32_t index = ( hal_time_stamp() + hal_get_gid() ) % (x_size * y_size); uint32_t x = index / y_size; uint32_t y = index % y_size; cxy_t child_cxy = (x<