/* * vfs.c - Virtual File System implementation. * * Author Mohamed Lamine Karaoui (2015) * Alain Greiner (2016,2017,2018) * * 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 variables ////////////////////////////////////////////////////////////////////////////////////////// extern vfs_ctx_t fs_context[FS_TYPES_NR]; // allocated in kernel_init.c extern chdev_directory_t chdev_dir; // allocated in kernel_init.c extern char * lock_type_str[]; // allocated in kernel_init.c /////////////////////////////////////////////////////////////////////////////////////////// // VFS Context related functions ////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// void vfs_ctx_init( vfs_fs_type_t type, uint32_t attr, uint32_t total_clusters, uint32_t cluster_size, xptr_t vfs_root_xp, void * extend ) { vfs_ctx_t * vfs_ctx = &fs_context[type]; vfs_ctx->type = type; vfs_ctx->attr = attr; vfs_ctx->total_clusters = total_clusters; vfs_ctx->cluster_size = cluster_size; vfs_ctx->vfs_root_xp = vfs_root_xp; vfs_ctx->extend = extend; busylock_init( &vfs_ctx->lock , LOCK_VFS_CTX ); bitmap_init( vfs_ctx->bitmap , BITMAP_SIZE(CONFIG_VFS_MAX_INODES) ); } //////////////////////////////////////////// error_t vfs_ctx_inum_alloc( vfs_ctx_t * ctx, uint32_t * inum ) { // get lock on inum allocator busylock_acquire( &ctx->lock ); // get lid from local inum allocator uint32_t lid = bitmap_ffc( ctx->bitmap , CONFIG_VFS_MAX_INODES ); if( lid == 0xFFFFFFFF ) // no more free slot => error { // release lock busylock_release( &ctx->lock ); // return error return 1; } else // found => return inum { // set slot allocated bitmap_set( ctx->bitmap , lid ); // release lock busylock_release( &ctx->lock ); // return inum *inum = (((uint32_t)local_cxy) << 16) | (lid & 0xFFFF); return 0; } } //////////////////////////////////////////// void vfs_ctx_inum_release( vfs_ctx_t * ctx, uint32_t inum ) { bitmap_clear( ctx->bitmap , inum & 0xFFFF ); } ////////////////////////////////////////////////////////////////////////////////////////// // VFS inode descriptor related functions ////////////////////////////////////////////////////////////////////////////////////////// const char * vfs_inode_type_str( vfs_inode_type_t type ) { switch ( type ) { case INODE_TYPE_FILE: return "FILE"; case INODE_TYPE_DIR: return "DIR "; case INODE_TYPE_FIFO: return "FIFO"; case INODE_TYPE_PIPE: return "PIPE"; case INODE_TYPE_SOCK: return "SOCK"; case INODE_TYPE_DEV: return "DEV "; case INODE_TYPE_BLK: return "BLK "; case INODE_TYPE_SYML: return "SYML"; default: return "undefined"; } } //////////////////////////////////////////////////// error_t vfs_inode_create( vfs_fs_type_t fs_type, vfs_inode_type_t inode_type, uint32_t attr, uint32_t rights, uid_t uid, gid_t gid, xptr_t * inode_xp ) { mapper_t * mapper; // associated mapper( to be allocated) vfs_inode_t * inode; // inode descriptor (to be allocated) uint32_t inum; // inode identifier (to be allocated) vfs_ctx_t * ctx; // file system context kmem_req_t req; // request to kernel memory allocator error_t error; #if DEBUG_VFS_INODE_CREATE char name[CONFIG_VFS_MAX_NAME_LENGTH]; uint32_t cycle = (uint32_t)hal_get_cycles(); cxy_t dentry_cxy = GET_CXY( dentry_xp ); vfs_dentry_t * dentry_ptr = GET_PTR( dentry_xp ); thread_t * this = CURRENT_THREAD; if( dentry_xp != XPTR_NULL ) hal_remote_strcpy( XPTR( local_cxy , name ), XPTR( dentry_cxy , dentry_ptr->name ) ); else strcpy( name , "/" ); if( DEBUG_VFS_INODE_CREATE < cycle ) printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, name, cycle ); #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 { ctx = NULL; assert( false , "illegal file system type = %d\n" , fs_type ); } // allocate inum error = vfs_ctx_inum_alloc( ctx , &inum ); if( error ) { printk("\n[ERROR] in %s : cannot allocate inum\n", __FUNCTION__ ); return ENOMEM; } // allocate memory for mapper mapper = mapper_create( fs_type ); if( mapper == NULL ) { printk("\n[ERROR] in %s : cannot allocate mapper\n", __FUNCTION__ ); vfs_ctx_inum_release( ctx , inum ); return ENOMEM; } // allocate memory for VFS inode descriptor req.type = KMEM_VFS_INODE; req.size = sizeof(vfs_inode_t); req.flags = AF_KERNEL | AF_ZERO; inode = (vfs_inode_t *)kmem_alloc( &req ); if( inode == NULL ) { printk("\n[ERROR] in %s : cannot allocate inode descriptor\n", __FUNCTION__ ); vfs_ctx_inum_release( ctx , inum ); mapper_destroy( mapper ); return -1; } // initialize inode descriptor inode->type = inode_type; inode->inum = inum; inode->attr = attr; inode->rights = rights; inode->uid = uid; inode->gid = gid; inode->ctx = ctx; inode->mapper = mapper; inode->extend = NULL; inode->links = 0; // initialise inode field in mapper mapper->inode = inode; // initialise threads waiting queue // xlist_root_init( XPTR( local_cxy , &inode->wait_root ) ); // initialize chidren dentries xhtab xhtab_init( &inode->children , XHTAB_DENTRY_TYPE ); // initialize parents dentries xlist xlist_root_init( XPTR( local_cxy , &inode->parents ) ); // initialize lock protecting size remote_rwlock_init( XPTR( local_cxy , &inode->size_lock ), LOCK_VFS_SIZE ); // initialise lock protecting inode tree traversal remote_rwlock_init( XPTR( local_cxy , &inode->main_lock ), LOCK_VFS_MAIN ); // return extended pointer on inode *inode_xp = XPTR( local_cxy , inode ); #if DEBUG_VFS_INODE_CREATE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_INODE_CREATE < cycle ) printk("\n[%s] thread[%x,%x] exit for <%s> / inode [%x,%x] / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, name, local_cxy, inode, cycle ); #endif return 0; } // end vfs_inode_create() ///////////////////////////////////////////// void vfs_inode_destroy( vfs_inode_t * inode ) { // release memory allocated for mapper mapper_destroy( inode->mapper ); // release memory allocate for inode descriptor kmem_req_t req; req.ptr = inode; req.type = KMEM_VFS_INODE; kmem_free( &req ); } // end vfs_inode_destroy() ////////////////////////////////////////////// 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 ); // get size remote_rwlock_rd_acquire( XPTR( cxy , &ptr->size_lock ) ); uint32_t size = hal_remote_l32( XPTR( cxy , &ptr->size ) ); remote_rwlock_rd_release( XPTR( cxy , &ptr->size_lock ) ); return size; } //////////////////////////////////////////// void vfs_inode_set_size( xptr_t inode_xp, uint32_t size ) { // get inode cluster and local pointer cxy_t cxy = GET_CXY( inode_xp ); vfs_inode_t * ptr = GET_PTR( inode_xp ); // set size remote_rwlock_wr_release( XPTR( cxy , &ptr->size_lock ) ); hal_remote_s32( XPTR( cxy , &ptr->size ) , size ); remote_rwlock_wr_release( XPTR( cxy , &ptr->size_lock ) ); } //////////////////////////////////////// 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_cxy = GET_CXY( inode_xp ); 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 { 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( vfs_inode_t * inode ) { assert( (inode != NULL) , "inode pointer is NULL\n" ); uint32_t page_id; xptr_t page_xp; mapper_t * mapper = inode->mapper; uint32_t size = inode->size; assert( (mapper != NULL) , "mapper pointer is NULL\n" ); #if DEBUG_VFS_INODE_LOAD_ALL 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( local_cxy , inode ) , 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, local_cxy, cycle ); #endif // compute number of pages uint32_t npages = size >> CONFIG_PPM_PAGE_SHIFT; if( (size & CONFIG_PPM_PAGE_MASK) || (size == 0) ) npages++; // loop on pages for( page_id = 0 ; page_id < npages ; page_id ++ ) { // If the mage is missing, this function allocates the missing page, // and load the page from IOC device into mapper page_xp = mapper_remote_get_page( XPTR( local_cxy , mapper ), page_id ); if( page_xp == XPTR_NULL ) return -1; } #if DEBUG_VFS_INODE_LOAD_ALL cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_INODE_LOAD_ALL < cycle ) printk("\n[%s] thread[%x,%x] exit for <%x> in cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, name, local_cxy, cycle ); #endif return 0; } // end vfs_inode_load_all_pages() //////////////////////////////////////////////////////////////////////////////////////////// // VFS dentry descriptor related functions ////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////// error_t vfs_dentry_create( vfs_fs_type_t fs_type, char * name, xptr_t * dentry_xp ) { vfs_ctx_t * ctx; // context descriptor vfs_dentry_t * dentry; // dentry descriptor (to be allocated) kmem_req_t req; // request to kernel memory allocator #if DEBUG_VFS_DENTRY_CREATE thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_DENTRY_CREATE < cycle ) printk("\n[%s] thread[%x,%x] enter for <%s> / parent_inode %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, name, parent, 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 { ctx = NULL; return -1; } // get name length uint32_t length = strlen( name ); if( length >= CONFIG_VFS_MAX_NAME_LENGTH ) return EINVAL; // allocate memory for dentry descriptor req.type = KMEM_VFS_DENTRY; req.size = sizeof(vfs_dentry_t); req.flags = AF_KERNEL | AF_ZERO; dentry = (vfs_dentry_t *)kmem_alloc( &req ); if( dentry == NULL ) { printk("\n[ERROR] in %s : cannot allocate dentry descriptor\n", __FUNCTION__ ); return -1; } // initialize dentry descriptor dentry->ctx = ctx; dentry->length = length; dentry->extend = NULL; strcpy( dentry->name , name ); // return extended pointer on dentry *dentry_xp = XPTR( local_cxy , dentry ); #if DEBUG_VFS_DENTRY_CREATE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_DENTRY_CREATE < cycle ) printk("\n[%s] thread[%x,%x] exit for <%s> / dentry [%x,%x] / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, name, local_cxy, dentry, cycle ); #endif return 0; } // end vfs_dentry_create() //////////////////////////////////////////////// void vfs_dentry_destroy( vfs_dentry_t * dentry ) { // release memory allocated to dentry kmem_req_t req; req.ptr = dentry; req.type = KMEM_VFS_DENTRY; kmem_free( &req ); } // end vfs_dentry_destroy() ////////////////////////////////////////////////////////////////////////////////////////// // VFS file descriptor related functions ////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////// error_t vfs_file_create( vfs_inode_t * inode, uint32_t attr, xptr_t * file_xp ) { vfs_file_t * file; kmem_req_t req; #if DEBUG_VFS_FILE_CREATE thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_OPEN < cycle ) printk("\n[%s] thread[%x,%x] enter for inode %x in cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, inode, local_cxy, cycle ); #endif // allocate memory for new file descriptor req.type = KMEM_VFS_FILE; req.size = sizeof(vfs_file_t); req.flags = AF_KERNEL | AF_ZERO; file = (vfs_file_t *)kmem_alloc( &req ); if( file == NULL ) return ENOMEM; // initializes new file descriptor file->gc = 0; file->type = inode->type; file->attr = attr; file->offset = 0; file->refcount = 1; file->inode = inode; file->ctx = inode->ctx; file->mapper = inode->mapper; remote_rwlock_init( XPTR( local_cxy , &file->lock ), LOCK_VFS_FILE ); *file_xp = XPTR( local_cxy , file ); #if DEBUG_VFS_FILE_CREATE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_OPEN < cycle ) printk("\n[%s] thread[%x,%x] created file %x in cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, file, local_cxy, cycle ); #endif return 0; } // end vfs_file_create() /////////////////////////////////////////// void vfs_file_destroy( vfs_file_t * file ) { // check refcount assert( (file->refcount == 0) , "refcount non zero\n" ); kmem_req_t req; req.ptr = file; req.type = KMEM_VFS_FILE; kmem_free( &req ); #if DEBUG_VFS_CLOSE thread_t * this = CURRENT_THREAD; uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_CLOSE < cycle ) printk("\n[%s] thread[%x,%x] deleted file %x in cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, file, local_cxy, cycle ); #endif } // end vfs_file_destroy() //////////////////////////////////////// void vfs_file_count_up( xptr_t file_xp ) { // get file cluster and local pointer cxy_t file_cxy = GET_CXY( file_xp ); vfs_file_t * file_ptr = GET_PTR( file_xp ); // atomically increment count hal_remote_atomic_add( XPTR( file_cxy , &file_ptr->refcount ) , 1 ); } ////////////////////////////////////////// void vfs_file_count_down( xptr_t file_xp ) { // get file cluster and local pointer cxy_t file_cxy = GET_CXY( file_xp ); vfs_file_t * file_ptr = GET_PTR( file_xp ); // atomically decrement count hal_remote_atomic_add( XPTR( file_cxy , &file_ptr->refcount ) , -1 ); } ////////////////////////////////////////////////////////////////////////////////////////// // "syscalls" API related functions ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////// error_t vfs_open( xptr_t root_xp, char * path, xptr_t process_xp, uint32_t flags, uint32_t mode, xptr_t * new_file_xp, uint32_t * new_file_id ) { error_t error; xptr_t inode_xp; // extended pointer on target inode cxy_t inode_cxy; // inode cluster identifier vfs_inode_t * inode_ptr; // inode local pointer uint32_t file_attr; // file descriptor attributes uint32_t lookup_mode; // lookup working mode xptr_t file_xp; // extended pointer on created file descriptor uint32_t file_id; // created file descriptor index in reference fd_array xptr_t vfs_root_xp; // extended pointer on VFS root inode vfs_inode_t * vfs_root_ptr; // local pointer on VFS root inode cxy_t vfs_root_cxy; // VFS root inode cluster identifier xptr_t lock_xp; // extended pointer on Inode Tree lock if( mode != 0 ) { printk("\n[ERROR] in %s : the mode parameter is not supported yet\n" ); return -1; } thread_t * this = CURRENT_THREAD; process_t * process = this->process; #if DEBUG_VFS_OPEN uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_OPEN < cycle ) printk("\n[%s] thread[%x,%x] enter for <%s> / root_inode (%x,%x) / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, GET_CXY(root_xp), GET_PTR(root_xp), cycle ); #endif // compute lookup working mode lookup_mode = VFS_LOOKUP_OPEN; if( (flags & O_DIR ) ) lookup_mode |= VFS_LOOKUP_DIR; if( (flags & O_CREAT ) ) lookup_mode |= VFS_LOOKUP_CREATE; if( (flags & O_EXCL ) ) lookup_mode |= VFS_LOOKUP_EXCL; // compute attributes for the created file file_attr = 0; if( (flags & O_RDONLY ) == 0 ) file_attr |= FD_ATTR_WRITE_ENABLE; if( (flags & O_WRONLY ) == 0 ) file_attr |= FD_ATTR_READ_ENABLE; if( (flags & O_SYNC ) ) file_attr |= FD_ATTR_SYNC; if( (flags & O_APPEND ) ) file_attr |= FD_ATTR_APPEND; if( (flags & O_CLOEXEC) ) file_attr |= FD_ATTR_CLOSE_EXEC; // build extended pointer on lock protecting Inode Tree vfs_root_xp = process->vfs_root_xp; vfs_root_ptr = GET_PTR( vfs_root_xp ); vfs_root_cxy = GET_CXY( vfs_root_xp ); lock_xp = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock ); // take lock protecting Inode Tree in read mode remote_rwlock_rd_acquire( lock_xp ); // get extended pointer on target inode error = vfs_lookup( root_xp, path, lookup_mode, &inode_xp, NULL ); // release lock protecting Inode Tree remote_rwlock_rd_release( lock_xp ); if( error ) { printk("\n[ERROR] in %s : cannot get inode <%s>\n", __FUNCTION__ , path ); return -1; } // get target inode cluster and local pointer inode_cxy = GET_CXY( inode_xp ); inode_ptr = GET_PTR( inode_xp ); #if (DEBUG_VFS_OPEN & 1) cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_OPEN < cycle ) printk("\n[%s] thread[%x,%x] found inode(%x,%x) for <%s>\n", __FUNCTION__, process->pid, this->trdid, inode_cxy, inode_ptr, path ); #endif // create a new file descriptor in cluster containing inode if( inode_cxy == local_cxy ) // target cluster is local { error = vfs_file_create( inode_ptr , file_attr , &file_xp ); } else // target cluster is remote { rpc_vfs_file_create_client( inode_cxy , inode_ptr , file_attr , &file_xp , &error ); } if( error ) return error; #if (DEBUG_VFS_OPEN & 1) cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_OPEN < cycle ) printk("\n[%s] thread[%x,%x] created file descriptor (%x,%x) for <%s>\n", __FUNCTION__, process->pid, this->trdid, GET_CXY(file_xp), GET_PTR(file_xp), path ); #endif // allocate and register a new file descriptor index in reference process error = process_fd_register( process_xp , file_xp , &file_id ); if( error ) return error; #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 / cluster %x / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, file_id, GET_CXY( file_xp ), cycle ); #endif // success *new_file_xp = file_xp; *new_file_id = file_id; return 0; } // end vfs_open() ////////////////////////////////////// int vfs_user_move( bool_t to_buffer, xptr_t file_xp, void * buffer, uint32_t size ) { cxy_t file_cxy; // remote file descriptor cluster vfs_file_t * file_ptr; // remote file descriptor local pointer vfs_inode_type_t inode_type; uint32_t file_offset; // current offset in file mapper_t * mapper; error_t error; // check argument assert( (file_xp != XPTR_NULL), "file_xp == XPTR_NULL\n" ); // get cluster and local pointer on remote file descriptor file_cxy = GET_CXY( file_xp ); file_ptr = GET_PTR( file_xp ); // get inode type from remote file descriptor inode_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) ); // check inode type assert( (inode_type == INODE_TYPE_FILE), "inode type is not INODE_TYPE_FILE" ); // get mapper pointer and file offset from file descriptor file_offset = hal_remote_l32( XPTR( file_cxy , &file_ptr->offset ) ); mapper = (mapper_t *)hal_remote_lpt( XPTR( file_cxy , &file_ptr->mapper ) ); // move data between mapper and buffer error = mapper_move_user( XPTR( file_cxy , mapper ), to_buffer, file_offset, buffer, size ); // update file offset in file descriptor hal_remote_atomic_add( XPTR( file_cxy , &file_ptr->offset ) , size ); if( error ) { return -1; } return size; } // end vfs_user_move() //////////////////////////////////////////// error_t vfs_kernel_move( bool_t to_buffer, xptr_t file_xp, xptr_t buffer_xp, uint32_t size ) { cxy_t file_cxy; // remote file descriptor cluster vfs_file_t * file_ptr; // remote file descriptor local pointer vfs_inode_type_t inode_type; // remote file type uint32_t file_offset; // current offset in file mapper_t * mapper_ptr; // remote mapper local pointer xptr_t mapper_xp; // remote mapper extended pointer error_t error; // check argument assert( (file_xp != XPTR_NULL) , "file_xp == XPTR_NULL\n" ); // get cluster and local pointer on remote file descriptor file_cxy = GET_CXY( file_xp ); file_ptr = GET_PTR( file_xp ); // get inode type from remote file descriptor inode_type = hal_remote_l32( XPTR( file_cxy , &file_ptr->type ) ); // action depends on inode type if( inode_type == INODE_TYPE_FILE ) { // 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 ) return -1; } else { printk("\n[ERROR] in %s : inode is not a file", __FUNCTION__ ); return -1; } 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 argument assert( (file_xp != XPTR_NULL) , "file_xp == XPTR_NULL\n" ); // get cluster and local pointer on remote file descriptor file_cxy = GET_CXY( file_xp ); file_ptr = GET_PTR( file_xp ); // get local pointer on remote inode inode_ptr = (vfs_inode_t *)hal_remote_lpt( XPTR( file_cxy , &file_ptr->inode ) ); // build extended pointers on lock, offset and size offset_xp = XPTR( file_cxy , &file_ptr->offset ); lock_xp = XPTR( file_cxy , &file_ptr->lock ); size_xp = XPTR( file_cxy , &inode_ptr->size ); // take file descriptor lock remote_rwlock_wr_acquire( lock_xp ); if ( whence == SEEK_CUR ) // new = current + offset { new = hal_remote_l32( offset_xp ) + offset; } else if ( whence == SEEK_SET ) // new = offset { new = offset; } else if ( whence == SEEK_END ) // new = size + offset { new = hal_remote_l32( size_xp ) + offset; } else { printk("\n[ERROR] in %s : illegal whence value\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } #if DEBUG_VFS_LSEEK uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; char name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( XPTR( file_cxy , inode_ptr ) , name ); if( cycle > DEBUG_VFS_LSEEK ) printk("\n[%s] thread[%x,%x] for <%s> / new offset %d / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, name, new, cycle ); #endif // set new offset hal_remote_s32( offset_xp , new ); // release file descriptor lock remote_rwlock_wr_release( lock_xp ); // success if ( new_offset != NULL ) *new_offset = new; return 0; } // vfs_lseek() /////////////////////////////////// error_t vfs_close( xptr_t file_xp, uint32_t file_id ) { cluster_t * cluster; // local pointer on local cluster cxy_t file_cxy; // cluster containing the file descriptor. vfs_file_t * file_ptr; // local ponter on file descriptor cxy_t owner_cxy; // process owner cluster lpid_t lpid; // process local index xptr_t root_xp; // root of list of process copies xptr_t lock_xp; // lock protecting the list of copies xptr_t iter_xp; // iterator on list of process copies xptr_t process_xp; // extended pointer on one process copy cxy_t process_cxy; // process copy cluster process_t * process_ptr; // process copy local pointer // check arguments assert( (file_xp != XPTR_NULL) , "file_xp == XPTR_NULL\n" ); assert( (file_id < CONFIG_PROCESS_FILE_MAX_NR) , "illegal file_id\n" ); thread_t * this = CURRENT_THREAD; process_t * process = this->process; #if DEBUG_VFS_CLOSE uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_CLOSE < cycle ) printk("\n[%s] thread[%x,%x] enter / fdid %d / cycle %d\n", __FUNCTION__, process->pid, this->trdid, file_id, cycle ); #endif // get local pointer on local cluster manager cluster = LOCAL_CLUSTER; // get owner process cluster and lpid owner_cxy = CXY_FROM_PID( process->pid ); lpid = LPID_FROM_PID( process->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] ); // 1) loop on the process descriptor copies to reset all fd_array[file_id] entries // take the lock protecting the list of copies remote_queuelock_acquire( lock_xp ); XLIST_FOREACH( root_xp , iter_xp ) { process_xp = XLIST_ELEMENT( iter_xp , process_t , copies_list ); process_cxy = GET_CXY( process_xp ); process_ptr = GET_PTR( process_xp ); #if (DEBUG_VFS_CLOSE & 1 ) if( DEBUG_VFS_CLOSE < cycle ) printk("\n[%s] reset fd_array[%d] for process %x in cluster %x\n", __FUNCTION__, file_id, process_ptr, process_cxy ); #endif // fd_array lock is required for atomic write of a 64 bits word // xptr_t fd_array_lock_xp = XPTR( process_cxy , &process_ptr->fd_array.lock ); xptr_t entry_xp = XPTR( process_cxy , &process_ptr->fd_array.array[file_id] ); // remote_rwlock_wr_acquire( fd_array_lock_xp ); hal_remote_s64( entry_xp , XPTR_NULL ); // remote_rwlock_wr_release( fd_array_lock_xp ); vfs_file_count_down( file_xp ); hal_fence(); } // release the lock protecting the list of copies remote_queuelock_release( lock_xp ); #if (DEBUG_VFS_CLOSE & 1) if( DEBUG_VFS_CLOSE < cycle ) printk("\n[%s] thread[%x,%x] reset all fd-array copies\n", __FUNCTION__, process->pid, this->trdid ); #endif // 2) release memory allocated to file descriptor in remote cluster // get cluster and local pointer on remote file descriptor file_cxy = GET_CXY( file_xp ); file_ptr = GET_PTR( file_xp ); if( file_cxy == local_cxy ) // file cluster is local { vfs_file_destroy( file_ptr ); } else // file cluster is local { rpc_vfs_file_destroy_client( file_cxy , file_ptr ); } #if DEBUG_VFS_CLOSE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_CLOSE < cycle ) printk("\n[%s] thread[%x,%x] exit / fdid %d closed / cycle %d\n", __FUNCTION__, process->pid, this->trdid, file_id, cycle ); #endif return 0; } // end vfs_close() //////////////////////////////////// error_t vfs_mkdir( xptr_t root_xp, char * path, uint32_t rights ) { error_t error; xptr_t vfs_root_xp; // extended pointer on VFS root inode vfs_inode_t * vfs_root_ptr; // local pointer on VFS root inode cxy_t vfs_root_cxy; // VFS root inode cluster identifier xptr_t lock_xp; // extended pointer on lock protecting Inode Tree xptr_t inode_xp; // extended pointer on new directory inode vfs_inode_t * inode_ptr; // local pointer on new directory inode cxy_t inode_cxy; // new directory inode cluster identifier xptr_t dentry_xp; // extended pointer on new dentry vfs_dentry_t * dentry_ptr; // new dentry local pointer xptr_t parent_xp; // extended pointer on parent inode vfs_inode_t * parent_ptr; // local pointer on parent inode cxy_t parent_cxy; // parent inode cluster identifier vfs_ctx_t * parent_ctx_ptr; // local pointer on parent inode context uint32_t parent_fs_type; // parent inode file system type xptr_t parents_root_xp; // extended pointer on parents field in inode (root) xptr_t parents_entry_xp; // extended pointer on parents field in dentry xptr_t children_xhtab_xp; // extended pointer on children field in inode (root) xptr_t children_entry_xp; // extended pointer on children field in dentry char last_name[CONFIG_VFS_MAX_NAME_LENGTH]; thread_t * this = CURRENT_THREAD; process_t * process = this->process; #if DEBUG_VFS_MKDIR char root_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( root_xp , root_name ); uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_MKDIR < cycle ) printk("\n[%s] thread[%x,%x] enter / root <%s> / path <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, root_name, path, cycle ); #endif // build extended pointer on lock protecting Inode Tree (in VFS root inode) vfs_root_xp = process->vfs_root_xp; vfs_root_ptr = GET_PTR( vfs_root_xp ); vfs_root_cxy = GET_CXY( vfs_root_xp ); lock_xp = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock ); // take the lock protecting Inode Tree in write mode remote_rwlock_wr_acquire( lock_xp ); // 1. get pointers on parent inode error = vfs_lookup( root_xp, path, VFS_LOOKUP_DIR | VFS_LOOKUP_PARENT, &parent_xp, last_name ); if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot get parent inode for <%s>\n", __FUNCTION__, path ); return -1; } // get parent inode cluster and local pointer parent_cxy = GET_CXY( parent_xp ); parent_ptr = GET_PTR( parent_xp ); #if( DEBUG_VFS_MKDIR & 1 ) if( DEBUG_VFS_MKDIR < cycle ) printk("\n[%s] thread[%x,%x] get parent inode (%x,%x) for <%s>\n", __FUNCTION__, process->pid, this->trdid, parent_cxy, parent_ptr, path ); #endif // get parent inode context, and FS type parent_ctx_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_ptr->ctx ) ); parent_fs_type = hal_remote_l32( XPTR( parent_cxy , &parent_ctx_ptr->type ) ); // 2. create one new dentry in parent cluster if( parent_cxy == local_cxy ) { error = vfs_dentry_create( parent_fs_type, last_name, &dentry_xp ); } else { rpc_vfs_dentry_create_client( parent_cxy, parent_fs_type, last_name, &dentry_xp, &error ); } if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot create new dentry in cluster %x for <%s>\n", __FUNCTION__, parent_cxy, path ); return -1; } // get local pointer on dentry dentry_ptr = GET_PTR( dentry_xp ); #if( DEBUG_VFS_MKDIR & 1 ) if( DEBUG_VFS_MKDIR < cycle ) printk("\n[%s] thread[%x,%x] created new dentry (%x,%x) for <%s>\n", __FUNCTION__, process->pid, this->trdid, parent_cxy, dentry_ptr, path ); #endif // 3. create new directory inode // TODO : define attr / uid / gid uint32_t attr = 0; uint32_t uid = 0; uint32_t gid = 0; // select a target cluster for new inode inode_cxy = cluster_random_select(); if( inode_cxy == local_cxy ) // target cluster is local { error = vfs_inode_create( parent_fs_type, INODE_TYPE_DIR, attr, rights, uid, gid, &inode_xp ); } else // target cluster is remote { rpc_vfs_inode_create_client( inode_cxy, parent_fs_type, INODE_TYPE_DIR, attr, rights, uid, gid, &inode_xp, &error ); } if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot create new inode in cluster %x for <%s>\n", __FUNCTION__ , inode_cxy , path ); if( parent_cxy == local_cxy ) vfs_dentry_destroy( dentry_ptr ); else rpc_vfs_dentry_destroy_client( parent_cxy , dentry_ptr ); return -1; } // get new inode local pointer inode_ptr = GET_PTR( inode_xp ); #if(DEBUG_VFS_MKDIR & 1) if( DEBUG_VFS_MKDIR < cycle ) printk("\n[%s] thread[%x,%x] created new inode (%x,%x) for <%s>\n", __FUNCTION__ , process->pid, this->trdid, inode_cxy, inode_ptr, path ); #endif // 4. register dentry in new inode list of parents parents_root_xp = XPTR( inode_cxy , &inode_ptr->parents ); parents_entry_xp = XPTR( parent_cxy , &dentry_ptr->parents ); xlist_add_first( parents_root_xp , parents_entry_xp ); hal_remote_atomic_add( XPTR( inode_cxy , &inode_ptr->links ) , 1 ); // 5. register dentry in parent inode children_xhtab_xp = XPTR( parent_cxy , &parent_ptr->children ); children_entry_xp = XPTR( parent_cxy , &dentry_ptr->children ); xhtab_insert( children_xhtab_xp , last_name , children_entry_xp ); // 6. update "parent" and "child_xp" fields in dentry hal_remote_s64( XPTR( parent_cxy , &dentry_ptr->child_xp ) , inode_xp ); hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->parent ) , parent_ptr ); #if(DEBUG_VFS_MKDIR & 1) if( DEBUG_VFS_MKDIR < cycle ) printk("\n[%s] thread[%x,%x] updated Inode Tree for <%s>\n", __FUNCTION__, process->pid, this->trdid, path ); #endif // 7. create the two special dentries <.> and <..> in new directory // both the new directory mapper, and the Inode Tree are updated error = vfs_add_special_dentries( inode_xp, parent_xp ); if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot create new inode in cluster %x for <%s>\n", __FUNCTION__ , inode_cxy , path ); if( parent_cxy == local_cxy ) vfs_dentry_destroy( dentry_ptr ); else rpc_vfs_dentry_destroy_client( parent_cxy , dentry_ptr ); 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 if (parent_cxy == local_cxy) { error = vfs_fs_add_dentry( parent_ptr, dentry_ptr ); } else { rpc_vfs_fs_add_dentry_client( parent_cxy, parent_ptr, dentry_ptr, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot update parent directory for <%s>\n", __FUNCTION__, path ); return -1; } #if(DEBUG_VFS_MKDIR & 1) if( DEBUG_VFS_MKDIR < cycle ) printk("\n[%s] thread[%x,%x] updated parent dir (mapper and IOC) for <%s>\n", __FUNCTION__, process->pid, this->trdid, path ); #endif return 0; } // end vfs_mkdir() /////////////////////////////////////// error_t vfs_link( xptr_t old_root_xp, char * old_path, xptr_t new_root_xp, char * new_path ) { error_t error; xptr_t vfs_root_xp; // extended pointer on VFS root inode vfs_inode_t * vfs_root_ptr; // local pointer on VFS root inode cxy_t vfs_root_cxy; // VFS root inode cluster identifier xptr_t lock_xp; // extended pointer on lock protecting Inode Tree xptr_t inode_xp; // extended pointer on target inode vfs_inode_t * inode_ptr; // local pointer on target inode cxy_t inode_cxy; // target inode cluster identifier uint32_t inode_type; // target inode type vfs_ctx_t * inode_ctx_ptr; // local pointer on target inode context uint32_t inode_fs_type; // target inode file system type xptr_t dentry_xp; // extended pointer on new dentry vfs_dentry_t * dentry_ptr; // target dentry local pointer xptr_t new_parent_xp; // extended pointer on new parent inode vfs_inode_t * new_parent_ptr; // local pointer on new parent inode cxy_t new_parent_cxy; // new parent inode cluster identifier xptr_t parents_root_xp; // extended pointer on parents field in inode (root) xptr_t parents_entry_xp; // extended pointer on parents field in dentry xptr_t children_xhtab_xp; // extended pointer on children field in inode (root) xptr_t children_entry_xp; // extended pointer on children field in dentry char new_name[CONFIG_VFS_MAX_NAME_LENGTH]; thread_t * this = CURRENT_THREAD; process_t * process = this->process; #if DEBUG_VFS_LINK char old_root_name[CONFIG_VFS_MAX_NAME_LENGTH]; char new_root_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( old_root_xp , old_root_name ); vfs_inode_get_name( new_root_xp , new_root_name ); uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_LINK < cycle ) printk("\n[%s] thread[%x,%x] enter / old_root <%s> / old_path <%s> / " "new_root <%s> / new_path <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, old_root_name, old_path, new_root_name, new_path, cycle ); #endif // build extended pointer on lock protecting Inode Tree (in VFS root inode) vfs_root_xp = process->vfs_root_xp; vfs_root_ptr = GET_PTR( vfs_root_xp ); vfs_root_cxy = GET_CXY( vfs_root_xp ); lock_xp = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock ); // take the lock protecting Inode Tree in write mode remote_rwlock_wr_acquire( lock_xp ); // get extended pointer on target inode error = vfs_lookup( old_root_xp, old_path, 0, &inode_xp, NULL ); if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot get target inode for <%s>\n", __FUNCTION__, old_path ); return -1; } #if( DEBUG_VFS_LINK & 1 ) if( DEBUG_VFS_LINK < cycle ) printk("\n[%s] thread[%x,%x] get child inode (%x,%x) for <%s>\n", __FUNCTION__, process->pid, this->trdid, GET_CXY(inode_xp), GET_PTR(inode_xp), old_path, cycle ); #endif // get extended pointer on parent inode in new path error = vfs_lookup( new_root_xp, new_path, VFS_LOOKUP_PARENT, &new_parent_xp, new_name ); if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot get parent inode for <%s>\n", __FUNCTION__, new_path ); return -1; } #if( DEBUG_VFS_LINK & 1 ) if( DEBUG_VFS_LINK < cycle ) printk("\n[%s] thread[%x,%x] get parent inode (%x,%x) for <%s>\n", __FUNCTION__, process->pid, this->trdid, GET_CXY(new_parent_xp), GET_PTR(new_parent_xp), new_path ); #endif // get target inode cluster and local pointer inode_cxy = GET_CXY( inode_xp ); inode_ptr = GET_PTR( inode_xp ); // get target inode type, context, and FS type inode_type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) ); inode_ctx_ptr = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->ctx ) ); inode_fs_type = hal_remote_l32( XPTR( inode_cxy , &inode_ctx_ptr->type ) ); // get new parent inode cluster an local pointer new_parent_ptr = GET_PTR( new_parent_xp ); new_parent_cxy = GET_CXY( new_parent_xp ); /////////////////////////////////////////////////////////////////////// if( (inode_type == INODE_TYPE_FILE) || (inode_type == INODE_TYPE_DIR) ) { // 1. create one new dentry if( new_parent_cxy == local_cxy ) { error = vfs_dentry_create( inode_fs_type, new_name, &dentry_xp ); } else { rpc_vfs_dentry_create_client( new_parent_cxy, inode_fs_type, new_name, &dentry_xp, &error ); } if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot create new dentry for <%s>\n", __FUNCTION__, new_path ); return -1; } // get local pointer on dentry dentry_ptr = GET_PTR( dentry_xp ); // 2. register dentry in target inode parents_root_xp = XPTR( inode_cxy , &inode_ptr->parents ); parents_entry_xp = XPTR( new_parent_cxy , &dentry_ptr->parents ); xlist_add_first( parents_root_xp , parents_entry_xp ); hal_remote_atomic_add( XPTR( inode_cxy , &inode_ptr->links ) , 1 ); // 3. register dentry in parent inode children_xhtab_xp = XPTR( new_parent_cxy , &new_parent_ptr->children ); children_entry_xp = XPTR( new_parent_cxy , &dentry_ptr->children ); xhtab_insert( children_xhtab_xp , new_name , children_entry_xp ); // 4. update "parent" and "child_xp" fields in dentry hal_remote_s64( XPTR( new_parent_cxy , &dentry_ptr->child_xp ) , inode_xp ); hal_remote_spt( XPTR( new_parent_cxy , &dentry_ptr->parent ) , new_parent_ptr ); #if(DEBUG_VFS_LINK & 1) if( DEBUG_VFS_LINK < cycle ) printk("\n[%s] thread[%x,%x] updated Inode Tree / old <%s> / new <%s>\n", __FUNCTION__, process->pid, this->trdid, old_path, new_path ); vfs_display( new_parent_xp ); #endif // release the lock protecting Inode Tree remote_rwlock_wr_release( lock_xp ); // 5. update new parent directory mapper in Inode Tree // and synchronize the parent directory on IOC device if (new_parent_cxy == local_cxy) { error = vfs_fs_add_dentry( new_parent_ptr, dentry_ptr ); } else { rpc_vfs_fs_add_dentry_client( new_parent_cxy, new_parent_ptr, dentry_ptr, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot update new parent directory for <%s>\n", __FUNCTION__, new_path ); return -1; } #if(DEBUG_VFS_LINK & 1) if( DEBUG_VFS_LINK < cycle ) printk("\n[%s] thread[%x,%x] updated new parent dir (mapper and IOC) / old <%s> / new <%s>\n", __FUNCTION__, process->pid, this->trdid, old_path, new_path ); #endif return 0; } else { // release the lock protecting Inode Tree remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : unsupported inode type %s\n", __FUNCTION__ , vfs_inode_type_str( inode_type ) ); return -1; } } // end vfs_link() ///////////////////////////////////// error_t vfs_unlink( xptr_t root_xp, char * path ) { error_t error; xptr_t vfs_root_xp; // extended pointer on VFS root inode vfs_inode_t * vfs_root_ptr; // local_pointer on VFS root inode cxy_t vfs_root_cxy; // VFS root inode cluster identifier xptr_t lock_xp; // extended pointer on lock protecting Inode Tree xptr_t parent_xp; // extended pointer on target inode cxy_t parent_cxy; // target inode cluster identifier vfs_inode_t * parent_ptr; // target inode local pointer xptr_t inode_xp; // extended pointer on target inode cxy_t inode_cxy; // target inode cluster identifier vfs_inode_t * inode_ptr; // target inode local pointer uint32_t inode_links; // target inode links count vfs_inode_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 char name[CONFIG_VFS_MAX_NAME_LENGTH]; // name of link to remove thread_t * this = CURRENT_THREAD; process_t * process = this->process; #if DEBUG_VFS_UNLINK uint32_t cycle = (uint32_t)hal_get_cycles(); char root_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( root_xp , root_name ); if( DEBUG_VFS_UNLINK < cycle ) printk("\n[%s] thread[%x,%x] enter / 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( root_xp ); vfs_root_cxy = GET_CXY( 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, name ); if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot get parent inode for <%s> in <%s>\n", __FUNCTION__, name, path ); return -1; } // get parent inode cluster and local pointer parent_cxy = GET_CXY( parent_xp ); parent_ptr = GET_PTR( parent_xp ); #if( DEBUG_VFS_UNLINK & 1 ) char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( parent_xp , parent_name ); 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 ); // get extended pointer on dentry to unlink dentry_xp = xhtab_lookup( children_xp , name ); if( dentry_xp == XPTR_NULL ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot get target dentry <%s> in <%s>\n", __FUNCTION__, name, path ); return -1; } // get local pointer on dentry to unlink dentry_ptr = GET_PTR( dentry_xp ); #if( DEBUG_VFS_UNLINK & 1 ) if( DEBUG_VFS_UNLINK < cycle ) printk("\n[%s] thread[%x,%x] dentry <%s> to unlink is (%x,%x)\n", __FUNCTION__, process->pid, this->trdid, name, parent_cxy, dentry_ptr ); #endif // get pointer on target inode inode_xp = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) ); inode_cxy = GET_CXY( inode_xp ); inode_ptr = GET_PTR( inode_xp ); #if( DEBUG_VFS_UNLINK & 1 ) char inode_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( inode_xp , inode_name ); if( DEBUG_VFS_UNLINK < cycle ) printk("\n[%s] thread[%x,%x] target inode <%s> is (%x,%x) / cycle %d\n", __FUNCTION__, process->pid, this->trdid, inode_name, inode_cxy, inode_ptr, cycle ); #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 ) ); // check target inode links counter assert( (inode_links >= 1), "illegal inode links count %d for <%s>\n", inode_links, path ); /////////////////////////////////////////////////////////////////////// if( (inode_type == INODE_TYPE_FILE) || (inode_type == INODE_TYPE_DIR) ) { // 1. Release clusters allocated to target inode // and synchronize the FAT on IOC device if last link. if( inode_links == 1 ) { // build extended pointer on target inode "children" number xptr_t inode_children_xp = XPTR( inode_cxy , &inode_ptr->children.items ); // get target inode number of children inode_children = hal_remote_l32( inode_children_xp ); // check no children if( inode_children != 0 ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot remove <%s> inode that has children\n", __FUNCTION__, path ); return -1; } // release clusters on IOC device error = vfs_fs_release_inode( inode_xp ); if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot update FAT mapper to remove <%s> inode\n", __FUNCTION__ , path ); return -1; } #if(DEBUG_VFS_UNLINK & 1) if( DEBUG_VFS_UNLINK < cycle ) printk("\n[%s] thread[%x,%x] removed <%s> inode from FAT (mapper and IOC device)\n", __FUNCTION__, process->pid, this->trdid, path ); #endif } // 2. update parent directory mapper // and synchronize the parent directory on IOC device if (parent_cxy == local_cxy) { error = vfs_fs_remove_dentry( parent_ptr, dentry_ptr ); } else { rpc_vfs_fs_remove_dentry_client( parent_cxy, parent_ptr, dentry_ptr, &error ); } if( error ) { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : cannot update dentry on device for <%s>\n", __FUNCTION__ , path ); return -1; } #if(DEBUG_VFS_UNLINK & 1) if( DEBUG_VFS_UNLINK < cycle ) printk("\n[%s] thread[%x,%x] removed <%s> inode from parent dir (mapper and IOC device)\n", __FUNCTION__, process->pid, this->trdid, path ); #endif // 3. remove dentry from Inode Tree (and associated chils inode when last link) vfs_remove_child_from_parent( dentry_xp ); // release the lock protecting Inode Tree remote_rwlock_wr_release( lock_xp ); #if DEBUG_VFS_UNLINK if( DEBUG_VFS_UNLINK < cycle ) printk("\n[%s] thread[%x,%x] exit / removed <%s> inode from Inode Tree / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif return 0; } else { remote_rwlock_wr_release( lock_xp ); printk("\n[ERROR] in %s : unsupported inode type %s\n", __FUNCTION__ , vfs_inode_type_str( inode_type ) ); return -1; } } // end vfs_unlink() //////////////////////////////////////////////// error_t vfs_stat( xptr_t root_inode_xp, char * path, struct stat * st ) { error_t error; xptr_t inode_xp; // extended pointer on target inode vfs_inode_t * inode_ptr; // local pointer on target inode cxy_t inode_cxy; // target inode cluster identifier xptr_t vfs_root_xp; // extended pointer on VFS root inode vfs_inode_t * vfs_root_ptr; // local_pointer on VFS root inode cxy_t vfs_root_cxy; // VFS root inode cluster identifier xptr_t lock_xp; // extended pointer on lock protecting Inode Tree thread_t * this = CURRENT_THREAD; process_t * process = this->process; // build extended pointer on lock protecting Inode Tree (in VFS root inode) vfs_root_xp = process->vfs_root_xp; vfs_root_ptr = GET_PTR( vfs_root_xp ); vfs_root_cxy = GET_CXY( vfs_root_xp ); lock_xp = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock ); // get the lock protecting Inode Tree in read mode remote_rwlock_rd_acquire( lock_xp ); // get extended pointer on target inode error = vfs_lookup( root_inode_xp, path, 0, &inode_xp, NULL ); // release the lock protecting Inode Tree remote_rwlock_rd_release( lock_xp ); if( error ) { printk("\n[ERROR] in %s : cannot found inode <%s>\n", __FUNCTION__ , path ); return -1; } // get cluster and local pointer on inode descriptor inode_ptr = GET_PTR( inode_xp ); inode_cxy = GET_CXY( inode_xp ); // get relevant infos from inode descriptor uint32_t inum = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->inum ) ); uint32_t size = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size ) ); uint32_t uid = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->uid ) ); uint32_t gid = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->gid ) ); uint32_t type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) ); uint32_t rights = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->rights ) ); // set stat structure fields st->st_ino = inum; st->st_gid = gid; st->st_uid = uid; st->st_size = size; st->st_mode = (type << 16) | rights; #if DEBUG_VFS_STAT uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_STAT < cycle ) printk("\n[%s] thread[%x,%x] set stat %x for inode %x in cluster %x / cycle %d\n" " %s / inum %d / size %d\n", __FUNCTION__, process->pid, this->trdid, st, inode_ptr, inode_cxy, cycle, vfs_inode_type_str( type ), inum, size ); #endif return 0; } // end vfs_stat() ///////////////////////////////////////////// error_t vfs_readdir( xptr_t file_xp, struct dirent * k_dirent ) { assert( false , "not implemented file_xp: %x, k_dirent ptr %x\n", file_xp, k_dirent ); return 0; } //////////////////////////////////// error_t vfs_rmdir( xptr_t file_xp, char * path ) { assert( false , "not implemented file_xp: %x, path <%s>\n", file_xp, path ); return 0; } //////////////////////////////////// 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_inode_type_t inode_type; // target inode type xptr_t vfs_root_xp; // extended pointer on VFS root inode vfs_inode_t * vfs_root_ptr; // local_pointer on VFS root inode cxy_t vfs_root_cxy; // VFS root inode cluster identifier xptr_t main_lock_xp; // extended pointer on lock protecting Inode Tree xptr_t ref_xp; // extended pointer on reference process process_t * ref_ptr; // local pointer on reference process cxy_t ref_cxy; // reference process cluster xptr_t cwd_lock_xp; // extended pointer on lock protecting CWD change xptr_t cwd_xp_xp; // extended pointer on cwd_xp in reference process thread_t * this = CURRENT_THREAD; process_t * process = this->process; #if DEBUG_VFS_CHDIR uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_CHDIR < cycle ) printk("\n[%s] thread[%x,%x] enter for path <%s> / cycle %d\n", __FUNCTION__, process->pid, this->trdid, path, cycle ); #endif // build extended pointer on lock protecting Inode Tree (in VFS root inode) vfs_root_xp = process->vfs_root_xp; vfs_root_ptr = GET_PTR( vfs_root_xp ); vfs_root_cxy = GET_CXY( vfs_root_xp ); main_lock_xp = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock ); // take lock protecting Inode Tree in read mode remote_rwlock_rd_acquire( main_lock_xp ); // get extended pointer on target inode error = vfs_lookup( root_xp, path, VFS_LOOKUP_DIR, &inode_xp, NULL ); // release lock protecting Inode Tree in read mode remote_rwlock_rd_release( main_lock_xp ); if( error ) { printk("\n[ERROR] in %s : <%s> not found\n", __FUNCTION__, path ); return -1; } // get inode type from remote file inode_cxy = GET_CXY( inode_xp ); inode_ptr = GET_PTR( inode_xp ); inode_type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) ); if( inode_type != INODE_TYPE_DIR ) { printk("\n[ERROR] in %s : <%s> is not a directory\n", __FUNCTION__, path ); return -1; } // build extended pointer on cwd_lock and cwd_xp ref_xp = process->ref_xp; ref_ptr = GET_PTR( ref_xp ); ref_cxy = GET_CXY( ref_xp ); cwd_lock_xp = XPTR( ref_cxy , &ref_ptr->cwd_lock ); cwd_xp_xp = XPTR( ref_cxy , &ref_ptr->cwd_xp ); // take lock protecting CWD changes remote_busylock_acquire( cwd_lock_xp ); // update cwd_xp field in reference process descriptor hal_remote_s64( cwd_xp_xp , inode_xp ); // release lock protecting CWD changes remote_busylock_release( cwd_lock_xp ); #if DEBUG_VFS_CHDIR cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_CHDIR < cycle ) printk("\n[%s] thread[%x,%x] exit : inode (%x,%x) / &cwd_xp (%x,%x) / cycle %d\n", __FUNCTION__, process->pid, this->trdid, inode_cxy, inode_ptr, GET_CXY(cwd_xp_xp), GET_PTR(cwd_xp_xp), cycle ); #endif return 0; } // end vfs_chdir() /////////////////////////////////// error_t vfs_chmod( xptr_t cwd_xp, char * path, uint32_t rights ) { error_t error; xptr_t inode_xp; // extended pointer on target inode cxy_t inode_cxy; // inode cluster identifier vfs_inode_t * inode_ptr; // inode local pointer vfs_inode_type_t inode_type; // target inode type // set lookup working mode assert( (rights == 0), __FUNCTION__, "access rights non implemented yet\n" ); // get extended pointer on target inode error = vfs_lookup( cwd_xp, path, 0, &inode_xp, NULL ); if( error ) return error; // get inode cluster and local pointer inode_cxy = GET_CXY( inode_xp ); inode_ptr = GET_PTR( inode_xp ); // get inode type from remote inode inode_type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) ); // TODO implement this function assert( false , "not implemented\n" ); return 0; } /////////////////////////////////// error_t vfs_mkfifo( xptr_t cwd_xp, char * path, uint32_t rights ) { assert( false , "not implemented cwd_xp: %x, path <%s>, rights %x\n", cwd_xp, path, rights ); return 0; } ////////////////////////////////////////////////////////////////////////////////////////// // Distributed Inode Tree access related functions ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // This static 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_inode_type_t inode_type; uint32_t inode_size; uint32_t inode_inum; uint32_t inode_attr; uint32_t inode_dirty; 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( (inode_xp != XPTR_NULL) , "inode_xp cannot be NULL\n" ); assert( (name_xp != XPTR_NULL) , "name_xp cannot be NULL\n" ); assert( (indent < 16) , "depth cannot be larger than 15\n" ); // 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_inum = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->inum ) ); inode_attr = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->attr ) ); 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); // display inode nolock_printk("%s%s <%s> : inum %d / %d bytes / dirty %d / cxy %x (inode %x / mapper %x)\n", indent_str[indent], vfs_inode_type_str( inode_type ), name, inode_inum, inode_size, inode_dirty, 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 == INODE_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***** file system 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 ) ); // FIXME : me must use mode if( (uid == client_uid) || (gid == client_gid) ) return false; else return true; } */ ////////////////////////////////////////////////////////////////////////////////////////// // This static function is used by the vfs_lookup() function. // It takes an extended pointer on a remote parent directory inode, a directory // entry name, and returns an extended pointer on the child inode. // It can be used by any thread running in any cluster. ////////////////////////////////////////////////////////////////////////////////////////// // @ parent_xp : extended pointer on parent inode in remote cluster. // @ name : dentry name // @ child_xp : [out] buffer for extended pointer on child inode. // @ return true if success / return false if not found. ////////////////////////////////////////////////////////////////////////////////////////// static bool_t vfs_get_child( xptr_t parent_xp, char * name, xptr_t * child_xp ) { xptr_t xhtab_xp; // extended pointer on hash table 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 xptr_t child_xp; // extended pointer on child inode cxy_t child_cxy; // cluster for child inode vfs_inode_t * child_ptr; // local pointer on child inode vfs_inode_type_t child_type; // child inode type vfs_fs_type_t fs_type; // File system type vfs_ctx_t * ctx_ptr; // local pointer on FS context char * current; // current pointer on path char * next; // next value for current pointer bool_t last; // true when the name is the last in path bool_t found; // true when a child has been found bool_t dir; // searched inode is a directory bool_t create; // searched inode must be created if not found bool_t excl; // searched inode must not exist bool_t par; // 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( ((pathname[0] != '/') || (root_xp == process->vfs_root_xp)), "root inode must be VFS root for path <%s>\n", pathname ); #if DEBUG_VFS_LOOKUP uint32_t cycle = (uint32_t)hal_get_cycles(); char root_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( root_xp , root_name ); if( DEBUG_VFS_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 // compute lookup flags dir = (lookup_mode & VFS_LOOKUP_DIR) == VFS_LOOKUP_DIR; create = (lookup_mode & VFS_LOOKUP_CREATE) == VFS_LOOKUP_CREATE; excl = (lookup_mode & VFS_LOOKUP_EXCL) == VFS_LOOKUP_EXCL; par = (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; // 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] look for <%s> in <%s> / last = %d\n", __FUNCTION__, process->pid, this->trdid, name, pathname, last ); #endif // search the child dentry matching name in parent inode found = vfs_get_child( parent_xp, name, &child_xp ); // get child inode local pointer and cluster child_ptr = GET_PTR( child_xp ); child_cxy = GET_CXY( child_xp ); // analyse found & last, depending on lookup_mode if( found == false ) // not found in Inode Tree { // when a inode is not found in the Inode Tree: // - if (last and par) 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 && par ) // 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(); // define child inode type if( dir ) child_type = INODE_TYPE_DIR; else child_type = INODE_TYPE_FILE; // insert a new child dentry/inode couple in inode tree error = vfs_add_child_in_parent( child_cxy, child_type, fs_type, parent_xp, name, &dentry_xp, &child_xp ); if( error ) { printk("\n[ERROR] in %s : cannot create inode <%s> in path <%s>\n", __FUNCTION__ , name, pathname ); return -1; } // get child inode local pointer child_ptr = GET_PTR( child_xp ); #if (DEBUG_VFS_LOOKUP & 1) if( DEBUG_VFS_LOOKUP < cycle ) printk("\n[%s] thread[%x,%x] created missing 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 if( parent_cxy == local_cxy ) { error = vfs_fs_get_dentry( parent_ptr, name, child_xp ); } else { rpc_vfs_fs_get_dentry_client( parent_cxy, parent_ptr, name, child_xp, &error ); } if ( error ) // child not found in parent mapper { if ( last && create ) // add a brand new dentry in parent { error = vfs_new_dentry_init( parent_xp, dentry_xp, child_xp ); if ( error ) { printk("\n[ERROR] in %s : cannot init inode <%s> in path <%s>\n", __FUNCTION__, name, pathname ); 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 => create it\n", __FUNCTION__, process->pid, this->trdid, name ); #endif } else // not last or not create => error { printk("\n[ERROR] in %s : <%s> node not found in parent for <%s>\n", __FUNCTION__ , name , pathname ); vfs_remove_child_from_parent( dentry_xp ); return -1; } } else // child has been found in parent mapper { // check the excl if( last && create && excl ) { printk("\n[ERROR] in %s : node already exist <%s>\n", __FUNCTION__, name ); 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 == INODE_TYPE_DIR ) { if( child_cxy == local_cxy ) { error = vfs_inode_load_all_pages( child_ptr ); } else { rpc_vfs_inode_load_all_pages_client( child_cxy, child_ptr, &error ); } if ( error ) { printk("\n[ERROR] in %s : cannot load <%s> from device\n", __FUNCTION__ , name ); vfs_remove_child_from_parent( 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 directly found in inode tree { #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 ) { printk("\n[ERROR] in %s : node <%s> already exist\n", __FUNCTION__, name ); return -1; } } // TODO check access rights here [AG] // error = vfs_access_denied( child_xp, // client_uid, // client_gid ); // if( error ) // { // printk("\n[ERROR] in %s : thread %x / permission denied for %s\n", // __FUNCTION__ , this , name ); // return EACCES; // } // take lock on child inode and release lock on parent // vfs_inode_lock( child_xp ); // vfs_inode_unlock( parent_xp ); // exit when last if ( last ) // last inode in path => return relevant info { if ( par ) // 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 inode in path => update loop variables { parent_xp = child_xp; current = next; } } return 0; } // end vfs_lookup() //////////////////////////////////////////////// error_t vfs_new_dentry_init( xptr_t parent_xp, xptr_t dentry_xp, xptr_t child_xp ) { error_t error; uint32_t cluster; uint32_t child_type; uint32_t child_size; #if DEBUG_VFS_NEW_CHILD_INIT char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; char child_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( parent_xp , parent_name ); vfs_inode_get_name( child_xp , child_name ); uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_VFS_NEW_CHILD_INIT < cycle ) printk("\n[%s] thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, parent_name, child_name, cycle ); #endif // 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 dentry local pointer vfs_dentry_t * dentry_ptr = GET_PTR( dentry_xp ); // get child inode cluster and local pointer cxy_t child_cxy = GET_CXY( child_xp ); vfs_inode_t * child_ptr = GET_PTR( child_xp ); // 1. allocate one free cluster to child inode // depending on the child inode FS type vfs_ctx_t * ctx = hal_remote_lpt( XPTR( child_cxy , &child_ptr->ctx ) ); error = vfs_fs_cluster_alloc( ctx->type, &cluster ); if ( error ) { printk("\n[ERROR] in %s : cannot find a free VFS cluster\n", __FUNCTION__ ); return -1; } #if( DEBUG_VFS_NEW_CHILD_INIT & 1) if( DEBUG_VFS_NEW_CHILD_INIT < cycle ) printk("\n[%s] thread[%x,%x] allocated one FAT cluster to <%s>\n", __FUNCTION__ , this->process->pid, this->trdid, child_name ); #endif // 2. update the child inode descriptor child_type = hal_remote_l32( XPTR( child_cxy , &child_ptr->type ) ); child_size = (child_type == INODE_TYPE_DIR) ? 4096 : 0; hal_remote_s32( XPTR( child_cxy , &child_ptr->size ) , child_size ); hal_remote_spt( XPTR( child_cxy , &child_ptr->extend ) , (void*)(intptr_t)cluster ); // 3. update the parent inode mapper, and // update the dentry extension if required if( local_cxy == parent_cxy ) { error = vfs_fs_add_dentry( parent_ptr, dentry_ptr ); } else { rpc_vfs_fs_add_dentry_client( parent_cxy, parent_ptr, dentry_ptr, &error ); } if ( error ) { printk("\n[ERROR] in %s : cannot register child in parent directory\n", __FUNCTION__ ); return -1; } #if DEBUG_VFS_NEW_CHILD_INIT cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_NEW_CHILD_INIT < cycle ) printk("\n[%s] thread[%x,%x] exit / parent <%s> / child <%s> / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, parent_name, child_name, cycle ); #endif return 0; } // end vfs_new_dentry_init() /////////////////////////////////////////////////// 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_inode_t * parent_ptr; // local pointer on parent inode directory cxy_t parent_cxy; // parent 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 parents_root_xp; // extended pointer on inode "parents" field // xptr_t parents_entry_xp; // extended pointer on dentry "parents" field xptr_t children_xhtab_xp; // extended pointer on inode "children" field xptr_t children_entry_xp; // extended pointer on dentry "children" field #if DEBUG_VFS_ADD_SPECIAL uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; char child_name[CONFIG_VFS_MAX_NAME_LENGTH]; char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( child_xp , child_name ); vfs_inode_get_name( parent_xp , parent_name ); if( DEBUG_VFS_ADD_SPECIAL < cycle ) printk("\n[%s] thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle ); #endif // get new directory cluster and local pointer child_cxy = GET_CXY( child_xp ); child_ptr = GET_PTR( child_xp ); // get parent directory cluster and local pointer parent_cxy = GET_CXY( parent_xp ); parent_ptr = GET_PTR( parent_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 <.> if( child_cxy == local_cxy ) { error = vfs_dentry_create( fs_type, ".", &dentry_xp ); } else { rpc_vfs_dentry_create_client( child_cxy, fs_type, ".", &dentry_xp, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot create dentry <.> in cluster %x\n", __FUNCTION__ , child_cxy ); return -1; } // get <.> dentry local pointer dentry_ptr = GET_PTR( dentry_xp ); #if(DEBUG_VFS_ADD_SPECIAL & 1) if( DEBUG_VFS_ADD_SPECIAL < cycle ) printk("\n[%s] thread[%x,%x] created dentry <.> (%x,%x)\n", __FUNCTION__, this->process->pid, this->trdid, child_cxy, dentry_ptr ); #endif // register <.> dentry in child inode xhtab of children children_xhtab_xp = XPTR( child_cxy , &child_ptr->children ); children_entry_xp = XPTR( child_cxy , &dentry_ptr->children ); error = xhtab_insert( children_xhtab_xp , "." , children_entry_xp ); if( error ) { printk("\n[ERROR] in %s : cannot register dentry <.> in xhtab\n", __FUNCTION__ ); return -1; } // don't register <.> dentry in child_inode xlist of parents // parents_root_xp = XPTR( child_cxy , &child_ptr->parents ); // parents_entry_xp = XPTR( child_cxy , &dentry_ptr->parents ); // xlist_add_first( parents_root_xp , parents_entry_xp ); // hal_remote_atomic_add( XPTR( child_cxy , &child_ptr->links ) , 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) if( DEBUG_VFS_ADD_SPECIAL < cycle ) printk("\n[%s] thread[%x,%x] linked dentry <.> to parent and child inodes\n", __FUNCTION__, this->process->pid, this->trdid ); #endif // introduce <.> dentry into child directory mapper if( child_cxy == local_cxy ) { error = vfs_fs_add_dentry( child_ptr, dentry_ptr ); } else { rpc_vfs_fs_add_dentry_client( child_cxy, child_ptr, dentry_ptr, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot introduce dentry <..> in mapper %x\n", __FUNCTION__ ); return -1; } #if(DEBUG_VFS_ADD_SPECIAL & 1) if( DEBUG_VFS_ADD_SPECIAL < cycle ) printk("\n[%s] thread[%x,%x] registered dentry <.> in child mapper\n", __FUNCTION__, this->process->pid, this->trdid ); #endif ///////////////////////////// create <..> dentry if( child_cxy == local_cxy ) { error = vfs_dentry_create( fs_type, "..", &dentry_xp ); } else { rpc_vfs_dentry_create_client( child_cxy, fs_type, "..", &dentry_xp, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot create dentry <..> in cluster %x\n", __FUNCTION__ , child_cxy ); return -1; } // get <..> dentry local pointer dentry_ptr = GET_PTR( dentry_xp ); #if(DEBUG_VFS_ADD_SPECIAL & 1) if( DEBUG_VFS_ADD_SPECIAL < cycle ) printk("\n[%s] thread[%x,%x] created dentry <..> (%x,%x)\n", __FUNCTION__, this->process->pid, this->trdid, child_cxy, dentry_ptr ); #endif // register <..> dentry in child_inode xhtab of children children_xhtab_xp = XPTR( child_cxy , &child_ptr->children ); children_entry_xp = XPTR( child_cxy , &dentry_ptr->children ); error = xhtab_insert( children_xhtab_xp , ".." , children_entry_xp ); if( error ) { printk("\n[ERROR] in %s : cannot register dentry <..> in xhtab\n", __FUNCTION__ ); return -1; } // don't register <..> dentry in parent_inode xlist of parents // parents_root_xp = XPTR( parent_cxy , &parent_ptr->parents ); // parents_entry_xp = XPTR( child_cxy , &dentry_ptr->parents ); // xlist_add_first( parents_root_xp , parents_entry_xp ); // hal_remote_atomic_add( XPTR( parent_cxy , &parent_ptr->links ) , 1 ); // 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) if( DEBUG_VFS_ADD_SPECIAL < cycle ) printk("\n[%s] thread[%x,%x] linked dentry <..> to parent and child inodes\n", __FUNCTION__, this->process->pid, this->trdid ); #endif // introduce <..> dentry into child directory mapper if( child_cxy == local_cxy ) { error = vfs_fs_add_dentry( child_ptr, dentry_ptr ); } else { rpc_vfs_fs_add_dentry_client( child_cxy, child_ptr, dentry_ptr, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot introduce dentry <..> in mapper %x\n", __FUNCTION__ ); return -1; } #if(DEBUG_VFS_ADD_SPECIAL & 1) if( DEBUG_VFS_ADD_SPECIAL < cycle ) printk("\n[%s] thread[%x,%x] registered dentry <..> in child mapper\n", __FUNCTION__, this->process->pid, this->trdid ); #endif #if DEBUG_VFS_ADD_SPECIAL cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_ADD_SPECIAL < cycle ) printk("\n[%s] thread[%x,%x] exit for child <%s> in parent <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle ); #endif return 0; } // end vfs_add_special_dentries() ////////////////////////////////////////// error_t vfs_get_path( xptr_t inode_xp, char * buffer, char ** first, uint32_t max_size ) { xptr_t dentry_xp; // extended pointer on current dentry vfs_dentry_t * dentry_ptr; // local pointer on current dentry cxy_t dentry_cxy; // current dentry cluster identifier xptr_t name_xp; // extended pointer on current dentry name uint32_t length; // length of current dentry name int32_t index; // slot index in buffer xptr_t current_xp; // extended pointer on current inode vfs_inode_t * current_ptr; // local pointer on current inode cxy_t current_cxy; // current inode cluster identifier xptr_t vfs_root_xp; // extended pointer on VFS root inode vfs_inode_t * vfs_root_ptr; // local pointer on VFS root inode cxy_t vfs_root_cxy; // VFS root inode cluster identifier xptr_t lock_xp; // extended pointer on Inode Tree lock xptr_t parents_root_xp; // extended pointer on current inode parents root bool_t found; // condition to exit the while loop thread_t * this = CURRENT_THREAD; process_t * process = this->process; #if DEBUG_VFS_GET_PATH uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_VFS_GET_PATH < cycle ) printk("\n[%s] thread[%x,%x] enter : inode (%x,%x) / cycle %d\n", __FUNCTION__ , process->pid, this->trdid, GET_CXY( inode_xp ), GET_PTR( inode_xp ), cycle ); #endif // set the NUL character in buffer / initialise buffer index buffer[max_size - 1] = 0; index = (int32_t)(max_size - 1); // initialize current inode current_xp = inode_xp; // build extended pointer on lock protecting Inode Tree vfs_root_xp = process->vfs_root_xp; vfs_root_ptr = GET_PTR( vfs_root_xp ); vfs_root_cxy = GET_CXY( vfs_root_xp ); lock_xp = XPTR( vfs_root_cxy , &vfs_root_ptr->main_lock ); // take lock protecting Inode Tree in read mode remote_rwlock_rd_acquire( lock_xp ); // traverse Inode Tree from target inode to VFS root // selecting always the first parent dentry // the buffer is written in "reverse order" (from target inode to root) // exit the while loop when the VFS root has been found do { // get current inode cluster and local pointer current_cxy = GET_CXY( current_xp ); current_ptr = GET_PTR( current_xp ); // build extended pointer on parents dentries root parents_root_xp = XPTR( current_cxy , ¤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( (index >= 0) , "kernel buffer too small\n" ); } } 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( (index >= 0) , "kernel buffer too small\n" ); // 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_inode_type_t child_type, 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 char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( parent_inode_xp , parent_name ); uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; 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 if( parent_cxy == local_cxy ) // parent cluster is local { error = vfs_dentry_create( fs_type, name, &new_dentry_xp ); } else // parent cluster is remote { rpc_vfs_dentry_create_client( parent_cxy, fs_type, name, &new_dentry_xp, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot create dentry <%s> in cluster %x\n", __FUNCTION__ , name , parent_cxy ); 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] / dentry <%s> created (%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; if( child_cxy == local_cxy ) // child cluster is local { error = vfs_inode_create( fs_type, child_type, attr, mode, uid, gid, &new_inode_xp ); } else // child cluster is remote { rpc_vfs_inode_create_client( child_cxy, fs_type, child_type, attr, mode, uid, gid, &new_inode_xp, &error ); } if( error ) { printk("\n[ERROR] in %s : cannot create inode in cluster %x\n", __FUNCTION__ , child_cxy ); if( parent_cxy == local_cxy ) vfs_dentry_destroy( new_dentry_ptr ); else rpc_vfs_dentry_destroy_client( parent_cxy , new_dentry_ptr ); 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] / inode <%s> created (%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] / dentry (%x,%x) registered in child inode (%x,%x)\n", __FUNCTION__, this->process->pid, this->trdid, parent_cxy, new_dentry_ptr, child_cxy, new_inode_ptr ); #endif // 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] / dentry (%x,%x) registered in parent inode (%x,%x)\n", __FUNCTION__, this->process->pid, this->trdid, parent_cxy, new_dentry_ptr, parent_cxy, parent_inode_ptr ); #endif // 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, (uint32_t)hal_get_cycles() ); #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 char dentry_name[CONFIG_VFS_MAX_NAME_LENGTH]; // 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 ); // remove dentry from parent_inode xhtab_remove( XPTR( parent_cxy , &parent_inode_ptr->children ), dentry_name, XPTR( parent_cxy , &dentry_ptr->children ) ); // remove dentry from child_inode xlist_unlink( XPTR( parent_cxy , &dentry_ptr->parents ) ); links = hal_remote_atomic_add( XPTR( child_cxy , &child_inode_ptr->links ) , -1 ); // delete dentry descriptor if( parent_cxy == local_cxy ) { vfs_dentry_destroy( dentry_ptr ); } else { rpc_vfs_dentry_destroy_client( parent_cxy, dentry_ptr ); } // delete child_inode descriptor if last link if( links == 1 ) { if( child_cxy == local_cxy ) { vfs_inode_destroy( child_inode_ptr ); } else { rpc_vfs_inode_destroy_client( child_cxy , child_inode_ptr ); } } } // end vfs_remove_child_from_parent() ////////////////////////////////////////////////////////////////////////////////////////// // API used by VFS to access a specific FS ////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////// error_t vfs_fs_move_page( xptr_t page_xp, bool_t to_mapper ) { error_t error = 0; assert( (page_xp != XPTR_NULL) , "page pointer is NULL\n" ); page_t * page_ptr = GET_PTR( page_xp ); cxy_t page_cxy = GET_CXY( page_xp ); // get local pointer on page mapper mapper_t * mapper = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) ); assert( (mapper != NULL) , "no mapper for page\n" ); // get FS type vfs_fs_type_t fs_type = hal_remote_l32( XPTR( page_cxy , &mapper->type ) ); // call relevant FS function if( fs_type == FS_TYPE_FATFS ) { error = fatfs_move_page( page_xp , to_mapper ); } else if( fs_type == FS_TYPE_RAMFS ) { assert( false , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { assert( false , "should not be called for DEVFS\n" ); } else { assert( false , "undefined file system type\n" ); } return error; } // end vfs_fs_move_page() //////////////////////////////////////////////// error_t vfs_fs_add_dentry( vfs_inode_t * inode, vfs_dentry_t * dentry ) { error_t error = 0; assert( (inode != NULL) , "inode pointer is NULL\n" ); assert( (dentry != NULL) , "dentry pointer is NULL\n" ); mapper_t * mapper = inode->mapper; assert( (mapper != NULL) , "mapper pointer is NULL\n" ); // get FS type vfs_fs_type_t fs_type = mapper->type; // call relevant FS function if( fs_type == FS_TYPE_FATFS ) { error = fatfs_add_dentry( inode , dentry ); } 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( false , "undefined file system type\n" ); } return error; } // end vfs_fs_add_dentry() /////////////////////////////////////////////////// error_t vfs_fs_remove_dentry( vfs_inode_t * inode, vfs_dentry_t * dentry ) { error_t error = 0; assert( (inode != NULL) , "inode pointer is NULL\n" ); assert( (dentry != NULL) , "dentry pointer is NULL\n" ); mapper_t * mapper = inode->mapper; assert( (mapper != NULL) , "mapper pointer is NULL\n" ); // get FS type vfs_fs_type_t fs_type = mapper->type; // call relevant FS function if( fs_type == FS_TYPE_FATFS ) { error = fatfs_remove_dentry( inode , dentry ); } 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( false , "undefined file system type\n" ); } return error; } // end vfs_fs_remove_dentry() //////////////////////////////////////////////// error_t vfs_fs_get_dentry( vfs_inode_t * parent, char * name, xptr_t child_xp ) { error_t error = 0; // check arguments assert( (parent != NULL) , "parent pointer is NULL\n"); assert( (child_xp != XPTR_NULL) , "child pointer is NULL\n"); // get parent inode FS type vfs_fs_type_t fs_type = parent->ctx->type; // call relevant FS function if( fs_type == FS_TYPE_FATFS ) { error = fatfs_get_dentry( parent , name , child_xp ); } else if( fs_type == FS_TYPE_RAMFS ) { assert( false , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { assert( false , "should not be called for DEVFS\n" ); } else { assert( false , "undefined file system type\n" ); } return error; } // end vfs_fs_get_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( (inode != NULL) , "parent pointer is NULL\n"); assert( (array != NULL) , "child pointer is NULL\n"); assert( (detailed == false) , "detailed argument not supported\n"); // check inode type if( inode->type != INODE_TYPE_DIR ) { printk("\n[ERROR] in %s : target inode is not a directory\n", __FUNCTION__ ); return -1; } // get parent inode FS type vfs_fs_type_t fs_type = inode->ctx->type; // call relevant FS function if( fs_type == FS_TYPE_FATFS ) { error = fatfs_get_user_dir( inode, array, max_dirent, min_dentry, detailed, entries, done ); } else if( fs_type == FS_TYPE_RAMFS ) { assert( false , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { error = devfs_get_user_dir( inode, array, max_dirent, min_dentry, detailed, entries, done ); } else { assert( false , "undefined file system type\n" ); } return error; } // end vfs_fs_get_user_dir() //////////////////////////////////////////////// error_t vfs_fs_sync_inode( vfs_inode_t * inode ) { error_t error = 0; // check arguments assert( (inode != NULL) , "inode pointer is NULL\n"); // get inode FS type vfs_fs_type_t fs_type = inode->ctx->type; // call relevant FS function if( fs_type == FS_TYPE_FATFS ) { error = fatfs_sync_inode( inode ); } else if( fs_type == FS_TYPE_RAMFS ) { assert( false , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { assert( false , "should not be called for DEVFS\n" ); } else { assert( false , "undefined file system type\n" ); } 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( false , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { assert( false , "should not be called for DEVFS\n" ); } else { assert( false , "undefined file system type\n" ); } return error; } // end vfs_fs_sync_fat() ////////////////////////////////////////////////////// error_t vfs_fs_sync_free_info( vfs_fs_type_t fs_type ) { error_t error = 0; // call relevant FS function if( fs_type == FS_TYPE_FATFS ) { error = fatfs_sync_free_info(); } else if( fs_type == FS_TYPE_RAMFS ) { assert( false , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { assert( false , "should not be called for DEVFS\n" ); } else { assert( false , "undefined file system type\n" ); } return error; } // end vfs_fs_sync_fat() ///////////////////////////////////////////////// error_t vfs_fs_cluster_alloc( uint32_t fs_type, uint32_t * cluster ) { error_t error = 0; // call relevant FS function if( fs_type == FS_TYPE_FATFS ) { error = fatfs_cluster_alloc( cluster ); } else if( fs_type == FS_TYPE_RAMFS ) { assert( false , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { assert( false , "should not be called for DEVFS\n" ); } else { assert( false , "undefined file system type\n" ); } return error; } // end vfs_fs_alloc_cluster() //////////////////////////////////////////////// error_t vfs_fs_release_inode( xptr_t inode_xp ) { error_t error = 0; assert( (inode_xp != XPTR_NULL) , "inode pointer is NULL\n") vfs_inode_t * inode_ptr = GET_PTR( inode_xp ); cxy_t inode_cxy = GET_CXY( inode_xp ); // get local pointer on page mapper mapper_t * mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) ); assert( (mapper != NULL) , "mapper pointer is NULL\n") // get FS type from mapper vfs_fs_type_t fs_type = hal_remote_l32( XPTR( inode_cxy , &mapper->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( false , "should not be called for RAMFS\n" ); } else if( fs_type == FS_TYPE_DEVFS ) { assert( false , "should not be called for DEVFS\n" ); } else { assert( false , "undefined file system type\n" ); } return error; } // end vfs_fs_release_inode()