On Wednesday, August 16, 2017, Nicolas Pitre wrote:
> Two new capabilities are introduced here:
> 
> - The ability to store some blocks uncompressed.
> 
> - The ability to locate blocks anywhere.
> 
> Those capabilities can be used independently, but the combination
> opens the possibility for execute-in-place (XIP) of program text segments
> that must remain uncompressed, and in the MMU case, must have a specific
> alignment.  It is even possible to still have the writable data segments
> from the same file compressed as they have to be copied into RAM anyway.
> 
> This is achieved by giving special meanings to some unused block pointer
> bits while remaining compatible with legacy cramfs images.
> 
> Signed-off-by: Nicolas Pitre <[email protected]>
> ---
>  fs/cramfs/README               | 31 ++++++++++++++-
>  fs/cramfs/inode.c              | 87 +++++++++++++++++++++++++++++++++----
> -----
>  include/uapi/linux/cramfs_fs.h | 20 +++++++++-
>  3 files changed, 118 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/cramfs/README b/fs/cramfs/README
> index 9d4e7ea311..d71b27e0ff 100644
> --- a/fs/cramfs/README
> +++ b/fs/cramfs/README
> @@ -49,17 +49,46 @@ same as the start of the (i+1)'th <block> if there is
> one).  The first
>  <block> immediately follows the last <block_pointer> for the file.
>  <block_pointer>s are each 32 bits long.
> 
> +When the CRAMFS_FLAG_EXT_BLOCK_POINTERS capability bit is set, each
> +<block_pointer>'s top bits may contain special flags as follows:
> +
> +CRAMFS_BLK_FLAG_UNCOMPRESSED (bit 31):
> +     The block data is not compressed and should be copied verbatim.
> +
> +CRAMFS_BLK_FLAG_DIRECT_PTR (bit 30):
> +     The <block_pointer> stores the actual block start offset and not
> +     its end, shifted right by 2 bits. The block must therefore be
> +     aligned to a 4-byte boundary. The block size is either blksize
> +     if CRAMFS_BLK_FLAG_UNCOMPRESSED is also specified, otherwise
> +     the compressed data length is included in the first 2 bytes of
> +     the block data. This is used to allow discontiguous data layout
> +     and specific data block alignments e.g. for XIP applications.
> +
> +
>  The order of <file_data>'s is a depth-first descent of the directory
>  tree, i.e. the same order as `find -size +0 \( -type f -o -type l \)
>  -print'.
> 
> 
>  <block>: The i'th <block> is the output of zlib's compress function
> -applied to the i'th blksize-sized chunk of the input data.
> +applied to the i'th blksize-sized chunk of the input data if the
> +corresponding CRAMFS_BLK_FLAG_UNCOMPRESSED <block_ptr> bit is not set,
> +otherwise it is the input data directly.
>  (For the last <block> of the file, the input may of course be smaller.)
>  Each <block> may be a different size.  (See <block_pointer> above.)
> +
>  <block>s are merely byte-aligned, not generally u32-aligned.
> 
> +When CRAMFS_BLK_FLAG_DIRECT_PTR is specified then the corresponding
> +<block> may be located anywhere and not necessarily contiguous with
> +the previous/next blocks. In that case it is minimally u32-aligned.
> +If CRAMFS_BLK_FLAG_UNCOMPRESSED is also specified then the size is always
> +blksize except for the last block which is limited by the file length.
> +If CRAMFS_BLK_FLAG_DIRECT_PTR is set and CRAMFS_BLK_FLAG_UNCOMPRESSED
> +is not set then the first 2 bytes of the block contains the size of the
> +remaining block data as this cannot be determined from the placement of
> +logically adjacent blocks.
> +
> 
>  Holes
>  -----
> diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
> index 393eb27ef4..b825ae162c 100644
> --- a/fs/cramfs/inode.c
> +++ b/fs/cramfs/inode.c
> @@ -636,33 +636,84 @@ static int cramfs_readpage(struct file *file, struct
> page *page)
>       if (page->index < maxblock) {
>               struct super_block *sb = inode->i_sb;
>               u32 blkptr_offset = OFFSET(inode) + page->index*4;
> -             u32 start_offset, compr_len;
> +             u32 block_ptr, block_start, block_len;
> +             bool uncompressed, direct;
> 
> -             start_offset = OFFSET(inode) + maxblock*4;
>               mutex_lock(&read_mutex);
> -             if (page->index)
> -                     start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4,
> -                             4);
> -             compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) -
> -                     start_offset);
> -             mutex_unlock(&read_mutex);
> +             block_ptr = *(u32 *) cramfs_read(sb, blkptr_offset, 4);
> +             uncompressed = (block_ptr & CRAMFS_BLK_FLAG_UNCOMPRESSED);
> +             direct = (block_ptr & CRAMFS_BLK_FLAG_DIRECT_PTR);
> +             block_ptr &= ~CRAMFS_BLK_FLAGS;
> +
> +             if (direct) {
> +                     /*
> +                      * The block pointer is an absolute start pointer,
> +                      * shifted by 2 bits. The size is included in the
> +                      * first 2 bytes of the data block when compressed,
> +                      * or PAGE_SIZE otherwise.
> +                      */
> +                     block_start = block_ptr << 2;
> +                     if (uncompressed) {
> +                             block_len = PAGE_SIZE;
> +                             /* if last block: cap to file length */
> +                             if (page->index == maxblock - 1)
> +                                     block_len = 
> offset_in_page(inode->i_size);
> +                     } else {
> +                             block_len = *(u16 *)
> +                                     cramfs_read(sb, block_start, 2);
> +                             block_start += 2;
> +                     }
> +             } else {
> +                     /*
> +                      * The block pointer indicates one past the end of
> +                      * the current block (start of next block). If this
> +                      * is the first block then it starts where the block
> +                      * pointer table ends, otherwise its start comes
> +                      * from the previous block's pointer.
> +                      */
> +                     block_start = OFFSET(inode) + maxblock*4;
> +                     if (page->index)
> +                             block_start = *(u32 *)
> +                                     cramfs_read(sb, blkptr_offset-4, 4);
> +                     /* Beware... previous ptr might be a direct ptr */
> +                     if (unlikely(block_start & CRAMFS_BLK_FLAG_DIRECT_PTR))
> {
> +                             /* See comments on earlier code. */
> +                             u32 prev_start = block_start;
> +                            block_start = prev_start & ~CRAMFS_BLK_FLAGS;
> +                            block_start <<= 2;
> +                             if (prev_start & CRAMFS_BLK_FLAG_UNCOMPRESSED) {
> +                                     block_start += PAGE_SIZE;
> +                             } else {
> +                                     block_len = *(u16 *)
> +                                             cramfs_read(sb, block_start, 2);
> +                                     block_start += 2 + block_len;
> +                             }
> +                     }
> +                     block_start &= ~CRAMFS_BLK_FLAGS;
> +                     block_len = block_ptr - block_start;
> +             }
> 
> -             if (compr_len == 0)
> +             if (block_len == 0)
>                       ; /* hole */
> -             else if (unlikely(compr_len > (PAGE_SIZE << 1))) {
> -                     pr_err("bad compressed blocksize %u\n",
> -                             compr_len);
> +             else if (unlikely(block_len > 2*PAGE_SIZE ||
> +                               (uncompressed && block_len > PAGE_SIZE))) {
> +                     mutex_unlock(&read_mutex);
> +                     pr_err("bad data blocksize %u\n", block_len);
>                       goto err;
> +             } else if (uncompressed) {
> +                     memcpy(pgdata,
> +                            cramfs_read(sb, block_start, block_len),
> +                            block_len);
> +                     bytes_filled = block_len;
>               } else {
> -                     mutex_lock(&read_mutex);
>                       bytes_filled = cramfs_uncompress_block(pgdata,
>                                PAGE_SIZE,
> -                              cramfs_read(sb, start_offset, compr_len),
> -                              compr_len);
> -                     mutex_unlock(&read_mutex);
> -                     if (unlikely(bytes_filled < 0))
> -                             goto err;
> +                              cramfs_read(sb, block_start, block_len),
> +                              block_len);
>               }
> +             mutex_unlock(&read_mutex);
> +             if (unlikely(bytes_filled < 0))
> +                     goto err;
>       }
> 
>       memset(pgdata + bytes_filled, 0, PAGE_SIZE - bytes_filled);
> diff --git a/include/uapi/linux/cramfs_fs.h
> b/include/uapi/linux/cramfs_fs.h
> index e4611a9b92..ed250aa372 100644
> --- a/include/uapi/linux/cramfs_fs.h
> +++ b/include/uapi/linux/cramfs_fs.h
> @@ -73,6 +73,7 @@ struct cramfs_super {
>  #define CRAMFS_FLAG_HOLES            0x00000100      /* support for holes */
>  #define CRAMFS_FLAG_WRONG_SIGNATURE  0x00000200      /* reserved */
>  #define CRAMFS_FLAG_SHIFTED_ROOT_OFFSET      0x00000400      /* shifted root 
> fs
> */
> +#define CRAMFS_FLAG_EXT_BLOCK_POINTERS       0x00000800      /* block pointer
> extensions */
> 
>  /*
>   * Valid values in super.flags.  Currently we refuse to mount
> @@ -82,7 +83,24 @@ struct cramfs_super {
>  #define CRAMFS_SUPPORTED_FLAGS       ( 0x000000ff \
>                               | CRAMFS_FLAG_HOLES \
>                               | CRAMFS_FLAG_WRONG_SIGNATURE \
> -                             | CRAMFS_FLAG_SHIFTED_ROOT_OFFSET )
> +                             | CRAMFS_FLAG_SHIFTED_ROOT_OFFSET \
> +                             | CRAMFS_FLAG_EXT_BLOCK_POINTERS )
> 
> +/*
> + * Block pointer flags
> + *
> + * The maximum block offset that needs to be represented is roughly:
> + * 


trailing whitespace


-Chris

Reply via email to