inode-item.c 11.7 KB
Newer Older
C
Chris Mason 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright (C) 2007 Oracle.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License v2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 021110-1307, USA.
 */

C
Chris Mason 已提交
19 20
#include "ctree.h"
#include "disk-io.h"
M
Mark Fasheh 已提交
21
#include "hash.h"
22
#include "transaction.h"
23
#include "print-tree.h"
C
Chris Mason 已提交
24

25
static int find_name_in_backref(struct btrfs_path *path, const char *name,
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
			 int name_len, struct btrfs_inode_ref **ref_ret)
{
	struct extent_buffer *leaf;
	struct btrfs_inode_ref *ref;
	unsigned long ptr;
	unsigned long name_ptr;
	u32 item_size;
	u32 cur_offset = 0;
	int len;

	leaf = path->nodes[0];
	item_size = btrfs_item_size_nr(leaf, path->slots[0]);
	ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
	while (cur_offset < item_size) {
		ref = (struct btrfs_inode_ref *)(ptr + cur_offset);
		len = btrfs_inode_ref_name_len(leaf, ref);
		name_ptr = (unsigned long)(ref + 1);
		cur_offset += len + sizeof(*ref);
		if (len != name_len)
			continue;
		if (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0) {
			*ref_ret = ref;
			return 1;
		}
	}
	return 0;
}

M
Mark Fasheh 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
int btrfs_find_name_in_ext_backref(struct btrfs_path *path, u64 ref_objectid,
				   const char *name, int name_len,
				   struct btrfs_inode_extref **extref_ret)
{
	struct extent_buffer *leaf;
	struct btrfs_inode_extref *extref;
	unsigned long ptr;
	unsigned long name_ptr;
	u32 item_size;
	u32 cur_offset = 0;
	int ref_name_len;

	leaf = path->nodes[0];
	item_size = btrfs_item_size_nr(leaf, path->slots[0]);
	ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);

	/*
	 * Search all extended backrefs in this item. We're only
	 * looking through any collisions so most of the time this is
	 * just going to compare against one buffer. If all is well,
	 * we'll return success and the inode ref object.
	 */
	while (cur_offset < item_size) {
		extref = (struct btrfs_inode_extref *) (ptr + cur_offset);
		name_ptr = (unsigned long)(&extref->name);
		ref_name_len = btrfs_inode_extref_name_len(leaf, extref);

		if (ref_name_len == name_len &&
		    btrfs_inode_extref_parent(leaf, extref) == ref_objectid &&
		    (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)) {
			if (extref_ret)
				*extref_ret = extref;
			return 1;
		}

		cur_offset += ref_name_len + sizeof(*extref);
	}
	return 0;
}

/* Returns NULL if no extref found */
struct btrfs_inode_extref *
btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans,
			  struct btrfs_root *root,
			  struct btrfs_path *path,
			  const char *name, int name_len,
			  u64 inode_objectid, u64 ref_objectid, int ins_len,
			  int cow)
{
	int ret;
	struct btrfs_key key;
	struct btrfs_inode_extref *extref;

	key.objectid = inode_objectid;
	key.type = BTRFS_INODE_EXTREF_KEY;
	key.offset = btrfs_extref_hash(ref_objectid, name, name_len);

	ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
	if (ret < 0)
		return ERR_PTR(ret);
	if (ret > 0)
		return NULL;
	if (!btrfs_find_name_in_ext_backref(path, ref_objectid, name, name_len, &extref))
		return NULL;
	return extref;
}

121 122 123 124 125
static int btrfs_del_inode_extref(struct btrfs_trans_handle *trans,
				  struct btrfs_root *root,
				  const char *name, int name_len,
				  u64 inode_objectid, u64 ref_objectid,
				  u64 *index)
M
Mark Fasheh 已提交
126 127 128 129 130 131 132 133 134 135 136 137
{
	struct btrfs_path *path;
	struct btrfs_key key;
	struct btrfs_inode_extref *extref;
	struct extent_buffer *leaf;
	int ret;
	int del_len = name_len + sizeof(*extref);
	unsigned long ptr;
	unsigned long item_start;
	u32 item_size;

	key.objectid = inode_objectid;
138
	key.type = BTRFS_INODE_EXTREF_KEY;
M
Mark Fasheh 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
	key.offset = btrfs_extref_hash(ref_objectid, name, name_len);

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	path->leave_spinning = 1;

	ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
	if (ret > 0)
		ret = -ENOENT;
	if (ret < 0)
		goto out;

	/*
	 * Sanity check - did we find the right item for this name?
	 * This should always succeed so error here will make the FS
	 * readonly.
	 */
	if (!btrfs_find_name_in_ext_backref(path, ref_objectid,
					    name, name_len, &extref)) {
160
		btrfs_handle_fs_error(root->fs_info, -ENOENT, NULL);
M
Mark Fasheh 已提交
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
		ret = -EROFS;
		goto out;
	}

	leaf = path->nodes[0];
	item_size = btrfs_item_size_nr(leaf, path->slots[0]);
	if (index)
		*index = btrfs_inode_extref_index(leaf, extref);

	if (del_len == item_size) {
		/*
		 * Common case only one ref in the item, remove the
		 * whole item.
		 */
		ret = btrfs_del_item(trans, root, path);
		goto out;
	}

	ptr = (unsigned long)extref;
	item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);

	memmove_extent_buffer(leaf, ptr, ptr + del_len,
			      item_size - (ptr + del_len - item_start));

