1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2007 Oracle. All rights reserved.
8 #include "transaction.h"
9 #include "print-tree.h"
11 int btrfs_find_name_in_backref(struct extent_buffer
*leaf
, int slot
,
13 int name_len
, struct btrfs_inode_ref
**ref_ret
)
15 struct btrfs_inode_ref
*ref
;
17 unsigned long name_ptr
;
22 item_size
= btrfs_item_size_nr(leaf
, slot
);
23 ptr
= btrfs_item_ptr_offset(leaf
, slot
);
24 while (cur_offset
< item_size
) {
25 ref
= (struct btrfs_inode_ref
*)(ptr
+ cur_offset
);
26 len
= btrfs_inode_ref_name_len(leaf
, ref
);
27 name_ptr
= (unsigned long)(ref
+ 1);
28 cur_offset
+= len
+ sizeof(*ref
);
31 if (memcmp_extent_buffer(leaf
, name
, name_ptr
, name_len
) == 0) {
40 int btrfs_find_name_in_ext_backref(struct extent_buffer
*leaf
, int slot
,
42 const char *name
, int name_len
,
43 struct btrfs_inode_extref
**extref_ret
)
45 struct btrfs_inode_extref
*extref
;
47 unsigned long name_ptr
;
52 item_size
= btrfs_item_size_nr(leaf
, slot
);
53 ptr
= btrfs_item_ptr_offset(leaf
, slot
);
56 * Search all extended backrefs in this item. We're only
57 * looking through any collisions so most of the time this is
58 * just going to compare against one buffer. If all is well,
59 * we'll return success and the inode ref object.
61 while (cur_offset
< item_size
) {
62 extref
= (struct btrfs_inode_extref
*) (ptr
+ cur_offset
);
63 name_ptr
= (unsigned long)(&extref
->name
);
64 ref_name_len
= btrfs_inode_extref_name_len(leaf
, extref
);
66 if (ref_name_len
== name_len
&&
67 btrfs_inode_extref_parent(leaf
, extref
) == ref_objectid
&&
68 (memcmp_extent_buffer(leaf
, name
, name_ptr
, name_len
) == 0)) {
74 cur_offset
+= ref_name_len
+ sizeof(*extref
);
79 /* Returns NULL if no extref found */
80 struct btrfs_inode_extref
*
81 btrfs_lookup_inode_extref(struct btrfs_trans_handle
*trans
,
82 struct btrfs_root
*root
,
83 struct btrfs_path
*path
,
84 const char *name
, int name_len
,
85 u64 inode_objectid
, u64 ref_objectid
, int ins_len
,
90 struct btrfs_inode_extref
*extref
;
92 key
.objectid
= inode_objectid
;
93 key
.type
= BTRFS_INODE_EXTREF_KEY
;
94 key
.offset
= btrfs_extref_hash(ref_objectid
, name
, name_len
);
96 ret
= btrfs_search_slot(trans
, root
, &key
, path
, ins_len
, cow
);
101 if (!btrfs_find_name_in_ext_backref(path
->nodes
[0], path
->slots
[0],
102 ref_objectid
, name
, name_len
,
108 static int btrfs_del_inode_extref(struct btrfs_trans_handle
*trans
,
109 struct btrfs_root
*root
,
110 const char *name
, int name_len
,
111 u64 inode_objectid
, u64 ref_objectid
,
114 struct btrfs_path
*path
;
115 struct btrfs_key key
;
116 struct btrfs_inode_extref
*extref
;
117 struct extent_buffer
*leaf
;
119 int del_len
= name_len
+ sizeof(*extref
);
121 unsigned long item_start
;
124 key
.objectid
= inode_objectid
;
125 key
.type
= BTRFS_INODE_EXTREF_KEY
;
126 key
.offset
= btrfs_extref_hash(ref_objectid
, name
, name_len
);
128 path
= btrfs_alloc_path();
132 path
->leave_spinning
= 1;
134 ret
= btrfs_search_slot(trans
, root
, &key
, path
, -1, 1);
141 * Sanity check - did we find the right item for this name?
142 * This should always succeed so error here will make the FS
145 if (!btrfs_find_name_in_ext_backref(path
->nodes
[0], path
->slots
[0],
147 name
, name_len
, &extref
)) {
148 btrfs_handle_fs_error(root
->fs_info
, -ENOENT
, NULL
);
153 leaf
= path
->nodes
[0];
154 item_size
= btrfs_item_size_nr(leaf
, path
->slots
[0]);
156 *index
= btrfs_inode_extref_index(leaf
, extref
);
158 if (del_len
== item_size
) {
160 * Common case only one ref in the item, remove the
163 ret
= btrfs_del_item(trans
, root
, path
);
167 ptr
= (unsigned long)extref
;
168 item_start
= btrfs_item_ptr_offset(leaf
, path
->slots
[0]);
170 memmove_extent_buffer(leaf
, ptr
, ptr
+ del_len
,
171 item_size
- (ptr
+ del_len
- item_start
));
173 btrfs_truncate_item(path
, item_size
- del_len
, 1);
176 btrfs_free_path(path
);
181 int btrfs_del_inode_ref(struct btrfs_trans_handle
*trans
,
182 struct btrfs_root
*root
,
183 const char *name
, int name_len
,
184 u64 inode_objectid
, u64 ref_objectid
, u64
*index
)
186 struct btrfs_path
*path
;
187 struct btrfs_key key
;
188 struct btrfs_inode_ref
*ref
;
189 struct extent_buffer
*leaf
;
191 unsigned long item_start
;
195 int search_ext_refs
= 0;
196 int del_len
= name_len
+ sizeof(*ref
);
198 key
.objectid
= inode_objectid
;
199 key
.offset
= ref_objectid
;
200 key
.type
= BTRFS_INODE_REF_KEY
;
202 path
= btrfs_alloc_path();
206 path
->leave_spinning
= 1;
208 ret
= btrfs_search_slot(trans
, root
, &key
, path
, -1, 1);
213 } else if (ret
< 0) {
216 if (!btrfs_find_name_in_backref(path
->nodes
[0], path
->slots
[0],
217 name
, name_len
, &ref
)) {
222 leaf
= path
->nodes
[0];
223 item_size
= btrfs_item_size_nr(leaf
, path
->slots
[0]);
226 *index
= btrfs_inode_ref_index(leaf
, ref
);
228 if (del_len
== item_size
) {
229 ret
= btrfs_del_item(trans
, root
, path
);
232 ptr
= (unsigned long)ref
;
233 sub_item_len
= name_len
+ sizeof(*ref
);
234 item_start
= btrfs_item_ptr_offset(leaf
, path
->slots
[0]);
235 memmove_extent_buffer(leaf
, ptr
, ptr
+ sub_item_len
,
236 item_size
- (ptr
+ sub_item_len
- item_start
));
237 btrfs_truncate_item(path
, item_size
- sub_item_len
, 1);
239 btrfs_free_path(path
);
241 if (search_ext_refs
) {
243 * No refs were found, or we could not find the
244 * name in our ref array. Find and remove the extended
247 return btrfs_del_inode_extref(trans
, root
, name
, name_len
,
248 inode_objectid
, ref_objectid
, index
);
255 * btrfs_insert_inode_extref() - Inserts an extended inode ref into a tree.
257 * The caller must have checked against BTRFS_LINK_MAX already.
259 static int btrfs_insert_inode_extref(struct btrfs_trans_handle
*trans
,
260 struct btrfs_root
*root
,
261 const char *name
, int name_len
,
262 u64 inode_objectid
, u64 ref_objectid
, u64 index
)
264 struct btrfs_inode_extref
*extref
;
266 int ins_len
= name_len
+ sizeof(*extref
);
268 struct btrfs_path
*path
;
269 struct btrfs_key key
;
270 struct extent_buffer
*leaf
;
271 struct btrfs_item
*item
;
273 key
.objectid
= inode_objectid
;
274 key
.type
= BTRFS_INODE_EXTREF_KEY
;
275 key
.offset
= btrfs_extref_hash(ref_objectid
, name
, name_len
);
277 path
= btrfs_alloc_path();
281 path
->leave_spinning
= 1;
282 ret
= btrfs_insert_empty_item(trans
, root
, path
, &key
,
284 if (ret
== -EEXIST
) {
285 if (btrfs_find_name_in_ext_backref(path
->nodes
[0],
288 name
, name_len
, NULL
))
291 btrfs_extend_item(path
, ins_len
);
297 leaf
= path
->nodes
[0];
298 item
= btrfs_item_nr(path
->slots
[0]);
299 ptr
= (unsigned long)btrfs_item_ptr(leaf
, path
->slots
[0], char);
300 ptr
+= btrfs_item_size(leaf
, item
) - ins_len
;
301 extref
= (struct btrfs_inode_extref
*)ptr
;
303 btrfs_set_inode_extref_name_len(path
->nodes
[0], extref
, name_len
);
304 btrfs_set_inode_extref_index(path
->nodes
[0], extref
, index
);
305 btrfs_set_inode_extref_parent(path
->nodes
[0], extref
, ref_objectid
);
307 ptr
= (unsigned long)&extref
->name
;
308 write_extent_buffer(path
->nodes
[0], name
, ptr
, name_len
);
309 btrfs_mark_buffer_dirty(path
->nodes
[0]);
312 btrfs_free_path(path
);
316 /* Will return 0, -ENOMEM, -EMLINK, or -EEXIST or anything from the CoW path */
317 int btrfs_insert_inode_ref(struct btrfs_trans_handle
*trans
,
318 struct btrfs_root
*root
,
319 const char *name
, int name_len
,
320 u64 inode_objectid
, u64 ref_objectid
, u64 index
)
322 struct btrfs_fs_info
*fs_info
= root
->fs_info
;
323 struct btrfs_path
*path
;
324 struct btrfs_key key
;
325 struct btrfs_inode_ref
*ref
;
328 int ins_len
= name_len
+ sizeof(*ref
);
330 key
.objectid
= inode_objectid
;
331 key
.offset
= ref_objectid
;
332 key
.type
= BTRFS_INODE_REF_KEY
;
334 path
= btrfs_alloc_path();
338 path
->leave_spinning
= 1;
339 path
->skip_release_on_error
= 1;
340 ret
= btrfs_insert_empty_item(trans
, root
, path
, &key
,
342 if (ret
== -EEXIST
) {
345 if (btrfs_find_name_in_backref(path
->nodes
[0], path
->slots
[0],
346 name
, name_len
, &ref
))
349 old_size
= btrfs_item_size_nr(path
->nodes
[0], path
->slots
[0]);
350 btrfs_extend_item(path
, ins_len
);
351 ref
= btrfs_item_ptr(path
->nodes
[0], path
->slots
[0],
352 struct btrfs_inode_ref
);
353 ref
= (struct btrfs_inode_ref
*)((unsigned long)ref
+ old_size
);
354 btrfs_set_inode_ref_name_len(path
->nodes
[0], ref
, name_len
);
355 btrfs_set_inode_ref_index(path
->nodes
[0], ref
, index
);
356 ptr
= (unsigned long)(ref
+ 1);
358 } else if (ret
< 0) {
359 if (ret
== -EOVERFLOW
) {
360 if (btrfs_find_name_in_backref(path
->nodes
[0],
362 name
, name_len
, &ref
))
369 ref
= btrfs_item_ptr(path
->nodes
[0], path
->slots
[0],
370 struct btrfs_inode_ref
);
371 btrfs_set_inode_ref_name_len(path
->nodes
[0], ref
, name_len
);
372 btrfs_set_inode_ref_index(path
->nodes
[0], ref
, index
);
373 ptr
= (unsigned long)(ref
+ 1);
375 write_extent_buffer(path
->nodes
[0], name
, ptr
, name_len
);
376 btrfs_mark_buffer_dirty(path
->nodes
[0]);
379 btrfs_free_path(path
);
381 if (ret
== -EMLINK
) {
382 struct btrfs_super_block
*disk_super
= fs_info
->super_copy
;
383 /* We ran out of space in the ref array. Need to
384 * add an extended ref. */
385 if (btrfs_super_incompat_flags(disk_super
)
386 & BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF
)
387 ret
= btrfs_insert_inode_extref(trans
, root
, name
,
390 ref_objectid
, index
);
396 int btrfs_insert_empty_inode(struct btrfs_trans_handle
*trans
,
397 struct btrfs_root
*root
,
398 struct btrfs_path
*path
, u64 objectid
)
400 struct btrfs_key key
;
402 key
.objectid
= objectid
;
403 key
.type
= BTRFS_INODE_ITEM_KEY
;
406 ret
= btrfs_insert_empty_item(trans
, root
, path
, &key
,
407 sizeof(struct btrfs_inode_item
));
411 int btrfs_lookup_inode(struct btrfs_trans_handle
*trans
, struct btrfs_root
412 *root
, struct btrfs_path
*path
,
413 struct btrfs_key
*location
, int mod
)
415 int ins_len
= mod
< 0 ? -1 : 0;
419 struct extent_buffer
*leaf
;
420 struct btrfs_key found_key
;
422 ret
= btrfs_search_slot(trans
, root
, location
, path
, ins_len
, cow
);
423 if (ret
> 0 && location
->type
== BTRFS_ROOT_ITEM_KEY
&&
424 location
->offset
== (u64
)-1 && path
->slots
[0] != 0) {
425 slot
= path
->slots
[0] - 1;
426 leaf
= path
->nodes
[0];
427 btrfs_item_key_to_cpu(leaf
, &found_key
, slot
);
428 if (found_key
.objectid
== location
->objectid
&&
429 found_key
.type
== location
->type
) {