Btrfs: working file_write, reorganized key flags
[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) * 2) / \
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         file_key.objectid = objectid;
23         file_key.offset = pos;
24         file_key.flags = 0;
25         btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
26
27         ret = btrfs_insert_empty_item(trans, root, path, &file_key,
28                                       sizeof(*item));
29         BUG_ON(ret);
30         item = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
31                               struct btrfs_file_extent_item);
32         btrfs_set_file_extent_disk_blocknr(item, offset);
33         btrfs_set_file_extent_disk_num_blocks(item, num_blocks);
34         btrfs_set_file_extent_offset(item, 0);
35         btrfs_set_file_extent_num_blocks(item, num_blocks);
36         btrfs_set_file_extent_generation(item, trans->transid);
37         btrfs_mark_buffer_dirty(path->nodes[0]);
38
39         btrfs_release_path(root, path);
40         btrfs_free_path(path);
41         return 0;
42 }
43
44 struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans,
45                                           struct btrfs_root *root,
46                                           struct btrfs_path *path,
47                                           u64 objectid, u64 offset,
48                                           int cow)
49 {
50         int ret;
51         struct btrfs_key file_key;
52         struct btrfs_key found_key;
53         struct btrfs_csum_item *item;
54         struct btrfs_leaf *leaf;
55         u64 csum_offset = 0;
56         int csums_in_item;
57
58         file_key.objectid = objectid;
59         file_key.offset = offset;
60         file_key.flags = 0;
61         btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
62         ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow);
63         if (ret < 0)
64                 goto fail;
65         leaf = btrfs_buffer_leaf(path->nodes[0]);
66         if (ret > 0) {
67                 ret = 1;
68                 if (path->slots[0] == 0)
69                         goto fail;
70                 path->slots[0]--;
71                 btrfs_disk_key_to_cpu(&found_key,
72                                       &leaf->items[path->slots[0]].key);
73                 if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
74                     found_key.objectid != objectid) {
75                         goto fail;
76                 }
77                 csum_offset = (offset - found_key.offset) >>
78                                 root->fs_info->sb->s_blocksize_bits;
79                 csums_in_item = btrfs_item_size(leaf->items + path->slots[0]);
80                 csums_in_item /= sizeof(struct btrfs_csum_item);
81
82                 if (csum_offset >= csums_in_item) {
83                         ret = -EFBIG;
84                         goto fail;
85                 }
86         }
87         item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
88         item += csum_offset;
89         return item;
90 fail:
91         if (ret > 0)
92                 ret = -ENOENT;
93         return ERR_PTR(ret);
94 }
95
96
97 int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
98                              struct btrfs_root *root,
99                              struct btrfs_path *path, u64 objectid,
100                              u64 offset, int mod)
101 {
102         int ret;
103         struct btrfs_key file_key;
104         int ins_len = mod < 0 ? -1 : 0;
105         int cow = mod != 0;
106
107         file_key.objectid = objectid;
108         file_key.offset = offset;
109         file_key.flags = 0;
110         btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
111         ret = btrfs_search_slot(trans, root, &file_key, path, ins_len, cow);
112         return ret;
113 }
114
115 int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
116                           struct btrfs_root *root,
117                           u64 objectid, u64 offset,
118                           char *data, size_t len)
119 {
120         int ret;
121         struct btrfs_key file_key;
122         struct btrfs_key found_key;
123         struct btrfs_path *path;
124         struct btrfs_csum_item *item;
125         struct btrfs_leaf *leaf;
126         u64 csum_offset;
127
128         path = btrfs_alloc_path();
129         BUG_ON(!path);
130
131         file_key.objectid = objectid;
132         file_key.offset = offset;
133         file_key.flags = 0;
134         btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
135
136         item = btrfs_lookup_csum(trans, root, path, objectid, offset, 1);
137         if (!IS_ERR(item))
138                 goto found;
139         ret = PTR_ERR(item);
140         if (ret == -EFBIG) {
141                 u32 item_size;
142                 /* we found one, but it isn't big enough yet */
143                 leaf = btrfs_buffer_leaf(path->nodes[0]);
144                 item_size = btrfs_item_size(leaf->items + path->slots[0]);
145                 if ((item_size / sizeof(struct btrfs_csum_item)) >=
146                     MAX_CSUM_ITEMS(root)) {
147                         /* already at max size, make a new one */
148                         goto insert;
149                 }
150         } else {
151                 /* we didn't find a csum item, insert one */
152                 goto insert;
153         }
154
155         /*
156          * at this point, we know the tree has an item, but it isn't big
157          * enough yet to put our csum in.  Grow it
158          */
159         btrfs_release_path(root, path);
160         ret = btrfs_search_slot(trans, root, &file_key, path,
161                                 sizeof(struct btrfs_csum_item), 1);
162         if (ret < 0)
163                 goto fail;
164         if (ret == 0) {
165                 BUG();
166         }
167         if (path->slots[0] == 0) {
168                 goto insert;
169         }
170         path->slots[0]--;
171         leaf = btrfs_buffer_leaf(path->nodes[0]);
172         btrfs_disk_key_to_cpu(&found_key, &leaf->items[path->slots[0]].key);
173         csum_offset = (offset - found_key.offset) >>
174                         root->fs_info->sb->s_blocksize_bits;
175         if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
176             found_key.objectid != objectid ||
177             csum_offset >= MAX_CSUM_ITEMS(root)) {
178                 WARN_ON(1);
179                 goto insert;
180         }
181         if (csum_offset >= btrfs_item_size(leaf->items + path->slots[0]) /
182             sizeof(struct btrfs_csum_item)) {
183                 u32 diff = (csum_offset + 1) * sizeof(struct btrfs_csum_item);
184                 diff = diff - btrfs_item_size(leaf->items + path->slots[0]);
185                 WARN_ON(diff != sizeof(struct btrfs_csum_item));
186                 ret = btrfs_extend_item(trans, root, path, diff);
187                 BUG_ON(ret);
188                 goto csum;
189         }
190
191 insert:
192         btrfs_release_path(root, path);
193         csum_offset = 0;
194         ret = btrfs_insert_empty_item(trans, root, path, &file_key,
195                                       sizeof(struct btrfs_csum_item));
196         if (ret != 0) {
197                 printk("at insert for %Lu %u %Lu ret is %d\n", file_key.objectid, file_key.flags, file_key.offset, ret);
198                 WARN_ON(1);
199                 goto fail;
200         }
201 csum:
202         item = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), path->slots[0],
203                               struct btrfs_csum_item);
204         ret = 0;
205         item += csum_offset;
206 found:
207         btrfs_check_bounds(item->csum, BTRFS_CSUM_SIZE, path->nodes[0]->b_data, root->fs_info->sb->s_blocksize);
208         ret = btrfs_csum_data(root, data, len, item->csum);
209         btrfs_mark_buffer_dirty(path->nodes[0]);
210 fail:
211         btrfs_release_path(root, path);
212         btrfs_free_path(path);
213         return ret;
214 }
215
216 int btrfs_csum_verify_file_block(struct btrfs_root *root,
217                                  u64 objectid, u64 offset,
218                                  char *data, size_t len)
219 {
220         int ret;
221         struct btrfs_key file_key;
222         struct btrfs_path *path;
223         struct btrfs_csum_item *item;
224         char result[BTRFS_CSUM_SIZE];
225
226         path = btrfs_alloc_path();
227         BUG_ON(!path);
228         btrfs_init_path(path);
229         file_key.objectid = objectid;
230         file_key.offset = offset;
231         file_key.flags = 0;
232         btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
233         mutex_lock(&root->fs_info->fs_mutex);
234
235         item = btrfs_lookup_csum(NULL, root, path, objectid, offset, 0);
236         if (IS_ERR(item)) {
237                 ret = PTR_ERR(item);
238                 /* a csum that isn't present is a preallocated region. */
239                 if (ret == -ENOENT || ret == -EFBIG)
240                         ret = 1;
241                 goto fail;
242         }
243
244         ret = btrfs_csum_data(root, data, len, result);
245         WARN_ON(ret);
246         if (memcmp(result, item->csum, BTRFS_CSUM_SIZE))
247                 ret = 1;
248 fail:
249         btrfs_release_path(root, path);
250         btrfs_free_path(path);
251         mutex_unlock(&root->fs_info->fs_mutex);
252         return ret;
253 }
254