Blame view

fs/squashfs/block.c 5.39 KB
e2780ab15   Phillip Lougher   Squashfs: block o...
1
2
3
4
  /*
   * Squashfs - a compressed read only filesystem for Linux
   *
   * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
d7f2ff671   Phillip Lougher   Squashfs: update ...
5
   * Phillip Lougher <phillip@squashfs.org.uk>
e2780ab15   Phillip Lougher   Squashfs: block o...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   *
   * 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; either version 2,
   * or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   *
   * block.c
   */
  
  /*
   * This file implements the low-level routines to read and decompress
   * datablocks and metadata blocks.
   */
  
  #include <linux/fs.h>
  #include <linux/vfs.h>
  #include <linux/slab.h>
e2780ab15   Phillip Lougher   Squashfs: block o...
32
33
  #include <linux/string.h>
  #include <linux/buffer_head.h>
e2780ab15   Phillip Lougher   Squashfs: block o...
34
35
36
  
  #include "squashfs_fs.h"
  #include "squashfs_fs_sb.h"
e2780ab15   Phillip Lougher   Squashfs: block o...
37
  #include "squashfs.h"
4c0f0bb23   Phillip Lougher   Squashfs: add a d...
38
  #include "decompressor.h"
e2780ab15   Phillip Lougher   Squashfs: block o...
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  
  /*
   * Read the metadata block length, this is stored in the first two
   * bytes of the metadata block.
   */
  static struct buffer_head *get_block_length(struct super_block *sb,
  			u64 *cur_index, int *offset, int *length)
  {
  	struct squashfs_sb_info *msblk = sb->s_fs_info;
  	struct buffer_head *bh;
  
  	bh = sb_bread(sb, *cur_index);
  	if (bh == NULL)
  		return NULL;
  
  	if (msblk->devblksize - *offset == 1) {
  		*length = (unsigned char) bh->b_data[*offset];
  		put_bh(bh);
  		bh = sb_bread(sb, ++(*cur_index));
  		if (bh == NULL)
  			return NULL;
  		*length |= (unsigned char) bh->b_data[0] << 8;
  		*offset = 1;
  	} else {
  		*length = (unsigned char) bh->b_data[*offset] |
  			(unsigned char) bh->b_data[*offset + 1] << 8;
  		*offset += 2;
3689456b4   Phillip Lougher   squashfs: fix use...
66
67
68
69
70
71
72
73
  
  		if (*offset == msblk->devblksize) {
  			put_bh(bh);
  			bh = sb_bread(sb, ++(*cur_index));
  			if (bh == NULL)
  				return NULL;
  			*offset = 0;
  		}
e2780ab15   Phillip Lougher   Squashfs: block o...
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  	}
  
  	return bh;
  }
  
  
  /*
   * Read and decompress a metadata block or datablock.  Length is non-zero
   * if a datablock is being read (the size is stored elsewhere in the
   * filesystem), otherwise the length is obtained from the first two bytes of
   * the metadata block.  A bit in the length field indicates if the block
   * is stored uncompressed in the filesystem (usually because compression
   * generated a larger block - this does occasionally happen with zlib).
   */
  int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
118e1ef6f   Phillip Lougher   Squashfs: Fix oop...
89
  			int length, u64 *next_index, int srclength, int pages)
