aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhillip Lougher <phillip@squashfs.org.uk>2013-11-17 21:59:12 -0500
committerPhillip Lougher <phillip@squashfs.org.uk>2013-11-19 22:59:01 -0500
commit846b730e99518a1c9945afcb2afbe4d08a02ed80 (patch)
treec70eb16cbec15e5bfa1db80c5f18e062673331a9
parentd208383d640727b70cd6689bc17e67e9b5ebf4ff (diff)
Squashfs: Generalise paging handling in the decompressors
Further generalise the decompressors by adding a page handler abstraction. This adds helpers to allow the decompressors to access and process the output buffers in an implementation independant manner. This allows different types of output buffer to be passed to the decompressors, with the implementation specific aspects handled at decompression time, but without the knowledge being held in the decompressor wrapper code. This will allow the decompressors to handle Squashfs cache buffers, and page cache pages. This patch adds the abstraction and an implementation for the caches. Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk> Reviewed-by: Minchan Kim <minchan@kernel.org>
-rw-r--r--fs/squashfs/block.c27
-rw-r--r--fs/squashfs/cache.c28
-rw-r--r--fs/squashfs/decompressor.c14
-rw-r--r--fs/squashfs/decompressor.h5
-rw-r--r--fs/squashfs/decompressor_multi.c7
-rw-r--r--fs/squashfs/decompressor_multi_percpu.c9
-rw-r--r--fs/squashfs/decompressor_single.c9
-rw-r--r--fs/squashfs/lzo_wrapper.c27
-rw-r--r--fs/squashfs/page_actor.h49
-rw-r--r--fs/squashfs/squashfs.h8
-rw-r--r--fs/squashfs/squashfs_fs_sb.h1
-rw-r--r--fs/squashfs/xz_wrapper.c22
-rw-r--r--fs/squashfs/zlib_wrapper.c24
13 files changed, 163 insertions, 67 deletions
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
index 4dd402597f22..0cea9b9236d0 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -36,6 +36,7 @@
36#include "squashfs_fs_sb.h" 36#include "squashfs_fs_sb.h"
37#include "squashfs.h" 37#include "squashfs.h"
38#include "decompressor.h" 38#include "decompressor.h"
39#include "page_actor.h"
39 40
40/* 41/*
41 * Read the metadata block length, this is stored in the first two 42 * Read the metadata block length, this is stored in the first two
@@ -86,16 +87,16 @@ static struct buffer_head *get_block_length(struct super_block *sb,
86 * generated a larger block - this does occasionally happen with compression 87 * generated a larger block - this does occasionally happen with compression
87 * algorithms). 88 * algorithms).
88 */ 89 */
89int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, 90int squashfs_read_data(struct super_block *sb, u64 index, int length,
90 int length, u64 *next_index, int srclength, int pages) 91 u64 *next_index, struct squashfs_page_actor *output)
91{ 92{
92 struct squashfs_sb_info *msblk = sb->s_fs_info; 93 struct squashfs_sb_info *msblk = sb->s_fs_info;
93 struct buffer_head **bh; 94 struct buffer_head **bh;
94 int offset = index & ((1 << msblk->devblksize_log2) - 1); 95 int offset = index & ((1 << msblk->devblksize_log2) - 1);
95 u64 cur_index = index >> msblk->devblksize_log2; 96 u64 cur_index = index >> msblk->devblksize_log2;
96 int bytes, compressed, b = 0, k = 0, page = 0, avail, i; 97 int bytes, compressed, b = 0, k = 0, avail, i;
97 98
98 bh = kcalloc(((srclength + msblk->devblksize - 1) 99 bh = kcalloc(((output->length + msblk->devblksize - 1)
99 >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); 100 >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
100 if (bh == NULL) 101 if (bh == NULL)
101 return -ENOMEM; 102 return -ENOMEM;
@@ -111,9 +112,9 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
111 *next_index = index + length; 112 *next_index = index + length;
112 113
113 TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", 114 TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
114 index, compressed ? "" : "un", length, srclength); 115 index, compressed ? "" : "un", length, output->length);
115 116
116 if (length < 0 || length > srclength || 117 if (length < 0 || length > output->length ||
117 (index + length) > msblk->bytes_used) 118 (index + length) > msblk->bytes_used)
118 goto read_failure; 119 goto read_failure;
119 120
@@ -145,7 +146,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
145 TRACE("Block @ 0x%llx, %scompressed size %d\n", index, 146 TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
146 compressed ? "" : "un", length); 147 compressed ? "" : "un", length);
147 148
148 if (length < 0 || length > srclength || 149 if (length < 0 || length > output->length ||
149 (index + length) > msblk->bytes_used) 150 (index + length) > msblk->bytes_used)
150 goto block_release; 151 goto block_release;
151 152
@@ -165,8 +166,8 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
165 } 166 }
166 167
167 if (compressed) { 168 if (compressed) {
168 length = squashfs_decompress(msblk, buffer, bh, b, offset, 169 length = squashfs_decompress(msblk, bh, b, offset, length,
169 length, srclength, pages); 170 output);
170 if (length < 0) 171 if (length < 0)
171 goto read_failure; 172 goto read_failure;
172 } else { 173 } else {
@@ -174,19 +175,20 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
174 * Block is uncompressed. 175 * Block is uncompressed.
175 */ 176 */
176 int in, pg_offset = 0; 177 int in, pg_offset = 0;
178 void *data = squashfs_first_page(output);
177 179
178 for (bytes = length; k < b; k++) { 180 for (bytes = length; k < b; k++) {
179 in = min(bytes, msblk->devblksize - offset); 181 in = min(bytes, msblk->devblksize - offset);
180 bytes -= in; 182 bytes -= in;
181 while (in) { 183 while (in) {
182 if (pg_offset == PAGE_CACHE_SIZE) { 184 if (pg_offset == PAGE_CACHE_SIZE) {
183 page++; 185 data = squashfs_next_page(output);
184 pg_offset = 0; 186 pg_offset = 0;
185 } 187 }
186 avail = min_t(int, in, PAGE_CACHE_SIZE - 188 avail = min_t(int, in, PAGE_CACHE_SIZE -
187 pg_offset); 189 pg_offset);
188 memcpy(buffer[page] + pg_offset, 190 memcpy(data + pg_offset, bh[k]->b_data + offset,
189 bh[k]->b_data + offset, avail); 191 avail);
190 in -= avail; 192 in -= avail;
191 pg_offset += avail; 193 pg_offset += avail;
192 offset += avail; 194 offset += avail;
@@ -194,6 +196,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
194 offset = 0; 196 offset = 0;
195 put_bh(bh[k]); 197 put_bh(bh[k]);
196 } 198 }
199 squashfs_finish_page(output);
197 } 200 }
198 201
199 kfree(bh); 202 kfree(bh);
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
index af0b73802592..1cb70a0b2168 100644
--- a/fs/squashfs/cache.c
+++ b/fs/squashfs/cache.c
@@ -56,6 +56,7 @@
56#include "squashfs_fs.h" 56#include "squashfs_fs.h"
57#include "squashfs_fs_sb.h" 57#include "squashfs_fs_sb.h"
58#include "squashfs.h" 58#include "squashfs.h"
59#include "page_actor.h"
59 60
60/* 61/*
61 * Look-up block in cache, and increment usage count. If not in cache, read 62 * Look-up block in cache, and increment usage count. If not in cache, read
@@ -119,9 +120,8 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,
119 entry->error = 0; 120 entry->error = 0;
120 spin_unlock(&cache->lock); 121 spin_unlock(&cache->lock);
121 122
122 entry->length = squashfs_read_data(sb, entry->data, 123 entry->length = squashfs_read_data(sb, block, length,
123 block, length, &entry->next_index, 124 &entry->next_index, entry->actor);
124 cache->block_size, cache->pages);
125 125
126 spin_lock(&cache->lock); 126 spin_lock(&cache->lock);
127 127
@@ -220,6 +220,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)
220 kfree(cache->entry[i].data[j]); 220 kfree(cache->entry[i].data[j]);
221 kfree(cache->entry[i].data); 221 kfree(cache->entry[i].data);
222 } 222 }
223 kfree(cache->entry[i].actor);
223 } 224 }
224 225
225 kfree(cache->entry); 226 kfree(cache->entry);
@@ -280,6 +281,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries,
280 goto cleanup; 281 goto cleanup;
281 } 282 }
282 } 283 }
284
285 entry->actor = squashfs_page_actor_init(entry->data,
286 cache->pages, 0);
287 if (entry->actor == NULL) {
288 ERROR("Failed to allocate %s cache entry\n", name);
289 goto cleanup;
290 }
283 } 291 }
284 292
285 return cache; 293 return cache;
@@ -410,6 +418,7 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length)
410 int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 418 int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
411 int i, res; 419 int i, res;
412 void *table, *buffer, **data; 420 void *table, *buffer, **data;
421 struct squashfs_page_actor *actor;
413 422
414 table = buffer = kmalloc(length, GFP_KERNEL); 423 table = buffer = kmalloc(length, GFP_KERNEL);
415 if (table == NULL) 424 if (table == NULL)
@@ -421,19 +430,28 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length)
421 goto failed; 430 goto failed;
422 } 431 }
423 432
433 actor = squashfs_page_actor_init(data, pages, length);
434 if (actor == NULL) {
435 res = -ENOMEM;
436 goto failed2;
437 }
438
424 for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) 439 for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
425 data[i] = buffer; 440 data[i] = buffer;
426 441
427 res = squashfs_read_data(sb, data, block, length | 442 res = squashfs_read_data(sb, block, length |
428 SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages); 443 SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
429 444
430 kfree(data); 445 kfree(data);
446 kfree(actor);
431 447
432 if (res < 0) 448 if (res < 0)
433 goto failed; 449 goto failed;
434 450
435 return table; 451 return table;
436 452
453failed2:
454 kfree(data);
437failed: 455failed:
438 kfree(table); 456 kfree(table);
439 return ERR_PTR(res); 457 return ERR_PTR(res);
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
index 234291f79ba5..ac22fe73b0ad 100644
--- a/fs/squashfs/decompressor.c
+++ b/fs/squashfs/decompressor.c
@@ -30,6 +30,7 @@
30#include "squashfs_fs_sb.h" 30#include "squashfs_fs_sb.h"
31#include "decompressor.h" 31#include "decompressor.h"
32#include "squashfs.h" 32#include "squashfs.h"
33#include "page_actor.h"
33 34
34/* 35/*
35 * This file (and decompressor.h) implements a decompressor framework for 36 * This file (and decompressor.h) implements a decompressor framework for
@@ -87,6 +88,7 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags)
87{ 88{
88 struct squashfs_sb_info *msblk = sb->s_fs_info; 89 struct squashfs_sb_info *msblk = sb->s_fs_info;
89 void *buffer = NULL, *comp_opts; 90 void *buffer = NULL, *comp_opts;
91 struct squashfs_page_actor *actor = NULL;
90 int length = 0; 92 int length = 0;
91 93
92 /* 94 /*
@@ -99,9 +101,14 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags)
99 goto out; 101 goto out;
100 } 102 }
101 103
102 length = squashfs_read_data(sb, &buffer, 104 actor = squashfs_page_actor_init(&buffer, 1, 0);
103 sizeof(struct squashfs_super_block), 0, NULL, 105 if (actor == NULL) {
104 PAGE_CACHE_SIZE, 1); 106 comp_opts = ERR_PTR(-ENOMEM);
107 goto out;
108 }
109
110 length = squashfs_read_data(sb,
111 sizeof(struct squashfs_super_block), 0, NULL, actor);
105 112
106 if (length < 0) { 113 if (length < 0) {
107 comp_opts = ERR_PTR(length); 114 comp_opts = ERR_PTR(length);
@@ -112,6 +119,7 @@ static void *get_comp_opts(struct super_block *sb, unsigned short flags)
112 comp_opts = squashfs_comp_opts(msblk, buffer, length); 119 comp_opts = squashfs_comp_opts(msblk, buffer, length);
113 120
114out: 121out:
122 kfree(actor);
115 kfree(buffer); 123 kfree(buffer);
116 return comp_opts; 124 return comp_opts;
117} 125}
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
index 6cdb20a3878a..af0985321808 100644
--- a/fs/squashfs/decompressor.h
+++ b/fs/squashfs/decompressor.h
@@ -27,8 +27,9 @@ struct squashfs_decompressor {
27 void *(*init)(struct squashfs_sb_info *, void *); 27 void *(*init)(struct squashfs_sb_info *, void *);
28 void *(*comp_opts)(struct squashfs_sb_info *, void *, int); 28 void *(*comp_opts)(struct squashfs_sb_info *, void *, int);
29 void (*free)(void *); 29 void (*free)(void *);
30 int (*decompress)(struct squashfs_sb_info *, void *, void **, 30 int (*decompress)(struct squashfs_sb_info *, void *,
31 struct buffer_head **, int, int, int, int, int); 31 struct buffer_head **, int, int, int,
32 struct squashfs_page_actor *);
32 int id; 33 int id;
33 char *name; 34 char *name;
34 int supported; 35 int supported;
diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c
index 462731db5130..ae54675a3526 100644
--- a/fs/squashfs/decompressor_multi.c
+++ b/fs/squashfs/decompressor_multi.c
@@ -183,15 +183,14 @@ wait:
183} 183}
184 184
185 185
186int squashfs_decompress(struct squashfs_sb_info *msblk, 186int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
187 void **buffer, struct buffer_head **bh, int b, int offset, int length, 187 int b, int offset, int length, struct squashfs_page_actor *output)
188 int srclength, int pages)
189{ 188{
190 int res; 189 int res;
191 struct squashfs_stream *stream = msblk->stream; 190 struct squashfs_stream *stream = msblk->stream;
192 struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream); 191 struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream);
193 res = msblk->decompressor->decompress(msblk, decomp_stream->stream, 192 res = msblk->decompressor->decompress(msblk, decomp_stream->stream,
194 buffer, bh, b, offset, length, srclength, pages); 193 bh, b, offset, length, output);
195 put_decomp_stream(decomp_stream, stream); 194 put_decomp_stream(decomp_stream, stream);
196 if (res < 0) 195 if (res < 0)
197 ERROR("%s decompression failed, data probably corrupt\n", 196 ERROR("%s decompression failed, data probably corrupt\n",
diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c
index 0e7b679bc4ad..23a9c28ad8ea 100644
--- a/fs/squashfs/decompressor_multi_percpu.c
+++ b/fs/squashfs/decompressor_multi_percpu.c
@@ -74,15 +74,14 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
74 } 74 }
75} 75}
76 76
77int squashfs_decompress(struct squashfs_sb_info *msblk, 77int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
78 void **buffer, struct buffer_head **bh, int b, int offset, int length, 78 int b, int offset, int length, struct squashfs_page_actor *output)
79 int srclength, int pages)
80{ 79{
81 struct squashfs_stream __percpu *percpu = 80 struct squashfs_stream __percpu *percpu =
82 (struct squashfs_stream __percpu *) msblk->stream; 81 (struct squashfs_stream __percpu *) msblk->stream;
83 struct squashfs_stream *stream = get_cpu_ptr(percpu); 82 struct squashfs_stream *stream = get_cpu_ptr(percpu);
84 int res = msblk->decompressor->decompress(msblk, stream->stream, buffer, 83 int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
85 bh, b, offset, length, srclength, pages); 84 offset, length, output);
86 put_cpu_ptr(stream); 85 put_cpu_ptr(stream);
87 86
88 if (res < 0) 87 if (res < 0)
diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c
index f857cf6f22d4..a6c75929a00e 100644
--- a/fs/squashfs/decompressor_single.c
+++ b/fs/squashfs/decompressor_single.c
@@ -61,16 +61,15 @@ void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
61 } 61 }
62} 62}
63 63
64int squashfs_decompress(struct squashfs_sb_info *msblk, 64int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh,
65 void **buffer, struct buffer_head **bh, int b, int offset, int length, 65 int b, int offset, int length, struct squashfs_page_actor *output)
66 int srclength, int pages)
67{ 66{
68 int res; 67 int res;
69 struct squashfs_stream *stream = msblk->stream; 68 struct squashfs_stream *stream = msblk->stream;
70 69
71 mutex_lock(&stream->mutex); 70 mutex_lock(&stream->mutex);
72 res = msblk->decompressor->decompress(msblk, stream->stream, buffer, 71 res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
73 bh, b, offset, length, srclength, pages); 72 offset, length, output);
74 mutex_unlock(&stream->mutex); 73 mutex_unlock(&stream->mutex);
75 74
76 if (res < 0) 75 if (res < 0)
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
index 75c3b5779172..244b9fbfff7b 100644
--- a/fs/squashfs/lzo_wrapper.c
+++ b/fs/squashfs/lzo_wrapper.c
@@ -31,6 +31,7 @@
31#include "squashfs_fs_sb.h" 31#include "squashfs_fs_sb.h"
32#include "squashfs.h" 32#include "squashfs.h"
33#include "decompressor.h" 33#include "decompressor.h"
34#include "page_actor.h"
34 35
35struct squashfs_lzo { 36struct squashfs_lzo {
36 void *input; 37 void *input;
@@ -75,13 +76,13 @@ static void lzo_free(void *strm)
75 76
76 77
77static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, 78static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
78 void **buffer, struct buffer_head **bh, int b, int offset, int length, 79 struct buffer_head **bh, int b, int offset, int length,
79 int srclength, int pages) 80 struct squashfs_page_actor *output)
80{ 81{
81 struct squashfs_lzo *stream = strm; 82 struct squashfs_lzo *stream = strm;
82 void *buff = stream->input; 83 void *buff = stream->input, *data;
83 int avail, i, bytes = length, res; 84 int avail, i, bytes = length, res;
84 size_t out_len = srclength; 85 size_t out_len = output->length;
85 86
86 for (i = 0; i < b; i++) { 87 for (i = 0; i < b; i++) {
87 avail = min(bytes, msblk->devblksize - offset); 88 avail = min(bytes, msblk->devblksize - offset);
@@ -98,12 +99,20 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm,
98 goto failed; 99 goto failed;
99 100
100 res = bytes = (int)out_len; 101 res = bytes = (int)out_len;
101 for (i = 0, buff = stream->output; bytes && i < pages; i++) { 102 data = squashfs_first_page(output);
102 avail = min_t(int, bytes, PAGE_CACHE_SIZE); 103 buff = stream->output;
103 memcpy(buffer[i], buff, avail); 104 while (data) {
104 buff += avail; 105 if (bytes <= PAGE_CACHE_SIZE) {
105 bytes -= avail; 106 memcpy(data, buff, bytes);
107 break;
108 } else {
109 memcpy(data, buff, PAGE_CACHE_SIZE);
110 buff += PAGE_CACHE_SIZE;
111 bytes -= PAGE_CACHE_SIZE;
112 data = squashfs_next_page(output);
113 }
106 } 114 }
115 squashfs_finish_page(output);
107 116
108 return res; 117 return res;
109 118
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h
new file mode 100644
index 000000000000..5b0ba5a7133a
--- /dev/null
+++ b/fs/squashfs/page_actor.h
@@ -0,0 +1,49 @@
1#ifndef PAGE_ACTOR_H
2#define PAGE_ACTOR_H
3/*
4 * Copyright (c) 2013
5 * Phillip Lougher <phillip@squashfs.org.uk>
6 *
7 * This work is licensed under the terms of the GNU GPL, version 2. See
8 * the COPYING file in the top-level directory.
9 */
10
11struct squashfs_page_actor {
12 void **page;
13 int pages;
14 int length;
15 int next_page;
16};
17
18static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
19 int pages, int length)
20{
21 struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
22
23 if (actor == NULL)
24 return NULL;
25
26 actor->length = length ? : pages * PAGE_CACHE_SIZE;
27 actor->page = page;
28 actor->pages = pages;
29 actor->next_page = 0;
30 return actor;
31}
32
33static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
34{
35 actor->next_page = 1;
36 return actor->page[0];
37}
38
39static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
40{
41 return actor->next_page == actor->pages ? NULL :
42 actor->page[actor->next_page++];
43}
44
45static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
46{
47 /* empty */
48}
49#endif
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 2e2751df8452..6a97e63ca173 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -28,8 +28,8 @@
28#define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args) 28#define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args)
29 29
30/* block.c */ 30/* block.c */
31extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *, 31extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
32 int, int); 32 struct squashfs_page_actor *);
33 33
34/* cache.c */ 34/* cache.c */
35extern struct squashfs_cache *squashfs_cache_init(char *, int, int); 35extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
@@ -53,8 +53,8 @@ extern void *squashfs_decompressor_setup(struct super_block *, unsigned short);
53/* decompressor_xxx.c */ 53/* decompressor_xxx.c */
54extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *); 54extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *);
55extern void squashfs_decompressor_destroy(struct squashfs_sb_info *); 55extern void squashfs_decompressor_destroy(struct squashfs_sb_info *);
56extern int squashfs_decompress(struct squashfs_sb_info *, void **, 56extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **,
57 struct buffer_head **, int, int, int, int, int); 57 int, int, int, struct squashfs_page_actor *);
58extern int squashfs_max_decompressors(void); 58extern int squashfs_max_decompressors(void);
59 59
60/* export.c */ 60/* export.c */
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index 9cdcf4150d59..1da565cb50c3 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -50,6 +50,7 @@ struct squashfs_cache_entry {
50 wait_queue_head_t wait_queue; 50 wait_queue_head_t wait_queue;
51 struct squashfs_cache *cache; 51 struct squashfs_cache *cache;
52 void **data; 52 void **data;
53 struct squashfs_page_actor *actor;
53}; 54};
54 55
55struct squashfs_sb_info { 56struct squashfs_sb_info {
diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c
index 5d1d07cca6b4..c609624e4b8a 100644
--- a/fs/squashfs/xz_wrapper.c
+++ b/fs/squashfs/xz_wrapper.c
@@ -32,6 +32,7 @@
32#include "squashfs_fs_sb.h" 32#include "squashfs_fs_sb.h"
33#include "squashfs.h" 33#include "squashfs.h"
34#include "decompressor.h" 34#include "decompressor.h"
35#include "page_actor.h"
35 36
36struct squashfs_xz { 37struct squashfs_xz {
37 struct xz_dec *state; 38 struct xz_dec *state;
@@ -129,11 +130,11 @@ static void squashfs_xz_free(void *strm)
129 130
130 131
131static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, 132static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
132 void **buffer, struct buffer_head **bh, int b, int offset, int length, 133 struct buffer_head **bh, int b, int offset, int length,
133 int srclength, int pages) 134 struct squashfs_page_actor *output)
134{ 135{
135 enum xz_ret xz_err; 136 enum xz_ret xz_err;
136 int avail, total = 0, k = 0, page = 0; 137 int avail, total = 0, k = 0;
137 struct squashfs_xz *stream = strm; 138 struct squashfs_xz *stream = strm;
138 139
139 xz_dec_reset(stream->state); 140 xz_dec_reset(stream->state);
@@ -141,7 +142,7 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
141 stream->buf.in_size = 0; 142 stream->buf.in_size = 0;
142 stream->buf.out_pos = 0; 143 stream->buf.out_pos = 0;
143 stream->buf.out_size = PAGE_CACHE_SIZE; 144 stream->buf.out_size = PAGE_CACHE_SIZE;
144 stream->buf.out = buffer[page++]; 145 stream->buf.out = squashfs_first_page(output);
145 146
146 do { 147 do {
147 if (stream->buf.in_pos == stream->buf.in_size && k < b) { 148 if (stream->buf.in_pos == stream->buf.in_size && k < b) {
@@ -153,11 +154,12 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
153 offset = 0; 154 offset = 0;
154 } 155 }
155 156
156 if (stream->buf.out_pos == stream->buf.out_size 157 if (stream->buf.out_pos == stream->buf.out_size) {
157 && page < pages) { 158 stream->buf.out = squashfs_next_page(output);
158 stream->buf.out = buffer[page++]; 159 if (stream->buf.out != NULL) {
159 stream->buf.out_pos = 0; 160 stream->buf.out_pos = 0;
160 total += PAGE_CACHE_SIZE; 161 total += PAGE_CACHE_SIZE;
162 }
161 } 163 }
162 164
163 xz_err = xz_dec_run(stream->state, &stream->buf); 165 xz_err = xz_dec_run(stream->state, &stream->buf);
@@ -166,6 +168,8 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
166 put_bh(bh[k++]); 168 put_bh(bh[k++]);
167 } while (xz_err == XZ_OK); 169 } while (xz_err == XZ_OK);
168 170
171 squashfs_finish_page(output);
172
169 if (xz_err != XZ_STREAM_END || k < b) 173 if (xz_err != XZ_STREAM_END || k < b)
170 goto out; 174 goto out;
171 175
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
index bb049027d15c..8727caba6882 100644
--- a/fs/squashfs/zlib_wrapper.c
+++ b/fs/squashfs/zlib_wrapper.c
@@ -32,6 +32,7 @@
32#include "squashfs_fs_sb.h" 32#include "squashfs_fs_sb.h"
33#include "squashfs.h" 33#include "squashfs.h"
34#include "decompressor.h" 34#include "decompressor.h"
35#include "page_actor.h"
35 36
36static void *zlib_init(struct squashfs_sb_info *dummy, void *buff) 37static void *zlib_init(struct squashfs_sb_info *dummy, void *buff)
37{ 38{
@@ -62,14 +63,14 @@ static void zlib_free(void *strm)
62 63
63 64
64static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, 65static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
65 void **buffer, struct buffer_head **bh, int b, int offset, int length, 66 struct buffer_head **bh, int b, int offset, int length,
66 int srclength, int pages) 67 struct squashfs_page_actor *output)
67{ 68{
68 int zlib_err, zlib_init = 0; 69 int zlib_err, zlib_init = 0, k = 0;
69 int k = 0, page = 0;
70 z_stream *stream = strm; 70 z_stream *stream = strm;
71 71
72 stream->avail_out = 0; 72 stream->avail_out = PAGE_CACHE_SIZE;
73 stream->next_out = squashfs_first_page(output);
73 stream->avail_in = 0; 74 stream->avail_in = 0;
74 75
75 do { 76 do {
@@ -81,15 +82,18 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
81 offset = 0; 82 offset = 0;
82 } 83 }
83 84
84 if (stream->avail_out == 0 && page < pages) { 85 if (stream->avail_out == 0) {
85 stream->next_out = buffer[page++]; 86 stream->next_out = squashfs_next_page(output);
86 stream->avail_out = PAGE_CACHE_SIZE; 87 if (stream->next_out != NULL)
88 stream->avail_out = PAGE_CACHE_SIZE;
87 } 89 }
88 90
89 if (!zlib_init) { 91 if (!zlib_init) {
90 zlib_err = zlib_inflateInit(stream); 92 zlib_err = zlib_inflateInit(stream);
91 if (zlib_err != Z_OK) 93 if (zlib_err != Z_OK) {
94 squashfs_finish_page(output);
92 goto out; 95 goto out;
96 }
93 zlib_init = 1; 97 zlib_init = 1;
94 } 98 }
95 99
@@ -99,6 +103,8 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm,
99 put_bh(bh[k++]); 103 put_bh(bh[k++]);
100 } while (zlib_err == Z_OK); 104 } while (zlib_err == Z_OK);
101 105
106 squashfs_finish_page(output);
107
102 if (zlib_err != Z_STREAM_END) 108 if (zlib_err != Z_STREAM_END)
103 goto out; 109 goto out;
104 110