/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////////////////// // Extern global 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 extern process_t process_zero; // 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 error_t error; #if DEBUG_VFS_INODE_CREATE || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; pid_t pid = this->process->pid; trdid_t trdid = this->trdid; #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 printk("\n[ERROR] in %s : thread[%x,%x] / illegal FS type / cycle %d\n", __FUNCTION__ , pid , trdid, cycle ); #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 printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate inum / cycle %d\n", __FUNCTION__ , pid , trdid, cycle ); #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 printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate mapper / cycle %d\n", __FUNCTION__ , pid , trdid, cycle ); #endif vfs_ctx_inum_release( XPTR( cxy , ctx ) , inum ); return -1; } mapper_ptr = GET_PTR( mapper_xp ); // allocate memory for inode descriptor inode_ptr = kmem_remote_alloc( cxy, bits_log2(sizeof(vfs_inode_t)), AF_ZERO ); if( inode_ptr == NULL ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate inode / cycle %d\n", __FUNCTION__ , pid , trdid, cycle ); #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__, pid, 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_remote_free( inode_cxy, inode_ptr, bits_log2(sizeof(vfs_inode_t)) ); } // 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 || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; #endif #if DEBUG_VFS_INODE_LOAD_ALL char name[CONFIG_VFS_MAX_NAME_LENGTH]; 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_ORDER; 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate memory for mapper / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cycle ); #endif 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 ) { 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 printk("\n[ERROR] in %s : thread[%x,%x] / undefined fs_type %d / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, fs_type, cycle ); #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 dentry_ptr = kmem_remote_alloc( cxy, bits_log2(sizeof(vfs_dentry_t)), AF_ZERO ); if( dentry_ptr == NULL ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot allocate dentry descriptor / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, cycle ); #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_remote_free( dentry_cxy, dentry_ptr, bits_log2(sizeof(vfs_dentry_t)) ); } // 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; 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 || DEBUG_VFS_ERROR thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); #endif #if DEBUG_VFS_FILE_CREATE 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 file_ptr = kmem_remote_alloc( inode_cxy, bits_log2(sizeof(vfs_file_t)), AF_ZERO ); if( file_ptr == NULL ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] / cannot allocate memory / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, cycle ); #endif 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_remote_free( file_cxy, file_ptr, bits_log2(sizeof(vfs_file_t)) ); #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 DEBUG_VFS_OPEN || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; pid_t pid = this->process->pid; trdid_t trdid = this->trdid; #endif if( mode != 0 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : the mode parameter is not supported yet\n" ); #endif return -1; } // 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 if( DEBUG_VFS_OPEN < cycle ) printk("\n[%s] thread[%x,%x] enter for <%s> / root_inode (%x,%x) / cycle %d\n", __FUNCTION__, pid, 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_zero.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 printk("\n[ERROR] in %s : thread[%x,%x] cannot get inode <%s> / cycle %d\n", __FUNCTION__ , pid, trdid , path , cycle ); #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) if( DEBUG_VFS_OPEN < cycle ) printk("\n[%s] thread[%x,%x] found inode(%x,%x) for <%s>\n", __FUNCTION__, pid, 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create file descriptor for <%s> / cycle %d\n", __FUNCTION__ , pid, trdid , path , cycle ); #endif return error; } #if (DEBUG_VFS_OPEN & 1) if( DEBUG_VFS_OPEN < cycle ) printk("\n[%s] thread[%x,%x] created file descriptor (%x,%x) for <%s>\n", __FUNCTION__, pid, 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot register file descriptor for <%s> / cycle %d\n", __FUNCTION__ , pid, trdid , path , cycle ); #endif 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__, pid, 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 printk("\n[ERROR] in %s thread[%x,%x] cannot move data / cycle %d", __FUNCTION__, this->process->pid, this->trdid, cycle ); #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 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 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" ); #if DEBUG_VFS_KERNEL_MOVE || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; #endif // get cluster and local pointer on remote file descriptor file_cxy = GET_CXY( file_xp ); file_ptr = GET_PTR( file_xp ); // 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] / cannot move data / cycle %d\n", __FUNCTION__ , this->process->pid , this->trdid , cycle ); #endif return -1; } #if DEBUG_VFS_KERNEL_MOVE char name[CONFIG_VFS_MAX_NAME_LENGTH]; 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" ); #if DEBUG_VFS_LSEEK || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; #endif // 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 { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] / undefined whence value / cycle %d", __FUNCTION__ , this->process->pid , this->trdid , cycle ); #endif 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; #if DEBUG_VFS_CLOSE || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); #endif // get file name vfs_file_get_name( file_xp , name ); #if DEBUG_VFS_CLOSE 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] / cannot synchronise dirty pages for <%s> / cycle %d\n", __FUNCTION__ , this->process->pid , this->trdid , name , cycle ); #endif return -1; } #if DEBUG_VFS_CLOSE if( DEBUG_VFS_CLOSE < cycle ) printk("\n[%s] thread[%x,%x] synchronised <%s> mapper 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] / cannot update size in parent / cycle %d\n", __FUNCTION__ , this->process->pid , this->trdid , cycle ); #endif 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] / cannot synchronise mapper & device / cycle %d\n", __FUNCTION__ , this->process->pid , this->trdid , cycle ); #endif 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]; #if DEBUG_VFS_MKDIR || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); #endif 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 ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot get parent inode for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path , cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create dentry in cluster %x for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, parent_cxy, path , cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create inode in cluster %x for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, parent_cxy, path , cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create <.> & <..> dentries for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path , cycle ); #endif vfs_remove_child_from_parent( dentry_xp ); remote_rwlock_wr_release( lock_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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot update parent directory for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path , cycle ); #endif vfs_remove_child_from_parent( dentry_xp ); return -1; } #if(DEBUG_VFS_MKDIR & 1) if( DEBUG_VFS_MKDIR < cycle ) printk("\n[%s] thread[%x,%x] created <%s> dir (Inode-Tree, Mapper and IOC)\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]; #if DEBUG_VFS_LINK || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); #endif 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 ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot get target inode for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, old_path , cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot get parent inode for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, new_path , cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create new dentry for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, new_path , cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot update parent directory for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, new_path , cycle ); #endif 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 { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] / unsupported inode type %s / cycle %d\n", __FUNCTION__, process->pid, this->trdid, vfs_inode_type_str( inode_type ), cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 #if DEBUG_VFS_UNLINK || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); #endif thread_t * this = CURRENT_THREAD; process_t * process = this->process; #if DEBUG_VFS_UNLINK 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot get parent inode for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path , cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create node <%s> in Inode_Tree / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot get dentry <%s> in parent <%s> mapper / cycle %d\n", __FUNCTION__, process->pid, this->trdid, child_name, parent_name, cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 parent 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot remove <%s> inode that has children / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif remote_rwlock_wr_release( lock_xp ); return -1; } // release space on IOC device error = vfs_fs_release_inode( inode_xp ); if( error ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot update FAT mapper to remove / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot update parent directory on IOC for / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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 { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] unsupported inode type %d for / cycle %d\n", __FUNCTION__, process->pid, this->trdid, vfs_inode_type_str( inode_type ), path, cycle ); #endif remote_rwlock_wr_release( lock_xp ); 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; #if DEBUG_VFS_STAT || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); #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 ); // 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot found inode <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif 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 if( DEBUG_VFS_STAT < cycle ) printk("\n[%s] thread[%x,%x] set stat for <%s> / %s / inum %d / size %d / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, vfs_inode_type_str( type ), inum, size, cycle ); #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 || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); #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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot found inode <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] / <%s> is not a directory / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif 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 if( DEBUG_VFS_CHDIR < cycle ) printk("\n[%s] thread[%x,%x] set new cwd <%s> / inode_xp (%x,%x) / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, inode_cxy, inode_ptr, 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 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 inode_xp; // extended pointer on target inode cxy_t inode_cxy; // inode cluster identifier vfs_inode_t * inode_ptr; // inode local pointer vfs_file_type_t inode_type; // inode type thread_t * this = CURRENT_THREAD; process_t * process = this->process; #if DEBUG_VFS_CHMOD || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); #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( cwd_xp, path, 0, &inode_xp, NULL ); // release lock protecting Inode Tree in read mode remote_rwlock_rd_release( main_lock_xp ); if( error ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot found inode <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif return -1; } // 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 fully implemented" ); // set inode rights in remote inode hal_remote_s32( XPTR( inode_cxy , &inode_ptr->rights ) , rights ); #if DEBUG_VFS_CHMOD if( DEBUG_VFS_CHMOD < cycle ) printk("\n[%s] thread[%x,%x] set access rights %x for <%s> / inode_xp (%x,%x) / cycle %d\n", __FUNCTION__, process->pid, this->trdid, rights, path, inode_cxy, inode_ptr, cycle ); #endif 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; #if DEBUG_VFS_MKFIFO || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); #endif // 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot found parent inode for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif remote_rwlock_wr_release( vfs_lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create fifo inode for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif remote_rwlock_wr_release( vfs_lock_xp ); 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create pipe for <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif vfs_remove_child_from_parent( fifo_dentry_xp ); remote_rwlock_wr_release( vfs_lock_xp ); 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 ); #if DEBUG_VFS_MKDIR if( DEBUG_VFS_MKDIR < cycle ) printk("\n[%s] thread[%x,%x] creared fifo <%s> / inode_xp [%x,%x] / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, fifo_cxy, fifo_inode_ptr, cycle ); #endif 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 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. // // 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 printk("\n[ERROR] in %s : thread[%x,%x] cannot create inode <%s> in path <%s> / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, name, pathname, cycle ); #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 printk("\n[ERROR] in %s : thread[%x,%x] cannot add dentry <%s> in parent dir / cycle %d\n", __FUNCTION__, process->pid, this->trdid, name, cycle ); #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 printk("\n[ERROR] in %s : thread[%x,%x] cannot found node <%s> in parent for <%s> / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, name, pathname, cycle ); #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 printk("\n[ERROR] in %s : thread[%x,%x] found an existing node <%s> / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, pathname, cycle ); #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 printk("\n[ERROR] in %s : thread[%x,%x] cannot load <%s> from device / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, name, cycle ); #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 printk("\n[ERROR] in %s : thread[%x,%x] found an existing node <%s> / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, pathname, cycle ); #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 || DEBUG_VFS_ERROR uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; process_t * process = this->process; #endif #if DEBUG_VFS_ADD_SPECIAL 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__, 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create dentry <.> in cluster %x / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, child_cxy, cycle ); #endif return -1; } // get <.> dentry local pointer dentry_ptr = GET_PTR( dentry_xp ); #if(DEBUG_VFS_ADD_SPECIAL & 1) if( DEBUG_VFS_ADD_SPECIAL < cycle ) printk("\n[%s] thread[%x,%x] created dentry <.> (%x,%x) / cycle %d\n", __FUNCTION__, 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot register dentry <.> in xhtab / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, cycle ); #endif 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__, 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot register dentry <.> in mapper / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, cycle ); #endif 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__, process->pid, this->trdid, cycle ); #endif } ///////////////////////////// create <..> dentry /////////////////////// error = vfs_dentry_create( child_cxy, fs_type, "..", &dentry_xp ); if( error ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot create dentry <..> in cluster %x / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, child_cxy, cycle ); #endif 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__, 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot register dentry <..> in xhtab / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, cycle ); #endif 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__, 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 ) { #if DEBUG_VFS_ERROR printk("\n[ERROR] in %s : thread[%x,%x] cannot register dentry <..> in mapper / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, cycle ); #endif 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__, 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__, 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(); #endif #if DEBUG_VFS_GET_PATH 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 , ¤t_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 printk("\n[ERROR] in %s : thread[%x,%x] cannot create dentry <%s> in cluster %x / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid , name , parent_cxy, cycle ); #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 printk("\n[ERROR] in %s : thread[%x,%x] cannot create inode in cluster %x / cycle %d\n", __FUNCTION__ , this->process->pid , this->trdid , child_cxy, cycle ); #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 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\n", __FUNCTION__ , this->process->pid , this->trdid , dentry_name ); } #if(DEBUG_VFS_REMOVE_CHILD & 1) if( DEBUG_VFS_REMOVE_CHILD < cycle ) printk("\n[%s] thread[%x,%x] removed dentry from parent inode\n", __FUNCTION__, this->process->pid, this->trdid ); #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 & 1) if( DEBUG_VFS_REMOVE_CHILD < cycle ) printk("\n[%s] thread[%x,%x] removed dentry from child inode\n", __FUNCTION__, this->process->pid, this->trdid ); #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"); assert( __FUNCTION__, (inode->type == FILE_TYPE_DIR), "inode is not a directory\n"); // 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()