xref: /openbmc/linux/fs/btrfs/dir-item.c (revision 54aa1f4d)
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 
192e635a27SChris Mason #include <linux/module.h>
2062e2749eSChris Mason #include "ctree.h"
2162e2749eSChris Mason #include "disk-io.h"
2262e2749eSChris Mason #include "hash.h"
23e089f05cSChris Mason #include "transaction.h"
2462e2749eSChris Mason 
2535b7e476SChris Mason static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
2635b7e476SChris Mason 						   *trans,
277e38180eSChris Mason 						   struct btrfs_root *root,
287e38180eSChris Mason 						   struct btrfs_path *path,
297e38180eSChris Mason 						   struct btrfs_key *cpu_key,
30e06afa83SChris Mason 						   u32 data_size,
31e06afa83SChris Mason 						   const char *name,
32e06afa83SChris Mason 						   int name_len)
337fcde0e3SChris Mason {
347fcde0e3SChris Mason 	int ret;
357e38180eSChris Mason 	char *ptr;
367e38180eSChris Mason 	struct btrfs_item *item;
377e38180eSChris Mason 	struct btrfs_leaf *leaf;
387fcde0e3SChris Mason 
397fcde0e3SChris Mason 	ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size);
407e38180eSChris Mason 	if (ret == -EEXIST) {
41e06afa83SChris Mason 		struct btrfs_dir_item *di;
42e06afa83SChris Mason 		di = btrfs_match_dir_item_name(root, path, name, name_len);
43e06afa83SChris Mason 		if (di)
44e06afa83SChris Mason 			return ERR_PTR(-EEXIST);
457e38180eSChris Mason 		ret = btrfs_extend_item(trans, root, path, data_size);
467e38180eSChris Mason 		WARN_ON(ret > 0);
477e38180eSChris Mason 		if (ret)
487e38180eSChris Mason 			return ERR_PTR(ret);
497fcde0e3SChris Mason 	}
5054aa1f4dSChris Mason 	if (ret < 0)
5154aa1f4dSChris Mason 		return ERR_PTR(ret);
527e38180eSChris Mason 	WARN_ON(ret > 0);
537e38180eSChris Mason 	leaf = btrfs_buffer_leaf(path->nodes[0]);
547e38180eSChris Mason 	item = leaf->items + path->slots[0];
557e38180eSChris Mason 	ptr = btrfs_item_ptr(leaf, path->slots[0], char);
567e38180eSChris Mason 	BUG_ON(data_size > btrfs_item_size(item));
577e38180eSChris Mason 	ptr += btrfs_item_size(item) - data_size;
587e38180eSChris Mason 	return (struct btrfs_dir_item *)ptr;
597fcde0e3SChris Mason }
607fcde0e3SChris Mason 
61e089f05cSChris Mason int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
62d6e4a428SChris Mason 			  *root, const char *name, int name_len, u64 dir,
63d6e4a428SChris Mason 			  struct btrfs_key *location, u8 type)
6462e2749eSChris Mason {
6562e2749eSChris Mason 	int ret = 0;
66e06afa83SChris Mason 	int ret2 = 0;
675caf2a00SChris Mason 	struct btrfs_path *path;
6862e2749eSChris Mason 	struct btrfs_dir_item *dir_item;
6962e2749eSChris Mason 	char *name_ptr;
7062e2749eSChris Mason 	struct btrfs_key key;
7162e2749eSChris Mason 	u32 data_size;
7262e2749eSChris Mason 
7362e2749eSChris Mason 	key.objectid = dir;
7462e2749eSChris Mason 	key.flags = 0;
751d4f6404SChris Mason 	btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
7662e2749eSChris Mason 	ret = btrfs_name_hash(name, name_len, &key.offset);
7762e2749eSChris Mason 	BUG_ON(ret);
785caf2a00SChris Mason 	path = btrfs_alloc_path();
7962e2749eSChris Mason 	data_size = sizeof(*dir_item) + name_len;
80e06afa83SChris Mason 	dir_item = insert_with_overflow(trans, root, path, &key, data_size,
81e06afa83SChris Mason 					name, name_len);
827e38180eSChris Mason 	if (IS_ERR(dir_item)) {
837e38180eSChris Mason 		ret = PTR_ERR(dir_item);
84e06afa83SChris Mason 		if (ret == -EEXIST)
85e06afa83SChris Mason 			goto second_insert;
8662e2749eSChris Mason 		goto out;
877e38180eSChris Mason 	}
8862e2749eSChris Mason 
89d6e4a428SChris Mason 	btrfs_cpu_key_to_disk(&dir_item->location, location);
9062e2749eSChris Mason 	btrfs_set_dir_type(dir_item, type);
9162e2749eSChris Mason 	btrfs_set_dir_flags(dir_item, 0);
92a8a2ee0cSChris Mason 	btrfs_set_dir_name_len(dir_item, name_len);
9362e2749eSChris Mason 	name_ptr = (char *)(dir_item + 1);
94c5739bbaSChris Mason 
95c5739bbaSChris Mason 	btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
96c5739bbaSChris Mason 	btrfs_mark_buffer_dirty(path->nodes[0]);
977e38180eSChris Mason 
98e06afa83SChris Mason second_insert:
997e38180eSChris Mason 	/* FIXME, use some real flag for selecting the extra index */
1007e38180eSChris Mason 	if (root == root->fs_info->tree_root) {
1017e38180eSChris Mason 		ret = 0;
1027e38180eSChris Mason 		goto out;
1037e38180eSChris Mason 	}
1045caf2a00SChris Mason 	btrfs_release_path(root, path);
1057e38180eSChris Mason 
1067e38180eSChris Mason 	btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
1077e38180eSChris Mason 	key.offset = location->objectid;
108e06afa83SChris Mason 	dir_item = insert_with_overflow(trans, root, path, &key, data_size,
109e06afa83SChris Mason 					name, name_len);
1107e38180eSChris Mason 	if (IS_ERR(dir_item)) {
111e06afa83SChris Mason 		ret2 = PTR_ERR(dir_item);
1127e38180eSChris Mason 		goto out;
1137e38180eSChris Mason 	}
1147e38180eSChris Mason 	btrfs_cpu_key_to_disk(&dir_item->location, location);
1157e38180eSChris Mason 	btrfs_set_dir_type(dir_item, type);
1167e38180eSChris Mason 	btrfs_set_dir_flags(dir_item, 0);
1177e38180eSChris Mason 	btrfs_set_dir_name_len(dir_item, name_len);
1187e38180eSChris Mason 	name_ptr = (char *)(dir_item + 1);
1197e38180eSChris Mason 	btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
1207e38180eSChris Mason 	btrfs_mark_buffer_dirty(path->nodes[0]);
1217e38180eSChris Mason out:
1225caf2a00SChris Mason 	btrfs_free_path(path);
123e06afa83SChris Mason 	if (ret)
12462e2749eSChris Mason 		return ret;
125e06afa83SChris Mason 	if (ret2)
126e06afa83SChris Mason 		return ret2;
127e06afa83SChris Mason 	return 0;
12862e2749eSChris Mason }
12962e2749eSChris Mason 
1307e38180eSChris Mason struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
1317e38180eSChris Mason 					     struct btrfs_root *root,
1327e38180eSChris Mason 					     struct btrfs_path *path, u64 dir,
1337e38180eSChris Mason 					     const char *name, int name_len,
1347e38180eSChris Mason 					     int mod)
13562e2749eSChris Mason {
1361d4f6404SChris Mason 	int ret;
13762e2749eSChris Mason 	struct btrfs_key key;
1381d4f6404SChris Mason 	int ins_len = mod < 0 ? -1 : 0;
1391d4f6404SChris Mason 	int cow = mod != 0;
1407fcde0e3SChris Mason 	struct btrfs_disk_key *found_key;
1417fcde0e3SChris Mason 	struct btrfs_leaf *leaf;
14262e2749eSChris Mason 
14362e2749eSChris Mason 	key.objectid = dir;
14462e2749eSChris Mason 	key.flags = 0;
1451d4f6404SChris Mason 	btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
14662e2749eSChris Mason 	ret = btrfs_name_hash(name, name_len, &key.offset);
14762e2749eSChris Mason 	BUG_ON(ret);
148e089f05cSChris Mason 	ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
1497fcde0e3SChris Mason 	if (ret < 0)
1507e38180eSChris Mason 		return ERR_PTR(ret);
1517fcde0e3SChris Mason 	if (ret > 0) {
1527fcde0e3SChris Mason 		if (path->slots[0] == 0)
1537e38180eSChris Mason 			return NULL;
1547fcde0e3SChris Mason 		path->slots[0]--;
1557fcde0e3SChris Mason 	}
1567fcde0e3SChris Mason 	leaf = btrfs_buffer_leaf(path->nodes[0]);
1577fcde0e3SChris Mason 	found_key = &leaf->items[path->slots[0]].key;
1587fcde0e3SChris Mason 
1597fcde0e3SChris Mason 	if (btrfs_disk_key_objectid(found_key) != dir ||
1607fcde0e3SChris Mason 	    btrfs_disk_key_type(found_key) != BTRFS_DIR_ITEM_KEY ||
1617fcde0e3SChris Mason 	    btrfs_disk_key_offset(found_key) != key.offset)
1627e38180eSChris Mason 		return NULL;
1637fcde0e3SChris Mason 
1647e38180eSChris Mason 	return btrfs_match_dir_item_name(root, path, name, name_len);
16562e2749eSChris Mason }
16662e2749eSChris Mason 
1677e38180eSChris Mason struct btrfs_dir_item *
1687e38180eSChris Mason btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
1697e38180eSChris Mason 			    struct btrfs_root *root,
1707e38180eSChris Mason 			    struct btrfs_path *path, u64 dir,
1717e38180eSChris Mason 			    u64 objectid, const char *name, int name_len,
1727e38180eSChris Mason 			    int mod)
1737e38180eSChris Mason {
1747e38180eSChris Mason 	int ret;
1757e38180eSChris Mason 	struct btrfs_key key;
1767e38180eSChris Mason 	int ins_len = mod < 0 ? -1 : 0;
1777e38180eSChris Mason 	int cow = mod != 0;
1787e38180eSChris Mason 
1797e38180eSChris Mason 	key.objectid = dir;
1807e38180eSChris Mason 	key.flags = 0;
1817e38180eSChris Mason 	btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
1827e38180eSChris Mason 	key.offset = objectid;
1837e38180eSChris Mason 
1847e38180eSChris Mason 	ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
1857e38180eSChris Mason 	if (ret < 0)
1867e38180eSChris Mason 		return ERR_PTR(ret);
1877e38180eSChris Mason 	if (ret > 0)
1887e38180eSChris Mason 		return ERR_PTR(-ENOENT);
1897e38180eSChris Mason 	return btrfs_match_dir_item_name(root, path, name, name_len);
1907e38180eSChris Mason }
1917e38180eSChris Mason 
1927e38180eSChris Mason struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
1937f5c1516SChris Mason 			      struct btrfs_path *path,
1947f5c1516SChris Mason 			      const char *name, int name_len)
19562e2749eSChris Mason {
19662e2749eSChris Mason 	struct btrfs_dir_item *dir_item;
19762e2749eSChris Mason 	char *name_ptr;
1987e38180eSChris Mason 	u32 total_len;
1997e38180eSChris Mason 	u32 cur = 0;
2007e38180eSChris Mason 	u32 this_len;
2017e38180eSChris Mason 	struct btrfs_leaf *leaf;
202a8a2ee0cSChris Mason 
2037e38180eSChris Mason 	leaf = btrfs_buffer_leaf(path->nodes[0]);
2047e38180eSChris Mason 	dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
2057e38180eSChris Mason 	total_len = btrfs_item_size(leaf->items + path->slots[0]);
2067e38180eSChris Mason 	while(cur < total_len) {
2077e38180eSChris Mason 		this_len = sizeof(*dir_item) + btrfs_dir_name_len(dir_item);
208a8a2ee0cSChris Mason 		name_ptr = (char *)(dir_item + 1);
2097e38180eSChris Mason 
2107e38180eSChris Mason 		if (btrfs_dir_name_len(dir_item) == name_len &&
2117e38180eSChris Mason 		    memcmp(name_ptr, name, name_len) == 0)
2127e38180eSChris Mason 			return dir_item;
2137e38180eSChris Mason 
2147e38180eSChris Mason 		cur += this_len;
2157e38180eSChris Mason 		dir_item = (struct btrfs_dir_item *)((char *)dir_item +
2167e38180eSChris Mason 						     this_len);
21762e2749eSChris Mason 	}
2187e38180eSChris Mason 	return NULL;
2197e38180eSChris Mason }
2207e38180eSChris Mason 
2217e38180eSChris Mason int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
2227e38180eSChris Mason 			      struct btrfs_root *root,
2237e38180eSChris Mason 			      struct btrfs_path *path,
2247e38180eSChris Mason 			      struct btrfs_dir_item *di)
2257e38180eSChris Mason {
2267e38180eSChris Mason 
2277e38180eSChris Mason 	struct btrfs_leaf *leaf;
2287e38180eSChris Mason 	u32 sub_item_len;
2297e38180eSChris Mason 	u32 item_len;
23054aa1f4dSChris Mason 	int ret = 0;
2317e38180eSChris Mason 
2327e38180eSChris Mason 	leaf = btrfs_buffer_leaf(path->nodes[0]);
2337e38180eSChris Mason 	sub_item_len = sizeof(*di) + btrfs_dir_name_len(di);
2347e38180eSChris Mason 	item_len = btrfs_item_size(leaf->items + path->slots[0]);
2357e38180eSChris Mason 	if (sub_item_len == btrfs_item_size(leaf->items + path->slots[0])) {
2367e38180eSChris Mason 		ret = btrfs_del_item(trans, root, path);
2377e38180eSChris Mason 	} else {
2387e38180eSChris Mason 		char *ptr = (char *)di;
2397e38180eSChris Mason 		char *start = btrfs_item_ptr(leaf, path->slots[0], char);
2407e38180eSChris Mason 		btrfs_memmove(root, leaf, ptr, ptr + sub_item_len,
2417e38180eSChris Mason 			item_len - (ptr + sub_item_len - start));
2427e38180eSChris Mason 		ret = btrfs_truncate_item(trans, root, path,
2437e38180eSChris Mason 					  item_len - sub_item_len);
2447e38180eSChris Mason 	}
2457e38180eSChris Mason 	return 0;
2467e38180eSChris Mason }
2477e38180eSChris Mason 
248