Commit | Line | Data |
---|---|---|
6cbd5570 CM |
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 | ||
2e635a27 | 19 | #include <linux/module.h> |
1e1d2701 | 20 | #include "ctree.h" |
dee26a9f | 21 | #include "disk-io.h" |
9f5fae2f | 22 | #include "transaction.h" |
1de037a4 | 23 | #include "print-tree.h" |
1e1d2701 | 24 | |
6567e837 | 25 | #define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \ |
509659cd CM |
26 | sizeof(struct btrfs_item) * 2) / \ |
27 | BTRFS_CRC32_SIZE) - 1)) | |
b18c6685 | 28 | int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, |
dee26a9f | 29 | struct btrfs_root *root, |
b18c6685 | 30 | u64 objectid, u64 pos, |
3a686375 CM |
31 | u64 offset, u64 disk_num_blocks, |
32 | u64 num_blocks) | |
9f5fae2f | 33 | { |
dee26a9f CM |
34 | int ret = 0; |
35 | struct btrfs_file_extent_item *item; | |
36 | struct btrfs_key file_key; | |
5caf2a00 | 37 | struct btrfs_path *path; |
dee26a9f | 38 | |
5caf2a00 CM |
39 | path = btrfs_alloc_path(); |
40 | BUG_ON(!path); | |
dee26a9f | 41 | file_key.objectid = objectid; |
b18c6685 | 42 | file_key.offset = pos; |
dee26a9f CM |
43 | file_key.flags = 0; |
44 | btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); | |
45 | ||
5caf2a00 | 46 | ret = btrfs_insert_empty_item(trans, root, path, &file_key, |
dee26a9f | 47 | sizeof(*item)); |
9773a788 | 48 | BUG_ON(ret); |
5caf2a00 | 49 | item = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0], |
dee26a9f | 50 | struct btrfs_file_extent_item); |
b18c6685 | 51 | btrfs_set_file_extent_disk_blocknr(item, offset); |
3a686375 | 52 | btrfs_set_file_extent_disk_num_blocks(item, disk_num_blocks); |
dee26a9f | 53 | btrfs_set_file_extent_offset(item, 0); |
b18c6685 | 54 | btrfs_set_file_extent_num_blocks(item, num_blocks); |
71951f35 | 55 | btrfs_set_file_extent_generation(item, trans->transid); |
236454df | 56 | btrfs_set_file_extent_type(item, BTRFS_FILE_EXTENT_REG); |
5caf2a00 | 57 | btrfs_mark_buffer_dirty(path->nodes[0]); |
a429e513 | 58 | |
5caf2a00 CM |
59 | btrfs_release_path(root, path); |
60 | btrfs_free_path(path); | |
9f5fae2f CM |
61 | return 0; |
62 | } | |
dee26a9f | 63 | |
b18c6685 CM |
64 | struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans, |
65 | struct btrfs_root *root, | |
66 | struct btrfs_path *path, | |
67 | u64 objectid, u64 offset, | |
68 | int cow) | |
6567e837 CM |
69 | { |
70 | int ret; | |
71 | struct btrfs_key file_key; | |
72 | struct btrfs_key found_key; | |
73 | struct btrfs_csum_item *item; | |
74 | struct btrfs_leaf *leaf; | |
75 | u64 csum_offset = 0; | |
a429e513 | 76 | int csums_in_item; |
6567e837 CM |
77 | |
78 | file_key.objectid = objectid; | |
79 | file_key.offset = offset; | |
80 | file_key.flags = 0; | |
81 | btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY); | |
b18c6685 | 82 | ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow); |
6567e837 CM |
83 | if (ret < 0) |
84 | goto fail; | |
85 | leaf = btrfs_buffer_leaf(path->nodes[0]); | |
86 | if (ret > 0) { | |
87 | ret = 1; | |
70b2befd | 88 | if (path->slots[0] == 0) |
6567e837 CM |
89 | goto fail; |
90 | path->slots[0]--; | |
91 | btrfs_disk_key_to_cpu(&found_key, | |
92 | &leaf->items[path->slots[0]].key); | |
93 | if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY || | |
94 | found_key.objectid != objectid) { | |
95 | goto fail; | |
96 | } | |
97 | csum_offset = (offset - found_key.offset) >> | |
98 | root->fs_info->sb->s_blocksize_bits; | |
a429e513 | 99 | csums_in_item = btrfs_item_size(leaf->items + path->slots[0]); |
509659cd | 100 | csums_in_item /= BTRFS_CRC32_SIZE; |
a429e513 CM |
101 | |
102 | if (csum_offset >= csums_in_item) { | |
103 | ret = -EFBIG; | |
6567e837 CM |
104 | goto fail; |
105 | } | |
106 | } | |
107 | item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); | |
509659cd CM |
108 | item = (struct btrfs_csum_item *)((unsigned char *)item + |
109 | csum_offset * BTRFS_CRC32_SIZE); | |
6567e837 CM |
110 | return item; |
111 | fail: | |
112 | if (ret > 0) | |
b18c6685 | 113 | ret = -ENOENT; |
6567e837 CM |
114 | return ERR_PTR(ret); |
115 | } | |
116 | ||
117 | ||
dee26a9f CM |
118 | int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, |
119 | struct btrfs_root *root, | |
120 | struct btrfs_path *path, u64 objectid, | |
9773a788 | 121 | u64 offset, int mod) |
dee26a9f CM |
122 | { |
123 | int ret; | |
124 | struct btrfs_key file_key; | |
125 | int ins_len = mod < 0 ? -1 : 0; | |
126 | int cow = mod != 0; | |
127 | ||
128 | file_key.objectid = objectid; | |
70b2befd | 129 | file_key.offset = offset; |
dee26a9f CM |
130 | file_key.flags = 0; |
131 | btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); | |
132 | ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow); | |
133 | return ret; | |
134 | } | |
f254e52c CM |
135 | |
136 | int btrfs_csum_file_block(struct btrfs_trans_handle *trans, | |
137 | struct btrfs_root *root, | |
138 | u64 objectid, u64 offset, | |
139 | char *data, size_t len) | |
140 | { | |
141 | int ret; | |
142 | struct btrfs_key file_key; | |
6567e837 | 143 | struct btrfs_key found_key; |
5caf2a00 | 144 | struct btrfs_path *path; |
f254e52c | 145 | struct btrfs_csum_item *item; |
6567e837 CM |
146 | struct btrfs_leaf *leaf; |
147 | u64 csum_offset; | |
f254e52c | 148 | |
5caf2a00 CM |
149 | path = btrfs_alloc_path(); |
150 | BUG_ON(!path); | |
b18c6685 | 151 | |
f254e52c CM |
152 | file_key.objectid = objectid; |
153 | file_key.offset = offset; | |
154 | file_key.flags = 0; | |
155 | btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY); | |
a429e513 CM |
156 | |
157 | item = btrfs_lookup_csum(trans, root, path, objectid, offset, 1); | |
158 | if (!IS_ERR(item)) | |
159 | goto found; | |
160 | ret = PTR_ERR(item); | |
161 | if (ret == -EFBIG) { | |
162 | u32 item_size; | |
163 | /* we found one, but it isn't big enough yet */ | |
164 | leaf = btrfs_buffer_leaf(path->nodes[0]); | |
165 | item_size = btrfs_item_size(leaf->items + path->slots[0]); | |
509659cd | 166 | if ((item_size / BTRFS_CRC32_SIZE) >= MAX_CSUM_ITEMS(root)) { |
a429e513 CM |
167 | /* already at max size, make a new one */ |
168 | goto insert; | |
169 | } | |
170 | } else { | |
171 | /* we didn't find a csum item, insert one */ | |
172 | goto insert; | |
173 | } | |
174 | ||
175 | /* | |
176 | * at this point, we know the tree has an item, but it isn't big | |
177 | * enough yet to put our csum in. Grow it | |
178 | */ | |
179 | btrfs_release_path(root, path); | |
6567e837 | 180 | ret = btrfs_search_slot(trans, root, &file_key, path, |
509659cd | 181 | BTRFS_CRC32_SIZE, 1); |
6567e837 CM |
182 | if (ret < 0) |
183 | goto fail; | |
184 | if (ret == 0) { | |
b18c6685 | 185 | BUG(); |
6567e837 CM |
186 | } |
187 | if (path->slots[0] == 0) { | |
6567e837 CM |
188 | goto insert; |
189 | } | |
190 | path->slots[0]--; | |
191 | leaf = btrfs_buffer_leaf(path->nodes[0]); | |
192 | btrfs_disk_key_to_cpu(&found_key, &leaf->items[path->slots[0]].key); | |
193 | csum_offset = (offset - found_key.offset) >> | |
194 | root->fs_info->sb->s_blocksize_bits; | |
195 | if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY || | |
196 | found_key.objectid != objectid || | |
197 | csum_offset >= MAX_CSUM_ITEMS(root)) { | |
6567e837 CM |
198 | goto insert; |
199 | } | |
200 | if (csum_offset >= btrfs_item_size(leaf->items + path->slots[0]) / | |
509659cd CM |
201 | BTRFS_CRC32_SIZE) { |
202 | u32 diff = (csum_offset + 1) * BTRFS_CRC32_SIZE; | |
a429e513 | 203 | diff = diff - btrfs_item_size(leaf->items + path->slots[0]); |
3a686375 CM |
204 | if (diff != BTRFS_CRC32_SIZE) |
205 | goto insert; | |
a429e513 | 206 | ret = btrfs_extend_item(trans, root, path, diff); |
6567e837 CM |
207 | BUG_ON(ret); |
208 | goto csum; | |
209 | } | |
210 | ||
211 | insert: | |
a429e513 | 212 | btrfs_release_path(root, path); |
6567e837 | 213 | csum_offset = 0; |
5caf2a00 | 214 | ret = btrfs_insert_empty_item(trans, root, path, &file_key, |
509659cd | 215 | BTRFS_CRC32_SIZE); |
a429e513 | 216 | if (ret != 0) { |
a429e513 | 217 | WARN_ON(1); |
f254e52c | 218 | goto fail; |
a429e513 | 219 | } |
6567e837 | 220 | csum: |
5caf2a00 | 221 | item = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0], |
f254e52c CM |
222 | struct btrfs_csum_item); |
223 | ret = 0; | |
509659cd CM |
224 | item = (struct btrfs_csum_item *)((unsigned char *)item + |
225 | csum_offset * BTRFS_CRC32_SIZE); | |
b18c6685 | 226 | found: |
509659cd CM |
227 | btrfs_check_bounds(&item->csum, BTRFS_CRC32_SIZE, |
228 | path->nodes[0]->b_data, | |
229 | root->fs_info->sb->s_blocksize); | |
230 | ret = btrfs_csum_data(root, data, len, &item->csum); | |
9ebefb18 | 231 | // printk("file %lu offset %llu csum %X\n", objectid, (unsigned long long)offset, *(int *)(&item->csum)); |
5caf2a00 | 232 | btrfs_mark_buffer_dirty(path->nodes[0]); |
f254e52c | 233 | fail: |
5caf2a00 CM |
234 | btrfs_release_path(root, path); |
235 | btrfs_free_path(path); | |
f254e52c CM |
236 | return ret; |
237 | } | |
238 | ||
1de037a4 CM |
239 | int btrfs_csum_truncate(struct btrfs_trans_handle *trans, |
240 | struct btrfs_root *root, struct btrfs_path *path, | |
241 | u64 isize) | |
242 | { | |
243 | struct btrfs_key key; | |
244 | struct btrfs_leaf *leaf = btrfs_buffer_leaf(path->nodes[0]); | |
245 | int slot = path->slots[0]; | |
246 | int ret; | |
247 | u32 new_item_size; | |
248 | u64 new_item_span; | |
249 | u64 blocks; | |
250 | ||
251 | btrfs_disk_key_to_cpu(&key, &leaf->items[slot].key); | |
252 | if (isize <= key.offset) | |
253 | return 0; | |
254 | new_item_span = isize - key.offset; | |
84f54cfa CM |
255 | blocks = (new_item_span + root->blocksize - 1) >> |
256 | root->fs_info->sb->s_blocksize_bits; | |
1de037a4 CM |
257 | new_item_size = blocks * BTRFS_CRC32_SIZE; |
258 | if (new_item_size >= btrfs_item_size(leaf->items + slot)) | |
259 | return 0; | |
260 | ret = btrfs_truncate_item(trans, root, path, new_item_size); | |
261 | BUG_ON(ret); | |
262 | return ret; | |
263 | } | |
264 | ||
f254e52c CM |
265 | int btrfs_csum_verify_file_block(struct btrfs_root *root, |
266 | u64 objectid, u64 offset, | |
267 | char *data, size_t len) | |
268 | { | |
269 | int ret; | |
270 | struct btrfs_key file_key; | |
5caf2a00 | 271 | struct btrfs_path *path; |
f254e52c | 272 | struct btrfs_csum_item *item; |
509659cd | 273 | char result[BTRFS_CRC32_SIZE]; |
f254e52c | 274 | |
5caf2a00 CM |
275 | path = btrfs_alloc_path(); |
276 | BUG_ON(!path); | |
f254e52c CM |
277 | file_key.objectid = objectid; |
278 | file_key.offset = offset; | |
279 | file_key.flags = 0; | |
280 | btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY); | |
2da566ed | 281 | mutex_lock(&root->fs_info->fs_mutex); |
6567e837 | 282 | |
b18c6685 | 283 | item = btrfs_lookup_csum(NULL, root, path, objectid, offset, 0); |
6567e837 CM |
284 | if (IS_ERR(item)) { |
285 | ret = PTR_ERR(item); | |
a429e513 CM |
286 | /* a csum that isn't present is a preallocated region. */ |
287 | if (ret == -ENOENT || ret == -EFBIG) | |
3a686375 | 288 | ret = -ENOENT; |
f254e52c | 289 | goto fail; |
6567e837 CM |
290 | } |
291 | ||
f254e52c CM |
292 | ret = btrfs_csum_data(root, data, len, result); |
293 | WARN_ON(ret); | |
509659cd | 294 | if (memcmp(result, &item->csum, BTRFS_CRC32_SIZE)) |
f254e52c CM |
295 | ret = 1; |
296 | fail: | |
5caf2a00 CM |
297 | btrfs_release_path(root, path); |
298 | btrfs_free_path(path); | |
2da566ed | 299 | mutex_unlock(&root->fs_info->fs_mutex); |
f254e52c CM |
300 | return ret; |
301 | } |