blob: 0782e924ddebf0194566b630eb46ca19ee8bfbaf [file] [log] [blame]
Chris Mason2e635a22007-03-21 11:12:56 -04001#include <linux/module.h>
Chris Mason1e1d2702007-03-15 19:03:33 -04002#include "ctree.h"
Chris Masondee26a92007-03-26 16:00:06 -04003#include "disk-io.h"
Chris Mason9f5fae22007-03-20 14:38:32 -04004#include "transaction.h"
Chris Mason1de037a2007-05-29 15:17:08 -04005#include "print-tree.h"
Chris Mason1e1d2702007-03-15 19:03:33 -04006
Chris Mason6567e832007-04-16 09:22:45 -04007#define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \
Chris Mason509659c2007-05-10 12:36:17 -04008 sizeof(struct btrfs_item) * 2) / \
9 BTRFS_CRC32_SIZE) - 1))
Chris Masonb18c6682007-04-17 13:26:50 -040010int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
Chris Masondee26a92007-03-26 16:00:06 -040011 struct btrfs_root *root,
Chris Masonb18c6682007-04-17 13:26:50 -040012 u64 objectid, u64 pos,
Chris Mason3a686372007-05-24 13:35:57 -040013 u64 offset, u64 disk_num_blocks,
14 u64 num_blocks)
Chris Mason9f5fae22007-03-20 14:38:32 -040015{
Chris Masondee26a92007-03-26 16:00:06 -040016 int ret = 0;
17 struct btrfs_file_extent_item *item;
18 struct btrfs_key file_key;
Chris Mason5caf2a02007-04-02 11:20:42 -040019 struct btrfs_path *path;
Chris Masondee26a92007-03-26 16:00:06 -040020
Chris Mason5caf2a02007-04-02 11:20:42 -040021 path = btrfs_alloc_path();
22 BUG_ON(!path);
23 btrfs_init_path(path);
Chris Masondee26a92007-03-26 16:00:06 -040024 file_key.objectid = objectid;
Chris Masonb18c6682007-04-17 13:26:50 -040025 file_key.offset = pos;
Chris Masondee26a92007-03-26 16:00:06 -040026 file_key.flags = 0;
27 btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
28
Chris Mason5caf2a02007-04-02 11:20:42 -040029 ret = btrfs_insert_empty_item(trans, root, path, &file_key,
Chris Masondee26a92007-03-26 16:00:06 -040030 sizeof(*item));
Chris Mason1de037a2007-05-29 15:17:08 -040031 if (ret) {
32printk("failed to insert %Lu %Lu ret %d\n", objectid, pos, ret);
33btrfs_print_leaf(root, btrfs_buffer_leaf(path->nodes[0]));
34 }
Chris Mason9773a782007-03-27 11:26:26 -040035 BUG_ON(ret);
Chris Mason5caf2a02007-04-02 11:20:42 -040036 item = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
Chris Masondee26a92007-03-26 16:00:06 -040037 struct btrfs_file_extent_item);
Chris Masonb18c6682007-04-17 13:26:50 -040038 btrfs_set_file_extent_disk_blocknr(item, offset);
Chris Mason3a686372007-05-24 13:35:57 -040039 btrfs_set_file_extent_disk_num_blocks(item, disk_num_blocks);
Chris Masondee26a92007-03-26 16:00:06 -040040 btrfs_set_file_extent_offset(item, 0);
Chris Masonb18c6682007-04-17 13:26:50 -040041 btrfs_set_file_extent_num_blocks(item, num_blocks);
Chris Mason71951f32007-03-27 09:16:29 -040042 btrfs_set_file_extent_generation(item, trans->transid);
Chris Mason236454df2007-04-19 13:37:44 -040043 btrfs_set_file_extent_type(item, BTRFS_FILE_EXTENT_REG);
Chris Mason5caf2a02007-04-02 11:20:42 -040044 btrfs_mark_buffer_dirty(path->nodes[0]);
Chris Masona429e512007-04-18 16:15:28 -040045
Chris Mason5caf2a02007-04-02 11:20:42 -040046 btrfs_release_path(root, path);
47 btrfs_free_path(path);
Chris Mason9f5fae22007-03-20 14:38:32 -040048 return 0;
49}
Chris Masondee26a92007-03-26 16:00:06 -040050
Chris Masonb18c6682007-04-17 13:26:50 -040051struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans,
52 struct btrfs_root *root,
53 struct btrfs_path *path,
54 u64 objectid, u64 offset,
55 int cow)
Chris Mason6567e832007-04-16 09:22:45 -040056{
57 int ret;
58 struct btrfs_key file_key;
59 struct btrfs_key found_key;
60 struct btrfs_csum_item *item;
61 struct btrfs_leaf *leaf;
62 u64 csum_offset = 0;
Chris Masona429e512007-04-18 16:15:28 -040063 int csums_in_item;
Chris Mason6567e832007-04-16 09:22:45 -040064
65 file_key.objectid = objectid;
66 file_key.offset = offset;
67 file_key.flags = 0;
68 btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
Chris Masonb18c6682007-04-17 13:26:50 -040069 ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow);
Chris Mason6567e832007-04-16 09:22:45 -040070 if (ret < 0)
71 goto fail;
72 leaf = btrfs_buffer_leaf(path->nodes[0]);
73 if (ret > 0) {
74 ret = 1;
Chris Mason70b2bef2007-04-17 15:39:32 -040075 if (path->slots[0] == 0)
Chris Mason6567e832007-04-16 09:22:45 -040076 goto fail;
77 path->slots[0]--;
78 btrfs_disk_key_to_cpu(&found_key,
79 &leaf->items[path->slots[0]].key);
80 if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
81 found_key.objectid != objectid) {
82 goto fail;
83 }
84 csum_offset = (offset - found_key.offset) >>
85 root->fs_info->sb->s_blocksize_bits;
Chris Masona429e512007-04-18 16:15:28 -040086 csums_in_item = btrfs_item_size(leaf->items + path->slots[0]);
Chris Mason509659c2007-05-10 12:36:17 -040087 csums_in_item /= BTRFS_CRC32_SIZE;
Chris Masona429e512007-04-18 16:15:28 -040088
89 if (csum_offset >= csums_in_item) {
90 ret = -EFBIG;
Chris Mason6567e832007-04-16 09:22:45 -040091 goto fail;
92 }
93 }
94 item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
Chris Mason509659c2007-05-10 12:36:17 -040095 item = (struct btrfs_csum_item *)((unsigned char *)item +
96 csum_offset * BTRFS_CRC32_SIZE);
Chris Mason6567e832007-04-16 09:22:45 -040097 return item;
98fail:
99 if (ret > 0)
Chris Masonb18c6682007-04-17 13:26:50 -0400100 ret = -ENOENT;
Chris Mason6567e832007-04-16 09:22:45 -0400101 return ERR_PTR(ret);
102}
103
104
Chris Masondee26a92007-03-26 16:00:06 -0400105int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
106 struct btrfs_root *root,
107 struct btrfs_path *path, u64 objectid,
Chris Mason9773a782007-03-27 11:26:26 -0400108 u64 offset, int mod)
Chris Masondee26a92007-03-26 16:00:06 -0400109{
110 int ret;
111 struct btrfs_key file_key;
112 int ins_len = mod < 0 ? -1 : 0;
113 int cow = mod != 0;
114
115 file_key.objectid = objectid;
Chris Mason70b2bef2007-04-17 15:39:32 -0400116 file_key.offset = offset;
Chris Masondee26a92007-03-26 16:00:06 -0400117 file_key.flags = 0;
118 btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
119 ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow);
120 return ret;
121}
Chris Masonf254e522007-03-29 15:15:27 -0400122
123int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
124 struct btrfs_root *root,
125 u64 objectid, u64 offset,
126 char *data, size_t len)
127{
128 int ret;
129 struct btrfs_key file_key;
Chris Mason6567e832007-04-16 09:22:45 -0400130 struct btrfs_key found_key;
Chris Mason5caf2a02007-04-02 11:20:42 -0400131 struct btrfs_path *path;
Chris Masonf254e522007-03-29 15:15:27 -0400132 struct btrfs_csum_item *item;
Chris Mason6567e832007-04-16 09:22:45 -0400133 struct btrfs_leaf *leaf;
134 u64 csum_offset;
Chris Masonf254e522007-03-29 15:15:27 -0400135
Chris Mason5caf2a02007-04-02 11:20:42 -0400136 path = btrfs_alloc_path();
137 BUG_ON(!path);
Chris Masonb18c6682007-04-17 13:26:50 -0400138
Chris Masonf254e522007-03-29 15:15:27 -0400139 file_key.objectid = objectid;
140 file_key.offset = offset;
141 file_key.flags = 0;
142 btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
Chris Masona429e512007-04-18 16:15:28 -0400143
144 item = btrfs_lookup_csum(trans, root, path, objectid, offset, 1);
145 if (!IS_ERR(item))
146 goto found;
147 ret = PTR_ERR(item);
148 if (ret == -EFBIG) {
149 u32 item_size;
150 /* we found one, but it isn't big enough yet */
151 leaf = btrfs_buffer_leaf(path->nodes[0]);
152 item_size = btrfs_item_size(leaf->items + path->slots[0]);
Chris Mason509659c2007-05-10 12:36:17 -0400153 if ((item_size / BTRFS_CRC32_SIZE) >= MAX_CSUM_ITEMS(root)) {
Chris Masona429e512007-04-18 16:15:28 -0400154 /* already at max size, make a new one */
155 goto insert;
156 }
157 } else {
158 /* we didn't find a csum item, insert one */
159 goto insert;
160 }
161
162 /*
163 * at this point, we know the tree has an item, but it isn't big
164 * enough yet to put our csum in. Grow it
165 */
166 btrfs_release_path(root, path);
Chris Mason6567e832007-04-16 09:22:45 -0400167 ret = btrfs_search_slot(trans, root, &file_key, path,
Chris Mason509659c2007-05-10 12:36:17 -0400168 BTRFS_CRC32_SIZE, 1);
Chris Mason6567e832007-04-16 09:22:45 -0400169 if (ret < 0)
170 goto fail;
171 if (ret == 0) {
Chris Masonb18c6682007-04-17 13:26:50 -0400172 BUG();
Chris Mason6567e832007-04-16 09:22:45 -0400173 }
174 if (path->slots[0] == 0) {
Chris Mason6567e832007-04-16 09:22:45 -0400175 goto insert;
176 }
177 path->slots[0]--;
178 leaf = btrfs_buffer_leaf(path->nodes[0]);
179 btrfs_disk_key_to_cpu(&found_key, &leaf->items[path->slots[0]].key);
180 csum_offset = (offset - found_key.offset) >>
181 root->fs_info->sb->s_blocksize_bits;
182 if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
183 found_key.objectid != objectid ||
184 csum_offset >= MAX_CSUM_ITEMS(root)) {
Chris Mason6567e832007-04-16 09:22:45 -0400185 goto insert;
186 }
187 if (csum_offset >= btrfs_item_size(leaf->items + path->slots[0]) /
Chris Mason509659c2007-05-10 12:36:17 -0400188 BTRFS_CRC32_SIZE) {
189 u32 diff = (csum_offset + 1) * BTRFS_CRC32_SIZE;
Chris Masona429e512007-04-18 16:15:28 -0400190 diff = diff - btrfs_item_size(leaf->items + path->slots[0]);
Chris Mason3a686372007-05-24 13:35:57 -0400191 if (diff != BTRFS_CRC32_SIZE)
192 goto insert;
Chris Masona429e512007-04-18 16:15:28 -0400193 ret = btrfs_extend_item(trans, root, path, diff);
Chris Mason6567e832007-04-16 09:22:45 -0400194 BUG_ON(ret);
195 goto csum;
196 }
197
198insert:
Chris Masona429e512007-04-18 16:15:28 -0400199 btrfs_release_path(root, path);
Chris Mason6567e832007-04-16 09:22:45 -0400200 csum_offset = 0;
Chris Mason5caf2a02007-04-02 11:20:42 -0400201 ret = btrfs_insert_empty_item(trans, root, path, &file_key,
Chris Mason509659c2007-05-10 12:36:17 -0400202 BTRFS_CRC32_SIZE);
Chris Masona429e512007-04-18 16:15:28 -0400203 if (ret != 0) {
204 printk("at insert for %Lu %u %Lu ret is %d\n", file_key.objectid, file_key.flags, file_key.offset, ret);
205 WARN_ON(1);
Chris Masonf254e522007-03-29 15:15:27 -0400206 goto fail;
Chris Masona429e512007-04-18 16:15:28 -0400207 }
Chris Mason6567e832007-04-16 09:22:45 -0400208csum:
Chris Mason5caf2a02007-04-02 11:20:42 -0400209 item = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
Chris Masonf254e522007-03-29 15:15:27 -0400210 struct btrfs_csum_item);
211 ret = 0;
Chris Mason509659c2007-05-10 12:36:17 -0400212 item = (struct btrfs_csum_item *)((unsigned char *)item +
213 csum_offset * BTRFS_CRC32_SIZE);
Chris Masonb18c6682007-04-17 13:26:50 -0400214found:
Chris Mason509659c2007-05-10 12:36:17 -0400215 btrfs_check_bounds(&item->csum, BTRFS_CRC32_SIZE,
216 path->nodes[0]->b_data,
217 root->fs_info->sb->s_blocksize);
218 ret = btrfs_csum_data(root, data, len, &item->csum);
Chris Mason5caf2a02007-04-02 11:20:42 -0400219 btrfs_mark_buffer_dirty(path->nodes[0]);
Chris Masonf254e522007-03-29 15:15:27 -0400220fail:
Chris Mason5caf2a02007-04-02 11:20:42 -0400221 btrfs_release_path(root, path);
222 btrfs_free_path(path);
Chris Masonf254e522007-03-29 15:15:27 -0400223 return ret;
224}
225
Chris Mason1de037a2007-05-29 15:17:08 -0400226int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
227 struct btrfs_root *root, struct btrfs_path *path,
228 u64 isize)
229{
230 struct btrfs_key key;
231 struct btrfs_leaf *leaf = btrfs_buffer_leaf(path->nodes[0]);
232 int slot = path->slots[0];
233 int ret;
234 u32 new_item_size;
235 u64 new_item_span;
236 u64 blocks;
237
238 btrfs_disk_key_to_cpu(&key, &leaf->items[slot].key);
239 if (isize <= key.offset)
240 return 0;
241 new_item_span = isize - key.offset;
242 blocks = (new_item_span + root->blocksize - 1) / root->blocksize;
243 new_item_size = blocks * BTRFS_CRC32_SIZE;
244 if (new_item_size >= btrfs_item_size(leaf->items + slot))
245 return 0;
246 ret = btrfs_truncate_item(trans, root, path, new_item_size);
247 BUG_ON(ret);
248 return ret;
249}
250
Chris Masonf254e522007-03-29 15:15:27 -0400251int btrfs_csum_verify_file_block(struct btrfs_root *root,
252 u64 objectid, u64 offset,
253 char *data, size_t len)
254{
255 int ret;
256 struct btrfs_key file_key;
Chris Mason5caf2a02007-04-02 11:20:42 -0400257 struct btrfs_path *path;
Chris Masonf254e522007-03-29 15:15:27 -0400258 struct btrfs_csum_item *item;
Chris Mason509659c2007-05-10 12:36:17 -0400259 char result[BTRFS_CRC32_SIZE];
Chris Masonf254e522007-03-29 15:15:27 -0400260
Chris Mason5caf2a02007-04-02 11:20:42 -0400261 path = btrfs_alloc_path();
262 BUG_ON(!path);
263 btrfs_init_path(path);
Chris Masonf254e522007-03-29 15:15:27 -0400264 file_key.objectid = objectid;
265 file_key.offset = offset;
266 file_key.flags = 0;
267 btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
Chris Mason2da566e2007-04-02 15:43:21 -0400268 mutex_lock(&root->fs_info->fs_mutex);
Chris Mason6567e832007-04-16 09:22:45 -0400269
Chris Masonb18c6682007-04-17 13:26:50 -0400270 item = btrfs_lookup_csum(NULL, root, path, objectid, offset, 0);
Chris Mason6567e832007-04-16 09:22:45 -0400271 if (IS_ERR(item)) {
272 ret = PTR_ERR(item);
Chris Masona429e512007-04-18 16:15:28 -0400273 /* a csum that isn't present is a preallocated region. */
274 if (ret == -ENOENT || ret == -EFBIG)
Chris Mason3a686372007-05-24 13:35:57 -0400275 ret = -ENOENT;
Chris Masonf254e522007-03-29 15:15:27 -0400276 goto fail;
Chris Mason6567e832007-04-16 09:22:45 -0400277 }
278
Chris Masonf254e522007-03-29 15:15:27 -0400279 ret = btrfs_csum_data(root, data, len, result);
280 WARN_ON(ret);
Chris Mason509659c2007-05-10 12:36:17 -0400281 if (memcmp(result, &item->csum, BTRFS_CRC32_SIZE))
Chris Masonf254e522007-03-29 15:15:27 -0400282 ret = 1;
283fail:
Chris Mason5caf2a02007-04-02 11:20:42 -0400284 btrfs_release_path(root, path);
285 btrfs_free_path(path);
Chris Mason2da566e2007-04-02 15:43:21 -0400286 mutex_unlock(&root->fs_info->fs_mutex);
Chris Masonf254e522007-03-29 15:15:27 -0400287 return ret;
288}
289