185
	btrfs_truncate_item(root, path, item_size - del_len, 1);
M
Mark Fasheh 已提交
186 187 188 189 190 191 192 193 194 195 196

out:
	btrfs_free_path(path);

	return ret;
}

int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
			struct btrfs_root *root,
			const char *name, int name_len,
			u64 inode_objectid, u64 ref_objectid, u64 *index)
197 198 199 200 201 202 203 204 205 206
{
	struct btrfs_path *path;
	struct btrfs_key key;
	struct btrfs_inode_ref *ref;
	struct extent_buffer *leaf;
	unsigned long ptr;
	unsigned long item_start;
	u32 item_size;
	u32 sub_item_len;
	int ret;
M
Mark Fasheh 已提交
207
	int search_ext_refs = 0;
208 209 210 211
	int del_len = name_len + sizeof(*ref);

	key.objectid = inode_objectid;
	key.offset = ref_objectid;
212
	key.type = BTRFS_INODE_REF_KEY;
213 214 215 216 217

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

218 219
	path->leave_spinning = 1;

220 221 222
	ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
	if (ret > 0) {
		ret = -ENOENT;
M
Mark Fasheh 已提交
223
		search_ext_refs = 1;
224 225 226 227 228 229
		goto out;
	} else if (ret < 0) {
		goto out;
	}
	if (!find_name_in_backref(path, name, name_len, &ref)) {
		ret = -ENOENT;
M
Mark Fasheh 已提交
230
		search_ext_refs = 1;
231 232 233 234
		goto out;
	}
	leaf = path->nodes[0];
	item_size = btrfs_item_size_nr(leaf, path->slots[0]);
235 236 237 238

	if (index)
		*index = btrfs_inode_ref_index(leaf, ref);

239 240 241 242 243 244 245 246 247
	if (del_len == item_size) {
		ret = btrfs_del_item(trans, root, path);
		goto out;
	}
	ptr = (unsigned long)ref;
	sub_item_len = name_len + sizeof(*ref);
	item_start = btrfs_item_ptr_offset(leaf, path->slots[0]);
	memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
			      item_size - (ptr + sub_item_len - item_start));
248
	btrfs_truncate_item(root, path, item_size - sub_item_len, 1);
M
Mark Fasheh 已提交
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
out:
	btrfs_free_path(path);

	if (search_ext_refs) {
		/*
		 * No refs were found, or we could not find the
		 * name in our ref array. Find and remove the extended
		 * inode ref then.
		 */
		return btrfs_del_inode_extref(trans, root, name, name_len,
					      inode_objectid, ref_objectid, index);
	}

	return ret;
}

/*
 * btrfs_insert_inode_extref() - Inserts an extended inode ref into a tree.
 *
 * The caller must have checked against BTRFS_LINK_MAX already.
 */
static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans,
				     struct btrfs_root *root,
				     const char *name, int name_len,
				     u64 inode_objectid, u64 ref_objectid, u64 index)
{
	struct btrfs_inode_extref *extref;
	int ret;
	int ins_len = name_len + sizeof(*extref);
	unsigned long ptr;
	struct btrfs_path *path;
	struct btrfs_key key;
	struct extent_buffer *leaf;
	struct btrfs_item *item;

	key.objectid = inode_objectid;
	key.type = BTRFS_INODE_EXTREF_KEY;
	key.offset = btrfs_extref_hash(ref_objectid, name, name_len);

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	path->leave_spinning = 1;
	ret = btrfs_insert_empty_item(trans, root, path, &key,
				      ins_len);
	if (ret == -EEXIST) {
		if (btrfs_find_name_in_ext_backref(path, ref_objectid,
						   name, name_len, NULL))
			goto out;

300
		btrfs_extend_item(root, path, ins_len);
M
Mark Fasheh 已提交
301 302 303 304 305 306
		ret = 0;
	}
	if (ret < 0)
		goto out;

	leaf = path->nodes[0];
307
	item = btrfs_item_nr(path->slots[0]);
M
Mark Fasheh 已提交
308 309 310 311 312 313 314 315 316 317 318 319
	ptr = (unsigned long)btrfs_item_ptr(leaf, path->slots[0], char);
	ptr += btrfs_item_size(leaf, item) - ins_len;
	extref = (struct btrfs_inode_extref *)ptr;

	btrfs_set_inode_extref_name_len(path->nodes[0], extref, name_len);
	btrfs_set_inode_extref_index(path->nodes[0], extref, index);
	btrfs_set_inode_extref_parent(path->nodes[0], extref, ref_objectid);

	ptr = (unsigned long)&extref->name;
	write_extent_buffer(path->nodes[0], name, ptr, name_len);
	btrfs_mark_buffer_dirty(path->nodes[0]);

320 321 322 323 324
out:
	btrfs_free_path(path);
	return ret;
}

