file-item.c 10.1 KB
Newer Older
C
Chris Mason 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright (C) 2007 Oracle.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License v2 as published by the Free Software Foundation.
 *
 * 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, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 021110-1307, USA.
 */

19 20 21
#include <linux/bio.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
C
Chris Mason 已提交
22
#include "ctree.h"
C
Chris Mason 已提交
23
#include "disk-io.h"
24
#include "transaction.h"
C
Chris Mason 已提交
25
#include "print-tree.h"
C
Chris Mason 已提交
26

27
#define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \
28 29
			       sizeof(struct btrfs_item) * 2) / \
			       BTRFS_CRC32_SIZE) - 1))
C
Chris Mason 已提交
30
int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
C
Chris Mason 已提交
31
			       struct btrfs_root *root,
C
Chris Mason 已提交
32
			       u64 objectid, u64 pos,
33 34
			       u64 offset, u64 disk_num_bytes,
			       u64 num_bytes)
35
{
C
Chris Mason 已提交
36 37 38
	int ret = 0;
	struct btrfs_file_extent_item *item;
	struct btrfs_key file_key;
39
	struct btrfs_path *path;
40
	struct extent_buffer *leaf;
C
Chris Mason 已提交
41

42 43
	path = btrfs_alloc_path();
	BUG_ON(!path);
C
Chris Mason 已提交
44
	file_key.objectid = objectid;
C
Chris Mason 已提交
45
	file_key.offset = pos;
C
Chris Mason 已提交
46 47
	btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);

48
	ret = btrfs_insert_empty_item(trans, root, path, &file_key,
C
Chris Mason 已提交
49
				      sizeof(*item));
50 51
	if (ret < 0)
		goto out;
C
Chris Mason 已提交
52
	BUG_ON(ret);
53 54
	leaf = path->nodes[0];
	item = btrfs_item_ptr(leaf, path->slots[0],
C
Chris Mason 已提交
55
			      struct btrfs_file_extent_item);
56 57
	btrfs_set_file_extent_disk_bytenr(leaf, item, offset);
	btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes);
58
	btrfs_set_file_extent_offset(leaf, item, 0);
59
	btrfs_set_file_extent_num_bytes(leaf, item, num_bytes);
60 61 62
	btrfs_set_file_extent_generation(leaf, item, trans->transid);
	btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG);
	btrfs_mark_buffer_dirty(leaf);
63
out:
64
	btrfs_free_path(path);
65
	return ret;
66
}
C
Chris Mason 已提交
67

C
Chris Mason 已提交
68 69 70 71 72
struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans,
					  struct btrfs_root *root,
					  struct btrfs_path *path,
					  u64 objectid, u64 offset,
					  int cow)
73 74 75 76 77
{
	int ret;
	struct btrfs_key file_key;
	struct btrfs_key found_key;
	struct btrfs_csum_item *item;
78
	struct extent_buffer *leaf;
79
	u64 csum_offset = 0;
80
	int csums_in_item;
81 82 83 84

	file_key.objectid = objectid;
	file_key.offset = offset;
	btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
C
Chris Mason 已提交
85
	ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow);
86 87
	if (ret < 0)
		goto fail;
88
	leaf = path->nodes[0];
89 90
	if (ret > 0) {
		ret = 1;
91
		if (path->slots[0] == 0)
92 93
			goto fail;
		path->slots[0]--;
94
		btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
95 96 97 98 99 100
		if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
		    found_key.objectid != objectid) {
			goto fail;
		}
		csum_offset = (offset - found_key.offset) >>
				root->fs_info->sb->s_blocksize_bits;
101
		csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]);
102
		csums_in_item /= BTRFS_CRC32_SIZE;
103 104 105

		if (csum_offset >= csums_in_item) {
			ret = -EFBIG;
106 107 108 109
			goto fail;
		}
	}
	item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
110 111
	item = (struct btrfs_csum_item *)((unsigned char *)item +
					  csum_offset * BTRFS_CRC32_SIZE);
112 113 114
	return item;
fail:
	if (ret > 0)
C
Chris Mason 已提交
115
		ret = -ENOENT;
116 117 118 119
	return ERR_PTR(ret);
}


C
Chris Mason 已提交
120 121 122
int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
			     struct btrfs_root *root,
			     struct btrfs_path *path, u64 objectid,
C
Chris Mason 已提交
123
			     u64 offset, int mod)
C
Chris Mason 已提交
124 125 126 127 128 129 130
{
	int ret;
	struct btrfs_key file_key;
	int ins_len = mod < 0 ? -1 : 0;
	int cow = mod != 0;

	file_key.objectid = objectid;
131
	file_key.offset = offset;
C
Chris Mason 已提交
132 133 134 135
	btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
	ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow);
	return ret;
}
C
Chris Mason 已提交
136

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
int btrfs_csum_one_bio(struct btrfs_root *root,
		       struct bio *bio, char **sums_ret)
{
	u32 *sums;
	char *data;
	struct bio_vec *bvec = bio->bi_io_vec;
	int bio_index = 0;

