2010-12-09 05:02:29 +03:00
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright ( c ) 2002 , 2003 , 2004 , 2005 , 2006 , 2007 , 2008 , 2009 , 2010
2011-05-26 13:39:56 +04:00
* Phillip Lougher < phillip @ squashfs . org . uk >
2010-12-09 05:02:29 +03:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2 ,
* or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* xz_wrapper . c
*/
# include <linux/mutex.h>
# include <linux/buffer_head.h>
# include <linux/slab.h>
# include <linux/xz.h>
2011-02-28 18:31:46 +03:00
# include <linux/bitops.h>
2010-12-09 05:02:29 +03:00
# include "squashfs_fs.h"
# include "squashfs_fs_sb.h"
# include "squashfs.h"
# include "decompressor.h"
2013-11-18 06:59:12 +04:00
# include "page_actor.h"
2010-12-09 05:02:29 +03:00
struct squashfs_xz {
struct xz_dec * state ;
struct xz_buf buf ;
} ;
2013-11-13 06:56:26 +04:00
struct disk_comp_opts {
2011-02-28 18:31:46 +03:00
__le32 dictionary_size ;
__le32 flags ;
} ;
2013-11-13 06:56:26 +04:00
struct comp_opts {
int dict_size ;
} ;
static void * squashfs_xz_comp_opts ( struct squashfs_sb_info * msblk ,
void * buff , int len )
2010-12-09 05:02:29 +03:00
{
2013-11-13 06:56:26 +04:00
struct disk_comp_opts * comp_opts = buff ;
struct comp_opts * opts ;
int err = 0 , n ;
opts = kmalloc ( sizeof ( * opts ) , GFP_KERNEL ) ;
if ( opts = = NULL ) {
err = - ENOMEM ;
goto out2 ;
}
2011-02-28 18:31:46 +03:00
if ( comp_opts ) {
/* check compressor options are the expected length */
if ( len < sizeof ( * comp_opts ) ) {
err = - EIO ;
2013-11-13 06:56:26 +04:00
goto out ;
2011-02-28 18:31:46 +03:00
}
2013-11-13 06:56:26 +04:00
opts - > dict_size = le32_to_cpu ( comp_opts - > dictionary_size ) ;
2011-02-28 18:31:46 +03:00
/* the dictionary size should be 2^n or 2^n+2^(n+1) */
2013-11-13 06:56:26 +04:00
n = ffs ( opts - > dict_size ) - 1 ;
if ( opts - > dict_size ! = ( 1 < < n ) & & opts - > dict_size ! = ( 1 < < n ) +
2011-02-28 18:31:46 +03:00
( 1 < < ( n + 1 ) ) ) {
err = - EIO ;
2013-11-13 06:56:26 +04:00
goto out ;
2011-02-28 18:31:46 +03:00
}
2013-11-13 06:56:26 +04:00
} else
/* use defaults */
opts - > dict_size = max_t ( int , msblk - > block_size ,
SQUASHFS_METADATA_SIZE ) ;
2011-02-28 18:31:46 +03:00
2013-11-13 06:56:26 +04:00
return opts ;
out :
kfree ( opts ) ;
out2 :
return ERR_PTR ( err ) ;
}
static void * squashfs_xz_init ( struct squashfs_sb_info * msblk , void * buff )
{
struct comp_opts * comp_opts = buff ;
struct squashfs_xz * stream ;
int err ;
2010-12-09 05:02:29 +03:00
2011-02-28 18:31:46 +03:00
stream = kmalloc ( sizeof ( * stream ) , GFP_KERNEL ) ;
if ( stream = = NULL ) {
err = - ENOMEM ;
2010-12-09 05:02:29 +03:00
goto failed ;
2011-02-28 18:31:46 +03:00
}
2010-12-09 05:02:29 +03:00
2013-11-13 06:56:26 +04:00
stream - > state = xz_dec_init ( XZ_PREALLOC , comp_opts - > dict_size ) ;
2011-02-28 18:31:46 +03:00
if ( stream - > state = = NULL ) {
kfree ( stream ) ;
err = - ENOMEM ;
2010-12-09 05:02:29 +03:00
goto failed ;
2011-02-28 18:31:46 +03:00
}
2010-12-09 05:02:29 +03:00
return stream ;
failed :
2011-02-28 18:31:46 +03:00
ERROR ( " Failed to initialise xz decompressor \n " ) ;
return ERR_PTR ( err ) ;
2010-12-09 05:02:29 +03:00
}
static void squashfs_xz_free ( void * strm )
{
struct squashfs_xz * stream = strm ;
if ( stream ) {
xz_dec_end ( stream - > state ) ;
kfree ( stream ) ;
}
}
2013-11-13 06:56:26 +04:00
static int squashfs_xz_uncompress ( struct squashfs_sb_info * msblk , void * strm ,
2013-11-18 06:59:12 +04:00
struct buffer_head * * bh , int b , int offset , int length ,
struct squashfs_page_actor * output )
2010-12-09 05:02:29 +03:00
{
enum xz_ret xz_err ;
2013-11-18 06:59:12 +04:00
int avail , total = 0 , k = 0 ;
2013-11-13 06:56:26 +04:00
struct squashfs_xz * stream = strm ;
2010-12-09 05:02:29 +03:00
xz_dec_reset ( stream - > state ) ;
stream - > buf . in_pos = 0 ;
stream - > buf . in_size = 0 ;
stream - > buf . out_pos = 0 ;
stream - > buf . out_size = PAGE_CACHE_SIZE ;
2013-11-18 06:59:12 +04:00
stream - > buf . out = squashfs_first_page ( output ) ;
2010-12-09 05:02:29 +03:00
do {
if ( stream - > buf . in_pos = = stream - > buf . in_size & & k < b ) {
avail = min ( length , msblk - > devblksize - offset ) ;
length - = avail ;
stream - > buf . in = bh [ k ] - > b_data + offset ;
stream - > buf . in_size = avail ;
stream - > buf . in_pos = 0 ;
offset = 0 ;
}
2013-11-18 06:59:12 +04:00
if ( stream - > buf . out_pos = = stream - > buf . out_size ) {
stream - > buf . out = squashfs_next_page ( output ) ;
if ( stream - > buf . out ! = NULL ) {
stream - > buf . out_pos = 0 ;
total + = PAGE_CACHE_SIZE ;
}
2010-12-09 05:02:29 +03:00
}
xz_err = xz_dec_run ( stream - > state , & stream - > buf ) ;
if ( stream - > buf . in_pos = = stream - > buf . in_size & & k < b )
put_bh ( bh [ k + + ] ) ;
} while ( xz_err = = XZ_OK ) ;
2013-11-18 06:59:12 +04:00
squashfs_finish_page ( output ) ;
2013-11-13 06:56:26 +04:00
if ( xz_err ! = XZ_STREAM_END | | k < b )
goto out ;
2010-12-09 05:02:29 +03:00
2013-11-13 06:56:26 +04:00
return total + stream - > buf . out_pos ;
2010-12-09 05:02:29 +03:00
2013-11-13 06:56:26 +04:00
out :
2010-12-09 05:02:29 +03:00
for ( ; k < b ; k + + )
put_bh ( bh [ k ] ) ;
return - EIO ;
}
const struct squashfs_decompressor squashfs_xz_comp_ops = {
. init = squashfs_xz_init ,
2013-11-13 06:56:26 +04:00
. comp_opts = squashfs_xz_comp_opts ,
2010-12-09 05:02:29 +03:00
. free = squashfs_xz_free ,
. decompress = squashfs_xz_uncompress ,
. id = XZ_COMPRESSION ,
. name = " xz " ,
. supported = 1
} ;