e2780ab15   Phillip Lougher   Squashfs: block o...
90
91
92
93
94
95
  {
  	struct squashfs_sb_info *msblk = sb->s_fs_info;
  	struct buffer_head **bh;
  	int offset = index & ((1 << msblk->devblksize_log2) - 1);
  	u64 cur_index = index >> msblk->devblksize_log2;
  	int bytes, compressed, b = 0, k = 0, page = 0, avail;
e0d1f7001   Phillip Lougher   squashfs: fix pot...
96
97
  	bh = kcalloc(((srclength + msblk->devblksize - 1)
  		>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
e2780ab15   Phillip Lougher   Squashfs: block o...
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
  	if (bh == NULL)
  		return -ENOMEM;
  
  	if (length) {
  		/*
  		 * Datablock.
  		 */
  		bytes = -offset;
  		compressed = SQUASHFS_COMPRESSED_BLOCK(length);
  		length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
  		if (next_index)
  			*next_index = index + length;
  
  		TRACE("Block @ 0x%llx, %scompressed size %d, src size %d
  ",
  			index, compressed ? "" : "un", length, srclength);
  
  		if (length < 0 || length > srclength ||
  				(index + length) > msblk->bytes_used)
  			goto read_failure;
  
  		for (b = 0; bytes < length; b++, cur_index++) {
  			bh[b] = sb_getblk(sb, cur_index);
  			if (bh[b] == NULL)
  				goto block_release;
  			bytes += msblk->devblksize;
  		}
  		ll_rw_block(READ, b, bh);
  	} else {
  		/*
  		 * Metadata block.
  		 */
  		if ((index + 2) > msblk->bytes_used)
  			goto read_failure;
  
  		bh[0] = get_block_length(sb, &cur_index, &offset, &length);
  		if (bh[0] == NULL)
  			goto read_failure;
  		b = 1;
  
  		bytes = msblk->devblksize - offset;
  		compressed = SQUASHFS_COMPRESSED(length);
  		length = SQUASHFS_COMPRESSED_SIZE(length);
  		if (next_index)
  			*next_index = index + length + 2;
  
  		TRACE("Block @ 0x%llx, %scompressed size %d
  ", index,
  				compressed ? "" : "un", length);
  
  		if (length < 0 || length > srclength ||
  					(index + length) > msblk->bytes_used)
  			goto block_release;
  
  		for (; bytes < length; b++) {
  			bh[b] = sb_getblk(sb, ++cur_index);
  			if (bh[b] == NULL)
  				goto block_release;
  			bytes += msblk->devblksize;
  		}
  		ll_rw_block(READ, b - 1, bh + 1);
  	}
  
  	if (compressed) {
4c0f0bb23   Phillip Lougher   Squashfs: add a d...
162
  		length = squashfs_decompress(msblk, buffer, bh, b, offset,
e6a6d3795   Phillip Lougher   Squashfs: move zl...
163
164
165
  			 length, srclength, pages);
  		if (length < 0)
  			goto read_failure;
e2780ab15   Phillip Lougher   Squashfs: block o...
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
  	} else {
  		/*
  		 * Block is uncompressed.
  		 */
  		int i, in, pg_offset = 0;
  
  		for (i = 0; i < b; i++) {
  			wait_on_buffer(bh[i]);
  			if (!buffer_uptodate(bh[i]))
  				goto block_release;
  		}
  
  		for (bytes = length; k < b; k++) {
  			in = min(bytes, msblk->devblksize - offset);
  			bytes -= in;
  			while (in) {
  				if (pg_offset == PAGE_CACHE_SIZE) {
  					page++;
  					pg_offset = 0;
  				}
  				avail = min_t(int, in, PAGE_CACHE_SIZE -
  						pg_offset);
  				memcpy(buffer[page] + pg_offset,
  						bh[k]->b_data + offset, avail);
  				in -= avail;
  				pg_offset += avail;
  				offset += avail;
  			}
  			offset = 0;
  			put_bh(bh[k]);
  		}
  	}
  
  	kfree(bh);
  	return length;
e2780ab15   Phillip Lougher   Squashfs: block o...
201
202
203
204
205
  block_release:
  	for (; k < b; k++)
  		put_bh(bh[k]);
  
  read_failure:
118e1ef6f   Phillip Lougher   Squashfs: Fix oop...
206
207
208
  	ERROR("squashfs_read_data failed to read block 0x%llx
  ",
  					(unsigned long long) index);
e2780ab15   Phillip Lougher   Squashfs: block o...
209
210
211
  	kfree(bh);
  	return -EIO;
  }