325
/* Will return 0, -ENOMEM, -EMLINK, or -EEXIST or anything from the CoW path */
326 327 328
int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
			   struct btrfs_root *root,
			   const char *name, int name_len,
329
			   u64 inode_objectid, u64 ref_objectid, u64 index)
330 331 332 333 334 335 336 337 338 339
{
	struct btrfs_path *path;
	struct btrfs_key key;
	struct btrfs_inode_ref *ref;
	unsigned long ptr;
	int ret;
	int ins_len = name_len + sizeof(*ref);

	key.objectid = inode_objectid;
	key.offset = ref_objectid;
340
	key.type = BTRFS_INODE_REF_KEY;
341 342 343 344 345

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

346
	path->leave_spinning = 1;
347
	path->skip_release_on_error = 1;
348 349 350 351 352 353 354 355 356
	ret = btrfs_insert_empty_item(trans, root, path, &key,
				      ins_len);
	if (ret == -EEXIST) {
		u32 old_size;

		if (find_name_in_backref(path, name, name_len, &ref))
			goto out;

		old_size = btrfs_item_size_nr(path->nodes[0], path->slots[0]);
357
		btrfs_extend_item(root, path, ins_len);
358 359 360 361
		ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
				     struct btrfs_inode_ref);
		ref = (struct btrfs_inode_ref *)((unsigned long)ref + old_size);
		btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
362
		btrfs_set_inode_ref_index(path->nodes[0], ref, index);
363 364 365
		ptr = (unsigned long)(ref + 1);
		ret = 0;
	} else if (ret < 0) {
366 367 368 369 370 371
		if (ret == -EOVERFLOW) {
			if (find_name_in_backref(path, name, name_len, &ref))
				ret = -EEXIST;
			else
				ret = -EMLINK;
		}
372 373 374 375 376
		goto out;
	} else {
		ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
				     struct btrfs_inode_ref);
		btrfs_set_inode_ref_name_len(path->nodes[0], ref, name_len);
377
		btrfs_set_inode_ref_index(path->nodes[0], ref, index);
378 379 380 381 382 383 384
		ptr = (unsigned long)(ref + 1);
	}
	write_extent_buffer(path->nodes[0], name, ptr, name_len);
	btrfs_mark_buffer_dirty(path->nodes[0]);

out:
	btrfs_free_path(path);
M
Mark Fasheh 已提交
385 386 387 388 389 390 391 392 393 394 395 396 397

	if (ret == -EMLINK) {
		struct btrfs_super_block *disk_super = root->fs_info->super_copy;
		/* We ran out of space in the ref array. Need to
		 * add an extended ref. */
		if (btrfs_super_incompat_flags(disk_super)
		    & BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF)
			ret = btrfs_insert_inode_extref(trans, root, name,
							name_len,
							inode_objectid,
							ref_objectid, index);
	}

398 399 400
	return ret;
}

401 402 403
int btrfs_insert_empty_inode(struct btrfs_trans_handle *trans,
			     struct btrfs_root *root,
			     struct btrfs_path *path, u64 objectid)
C
Chris Mason 已提交
404 405 406 407
{
	struct btrfs_key key;
	int ret;
	key.objectid = objectid;
408
	key.type = BTRFS_INODE_ITEM_KEY;
C
Chris Mason 已提交
409 410
	key.offset = 0;

411 412
	ret = btrfs_insert_empty_item(trans, root, path, &key,
				      sizeof(struct btrfs_inode_item));
C
Chris Mason 已提交
413 414 415
	return ret;
}

416
int btrfs_lookup_inode(struct btrfs_trans_handle *trans, struct btrfs_root
417 418
		       *root, struct btrfs_path *path,
		       struct btrfs_key *location, int mod)
C
Chris Mason 已提交
419 420 421
{
	int ins_len = mod < 0 ? -1 : 0;
	int cow = mod != 0;
422 423
	int ret;
	int slot;
424
	struct extent_buffer *leaf;
425
	struct btrfs_key found_key;
C
Chris Mason 已提交
426

427
	ret = btrfs_search_slot(trans, root, location, path, ins_len, cow);
428
	if (ret > 0 && location->type == BTRFS_ROOT_ITEM_KEY &&
429 430
	    location->offset == (u64)-1 && path->slots[0] != 0) {
		slot = path->slots[0] - 1;
431 432
		leaf = path->nodes[0];
		btrfs_item_key_to_cpu(leaf, &found_key, slot);
433
		if (found_key.objectid == location->objectid &&
434
		    found_key.type == location->type) {
435 436 437 438 439
			path->slots[0]--;
			return 0;
		}
	}
	return ret;
C
Chris Mason 已提交
440
}