Blame view
fs/squashfs/block.c
5.58 KB
e2780ab15 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 Squashfs: update ... |
5 |
* Phillip Lougher <phillip@squashfs.org.uk> |
e2780ab15 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 Squashfs: block o... |
32 33 |
#include <linux/string.h> #include <linux/buffer_head.h> |
2f8b54447 block,fs: untangl... |
34 |
#include <linux/bio.h> |
e2780ab15 Squashfs: block o... |
35 36 37 |
#include "squashfs_fs.h" #include "squashfs_fs_sb.h" |
e2780ab15 Squashfs: block o... |
38 |
#include "squashfs.h" |
4c0f0bb23 Squashfs: add a d... |
39 |
#include "decompressor.h" |
846b730e9 Squashfs: General... |
40 |
#include "page_actor.h" |
e2780ab15 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 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 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 Squashfs: update ... |
88 89 |
* generated a larger block - this does occasionally happen with compression * algorithms). |
e2780ab15 Squashfs: block o... |
90 |
*/ |
846b730e9 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 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 Squashfs: General... |
98 |
int bytes, compressed, b = 0, k = 0, avail, i; |
e2780ab15 Squashfs: block o... |
99 |
|
846b730e9 Squashfs: General... |
100 |
bh = kcalloc(((output->length + msblk->devblksize - 1) |
e0d1f7001 squashfs: fix pot... |
101 |
>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); |
e2780ab15 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 Squashfs: General... |
117 |
index, compressed ? "" : "un", length, output->length); |
e2780ab15 Squashfs: block o... |
118 |
|
846b730e9 Squashfs: General... |
119 |
if (length < 0 || length > output->length || |
e2780ab15 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 fs: have ll_rw_bl... |
129 |
ll_rw_block(REQ_OP_READ, 0, b, bh); |
e2780ab15 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 Squashfs: General... |
151 |
if (length < 0 || length > output->length || |
e2780ab15 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 fs: have ll_rw_bl... |
161 |
ll_rw_block(REQ_OP_READ, 0, b - 1, bh + 1); |
e2780ab15 Squashfs: block o... |
162 |
} |
9508c6b90 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 Squashfs: block o... |
168 |
if (compressed) { |
953f918d5 squashfs: more me... |
169 170 |
if (!msblk->stream) goto read_failure; |
846b730e9 Squashfs: General... |
171 172 |
length = squashfs_decompress(msblk, bh, b, offset, length, output); |
e6a6d3795 Squashfs: move zl... |
173 174 |
if (length < 0) goto read_failure; |
e2780ab15 Squashfs: block o... |
175 176 177 178 |
} else { /* * Block is uncompressed. */ |
e0125262a Squashfs: Optimiz... |
179 |
int in, pg_offset = 0; |
846b730e9 Squashfs: General... |
180 |
void *data = squashfs_first_page(output); |
e2780ab15 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 mm, fs: get rid o... |
186 |
if (pg_offset == PAGE_SIZE) { |
846b730e9 Squashfs: General... |
187 |
data = squashfs_next_page(output); |
e2780ab15 Squashfs: block o... |
188 189 |
pg_offset = 0; } |
09cbfeaf1 mm, fs: get rid o... |
190 |
avail = min_t(int, in, PAGE_SIZE - |
e2780ab15 Squashfs: block o... |
191 |
pg_offset); |
846b730e9 Squashfs: General... |
192 193 |
memcpy(data + pg_offset, bh[k]->b_data + offset, avail); |
e2780ab15 Squashfs: block o... |
194 195 196 197 198 199 200 |
in -= avail; pg_offset += avail; offset += avail; } offset = 0; put_bh(bh[k]); } |
846b730e9 Squashfs: General... |
201 |
squashfs_finish_page(output); |
e2780ab15 Squashfs: block o... |
202 203 204 205 |
} kfree(bh); return length; |
e2780ab15 Squashfs: block o... |
206 207 208 209 210 |
block_release: for (; k < b; k++) put_bh(bh[k]); read_failure: |
118e1ef6f Squashfs: Fix oop... |
211 212 213 |
ERROR("squashfs_read_data failed to read block 0x%llx ", (unsigned long long) index); |
e2780ab15 Squashfs: block o... |
214 215 216 |
kfree(bh); return -EIO; } |