file-item.c revision aadfeb6e39ad6bde080cb3ab23f4da57ccb25f4a
1/*
2 * Copyright (C) 2007 Oracle.  All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
17 */
18
19#include "ctree.h"
20#include "disk-io.h"
21#include "transaction.h"
22#include "print-tree.h"
23
24#define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \
25			       sizeof(struct btrfs_item) * 2) / \
26			       BTRFS_CRC32_SIZE) - 1))
27int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
28			       struct btrfs_root *root,
29			       u64 objectid, u64 pos,
30			       u64 offset, u64 disk_num_bytes,
31			       u64 num_bytes)
32{
33	int ret = 0;
34	struct btrfs_file_extent_item *item;
35	struct btrfs_key file_key;
36	struct btrfs_path *path;
37	struct extent_buffer *leaf;
38
39	path = btrfs_alloc_path();
40	BUG_ON(!path);
41	file_key.objectid = objectid;
42	file_key.offset = pos;
43	btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
44
45	ret = btrfs_insert_empty_item(trans, root, path, &file_key,
46				      sizeof(*item));
47	if (ret < 0)
48		goto out;
49	BUG_ON(ret);
50	leaf = path->nodes[0];
51	item = btrfs_item_ptr(leaf, path->slots[0],
52			      struct btrfs_file_extent_item);
53	btrfs_set_file_extent_disk_bytenr(leaf, item, offset);
54	btrfs_set_file_extent_disk_num_bytes(leaf, item, disk_num_bytes);
55	btrfs_set_file_extent_offset(leaf, item, 0);
56	btrfs_set_file_extent_num_bytes(leaf, item, num_bytes);
57	btrfs_set_file_extent_generation(leaf, item, trans->transid);
58	btrfs_set_file_extent_type(leaf, item, BTRFS_FILE_EXTENT_REG);
59	btrfs_mark_buffer_dirty(leaf);
60out:
61	btrfs_free_path(path);
62	return ret;
63}
64
65struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans,
66					  struct btrfs_root *root,
67					  struct btrfs_path *path,
68					  u64 objectid, u64 offset,
69					  int cow)
70{
71	int ret;
72	struct btrfs_key file_key;
73	struct btrfs_key found_key;
74	struct btrfs_csum_item *item;
75	struct extent_buffer *leaf;
76	u64 csum_offset = 0;
77	int csums_in_item;
78
79	file_key.objectid = objectid;
80	file_key.offset = offset;
81	btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
82	ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow);
83	if (ret < 0)
84		goto fail;
85	leaf = path->nodes[0];
86	if (ret > 0) {
87		ret = 1;
88		if (path->slots[0] == 0)
89			goto fail;
90		path->slots[0]--;
91		btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
92		if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
93		    found_key.objectid != objectid) {
94			goto fail;
95		}
96		csum_offset = (offset - found_key.offset) >>
97				root->fs_info->sb->s_blocksize_bits;
98		csums_in_item = btrfs_item_size_nr(leaf, path->slots[0]);
99		csums_in_item /= BTRFS_CRC32_SIZE;
100
101		if (csum_offset >= csums_in_item) {
102			ret = -EFBIG;
103			goto fail;
104		}
105	}
106	item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
107	item = (struct btrfs_csum_item *)((unsigned char *)item +
108					  csum_offset * BTRFS_CRC32_SIZE);
109	return item;
110fail:
111	if (ret > 0)
112		ret = -ENOENT;
113	return ERR_PTR(ret);
114}
115
116
117int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
118			     struct btrfs_root *root,
119			     struct btrfs_path *path, u64 objectid,
120			     u64 offset, int mod)
121{
122	int ret;
123	struct btrfs_key file_key;
124	int ins_len = mod < 0 ? -1 : 0;
125	int cow = mod != 0;
126
127	file_key.objectid = objectid;
128	file_key.offset = offset;
129	btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
130	ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow);
131	return ret;
132}
133
134int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
135			  struct btrfs_root *root,
136			  struct inode *inode,
137			  u64 objectid, u64 offset,
138			  char *data, size_t len)
139{
140	int ret;
141	struct btrfs_key file_key;
142	struct btrfs_key found_key;
143	u64 next_offset = (u64)-1;
144	int found_next = 0;
145	struct btrfs_path *path;
146	struct btrfs_csum_item *item;
147	struct extent_buffer *leaf = NULL;
148	u64 csum_offset;
149	u32 csum_result = ~(u32)0;
150	u32 nritems;
151	u32 ins_size;
152
153	path = btrfs_alloc_path();
154	BUG_ON(!path);
155
156	file_key.objectid = objectid;
157	file_key.offset = offset;
158	btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
159
160	item = btrfs_lookup_csum(trans, root, path, objectid, offset, 1);
161	if (!IS_ERR(item)) {
162		leaf = path->nodes[0];
163		goto found;
164	}
165	ret = PTR_ERR(item);
166	if (ret == -EFBIG) {
167		u32 item_size;
168		/* we found one, but it isn't big enough yet */
169		leaf = path->nodes[0];
170		item_size = btrfs_item_size_nr(leaf, path->slots[0]);
171		if ((item_size / BTRFS_CRC32_SIZE) >= MAX_CSUM_ITEMS(root)) {
172			/* already at max size, make a new one */
173			goto insert;
174		}
175	} else {
176		int slot = path->slots[0] + 1;
177		/* we didn't find a csum item, insert one */
178		nritems = btrfs_header_nritems(path->nodes[0]);
179		if (path->slots[0] >= nritems - 1) {
180			ret = btrfs_next_leaf(root, path);
181			if (ret == 1)
182				found_next = 1;
183			if (ret != 0)
184				goto insert;
185			slot = 0;
186		}
187		btrfs_item_key_to_cpu(path->nodes[0], &found_key, slot);
188		if (found_key.objectid != objectid ||
189		    found_key.type != BTRFS_CSUM_ITEM_KEY) {
190			found_next = 1;
191			goto insert;
192		}
193		next_offset = found_key.offset;
194		found_next = 1;
195		goto insert;
196	}
197
198	/*
199	 * at this point, we know the tree has an item, but it isn't big
200	 * enough yet to put our csum in.  Grow it
201	 */
202	btrfs_release_path(root, path);
203	ret = btrfs_search_slot(trans, root, &file_key, path,
204				BTRFS_CRC32_SIZE, 1);
205	if (ret < 0)
206		goto fail;
207	if (ret == 0) {
208		BUG();
209	}
210	if (path->slots[0] == 0) {
211		goto insert;
212	}
213	path->slots[0]--;
214	leaf = path->nodes[0];
215	btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
216	csum_offset = (offset - found_key.offset) >>
217			root->fs_info->sb->s_blocksize_bits;
218	if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
219	    found_key.objectid != objectid ||
220	    csum_offset >= MAX_CSUM_ITEMS(root)) {
221		goto insert;
222	}
223	if (csum_offset >= btrfs_item_size_nr(leaf, path->slots[0]) /
224	    BTRFS_CRC32_SIZE) {
225		u32 diff = (csum_offset + 1) * BTRFS_CRC32_SIZE;
226		diff = diff - btrfs_item_size_nr(leaf, path->slots[0]);
227		if (diff != BTRFS_CRC32_SIZE)
228			goto insert;
229		ret = btrfs_extend_item(trans, root, path, diff);
230		BUG_ON(ret);
231		goto csum;
232	}
233
234insert:
235	btrfs_release_path(root, path);
236	csum_offset = 0;
237	if (found_next) {
238		u64 tmp = min((u64)i_size_read(inode), next_offset);
239		tmp -= offset & ~((u64)root->sectorsize -1);
240		tmp >>= root->fs_info->sb->s_blocksize_bits;
241		tmp = max((u64)1, tmp);
242		tmp = min(tmp, (u64)MAX_CSUM_ITEMS(root));
243		ins_size = BTRFS_CRC32_SIZE * tmp;
244	} else {
245		ins_size = BTRFS_CRC32_SIZE;
246	}
247	ret = btrfs_insert_empty_item(trans, root, path, &file_key,
248				      ins_size);
249	if (ret < 0)
250		goto fail;
251	if (ret != 0) {
252		WARN_ON(1);
253		goto fail;
254	}
255csum:
256	leaf = path->nodes[0];
257	item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
258	ret = 0;
259	item = (struct btrfs_csum_item *)((unsigned char *)item +
260					  csum_offset * BTRFS_CRC32_SIZE);
261found:
262	csum_result = btrfs_csum_data(root, data, csum_result, len);
263	btrfs_csum_final(csum_result, (char *)&csum_result);
264	if (csum_result == 0) {
265		printk("csum result is 0 for inode %lu offset %Lu\n", inode->i_ino, offset);
266	}
267
268	write_extent_buffer(leaf, &csum_result, (unsigned long)item,
269			    BTRFS_CRC32_SIZE);
270	btrfs_mark_buffer_dirty(path->nodes[0]);
271fail:
272	btrfs_release_path(root, path);
273	btrfs_free_path(path);
274	return ret;
275}
276
277int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
278			struct btrfs_root *root, struct btrfs_path *path,
279			u64 isize)
280{
281	struct btrfs_key key;
282	struct extent_buffer *leaf = path->nodes[0];
283	int slot = path->slots[0];
284	int ret;
285	u32 new_item_size;
286	u64 new_item_span;
287	u64 blocks;
288
289	btrfs_item_key_to_cpu(leaf, &key, slot);
290	if (isize <= key.offset)
291		return 0;
292	new_item_span = isize - key.offset;
293	blocks = (new_item_span + root->sectorsize - 1) >>
294		root->fs_info->sb->s_blocksize_bits;
295	new_item_size = blocks * BTRFS_CRC32_SIZE;
296	if (new_item_size >= btrfs_item_size_nr(leaf, slot))
297		return 0;
298	ret = btrfs_truncate_item(trans, root, path, new_item_size, 1);
299	BUG_ON(ret);
300	return ret;
301}
302
303