int memfile_open( rtems_libio_t *iop, const char *pathname, int oflag, mode_t mode ) { IMFS_jnode_t *the_jnode; the_jnode = iop->pathinfo.node_access; /* * Perform 'copy on write' for linear files */ if ((iop->flags & LIBIO_FLAGS_WRITE) && (IMFS_type( the_jnode ) == IMFS_LINEAR_FILE)) { uint32_t count = the_jnode->info.linearfile.size; const unsigned char *buffer = the_jnode->info.linearfile.direct; the_jnode->control = &IMFS_node_control_memfile; the_jnode->info.file.size = 0; the_jnode->info.file.indirect = 0; the_jnode->info.file.doubly_indirect = 0; the_jnode->info.file.triply_indirect = 0; if ((count != 0) && (IMFS_memfile_write(the_jnode, 0, buffer, count) == -1)) return -1; } return 0; }
/* * IMFS_memfile_addblock * * This routine adds a single block to the specified in-memory file. */ MEMFILE_STATIC int IMFS_memfile_addblock( IMFS_jnode_t *the_jnode, unsigned int block ) { block_p memory; block_p *block_entry_ptr; IMFS_assert( the_jnode ); IMFS_assert( IMFS_type( the_jnode ) == IMFS_MEMORY_FILE ); /* * Obtain the pointer for the specified block number */ block_entry_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 1 ); if ( !block_entry_ptr ) return 1; if ( *block_entry_ptr ) return 0; /* * There is no memory for this block number so allocate it. */ memory = memfile_alloc_block(); if ( !memory ) return 1; *block_entry_ptr = memory; return 0; }
/* * IMFS_memfile_extend * * This routine insures that the in-memory file is of the length * specified. If necessary, it will allocate memory blocks to * extend the file. */ MEMFILE_STATIC int IMFS_memfile_extend( IMFS_jnode_t *the_jnode, off_t new_length ) { unsigned int block; unsigned int new_blocks; unsigned int old_blocks; /* * Perform internal consistency checks */ IMFS_assert( the_jnode ); IMFS_assert( IMFS_type( the_jnode ) == IMFS_MEMORY_FILE ); /* * Verify new file size is supported */ if ( new_length >= IMFS_MEMFILE_MAXIMUM_SIZE ) rtems_set_errno_and_return_minus_one( EINVAL ); /* * Verify new file size is actually larger than current size */ if ( new_length <= the_jnode->info.file.size ) return 0; /* * Calculate the number of range of blocks to allocate */ new_blocks = new_length / IMFS_MEMFILE_BYTES_PER_BLOCK; old_blocks = the_jnode->info.file.size / IMFS_MEMFILE_BYTES_PER_BLOCK; /* * Now allocate each of those blocks. */ for ( block=old_blocks ; block<=new_blocks ; block++ ) { if ( IMFS_memfile_addblock( the_jnode, block ) ) { for ( ; block>=old_blocks ; block-- ) { IMFS_memfile_remove_block( the_jnode, block ); } rtems_set_errno_and_return_minus_one( ENOSPC ); } } /* * Set the new length of the file. */ the_jnode->info.file.size = new_length; IMFS_update_ctime(the_jnode); IMFS_update_mtime(the_jnode); return 0; }
rtems_filesystem_node_types_t IMFS_node_type( const rtems_filesystem_location_info_t *loc ) { const IMFS_jnode_t *node = loc->node_access; IMFS_jnode_types_t imfs_type = IMFS_type( node ); rtems_filesystem_node_types_t type; switch ( imfs_type ) { case IMFS_HARD_LINK: type = IMFS_type( node->info.hard_link.link_node ); break; case IMFS_LINEAR_FILE: type = RTEMS_FILESYSTEM_MEMORY_FILE; break; default: type = imfs_type; break; } return type; }
static int IMFS_stat_link( const rtems_filesystem_location_info_t *loc, struct stat *buf ) { const IMFS_jnode_t *node = loc->node_access; if ( IMFS_type( node ) != IMFS_HARD_LINK ) { buf->st_size = strlen( node->info.sym_link.name ); return IMFS_stat( loc, buf ); } else { rtems_filesystem_location_info_t targetloc = *loc; targetloc.node_access = node->info.hard_link.link_node; IMFS_Set_handlers( &targetloc ); return (targetloc.handlers->fstat_h)( &targetloc, buf ); } }
/* * memfile_lseek * * This routine processes the lseek() system call. */ off_t memfile_lseek( rtems_libio_t *iop, off_t offset, int whence ) { IMFS_jnode_t *the_jnode; the_jnode = iop->pathinfo.node_access; if (IMFS_type( the_jnode ) == IMFS_LINEAR_FILE) { if (iop->offset > the_jnode->info.linearfile.size) iop->offset = the_jnode->info.linearfile.size; } else { /* Must be a block file (IMFS_MEMORY_FILE). */ if (IMFS_memfile_extend( the_jnode, iop->offset )) rtems_set_errno_and_return_minus_one( ENOSPC ); iop->size = the_jnode->info.file.size; } return iop->offset; }
static rtems_filesystem_eval_path_generic_status IMFS_eval_token( rtems_filesystem_eval_path_context_t *ctx, void *arg, const char *token, size_t tokenlen ) { rtems_filesystem_eval_path_generic_status status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE; rtems_filesystem_location_info_t *currentloc = rtems_filesystem_eval_path_get_currentloc( ctx ); IMFS_jnode_t *dir = currentloc->node_access; bool access_ok = rtems_filesystem_eval_path_check_access( ctx, RTEMS_FS_PERMS_EXEC, dir->st_mode, dir->st_uid, dir->st_gid ); if ( access_ok ) { IMFS_jnode_t *entry = IMFS_search_in_directory( dir, token, tokenlen ); if ( entry != NULL ) { bool terminal = !rtems_filesystem_eval_path_has_path( ctx ); int eval_flags = rtems_filesystem_eval_path_get_flags( ctx ); bool follow_hard_link = (eval_flags & RTEMS_FS_FOLLOW_HARD_LINK) != 0; bool follow_sym_link = (eval_flags & RTEMS_FS_FOLLOW_SYM_LINK) != 0; IMFS_jnode_types_t type = IMFS_type( entry ); rtems_filesystem_eval_path_clear_token( ctx ); if ( type == IMFS_HARD_LINK && (follow_hard_link || !terminal)) { entry = entry->info.hard_link.link_node; } if ( type == IMFS_SYM_LINK && (follow_sym_link || !terminal)) { const char *target = entry->info.sym_link.name; rtems_filesystem_eval_path_recursive( ctx, target, strlen( target ) ); } else { rtems_filesystem_global_location_t **fs_root_ptr = IMFS_is_mount_point( entry, type ); if ( fs_root_ptr == NULL ) { --dir->reference_count; ++entry->reference_count; currentloc->node_access = entry; currentloc->node_access_2 = IMFS_generic_get_context_by_node( entry ); IMFS_Set_handlers( currentloc ); if ( !terminal ) { status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE; } } else { access_ok = rtems_filesystem_eval_path_check_access( ctx, RTEMS_FS_PERMS_EXEC, entry->st_mode, entry->st_uid, entry->st_gid ); if ( access_ok ) { rtems_filesystem_eval_path_restart( ctx, fs_root_ptr ); } } } } else { status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY; } } return status; }
block_p *IMFS_memfile_get_block_pointer( #endif IMFS_jnode_t *the_jnode, unsigned int block, int malloc_it ) { unsigned int my_block; IMFS_memfile_t *info; unsigned int singly; unsigned int doubly; unsigned int triply; block_p *p; block_p *p1; block_p *p2; /* * Perform internal consistency checks */ IMFS_assert( the_jnode ); IMFS_assert( IMFS_type( the_jnode ) == IMFS_MEMORY_FILE ); info = &the_jnode->info.file; my_block = block; /* * Is the block number in the simple indirect portion? */ if ( my_block <= LAST_INDIRECT ) { p = info->indirect; if ( malloc_it ) { if ( !p ) { p = memfile_alloc_block(); if ( !p ) return 0; info->indirect = p; } return &info->indirect[ my_block ]; } if ( !p ) return 0; return &info->indirect[ my_block ]; } /* * Is the block number in the doubly indirect portion? */ if ( my_block <= LAST_DOUBLY_INDIRECT ) { my_block -= FIRST_DOUBLY_INDIRECT; singly = my_block % IMFS_MEMFILE_BLOCK_SLOTS; doubly = my_block / IMFS_MEMFILE_BLOCK_SLOTS; p = info->doubly_indirect; if ( malloc_it ) { if ( !p ) { p = memfile_alloc_block(); if ( !p ) return 0; info->doubly_indirect = p; } p1 = (block_p *)p[ doubly ]; if ( !p1 ) { p1 = memfile_alloc_block(); if ( !p1 ) return 0; p[ doubly ] = (block_p) p1; } return (block_p *)&p1[ singly ]; } if ( !p ) return 0; p = (block_p *)p[ doubly ]; if ( !p ) return 0; return (block_p *)&p[ singly ]; } /* * Is the block number in the triply indirect portion? */ if ( my_block <= LAST_TRIPLY_INDIRECT ) { my_block -= FIRST_TRIPLY_INDIRECT; singly = my_block % IMFS_MEMFILE_BLOCK_SLOTS; doubly = my_block / IMFS_MEMFILE_BLOCK_SLOTS; triply = doubly / IMFS_MEMFILE_BLOCK_SLOTS; doubly %= IMFS_MEMFILE_BLOCK_SLOTS; p = info->triply_indirect; if ( malloc_it ) { if ( !p ) { p = memfile_alloc_block(); if ( !p ) return 0; info->triply_indirect = p; } p1 = (block_p *) p[ triply ]; if ( !p1 ) { p1 = memfile_alloc_block(); if ( !p1 ) return 0; p[ triply ] = (block_p) p1; } p2 = (block_p *)p1[ doubly ]; if ( !p2 ) { p2 = memfile_alloc_block(); if ( !p2 ) return 0; p1[ doubly ] = (block_p) p2; } return (block_p *)&p2[ singly ]; } if ( !p ) return 0; p1 = (block_p *) p[ triply ]; if ( !p1 ) return 0; p2 = (block_p *)p1[ doubly ]; if ( !p2 ) return 0; return (block_p *)&p2[ singly ]; } /* * This means the requested block number is out of range. */ return 0; }
/* * IMFS_memfile_write * * This routine writes the specified data buffer into the in memory * file pointed to by the_jnode. The file is extended as needed. */ MEMFILE_STATIC ssize_t IMFS_memfile_write( IMFS_jnode_t *the_jnode, off_t start, const unsigned char *source, unsigned int length ) { block_p *block_ptr; unsigned int block; int status; unsigned int my_length; unsigned int to_copy = 0; unsigned int last_byte; unsigned int start_offset; int copied; const unsigned char *src; src = source; /* * Perform internal consistency checks */ IMFS_assert( source ); IMFS_assert( the_jnode ); IMFS_assert( IMFS_type( the_jnode ) == IMFS_MEMORY_FILE ); my_length = length; /* * If the last byte we are supposed to write is past the end of this * in memory file, then extend the length. */ last_byte = start + my_length; if ( last_byte > the_jnode->info.file.size ) { bool zero_fill = start > the_jnode->info.file.size; status = IMFS_memfile_extend( the_jnode, zero_fill, last_byte ); if ( status ) return status; } copied = 0; /* * Three phases to the write: * + possibly the last part of one block * + all of zero of more blocks * + possibly the first part of one block */ /* * Phase 1: possibly the last part of one block */ start_offset = start % IMFS_MEMFILE_BYTES_PER_BLOCK; block = start / IMFS_MEMFILE_BYTES_PER_BLOCK; if ( start_offset ) { to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK - start_offset; if ( to_copy > my_length ) to_copy = my_length; block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); if ( !block_ptr ) return copied; #if 0 fprintf( stderr, "write %d at %d in %d: %*s\n", to_copy, start_offset, block, to_copy, src ); #endif memcpy( &(*block_ptr)[ start_offset ], src, to_copy ); src += to_copy; block++; my_length -= to_copy; copied += to_copy; } /* * Phase 2: all of zero of more blocks */ to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK; while ( my_length >= IMFS_MEMFILE_BYTES_PER_BLOCK ) { block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); if ( !block_ptr ) return copied; #if 0 fprintf(stdout, "write %d in %d: %*s\n", to_copy, block, to_copy, src ); #endif memcpy( &(*block_ptr)[ 0 ], src, to_copy ); src += to_copy; block++; my_length -= to_copy; copied += to_copy; } /* * Phase 3: possibly the first part of one block */ IMFS_assert( my_length < IMFS_MEMFILE_BYTES_PER_BLOCK ); to_copy = my_length; if ( my_length ) { block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); if ( !block_ptr ) return copied; #if 0 fprintf(stdout, "write %d in %d: %*s\n", to_copy, block, to_copy, src ); #endif memcpy( &(*block_ptr)[ 0 ], src, my_length ); my_length = 0; copied += to_copy; } IMFS_mtime_ctime_update( the_jnode ); return copied; }
/* * IMFS_memfile_read * * This routine read from memory file pointed to by the_jnode into * the specified data buffer specified by destination. The file * is NOT extended. An offset greater than the length of the file * is considered an error. Read from an offset for more bytes than * are between the offset and the end of the file will result in * reading the data between offset and the end of the file (truncated * read). */ MEMFILE_STATIC ssize_t IMFS_memfile_read( IMFS_jnode_t *the_jnode, off_t start, unsigned char *destination, unsigned int length ) { block_p *block_ptr; unsigned int block; unsigned int my_length; unsigned int to_copy = 0; unsigned int last_byte; unsigned int copied; unsigned int start_offset; unsigned char *dest; dest = destination; /* * Perform internal consistency checks */ IMFS_assert( the_jnode ); IMFS_assert( IMFS_type( the_jnode ) == IMFS_MEMORY_FILE || IMFS_type( the_jnode ) == IMFS_LINEAR_FILE ); IMFS_assert( dest ); /* * Linear files (as created from a tar file are easier to handle * than block files). */ my_length = length; if ( IMFS_type( the_jnode ) == IMFS_LINEAR_FILE ) { unsigned char *file_ptr; file_ptr = (unsigned char *)the_jnode->info.linearfile.direct; if (my_length > (the_jnode->info.linearfile.size - start)) my_length = the_jnode->info.linearfile.size - start; memcpy(dest, &file_ptr[start], my_length); IMFS_update_atime( the_jnode ); return my_length; } /* * If the last byte we are supposed to read is past the end of this * in memory file, then shorten the length to read. */ last_byte = start + length; if ( last_byte > the_jnode->info.file.size ) my_length = the_jnode->info.file.size - start; copied = 0; /* * Three phases to the read: * + possibly the last part of one block * + all of zero of more blocks * + possibly the first part of one block */ /* * Phase 1: possibly the last part of one block */ start_offset = start % IMFS_MEMFILE_BYTES_PER_BLOCK; block = start / IMFS_MEMFILE_BYTES_PER_BLOCK; if ( start_offset ) { to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK - start_offset; if ( to_copy > my_length ) to_copy = my_length; block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); if ( !block_ptr ) return copied; memcpy( dest, &(*block_ptr)[ start_offset ], to_copy ); dest += to_copy; block++; my_length -= to_copy; copied += to_copy; } /* * Phase 2: all of zero of more blocks */ to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK; while ( my_length >= IMFS_MEMFILE_BYTES_PER_BLOCK ) { block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); if ( !block_ptr ) return copied; memcpy( dest, &(*block_ptr)[ 0 ], to_copy ); dest += to_copy; block++; my_length -= to_copy; copied += to_copy; } /* * Phase 3: possibly the first part of one block */ IMFS_assert( my_length < IMFS_MEMFILE_BYTES_PER_BLOCK ); if ( my_length ) { block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); if ( !block_ptr ) return copied; memcpy( dest, &(*block_ptr)[ 0 ], my_length ); copied += my_length; } IMFS_update_atime( the_jnode ); return copied; }
/* * IMFS_memfile_remove * * This routine frees all memory associated with an in memory file. * * NOTE: This is an exceptionally conservative implementation. * It will check EVERY pointer which is non-NULL and insure * any child non-NULL pointers are freed. Optimistically, all that * is necessary is to scan until a NULL pointer is found. There * should be no allocated data past that point. * * In experimentation on the powerpc simulator, it was noted * that using blocks which held 128 slots versus 16 slots made * a significant difference in the performance of this routine. * * Regardless until the IMFS implementation is proven, it * is better to stick to simple, easy to understand algorithms. */ IMFS_jnode_t *IMFS_memfile_remove( IMFS_jnode_t *the_jnode ) { IMFS_memfile_t *info; int i; int j; unsigned int to_free; block_p *p; /* * Perform internal consistency checks */ IMFS_assert( the_jnode ); IMFS_assert( IMFS_type( the_jnode ) == IMFS_MEMORY_FILE ); /* * Eventually this could be set smarter at each call to * memfile_free_blocks_in_table to greatly speed this up. */ to_free = IMFS_MEMFILE_BLOCK_SLOTS; /* * Now start freeing blocks in this order: * + indirect * + doubly indirect * + triply indirect */ info = &the_jnode->info.file; if ( info->indirect ) { memfile_free_blocks_in_table( &info->indirect, to_free ); } if ( info->doubly_indirect ) { for ( i=0 ; i<IMFS_MEMFILE_BLOCK_SLOTS ; i++ ) { if ( info->doubly_indirect[i] ) { memfile_free_blocks_in_table( (block_p **)&info->doubly_indirect[i], to_free ); } } memfile_free_blocks_in_table( &info->doubly_indirect, to_free ); } if ( info->triply_indirect ) { for ( i=0 ; i<IMFS_MEMFILE_BLOCK_SLOTS ; i++ ) { p = (block_p *) info->triply_indirect[i]; if ( !p ) /* ensure we have a valid pointer */ break; for ( j=0 ; j<IMFS_MEMFILE_BLOCK_SLOTS ; j++ ) { if ( p[j] ) { memfile_free_blocks_in_table( (block_p **)&p[j], to_free); } } memfile_free_blocks_in_table( (block_p **)&info->triply_indirect[i], to_free ); } memfile_free_blocks_in_table( (block_p **)&info->triply_indirect, to_free ); } return the_jnode; }
/* * IMFS_memfile_extend * * This routine insures that the in-memory file is of the length * specified. If necessary, it will allocate memory blocks to * extend the file. */ MEMFILE_STATIC int IMFS_memfile_extend( IMFS_jnode_t *the_jnode, bool zero_fill, off_t new_length ) { unsigned int block; unsigned int new_blocks; unsigned int old_blocks; unsigned int offset; /* * Perform internal consistency checks */ IMFS_assert( the_jnode ); IMFS_assert( IMFS_type( the_jnode ) == IMFS_MEMORY_FILE ); /* * Verify new file size is supported */ if ( new_length >= IMFS_MEMFILE_MAXIMUM_SIZE ) rtems_set_errno_and_return_minus_one( EFBIG ); /* * Verify new file size is actually larger than current size */ if ( new_length <= the_jnode->info.file.size ) return 0; /* * Calculate the number of range of blocks to allocate */ new_blocks = new_length / IMFS_MEMFILE_BYTES_PER_BLOCK; old_blocks = the_jnode->info.file.size / IMFS_MEMFILE_BYTES_PER_BLOCK; offset = the_jnode->info.file.size - old_blocks * IMFS_MEMFILE_BYTES_PER_BLOCK; /* * Now allocate each of those blocks. */ for ( block=old_blocks ; block<=new_blocks ; block++ ) { if ( !IMFS_memfile_addblock( the_jnode, block ) ) { if ( zero_fill ) { size_t count = IMFS_MEMFILE_BYTES_PER_BLOCK - offset; block_p *block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); memset( &(*block_ptr) [offset], 0, count); offset = 0; } } else { for ( ; block>=old_blocks ; block-- ) { IMFS_memfile_remove_block( the_jnode, block ); } rtems_set_errno_and_return_minus_one( ENOSPC ); } } /* * Set the new length of the file. */ the_jnode->info.file.size = new_length; IMFS_mtime_ctime_update(the_jnode); return 0; }