	sums = kmalloc(bio->bi_vcnt * BTRFS_CRC32_SIZE, GFP_NOFS);
	if (!sums)
		return -ENOMEM;
	*sums_ret = (char *)sums;

	while(bio_index < bio->bi_vcnt) {
		data = kmap_atomic(bvec->bv_page, KM_USER0);
		*sums = ~(u32)0;
		*sums = btrfs_csum_data(root, data + bvec->bv_offset,
					*sums, bvec->bv_len);
		kunmap_atomic(data, KM_USER0);
		btrfs_csum_final(*sums, (char *)sums);
		sums++;
		bio_index++;
		bvec++;
	}
	return 0;
}

164 165
int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
			   struct btrfs_root *root, struct inode *inode,
166
			   struct bio *bio, char *sums)
C
Chris Mason 已提交
167
{
168 169
	u64 objectid = inode->i_ino;
	u64 offset;
C
Chris Mason 已提交
170 171
	int ret;
	struct btrfs_key file_key;
172
	struct btrfs_key found_key;
173 174
	u64 next_offset;
	int found_next;
175
	struct btrfs_path *path;
C
Chris Mason 已提交
176
	struct btrfs_csum_item *item;
177
	struct btrfs_csum_item *item_end;
178
	struct extent_buffer *leaf = NULL;
179
	u64 csum_offset;
180
	u32 *sums32 = (u32 *)sums;
181 182
	u32 nritems;
	u32 ins_size;
183 184
	int bio_index = 0;
	struct bio_vec *bvec = bio->bi_io_vec;
185 186 187 188 189
	char *eb_map;
	char *eb_token;
	unsigned long map_len;
	unsigned long map_start;

190 191
	path = btrfs_alloc_path();
	BUG_ON(!path);
192 193 194 195
again:
	next_offset = (u64)-1;
	found_next = 0;
	offset = page_offset(bvec->bv_page) + bvec->bv_offset;
C
Chris Mason 已提交
196 197 198
	file_key.objectid = objectid;
	file_key.offset = offset;
	btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
199 200

	item = btrfs_lookup_csum(trans, root, path, objectid, offset, 1);
201 202
	if (!IS_ERR(item)) {
		leaf = path->nodes[0];
203
		goto found;
204
	}
205 206 207 208
	ret = PTR_ERR(item);
	if (ret == -EFBIG) {
		u32 item_size;
		/* we found one, but it isn't big enough yet */
209 210
		leaf = path->nodes[0];
		item_size = btrfs_item_size_nr(leaf, path->slots[0]);
211
		if ((item_size / BTRFS_CRC32_SIZE) >= MAX_CSUM_ITEMS(root)) {
212 213 214 215
			/* already at max size, make a new one */
			goto insert;
		}
	} else {
216
		int slot = path->slots[0] + 1;
217
		/* we didn't find a csum item, insert one */
218 219 220
		nritems = btrfs_header_nritems(path->nodes[0]);
		if (path->slots[0] >= nritems - 1) {
			ret = btrfs_next_leaf(root, path);
Y
Yan 已提交
221
			if (ret == 1)
222
				found_next = 1;
Y
Yan 已提交
223
			if (ret != 0)
224
				goto insert;
Y
Yan 已提交
225
			slot = 0;
226 227 228 229 230 231 232 233 234
		}
		btrfs_item_key_to_cpu(path->nodes[0], &found_key, slot);
		if (found_key.objectid != objectid ||
		    found_key.type != BTRFS_CSUM_ITEM_KEY) {
			found_next = 1;
			goto insert;
		}
		next_offset = found_key.offset;
		found_next = 1;
235 236 237 238 239 240 241 242
		goto insert;
	}

	/*
	 * at this point, we know the tree has an item, but it isn't big
	 * enough yet to put our csum in.  Grow it
	 */
	btrfs_release_path(root, path);
243
	ret = btrfs_search_slot(trans, root, &file_key, path,
244
				BTRFS_CRC32_SIZE, 1);
245 246 247
	if (ret < 0)
		goto fail;
	if (ret == 0) {
C
Chris Mason 已提交
248
		BUG();
249 250 251 252 253
	}
	if (path->slots[0] == 0) {
		goto insert;
	}
	path->slots[0]--;
254 255
	leaf = path->nodes[0];
	btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
256 257 258 259 260 261 262
	csum_offset = (offset - found_key.offset) >>
			root->fs_info->sb->s_blocksize_bits;
	if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
	    found_key.objectid != objectid ||
	    csum_offset >= MAX_CSUM_ITEMS(root)) {
		goto insert;
	}
263
	if (csum_offset >= btrfs_item_size_nr(leaf, path->slots[0]) /
264 265
	    BTRFS_CRC32_SIZE) {
		u32 diff = (csum_offset + 1) * BTRFS_CRC32_SIZE;
266
		diff = diff - btrfs_item_size_nr(leaf, path->slots[0]);
C
Chris Mason 已提交
267 268
		if (diff != BTRFS_CRC32_SIZE)
			goto insert;
269
		ret = btrfs_extend_item(trans, root, path, diff);
270 271 272 273 274
		BUG_ON(ret);
		goto csum;
	}

