btrfs-progs: tests: Use '-t btrfs' mount option in tests
[platform/upstream/btrfs-progs.git] / inode-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 "ctree.h"
20 #include "disk-io.h"
21 #include "transaction.h"
22 #include "hash.h"
23
24 static int find_name_in_backref(struct btrfs_path *path, const char * name,
25                          int name_len, struct btrfs_inode_ref **ref_ret)
26 {
27         struct extent_buffer *leaf;
28         struct btrfs_inode_ref *ref;
29         unsigned long ptr;
30         unsigned long name_ptr;
31         u32 item_size;
32         u32 cur_offset = 0;
33         int len;
34
35         leaf = path->nodes[0];
36         item_size = btrfs_item_size_nr(leaf, path->slots[0]);
37         ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
38         while (cur_offset < item_size) {
39                 ref = (struct btrfs_inode_ref *)(ptr + cur_offset);
40                 len = btrfs_inode_ref_name_len(leaf, ref);
41                 name_ptr = (unsigned long)(ref + 1);
42                 cur_offset += len + sizeof(*ref);
43                 if (len != name_len)
44                         continue;
45                 if (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) {
46                         *ref_ret = ref;
47                         return 1;
48                 }
49         }
50         return 0;
51 }
52
53 int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
54                            struct btrfs_root *root,
55                            const char *name, int name_len,
56                            u64 inode_objectid, u64 ref_objectid, u64 index)
57 {
58         struct btrfs_path *path;
59         struct btrfs_key key;
60         struct btrfs_inode_ref *ref;
61         unsigned long ptr;
62         int ret;
63         int ins_len = name_len + sizeof(*ref);
64
65         key.objectid = inode_objectid;
66         key.offset = ref_objectid;
67         key.type = BTRFS_INODE_REF_KEY;
68
69         path = btrfs_alloc_path();
70         if (!path)
71                 return -ENOMEM;
72
73         ret = btrfs_insert_empty_item(trans, root, path, &key,
74                                       ins_len);
75         if (ret == -EEXIST) {
76                 u32 old_size;
77
78                 if (find_name_in_backref(path, name, name_len, &ref))
79                         goto out;
80
81                 old_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]);
82                 ret = btrfs_extend_item(root, path, ins_len);
83                 BUG_ON(ret);
84                 ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
85                                      struct btrfs_inode_ref);
86                 ref = (struct btrfs_inode_ref *)((unsigned long)ref + old_size);
87                 btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
88                 btrfs_set_inode_ref_index(path->nodes[0], ref, index);
89                 ptr = (unsigned long)(ref + 1);
90                 ret = 0;
91         } else if (ret < 0) {
92                 if (ret == -EOVERFLOW)
93                         ret = -EMLINK;
94                 goto out;
95         } else {
96                 ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
97                                      struct btrfs_inode_ref);
98                 btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
99                 btrfs_set_inode_ref_index(path->nodes[0], ref, index);
100                 ptr = (unsigned long)(ref + 1);
101         }
102         write_extent_buffer(path->nodes[0], name, ptr, name_len);
103         btrfs_mark_buffer_dirty(path->nodes[0]);
104
105 out:
106         btrfs_free_path(path);
107
108         if (ret == -EMLINK) {
109                 if (btrfs_fs_incompat(root->fs_info, EXTENDED_IREF))
110                         ret = btrfs_insert_inode_extref(trans, root, name,
111                                                         name_len,
112                                                         inode_objectid,
113                                                         ref_objectid, index);
114         }
115         return ret;
116 }
117
118 int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root
119                        *root, struct btrfs_path *path,
120                        struct btrfs_key *location, int mod)
121 {
122         int ins_len = mod < 0 ? -1 : 0;
123         int cow = mod != 0;
124         int ret;
125         int slot;
126         struct extent_buffer *leaf;
127         struct btrfs_key found_key;
128
129         ret = btrfs_search_slot(trans, root, location, path, ins_len, cow);
130         if (ret > 0 && location->type == BTRFS_ROOT_ITEM_KEY &&
131             location->offset == (u64)-1 && path->slots[0] != 0) {
132                 slot = path->slots[0] - 1;
133                 leaf = path->nodes[0];
134                 btrfs_item_key_to_cpu(leaf, &found_key, slot);
135                 if (found_key.objectid == location->objectid &&
136                     found_key.type == location->type) {
137                         path->slots[0]--;
138                         return 0;
139                 }
140         }
141         return ret;
142 }
143
144 int btrfs_insert_inode(struct btrfs_trans_handle *trans, struct btrfs_root
145                        *root, u64 objectid, struct btrfs_inode_item
146                        *inode_item)
147 {
148         int ret;
149         struct btrfs_key key;
150
151         key.objectid = objectid;
152         key.type = BTRFS_INODE_ITEM_KEY;
153         key.offset = 0;
154
155         ret = btrfs_insert_item(trans, root, &key, inode_item,
156                                 sizeof(*inode_item));
157         return ret;
158 }
159
160 struct btrfs_inode_ref *btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans,
161                 struct btrfs_root *root, struct btrfs_path *path,
162                 const char *name, int namelen, u64 ino, u64 parent_ino,
163                 int ins_len)
164 {
165         struct btrfs_key key;
166         struct btrfs_inode_ref *ret_inode_ref = NULL;
167         int ret = 0;
168
169         key.objectid = ino;
170         key.type = BTRFS_INODE_REF_KEY;
171         key.offset = parent_ino;
172
173         ret = btrfs_search_slot(trans, root, &key, path, ins_len,
174                                 ins_len ? 1 : 0);
175         if (ret)
176                 goto out;
177
178         find_name_in_backref(path, name, namelen, &ret_inode_ref);
179 out:
180         if (ret < 0)
181                 return ERR_PTR(ret);
182         else
183                 return ret_inode_ref;
184 }
185
186 static int btrfs_find_name_in_ext_backref(struct btrfs_path *path,
187                 u64 parent_ino, const char *name, int namelen,
188                 struct btrfs_inode_extref **extref_ret)
189 {
190         struct extent_buffer *node;
191         struct btrfs_inode_extref *extref;
192         unsigned long ptr;
193         unsigned long name_ptr;
194         u32 item_size;
195         u32 cur_offset = 0;
196         int ref_name_len;
197         int slot;
198
199         node = path->nodes[0];
200         slot = path->slots[0];
201         item_size = btrfs_item_size_nr(node, slot);
202         ptr = btrfs_item_ptr_offset(node, slot);
203
204         /*
205          * Search all extended backrefs in this item. We're only looking
206          * through any collisions so most of the time this is just going to
207          * compare against one buffer. If all is well, we'll return success and
208          * the inode ref object.
209          */
210         while (cur_offset < item_size) {
211                 extref = (struct btrfs_inode_extref *) (ptr + cur_offset);
212                 name_ptr = (unsigned long)(&extref->name);
213                 ref_name_len = btrfs_inode_extref_name_len(node, extref);
214
215                 if (ref_name_len == namelen &&
216                     btrfs_inode_extref_parent(node, extref) == parent_ino &&
217                     (memcmp_extent_buffer(node, name, name_ptr, namelen) == 0))
218                 {
219                         if (extref_ret)
220                                 *extref_ret = extref;
221                         return 1;
222                 }
223
224                 cur_offset += ref_name_len + sizeof(*extref);
225         }
226
227         return 0;
228 }
229
230 struct btrfs_inode_extref *btrfs_lookup_inode_extref(struct btrfs_trans_handle
231                 *trans, struct btrfs_path *path, struct btrfs_root *root,
232                 u64 ino, u64 parent_ino, u64 index, const char *name,
233                 int namelen, int ins_len)
234 {
235         struct btrfs_key key;
236         struct btrfs_inode_extref *extref;
237         int ret = 0;
238
239         key.objectid = ino;
240         key.type = BTRFS_INODE_EXTREF_KEY;
241         key.offset = btrfs_extref_hash(parent_ino, name, namelen);
242
243         ret = btrfs_search_slot(trans, root, &key, path, ins_len,
244                         ins_len ? 1 : 0);
245         if (ret < 0)
246                 return ERR_PTR(ret);
247         if (ret > 0)
248                 return NULL;
249         if (!btrfs_find_name_in_ext_backref(path, parent_ino, name,
250                                             namelen, &extref))
251                 return NULL;
252
253         return extref;
254 }
255
256 int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
257                            struct btrfs_root *root,
258                            const char *name, int name_len,
259                            u64 inode_objectid, u64 ref_objectid,
260                            u64 *index)
261 {
262         struct btrfs_path *path;
263         struct btrfs_key key;
264         struct btrfs_inode_extref *extref;
265         struct extent_buffer *leaf;
266         int ret;
267         int del_len = name_len + sizeof(*extref);
268         unsigned long ptr;
269         unsigned long item_start;
270         u32 item_size;
271
272         key.objectid = inode_objectid;
273         key.type = BTRFS_INODE_EXTREF_KEY;
274         key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
275
276         path = btrfs_alloc_path();
277         if (!path)
278                 return -ENOMEM;
279
280         ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
281         if (ret > 0)
282                 ret = -ENOENT;
283         if (ret < 0)
284                 goto out;
285
286         /*
287          * Sanity check - did we find the right item for this name?  This
288          * should always succeed so error here will make the FS readonly.
289          */
290         if (!btrfs_find_name_in_ext_backref(path, ref_objectid,
291                                             name, name_len, &extref)) {
292                 ret = -ENOENT;
293                 goto out;
294         }
295
296         leaf = path->nodes[0];
297         item_size = btrfs_item_size_nr(leaf, path->slots[0]);
298         if (index)
299                 *index = btrfs_inode_extref_index(leaf, extref);
300
301         if (del_len == item_size) {
302                 /*
303                  * Common case only one ref in the item, remove the whole item.
304                  */
305                 ret = btrfs_del_item(trans, root, path);
306                 goto out;
307         }
308
309         ptr = (unsigned long)extref;
310         item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
311
312         memmove_extent_buffer(leaf, ptr, ptr + del_len,
313                               item_size - (ptr + del_len - item_start));
314
315         btrfs_truncate_item(root, path, item_size - del_len, 1);
316
317 out:
318         btrfs_free_path(path);
319
320         return ret;
321 }
322
323 /*
324  * btrfs_insert_inode_extref() - Inserts an extended inode ref into a tree.
325  *
326  * The caller must have checked against BTRFS_LINK_MAX already.
327  */
328 int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
329                               struct btrfs_root *root,
330                               const char *name, int name_len,
331                               u64 inode_objectid, u64 ref_objectid, u64 index)
332 {
333         struct btrfs_inode_extref *extref;
334         int ret;
335         int ins_len = name_len + sizeof(*extref);
336         unsigned long ptr;
337         struct btrfs_path *path;
338         struct btrfs_key key;
339         struct extent_buffer *leaf;
340         struct btrfs_item *item;
341
342         key.objectid = inode_objectid;
343         key.type = BTRFS_INODE_EXTREF_KEY;
344         key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
345
346         path = btrfs_alloc_path();
347         if (!path)
348                 return -ENOMEM;
349
350         ret = btrfs_insert_empty_item(trans, root, path, &key,
351                                       ins_len);
352         if (ret == -EEXIST) {
353                 if (btrfs_find_name_in_ext_backref(path, ref_objectid,
354                                                    name, name_len, NULL))
355                         goto out;
356
357                 btrfs_extend_item(root, path, ins_len);
358                 ret = 0;
359         }
360
361         if (ret < 0)
362                 goto out;
363
364         leaf = path->nodes[0];
365         item = btrfs_item_nr(path->slots[0]);
366         ptr = (unsigned long)btrfs_item_ptr(leaf, path->slots[0], char);
367         ptr += btrfs_item_size(leaf, item) - ins_len;
368         extref = (struct btrfs_inode_extref *)ptr;
369
370         btrfs_set_inode_extref_name_len(path->nodes[0], extref, name_len);
371         btrfs_set_inode_extref_index(path->nodes[0], extref, index);
372         btrfs_set_inode_extref_parent(path->nodes[0], extref, ref_objectid);
373
374         ptr = (unsigned long)&extref->name;
375         write_extent_buffer(path->nodes[0], name, ptr, name_len);
376         btrfs_mark_buffer_dirty(path->nodes[0]);
377
378 out:
379         btrfs_free_path(path);
380
381         return ret;
382 }
383
384 int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
385                         struct btrfs_root *root, const char *name, int name_len,
386                         u64 ino, u64 parent_ino, u64 *index)
387 {
388         struct btrfs_path *path;
389         struct btrfs_key key;
390         struct btrfs_inode_ref *ref;
391         struct extent_buffer *leaf;
392         unsigned long ptr;
393         unsigned long item_start;
394         u32 item_size;
395         u32 sub_item_len;
396         int ret;
397         int search_ext_refs = 0;
398         int del_len = name_len + sizeof(*ref);
399
400         key.objectid = ino;
401         key.offset = parent_ino;
402         key.type = BTRFS_INODE_REF_KEY;
403
404         path = btrfs_alloc_path();
405         if (!path)
406                 return -ENOMEM;
407
408         ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
409         if (ret > 0) {
410                 ret = -ENOENT;
411                 search_ext_refs = 1;
412                 goto out;
413         } else if (ret < 0) {
414                 goto out;
415         }
416         if (!find_name_in_backref(path, name, name_len, &ref)) {
417                 ret = -ENOENT;
418                 search_ext_refs = 1;
419                 goto out;
420         }
421         leaf = path->nodes[0];
422         item_size = btrfs_item_size_nr(leaf, path->slots[0]);
423
424         if (index)
425                 *index = btrfs_inode_ref_index(leaf, ref);
426
427         if (del_len == item_size) {
428                 ret = btrfs_del_item(trans, root, path);
429                 goto out;
430         }
431         ptr = (unsigned long)ref;
432         sub_item_len = name_len + sizeof(*ref);
433         item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
434         memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
435                               item_size - (ptr + sub_item_len - item_start));
436         btrfs_truncate_item(root, path, item_size - sub_item_len, 1);
437         btrfs_mark_buffer_dirty(path->nodes[0]);
438 out:
439         btrfs_free_path(path);
440
441         if (search_ext_refs &&
442             btrfs_fs_incompat(root->fs_info, EXTENDED_IREF)) {
443                 /*
444                  * No refs were found, or we could not find the name in our ref
445                  * array. Find and remove the extended inode ref then.
446                  */
447                 return btrfs_del_inode_extref(trans, root, name, name_len,
448                                               ino, parent_ino, index);
449         }
450
451         return ret;
452 }