GNU Linux-libre 4.19.211-gnu1
[releases.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 #include <linux/bio.h>
35
36 #include "squashfs_fs.h"
37 #include "squashfs_fs_sb.h"
38 #include "squashfs.h"
39 #include "decompressor.h"
40 #include "page_actor.h"
41
42 /*
43  * Read the metadata block length, this is stored in the first two
44  * bytes of the metadata block.
45  */
46 static struct buffer_head *get_block_length(struct super_block *sb,
47                         u64 *cur_index, int *offset, int *length)
48 {
49         struct squashfs_sb_info *msblk = sb->s_fs_info;
50         struct buffer_head *bh;
51
52         bh = sb_bread(sb, *cur_index);
53         if (bh == NULL)
54                 return NULL;
55
56         if (msblk->devblksize - *offset == 1) {
57                 *length = (unsigned char) bh->b_data[*offset];
58                 put_bh(bh);
59                 bh = sb_bread(sb, ++(*cur_index));
60                 if (bh == NULL)
61                         return NULL;
62                 *length |= (unsigned char) bh->b_data[0] << 8;
63                 *offset = 1;
64         } else {
65                 *length = (unsigned char) bh->b_data[*offset] |
66                         (unsigned char) bh->b_data[*offset + 1] << 8;
67                 *offset += 2;
68
69                 if (*offset == msblk->devblksize) {
70                         put_bh(bh);
71                         bh = sb_bread(sb, ++(*cur_index));
72                         if (bh == NULL)
73                                 return NULL;
74                         *offset = 0;
75                 }
76         }
77
78         return bh;
79 }
80
81
82 /*
83  * Read and decompress a metadata block or datablock.  Length is non-zero
84  * if a datablock is being read (the size is stored elsewhere in the
85  * filesystem), otherwise the length is obtained from the first two bytes of
86  * the metadata block.  A bit in the length field indicates if the block
87  * is stored uncompressed in the filesystem (usually because compression
88  * generated a larger block - this does occasionally happen with compression
89  * algorithms).
90  */
91 int squashfs_read_data(struct super_block *sb, u64 index, int length,
92                 u64 *next_index, struct squashfs_page_actor *output)
93 {
94         struct squashfs_sb_info *msblk = sb->s_fs_info;
95         struct buffer_head **bh;
96         int offset = index & ((1 << msblk->devblksize_log2) - 1);
97         u64 cur_index = index >> msblk->devblksize_log2;
98         int bytes, compressed, b = 0, k = 0, avail, i;
99
100         bh = kcalloc(((output->length + msblk->devblksize - 1)
101                 >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
102         if (bh == NULL)
103                 return -ENOMEM;
104
105         if (length) {
106                 /*
107                  * Datablock.
108                  */
109                 bytes = -offset;
110                 compressed = SQUASHFS_COMPRESSED_BLOCK(length);
111                 length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
112                 if (next_index)
113                         *next_index = index + length;
114
115                 TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
116                         index, compressed ? "" : "un", length, output->length);
117
118                 if (length < 0 || length > output->length ||
119                                 (index + length) > msblk->bytes_used)
120                         goto read_failure;
121
122                 for (b = 0; bytes < length; b++, cur_index++) {
123                         bh[b] = sb_getblk(sb, cur_index);
124                         if (bh[b] == NULL)
125                                 goto block_release;
126                         bytes += msblk->devblksize;
127                 }
128                 ll_rw_block(REQ_OP_READ, 0, b, bh);
129         } else {
130                 /*
131                  * Metadata block.
132                  */
133                 if ((index + 2) > msblk->bytes_used)
134                         goto read_failure;
135
136                 bh[0] = get_block_length(sb, &cur_index, &offset, &length);
137                 if (bh[0] == NULL)
138                         goto read_failure;
139                 b = 1;
140
141                 bytes = msblk->devblksize - offset;
142                 compressed = SQUASHFS_COMPRESSED(length);
143                 length = SQUASHFS_COMPRESSED_SIZE(length);
144                 if (next_index)
145                         *next_index = index + length + 2;
146
147                 TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
148                                 compressed ? "" : "un", length);
149
150                 if (length < 0 || length > output->length ||
151                                         (index + length) > msblk->bytes_used)
152                         goto block_release;
153
154                 for (; bytes < length; b++) {
155                         bh[b] = sb_getblk(sb, ++cur_index);
156                         if (bh[b] == NULL)
157                                 goto block_release;
158                         bytes += msblk->devblksize;
159                 }
160                 ll_rw_block(REQ_OP_READ, 0, b - 1, bh + 1);
161         }
162
163         for (i = 0; i < b; i++) {
164                 wait_on_buffer(bh[i]);
165                 if (!buffer_uptodate(bh[i]))
166                         goto block_release;
167         }
168
169         if (compressed) {
170                 if (!msblk->stream)
171                         goto read_failure;
172                 length = squashfs_decompress(msblk, bh, b, offset, length,
173                         output);
174                 if (length < 0)
175                         goto read_failure;
176         } else {
177                 /*
178                  * Block is uncompressed.
179                  */
180                 int in, pg_offset = 0;
181                 void *data = squashfs_first_page(output);
182
183                 for (bytes = length; k < b; k++) {
184                         in = min(bytes, msblk->devblksize - offset);
185                         bytes -= in;
186                         while (in) {
187                                 if (pg_offset == PAGE_SIZE) {
188                                         data = squashfs_next_page(output);
189                                         pg_offset = 0;
190                                 }
191                                 avail = min_t(int, in, PAGE_SIZE -
192                                                 pg_offset);
193                                 memcpy(data + pg_offset, bh[k]->b_data + offset,
194                                                 avail);
195                                 in -= avail;
196                                 pg_offset += avail;
197                                 offset += avail;
198                         }
199                         offset = 0;
200                         put_bh(bh[k]);
201                 }
202                 squashfs_finish_page(output);
203         }
204
205         kfree(bh);
206         return length;
207
208 block_release:
209         for (; k < b; k++)
210                 put_bh(bh[k]);
211
212 read_failure:
213         ERROR("squashfs_read_data failed to read block 0x%llx\n",
214                                         (unsigned long long) index);
215         kfree(bh);
216         return -EIO;
217 }