insert:
275
	btrfs_release_path(root, path);
276
	csum_offset = 0;
277 278
	if (found_next) {
		u64 tmp = min((u64)i_size_read(inode), next_offset);
Y
Yan 已提交
279
		tmp -= offset & ~((u64)root->sectorsize -1);
280 281 282 283 284 285 286
		tmp >>= root->fs_info->sb->s_blocksize_bits;
		tmp = max((u64)1, tmp);
		tmp = min(tmp, (u64)MAX_CSUM_ITEMS(root));
		ins_size = BTRFS_CRC32_SIZE * tmp;
	} else {
		ins_size = BTRFS_CRC32_SIZE;
	}
287
	ret = btrfs_insert_empty_item(trans, root, path, &file_key,
288
				      ins_size);
289 290
	if (ret < 0)
		goto fail;
291 292
	if (ret != 0) {
		WARN_ON(1);
C
Chris Mason 已提交
293
		goto fail;
294
	}
295
csum:
296 297
	leaf = path->nodes[0];
	item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
C
Chris Mason 已提交
298
	ret = 0;
299 300
	item = (struct btrfs_csum_item *)((unsigned char *)item +
					  csum_offset * BTRFS_CRC32_SIZE);
C
Chris Mason 已提交
301
found:
302 303 304
	item_end = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
	item_end = (struct btrfs_csum_item *)((unsigned char *)item_end +
				      btrfs_item_size_nr(leaf, path->slots[0]));
305
	eb_token = NULL;
306
next_bvec:
307

308 309 310 311 312
	if (!eb_token ||
	   (unsigned long)item  + BTRFS_CRC32_SIZE >= map_start + map_len) {
		int err;

		if (eb_token)
313
			unmap_extent_buffer(leaf, eb_token, KM_USER1);
314 315 316 317
		eb_token = NULL;
		err = map_private_extent_buffer(leaf, (unsigned long)item,
						BTRFS_CRC32_SIZE,
						&eb_token, &eb_map,
318
						&map_start, &map_len, KM_USER1);
319 320 321 322 323
		if (err)
			eb_token = NULL;
	}
	if (eb_token) {
		memcpy(eb_token + ((unsigned long)item & (PAGE_CACHE_SIZE - 1)),
324
		       sums32, BTRFS_CRC32_SIZE);
325
	} else {
326
		write_extent_buffer(leaf, sums32, (unsigned long)item,
327 328
				    BTRFS_CRC32_SIZE);
	}
329 330
	bio_index++;
	bvec++;
331
	sums32++;
332
	if (bio_index < bio->bi_vcnt) {
333 334
		item = (struct btrfs_csum_item *)((char *)item +
						  BTRFS_CRC32_SIZE);
335 336 337
		if (item < item_end && offset + PAGE_CACHE_SIZE ==
		    page_offset(bvec->bv_page)) {
			offset = page_offset(bvec->bv_page);
338
			goto next_bvec;
339
		}
340
	}
341
	if (eb_token) {
342
		unmap_extent_buffer(leaf, eb_token, KM_USER1);
343 344
		eb_token = NULL;
	}
345
	btrfs_mark_buffer_dirty(path->nodes[0]);
346 347 348 349
	if (bio_index < bio->bi_vcnt) {
		btrfs_release_path(root, path);
		goto again;
	}
C
Chris Mason 已提交
350
fail:
351
	btrfs_free_path(path);
C
Chris Mason 已提交
352 353 354
	return ret;
}

C
Chris Mason 已提交
355 356 357 358 359
int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
			struct btrfs_root *root, struct btrfs_path *path,
			u64 isize)
{
	struct btrfs_key key;
360
	struct extent_buffer *leaf = path->nodes[0];
C
Chris Mason 已提交
361 362 363 364 365 366
	int slot = path->slots[0];
	int ret;
	u32 new_item_size;
	u64 new_item_span;
	u64 blocks;

367
	btrfs_item_key_to_cpu(leaf, &key, slot);
C
Chris Mason 已提交
368 369 370
	if (isize <= key.offset)
		return 0;
	new_item_span = isize - key.offset;
371
	blocks = (new_item_span + root->sectorsize - 1) >>
C
Chris Mason 已提交
372
		root->fs_info->sb->s_blocksize_bits;
C
Chris Mason 已提交
373
	new_item_size = blocks * BTRFS_CRC32_SIZE;
374
	if (new_item_size >= btrfs_item_size_nr(leaf, slot))
C
Chris Mason 已提交
375
		return 0;
376
	ret = btrfs_truncate_item(trans, root, path, new_item_size, 1);
C
Chris Mason 已提交
377 378 379 380
	BUG_ON(ret);
	return ret;
}