dir-item.c 6.2 KB
Newer Older
1
#include <linux/module.h>
2 3 4
#include "ctree.h"
#include "disk-io.h"
#include "hash.h"
5
#include "transaction.h"
6

C
Chris Mason 已提交
7 8 9 10 11
static struct btrfs_dir_item *insert_with_overflow(struct btrfs_trans_handle
						   *trans,
						   struct btrfs_root *root,
						   struct btrfs_path *path,
						   struct btrfs_key *cpu_key,
C
Chris Mason 已提交
12 13 14
						   u32 data_size,
						   const char *name,
						   int name_len)
15 16
{
	int ret;
17 18 19
	char *ptr;
	struct btrfs_item *item;
	struct btrfs_leaf *leaf;
20 21

	ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size);
22
	if (ret == -EEXIST) {
C
Chris Mason 已提交
23 24 25 26
		struct btrfs_dir_item *di;
		di = btrfs_match_dir_item_name(root, path, name, name_len);
		if (di)
			return ERR_PTR(-EEXIST);
27 28 29 30
		ret = btrfs_extend_item(trans, root, path, data_size);
		WARN_ON(ret > 0);
		if (ret)
			return ERR_PTR(ret);
31
	}
32 33 34 35 36 37 38
	WARN_ON(ret > 0);
	leaf = btrfs_buffer_leaf(path->nodes[0]);
	item = leaf->items + path->slots[0];
	ptr = btrfs_item_ptr(leaf, path->slots[0], char);
	BUG_ON(data_size > btrfs_item_size(item));
	ptr += btrfs_item_size(item) - data_size;
	return (struct btrfs_dir_item *)ptr;
39 40
}

41
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
42 43
			  *root, const char *name, int name_len, u64 dir,
			  struct btrfs_key *location, u8 type)
44 45
{
	int ret = 0;
C
Chris Mason 已提交
46
	int ret2 = 0;
47
	struct btrfs_path *path;
48 49 50 51 52 53 54
	struct btrfs_dir_item *dir_item;
	char *name_ptr;
	struct btrfs_key key;
	u32 data_size;

	key.objectid = dir;
	key.flags = 0;
55
	btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
C
Chris Mason 已提交
56
	ret = btrfs_name_hash(name, name_len, &key.offset);
57
	BUG_ON(ret);
58 59
	path = btrfs_alloc_path();
	btrfs_init_path(path);
60
	data_size = sizeof(*dir_item) + name_len;
C
Chris Mason 已提交
61 62
	dir_item = insert_with_overflow(trans, root, path, &key, data_size,
					name, name_len);
63 64
	if (IS_ERR(dir_item)) {
		ret = PTR_ERR(dir_item);
C
Chris Mason 已提交
65 66
		if (ret == -EEXIST)
			goto second_insert;
67
		goto out;
68
	}
69

70
	btrfs_cpu_key_to_disk(&dir_item->location, location);
71 72
	btrfs_set_dir_type(dir_item, type);
	btrfs_set_dir_flags(dir_item, 0);
73
	btrfs_set_dir_name_len(dir_item, name_len);
74
	name_ptr = (char *)(dir_item + 1);
C
Chris Mason 已提交
75 76 77

	btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
	btrfs_mark_buffer_dirty(path->nodes[0]);
78

C
Chris Mason 已提交
79
second_insert:
80 81 82 83 84
	/* FIXME, use some real flag for selecting the extra index */
	if (root == root->fs_info->tree_root) {
		ret = 0;
		goto out;
	}
85
	btrfs_release_path(root, path);
86 87 88

	btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
	key.offset = location->objectid;
C
Chris Mason 已提交
89 90
	dir_item = insert_with_overflow(trans, root, path, &key, data_size,
					name, name_len);
91
	if (IS_ERR(dir_item)) {
C
Chris Mason 已提交
92
		ret2 = PTR_ERR(dir_item);
93 94 95 96 97 98 99 100 101 102
		goto out;
	}
	btrfs_cpu_key_to_disk(&dir_item->location, location);
	btrfs_set_dir_type(dir_item, type);
	btrfs_set_dir_flags(dir_item, 0);
	btrfs_set_dir_name_len(dir_item, name_len);
	name_ptr = (char *)(dir_item + 1);
	btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
	btrfs_mark_buffer_dirty(path->nodes[0]);
out:
103
	btrfs_free_path(path);
C
Chris Mason 已提交
104 105 106 107 108
	if (ret)
		return ret;
	if (ret2)
		return ret2;
	return 0;
109 110
}

