18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Squashfs - a compressed read only filesystem for Linux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 68c2ecf20Sopenharmony_ci * Phillip Lougher <phillip@squashfs.org.uk> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * zlib_wrapper.c 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/mutex.h> 138c2ecf20Sopenharmony_ci#include <linux/bio.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/zlib.h> 168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "squashfs_fs.h" 198c2ecf20Sopenharmony_ci#include "squashfs_fs_sb.h" 208c2ecf20Sopenharmony_ci#include "squashfs.h" 218c2ecf20Sopenharmony_ci#include "decompressor.h" 228c2ecf20Sopenharmony_ci#include "page_actor.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void *zlib_init(struct squashfs_sb_info *dummy, void *buff) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); 278c2ecf20Sopenharmony_ci if (stream == NULL) 288c2ecf20Sopenharmony_ci goto failed; 298c2ecf20Sopenharmony_ci stream->workspace = vmalloc(zlib_inflate_workspacesize()); 308c2ecf20Sopenharmony_ci if (stream->workspace == NULL) 318c2ecf20Sopenharmony_ci goto failed; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci return stream; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cifailed: 368c2ecf20Sopenharmony_ci ERROR("Failed to allocate zlib workspace\n"); 378c2ecf20Sopenharmony_ci kfree(stream); 388c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void zlib_free(void *strm) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci z_stream *stream = strm; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (stream) 478c2ecf20Sopenharmony_ci vfree(stream->workspace); 488c2ecf20Sopenharmony_ci kfree(stream); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, 538c2ecf20Sopenharmony_ci struct bio *bio, int offset, int length, 548c2ecf20Sopenharmony_ci struct squashfs_page_actor *output) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct bvec_iter_all iter_all = {}; 578c2ecf20Sopenharmony_ci struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 588c2ecf20Sopenharmony_ci int zlib_init = 0, error = 0; 598c2ecf20Sopenharmony_ci z_stream *stream = strm; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci stream->avail_out = PAGE_SIZE; 628c2ecf20Sopenharmony_ci stream->next_out = squashfs_first_page(output); 638c2ecf20Sopenharmony_ci stream->avail_in = 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci for (;;) { 668c2ecf20Sopenharmony_ci int zlib_err; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (stream->avail_in == 0) { 698c2ecf20Sopenharmony_ci const void *data; 708c2ecf20Sopenharmony_ci int avail; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!bio_next_segment(bio, &iter_all)) { 738c2ecf20Sopenharmony_ci /* Z_STREAM_END must be reached. */ 748c2ecf20Sopenharmony_ci error = -EIO; 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci avail = min(length, ((int)bvec->bv_len) - offset); 798c2ecf20Sopenharmony_ci data = page_address(bvec->bv_page) + bvec->bv_offset; 808c2ecf20Sopenharmony_ci length -= avail; 818c2ecf20Sopenharmony_ci stream->next_in = data + offset; 828c2ecf20Sopenharmony_ci stream->avail_in = avail; 838c2ecf20Sopenharmony_ci offset = 0; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (stream->avail_out == 0) { 878c2ecf20Sopenharmony_ci stream->next_out = squashfs_next_page(output); 888c2ecf20Sopenharmony_ci if (stream->next_out != NULL) 898c2ecf20Sopenharmony_ci stream->avail_out = PAGE_SIZE; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (!zlib_init) { 938c2ecf20Sopenharmony_ci zlib_err = zlib_inflateInit(stream); 948c2ecf20Sopenharmony_ci if (zlib_err != Z_OK) { 958c2ecf20Sopenharmony_ci error = -EIO; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci zlib_init = 1; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci zlib_err = zlib_inflate(stream, Z_SYNC_FLUSH); 1028c2ecf20Sopenharmony_ci if (zlib_err == Z_STREAM_END) 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci if (zlib_err != Z_OK) { 1058c2ecf20Sopenharmony_ci error = -EIO; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci squashfs_finish_page(output); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!error) 1138c2ecf20Sopenharmony_ci if (zlib_inflateEnd(stream) != Z_OK) 1148c2ecf20Sopenharmony_ci error = -EIO; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return error ? error : stream->total_out; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ciconst struct squashfs_decompressor squashfs_zlib_comp_ops = { 1208c2ecf20Sopenharmony_ci .init = zlib_init, 1218c2ecf20Sopenharmony_ci .free = zlib_free, 1228c2ecf20Sopenharmony_ci .decompress = zlib_uncompress, 1238c2ecf20Sopenharmony_ci .id = ZLIB_COMPRESSION, 1248c2ecf20Sopenharmony_ci .name = "zlib", 1258c2ecf20Sopenharmony_ci .supported = 1 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 128