dir-item.c 5.8 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

7 8 9 10 11
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,
					    u32 data_size)
12 13
{
	int ret;
14 15 16
	char *ptr;
	struct btrfs_item *item;
	struct btrfs_leaf *leaf;
17 18

	ret = btrfs_insert_empty_item(trans, root, path, cpu_key, data_size);
19 20 21 22 23
	if (ret == -EEXIST) {
		ret = btrfs_extend_item(trans, root, path, data_size);
		WARN_ON(ret > 0);
		if (ret)
			return ERR_PTR(ret);
24
	}
25 26 27 28 29 30 31
	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;
32 33
}

34
int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
35 36
			  *root, const char *name, int name_len, u64 dir,
			  struct btrfs_key *location, u8 type)
37 38
{
	int ret = 0;
39
	struct btrfs_path *path;
40 41 42 43 44 45 46
	struct btrfs_dir_item *dir_item;
	char *name_ptr;
	struct btrfs_key key;
	u32 data_size;

	key.objectid = dir;
	key.flags = 0;
47
	btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
C
Chris Mason 已提交
48
	ret = btrfs_name_hash(name, name_len, &key.offset);
49
	BUG_ON(ret);
50 51
	path = btrfs_alloc_path();
	btrfs_init_path(path);
52
	data_size = sizeof(*dir_item) + name_len;
53 54 55
	dir_item = insert_with_overflow(trans, root, path, &key, data_size);
	if (IS_ERR(dir_item)) {
		ret = PTR_ERR(dir_item);
56
		goto out;
57
	}
58

59
	btrfs_cpu_key_to_disk(&dir_item->location, location);
60 61
	btrfs_set_dir_type(dir_item, type);
	btrfs_set_dir_flags(dir_item, 0);
62
	btrfs_set_dir_name_len(dir_item, name_len);
63
	name_ptr = (char *)(dir_item + 1);
C
Chris Mason 已提交
64 65 66

	btrfs_memcpy(root, path->nodes[0]->b_data, name_ptr, name, name_len);
	btrfs_mark_buffer_dirty(path->nodes[0]);
67 68 69 70 71 72 73

	/* FIXME, use some real flag for selecting the extra index */
	if (root == root->fs_info->tree_root) {
		ret = 0;
		goto out;
	}

74
	btrfs_release_path(root, path);
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

	btrfs_set_key_type(&key, BTRFS_DIR_INDEX_KEY);
	key.offset = location->objectid;
	dir_item = insert_with_overflow(trans, root, path, &key, data_size);
	if (IS_ERR(dir_item)) {
		ret = PTR_ERR(dir_item);
		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:
91
	btrfs_free_path(path);
92 93 94
	return ret;
}

95 96 97 98 99
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)
100
{
101
	int ret;
102
	struct btrfs_key key;
103 104
	int ins_len = mod < 0 ? -1 : 0;
	int cow = mod != 0;
105 106
	struct btrfs_disk_key *found_key;
	struct btrfs_leaf *leaf;
107 108 109

	key.objectid = dir;
	key.flags = 0;
110
	btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
111 112
	ret = btrfs_name_hash(name, name_len, &key.offset);
	BUG_ON(ret);
113 114 115 116 117 118 119
	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]--;
120
	}
121 122 123 124 125 126 127 128 129
	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);
130 131
}

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
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,
158 159
			      struct btrfs_path *path,
			      const char *name, int name_len)
160 161 162
{
	struct btrfs_dir_item *dir_item;
	char *name_ptr;
163 164 165 166
	u32 total_len;
	u32 cur = 0;
	u32 this_len;
	struct btrfs_leaf *leaf;
167

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	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;
184
}
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

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;
}