/* * IMFS Device Node Handlers * * This file contains the set of handlers used to process operations on * IMFS memory file nodes. The memory files are created in memory using * malloc'ed memory. Thus any data stored in one of these files is lost * at system shutdown unless special arrangements to copy the data to * some type of non-volailte storage are made by the application. * * COPYRIGHT (c) 1989-1999. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * * $Id$ */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "imfs.h" #include #include #define MEMFILE_STATIC /* * Prototypes of private routines */ MEMFILE_STATIC int IMFS_memfile_extend( IMFS_jnode_t *the_jnode, off_t new_length ); MEMFILE_STATIC int IMFS_memfile_addblock( IMFS_jnode_t *the_jnode, unsigned int block ); MEMFILE_STATIC int IMFS_memfile_remove_block( IMFS_jnode_t *the_jnode, unsigned int block ); MEMFILE_STATIC block_p *IMFS_memfile_get_block_pointer( IMFS_jnode_t *the_jnode, unsigned int block, int malloc_it ); MEMFILE_STATIC ssize_t IMFS_memfile_read( IMFS_jnode_t *the_jnode, off_t start, unsigned char *destination, unsigned int length ); ssize_t IMFS_memfile_write( /* cannot be static as used in imfs_fchmod.c */ IMFS_jnode_t *the_jnode, off_t start, const unsigned char *source, unsigned int length ); int memfile_check_rmnod( IMFS_jnode_t *the_jnode ); void *memfile_alloc_block(void); void memfile_free_block( void *memory ); /* * memfile_open * * This routine processes the open() system call. Note that there is * nothing special to be done at open() time. */ int memfile_open( rtems_libio_t *iop, const char *pathname, uint32_t flag, uint32_t mode ) { IMFS_jnode_t *the_jnode; the_jnode = iop->file_info; /* * Perform 'copy on write' for linear files */ if ((iop->flags & (LIBIO_FLAGS_WRITE | LIBIO_FLAGS_APPEND)) && (the_jnode->type == IMFS_LINEAR_FILE)) { uint32_t count = the_jnode->info.linearfile.size; const unsigned char *buffer = the_jnode->info.linearfile.direct; the_jnode->type = IMFS_MEMORY_FILE; 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; } if (iop->flags & LIBIO_FLAGS_APPEND) iop->offset = the_jnode->info.file.size; iop->size = the_jnode->info.file.size; return 0; } /* * memfile_close * * This routine processes the close() system call. Note that there is * nothing to flush or memory to free at this point. */ int memfile_close( rtems_libio_t *iop ) { IMFS_jnode_t *the_jnode; the_jnode = iop->file_info; if (iop->flags & LIBIO_FLAGS_APPEND) iop->offset = the_jnode->info.file.size; memfile_check_rmnod( the_jnode ); return 0; } /* * memfile_read * * This routine processes the read() system call. */ ssize_t memfile_read( rtems_libio_t *iop, void *buffer, size_t count ) { IMFS_jnode_t *the_jnode; the_jnode = iop->file_info; return IMFS_memfile_read( the_jnode, iop->offset, buffer, count ); } /* * memfile_write * * This routine processes the write() system call. */ ssize_t memfile_write( rtems_libio_t *iop, const void *buffer, size_t count ) { IMFS_jnode_t *the_jnode; ssize_t status; the_jnode = iop->file_info; status = IMFS_memfile_write( the_jnode, iop->offset, buffer, count ); iop->size = the_jnode->info.file.size; return status; } /* * memfile_ioctl * * This routine processes the ioctl() system call. * * NOTE: No ioctl()'s are supported for in-memory files. */ int memfile_ioctl( rtems_libio_t *iop, uint32_t command, void *buffer ) { IMFS_jnode_t *the_jnode; the_jnode = iop->file_info; return 0; } /* * memfile_lseek * * This routine processes the lseek() system call. */ int memfile_lseek( rtems_libio_t *iop, off_t offset, int whence ) { IMFS_jnode_t *the_jnode; the_jnode = iop->file_info; if (the_jnode->type == 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; } /* * memfile_stat * * This IMFS_stat() can be used. */ /* * memfile_ftruncate * * This routine processes the ftruncate() system call. */ int memfile_ftruncate( rtems_libio_t *iop, off_t length ) { IMFS_jnode_t *the_jnode; the_jnode = iop->file_info; /* * POSIX 1003.1b does not specify what happens if you truncate a file * and the new length is greater than the current size. We treat this * as an extend operation. */ if ( length > the_jnode->info.file.size ) return IMFS_memfile_extend( the_jnode, length ); /* * The in-memory files do not currently reclaim memory until the file is * deleted. So we leave the previously allocated blocks in place for * future use and just set the length. */ the_jnode->info.file.size = length; iop->size = the_jnode->info.file.size; IMFS_update_atime( the_jnode ); 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 */ assert( the_jnode ); if ( !the_jnode ) rtems_set_errno_and_return_minus_one( EIO ); assert( the_jnode->type == IMFS_MEMORY_FILE ); if ( the_jnode->type != IMFS_MEMORY_FILE ) rtems_set_errno_and_return_minus_one( EIO ); if ( new_length >= IMFS_MEMFILE_MAXIMUM_SIZE ) rtems_set_errno_and_return_minus_one( EINVAL ); 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; 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; assert( the_jnode ); if ( !the_jnode ) rtems_set_errno_and_return_minus_one( EIO ); assert( the_jnode->type == IMFS_MEMORY_FILE ); if ( the_jnode->type != IMFS_MEMORY_FILE ) rtems_set_errno_and_return_minus_one( EIO ); block_entry_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 1 ); if ( *block_entry_ptr ) return 0; #if 0 fprintf(stdout, "%d %p", block, block_entry_ptr ); fflush(stdout); #endif memory = memfile_alloc_block(); if ( !memory ) return 1; *block_entry_ptr = memory; return 0; } /* * IMFS_memfile_remove_block * * This routine removes the specified block from the in-memory file. * * NOTE: This is a support routine and is called only to remove * the last block or set of blocks in a file. Removing a * block from the middle of a file would be exceptionally * dangerous and the results unpredictable. */ MEMFILE_STATIC int IMFS_memfile_remove_block( IMFS_jnode_t *the_jnode, unsigned int block ) { block_p *block_entry_ptr; block_p ptr; block_entry_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); ptr = *block_entry_ptr; *block_entry_ptr = 0; memfile_free_block( ptr ); return 1; } /* * memfile_free_blocks_in_table * * This is a support routine for IMFS_memfile_remove. It frees all the * blocks in one of the indirection tables. */ void memfile_free_blocks_in_table( block_p **block_table, int entries ) { int i; block_p *b; /* * Perform internal consistency checks */ assert( block_table ); if ( !block_table ) return; /* * Now go through all the slots in the table and free the memory. */ b = *block_table; for ( i=0 ; itype == IMFS_MEMORY_FILE ); if ( the_jnode->type != IMFS_MEMORY_FILE ) rtems_set_errno_and_return_minus_one( EIO ); /* * 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 ; idoubly_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 ; itriply_indirect[i]; if ( !p ) /* ensure we have a valid pointer */ break; for ( j=0 ; jtriply_indirect[i], to_free ); } memfile_free_blocks_in_table( (block_p **)&info->triply_indirect, to_free ); } return 0; } /* * 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 */ assert( the_jnode ); if ( !the_jnode ) rtems_set_errno_and_return_minus_one( EIO ); assert( the_jnode->type == IMFS_MEMORY_FILE || the_jnode->type == IMFS_LINEAR_FILE ); if ( the_jnode->type != IMFS_MEMORY_FILE && the_jnode->type != IMFS_LINEAR_FILE ) rtems_set_errno_and_return_minus_one( EIO ); /* * Error checks on arguments */ assert( dest ); if ( !dest ) rtems_set_errno_and_return_minus_one( EINVAL ); /* * If there is nothing to read, then quick exit. */ my_length = length; if ( !my_length ) rtems_set_errno_and_return_minus_one( EINVAL ); /* * Linear files (as created from a tar file are easier to handle * than block files). */ if (the_jnode->type == 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 ); assert( block_ptr ); 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 ); assert( block_ptr ); 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 */ assert( my_length < IMFS_MEMFILE_BYTES_PER_BLOCK ); if ( my_length ) { block_ptr = IMFS_memfile_get_block_pointer( the_jnode, block, 0 ); assert( block_ptr ); if ( !block_ptr ) return copied; memcpy( dest, &(*block_ptr)[ 0 ], my_length ); copied += my_length; } IMFS_update_atime( the_jnode ); return copied; } /* * 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 */ assert( the_jnode ); if ( !the_jnode ) rtems_set_errno_and_return_minus_one( EIO ); assert( the_jnode->type == IMFS_MEMORY_FILE ); if ( the_jnode->type != IMFS_MEMORY_FILE ) rtems_set_errno_and_return_minus_one( EIO ); /* * Error check arguments */ assert( source ); if ( !source ) rtems_set_errno_and_return_minus_one( EINVAL ); /* * If there is nothing to write, then quick exit. */ my_length = length; if ( !my_length ) rtems_set_errno_and_return_minus_one( EINVAL ); /* * 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 + length; if ( last_byte > the_jnode->info.file.size ) { status = IMFS_memfile_extend( the_jnode, last_byte ); if ( status ) rtems_set_errno_and_return_minus_one( ENOSPC ); } 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 ); assert( block_ptr ); if ( !block_ptr ) return copied; #if 0 fprintf(stdout, "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 ); assert( block_ptr ); 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 */ 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 ); assert( block_ptr ); 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_atime_mtime_update( the_jnode ); return copied; } /* * IMFS_memfile_get_block_pointer * * This routine looks up the block pointer associated with the given block * number. If that block has not been allocated and "malloc_it" is * TRUE, then the block is allocated. Otherwise, it is an error. */ #if 0 block_p *IMFS_memfile_get_block_pointer_DEBUG( IMFS_jnode_t *the_jnode, unsigned int block, int malloc_it ); block_p *IMFS_memfile_get_block_pointer( IMFS_jnode_t *the_jnode, unsigned int block, int malloc_it ) { block_p *p; p = IMFS_memfile_get_block_pointer_DEBUG( the_jnode, block, malloc_it ); fprintf(stdout, "(%d -> %p) ", block, p ); return p; } block_p *IMFS_memfile_get_block_pointer_DEBUG( #else 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 */ assert( the_jnode ); if ( !the_jnode ) return NULL; assert( the_jnode->type == IMFS_MEMORY_FILE ); if ( the_jnode->type != IMFS_MEMORY_FILE ) return NULL; info = &the_jnode->info.file; my_block = block; /* * Is the block number in the simple indirect portion? */ if ( my_block <= LAST_INDIRECT ) { #if 0 fprintf(stdout, "(s %d) ", block ); fflush(stdout); #endif 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 ) { #if 0 fprintf(stdout, "(d %d) ", block ); fflush(stdout); #endif 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; #if 0 fprintf(stdout, "(d %d %d %d %d %p %p) ", block, my_block, doubly, singly, p, &p[singly] ); fflush(stdout); #endif return (block_p *)&p[ singly ]; } #if 0 fprintf(stdout, "(t %d) ", block ); fflush(stdout); #endif /* * 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; #if 0 fprintf(stdout, "(t %d %d %d %d %d) ", block, my_block, triply, doubly, singly ); fflush(stdout); #endif p1 = (block_p *) p[ triply ]; if ( !p1 ) return 0; p2 = (block_p *)p1[ doubly ]; if ( !p ) return 0; return (block_p *)&p2[ singly ]; } /* * This means the requested block number is out of range. */ return 0; } /* * memfile_alloc_block * * Allocate a block for an in-memory file. */ int memfile_blocks_allocated = 0; void *memfile_alloc_block(void) { void *memory; memory = (void *)calloc(1, IMFS_MEMFILE_BYTES_PER_BLOCK); if ( memory ) memfile_blocks_allocated++; return memory; } /* * memfile_free_block * * Free a block from an in-memory file. */ void memfile_free_block( void *memory ) { #if 0 fprintf(stdout, "(d %p) ", memory ); fflush(stdout); #endif free(memory); memfile_blocks_allocated--; } /* * memfile_rmnod * * This routine is available from the optable to remove a node * from the IMFS file system. */ int memfile_rmnod( rtems_filesystem_location_info_t *pathloc /* IN */ ) { IMFS_jnode_t *the_jnode; the_jnode = (IMFS_jnode_t *) pathloc->node_access; /* * Take the node out of the parent's chain that contains this node */ if ( the_jnode->Parent != NULL ) { Chain_Extract( (Chain_Node *) the_jnode ); the_jnode->Parent = NULL; } /* * Decrement the link counter and see if we can free the space. */ the_jnode->st_nlink--; IMFS_update_ctime( the_jnode ); return memfile_check_rmnod( the_jnode ); } int memfile_check_rmnod( IMFS_jnode_t *the_jnode ){ /* * The file cannot be open and the link must be less than 1 to free. */ if ( !rtems_libio_is_file_open( the_jnode ) && (the_jnode->st_nlink < 1) ) { /* * Is the rtems_filesystem_current is this node? */ if ( rtems_filesystem_current.node_access == the_jnode ) rtems_filesystem_current.node_access = NULL; /* * Free memory associated with a memory file. */ if (the_jnode->type != IMFS_LINEAR_FILE) IMFS_memfile_remove( the_jnode ); free( the_jnode ); } return 0; }