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(size: sizeof(z_stream), GFP_KERNEL); |
27 | if (stream == NULL) |
28 | goto failed; |
29 | stream->workspace = vmalloc(size: 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(objp: stream); |
38 | return ERR_PTR(error: -ENOMEM); |
39 | } |
40 | |
41 | |
42 | static void zlib_free(void *strm) |
43 | { |
44 | z_stream *stream = strm; |
45 | |
46 | if (stream) |
47 | vfree(addr: stream->workspace); |
48 | kfree(objp: 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: &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(actor: output); |
63 | stream->avail_in = 0; |
64 | |
65 | if (IS_ERR(ptr: stream->next_out)) { |
66 | error = PTR_ERR(ptr: 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: &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(actor: output); |
93 | if (IS_ERR(ptr: stream->next_out)) { |
94 | error = PTR_ERR(ptr: 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(strm: 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(actor: output); |
120 | |
121 | if (!error) |
122 | if (zlib_inflateEnd(strm: 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 | |