/* * 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 #include #include #include ////////////////////////////////////////////////////////////////////////////////////////// // Extern variables ////////////////////////////////////////////////////////////////////////////////////////// extern vfs_ctx_t fs_context[FS_TYPES_NR]; // allocate in kernel_init.c ////////////////////////////////////////////////////////////////////////////////////////// // 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->bitmap , 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->bitmap , 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->bitmap , inum & 0xFFFF ); } ////////////////////////////////////////////////////////////////////////////////////////// // Inode related functions ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////// error_t vfs_inode_create( xptr_t dentry_xp, vfs_fs_type_t fs_type, vfs_inode_type_t inode_type, uint32_t attr, uint32_t rights, 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 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 { 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 VFS 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->type = inode_type; inode->inum = inum; inode->attr = attr; inode->rights = rights; 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( inode_type == INODE_TYPE_DIR ) xhtab_init( &inode->children , XHTAB_DENTRY_TYPE ); // initialize inode locks remote_rwlock_init( XPTR( local_cxy , &inode->data_lock ) ); remote_spinlock_init( XPTR( local_cxy , &inode->main_lock ) ); // 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->data_lock ) ); uint32_t size = hal_remote_lw( XPTR( cxy , &ptr->size ) ); remote_rwlock_rd_unlock( XPTR( cxy , &ptr->data_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->data_lock ) ); hal_remote_sw( XPTR( cxy , &ptr->size ) , size ); remote_rwlock_wr_unlock( XPTR( cxy , &ptr->data_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( vfs_fs_type_t fs_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 // check 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 { 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 ) { 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 ); // register dentry in hash table rooted in parent inode xhtab_insert( XPTR( local_cxy , &parent->children ), name, XPTR( local_cxy , &dentry->xlist ) ); // return extended pointer on dentry *dentry_xp = XPTR( local_cxy , dentry ); 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 ////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////// error_t vfs_file_create( vfs_inode_t * inode, uint32_t attr, xptr_t * file_xp ) { vfs_file_t * file; kmem_req_t req; // allocate memory for new file descriptor req.type = KMEM_VFS_FILE; req.size = sizeof(vfs_file_t); req.flags = AF_KERNEL | AF_ZERO; file = (vfs_file_t *)kmem_alloc( &req ); if( file == NULL ) return ENOMEM; // initializes new file descriptor file->gc = 0; file->type = inode->type; file->attr = attr; file->offset = 0; file->refcount = 0; file->inode = inode; file->ctx = inode->ctx; file->mapper = inode->mapper; remote_rwlock_init( XPTR( local_cxy , &file->lock ) ); *file_xp = XPTR( local_cxy , file ); return 0; } // end vfs_file_create() /////////////////////////////////////////// void vfs_file_destroy( vfs_file_t * file ) { if( file->refcount ) { printk("\n[PANIC] in %s : file refcount non zero\n", __FUNCTION__ ); hal_core_sleep(); } kmem_req_t req; req.ptr = file; req.type = KMEM_VFS_FILE; kmem_free( &req ); } // end vfs_file_destroy() //////////////////////////////////////// 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 ); } ////////////////////////////////////////////////////////////////////////////////////////// // File access related functions ////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////// error_t vfs_open( xptr_t cwd_xp, char * path, 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 // 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; // compute attributes for the created file file_attr = 0; if( (flags & O_RDONLY ) == 0 ) file_attr |= FD_ATTR_READ_ENABLE; if( (flags & O_WRONLY ) == 0 ) file_attr |= FD_ATTR_WRITE_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; // get extended pointer on target inode error = vfs_lookup( cwd_xp , path , lookup_mode , &inode_xp ); if( error ) return error; // get target inode cluster and local pointer inode_cxy = GET_CXY( inode_xp ); inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp ); // create a new file descriptor in cluster containing inode if( inode_cxy == local_cxy ) // target cluster is local { error = vfs_file_create( inode_ptr , file_attr , &file_xp ); } else // target cluster is remote { rpc_vfs_file_create_client( inode_cxy , inode_ptr , file_attr , &file_xp , &error ); } if( error ) return error; // allocate and register a new file descriptor index in reference cluster fd_array error = process_fd_register( file_xp , &file_id ); if( error ) return error; // success *new_file_xp = file_xp; *new_file_id = file_id; return 0; } // end vfs_open() ///////////////////////////////////// error_t vfs_move( bool_t to_buffer, xptr_t file_xp, void * buffer, uint32_t size ) { assert( ( file_xp != XPTR_NULL ) , __FUNCTION__ , "file_xp == XPTR_NULL" ); cxy_t file_cxy; // remote file descriptor cluster vfs_file_t * file_ptr; // remote file descriptor local pointer vfs_inode_type_t inode_type; uint32_t file_offset; // current offset in file mapper_t * mapper; error_t error; // get cluster and local pointer on remote file descriptor file_cxy = GET_CXY( file_xp ); file_ptr = (vfs_file_t *)GET_PTR( file_xp ); // get inode type from remote file descriptor inode_type = hal_remote_lw( XPTR( file_cxy , &file_ptr->type ) ); // action depends on inode type if( inode_type == INODE_TYPE_FILE ) { // get mapper pointer and file offset from file descriptor file_offset = hal_remote_lw( XPTR( file_cxy , &file_ptr->offset ) ); mapper = (mapper_t *)hal_remote_lpt( XPTR( file_cxy , &file_ptr->mapper ) ); // move data between mapper and buffer if( file_cxy == local_cxy ) { error = mapper_move( mapper, to_buffer, file_offset, buffer, size ); } else { rpc_mapper_move_client( file_cxy, mapper, to_buffer, file_offset, buffer, size, &error ); } return error; } else if (inode_type == INODE_TYPE_DIR ) { printk("\n[ERROR] in %s : inode is a directory", __FUNCTION__ ); return EINVAL; } else if (inode_type == INODE_TYPE_DEV ) { // TODO return 0; } else { printk("\n[PANIC] in %s : illegal inode type\n", __FUNCTION__ ); hal_core_sleep(); return -1; } } // end vfs_access() ////////////////////////////////////// error_t vfs_lseek( xptr_t file_xp, uint32_t offset, uint32_t whence, uint32_t * new_offset ) { printk("\n[PANIC] %s non implemented\n", __FUNCTION__ ); hal_core_sleep(); return 0; assert( ( file_xp != XPTR_NULL ) , __FUNCTION__ , "file_xp == XPTR_NULL" ); } // vfs_lseek() /////////////////////////////////// error_t vfs_close( xptr_t file_xp, uint32_t file_id ) { assert( (file_xp != XPTR_NULL) , __FUNCTION__ , "file_xp == XPTR_NULL" ); assert( (file_id < CONFIG_PROCESS_FILE_MAX_NR) , __FUNCTION__ , "illegal file_id" ); thread_t * this = CURRENT_THREAD; 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 ); // get local pointer on local cluster manager cluster_t * 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 ); // 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 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 ); hal_remote_swd( entry_xp , XPTR_NULL ); remote_rwlock_wr_unlock( lock_xp ); hal_wbflush(); } // 2) release memory allocated to file descriptor in remote cluster if( file_cxy == local_cxy ) // file cluster is local { vfs_file_destroy( file_ptr ); } else // file cluster is local { rpc_vfs_file_destroy_client( file_cxy , file_ptr ); } return 0; } // end vfs_close() //////////////////////////////////// error_t vfs_unlink( xptr_t cwd_xp, char * path ) { printk("\n[PANIC] %s non implemented\n", __FUNCTION__ ); hal_core_sleep(); return 0; } // vfs_unlink() /////////////////////////////////////// error_t vfs_stat( xptr_t file_xp, vfs_stat_t * k_stat ) { printk("\n[PANIC] %s non implemented\n", __FUNCTION__ ); hal_core_sleep(); return 0; } //////////////////////////////////////////// error_t vfs_readdir( xptr_t file_xp, vfs_dirent_t * k_dirent ) { printk("\n[PANIC] %s non implemented\n", __FUNCTION__ ); hal_core_sleep(); return 0; } ////////////////////////////////////// error_t vfs_mkdir( xptr_t file_xp, char * path, uint32_t mode ) { printk("\n[PANIC] %s non implemented\n", __FUNCTION__ ); hal_core_sleep(); return 0; } //////////////////////////////////// error_t vfs_rmdir( xptr_t file_xp, char * path ) { printk("\n[PANIC] %s non implemented\n", __FUNCTION__ ); hal_core_sleep(); return 0; } /////////////////////////////////// error_t vfs_chdir( xptr_t cwd_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 uint32_t mode; // lookup working mode vfs_inode_type_t inode_type; // target inode type // set lookup working mode mode = 0; // get extended pointer on target inode error = vfs_lookup( cwd_xp , path , mode , &inode_xp ); if( error ) return error; // get inode cluster and local pointer inode_cxy = GET_CXY( inode_xp ); inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp ); // get inode type from remote file inode_type = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->type ) ); if( inode_type != INODE_TYPE_DIR ) { CURRENT_THREAD->errno = ENOTDIR; return -1; } printk("\n[PANIC] %s non fully implemented\n", __FUNCTION__ ); hal_core_sleep(); return 0; } /////////////////////////////////// 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 uint32_t mode; // lookup working mode vfs_inode_type_t inode_type; // target inode type // set lookup working mode mode = 0; // get extended pointer on target inode error = vfs_lookup( cwd_xp , path , mode , &inode_xp ); if( error ) return error; // get inode cluster and local pointer inode_cxy = GET_CXY( inode_xp ); inode_ptr = (vfs_inode_t *)GET_PTR( inode_xp ); // get inode type from remote inode inode_type = hal_remote_lw( XPTR( inode_cxy , &inode_ptr->type ) ); printk("\n[PANIC] %s non fully implemented\n", __FUNCTION__ ); hal_core_sleep(); return 0; } /////////////////////////////////// error_t vfs_mkfifo( xptr_t cwd_xp, char * path, uint32_t rights ) { printk("\n[PANIC] in %s : not implemented yet\n", __FUNCTION__ ); hal_core_sleep(); return 0; } /////////////////////////////////////////////////////////////////////////////////////////r // Inode Tree functions ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // This 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 mode, xptr_t * inode_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 parent 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 vfs_inode_type_t inode_type; // child inode type 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 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; // 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 one name from path and the last flag 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 ); // get cluster and local pointer on parent inode parent_cxy = GET_CXY( parent_xp ); parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp ); // get parent inode FS type ctx_ptr = (vfs_ctx_t *)hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) ); fs_type = ctx_ptr->type; // get child inode type if( (last == false) || (mode & VFS_LOOKUP_DIR) ) inode_type = INODE_TYPE_DIR; else inode_type = INODE_TYPE_FILE; // insert a child dentry/inode in parent inode error = vfs_add_child_in_parent( inode_type, fs_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; 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( vfs_inode_type_t inode_type, vfs_fs_type_t fs_type, xptr_t parent_xp, char * name, xptr_t * child_xp ) { error_t error; xptr_t dentry_xp; // extended pointer on created dentry xptr_t inode_xp; // extended pointer on created inode cxy_t parent_cxy; // parent inode cluster identifier vfs_inode_t * parent_ptr; // parent inode local pointer vfs_ctx_t * parent_ctx; // parent inode context local pointer // get parent inode cluster and local pointer parent_cxy = GET_CXY( parent_xp ); parent_ptr = (vfs_inode_t *)GET_PTR( parent_xp ); // get parent inode context local pointer parent_ctx = (vfs_ctx_t *)hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) ); // create dentry if( parent_cxy == local_cxy ) // parent cluster is the local cluster { error = vfs_dentry_create( fs_type, name, parent_ptr, &dentry_xp ); } else // parent cluster is remote { rpc_vfs_dentry_create_client( parent_cxy, fs_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<mapper; assert( (mapper != NULL) , __FUNCTION__ , "no mapper for page\n" ); // get FS type vfs_fs_type_t fs_type = mapper->inode->ctx->type; // update mapper if permitted by file system type if( fs_type == FS_TYPE_FATFS ) { // get mapper lock in WRITE_MODE rwlock_wr_lock( &mapper->lock ); error = fatfs_read_page( page ); // release mapper lock rwlock_wr_unlock( &mapper->lock ); } else if( fs_type == FS_TYPE_RAMFS ) { assert( false , __FUNCTION__ , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { assert( false , __FUNCTION__ , "should not be called for DEVFS\n" ); } else { assert( false , __FUNCTION__ , "undefined file system type\n" ); } return error; } // end vfs_move_page_to_mapper() ////////////////////////////////////////////////// error_t vfs_move_page_from_mapper( page_t * page ) { error_t error = 0; assert( (page != NULL) , __FUNCTION__ , "page pointer is NULL\n" ); mapper_t * mapper = page->mapper; assert( (mapper != NULL) , __FUNCTION__ , "no mapper for page\n" ); // get FS type vfs_fs_type_t fs_type = mapper->inode->ctx->type; // update file system if permitted by file system type if( fs_type == FS_TYPE_FATFS ) { if( page_is_flag( page , PG_DIRTY ) ) { // get mapper lock in READ_MODE rwlock_rd_lock( &mapper->lock ); error = fatfs_write_page( page ); // release mapper lock from READ_MODE rwlock_rd_unlock( &mapper->lock ); // clear dirty bit if success if( error == 0 ) page_undo_dirty( page ); } } else if( fs_type == FS_TYPE_RAMFS ) { assert( false , __FUNCTION__ , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { assert( false , __FUNCTION__ , "should not be called for DEVFS\n" ); } else { assert( false , __FUNCTION__ , "undefined file system type\n" ); } return error; } // end vfs_move_page_from_mapper()