xref: /openbmc/linux/fs/btrfs/inode-item.c (revision 48a3b636)
16cbd5570SChris Mason /*
26cbd5570SChris Mason  * Copyright (C) 2007 Oracle.  All rights reserved.
36cbd5570SChris Mason  *
46cbd5570SChris Mason  * This program is free software; you can redistribute it and/or
56cbd5570SChris Mason  * modify it under the terms of the GNU General Public
66cbd5570SChris Mason  * License v2 as published by the Free Software Foundation.
76cbd5570SChris Mason  *
86cbd5570SChris Mason  * This program is distributed in the hope that it will be useful,
96cbd5570SChris Mason  * but WITHOUT ANY WARRANTY; without even the implied warranty of
106cbd5570SChris Mason  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
116cbd5570SChris Mason  * General Public License for more details.
126cbd5570SChris Mason  *
136cbd5570SChris Mason  * You should have received a copy of the GNU General Public
146cbd5570SChris Mason  * License along with this program; if not, write to the
156cbd5570SChris Mason  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
166cbd5570SChris Mason  * Boston, MA 021110-1307, USA.
176cbd5570SChris Mason  */
186cbd5570SChris Mason 
191e1d2701SChris Mason #include "ctree.h"
201e1d2701SChris Mason #include "disk-io.h"
21f186373fSMark Fasheh #include "hash.h"
22e089f05cSChris Mason #include "transaction.h"
23727011e0SChris Mason #include "print-tree.h"
241e1d2701SChris Mason 
25b2950863SChristoph Hellwig static int find_name_in_backref(struct btrfs_path *path, const char *name,
263954401fSChris Mason 			 int name_len, struct btrfs_inode_ref **ref_ret)
273954401fSChris Mason {
283954401fSChris Mason 	struct extent_buffer *leaf;
293954401fSChris Mason 	struct btrfs_inode_ref *ref;
303954401fSChris Mason 	unsigned long ptr;
313954401fSChris Mason 	unsigned long name_ptr;
323954401fSChris Mason 	u32 item_size;
333954401fSChris Mason 	u32 cur_offset = 0;
343954401fSChris Mason 	int len;
353954401fSChris Mason 
363954401fSChris Mason 	leaf = path->nodes[0];
373954401fSChris Mason 	item_size = btrfs_item_size_nr(leaf, path->slots[0]);
383954401fSChris Mason 	ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
393954401fSChris Mason 	while (cur_offset < item_size) {
403954401fSChris Mason 		ref = (struct btrfs_inode_ref *)(ptr + cur_offset);
413954401fSChris Mason 		len = btrfs_inode_ref_name_len(leaf, ref);
423954401fSChris Mason 		name_ptr = (unsigned long)(ref + 1);
433954401fSChris Mason 		cur_offset += len + sizeof(*ref);
443954401fSChris Mason 		if (len != name_len)
453954401fSChris Mason 			continue;
463954401fSChris Mason 		if (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) {
473954401fSChris Mason 			*ref_ret = ref;
483954401fSChris Mason 			return 1;
493954401fSChris Mason 		}
503954401fSChris Mason 	}
513954401fSChris Mason 	return 0;
523954401fSChris Mason }
533954401fSChris Mason 
54f186373fSMark Fasheh int btrfs_find_name_in_ext_backref(struct btrfs_path *path, u64 ref_objectid,
55f186373fSMark Fasheh 				   const char *name, int name_len,
56f186373fSMark Fasheh 				   struct btrfs_inode_extref **extref_ret)
57f186373fSMark Fasheh {
58f186373fSMark Fasheh 	struct extent_buffer *leaf;
59f186373fSMark Fasheh 	struct btrfs_inode_extref *extref;
60f186373fSMark Fasheh 	unsigned long ptr;
61f186373fSMark Fasheh 	unsigned long name_ptr;
62f186373fSMark Fasheh 	u32 item_size;
63f186373fSMark Fasheh 	u32 cur_offset = 0;
64f186373fSMark Fasheh 	int ref_name_len;
65f186373fSMark Fasheh 
66f186373fSMark Fasheh 	leaf = path->nodes[0];
67f186373fSMark Fasheh 	item_size = btrfs_item_size_nr(leaf, path->slots[0]);
68f186373fSMark Fasheh 	ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
69f186373fSMark Fasheh 
70f186373fSMark Fasheh 	/*
71f186373fSMark Fasheh 	 * Search all extended backrefs in this item. We're only
72f186373fSMark Fasheh 	 * looking through any collisions so most of the time this is
73f186373fSMark Fasheh 	 * just going to compare against one buffer. If all is well,
74f186373fSMark Fasheh 	 * we'll return success and the inode ref object.
75f186373fSMark Fasheh 	 */
76f186373fSMark Fasheh 	while (cur_offset < item_size) {
77f186373fSMark Fasheh 		extref = (struct btrfs_inode_extref *) (ptr + cur_offset);
78f186373fSMark Fasheh 		name_ptr = (unsigned long)(&extref->name);
79f186373fSMark Fasheh 		ref_name_len = btrfs_inode_extref_name_len(leaf, extref);
80f186373fSMark Fasheh 
81f186373fSMark Fasheh 		if (ref_name_len == name_len &&
82f186373fSMark Fasheh 		    btrfs_inode_extref_parent(leaf, extref) == ref_objectid &&
83f186373fSMark Fasheh 		    (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)) {
84f186373fSMark Fasheh 			if (extref_ret)
85f186373fSMark Fasheh 				*extref_ret = extref;
86f186373fSMark Fasheh 			return 1;
87f186373fSMark Fasheh 		}
88f186373fSMark Fasheh 
89f186373fSMark Fasheh 		cur_offset += ref_name_len + sizeof(*extref);
90f186373fSMark Fasheh 	}
91f186373fSMark Fasheh 	return 0;
92f186373fSMark Fasheh }
93f186373fSMark Fasheh 
94f186373fSMark Fasheh static struct btrfs_inode_ref *
95a22285a6SYan, Zheng btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans,
96a22285a6SYan, Zheng 		       struct btrfs_root *root,
97a22285a6SYan, Zheng 		       struct btrfs_path *path,
98a22285a6SYan, Zheng 		       const char *name, int name_len,
99f186373fSMark Fasheh 		       u64 inode_objectid, u64 ref_objectid, int ins_len,
100f186373fSMark Fasheh 		       int cow)
101a22285a6SYan, Zheng {
102f186373fSMark Fasheh 	int ret;
103a22285a6SYan, Zheng 	struct btrfs_key key;
104a22285a6SYan, Zheng 	struct btrfs_inode_ref *ref;
105a22285a6SYan, Zheng 
106a22285a6SYan, Zheng 	key.objectid = inode_objectid;
107a22285a6SYan, Zheng 	key.type = BTRFS_INODE_REF_KEY;
108a22285a6SYan, Zheng 	key.offset = ref_objectid;
109a22285a6SYan, Zheng 
110a22285a6SYan, Zheng 	ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
111a22285a6SYan, Zheng 	if (ret < 0)
112a22285a6SYan, Zheng 		return ERR_PTR(ret);
113a22285a6SYan, Zheng 	if (ret > 0)
114a22285a6SYan, Zheng 		return NULL;
115a22285a6SYan, Zheng 	if (!find_name_in_backref(path, name, name_len, &ref))
116a22285a6SYan, Zheng 		return NULL;
117a22285a6SYan, Zheng 	return ref;
118a22285a6SYan, Zheng }
119a22285a6SYan, Zheng 
120f186373fSMark Fasheh /* Returns NULL if no extref found */
121f186373fSMark Fasheh struct btrfs_inode_extref *
122f186373fSMark Fasheh btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
123f186373fSMark Fasheh 			  struct btrfs_root *root,
124f186373fSMark Fasheh 			  struct btrfs_path *path,
125f186373fSMark Fasheh 			  const char *name, int name_len,
126f186373fSMark Fasheh 			  u64 inode_objectid, u64 ref_objectid, int ins_len,
127f186373fSMark Fasheh 			  int cow)
128f186373fSMark Fasheh {
129f186373fSMark Fasheh 	int ret;
130f186373fSMark Fasheh 	struct btrfs_key key;
131f186373fSMark Fasheh 	struct btrfs_inode_extref *extref;
132f186373fSMark Fasheh 
133f186373fSMark Fasheh 	key.objectid = inode_objectid;
134f186373fSMark Fasheh 	key.type = BTRFS_INODE_EXTREF_KEY;
135f186373fSMark Fasheh 	key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
136f186373fSMark Fasheh 
137f186373fSMark Fasheh 	ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
138f186373fSMark Fasheh 	if (ret < 0)
139f186373fSMark Fasheh 		return ERR_PTR(ret);
140f186373fSMark Fasheh 	if (ret > 0)
141f186373fSMark Fasheh 		return NULL;
142f186373fSMark Fasheh 	if (!btrfs_find_name_in_ext_backref(path, ref_objectid, name, name_len, &extref))
143f186373fSMark Fasheh 		return NULL;
144f186373fSMark Fasheh 	return extref;
145f186373fSMark Fasheh }
146f186373fSMark Fasheh 
147f186373fSMark Fasheh int btrfs_get_inode_ref_index(struct btrfs_trans_handle *trans,
148f186373fSMark Fasheh 			      struct btrfs_root *root,
149f186373fSMark Fasheh 			      struct btrfs_path *path,
150f186373fSMark Fasheh 			      const char *name, int name_len,
151f186373fSMark Fasheh 			      u64 inode_objectid, u64 ref_objectid, int mod,
152f186373fSMark Fasheh 			      u64 *ret_index)
153f186373fSMark Fasheh {
154f186373fSMark Fasheh 	struct btrfs_inode_ref *ref;
155f186373fSMark Fasheh 	struct btrfs_inode_extref *extref;
156f186373fSMark Fasheh 	int ins_len = mod < 0 ? -1 : 0;
157f186373fSMark Fasheh 	int cow = mod != 0;
158f186373fSMark Fasheh 
159f186373fSMark Fasheh 	ref = btrfs_lookup_inode_ref(trans, root, path, name, name_len,
160f186373fSMark Fasheh 				     inode_objectid, ref_objectid, ins_len,
161f186373fSMark Fasheh 				     cow);
162f186373fSMark Fasheh 	if (IS_ERR(ref))
163f186373fSMark Fasheh 		return PTR_ERR(ref);
164f186373fSMark Fasheh 
165f186373fSMark Fasheh 	if (ref != NULL) {
166f186373fSMark Fasheh 		*ret_index = btrfs_inode_ref_index(path->nodes[0], ref);
167f186373fSMark Fasheh 		return 0;
168f186373fSMark Fasheh 	}
169f186373fSMark Fasheh 
170f186373fSMark Fasheh 	btrfs_release_path(path);
171f186373fSMark Fasheh 
172f186373fSMark Fasheh 	extref = btrfs_lookup_inode_extref(trans, root, path, name,
173f186373fSMark Fasheh 					   name_len, inode_objectid,
174f186373fSMark Fasheh 					   ref_objectid, ins_len, cow);
175f186373fSMark Fasheh 	if (IS_ERR(extref))
176f186373fSMark Fasheh 		return PTR_ERR(extref);
177f186373fSMark Fasheh 
178f186373fSMark Fasheh 	if (extref) {
179f186373fSMark Fasheh 		*ret_index = btrfs_inode_extref_index(path->nodes[0], extref);
180f186373fSMark Fasheh 		return 0;
181f186373fSMark Fasheh 	}
182f186373fSMark Fasheh 
183f186373fSMark Fasheh 	return -ENOENT;
184f186373fSMark Fasheh }
185f186373fSMark Fasheh 
18648a3b636SEric Sandeen static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
187f186373fSMark Fasheh 				  struct btrfs_root *root,
188f186373fSMark Fasheh 				  const char *name, int name_len,
18948a3b636SEric Sandeen 				  u64 inode_objectid, u64 ref_objectid,
19048a3b636SEric Sandeen 				  u64 *index)
191f186373fSMark Fasheh {
192f186373fSMark Fasheh 	struct btrfs_path *path;
193f186373fSMark Fasheh 	struct btrfs_key key;
194f186373fSMark Fasheh 	struct btrfs_inode_extref *extref;
195f186373fSMark Fasheh 	struct extent_buffer *leaf;
196f186373fSMark Fasheh 	int ret;
197f186373fSMark Fasheh 	int del_len = name_len + sizeof(*extref);
198f186373fSMark Fasheh 	unsigned long ptr;
199f186373fSMark Fasheh 	unsigned long item_start;
200f186373fSMark Fasheh 	u32 item_size;
201f186373fSMark Fasheh 
202f186373fSMark Fasheh 	key.objectid = inode_objectid;
203f186373fSMark Fasheh 	btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY);
204f186373fSMark Fasheh 	key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
205f186373fSMark Fasheh 
206f186373fSMark Fasheh 	path = btrfs_alloc_path();
207f186373fSMark Fasheh 	if (!path)
208f186373fSMark Fasheh 		return -ENOMEM;
209f186373fSMark Fasheh 
210f186373fSMark Fasheh 	path->leave_spinning = 1;
211f186373fSMark Fasheh 
212f186373fSMark Fasheh 	ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
213f186373fSMark Fasheh 	if (ret > 0)
214f186373fSMark Fasheh 		ret = -ENOENT;
215f186373fSMark Fasheh 	if (ret < 0)
216f186373fSMark Fasheh 		goto out;
217f186373fSMark Fasheh 
218f186373fSMark Fasheh 	/*
219f186373fSMark Fasheh 	 * Sanity check - did we find the right item for this name?
220f186373fSMark Fasheh 	 * This should always succeed so error here will make the FS
221f186373fSMark Fasheh 	 * readonly.
222f186373fSMark Fasheh 	 */
223f186373fSMark Fasheh 	if (!btrfs_find_name_in_ext_backref(path, ref_objectid,
224f186373fSMark Fasheh 					    name, name_len, &extref)) {
225f186373fSMark Fasheh 		btrfs_std_error(root->fs_info, -ENOENT);
226f186373fSMark Fasheh 		ret = -EROFS;
227f186373fSMark Fasheh 		goto out;
228f186373fSMark Fasheh 	}
229f186373fSMark Fasheh 
230f186373fSMark Fasheh 	leaf = path->nodes[0];
231f186373fSMark Fasheh 	item_size = btrfs_item_size_nr(leaf, path->slots[0]);
232f186373fSMark Fasheh 	if (index)
233f186373fSMark Fasheh 		*index = btrfs_inode_extref_index(leaf, extref);
234f186373fSMark Fasheh 
235f186373fSMark Fasheh 	if (del_len == item_size) {
236f186373fSMark Fasheh 		/*
237f186373fSMark Fasheh 		 * Common case only one ref in the item, remove the
238f186373fSMark Fasheh 		 * whole item.
239f186373fSMark Fasheh 		 */
240f186373fSMark Fasheh 		ret = btrfs_del_item(trans, root, path);
241f186373fSMark Fasheh 		goto out;
242f186373fSMark Fasheh 	}
243f186373fSMark Fasheh 
244f186373fSMark Fasheh 	ptr = (unsigned long)extref;
245f186373fSMark Fasheh 	item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
246f186373fSMark Fasheh 
247f186373fSMark Fasheh 	memmove_extent_buffer(leaf, ptr, ptr + del_len,
248f186373fSMark Fasheh 			      item_size - (ptr + del_len - item_start));
249f186373fSMark Fasheh 
250afe5fea7STsutomu Itoh 	btrfs_truncate_item(root, path, item_size - del_len, 1);
251f186373fSMark Fasheh 
252f186373fSMark Fasheh out:
253f186373fSMark Fasheh 	btrfs_free_path(path);
254f186373fSMark Fasheh 
255f186373fSMark Fasheh 	return ret;
256f186373fSMark Fasheh }
257f186373fSMark Fasheh 
2583954401fSChris Mason int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
2593954401fSChris Mason 			struct btrfs_root *root,
2603954401fSChris Mason 			const char *name, int name_len,
261aec7477bSJosef Bacik 			u64 inode_objectid, u64 ref_objectid, u64 *index)
2623954401fSChris Mason {
2633954401fSChris Mason 	struct btrfs_path *path;
2643954401fSChris Mason 	struct btrfs_key key;
2653954401fSChris Mason 	struct btrfs_inode_ref *ref;
2663954401fSChris Mason 	struct extent_buffer *leaf;
2673954401fSChris Mason 	unsigned long ptr;
2683954401fSChris Mason 	unsigned long item_start;
2693954401fSChris Mason 	u32 item_size;
2703954401fSChris Mason 	u32 sub_item_len;
2713954401fSChris Mason 	int ret;
272f186373fSMark Fasheh 	int search_ext_refs = 0;
2733954401fSChris Mason 	int del_len = name_len + sizeof(*ref);
2743954401fSChris Mason 
2753954401fSChris Mason 	key.objectid = inode_objectid;
2763954401fSChris Mason 	key.offset = ref_objectid;
2773954401fSChris Mason 	btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY);
2783954401fSChris Mason 
2793954401fSChris Mason 	path = btrfs_alloc_path();
2803954401fSChris Mason 	if (!path)
2813954401fSChris Mason 		return -ENOMEM;
2823954401fSChris Mason 
283b9473439SChris Mason 	path->leave_spinning = 1;
284b9473439SChris Mason 
2853954401fSChris Mason 	ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
2863954401fSChris Mason 	if (ret > 0) {
2873954401fSChris Mason 		ret = -ENOENT;
288f186373fSMark Fasheh 		search_ext_refs = 1;
2893954401fSChris Mason 		goto out;
2903954401fSChris Mason 	} else if (ret < 0) {
2913954401fSChris Mason 		goto out;
2923954401fSChris Mason 	}
2933954401fSChris Mason 	if (!find_name_in_backref(path, name, name_len, &ref)) {
2943954401fSChris Mason 		ret = -ENOENT;
295f186373fSMark Fasheh 		search_ext_refs = 1;
2963954401fSChris Mason 		goto out;
2973954401fSChris Mason 	}
2983954401fSChris Mason 	leaf = path->nodes[0];
2993954401fSChris Mason 	item_size = btrfs_item_size_nr(leaf, path->slots[0]);
300aec7477bSJosef Bacik 
301aec7477bSJosef Bacik 	if (index)
302aec7477bSJosef Bacik 		*index = btrfs_inode_ref_index(leaf, ref);
303aec7477bSJosef Bacik 
3043954401fSChris Mason 	if (del_len == item_size) {
3053954401fSChris Mason 		ret = btrfs_del_item(trans, root, path);
3063954401fSChris Mason 		goto out;
3073954401fSChris Mason 	}
3083954401fSChris Mason 	ptr = (unsigned long)ref;
3093954401fSChris Mason 	sub_item_len = name_len + sizeof(*ref);
3103954401fSChris Mason 	item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
3113954401fSChris Mason 	memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
3123954401fSChris Mason 			      item_size - (ptr + sub_item_len - item_start));
313afe5fea7STsutomu Itoh 	btrfs_truncate_item(root, path, item_size - sub_item_len, 1);
314f186373fSMark Fasheh out:
315f186373fSMark Fasheh 	btrfs_free_path(path);
316f186373fSMark Fasheh 
317f186373fSMark Fasheh 	if (search_ext_refs) {
318f186373fSMark Fasheh 		/*
319f186373fSMark Fasheh 		 * No refs were found, or we could not find the
320f186373fSMark Fasheh 		 * name in our ref array. Find and remove the extended
321f186373fSMark Fasheh 		 * inode ref then.
322f186373fSMark Fasheh 		 */
323f186373fSMark Fasheh 		return btrfs_del_inode_extref(trans, root, name, name_len,
324f186373fSMark Fasheh 					      inode_objectid, ref_objectid, index);
325f186373fSMark Fasheh 	}
326f186373fSMark Fasheh 
327f186373fSMark Fasheh 	return ret;
328f186373fSMark Fasheh }
329f186373fSMark Fasheh 
330f186373fSMark Fasheh /*
331f186373fSMark Fasheh  * btrfs_insert_inode_extref() - Inserts an extended inode ref into a tree.
332f186373fSMark Fasheh  *
333f186373fSMark Fasheh  * The caller must have checked against BTRFS_LINK_MAX already.
334f186373fSMark Fasheh  */
335f186373fSMark Fasheh static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
336f186373fSMark Fasheh 				     struct btrfs_root *root,
337f186373fSMark Fasheh 				     const char *name, int name_len,
338f186373fSMark Fasheh 				     u64 inode_objectid, u64 ref_objectid, u64 index)
339f186373fSMark Fasheh {
340f186373fSMark Fasheh 	struct btrfs_inode_extref *extref;
341f186373fSMark Fasheh 	int ret;
342f186373fSMark Fasheh 	int ins_len = name_len + sizeof(*extref);
343f186373fSMark Fasheh 	unsigned long ptr;
344f186373fSMark Fasheh 	struct btrfs_path *path;
345f186373fSMark Fasheh 	struct btrfs_key key;
346f186373fSMark Fasheh 	struct extent_buffer *leaf;
347f186373fSMark Fasheh 	struct btrfs_item *item;
348f186373fSMark Fasheh 
349f186373fSMark Fasheh 	key.objectid = inode_objectid;
350f186373fSMark Fasheh 	key.type = BTRFS_INODE_EXTREF_KEY;
351f186373fSMark Fasheh 	key.offset = btrfs_extref_hash(ref_objectid, name, name_len);
352f186373fSMark Fasheh 
353f186373fSMark Fasheh 	path = btrfs_alloc_path();
354f186373fSMark Fasheh 	if (!path)
355f186373fSMark Fasheh 		return -ENOMEM;
356f186373fSMark Fasheh 
357f186373fSMark Fasheh 	path->leave_spinning = 1;
358f186373fSMark Fasheh 	ret = btrfs_insert_empty_item(trans, root, path, &key,
359f186373fSMark Fasheh 				      ins_len);
360f186373fSMark Fasheh 	if (ret == -EEXIST) {
361f186373fSMark Fasheh 		if (btrfs_find_name_in_ext_backref(path, ref_objectid,
362f186373fSMark Fasheh 						   name, name_len, NULL))
363f186373fSMark Fasheh 			goto out;
364f186373fSMark Fasheh 
3654b90c680STsutomu Itoh 		btrfs_extend_item(root, path, ins_len);
366f186373fSMark Fasheh 		ret = 0;
367f186373fSMark Fasheh 	}
368f186373fSMark Fasheh 	if (ret < 0)
369f186373fSMark Fasheh 		goto out;
370f186373fSMark Fasheh 
371f186373fSMark Fasheh 	leaf = path->nodes[0];
372f186373fSMark Fasheh 	item = btrfs_item_nr(leaf, path->slots[0]);
373f186373fSMark Fasheh 	ptr = (unsigned long)btrfs_item_ptr(leaf, path->slots[0], char);
374f186373fSMark Fasheh 	ptr += btrfs_item_size(leaf, item) - ins_len;
375f186373fSMark Fasheh 	extref = (struct btrfs_inode_extref *)ptr;
376f186373fSMark Fasheh 
377f186373fSMark Fasheh 	btrfs_set_inode_extref_name_len(path->nodes[0], extref, name_len);
378f186373fSMark Fasheh 	btrfs_set_inode_extref_index(path->nodes[0], extref, index);
379f186373fSMark Fasheh 	btrfs_set_inode_extref_parent(path->nodes[0], extref, ref_objectid);
380f186373fSMark Fasheh 
381f186373fSMark Fasheh 	ptr = (unsigned long)&extref->name;
382f186373fSMark Fasheh 	write_extent_buffer(path->nodes[0], name, ptr, name_len);
383f186373fSMark Fasheh 	btrfs_mark_buffer_dirty(path->nodes[0]);
384f186373fSMark Fasheh 
3853954401fSChris Mason out:
3863954401fSChris Mason 	btrfs_free_path(path);
3873954401fSChris Mason 	return ret;
3883954401fSChris Mason }
3893954401fSChris Mason 
39079787eaaSJeff Mahoney /* Will return 0, -ENOMEM, -EMLINK, or -EEXIST or anything from the CoW path */
3913954401fSChris Mason int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
3923954401fSChris Mason 			   struct btrfs_root *root,
3933954401fSChris Mason 			   const char *name, int name_len,
394aec7477bSJosef Bacik 			   u64 inode_objectid, u64 ref_objectid, u64 index)
3953954401fSChris Mason {
3963954401fSChris Mason 	struct btrfs_path *path;
3973954401fSChris Mason 	struct btrfs_key key;
3983954401fSChris Mason 	struct btrfs_inode_ref *ref;
3993954401fSChris Mason 	unsigned long ptr;
4003954401fSChris Mason 	int ret;
4013954401fSChris Mason 	int ins_len = name_len + sizeof(*ref);
4023954401fSChris Mason 
4033954401fSChris Mason 	key.objectid = inode_objectid;
4043954401fSChris Mason 	key.offset = ref_objectid;
4053954401fSChris Mason 	btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY);
4063954401fSChris Mason 
4073954401fSChris Mason 	path = btrfs_alloc_path();
4083954401fSChris Mason 	if (!path)
4093954401fSChris Mason 		return -ENOMEM;
4103954401fSChris Mason 
411b9473439SChris Mason 	path->leave_spinning = 1;
4123954401fSChris Mason 	ret = btrfs_insert_empty_item(trans, root, path, &key,
4133954401fSChris Mason 				      ins_len);
4143954401fSChris Mason 	if (ret == -EEXIST) {
4153954401fSChris Mason 		u32 old_size;
4163954401fSChris Mason 
4173954401fSChris Mason 		if (find_name_in_backref(path, name, name_len, &ref))
4183954401fSChris Mason 			goto out;
4193954401fSChris Mason 
4203954401fSChris Mason 		old_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]);
4214b90c680STsutomu Itoh 		btrfs_extend_item(root, path, ins_len);
4223954401fSChris Mason 		ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
4233954401fSChris Mason 				     struct btrfs_inode_ref);
4243954401fSChris Mason 		ref = (struct btrfs_inode_ref *)((unsigned long)ref + old_size);
4253954401fSChris Mason 		btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
426aec7477bSJosef Bacik 		btrfs_set_inode_ref_index(path->nodes[0], ref, index);
4273954401fSChris Mason 		ptr = (unsigned long)(ref + 1);
4283954401fSChris Mason 		ret = 0;
4293954401fSChris Mason 	} else if (ret < 0) {
430a5719521SYan, Zheng 		if (ret == -EOVERFLOW)
431a5719521SYan, Zheng 			ret = -EMLINK;
4323954401fSChris Mason 		goto out;
4333954401fSChris Mason 	} else {
4343954401fSChris Mason 		ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
4353954401fSChris Mason 				     struct btrfs_inode_ref);
4363954401fSChris Mason 		btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
437aec7477bSJosef Bacik 		btrfs_set_inode_ref_index(path->nodes[0], ref, index);
4383954401fSChris Mason 		ptr = (unsigned long)(ref + 1);
4393954401fSChris Mason 	}
4403954401fSChris Mason 	write_extent_buffer(path->nodes[0], name, ptr, name_len);
4413954401fSChris Mason 	btrfs_mark_buffer_dirty(path->nodes[0]);
4423954401fSChris Mason 
4433954401fSChris Mason out:
4443954401fSChris Mason 	btrfs_free_path(path);
445f186373fSMark Fasheh 
446f186373fSMark Fasheh 	if (ret == -EMLINK) {
447f186373fSMark Fasheh 		struct btrfs_super_block *disk_super = root->fs_info->super_copy;
448f186373fSMark Fasheh 		/* We ran out of space in the ref array. Need to
449f186373fSMark Fasheh 		 * add an extended ref. */
450f186373fSMark Fasheh 		if (btrfs_super_incompat_flags(disk_super)
451f186373fSMark Fasheh 		    & BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
452f186373fSMark Fasheh 			ret = btrfs_insert_inode_extref(trans, root, name,
453f186373fSMark Fasheh 							name_len,
454f186373fSMark Fasheh 							inode_objectid,
455f186373fSMark Fasheh 							ref_objectid, index);
456f186373fSMark Fasheh 	}
457f186373fSMark Fasheh 
4583954401fSChris Mason 	return ret;
4593954401fSChris Mason }
4603954401fSChris Mason 
4615f39d397SChris Mason int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans,
4625f39d397SChris Mason 			     struct btrfs_root *root,
4635f39d397SChris Mason 			     struct btrfs_path *path, u64 objectid)
4641e1d2701SChris Mason {
4651e1d2701SChris Mason 	struct btrfs_key key;
4661e1d2701SChris Mason 	int ret;
4671e1d2701SChris Mason 	key.objectid = objectid;
4681e1d2701SChris Mason 	btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
4691e1d2701SChris Mason 	key.offset = 0;
4701e1d2701SChris Mason 
4715f39d397SChris Mason 	ret = btrfs_insert_empty_item(trans, root, path, &key,
4725f39d397SChris Mason 				      sizeof(struct btrfs_inode_item));
4731e1d2701SChris Mason 	return ret;
4741e1d2701SChris Mason }
4751e1d2701SChris Mason 
476e089f05cSChris Mason int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root
477d6e4a428SChris Mason 		       *root, struct btrfs_path *path,
478d6e4a428SChris Mason 		       struct btrfs_key *location, int mod)
4791e1d2701SChris Mason {
4801e1d2701SChris Mason 	int ins_len = mod < 0 ? -1 : 0;
4811e1d2701SChris Mason 	int cow = mod != 0;
482d6e4a428SChris Mason 	int ret;
483d6e4a428SChris Mason 	int slot;
4845f39d397SChris Mason 	struct extent_buffer *leaf;
485d6e4a428SChris Mason 	struct btrfs_key found_key;
4861e1d2701SChris Mason 
487d6e4a428SChris Mason 	ret = btrfs_search_slot(trans, root, location, path, ins_len, cow);
488d6e4a428SChris Mason 	if (ret > 0 && btrfs_key_type(location) == BTRFS_ROOT_ITEM_KEY &&
489d6e4a428SChris Mason 	    location->offset == (u64)-1 && path->slots[0] != 0) {
490d6e4a428SChris Mason 		slot = path->slots[0] - 1;
4915f39d397SChris Mason 		leaf = path->nodes[0];
4925f39d397SChris Mason 		btrfs_item_key_to_cpu(leaf, &found_key, slot);
493d6e4a428SChris Mason 		if (found_key.objectid == location->objectid &&
494d6e4a428SChris Mason 		    btrfs_key_type(&found_key) == btrfs_key_type(location)) {
495d6e4a428SChris Mason 			path->slots[0]--;
496d6e4a428SChris Mason 			return 0;
497d6e4a428SChris Mason 		}
498d6e4a428SChris Mason 	}
499d6e4a428SChris Mason 	return ret;
5001e1d2701SChris Mason }
501