Btrfs: rework csums and extent item ordering
[linux-2.6-block.git] / fs / btrfs / file-item.c
1 #include <linux/module.h>
2 #include "ctree.h"
3 #include "disk-io.h"
4 #include "transaction.h"
5
6 #define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \
7                                  sizeof(struct btrfs_item)) / \
8                                 sizeof(struct btrfs_csum_item)) - 1))
9 int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
10                                struct btrfs_root *root,
11                                u64 objectid, u64 pos,
12                                u64 offset, u64 num_blocks)
13 {
14         int ret = 0;
15         struct btrfs_file_extent_item *item;
16         struct btrfs_key file_key;
17         struct btrfs_path *path;
18
19         path = btrfs_alloc_path();
20         BUG_ON(!path);
21         btrfs_init_path(path);
22         /*
23         ret = btrfs_alloc_extent(trans, root, num_blocks, hint_block,
24                                  (u64)-1, &ins);
25                                  */
26         BUG_ON(ret);
27         file_key.objectid = objectid;
28         file_key.offset = pos;
29         file_key.flags = 0;
30         btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
31
32         ret = btrfs_insert_empty_item(trans, root, path, &file_key,
33                                       sizeof(*item));
34         BUG_ON(ret);
35         item = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
36                               struct btrfs_file_extent_item);
37         btrfs_set_file_extent_disk_blocknr(item, offset);
38         btrfs_set_file_extent_disk_num_blocks(item, num_blocks);
39         btrfs_set_file_extent_offset(item, 0);
40         btrfs_set_file_extent_num_blocks(item, num_blocks);
41         btrfs_set_file_extent_generation(item, trans->transid);
42         btrfs_mark_buffer_dirty(path->nodes[0]);
43         btrfs_release_path(root, path);
44         btrfs_free_path(path);
45         return 0;
46 }
47
48 struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans,
49                                           struct btrfs_root *root,
50                                           struct btrfs_path *path,
51                                           u64 objectid, u64 offset,
52                                           int cow)
53 {
54         int ret;
55         struct btrfs_key file_key;
56         struct btrfs_key found_key;
57         struct btrfs_csum_item *item;
58         struct btrfs_leaf *leaf;
59         u64 csum_offset = 0;
60
61         file_key.objectid = objectid;
62         file_key.offset = offset;
63         file_key.flags = 0;
64         btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
65         ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow);
66         if (ret < 0)
67                 goto fail;
68         leaf = btrfs_buffer_leaf(path->nodes[0]);
69         if (ret > 0) {
70                 ret = 1;
71                 if (path->slots[0] == 0)
72                         goto fail;
73                 path->slots[0]--;
74                 btrfs_disk_key_to_cpu(&found_key,
75                                       &leaf->items[path->slots[0]].key);
76                 if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
77                     found_key.objectid != objectid) {
78                         goto fail;
79                 }
80                 csum_offset = (offset - found_key.offset) >>
81                                 root->fs_info->sb->s_blocksize_bits;
82                 if (csum_offset >=
83                     btrfs_item_size(leaf->items + path->slots[0]) /
84                     sizeof(struct btrfs_csum_item)) {
85                         goto fail;
86                 }
87         }
88         item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
89         item += csum_offset;
90         return item;
91 fail:
92         if (ret > 0)
93                 ret = -ENOENT;
94         return ERR_PTR(ret);
95 }
96
97
98 int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
99                              struct btrfs_root *root,
100                              struct btrfs_path *path, u64 objectid,
101                              u64 offset, int mod)
102 {
103         int ret;
104         struct btrfs_key file_key;
105         int ins_len = mod < 0 ? -1 : 0;
106         int cow = mod != 0;
107
108         file_key.objectid = objectid;
109         file_key.offset = offset;
110         file_key.flags = 0;
111         btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
112         ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow);
113         return ret;
114 }
115
116 int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
117                           struct btrfs_root *root,
118                           u64 objectid, u64 offset,
119                           char *data, size_t len)
120 {
121         int ret;
122         struct btrfs_key file_key;
123         struct btrfs_key found_key;
124         struct btrfs_path *path;
125         struct btrfs_csum_item *item;
126         struct btrfs_leaf *leaf;
127         u64 csum_offset;
128
129         path = btrfs_alloc_path();
130         BUG_ON(!path);
131         btrfs_init_path(path);
132
133         item = btrfs_lookup_csum(trans, root, path, objectid, offset, 0);
134         if (!IS_ERR(item))
135                 goto found;
136         btrfs_release_path(root, path);
137         file_key.objectid = objectid;
138         file_key.offset = offset;
139         file_key.flags = 0;
140         btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
141         ret = btrfs_search_slot(trans, root, &file_key, path,
142                                 sizeof(struct btrfs_csum_item), 1);
143         if (ret < 0)
144                 goto fail;
145         if (ret == 0) {
146                 BUG();
147         }
148         if (path->slots[0] == 0) {
149                 btrfs_release_path(root, path);
150                 goto insert;
151         }
152         path->slots[0]--;
153         leaf = btrfs_buffer_leaf(path->nodes[0]);
154         btrfs_disk_key_to_cpu(&found_key, &leaf->items[path->slots[0]].key);
155         csum_offset = (offset - found_key.offset) >>
156                         root->fs_info->sb->s_blocksize_bits;
157         if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
158             found_key.objectid != objectid ||
159             csum_offset >= MAX_CSUM_ITEMS(root)) {
160                 btrfs_release_path(root, path);
161                 goto insert;
162         }
163         if (csum_offset >= btrfs_item_size(leaf->items + path->slots[0]) /
164             sizeof(struct btrfs_csum_item)) {
165                 ret = btrfs_extend_item(trans, root, path,
166                                         sizeof(struct btrfs_csum_item));
167                 BUG_ON(ret);
168                 goto csum;
169         }
170
171 insert:
172         csum_offset = 0;
173         ret = btrfs_insert_empty_item(trans, root, path, &file_key,
174                                       sizeof(struct btrfs_csum_item));
175         if (ret != 0 && ret != -EEXIST)
176                 goto fail;
177 csum:
178         item = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
179                               struct btrfs_csum_item);
180         ret = 0;
181         item += csum_offset;
182 found:
183         ret = btrfs_csum_data(root, data, len, item->csum);
184         btrfs_mark_buffer_dirty(path->nodes[0]);
185 fail:
186         btrfs_release_path(root, path);
187         btrfs_free_path(path);
188         return ret;
189 }
190
191 int btrfs_csum_verify_file_block(struct btrfs_root *root,
192                                  u64 objectid, u64 offset,
193                                  char *data, size_t len)
194 {
195         int ret;
196         struct btrfs_key file_key;
197         struct btrfs_path *path;
198         struct btrfs_csum_item *item;
199         char result[BTRFS_CSUM_SIZE];
200
201         path = btrfs_alloc_path();
202         BUG_ON(!path);
203         btrfs_init_path(path);
204         file_key.objectid = objectid;
205         file_key.offset = offset;
206         file_key.flags = 0;
207         btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
208         mutex_lock(&root->fs_info->fs_mutex);
209
210         item = btrfs_lookup_csum(NULL, root, path, objectid, offset, 0);
211         if (IS_ERR(item)) {
212                 ret = PTR_ERR(item);
213                 goto fail;
214         }
215
216         ret = btrfs_csum_data(root, data, len, result);
217         WARN_ON(ret);
218         if (memcmp(result, item->csum, BTRFS_CSUM_SIZE))
219                 ret = 1;
220 fail:
221         btrfs_release_path(root, path);
222         btrfs_free_path(path);
223         mutex_unlock(&root->fs_info->fs_mutex);
224         return ret;
225 }
226