RE: [PATCH v2 3/5] cramfs: implement uncompressed and arbitrary data block positioning

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <nico@xxxxxxxxxx>
> ---
>  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




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux