Blame view

fs/isofs/compress.c 9.88 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /* -*- linux-c -*- ------------------------------------------------------- *
   *   
   *   Copyright 2001 H. Peter Anvin - All Rights Reserved
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
   *   USA; either version 2 of the License, or (at your option) any later
   *   version; incorporated herein by reference.
   *
   * ----------------------------------------------------------------------- */
  
  /*
   * linux/fs/isofs/compress.c
   *
   * Transparent decompression of files on an iso9660 filesystem
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include <linux/init.h>
94f2f7157   Al Viro   [PATCH] isofs inc...
20

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
  #include <linux/vmalloc.h>
  #include <linux/zlib.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23

94f2f7157   Al Viro   [PATCH] isofs inc...
24
  #include "isofs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  #include "zisofs.h"
  
  /* This should probably be global. */
  static char zisofs_sink_page[PAGE_CACHE_SIZE];
  
  /*
   * This contains the zlib memory allocation and the mutex for the
   * allocation; this avoids failures at block-decompression time.
   */
  static void *zisofs_zlib_workspace;
  static struct semaphore zisofs_zlib_semaphore;
  
  /*
   * When decompressing, we typically obtain more than one page
   * per reference.  We inject the additional pages into the page
   * cache as a form of readahead.
   */
  static int zisofs_readpage(struct file *file, struct page *page)
  {
2485822d5   Josef "Jeff" Sipek   [PATCH] isofs: ch...
44
  	struct inode *inode = file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  	struct address_space *mapping = inode->i_mapping;
  	unsigned int maxpage, xpage, fpage, blockindex;
  	unsigned long offset;
  	unsigned long blockptr, blockendptr, cstart, cend, csize;
  	struct buffer_head *bh, *ptrbh[2];
  	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
  	unsigned int bufshift = ISOFS_BUFFER_BITS(inode);
  	unsigned long bufmask  = bufsize - 1;
  	int err = -EIO;
  	int i;
  	unsigned int header_size = ISOFS_I(inode)->i_format_parm[0];
  	unsigned int zisofs_block_shift = ISOFS_I(inode)->i_format_parm[1];
  	/* unsigned long zisofs_block_size = 1UL << zisofs_block_shift; */
  	unsigned int zisofs_block_page_shift = zisofs_block_shift-PAGE_CACHE_SHIFT;
  	unsigned long zisofs_block_pages = 1UL << zisofs_block_page_shift;
  	unsigned long zisofs_block_page_mask = zisofs_block_pages-1;
  	struct page *pages[zisofs_block_pages];
  	unsigned long index = page->index;
  	int indexblocks;
  
  	/* We have already been given one page, this is the one
  	   we must do. */
  	xpage = index & zisofs_block_page_mask;
  	pages[xpage] = page;
   
  	/* The remaining pages need to be allocated and inserted */
  	offset = index & ~zisofs_block_page_mask;
  	blockindex = offset >> zisofs_block_page_shift;
  	maxpage = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
  	maxpage = min(zisofs_block_pages, maxpage-offset);
  
  	for ( i = 0 ; i < maxpage ; i++, offset++ ) {
  		if ( i != xpage ) {
  			pages[i] = grab_cache_page_nowait(mapping, offset);
  		}
  		page = pages[i];
  		if ( page ) {
  			ClearPageError(page);
  			kmap(page);
  		}
  	}
  
  	/* This is the last page filled, plus one; used in case of abort. */
  	fpage = 0;
  
  	/* Find the pointer to this specific chunk */
  	/* Note: we're not using isonum_731() here because the data is known aligned */
  	/* Note: header_size is in 32-bit words (4 bytes) */
  	blockptr = (header_size + blockindex) << 2;
  	blockendptr = blockptr + 4;
  
  	indexblocks = ((blockptr^blockendptr) >> bufshift) ? 2 : 1;
  	ptrbh[0] = ptrbh[1] = NULL;
  
  	if ( isofs_get_blocks(inode, blockptr >> bufshift, ptrbh, indexblocks) != indexblocks ) {
  		if ( ptrbh[0] ) brelse(ptrbh[0]);
  		printk(KERN_DEBUG "zisofs: Null buffer on reading block table, inode = %lu, block = %lu
  ",
  		       inode->i_ino, blockptr >> bufshift);
  		goto eio;
  	}
  	ll_rw_block(READ, indexblocks, ptrbh);
  
  	bh = ptrbh[0];
  	if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
  		printk(KERN_DEBUG "zisofs: Failed to read block table, inode = %lu, block = %lu
  ",
  		       inode->i_ino, blockptr >> bufshift);
  		if ( ptrbh[1] )
  			brelse(ptrbh[1]);
  		goto eio;
  	}
  	cstart = le32_to_cpu(*(__le32 *)(bh->b_data + (blockptr & bufmask)));
  
  	if ( indexblocks == 2 ) {
  		/* We just crossed a block boundary.  Switch to the next block */
  		brelse(bh);
  		bh = ptrbh[1];
  		if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
  			printk(KERN_DEBUG "zisofs: Failed to read block table, inode = %lu, block = %lu
  ",
  			       inode->i_ino, blockendptr >> bufshift);
  			goto eio;
  		}
  	}
  	cend = le32_to_cpu(*(__le32 *)(bh->b_data + (blockendptr & bufmask)));
  	brelse(bh);
