forked from Imagelibrary/rtems
readdir support. The next step is to add a mount table and configure either the miniIMFS or the full IMFS at the application level.
1059 lines
22 KiB
C
1059 lines
22 KiB
C
/*
|
|
* 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-1998.
|
|
* On-Line Applications Research Corporation (OAR).
|
|
* Copyright assigned to U.S. Government, 1994.
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.OARcorp.com/rtems/license.html.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/libio.h>
|
|
#include "imfs.h"
|
|
#include "libio_.h"
|
|
|
|
#define MEMFILE_STATIC
|
|
|
|
/*
|
|
* Set of operations handlers for operations on memfile entities.
|
|
*/
|
|
|
|
rtems_filesystem_file_handlers_r IMFS_memfile_handlers = {
|
|
memfile_open,
|
|
memfile_close,
|
|
memfile_read,
|
|
memfile_write,
|
|
memfile_ioctl,
|
|
memfile_lseek,
|
|
IMFS_stat,
|
|
IMFS_fchmod,
|
|
memfile_ftruncate,
|
|
NULL, /* fpathconf */
|
|
NULL, /* fsync */
|
|
IMFS_fdatasync,
|
|
IMFS_fcntl
|
|
};
|
|
|
|
/*
|
|
* 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 int IMFS_memfile_read(
|
|
IMFS_jnode_t *the_jnode,
|
|
off_t start,
|
|
unsigned char *destination,
|
|
unsigned int length
|
|
);
|
|
|
|
MEMFILE_STATIC int IMFS_memfile_write(
|
|
IMFS_jnode_t *the_jnode,
|
|
off_t start,
|
|
const unsigned char *source,
|
|
unsigned int length
|
|
);
|
|
|
|
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,
|
|
unsigned32 flag,
|
|
unsigned32 mode
|
|
)
|
|
{
|
|
IMFS_jnode_t *the_jnode;
|
|
|
|
the_jnode = iop->file_info;
|
|
|
|
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;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* memfile_read
|
|
*
|
|
* This routine processes the read() system call.
|
|
*/
|
|
|
|
int memfile_read(
|
|
rtems_libio_t *iop,
|
|
void *buffer,
|
|
unsigned32 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.
|
|
*/
|
|
|
|
int memfile_write(
|
|
rtems_libio_t *iop,
|
|
const void *buffer,
|
|
unsigned32 count
|
|
)
|
|
{
|
|
IMFS_jnode_t *the_jnode;
|
|
int 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,
|
|
unsigned32 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 (IMFS_memfile_extend( the_jnode, iop->offset ))
|
|
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 )
|
|
set_errno_and_return_minus_one( EIO );
|
|
|
|
assert( the_jnode->type == IMFS_MEMORY_FILE );
|
|
if ( the_jnode->type != IMFS_MEMORY_FILE )
|
|
set_errno_and_return_minus_one( EIO );
|
|
|
|
if ( new_length >= IMFS_MEMFILE_MAXIMUM_SIZE )
|
|
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 );
|
|
}
|
|
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 )
|
|
set_errno_and_return_minus_one( EIO );
|
|
|
|
assert( the_jnode->type == IMFS_MEMORY_FILE );
|
|
if ( the_jnode->type != IMFS_MEMORY_FILE )
|
|
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
|
|
printf( "%d %p", block, block_entry_ptr );
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
memory = memfile_alloc_block();
|
|
assert( memory );
|
|
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 ; i<entries ; i++ ) {
|
|
if ( b[i] ) {
|
|
memfile_free_block( b[i] );
|
|
b[i] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now that all the blocks in the block table are free, we can
|
|
* free the block table itself.
|
|
*/
|
|
|
|
memfile_free_block( *block_table );
|
|
*block_table = 0;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
int 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
|
|
*/
|
|
|
|
assert( the_jnode );
|
|
if ( !the_jnode )
|
|
set_errno_and_return_minus_one( EIO );
|
|
|
|
assert( the_jnode->type == IMFS_MEMORY_FILE );
|
|
if ( the_jnode->type != IMFS_MEMORY_FILE )
|
|
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 ; 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];
|
|
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 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 int 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 )
|
|
set_errno_and_return_minus_one( EIO );
|
|
|
|
assert( the_jnode->type == IMFS_MEMORY_FILE );
|
|
if ( the_jnode->type != IMFS_MEMORY_FILE )
|
|
set_errno_and_return_minus_one( EIO );
|
|
|
|
/*
|
|
* Error checks on arguments
|
|
*/
|
|
|
|
assert( dest );
|
|
if ( !dest )
|
|
set_errno_and_return_minus_one( EINVAL );
|
|
|
|
/*
|
|
* If there is nothing to read, then quick exit.
|
|
*/
|
|
|
|
my_length = length;
|
|
if ( !my_length )
|
|
set_errno_and_return_minus_one( EINVAL );
|
|
|
|
/*
|
|
* 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 int 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 )
|
|
set_errno_and_return_minus_one( EIO );
|
|
|
|
assert( the_jnode->type == IMFS_MEMORY_FILE );
|
|
if ( the_jnode->type != IMFS_MEMORY_FILE )
|
|
set_errno_and_return_minus_one( EIO );
|
|
|
|
/*
|
|
* Error check arguments
|
|
*/
|
|
|
|
assert( source );
|
|
if ( !source )
|
|
set_errno_and_return_minus_one( EINVAL );
|
|
|
|
|
|
/*
|
|
* If there is nothing to write, then quick exit.
|
|
*/
|
|
|
|
my_length = length;
|
|
if ( !my_length )
|
|
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 )
|
|
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
|
|
printf( "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
|
|
printf( "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
|
|
printf( "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 );
|
|
printf( "(%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
|
|
printf( "(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
|
|
printf( "(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
|
|
printf( "(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
|
|
printf( "(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
|
|
printf( "(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
|
|
printf( "(d %p) ", memory );
|
|
fflush(stdout);
|
|
#endif
|
|
free(memory);
|
|
memfile_blocks_allocated--;
|
|
}
|
|
|