1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Squashfs - a compressed read only filesystem for Linux 4 * 5 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 6 * Phillip Lougher <phillip@squashfs.org.uk> 7 * 8 * zlib_wrapper.c 9 */ 10 11 12 #include <linux/mutex.h> 13 #include <linux/bio.h> 14 #include <linux/slab.h> 15 #include <linux/zlib.h> 16 #include <linux/vmalloc.h> 17 18 #include "squashfs_fs.h" 19 #include "squashfs_fs_sb.h" 20 #include "squashfs.h" 21 #include "decompressor.h" 22 #include "page_actor.h" 23 24 static void *zlib_init(struct squashfs_sb_info *dummy, void *buff) 25 { 26 z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); 27 if (stream == NULL) 28 goto failed; 29 stream->workspace = vmalloc(zlib_inflate_workspacesize()); 30 if (stream->workspace == NULL) 31 goto failed; 32 33 return stream; 34 35 failed: 36 ERROR("Failed to allocate zlib workspace\n"); 37 kfree(stream); 38 return ERR_PTR(-ENOMEM); 39 } 40 41 42 static void zlib_free(void *strm) 43 { 44 z_stream *stream = strm; 45 46 if (stream) 47 vfree(stream->workspace); 48 kfree(stream); 49 } 50 51 52 static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, 53 struct bio *bio, int offset, int length, 54 struct squashfs_page_actor *output) 55 { 56 struct bvec_iter_all iter_all = {}; 57 struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 58 int zlib_init = 0, error = 0; 59 z_stream *stream = strm; 60 61 stream->avail_out = PAGE_SIZE; 62 stream->next_out = squashfs_first_page(output); 63 stream->avail_in = 0; 64 65 for (;;) { 66 int zlib_err; 67 68 if (stream->avail_in == 0) { 69 const void *data; 70 int avail; 71 72 if (!bio_next_segment(bio, &iter_all)) { 73 /* Z_STREAM_END must be reached. */ 74 error = -EIO; 75 break; 76 } 77 78 avail = min(length, ((int)bvec->bv_len) - offset); 79 data = page_address(bvec->bv_page) + bvec->bv_offset; 80 length -= avail; 81 stream->next_in = data + offset; 82 stream->avail_in = avail; 83 offset = 0; 84 } 85 86 if (stream->avail_out == 0) { 87 stream->next_out = squashfs_next_page(output); 88 if (stream->next_out != NULL) 89 stream->avail_out = PAGE_SIZE; 90 } 91 92 if (!zlib_init) { 93 zlib_err = zlib_inflateInit(stream); 94 if (zlib_err != Z_OK) { 95 error = -EIO; 96 break; 97 } 98 zlib_init = 1; 99 } 100 101 zlib_err = zlib_inflate(stream, Z_SYNC_FLUSH); 102 if (zlib_err == Z_STREAM_END) 103 break; 104 if (zlib_err != Z_OK) { 105 error = -EIO; 106 break; 107 } 108 } 109 110 squashfs_finish_page(output); 111 112 if (!error) 113 if (zlib_inflateEnd(stream) != Z_OK) 114 error = -EIO; 115 116 return error ? error : stream->total_out; 117 } 118 119 const struct squashfs_decompressor squashfs_zlib_comp_ops = { 120 .init = zlib_init, 121 .free = zlib_free, 122 .decompress = zlib_uncompress, 123 .id = ZLIB_COMPRESSION, 124 .name = "zlib", 125 .supported = 1 126 }; 127 128