fab5a60a2   Linus Torvalds   Check input buffe...
132
133
134
  	if (cstart > cend)
  		goto eio;
  		
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  	csize = cend-cstart;
fab5a60a2   Linus Torvalds   Check input buffe...
136
137
  	if (csize > deflateBound(1UL << zisofs_block_shift))
  		goto eio;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  	/* Now page[] contains an array of pages, any of which can be NULL,
  	   and the locks on which we hold.  We should now read the data and
  	   release the pages.  If the pages are NULL the decompressed data
  	   for that particular page should be discarded. */
  	
  	if ( csize == 0 ) {
  		/* This data block is empty. */
  
  		for ( fpage = 0 ; fpage < maxpage ; fpage++ ) {
  			if ( (page = pages[fpage]) != NULL ) {
  				memset(page_address(page), 0, PAGE_CACHE_SIZE);
  				
  				flush_dcache_page(page);
  				SetPageUptodate(page);
  				kunmap(page);
  				unlock_page(page);
  				if ( fpage == xpage )
  					err = 0; /* The critical page */
  				else
  					page_cache_release(page);
  			}
  		}
  	} else {
  		/* This data block is compressed. */
  		z_stream stream;
  		int bail = 0, left_out = -1;
  		int zerr;
  		int needblocks = (csize + (cstart & bufmask) + bufmask) >> bufshift;
  		int haveblocks;
  		struct buffer_head *bhs[needblocks+1];
  		struct buffer_head **bhptr;
  
  		/* Because zlib is not thread-safe, do all the I/O at the top. */
  
  		blockptr = cstart >> bufshift;
  		memset(bhs, 0, (needblocks+1)*sizeof(struct buffer_head *));
  		haveblocks = isofs_get_blocks(inode, blockptr, bhs, needblocks);
  		ll_rw_block(READ, haveblocks, bhs);
  
  		bhptr = &bhs[0];
  		bh = *bhptr++;
  
  		/* First block is special since it may be fractional.
  		   We also wait for it before grabbing the zlib
  		   semaphore; odds are that the subsequent blocks are
  		   going to come in in short order so we don't hold
  		   the zlib semaphore longer than necessary. */
  
  		if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
  			printk(KERN_DEBUG "zisofs: Hit null buffer, fpage = %d, xpage = %d, csize = %ld
  ",
  			       fpage, xpage, csize);
  			goto b_eio;
  		}
  		stream.next_in  = bh->b_data + (cstart & bufmask);
  		stream.avail_in = min(bufsize-(cstart & bufmask), csize);
  		csize -= stream.avail_in;
  
  		stream.workspace = zisofs_zlib_workspace;
  		down(&zisofs_zlib_semaphore);
  		
  		zerr = zlib_inflateInit(&stream);
  		if ( zerr != Z_OK ) {
  			if ( err && zerr == Z_MEM_ERROR )
  				err = -ENOMEM;
  			printk(KERN_DEBUG "zisofs: zisofs_inflateInit returned %d
  ",
  			       zerr);
  			goto z_eio;
  		}
  
  		while ( !bail && fpage < maxpage ) {
  			page = pages[fpage];
  			if ( page )
  				stream.next_out = page_address(page);
  			else
  				stream.next_out = (void *)&zisofs_sink_page;
  			stream.avail_out = PAGE_CACHE_SIZE;
  
  			while ( stream.avail_out ) {
  				int ao, ai;
  				if ( stream.avail_in == 0 && left_out ) {
  					if ( !csize ) {
  						printk(KERN_WARNING "zisofs: ZF read beyond end of input
  ");
  						bail = 1;
  						break;
  					} else {
  						bh = *bhptr++;
  						if ( !bh ||
  						     (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
  							/* Reached an EIO */
   							printk(KERN_DEBUG "zisofs: Hit null buffer, fpage = %d, xpage = %d, csize = %ld
  ",
  							       fpage, xpage, csize);
  							       
  							bail = 1;
  							break;
  						}
  						stream.next_in = bh->b_data;
  						stream.avail_in = min(csize,bufsize);
  						csize -= stream.avail_in;
  					}
  				}
  				ao = stream.avail_out;  ai = stream.avail_in;
  				zerr = zlib_inflate(&stream, Z_SYNC_FLUSH);
  				left_out = stream.avail_out;
  				if ( zerr == Z_BUF_ERROR && stream.avail_in == 0 )
  					continue;
  				if ( zerr != Z_OK ) {
  					/* EOF, error, or trying to read beyond end of input */
  					if ( err && zerr == Z_MEM_ERROR )
  						err = -ENOMEM;
  					if ( zerr != Z_STREAM_END )
  						printk(KERN_DEBUG "zisofs: zisofs_inflate returned %d, inode = %lu, index = %lu, fpage = %d, xpage = %d, avail_in = %d, avail_out = %d, ai = %d, ao = %d
  ",
  						       zerr, inode->i_ino, index,
  						       fpage, xpage,
  						       stream.avail_in, stream.avail_out,
  						       ai, ao);
  					bail = 1;
  					break;
  				}
  			}
  
  			if ( stream.avail_out && zerr == Z_STREAM_END ) {
  				/* Fractional page written before EOF.  This may
  				   be the last page in the file. */
  				memset(stream.next_out, 0, stream.avail_out);
  				stream.avail_out = 0;
  			}
  
  			if ( !stream.avail_out ) {
  				/* This page completed */
  				if ( page ) {
  					flush_dcache_page(page);
  					SetPageUptodate(page);
  					kunmap(page);
  					unlock_page(page);
  					if ( fpage == xpage )
  						err = 0; /* The critical page */
  					else
  						page_cache_release(page);
  				}
  				fpage++;
  			}
  		}
  		zlib_inflateEnd(&stream);
  
  	z_eio:
  		up(&zisofs_zlib_semaphore);
  
  	b_eio:
  		for ( i = 0 ; i < haveblocks ; i++ ) {
  			if ( bhs[i] )
  				brelse(bhs[i]);
  		}
  	}
  
  eio:
  
  	/* Release any residual pages, do not SetPageUptodate */
  	while ( fpage < maxpage ) {
  		page = pages[fpage];
  		if ( page ) {
  			flush_dcache_page(page);
  			if ( fpage == xpage )
  				SetPageError(page);
  			kunmap(page);
  			unlock_page(page);
  			if ( fpage != xpage )
  				page_cache_release(page);
  		}
  		fpage++;
  	}			
  
  	/* At this point, err contains 0 or -EIO depending on the "critical" page */
  	return err;
  }
f5e54d6e5   Christoph Hellwig   [PATCH] mark addr...
317
  const struct address_space_operations zisofs_aops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
  	.readpage = zisofs_readpage,
  	/* No sync_page operation supported? */
  	/* No bmap operation supported */
  };
  
  static int initialized;
  
  int __init zisofs_init(void)
  {
  	if ( initialized ) {
  		printk("zisofs_init: called more than once
  ");
  		return 0;
  	}
  
  	zisofs_zlib_workspace = vmalloc(zlib_inflate_workspacesize());
  	if ( !zisofs_zlib_workspace )
  		return -ENOMEM;
  	init_MUTEX(&zisofs_zlib_semaphore);
  
  	initialized = 1;
  	return 0;
  }
  
  void zisofs_cleanup(void)
  {
  	if ( !initialized ) {
  		printk("zisofs_cleanup: called without initialization
  ");
  		return;
  	}
  
  	vfree(zisofs_zlib_workspace);
  	initialized = 0;
  }