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

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

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

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

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

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

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

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

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

	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:
92
	btrfs_free_path(path);
93 94 95
	return ret;
}

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

	key.objectid = dir;
	key.flags = 0;
111
	btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
112 113
	ret = btrfs_name_hash(name, name_len, &key.offset);
	BUG_ON(ret);
114 115 116 117 118 119 120
	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]--;
121
	}
122 123 124 125 126 127 128 129 130
	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);
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 158
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,
159 160
			      struct btrfs_path *path,
			      const char *name, int name_len)
161 162 163
{
	struct btrfs_dir_item *dir_item;
	char *name_ptr;
164 165 166 167
	u32 total_len;
	u32 cur = 0;
	u32 this_len;
	struct btrfs_leaf *leaf;
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
	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;
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 215

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