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 if (IS_ERR(stream->next_out)) { 66 error = PTR_ERR(stream->next_out); 67 goto finish; 68 } 69 70 for (;;) { 71 int zlib_err; 72 73 if (stream->avail_in == 0) { 74 const void *data; 75 int avail; 76 77 if (!bio_next_segment(bio, &iter_all)) { 78 /* Z_STREAM_END must be reached. */ 79 error = -EIO; 80 break; 81 } 82 83 avail = min(length, ((int)bvec->bv_len) - offset); 84 data = bvec_virt(bvec); 85 length -= avail; 86 stream->next_in = data + offset; 87 stream->avail_in = avail; 88 offset = 0; 89 } 90 91 if (stream->avail_out == 0) { 92 stream->next_out = squashfs_next_page(output); 93 if (IS_ERR(stream->next_out)) { 94 error = PTR_ERR(stream->next_out); 95 break; 96 } else if (stream->next_out != NULL) 97 stream->avail_out = PAGE_SIZE; 98 } 99 100 if (!zlib_init) { 101 zlib_err = zlib_inflateInit(stream); 102 if (zlib_err != Z_OK) { 103 error = -EIO; 104 break; 105 } 106 zlib_init = 1; 107 } 108 109 zlib_err = zlib_inflate(stream, Z_SYNC_FLUSH); 110 if (zlib_err == Z_STREAM_END) 111 break; 112 if (zlib_err != Z_OK) { 113 error = -EIO; 114 break; 115 } 116 } 117 118 finish: 119 squashfs_finish_page(output); 120 121 if (!error) 122 if (zlib_inflateEnd(stream) != Z_OK) 123 error = -EIO; 124 125 return error ? error : stream->total_out; 126 } 127 128 const struct squashfs_decompressor squashfs_zlib_comp_ops = { 129 .init = zlib_init, 130 .free = zlib_free, 131 .decompress = zlib_uncompress, 132 .id = ZLIB_COMPRESSION, 133 .name = "zlib", 134 .alloc_buffer = 1, 135 .supported = 1 136 }; 137 138