111 112 113 114 115
struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
					     struct btrfs_root *root,
					     struct btrfs_path *path, u64 dir,
					     const char *name, int name_len,
					     int mod)
116
{
117
	int ret;
118
	struct btrfs_key key;
119 120
	int ins_len = mod < 0 ? -1 : 0;
	int cow = mod != 0;
121 122
	struct btrfs_disk_key *found_key;
	struct btrfs_leaf *leaf;
123 124 125

	key.objectid = dir;
	key.flags = 0;
126
	btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
127 128
	ret = btrfs_name_hash(name, name_len, &key.offset);
	BUG_ON(ret);
129 130 131 132 133 134 135
	ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
	if (ret < 0)
		return ERR_PTR(ret);
	if (ret > 0) {
		if (path->slots[0] == 0)
			return NULL;
		path->slots[0]--;
136
	}
137 138 139 140 141 142 143 144 145
	leaf = btrfs_buffer_leaf(path->nodes[0]);
	found_key = &leaf->items[path->slots[0]].key;

	if (btrfs_disk_key_objectid(found_key) != dir ||
	    btrfs_disk_key_type(found_key) != BTRFS_DIR_ITEM_KEY ||
	    btrfs_disk_key_offset(found_key) != key.offset)
		return NULL;

	return btrfs_match_dir_item_name(root, path, name, name_len);
146 147
}

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
struct btrfs_dir_item *
btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
			    struct btrfs_root *root,
			    struct btrfs_path *path, u64 dir,
			    u64 objectid, const char *name, int name_len,
			    int mod)
{
	int ret;
	struct btrfs_key key;
	int ins_len = mod < 0 ? -1 : 0;
	int cow = mod != 0;

	key.objectid = dir;
	key.flags = 0;
	btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
	key.offset = objectid;

	ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
	if (ret < 0)
		return ERR_PTR(ret);
	if (ret > 0)
		return ERR_PTR(-ENOENT);
	return btrfs_match_dir_item_name(root, path, name, name_len);
}

struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
174 175
			      struct btrfs_path *path,
			      const char *name, int name_len)
176 177 178
{
	struct btrfs_dir_item *dir_item;
	char *name_ptr;
179 180 181 182
	u32 total_len;
	u32 cur = 0;
	u32 this_len;
	struct btrfs_leaf *leaf;
183

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
	leaf = btrfs_buffer_leaf(path->nodes[0]);
	dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
	total_len = btrfs_item_size(leaf->items + path->slots[0]);
	while(cur < total_len) {
		this_len = sizeof(*dir_item) + btrfs_dir_name_len(dir_item);
		name_ptr = (char *)(dir_item + 1);

		if (btrfs_dir_name_len(dir_item) == name_len &&
		    memcmp(name_ptr, name, name_len) == 0)
			return dir_item;

		cur += this_len;
		dir_item = (struct btrfs_dir_item *)((char *)dir_item +
						     this_len);
	}
	return NULL;
200
}
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
			      struct btrfs_root *root,
			      struct btrfs_path *path,
			      struct btrfs_dir_item *di)
{

	struct btrfs_leaf *leaf;
	u32 sub_item_len;
	u32 item_len;
	int ret;

	leaf = btrfs_buffer_leaf(path->nodes[0]);
	sub_item_len = sizeof(*di) + btrfs_dir_name_len(di);
	item_len = btrfs_item_size(leaf->items + path->slots[0]);
	if (sub_item_len == btrfs_item_size(leaf->items + path->slots[0])) {
		ret = btrfs_del_item(trans, root, path);
		BUG_ON(ret);
	} else {
		char *ptr = (char *)di;
		char *start = btrfs_item_ptr(leaf, path->slots[0], char);
		btrfs_memmove(root, leaf, ptr, ptr + sub_item_len,
			item_len - (ptr + sub_item_len - start));
		ret = btrfs_truncate_item(trans, root, path,
					  item_len - sub_item_len);
		BUG_ON(ret);
	}
	return 0;
}