| .. | .. | 
|---|
|  | 1 | +// SPDX-License-Identifier: GPL-2.0-or-later | 
|---|
| 1 | 2 | /* | 
|---|
| 2 | 3 | * Squashfs - a compressed read only filesystem for Linux | 
|---|
| 3 | 4 | * | 
|---|
| 4 | 5 | * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 | 
|---|
| 5 | 6 | * 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 | 7 | * | 
|---|
| 21 | 8 | * block.c | 
|---|
| 22 | 9 | */ | 
|---|
| .. | .. | 
|---|
| 26 | 13 | * datablocks and metadata blocks. | 
|---|
| 27 | 14 | */ | 
|---|
| 28 | 15 |  | 
|---|
|  | 16 | +#include <linux/blkdev.h> | 
|---|
| 29 | 17 | #include <linux/fs.h> | 
|---|
| 30 | 18 | #include <linux/vfs.h> | 
|---|
| 31 | 19 | #include <linux/slab.h> | 
|---|
| .. | .. | 
|---|
| 40 | 28 | #include "page_actor.h" | 
|---|
| 41 | 29 |  | 
|---|
| 42 | 30 | /* | 
|---|
| 43 |  | - * Read the metadata block length, this is stored in the first two | 
|---|
| 44 |  | - * bytes of the metadata block. | 
|---|
|  | 31 | + * Returns the amount of bytes copied to the page actor. | 
|---|
| 45 | 32 | */ | 
|---|
| 46 |  | -static struct buffer_head *get_block_length(struct super_block *sb, | 
|---|
| 47 |  | -			u64 *cur_index, int *offset, int *length) | 
|---|
|  | 33 | +static int copy_bio_to_actor(struct bio *bio, | 
|---|
|  | 34 | +			     struct squashfs_page_actor *actor, | 
|---|
|  | 35 | +			     int offset, int req_length) | 
|---|
| 48 | 36 | { | 
|---|
| 49 |  | -	struct squashfs_sb_info *msblk = sb->s_fs_info; | 
|---|
| 50 |  | -	struct buffer_head *bh; | 
|---|
|  | 37 | +	void *actor_addr = squashfs_first_page(actor); | 
|---|
|  | 38 | +	struct bvec_iter_all iter_all = {}; | 
|---|
|  | 39 | +	struct bio_vec *bvec = bvec_init_iter_all(&iter_all); | 
|---|
|  | 40 | +	int copied_bytes = 0; | 
|---|
|  | 41 | +	int actor_offset = 0; | 
|---|
| 51 | 42 |  | 
|---|
| 52 |  | -	bh = sb_bread(sb, *cur_index); | 
|---|
| 53 |  | -	if (bh == NULL) | 
|---|
| 54 |  | -		return NULL; | 
|---|
|  | 43 | +	if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) | 
|---|
|  | 44 | +		return 0; | 
|---|
| 55 | 45 |  | 
|---|
| 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; | 
|---|
|  | 46 | +	while (copied_bytes < req_length) { | 
|---|
|  | 47 | +		int bytes_to_copy = min_t(int, bvec->bv_len - offset, | 
|---|
|  | 48 | +					  PAGE_SIZE - actor_offset); | 
|---|
| 68 | 49 |  | 
|---|
| 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; | 
|---|
|  | 50 | +		bytes_to_copy = min_t(int, bytes_to_copy, | 
|---|
|  | 51 | +				      req_length - copied_bytes); | 
|---|
|  | 52 | +		memcpy(actor_addr + actor_offset, | 
|---|
|  | 53 | +		       page_address(bvec->bv_page) + bvec->bv_offset + offset, | 
|---|
|  | 54 | +		       bytes_to_copy); | 
|---|
|  | 55 | + | 
|---|
|  | 56 | +		actor_offset += bytes_to_copy; | 
|---|
|  | 57 | +		copied_bytes += bytes_to_copy; | 
|---|
|  | 58 | +		offset += bytes_to_copy; | 
|---|
|  | 59 | + | 
|---|
|  | 60 | +		if (actor_offset >= PAGE_SIZE) { | 
|---|
|  | 61 | +			actor_addr = squashfs_next_page(actor); | 
|---|
|  | 62 | +			if (!actor_addr) | 
|---|
|  | 63 | +				break; | 
|---|
|  | 64 | +			actor_offset = 0; | 
|---|
|  | 65 | +		} | 
|---|
|  | 66 | +		if (offset >= bvec->bv_len) { | 
|---|
|  | 67 | +			if (!bio_next_segment(bio, &iter_all)) | 
|---|
|  | 68 | +				break; | 
|---|
|  | 69 | +			offset = 0; | 
|---|
| 75 | 70 | } | 
|---|
| 76 | 71 | } | 
|---|
| 77 |  | - | 
|---|
| 78 |  | -	return bh; | 
|---|
|  | 72 | +	squashfs_finish_page(actor); | 
|---|
|  | 73 | +	return copied_bytes; | 
|---|
| 79 | 74 | } | 
|---|
| 80 | 75 |  | 
|---|
|  | 76 | +static int squashfs_bio_read(struct super_block *sb, u64 index, int length, | 
|---|
|  | 77 | +			     struct bio **biop, int *block_offset) | 
|---|
|  | 78 | +{ | 
|---|
|  | 79 | +	struct squashfs_sb_info *msblk = sb->s_fs_info; | 
|---|
|  | 80 | +	const u64 read_start = round_down(index, msblk->devblksize); | 
|---|
|  | 81 | +	const sector_t block = read_start >> msblk->devblksize_log2; | 
|---|
|  | 82 | +	const u64 read_end = round_up(index + length, msblk->devblksize); | 
|---|
|  | 83 | +	const sector_t block_end = read_end >> msblk->devblksize_log2; | 
|---|
|  | 84 | +	int offset = read_start - round_down(index, PAGE_SIZE); | 
|---|
|  | 85 | +	int total_len = (block_end - block) << msblk->devblksize_log2; | 
|---|
|  | 86 | +	const int page_count = DIV_ROUND_UP(total_len + offset, PAGE_SIZE); | 
|---|
|  | 87 | +	int error, i; | 
|---|
|  | 88 | +	struct bio *bio; | 
|---|
|  | 89 | + | 
|---|
|  | 90 | +	if (page_count <= BIO_MAX_PAGES) | 
|---|
|  | 91 | +		bio = bio_alloc(GFP_NOIO, page_count); | 
|---|
|  | 92 | +	else | 
|---|
|  | 93 | +		bio = bio_kmalloc(GFP_NOIO, page_count); | 
|---|
|  | 94 | + | 
|---|
|  | 95 | +	if (!bio) | 
|---|
|  | 96 | +		return -ENOMEM; | 
|---|
|  | 97 | + | 
|---|
|  | 98 | +	bio_set_dev(bio, sb->s_bdev); | 
|---|
|  | 99 | +	bio->bi_opf = READ; | 
|---|
|  | 100 | +	bio->bi_iter.bi_sector = block * (msblk->devblksize >> SECTOR_SHIFT); | 
|---|
|  | 101 | + | 
|---|
|  | 102 | +	for (i = 0; i < page_count; ++i) { | 
|---|
|  | 103 | +		unsigned int len = | 
|---|
|  | 104 | +			min_t(unsigned int, PAGE_SIZE - offset, total_len); | 
|---|
|  | 105 | +		struct page *page = alloc_page(GFP_NOIO); | 
|---|
|  | 106 | + | 
|---|
|  | 107 | +		if (!page) { | 
|---|
|  | 108 | +			error = -ENOMEM; | 
|---|
|  | 109 | +			goto out_free_bio; | 
|---|
|  | 110 | +		} | 
|---|
|  | 111 | +		if (!bio_add_page(bio, page, len, offset)) { | 
|---|
|  | 112 | +			error = -EIO; | 
|---|
|  | 113 | +			goto out_free_bio; | 
|---|
|  | 114 | +		} | 
|---|
|  | 115 | +		offset = 0; | 
|---|
|  | 116 | +		total_len -= len; | 
|---|
|  | 117 | +	} | 
|---|
|  | 118 | + | 
|---|
|  | 119 | +	error = submit_bio_wait(bio); | 
|---|
|  | 120 | +	if (error) | 
|---|
|  | 121 | +		goto out_free_bio; | 
|---|
|  | 122 | + | 
|---|
|  | 123 | +	*biop = bio; | 
|---|
|  | 124 | +	*block_offset = index & ((1 << msblk->devblksize_log2) - 1); | 
|---|
|  | 125 | +	return 0; | 
|---|
|  | 126 | + | 
|---|
|  | 127 | +out_free_bio: | 
|---|
|  | 128 | +	bio_free_pages(bio); | 
|---|
|  | 129 | +	bio_put(bio); | 
|---|
|  | 130 | +	return error; | 
|---|
|  | 131 | +} | 
|---|
| 81 | 132 |  | 
|---|
| 82 | 133 | /* | 
|---|
| 83 | 134 | * Read and decompress a metadata block or datablock.  Length is non-zero | 
|---|
| .. | .. | 
|---|
| 89 | 140 | * algorithms). | 
|---|
| 90 | 141 | */ | 
|---|
| 91 | 142 | int squashfs_read_data(struct super_block *sb, u64 index, int length, | 
|---|
| 92 |  | -		u64 *next_index, struct squashfs_page_actor *output) | 
|---|
|  | 143 | +		       u64 *next_index, struct squashfs_page_actor *output) | 
|---|
| 93 | 144 | { | 
|---|
| 94 | 145 | 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; | 
|---|
|  | 146 | +	struct bio *bio = NULL; | 
|---|
|  | 147 | +	int compressed; | 
|---|
|  | 148 | +	int res; | 
|---|
|  | 149 | +	int offset; | 
|---|
| 104 | 150 |  | 
|---|
| 105 | 151 | if (length) { | 
|---|
| 106 | 152 | /* | 
|---|
| 107 | 153 | * Datablock. | 
|---|
| 108 | 154 | */ | 
|---|
| 109 |  | -		bytes = -offset; | 
|---|
| 110 | 155 | compressed = SQUASHFS_COMPRESSED_BLOCK(length); | 
|---|
| 111 | 156 | length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); | 
|---|
| 112 |  | -		if (next_index) | 
|---|
| 113 |  | -			*next_index = index + length; | 
|---|
| 114 |  | - | 
|---|
| 115 | 157 | TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", | 
|---|
| 116 | 158 | 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 | 159 | } else { | 
|---|
| 130 | 160 | /* | 
|---|
| 131 | 161 | * Metadata block. | 
|---|
| 132 | 162 | */ | 
|---|
| 133 |  | -		if ((index + 2) > msblk->bytes_used) | 
|---|
| 134 |  | -			goto read_failure; | 
|---|
|  | 163 | +		const u8 *data; | 
|---|
|  | 164 | +		struct bvec_iter_all iter_all = {}; | 
|---|
|  | 165 | +		struct bio_vec *bvec = bvec_init_iter_all(&iter_all); | 
|---|
| 135 | 166 |  | 
|---|
| 136 |  | -		bh[0] = get_block_length(sb, &cur_index, &offset, &length); | 
|---|
| 137 |  | -		if (bh[0] == NULL) | 
|---|
| 138 |  | -			goto read_failure; | 
|---|
| 139 |  | -		b = 1; | 
|---|
|  | 167 | +		if (index + 2 > msblk->bytes_used) { | 
|---|
|  | 168 | +			res = -EIO; | 
|---|
|  | 169 | +			goto out; | 
|---|
|  | 170 | +		} | 
|---|
|  | 171 | +		res = squashfs_bio_read(sb, index, 2, &bio, &offset); | 
|---|
|  | 172 | +		if (res) | 
|---|
|  | 173 | +			goto out; | 
|---|
| 140 | 174 |  | 
|---|
| 141 |  | -		bytes = msblk->devblksize - offset; | 
|---|
|  | 175 | +		if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) { | 
|---|
|  | 176 | +			res = -EIO; | 
|---|
|  | 177 | +			goto out_free_bio; | 
|---|
|  | 178 | +		} | 
|---|
|  | 179 | +		/* Extract the length of the metadata block */ | 
|---|
|  | 180 | +		data = page_address(bvec->bv_page) + bvec->bv_offset; | 
|---|
|  | 181 | +		length = data[offset]; | 
|---|
|  | 182 | +		if (offset < bvec->bv_len - 1) { | 
|---|
|  | 183 | +			length |= data[offset + 1] << 8; | 
|---|
|  | 184 | +		} else { | 
|---|
|  | 185 | +			if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) { | 
|---|
|  | 186 | +				res = -EIO; | 
|---|
|  | 187 | +				goto out_free_bio; | 
|---|
|  | 188 | +			} | 
|---|
|  | 189 | +			data = page_address(bvec->bv_page) + bvec->bv_offset; | 
|---|
|  | 190 | +			length |= data[0] << 8; | 
|---|
|  | 191 | +		} | 
|---|
|  | 192 | +		bio_free_pages(bio); | 
|---|
|  | 193 | +		bio_put(bio); | 
|---|
|  | 194 | + | 
|---|
| 142 | 195 | compressed = SQUASHFS_COMPRESSED(length); | 
|---|
| 143 | 196 | length = SQUASHFS_COMPRESSED_SIZE(length); | 
|---|
| 144 |  | -		if (next_index) | 
|---|
| 145 |  | -			*next_index = index + length + 2; | 
|---|
|  | 197 | +		index += 2; | 
|---|
| 146 | 198 |  | 
|---|
| 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); | 
|---|
|  | 199 | +		TRACE("Block @ 0x%llx, %scompressed size %d\n", index - 2, | 
|---|
|  | 200 | +		      compressed ? "" : "un", length); | 
|---|
|  | 201 | +	} | 
|---|
|  | 202 | +	if (length < 0 || length > output->length || | 
|---|
|  | 203 | +			(index + length) > msblk->bytes_used) { | 
|---|
|  | 204 | +		res = -EIO; | 
|---|
|  | 205 | +		goto out; | 
|---|
| 161 | 206 | } | 
|---|
| 162 | 207 |  | 
|---|
| 163 |  | -	for (i = 0; i < b; i++) { | 
|---|
| 164 |  | -		wait_on_buffer(bh[i]); | 
|---|
| 165 |  | -		if (!buffer_uptodate(bh[i])) | 
|---|
| 166 |  | -			goto block_release; | 
|---|
| 167 |  | -	} | 
|---|
|  | 208 | +	if (next_index) | 
|---|
|  | 209 | +		*next_index = index + length; | 
|---|
|  | 210 | + | 
|---|
|  | 211 | +	res = squashfs_bio_read(sb, index, length, &bio, &offset); | 
|---|
|  | 212 | +	if (res) | 
|---|
|  | 213 | +		goto out; | 
|---|
| 168 | 214 |  | 
|---|
| 169 | 215 | 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]); | 
|---|
|  | 216 | +		if (!msblk->stream) { | 
|---|
|  | 217 | +			res = -EIO; | 
|---|
|  | 218 | +			goto out_free_bio; | 
|---|
| 201 | 219 | } | 
|---|
| 202 |  | -		squashfs_finish_page(output); | 
|---|
|  | 220 | +		res = squashfs_decompress(msblk, bio, offset, length, output); | 
|---|
|  | 221 | +	} else { | 
|---|
|  | 222 | +		res = copy_bio_to_actor(bio, output, offset, length); | 
|---|
| 203 | 223 | } | 
|---|
| 204 | 224 |  | 
|---|
| 205 |  | -	kfree(bh); | 
|---|
| 206 |  | -	return length; | 
|---|
|  | 225 | +out_free_bio: | 
|---|
|  | 226 | +	bio_free_pages(bio); | 
|---|
|  | 227 | +	bio_put(bio); | 
|---|
|  | 228 | +out: | 
|---|
|  | 229 | +	if (res < 0) | 
|---|
|  | 230 | +		ERROR("Failed to read block 0x%llx: %d\n", index, res); | 
|---|
| 207 | 231 |  | 
|---|
| 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; | 
|---|
|  | 232 | +	return res; | 
|---|
| 217 | 233 | } | 
|---|