Update btrfs-progs to better match the kernel
[platform/upstream/btrfs-progs.git] / file-item.c
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 <stdio.h>
20 #include <stdlib.h>
21 #include "kerncompat.h"
22 #include "radix-tree.h"
23 #include "ctree.h"
24 #include "disk-io.h"
25 #include "transaction.h"
26 #include "crc32c.h"
27
28 #define MAX_CSUM_ITEMS(r) ((((BTRFS_LEAF_DATA_SIZE(r) - \
29                                sizeof(struct btrfs_item) * 2) / \
30                                BTRFS_CRC32_SIZE) - 1))
31 int btrfs_create_file(struct btrfs_trans_handle *trans,
32                       struct btrfs_root *root, u64 dirid, u64 *objectid)
33 {
34         return 0;
35 }
36
37 int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
38                                struct btrfs_root *root,
39                                u64 objectid, u64 pos,
40                                u64 offset, u64 disk_num_bytes,
41                                u64 num_bytes)
42 {
43         int ret = 0;
44         struct btrfs_file_extent_item *item;
45         struct btrfs_key file_key;
46         struct btrfs_path path;
47         struct btrfs_leaf *leaf;
48
49
50         btrfs_init_path(&path);
51         file_key.objectid = objectid;
52         file_key.offset = pos;
53         btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
54
55         ret = btrfs_insert_empty_item(trans, root, &path, &file_key,
56                                       sizeof(*item));
57         if (ret < 0)
58                 goto out;
59         BUG_ON(ret);
60         leaf = &path.nodes[0]->leaf;
61         item = btrfs_item_ptr(leaf, path.slots[0],
62                               struct btrfs_file_extent_item);
63         btrfs_set_file_extent_disk_bytenr(item, offset);
64         btrfs_set_file_extent_disk_num_bytes(item, disk_num_bytes);
65         btrfs_set_file_extent_offset(item, 0);
66         btrfs_set_file_extent_num_bytes(item, num_bytes);
67         btrfs_set_file_extent_generation(item, trans->transid);
68         btrfs_set_file_extent_type(item, BTRFS_FILE_EXTENT_REG);
69 out:
70         btrfs_release_path(root, &path);
71         return ret;
72 }
73
74 int btrfs_insert_inline_file_extent(struct btrfs_trans_handle *trans,
75                                     struct btrfs_root *root, u64 objectid,
76                                     u64 offset, char *buffer, size_t size)
77 {
78         int ret;
79         char *ptr;
80         u32 datasize;
81         struct btrfs_key key;
82         struct btrfs_path path;
83         struct btrfs_leaf *leaf;
84         struct btrfs_file_extent_item *ei;
85
86         btrfs_init_path(&path);
87         key.objectid = objectid;
88         key.offset = offset;
89         btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
90
91         datasize = btrfs_file_extent_calc_inline_size(size);
92         ret = btrfs_insert_empty_item(trans, root, &path, &key,
93                                       datasize);
94         BUG_ON(ret);
95         leaf = &path.nodes[0]->leaf;
96         ei = btrfs_item_ptr(leaf, path.slots[0],
97                             struct btrfs_file_extent_item);
98         btrfs_set_file_extent_generation(ei, trans->transid);
99         btrfs_set_file_extent_type(ei, BTRFS_FILE_EXTENT_INLINE);
100         ptr = btrfs_file_extent_inline_start(ei);
101         memcpy(ptr, buffer, size);
102         btrfs_release_path(root, &path);
103         return 0;
104 }
105
106 int btrfs_lookup_csum(struct btrfs_trans_handle *trans,
107                       struct btrfs_root *root,
108                       struct btrfs_path *path,
109                       u64 objectid, u64 offset, int cow,
110                       struct btrfs_csum_item **item_ret)
111 {
112         int ret;
113         int slot;
114         struct btrfs_key file_key;
115         struct btrfs_key found_key;
116         struct btrfs_csum_item *item;
117         struct btrfs_leaf *leaf;
118         u64 csum_offset = 0;
119         int csums_in_item;
120
121         file_key.objectid = objectid;
122         file_key.offset = offset;
123         btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
124         ret = btrfs_search_slot(trans, root, &file_key, path, 0, cow);
125         if (ret < 0)
126                 goto fail;
127         leaf = &path->nodes[0]->leaf;
128         if (ret > 0) {
129                 if (path->slots[0] == 0)
130                         goto fail;
131                 path->slots[0]--;
132
133                 slot = path->slots[0];
134                 btrfs_disk_key_to_cpu(&found_key, &leaf->items[slot].key);
135                 if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
136                     found_key.objectid != objectid) {
137                         goto fail;
138                 }
139                 csum_offset = (offset - found_key.offset) / root->sectorsize;
140                 csums_in_item = btrfs_item_size(&leaf->items[slot]);
141                 csums_in_item /= BTRFS_CRC32_SIZE;
142
143                 if (csum_offset >= csums_in_item) {
144                         ret = -EFBIG;
145                         goto fail;
146                 }
147                 ret = 0;
148         }
149         item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item);
150         item = (struct btrfs_csum_item *)((unsigned char *)item +
151                                           csum_offset * BTRFS_CRC32_SIZE);
152         *item_ret = item;
153 fail:
154         if (ret > 0)
155                 ret = -ENOENT;
156         return ret;
157 }
158
159 int btrfs_csum_file_block(struct btrfs_trans_handle *trans,
160                           struct btrfs_root *root,
161                           struct btrfs_inode_item *inode,
162                           u64 objectid, u64 offset,
163                           char *data, size_t len)
164 {
165         int ret;
166         int slot;
167         struct btrfs_key file_key;
168         struct btrfs_key found_key;
169         u64 next_offset = (u64)-1;
170         int found_next = 0;
171         struct btrfs_path path;
172         struct btrfs_csum_item *item;
173         struct btrfs_leaf *leaf = NULL;
174         u64 csum_offset;
175         u32 csum_result = ~(u32)0;
176         u32 nritems;
177         u32 ins_size;
178
179         btrfs_init_path(&path);
180
181         file_key.objectid = objectid;
182         file_key.offset = offset;
183         btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
184
185         ret = btrfs_lookup_csum(trans, root, &path, objectid,
186                                 offset, 1, &item);
187         if (!ret) {
188                 leaf = &path.nodes[0]->leaf;
189                 goto found;
190         }
191         if (ret != -EFBIG && ret != -ENOENT)
192                 goto fail;
193         leaf = &path.nodes[0]->leaf;
194         if (ret == -EFBIG) {
195                 u32 item_size;
196                 slot = path.slots[0];
197                 /* we found one, but it isn't big enough yet */
198                 item_size = btrfs_item_size(&leaf->items[slot]);
199                 if ((item_size / BTRFS_CRC32_SIZE) >= MAX_CSUM_ITEMS(root)) {
200                         /* already at max size, make a new one */
201                         goto insert;
202                 }
203         } else {
204                 slot = path.slots[0] + 1;
205                 /* we didn't find a csum item, insert one */
206                 nritems = btrfs_header_nritems(&leaf->header);
207                 if (path.slots[0] >= nritems - 1) {
208                         ret = btrfs_next_leaf(root, &path);
209                         if (ret == 1)
210                                 found_next = 1;
211                         if (ret != 0)
212                                 goto insert;
213                         slot = 0;
214                 }
215                 btrfs_disk_key_to_cpu(&found_key, &leaf->items[slot].key);
216                 if (found_key.objectid != objectid ||
217                     found_key.type != BTRFS_CSUM_ITEM_KEY) {
218                         found_next = 1;
219                         goto insert;
220                 }
221                 next_offset = found_key.offset;
222                 found_next = 1;
223                 goto insert;
224         }
225
226         /*
227          * at this point, we know the tree has an item, but it isn't big
228          * enough yet to put our csum in.  Grow it
229          */
230         btrfs_release_path(root, &path);
231         ret = btrfs_search_slot(trans, root, &file_key, &path,
232                                 BTRFS_CRC32_SIZE, 1);
233         if (ret < 0)
234                 goto fail;
235         BUG_ON(ret == 0);
236         if (path.slots[0] == 0) {
237                 goto insert;
238         }
239         path.slots[0]--;
240         slot = path.slots[0];
241         leaf = &path.nodes[0]->leaf;
242         btrfs_disk_key_to_cpu(&found_key, &leaf->items[slot].key);
243         csum_offset = (offset - found_key.offset) / root->sectorsize;
244         if (btrfs_key_type(&found_key) != BTRFS_CSUM_ITEM_KEY ||
245             found_key.objectid != objectid ||
246             csum_offset >= MAX_CSUM_ITEMS(root)) {
247                 goto insert;
248         }
249         if (csum_offset >= btrfs_item_size(&leaf->items[slot]) /
250             BTRFS_CRC32_SIZE) {
251                 u32 diff = (csum_offset + 1) * BTRFS_CRC32_SIZE;
252                 diff = diff - btrfs_item_size(&leaf->items[slot]);
253                 if (diff != BTRFS_CRC32_SIZE)
254                         goto insert;
255                 ret = btrfs_extend_item(trans, root, &path, diff);
256                 BUG_ON(ret);
257                 goto csum;
258         }
259
260 insert:
261         btrfs_release_path(root, &path);
262         csum_offset = 0;
263         if (found_next) {
264                 u64 tmp;
265                 if (next_offset > btrfs_inode_size(inode))
266                         next_offset = btrfs_inode_size(inode);
267                 tmp = next_offset - offset + root->sectorsize - 1;
268                 tmp /= root->sectorsize;
269                 if (tmp > MAX_CSUM_ITEMS(root))
270                         tmp =  MAX_CSUM_ITEMS(root);
271                 ins_size = BTRFS_CRC32_SIZE * tmp;
272         } else {
273                 ins_size = BTRFS_CRC32_SIZE;
274         }
275         ret = btrfs_insert_empty_item(trans, root, &path, &file_key,
276                                       ins_size);
277         if (ret < 0)
278                 goto fail;
279         BUG_ON(ret != 0);
280 csum:
281         slot = path.slots[0];
282         leaf = &path.nodes[0]->leaf;
283         item = btrfs_item_ptr(leaf, slot, struct btrfs_csum_item);
284         item = (struct btrfs_csum_item *)((unsigned char *)item +
285                                           csum_offset * BTRFS_CRC32_SIZE);
286 found:
287         csum_result = crc32c(csum_result, data, len);
288         csum_result = ~cpu_to_le32(csum_result);
289         memcpy(item, &csum_result, BTRFS_CRC32_SIZE);
290         ret = 0;
291 fail:
292         btrfs_release_path(root, &path);
293         return ret;
294 }