Merge branch 'clockevents/fixes' of git://git.linaro.org/people/daniel.lezcano/linux...
[linux-drm-fsl-dcu.git] / fs / squashfs / block.c
1 /*
2  * Squashfs - a compressed read only filesystem for Linux
3  *
4  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
5  * Phillip Lougher <phillip@squashfs.org.uk>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2,
10  * or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * block.c
22  */
23
24 /*
25  * This file implements the low-level routines to read and decompress
26  * datablocks and metadata blocks.
27  */
28
29 #include <linux/fs.h>
30 #include <linux/vfs.h>
31 #include <linux/slab.h>
32 #include <linux/string.h>
33 #include <linux/buffer_head.h>
34
35 #include "squashfs_fs.h"
36 #include "squashfs_fs_sb.h"
37 #include "squashfs.h"
38 #include "decompressor.h"
39 #include "page_actor.h"
40
41 /*
42  * Read the metadata block length, this is stored in the first two
43  * bytes of the metadata block.
44  */
45 static struct buffer_head *get_block_length(struct super_block *sb,
46                         u64 *cur_index, int *offset, int *length)
47 {
48         struct squashfs_sb_info *msblk = sb->s_fs_info;
49         struct buffer_head *bh;
50
51         bh = sb_bread(sb, *cur_index);
52         if (bh == NULL)
53                 return NULL;
54
55         if (msblk->devblksize - *offset == 1) {
56                 *length = (unsigned char) bh->b_data[*offset];
57                 put_bh(bh);
58                 bh = sb_bread(sb, ++(*cur_index));
59                 if (bh == NULL)
60                         return NULL;
61                 *length |= (unsigned char) bh->b_data[0] << 8;
62                 *offset = 1;
63         } else {
64                 *length = (unsigned char) bh->b_data[*offset] |
65                         (unsigned char) bh->b_data[*offset + 1] << 8;
66                 *offset += 2;
67
68                 if (*offset == msblk->devblksize) {
69                         put_bh(bh);
70                         bh = sb_bread(sb, ++(*cur_index));
71                         if (bh == NULL)
72                                 return NULL;
73                         *offset = 0;
74                 }
75         }
76
77         return bh;
78 }
79
80
81 /*
82  * Read and decompress a metadata block or datablock.  Length is non-zero
83  * if a datablock is being read (the size is stored elsewhere in the
84  * filesystem), otherwise the length is obtained from the first two bytes of
85  * the metadata block.  A bit in the length field indicates if the block
86  * is stored uncompressed in the filesystem (usually because compression
87  * generated a larger block - this does occasionally happen with compression
88  * algorithms).
89  */
90 int squashfs_read_data(struct super_block *sb, u64 index, int length,
91                 u64 *next_index, struct squashfs_page_actor *output)
92 {
93         struct squashfs_sb_info *msblk = sb->s_fs_info;
94         struct buffer_head **bh;
95         int offset = index & ((1 << msblk->devblksize_log2) - 1);
96         u64 cur_index = index >> msblk->devblksize_log2;
97         int bytes, compressed, b = 0, k = 0, avail, i;
98
99         bh = kcalloc(((output->length + msblk->devblksize - 1)
100                 >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
101         if (bh == NULL)
102                 return -ENOMEM;
103
104         if (length) {
105                 /*
106                  * Datablock.
107                  */
108                 bytes = -offset;
109                 compressed = SQUASHFS_COMPRESSED_BLOCK(length);
110                 length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
111                 if (next_index)
112                         *next_index = index + length;
113
114                 TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
115                         index, compressed ? "" : "un", length, output->length);
116
117                 if (length < 0 || length > output->length ||
118                                 (index + length) > msblk->bytes_used)
119                         goto read_failure;
120
121                 for (b = 0; bytes < length; b++, cur_index++) {
122                         bh[b] = sb_getblk(sb, cur_index);
123                         if (bh[b] == NULL)
124                                 goto block_release;
125                         bytes += msblk->devblksize;
126                 }
127                 ll_rw_block(READ, b, bh);
128         } else {
129                 /*
130                  * Metadata block.
131                  */
132                 if ((index + 2) > msblk->bytes_used)
133                         goto read_failure;
134
135                 bh[0] = get_block_length(sb, &cur_index, &offset, &length);
136                 if (bh[0] == NULL)
137                         goto read_failure;
138                 b = 1;
139
140                 bytes = msblk->devblksize - offset;
141                 compressed = SQUASHFS_COMPRESSED(length);
142                 length = SQUASHFS_COMPRESSED_SIZE(length);
143                 if (next_index)
144                         *next_index = index + length + 2;
145
146                 TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
147                                 compressed ? "" : "un", length);
148
149                 if (length < 0 || length > output->length ||
150                                         (index + length) > msblk->bytes_used)
151                         goto block_release;
152
153                 for (; bytes < length; b++) {
154                         bh[b] = sb_getblk(sb, ++cur_index);
155                         if (bh[b] == NULL)
156                                 goto block_release;
157                         bytes += msblk->devblksize;
158                 }
159                 ll_rw_block(READ, b - 1, bh + 1);
160         }
161
162         for (i = 0; i < b; i++) {
163                 wait_on_buffer(bh[i]);
164                 if (!buffer_uptodate(bh[i]))
165                         goto block_release;
166         }
167
168         if (compressed) {
169                 length = squashfs_decompress(msblk, bh, b, offset, length,
170                         output);
171                 if (length < 0)
172                         goto read_failure;
173         } else {
174                 /*
175                  * Block is uncompressed.
176                  */
177                 int in, pg_offset = 0;
178                 void *data = squashfs_first_page(output);
179
180                 for (bytes = length; k < b; k++) {
181                         in = min(bytes, msblk->devblksize - offset);
182                         bytes -= in;
183                         while (in) {
184                                 if (pg_offset == PAGE_CACHE_SIZE) {
185                                         data = squashfs_next_page(output);
186                                         pg_offset = 0;
187                                 }
188                                 avail = min_t(int, in, PAGE_CACHE_SIZE -
189                                                 pg_offset);
190                                 memcpy(data + pg_offset, bh[k]->b_data + offset,
191                                                 avail);
192                                 in -= avail;
193                                 pg_offset += avail;
194                                 offset += avail;
195                         }
196                         offset = 0;
197                         put_bh(bh[k]);
198                 }
199                 squashfs_finish_page(output);
200         }
201
202         kfree(bh);
203         return length;
204
205 block_release:
206         for (; k < b; k++)
207                 put_bh(bh[k]);
208
209 read_failure:
210         ERROR("squashfs_read_data failed to read block 0x%llx\n",
211                                         (unsigned long long) index);
212         kfree(bh);
213         return -EIO;
214 }