Blame view

fs/squashfs/block.c 5.58 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>
2f8b54447   Christoph Hellwig   block,fs: untangl...
34
  #include <linux/bio.h>
e2780ab15   Phillip Lougher   Squashfs: block o...
35
36
37
  
  #include "squashfs_fs.h"
  #include "squashfs_fs_sb.h"
e2780ab15   Phillip Lougher   Squashfs: block o...
38
  #include "squashfs.h"
4c0f0bb23   Phillip Lougher   Squashfs: add a d...
39
  #include "decompressor.h"
846b730e9   Phillip Lougher   Squashfs: General...
40
  #include "page_actor.h"
e2780ab15   Phillip Lougher   Squashfs: block o...
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
66
67
  
  /*
   * 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...
68
69
70
71
72
73
74
75
  
  		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...
76
77
78
79
80
81
82
83
84
85
86
87
  	}
  
  	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
ec9267b61   Phillip Lougher   Squashfs: update ...
88
89
   * generated a larger block - this does occasionally happen with compression
   * algorithms).
e2780ab15   Phillip Lougher   Squashfs: block o...
90
   */
846b730e9   Phillip Lougher   Squashfs: General...
91
92
  int squashfs_read_data(struct super_block *sb, u64 index, int length,
  		u64 *next_index, struct squashfs_page_actor *output)
e2780ab15   Phillip Lougher   Squashfs: block o...
93
94
95
96
97
  {
  	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;
846b730e9   Phillip Lougher   Squashfs: General...
98
  	int bytes, compressed, b = 0, k = 0, avail, i;
e2780ab15   Phillip Lougher   Squashfs: block o...
99

846b730e9   Phillip Lougher   Squashfs: General...
100
  	bh = kcalloc(((output->length + msblk->devblksize - 1)
e0d1f7001   Phillip Lougher   squashfs: fix pot...
101
  		>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
e2780ab15   Phillip Lougher   Squashfs: block o...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  	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
  ",
846b730e9   Phillip Lougher   Squashfs: General...
117
  			index, compressed ? "" : "un", length, output->length);
e2780ab15   Phillip Lougher   Squashfs: block o...
118

846b730e9   Phillip Lougher   Squashfs: General...
119
  		if (length < 0 || length > output->length ||
e2780ab15   Phillip Lougher   Squashfs: block o...
120
121
122
123
124
125
126
127
128
  				(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;
  		}
dfec8a14f   Mike Christie   fs: have ll_rw_bl...
129
  		ll_rw_block(REQ_OP_READ, 0, b, bh);
e2780ab15   Phillip Lougher   Squashfs: block o...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  	} 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);
846b730e9   Phillip Lougher   Squashfs: General...
151
  		if (length < 0 || length > output->length ||
e2780ab15   Phillip Lougher   Squashfs: block o...
152
153
154
155
156
157
158
159
160
  					(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;
  		}
dfec8a14f   Mike Christie   fs: have ll_rw_bl...
161
  		ll_rw_block(REQ_OP_READ, 0, b - 1, bh + 1);
e2780ab15   Phillip Lougher   Squashfs: block o...
162
  	}
9508c6b90   Phillip Lougher   Squashfs: Refacto...
163
164
165
166
167
  	for (i = 0; i < b; i++) {
  		wait_on_buffer(bh[i]);
  		if (!buffer_uptodate(bh[i]))
  			goto block_release;
  	}
e2780ab15   Phillip Lougher   Squashfs: block o...
168
  	if (compressed) {
953f918d5   Linus Torvalds   squashfs: more me...
169
170
  		if (!msblk->stream)
  			goto read_failure;
846b730e9   Phillip Lougher   Squashfs: General...
171
172
  		length = squashfs_decompress(msblk, bh, b, offset, length,
  			output);
e6a6d3795   Phillip Lougher   Squashfs: move zl...
173
174
  		if (length < 0)
  			goto read_failure;
e2780ab15   Phillip Lougher   Squashfs: block o...
175
176
177
178
  	} else {
  		/*
  		 * Block is uncompressed.
  		 */
e0125262a   Manish Sharma   Squashfs: Optimiz...
179
  		int in, pg_offset = 0;
846b730e9   Phillip Lougher   Squashfs: General...
180
  		void *data = squashfs_first_page(output);
e2780ab15   Phillip Lougher   Squashfs: block o...
181
182
183
184
185
  
  		for (bytes = length; k < b; k++) {
  			in = min(bytes, msblk->devblksize - offset);
  			bytes -= in;
  			while (in) {
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
186
  				if (pg_offset == PAGE_SIZE) {
846b730e9   Phillip Lougher   Squashfs: General...
187
  					data = squashfs_next_page(output);
e2780ab15   Phillip Lougher   Squashfs: block o...
188
189
  					pg_offset = 0;
  				}
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
190
  				avail = min_t(int, in, PAGE_SIZE -
e2780ab15   Phillip Lougher   Squashfs: block o...
191
  						pg_offset);
846b730e9   Phillip Lougher   Squashfs: General...
192
193
  				memcpy(data + pg_offset, bh[k]->b_data + offset,
  						avail);
e2780ab15   Phillip Lougher   Squashfs: block o...
194
195
196
197
198
199
200
  				in -= avail;
  				pg_offset += avail;
  				offset += avail;
  			}
  			offset = 0;
  			put_bh(bh[k]);
  		}
846b730e9   Phillip Lougher   Squashfs: General...
201
  		squashfs_finish_page(output);
e2780ab15   Phillip Lougher   Squashfs: block o...
202
203
204
205
  	}
  
  	kfree(bh);
  	return length;
e2780ab15   Phillip Lougher   Squashfs: block o...
206
207
208
209
210
  block_release:
  	for (; k < b; k++)
  		put_bh(bh[k]);
  
  read_failure:
118e1ef6f   Phillip Lougher   Squashfs: Fix oop...
211
212
213
  	ERROR("squashfs_read_data failed to read block 0x%llx
  ",
  					(unsigned long long) index);
e2780ab15   Phillip Lougher   Squashfs: block o...
214
215
216
  	kfree(bh);
  	return -EIO;
  }