Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:17

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup IMFS
0007  *
0008  * @brief IMFS Memory File Handlers
0009  */
0010 
0011 /*
0012  *  COPYRIGHT (c) 1989-2010.
0013  *  On-Line Applications Research Corporation (OAR).
0014  *
0015  * Redistribution and use in source and binary forms, with or without
0016  * modification, are permitted provided that the following conditions
0017  * are met:
0018  * 1. Redistributions of source code must retain the above copyright
0019  *    notice, this list of conditions and the following disclaimer.
0020  * 2. Redistributions in binary form must reproduce the above copyright
0021  *    notice, this list of conditions and the following disclaimer in the
0022  *    documentation and/or other materials provided with the distribution.
0023  *
0024  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0025  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0026  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0027  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0028  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0029  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0030  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0031  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0032  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0033  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0034  * POSSIBILITY OF SUCH DAMAGE.
0035  */
0036 
0037 #ifdef HAVE_CONFIG_H
0038 #include "config.h"
0039 #endif
0040 
0041 #include <rtems/imfsimpl.h>
0042 
0043 #include <stdlib.h>
0044 #include <string.h>
0045 
0046 /*
0047  *  Prototypes of private routines
0048  */
0049 static int IMFS_memfile_extend(
0050    IMFS_memfile_t *memfile,
0051    bool            zero_fill,
0052    off_t           new_length
0053 );
0054 
0055 static int IMFS_memfile_addblock(
0056    IMFS_memfile_t *memfile,
0057    unsigned int    block
0058 );
0059 
0060 static void IMFS_memfile_remove_block(
0061    IMFS_memfile_t *memfile,
0062    unsigned int    block
0063 );
0064 
0065 static block_p *IMFS_memfile_get_block_pointer(
0066    IMFS_memfile_t *memfile,
0067    unsigned int    block,
0068    int             malloc_it
0069 );
0070 
0071 static ssize_t IMFS_memfile_read(
0072    IMFS_file_t     *file,
0073    off_t            start,
0074    unsigned char   *destination,
0075    unsigned int     length
0076 );
0077 
0078 static void *memfile_alloc_block(void);
0079 
0080 static void memfile_free_block(
0081   void *memory
0082 );
0083 
0084 static ssize_t memfile_read(
0085   rtems_libio_t *iop,
0086   void          *buffer,
0087   size_t         count
0088 )
0089 {
0090   IMFS_file_t *file = IMFS_iop_to_file( iop );
0091   ssize_t      status;
0092 
0093   status = IMFS_memfile_read( file, iop->offset, buffer, count );
0094 
0095   if ( status > 0 )
0096     iop->offset += status;
0097 
0098   return status;
0099 }
0100 
0101 static ssize_t memfile_write(
0102   rtems_libio_t *iop,
0103   const void    *buffer,
0104   size_t         count
0105 )
0106 {
0107   IMFS_memfile_t *memfile = IMFS_iop_to_memfile( iop );
0108   ssize_t         status;
0109 
0110   if (rtems_libio_iop_is_append(iop))
0111     iop->offset = memfile->File.size;
0112 
0113   status = IMFS_memfile_write( memfile, iop->offset, buffer, count );
0114 
0115   if ( status > 0 )
0116     iop->offset += status;
0117 
0118   return status;
0119 }
0120 
0121 /*
0122  *  memfile_stat
0123  *
0124  *  This IMFS_stat() can be used.
0125  */
0126 
0127 static int memfile_ftruncate(
0128   rtems_libio_t        *iop,
0129   off_t                 length
0130 )
0131 {
0132   IMFS_memfile_t *memfile = IMFS_iop_to_memfile( iop );
0133 
0134   /*
0135    *  POSIX 1003.1b does not specify what happens if you truncate a file
0136    *  and the new length is greater than the current size.  We treat this
0137    *  as an extend operation.
0138    */
0139 
0140   if ( length > memfile->File.size )
0141     return IMFS_memfile_extend( memfile, true, length );
0142 
0143   /*
0144    *  The in-memory files do not currently reclaim memory until the file is
0145    *  deleted.  So we leave the previously allocated blocks in place for
0146    *  future use and just set the length.
0147    */
0148   memfile->File.size = length;
0149 
0150   IMFS_mtime_ctime_update( &memfile->File.Node );
0151 
0152   return 0;
0153 }
0154 
0155 /*
0156  *  IMFS_memfile_extend
0157  *
0158  *  This routine insures that the in-memory file is of the length
0159  *  specified.  If necessary, it will allocate memory blocks to
0160  *  extend the file.
0161  */
0162 static int IMFS_memfile_extend(
0163    IMFS_memfile_t *memfile,
0164    bool            zero_fill,
0165    off_t           new_length
0166 )
0167 {
0168   unsigned int   block;
0169   unsigned int   new_blocks;
0170   unsigned int   old_blocks;
0171   unsigned int   offset;
0172 
0173   /*
0174    *  Perform internal consistency checks
0175    */
0176   IMFS_assert( memfile );
0177 
0178   /*
0179    *  Verify new file size is supported
0180    */
0181   if ( new_length >= IMFS_MEMFILE_MAXIMUM_SIZE )
0182     rtems_set_errno_and_return_minus_one( EFBIG );
0183 
0184   /*
0185    *  Verify new file size is actually larger than current size
0186    */
0187   if ( new_length <= memfile->File.size )
0188     return 0;
0189 
0190   /*
0191    *  Calculate the number of range of blocks to allocate
0192    */
0193   new_blocks = new_length / IMFS_MEMFILE_BYTES_PER_BLOCK;
0194   old_blocks = memfile->File.size / IMFS_MEMFILE_BYTES_PER_BLOCK;
0195   offset = memfile->File.size - old_blocks * IMFS_MEMFILE_BYTES_PER_BLOCK;
0196 
0197   /*
0198    *  Now allocate each of those blocks.
0199    */
0200   for ( block=old_blocks ; block<=new_blocks ; block++ ) {
0201     if ( !IMFS_memfile_addblock( memfile, block ) ) {
0202        if ( zero_fill ) {
0203           size_t count = IMFS_MEMFILE_BYTES_PER_BLOCK - offset;
0204           block_p *block_ptr =
0205             IMFS_memfile_get_block_pointer( memfile, block, 0 );
0206 
0207           memset( &(*block_ptr) [offset], 0, count);
0208           offset = 0;
0209        }
0210     } else {
0211        for ( ; block>old_blocks ; block-- ) {
0212          IMFS_memfile_remove_block( memfile, block );
0213        }
0214        IMFS_memfile_remove_block( memfile, old_blocks );
0215        rtems_set_errno_and_return_minus_one( ENOSPC );
0216     }
0217   }
0218 
0219   /*
0220    *  Set the new length of the file.
0221    */
0222   memfile->File.size = new_length;
0223 
0224   IMFS_mtime_ctime_update( &memfile->File.Node );
0225 
0226   return 0;
0227 }
0228 
0229 /*
0230  *  IMFS_memfile_addblock
0231  *
0232  *  This routine adds a single block to the specified in-memory file.
0233  */
0234 static int IMFS_memfile_addblock(
0235    IMFS_memfile_t *memfile,
0236    unsigned int    block
0237 )
0238 {
0239   block_p  memory;
0240   block_p *block_entry_ptr;
0241 
0242   IMFS_assert( memfile );
0243 
0244   /*
0245    * Obtain the pointer for the specified block number
0246    */
0247   block_entry_ptr = IMFS_memfile_get_block_pointer( memfile, block, 1 );
0248   if ( !block_entry_ptr )
0249     return 1;
0250 
0251   if ( *block_entry_ptr )
0252     return 0;
0253 
0254   /*
0255    *  There is no memory for this block number so allocate it.
0256    */
0257   memory = memfile_alloc_block();
0258   if ( !memory )
0259     return 1;
0260 
0261   *block_entry_ptr = memory;
0262   return 0;
0263 }
0264 
0265 /*
0266  *  IMFS_memfile_remove_block
0267  *
0268  *  This routine removes the specified block from the in-memory file.
0269  *
0270  *  NOTE:  This is a support routine and is called only to remove
0271  *         the last block or set of blocks in a file.  Removing a
0272  *         block from the middle of a file would be exceptionally
0273  *         dangerous and the results unpredictable.
0274  */
0275 static void IMFS_memfile_remove_block(
0276    IMFS_memfile_t *memfile,
0277    unsigned int    block
0278 )
0279 {
0280   block_p *block_ptr;
0281   block_p  ptr;
0282 
0283   block_ptr = IMFS_memfile_get_block_pointer( memfile, block, 0 );
0284   if ( block_ptr ) {
0285     ptr = *block_ptr;
0286     *block_ptr = 0;
0287     memfile_free_block( ptr );
0288   }
0289 }
0290 
0291 /*
0292  *  memfile_free_blocks_in_table
0293  *
0294  *  This is a support routine for IMFS_memfile_remove.  It frees all the
0295  *  blocks in one of the indirection tables.
0296  */
0297 static void memfile_free_blocks_in_table(
0298   block_p **block_table,
0299   int       entries
0300 )
0301 {
0302   int      i;
0303   block_p *b;
0304 
0305   /*
0306    *  Perform internal consistency checks
0307    */
0308   IMFS_assert( block_table );
0309 
0310   /*
0311    *  Now go through all the slots in the table and free the memory.
0312    */
0313   b = *block_table;
0314 
0315   for ( i=0 ; i<entries ; i++ ) {
0316     if ( b[i] ) {
0317       memfile_free_block( b[i] );
0318       b[i] = 0;
0319     }
0320   }
0321 
0322   /*
0323    *  Now that all the blocks in the block table are free, we can
0324    *  free the block table itself.
0325    */
0326   memfile_free_block( *block_table );
0327   *block_table = 0;
0328 }
0329 
0330 /*
0331  *  IMFS_memfile_destroy
0332  *
0333  *  This routine frees all memory associated with an in memory file.
0334  *
0335  *  NOTE:  This is an exceptionally conservative implementation.
0336  *         It will check EVERY pointer which is non-NULL and insure
0337  *         any child non-NULL pointers are freed.  Optimistically, all that
0338  *         is necessary is to scan until a NULL pointer is found.  There
0339  *         should be no allocated data past that point.
0340  *
0341  *         In experimentation on the powerpc simulator, it was noted
0342  *         that using blocks which held 128 slots versus 16 slots made
0343  *         a significant difference in the performance of this routine.
0344  *
0345  *         Regardless until the IMFS implementation is proven, it
0346  *         is better to stick to simple, easy to understand algorithms.
0347  */
0348 static void IMFS_memfile_destroy(
0349  IMFS_jnode_t  *the_jnode
0350 )
0351 {
0352   IMFS_memfile_t  *memfile;
0353   int              i;
0354   int              j;
0355   unsigned int     to_free;
0356   block_p         *p;
0357 
0358   memfile = (IMFS_memfile_t *) the_jnode;
0359 
0360   /*
0361    *  Perform internal consistency checks
0362    */
0363   IMFS_assert( memfile );
0364 
0365   /*
0366    *  Eventually this could be set smarter at each call to
0367    *  memfile_free_blocks_in_table to greatly speed this up.
0368    */
0369   to_free = IMFS_MEMFILE_BLOCK_SLOTS;
0370 
0371   /*
0372    *  Now start freeing blocks in this order:
0373    *    + indirect
0374    *    + doubly indirect
0375    *    + triply indirect
0376    */
0377 
0378   if ( memfile->indirect ) {
0379     memfile_free_blocks_in_table( &memfile->indirect, to_free );
0380   }
0381 
0382   if ( memfile->doubly_indirect ) {
0383     for ( i=0 ; i<IMFS_MEMFILE_BLOCK_SLOTS ; i++ ) {
0384       if ( memfile->doubly_indirect[i] ) {
0385         memfile_free_blocks_in_table(
0386          (block_p **)&memfile->doubly_indirect[i], to_free );
0387       }
0388     }
0389     memfile_free_blocks_in_table( &memfile->doubly_indirect, to_free );
0390 
0391   }
0392 
0393   if ( memfile->triply_indirect ) {
0394     for ( i=0 ; i<IMFS_MEMFILE_BLOCK_SLOTS ; i++ ) {
0395       p = (block_p *) memfile->triply_indirect[i];
0396       if ( !p )  /* ensure we have a valid pointer */
0397          break;
0398       for ( j=0 ; j<IMFS_MEMFILE_BLOCK_SLOTS ; j++ ) {
0399         if ( p[j] ) {
0400           memfile_free_blocks_in_table( (block_p **)&p[j], to_free);
0401         }
0402       }
0403       memfile_free_blocks_in_table(
0404         (block_p **)&memfile->triply_indirect[i], to_free );
0405     }
0406     memfile_free_blocks_in_table(
0407         (block_p **)&memfile->triply_indirect, to_free );
0408   }
0409 
0410   IMFS_node_destroy_default( the_jnode );
0411 }
0412 
0413 /*
0414  *  IMFS_memfile_read
0415  *
0416  *  This routine read from memory file pointed to by the_jnode into
0417  *  the specified data buffer specified by destination.  The file
0418  *  is NOT extended.  An offset greater than the length of the file
0419  *  is considered an error.  Read from an offset for more bytes than
0420  *  are between the offset and the end of the file will result in
0421  *  reading the data between offset and the end of the file (truncated
0422  *  read).
0423  */
0424 static ssize_t IMFS_memfile_read(
0425    IMFS_file_t     *file,
0426    off_t            start,
0427    unsigned char   *destination,
0428    unsigned int     length
0429 )
0430 {
0431   block_p             *block_ptr;
0432   unsigned int         block;
0433   unsigned int         my_length;
0434   unsigned int         to_copy = 0;
0435   unsigned int         last_byte;
0436   unsigned int         copied;
0437   unsigned int         start_offset;
0438   unsigned char       *dest;
0439 
0440   dest = destination;
0441 
0442   /*
0443    *  Perform internal consistency checks
0444    */
0445   IMFS_assert( file );
0446   IMFS_assert( dest );
0447 
0448   /*
0449    *  Linear files (as created from a tar file are easier to handle
0450    *  than block files).
0451    */
0452   my_length = length;
0453 
0454   /*
0455    *  If the last byte we are supposed to read is past the end of this
0456    *  in memory file, then shorten the length to read.
0457    */
0458   last_byte = start + length;
0459   if ( last_byte > file->Memfile.File.size )
0460     my_length = file->Memfile.File.size - start;
0461 
0462   copied = 0;
0463 
0464   /*
0465    *  Three phases to the read:
0466    *    + possibly the last part of one block
0467    *    + all of zero of more blocks
0468    *    + possibly the first part of one block
0469    */
0470 
0471   /*
0472    *  Phase 1: possibly the last part of one block
0473    */
0474   start_offset = start % IMFS_MEMFILE_BYTES_PER_BLOCK;
0475   block = start / IMFS_MEMFILE_BYTES_PER_BLOCK;
0476   if ( start_offset )  {
0477     to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK - start_offset;
0478     if ( to_copy > my_length )
0479       to_copy = my_length;
0480     block_ptr = IMFS_memfile_get_block_pointer( &file->Memfile, block, 0 );
0481     if ( !block_ptr )
0482       return copied;
0483     memcpy( dest, &(*block_ptr)[ start_offset ], to_copy );
0484     dest += to_copy;
0485     block++;
0486     my_length -= to_copy;
0487     copied += to_copy;
0488   }
0489 
0490   /*
0491    *  Phase 2: all of zero of more blocks
0492    */
0493   to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK;
0494   while ( my_length >= IMFS_MEMFILE_BYTES_PER_BLOCK ) {
0495     block_ptr = IMFS_memfile_get_block_pointer( &file->Memfile, block, 0 );
0496     if ( !block_ptr )
0497       return copied;
0498     memcpy( dest, &(*block_ptr)[ 0 ], to_copy );
0499     dest += to_copy;
0500     block++;
0501     my_length -= to_copy;
0502     copied += to_copy;
0503   }
0504 
0505   /*
0506    *  Phase 3: possibly the first part of one block
0507    */
0508   IMFS_assert( my_length < IMFS_MEMFILE_BYTES_PER_BLOCK );
0509 
0510   if ( my_length ) {
0511     block_ptr = IMFS_memfile_get_block_pointer( &file->Memfile, block, 0 );
0512     if ( !block_ptr )
0513       return copied;
0514     memcpy( dest, &(*block_ptr)[ 0 ], my_length );
0515     copied += my_length;
0516   }
0517 
0518   IMFS_update_atime( &file->Node );
0519 
0520   return copied;
0521 }
0522 
0523 /*
0524  *  IMFS_memfile_write
0525  *
0526  *  This routine writes the specified data buffer into the in memory
0527  *  file pointed to by memfile.  The file is extended as needed.
0528  */
0529 ssize_t IMFS_memfile_write(
0530    IMFS_memfile_t        *memfile,
0531    off_t                  start,
0532    const unsigned char   *source,
0533    unsigned int           length
0534 )
0535 {
0536   block_p             *block_ptr;
0537   unsigned int         block;
0538   int                  status;
0539   unsigned int         my_length;
0540   unsigned int         to_copy = 0;
0541   unsigned int         last_byte;
0542   unsigned int         start_offset;
0543   int                  copied;
0544   const unsigned char *src;
0545 
0546   src = source;
0547 
0548   /*
0549    *  Perform internal consistency checks
0550    */
0551   IMFS_assert( source );
0552   IMFS_assert( memfile );
0553 
0554   my_length = length;
0555   /*
0556    *  If the last byte we are supposed to write is past the end of this
0557    *  in memory file, then extend the length.
0558    */
0559 
0560   last_byte = start + my_length;
0561   if ( last_byte > memfile->File.size ) {
0562     bool zero_fill = start > memfile->File.size;
0563 
0564     status = IMFS_memfile_extend( memfile, zero_fill, last_byte );
0565     if ( status )
0566       return status;
0567   }
0568 
0569   copied = 0;
0570 
0571   /*
0572    *  Three phases to the write:
0573    *    + possibly the last part of one block
0574    *    + all of zero of more blocks
0575    *    + possibly the first part of one block
0576    */
0577 
0578   /*
0579    *  Phase 1: possibly the last part of one block
0580    */
0581   start_offset = start % IMFS_MEMFILE_BYTES_PER_BLOCK;
0582   block = start / IMFS_MEMFILE_BYTES_PER_BLOCK;
0583   if ( start_offset )  {
0584     to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK - start_offset;
0585     if ( to_copy > my_length )
0586       to_copy = my_length;
0587     block_ptr = IMFS_memfile_get_block_pointer( memfile, block, 0 );
0588     if ( !block_ptr )
0589       return copied;
0590     #if 0
0591       fprintf(
0592         stderr,
0593         "write %d at %d in %d: %*s\n",
0594         to_copy,
0595         start_offset,
0596         block,
0597         to_copy,
0598         src
0599       );
0600     #endif
0601     memcpy( &(*block_ptr)[ start_offset ], src, to_copy );
0602     src += to_copy;
0603     block++;
0604     my_length -= to_copy;
0605     copied += to_copy;
0606   }
0607 
0608   /*
0609    *  Phase 2: all of zero of more blocks
0610    */
0611 
0612   to_copy = IMFS_MEMFILE_BYTES_PER_BLOCK;
0613   while ( my_length >= IMFS_MEMFILE_BYTES_PER_BLOCK ) {
0614     block_ptr = IMFS_memfile_get_block_pointer( memfile, block, 0 );
0615     if ( !block_ptr )
0616       return copied;
0617     #if 0
0618       fprintf(stdout, "write %d in %d: %*s\n", to_copy, block, to_copy, src );
0619     #endif
0620     memcpy( &(*block_ptr)[ 0 ], src, to_copy );
0621     src += to_copy;
0622     block++;
0623     my_length -= to_copy;
0624     copied += to_copy;
0625   }
0626 
0627   /*
0628    *  Phase 3: possibly the first part of one block
0629    */
0630   IMFS_assert( my_length < IMFS_MEMFILE_BYTES_PER_BLOCK );
0631 
0632   to_copy = my_length;
0633   if ( my_length ) {
0634     block_ptr = IMFS_memfile_get_block_pointer( memfile, block, 0 );
0635     if ( !block_ptr )
0636       return copied;
0637     #if 0
0638     fprintf(stdout, "write %d in %d: %*s\n", to_copy, block, to_copy, src );
0639     #endif
0640     memcpy( &(*block_ptr)[ 0 ], src, my_length );
0641     copied += to_copy;
0642   }
0643 
0644   IMFS_mtime_ctime_update( &memfile->File.Node );
0645 
0646   return copied;
0647 }
0648 
0649 /*
0650  *  IMFS_memfile_get_block_pointer
0651  *
0652  *  This routine looks up the block pointer associated with the given block
0653  *  number.  If that block has not been allocated and "malloc_it" is
0654  *  TRUE, then the block is allocated.  Otherwise, it is an error.
0655  */
0656 #if 0
0657 block_p *IMFS_memfile_get_block_pointer_DEBUG(
0658    IMFS_jnode_t   *the_jnode,
0659    unsigned int    block,
0660    int             malloc_it
0661 );
0662 
0663 block_p *IMFS_memfile_get_block_pointer(
0664    IMFS_jnode_t   *the_jnode,
0665    unsigned int    block,
0666    int             malloc_it
0667 )
0668 {
0669   block_p *p;
0670 
0671   p = IMFS_memfile_get_block_pointer_DEBUG( the_jnode, block, malloc_it );
0672   fprintf(stdout, "(%d -> %p) ", block, p );
0673   return p;
0674 }
0675 
0676 block_p *IMFS_memfile_get_block_pointer_DEBUG(
0677 #else
0678 block_p *IMFS_memfile_get_block_pointer(
0679 #endif
0680    IMFS_memfile_t *memfile,
0681    unsigned int    block,
0682    int             malloc_it
0683 )
0684 {
0685   unsigned int    my_block;
0686   unsigned int    singly;
0687   unsigned int    doubly;
0688   unsigned int    triply;
0689   block_p        *p;
0690   block_p        *p1;
0691   block_p        *p2;
0692 
0693   /*
0694    *  Perform internal consistency checks
0695    */
0696   IMFS_assert( memfile );
0697 
0698   my_block = block;
0699 
0700   /*
0701    *  Is the block number in the simple indirect portion?
0702    */
0703   if ( my_block <= LAST_INDIRECT ) {
0704     p = memfile->indirect;
0705 
0706     if ( malloc_it ) {
0707 
0708       if ( !p ) {
0709         p = memfile_alloc_block();
0710         if ( !p )
0711            return 0;
0712         memfile->indirect = p;
0713       }
0714       return &memfile->indirect[ my_block ];
0715     }
0716 
0717     if ( !p )
0718       return 0;
0719 
0720     return &memfile->indirect[ my_block ];
0721   }
0722 
0723   /*
0724    *  Is the block number in the doubly indirect portion?
0725    */
0726 
0727   if ( my_block <= LAST_DOUBLY_INDIRECT ) {
0728     my_block -= FIRST_DOUBLY_INDIRECT;
0729 
0730     singly = my_block % IMFS_MEMFILE_BLOCK_SLOTS;
0731     doubly = my_block / IMFS_MEMFILE_BLOCK_SLOTS;
0732 
0733     p = memfile->doubly_indirect;
0734     if ( malloc_it ) {
0735 
0736       if ( !p ) {
0737         p = memfile_alloc_block();
0738         if ( !p )
0739            return 0;
0740         memfile->doubly_indirect = p;
0741       }
0742 
0743       p1 = (block_p *)p[ doubly ];
0744       if ( !p1 ) {
0745         p1 = memfile_alloc_block();
0746         if ( !p1 )
0747            return 0;
0748         p[ doubly ] = (block_p) p1;
0749       }
0750 
0751       return (block_p *)&p1[ singly ];
0752     }
0753 
0754     if ( !p )
0755       return 0;
0756 
0757     p = (block_p *)p[ doubly ];
0758     if ( !p )
0759       return 0;
0760 
0761     return (block_p *)&p[ singly ];
0762   }
0763 
0764   /*
0765    *  Is the block number in the triply indirect portion?
0766    */
0767   if ( my_block <= LAST_TRIPLY_INDIRECT ) {
0768     my_block -= FIRST_TRIPLY_INDIRECT;
0769 
0770     singly = my_block % IMFS_MEMFILE_BLOCK_SLOTS;
0771     doubly = my_block / IMFS_MEMFILE_BLOCK_SLOTS;
0772     triply = doubly / IMFS_MEMFILE_BLOCK_SLOTS;
0773     doubly %= IMFS_MEMFILE_BLOCK_SLOTS;
0774 
0775     p = memfile->triply_indirect;
0776 
0777     if ( malloc_it ) {
0778       if ( !p ) {
0779         p = memfile_alloc_block();
0780         if ( !p )
0781            return 0;
0782         memfile->triply_indirect = p;
0783       }
0784 
0785       p1 = (block_p *) p[ triply ];
0786       if ( !p1 ) {
0787         p1 = memfile_alloc_block();
0788         if ( !p1 )
0789            return 0;
0790         p[ triply ] = (block_p) p1;
0791       }
0792 
0793       p2 = (block_p *)p1[ doubly ];
0794       if ( !p2 ) {
0795         p2 = memfile_alloc_block();
0796         if ( !p2 )
0797            return 0;
0798         p1[ doubly ] = (block_p) p2;
0799       }
0800       return (block_p *)&p2[ singly ];
0801     }
0802 
0803     if ( !p )
0804       return 0;
0805 
0806     p1 = (block_p *) p[ triply ];
0807     if ( !p1 )
0808       return 0;
0809 
0810     p2 = (block_p *)p1[ doubly ];
0811     if ( !p2 )
0812       return 0;
0813 
0814     return (block_p *)&p2[ singly ];
0815   }
0816 
0817   /*
0818    *  This means the requested block number is out of range.
0819    */
0820   return 0;
0821 }
0822 
0823 /*
0824  *  memfile_alloc_block
0825  *
0826  *  Allocate a block for an in-memory file.
0827  */
0828 int memfile_blocks_allocated = 0;
0829 
0830 void *memfile_alloc_block(void)
0831 {
0832   void *memory;
0833 
0834   memory = (void *)calloc(1, IMFS_MEMFILE_BYTES_PER_BLOCK);
0835   if ( memory )
0836     memfile_blocks_allocated++;
0837 
0838   return memory;
0839 }
0840 
0841 /*
0842  *  memfile_free_block
0843  *
0844  *  Free a block from an in-memory file.
0845  */
0846 void memfile_free_block(
0847   void *memory
0848 )
0849 {
0850   free(memory);
0851   memfile_blocks_allocated--;
0852 }
0853 
0854 static const rtems_filesystem_file_handlers_r IMFS_memfile_handlers = {
0855   .open_h = rtems_filesystem_default_open,
0856   .close_h = rtems_filesystem_default_close,
0857   .read_h = memfile_read,
0858   .write_h = memfile_write,
0859   .ioctl_h = rtems_filesystem_default_ioctl,
0860   .lseek_h = rtems_filesystem_default_lseek_file,
0861   .fstat_h = IMFS_stat_file,
0862   .ftruncate_h = memfile_ftruncate,
0863   .fsync_h = rtems_filesystem_default_fsync_or_fdatasync_success,
0864   .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync_success,
0865   .fcntl_h = rtems_filesystem_default_fcntl,
0866   .kqfilter_h = rtems_filesystem_default_kqfilter,
0867   .mmap_h = rtems_filesystem_default_mmap,
0868   .poll_h = rtems_filesystem_default_poll,
0869   .readv_h = rtems_filesystem_default_readv,
0870   .writev_h = rtems_filesystem_default_writev
0871 };
0872 
0873 const IMFS_mknod_control IMFS_mknod_control_memfile = {
0874   {
0875     .handlers = &IMFS_memfile_handlers,
0876     .node_initialize = IMFS_node_initialize_default,
0877     .node_remove = IMFS_node_remove_default,
0878     .node_destroy = IMFS_memfile_destroy
0879   },
0880   .node_size = sizeof( IMFS_file_t )
0881 };