xfs_attr_leaf.c 81.6 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
Linus Torvalds's avatar
Linus Torvalds committed
2
/*
3
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
4
 * Copyright (c) 2013 Red Hat, Inc.
5
 * All Rights Reserved.
Linus Torvalds's avatar
Linus Torvalds committed
6 7
 */
#include "xfs.h"
8
#include "xfs_fs.h"
9
#include "xfs_shared.h"
10
#include "xfs_format.h"
11 12
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
Linus Torvalds's avatar
Linus Torvalds committed
13 14
#include "xfs_sb.h"
#include "xfs_mount.h"
15
#include "xfs_da_format.h"
16
#include "xfs_da_btree.h"
Linus Torvalds's avatar
Linus Torvalds committed
17
#include "xfs_inode.h"
18
#include "xfs_trans.h"
19
#include "xfs_bmap_btree.h"
Linus Torvalds's avatar
Linus Torvalds committed
20
#include "xfs_bmap.h"
21 22
#include "xfs_attr_sf.h"
#include "xfs_attr_remote.h"
Linus Torvalds's avatar
Linus Torvalds committed
23 24 25
#include "xfs_attr.h"
#include "xfs_attr_leaf.h"
#include "xfs_error.h"
26
#include "xfs_trace.h"
27
#include "xfs_buf_item.h"
28
#include "xfs_dir2.h"
29
#include "xfs_log.h"
30

Linus Torvalds's avatar
Linus Torvalds committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44

/*
 * xfs_attr_leaf.c
 *
 * Routines to implement leaf blocks of attributes as Btrees of hashed names.
 */

/*========================================================================
 * Function prototypes for the kernel.
 *========================================================================*/

/*
 * Routines used for growing the Btree.
 */
45 46 47 48 49 50 51 52 53
STATIC int xfs_attr3_leaf_create(struct xfs_da_args *args,
				 xfs_dablk_t which_block, struct xfs_buf **bpp);
STATIC int xfs_attr3_leaf_add_work(struct xfs_buf *leaf_buffer,
				   struct xfs_attr3_icleaf_hdr *ichdr,
				   struct xfs_da_args *args, int freemap_index);
STATIC void xfs_attr3_leaf_compact(struct xfs_da_args *args,
				   struct xfs_attr3_icleaf_hdr *ichdr,
				   struct xfs_buf *leaf_buffer);
STATIC void xfs_attr3_leaf_rebalance(xfs_da_state_t *state,
Linus Torvalds's avatar
Linus Torvalds committed
54 55
						   xfs_da_state_blk_t *blk1,
						   xfs_da_state_blk_t *blk2);
56 57 58 59 60 61 62
STATIC int xfs_attr3_leaf_figure_balance(xfs_da_state_t *state,
			xfs_da_state_blk_t *leaf_blk_1,
			struct xfs_attr3_icleaf_hdr *ichdr1,
			xfs_da_state_blk_t *leaf_blk_2,
			struct xfs_attr3_icleaf_hdr *ichdr2,
			int *number_entries_in_blk1,
			int *number_usedbytes_in_blk1);
Linus Torvalds's avatar
Linus Torvalds committed
63 64 65 66

/*
 * Utility routines.
 */
67 68
STATIC void xfs_attr3_leaf_moveents(struct xfs_da_args *args,
			struct xfs_attr_leafblock *src_leaf,
69 70 71
			struct xfs_attr3_icleaf_hdr *src_ichdr, int src_start,
			struct xfs_attr_leafblock *dst_leaf,
			struct xfs_attr3_icleaf_hdr *dst_ichdr, int dst_start,
72
			int move_count);
73
STATIC int xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index);
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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
/*
 * attr3 block 'firstused' conversion helpers.
 *
 * firstused refers to the offset of the first used byte of the nameval region
 * of an attr leaf block. The region starts at the tail of the block and expands
 * backwards towards the middle. As such, firstused is initialized to the block
 * size for an empty leaf block and is reduced from there.
 *
 * The attr3 block size is pegged to the fsb size and the maximum fsb is 64k.
 * The in-core firstused field is 32-bit and thus supports the maximum fsb size.
 * The on-disk field is only 16-bit, however, and overflows at 64k. Since this
 * only occurs at exactly 64k, we use zero as a magic on-disk value to represent
 * the attr block size. The following helpers manage the conversion between the
 * in-core and on-disk formats.
 */

static void
xfs_attr3_leaf_firstused_from_disk(
	struct xfs_da_geometry		*geo,
	struct xfs_attr3_icleaf_hdr	*to,
	struct xfs_attr_leafblock	*from)
{
	struct xfs_attr3_leaf_hdr	*hdr3;

	if (from->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)) {
		hdr3 = (struct xfs_attr3_leaf_hdr *) from;
		to->firstused = be16_to_cpu(hdr3->firstused);
	} else {
		to->firstused = be16_to_cpu(from->hdr.firstused);
	}

	/*
	 * Convert from the magic fsb size value to actual blocksize. This
	 * should only occur for empty blocks when the block size overflows
	 * 16-bits.
	 */
	if (to->firstused == XFS_ATTR3_LEAF_NULLOFF) {
		ASSERT(!to->count && !to->usedbytes);
		ASSERT(geo->blksize > USHRT_MAX);
		to->firstused = geo->blksize;
	}
}

static void
xfs_attr3_leaf_firstused_to_disk(
	struct xfs_da_geometry		*geo,
	struct xfs_attr_leafblock	*to,
	struct xfs_attr3_icleaf_hdr	*from)
{
	struct xfs_attr3_leaf_hdr	*hdr3;
	uint32_t			firstused;

	/* magic value should only be seen on disk */
	ASSERT(from->firstused != XFS_ATTR3_LEAF_NULLOFF);

	/*
	 * Scale down the 32-bit in-core firstused value to the 16-bit on-disk
	 * value. This only overflows at the max supported value of 64k. Use the
	 * magic on-disk value to represent block size in this case.
	 */
	firstused = from->firstused;
	if (firstused > USHRT_MAX) {
		ASSERT(from->firstused == geo->blksize);
		firstused = XFS_ATTR3_LEAF_NULLOFF;
	}

	if (from->magic == XFS_ATTR3_LEAF_MAGIC) {
		hdr3 = (struct xfs_attr3_leaf_hdr *) to;
		hdr3->firstused = cpu_to_be16(firstused);
	} else {
		to->hdr.firstused = cpu_to_be16(firstused);
	}
}

149 150
void
xfs_attr3_leaf_hdr_from_disk(
151
	struct xfs_da_geometry		*geo,
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
	struct xfs_attr3_icleaf_hdr	*to,
	struct xfs_attr_leafblock	*from)
{
	int	i;

	ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC) ||
	       from->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC));

	if (from->hdr.info.magic == cpu_to_be16(XFS_ATTR3_LEAF_MAGIC)) {
		struct xfs_attr3_leaf_hdr *hdr3 = (struct xfs_attr3_leaf_hdr *)from;

		to->forw = be32_to_cpu(hdr3->info.hdr.forw);
		to->back = be32_to_cpu(hdr3->info.hdr.back);
		to->magic = be16_to_cpu(hdr3->info.hdr.magic);
		to->count = be16_to_cpu(hdr3->count);
		to->usedbytes = be16_to_cpu(hdr3->usedbytes);
168
		xfs_attr3_leaf_firstused_from_disk(geo, to, from);
169 170 171 172 173 174 175 176 177 178 179 180 181
		to->holes = hdr3->holes;

		for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
			to->freemap[i].base = be16_to_cpu(hdr3->freemap[i].base);
			to->freemap[i].size = be16_to_cpu(hdr3->freemap[i].size);
		}
		return;
	}
	to->forw = be32_to_cpu(from->hdr.info.forw);
	to->back = be32_to_cpu(from->hdr.info.back);
	to->magic = be16_to_cpu(from->hdr.info.magic);
	to->count = be16_to_cpu(from->hdr.count);
	to->usedbytes = be16_to_cpu(from->hdr.usedbytes);
182
	xfs_attr3_leaf_firstused_from_disk(geo, to, from);
183 184 185 186 187 188 189 190 191 192
	to->holes = from->hdr.holes;

	for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
		to->freemap[i].base = be16_to_cpu(from->hdr.freemap[i].base);
		to->freemap[i].size = be16_to_cpu(from->hdr.freemap[i].size);
	}
}

void
xfs_attr3_leaf_hdr_to_disk(
193
	struct xfs_da_geometry		*geo,
194 195 196
	struct xfs_attr_leafblock	*to,
	struct xfs_attr3_icleaf_hdr	*from)
{
197
	int				i;
198 199 200 201 202 203 204 205 206 207 208 209

	ASSERT(from->magic == XFS_ATTR_LEAF_MAGIC ||
	       from->magic == XFS_ATTR3_LEAF_MAGIC);

	if (from->magic == XFS_ATTR3_LEAF_MAGIC) {
		struct xfs_attr3_leaf_hdr *hdr3 = (struct xfs_attr3_leaf_hdr *)to;

		hdr3->info.hdr.forw = cpu_to_be32(from->forw);
		hdr3->info.hdr.back = cpu_to_be32(from->back);
		hdr3->info.hdr.magic = cpu_to_be16(from->magic);
		hdr3->count = cpu_to_be16(from->count);
		hdr3->usedbytes = cpu_to_be16(from->usedbytes);
210
		xfs_attr3_leaf_firstused_to_disk(geo, to, from);
211 212 213 214 215 216 217 218 219 220 221 222 223 224
		hdr3->holes = from->holes;
		hdr3->pad1 = 0;

		for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
			hdr3->freemap[i].base = cpu_to_be16(from->freemap[i].base);
			hdr3->freemap[i].size = cpu_to_be16(from->freemap[i].size);
		}
		return;
	}
	to->hdr.info.forw = cpu_to_be32(from->forw);
	to->hdr.info.back = cpu_to_be32(from->back);
	to->hdr.info.magic = cpu_to_be16(from->magic);
	to->hdr.count = cpu_to_be16(from->count);
	to->hdr.usedbytes = cpu_to_be16(from->usedbytes);
225
	xfs_attr3_leaf_firstused_to_disk(geo, to, from);
226 227 228 229 230 231 232 233 234
	to->hdr.holes = from->holes;
	to->hdr.pad1 = 0;

	for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
		to->hdr.freemap[i].base = cpu_to_be16(from->freemap[i].base);
		to->hdr.freemap[i].size = cpu_to_be16(from->freemap[i].size);
	}
}

235
static xfs_failaddr_t
236
xfs_attr3_leaf_verify(
237
	struct xfs_buf			*bp)
238
{
239
	struct xfs_attr3_icleaf_hdr	ichdr;
240
	struct xfs_mount		*mp = bp->b_mount;
241 242
	struct xfs_attr_leafblock	*leaf = bp->b_addr;
	struct xfs_attr_leaf_entry	*entries;
243
	uint32_t			end;	/* must be 32bit - see below */
244
	int				i;
245
	xfs_failaddr_t			fa;
246

247
	xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);
248

249 250 251
	fa = xfs_da3_blkinfo_verify(bp, bp->b_addr);
	if (fa)
		return fa;
252

253 254 255 256 257
	/*
	 * In recovery there is a transient state where count == 0 is valid
	 * because we may have transitioned an empty shortform attr to a leaf
	 * if the attr didn't fit in shortform.
	 */
258
	if (!xfs_log_in_recovery(mp) && ichdr.count == 0)
259
		return __this_address;
260

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
	/*
	 * firstused is the block offset of the first name info structure.
	 * Make sure it doesn't go off the block or crash into the header.
	 */
	if (ichdr.firstused > mp->m_attr_geo->blksize)
		return __this_address;
	if (ichdr.firstused < xfs_attr3_leaf_hdr_size(leaf))
		return __this_address;

	/* Make sure the entries array doesn't crash into the name info. */
	entries = xfs_attr3_leaf_entryp(bp->b_addr);
	if ((char *)&entries[ichdr.count] >
	    (char *)bp->b_addr + ichdr.firstused)
		return __this_address;

276 277 278
	/* XXX: need to range check rest of attr header values */
	/* XXX: hash order check? */

279 280 281
	/*
	 * Quickly check the freemap information.  Attribute data has to be
	 * aligned to 4-byte boundaries, and likewise for the free space.
282 283 284 285 286
	 *
	 * Note that for 64k block size filesystems, the freemap entries cannot
	 * overflow as they are only be16 fields. However, when checking end
	 * pointer of the freemap, we have to be careful to detect overflows and
	 * so use uint32_t for those checks.
287 288 289 290 291 292 293 294 295 296
	 */
	for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
		if (ichdr.freemap[i].base > mp->m_attr_geo->blksize)
			return __this_address;
		if (ichdr.freemap[i].base & 0x3)
			return __this_address;
		if (ichdr.freemap[i].size > mp->m_attr_geo->blksize)
			return __this_address;
		if (ichdr.freemap[i].size & 0x3)
			return __this_address;
297 298 299

		/* be care of 16 bit overflows here */
		end = (uint32_t)ichdr.freemap[i].base + ichdr.freemap[i].size;
300 301 302 303 304 305
		if (end < ichdr.freemap[i].base)
			return __this_address;
		if (end > mp->m_attr_geo->blksize)
			return __this_address;
	}

306
	return NULL;
307 308 309
}

static void
310
xfs_attr3_leaf_write_verify(
311 312
	struct xfs_buf	*bp)
{
313
	struct xfs_mount	*mp = bp->b_mount;
314
	struct xfs_buf_log_item	*bip = bp->b_log_item;
315
	struct xfs_attr3_leaf_hdr *hdr3 = bp->b_addr;
316
	xfs_failaddr_t		fa;
317

318 319 320
	fa = xfs_attr3_leaf_verify(bp);
	if (fa) {
		xfs_verifier_error(bp, -EFSCORRUPTED, fa);
321 322 323 324 325 326 327 328 329
		return;
	}

	if (!xfs_sb_version_hascrc(&mp->m_sb))
		return;

	if (bip)
		hdr3->info.lsn = cpu_to_be64(bip->bli_item.li_lsn);

330
	xfs_buf_update_cksum(bp, XFS_ATTR3_LEAF_CRC_OFF);
331
}
332

333 334 335 336 337 338
/*
 * leaf/node format detection on trees is sketchy, so a node read can be done on
 * leaf level blocks when detection identifies the tree as a node format tree
 * incorrectly. In this case, we need to swap the verifier to match the correct
 * format of the block being read.
 */
339
static void
340 341
xfs_attr3_leaf_read_verify(
	struct xfs_buf		*bp)
342
{
343
	struct xfs_mount	*mp = bp->b_mount;
344
	xfs_failaddr_t		fa;
345

346 347
	if (xfs_sb_version_hascrc(&mp->m_sb) &&
	     !xfs_buf_verify_cksum(bp, XFS_ATTR3_LEAF_CRC_OFF))
348 349 350 351 352 353
		xfs_verifier_error(bp, -EFSBADCRC, __this_address);
	else {
		fa = xfs_attr3_leaf_verify(bp);
		if (fa)
			xfs_verifier_error(bp, -EFSCORRUPTED, fa);
	}
354 355
}

356
const struct xfs_buf_ops xfs_attr3_leaf_buf_ops = {
357
	.name = "xfs_attr3_leaf",
358 359
	.magic16 = { cpu_to_be16(XFS_ATTR_LEAF_MAGIC),
		     cpu_to_be16(XFS_ATTR3_LEAF_MAGIC) },
360 361
	.verify_read = xfs_attr3_leaf_read_verify,
	.verify_write = xfs_attr3_leaf_write_verify,
362
	.verify_struct = xfs_attr3_leaf_verify,
363
};
364

365
int
366
xfs_attr3_leaf_read(
367 368 369 370 371 372
	struct xfs_trans	*tp,
	struct xfs_inode	*dp,
	xfs_dablk_t		bno,
	xfs_daddr_t		mappedbno,
	struct xfs_buf		**bpp)
{
373 374 375
	int			err;

	err = xfs_da_read_buf(tp, dp, bno, mappedbno, bpp,
376
				XFS_ATTR_FORK, &xfs_attr3_leaf_buf_ops);
377
	if (!err && tp && *bpp)
378
		xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_ATTR_LEAF_BUF);
379
	return err;
380 381
}

382 383 384 385 386 387 388 389
/*========================================================================
 * Namespace helper routines
 *========================================================================*/

/*
 * If namespace bits don't match return 0.
 * If all match then return 1.
 */
390
STATIC int
391 392 393 394 395
xfs_attr_namesp_match(int arg_flags, int ondisk_flags)
{
	return XFS_ATTR_NSP_ONDISK(ondisk_flags) == XFS_ATTR_NSP_ARGS_TO_ONDISK(arg_flags);
}

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
static int
xfs_attr_copy_value(
	struct xfs_da_args	*args,
	unsigned char		*value,
	int			valuelen)
{
	/*
	 * No copy if all we have to do is get the length
	 */
	if (args->flags & ATTR_KERNOVAL) {
		args->valuelen = valuelen;
		return 0;
	}

	/*
	 * No copy if the length of the existing buffer is too small
	 */
	if (args->valuelen < valuelen) {
		args->valuelen = valuelen;
		return -ERANGE;
	}
417 418 419 420 421 422

	if (args->op_flags & XFS_DA_OP_ALLOCVAL) {
		args->value = kmem_alloc_large(valuelen, 0);
		if (!args->value)
			return -ENOMEM;
	}
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
	args->valuelen = valuelen;

	/* remote block xattr requires IO for copy-in */
	if (args->rmtblkno)
		return xfs_attr_rmtval_get(args);

	/*
	 * This is to prevent a GCC warning because the remote xattr case
	 * doesn't have a value to pass in. In that case, we never reach here,
	 * but GCC can't work that out and so throws a "passing NULL to
	 * memcpy" warning.
	 */
	if (!value)
		return -EINVAL;
	memcpy(args->value, value, valuelen);
	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
440 441

/*========================================================================
442
 * External routines when attribute fork size < XFS_LITINO(mp).
Linus Torvalds's avatar
Linus Torvalds committed
443 444 445
 *========================================================================*/

/*
446 447
 * Query whether the requested number of additional bytes of extended
 * attribute space will be able to fit inline.
448
 *
449 450 451 452 453
 * Returns zero if not, else the di_forkoff fork offset to be used in the
 * literal area for attribute data once the new bytes have been added.
 *
 * di_forkoff must be 8 byte aligned, hence is stored as a >>3 value;
 * special case for dev/uuid inodes, they have fixed size data forks.
Linus Torvalds's avatar
Linus Torvalds committed
454 455
 */
int
456 457 458
xfs_attr_shortform_bytesfit(
	struct xfs_inode	*dp,
	int			bytes)
459
{
460 461 462 463 464
	struct xfs_mount	*mp = dp->i_mount;
	int64_t			dsize;
	int			minforkoff;
	int			maxforkoff;
	int			offset;
465

466 467
	/* rounded down */
	offset = (XFS_LITINO(mp, dp->i_d.di_version) - bytes) >> 3;
468

469
	if (dp->i_d.di_format == XFS_DINODE_FMT_DEV) {
470 471 472 473
		minforkoff = roundup(sizeof(xfs_dev_t), 8) >> 3;
		return (offset >= minforkoff) ? minforkoff : 0;
	}

474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
	/*
	 * If the requested numbers of bytes is smaller or equal to the
	 * current attribute fork size we can always proceed.
	 *
	 * Note that if_bytes in the data fork might actually be larger than
	 * the current data fork size is due to delalloc extents. In that
	 * case either the extent count will go down when they are converted
	 * to real extents, or the delalloc conversion will take care of the
	 * literal area rebalancing.
	 */
	if (bytes <= XFS_IFORK_ASIZE(dp))
		return dp->i_d.di_forkoff;

	/*
	 * For attr2 we can try to move the forkoff if there is space in the
	 * literal area, but for the old format we are done if there is no
	 * space in the fixed attribute fork.
	 */
	if (!(mp->m_flags & XFS_MOUNT_ATTR2))
493 494
		return 0;

495
	dsize = dp->i_df.if_bytes;
496

497 498
	switch (dp->i_d.di_format) {
	case XFS_DINODE_FMT_EXTENTS:
499
		/*
500
		 * If there is no attr fork and the data fork is extents, 
501 502 503
		 * determine if creating the default attr fork will result
		 * in the extents form migrating to btree. If so, the
		 * minimum offset only needs to be the space required for
504
		 * the btree root.
505
		 */
Christoph Hellwig's avatar
Christoph Hellwig committed
506 507
		if (!dp->i_d.di_forkoff && dp->i_df.if_bytes >
		    xfs_default_attroffset(dp))
508 509 510 511
			dsize = XFS_BMDR_SPACE_CALC(MINDBTPTRS);
		break;
	case XFS_DINODE_FMT_BTREE:
		/*
512 513 514
		 * If we have a data btree then keep forkoff if we have one,
		 * otherwise we are adding a new attr, so then we set
		 * minforkoff to where the btree root can finish so we have
515 516 517
		 * plenty of room for attrs
		 */
		if (dp->i_d.di_forkoff) {
518
			if (offset < dp->i_d.di_forkoff)
519
				return 0;
520 521
			return dp->i_d.di_forkoff;
		}
522
		dsize = XFS_BMAP_BROOT_SPACE(mp, dp->i_df.if_broot);
523 524
		break;
	}
525 526 527

	/*
	 * A data fork btree root must have space for at least
528 529
	 * MINDBTPTRS key/ptr pairs if the data fork is small or empty.
	 */
530
	minforkoff = max_t(int64_t, dsize, XFS_BMDR_SPACE_CALC(MINDBTPTRS));
531 532 533
	minforkoff = roundup(minforkoff, 8) >> 3;

	/* attr fork btree root can have at least this many key/ptr pairs */
534 535
	maxforkoff = XFS_LITINO(mp, dp->i_d.di_version) -
			XFS_BMDR_SPACE_CALC(MINABTPTRS);
536 537 538 539
	maxforkoff = maxforkoff >> 3;	/* rounded down */

	if (offset >= maxforkoff)
		return maxforkoff;
540 541
	if (offset >= minforkoff)
		return offset;
542 543 544 545 546 547 548 549 550
	return 0;
}

/*
 * Switch on the ATTR2 superblock bit (implies also FEATURES2)
 */
STATIC void
xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp)
{
551
	if ((mp->m_flags & XFS_MOUNT_ATTR2) &&
552
	    !(xfs_sb_version_hasattr2(&mp->m_sb))) {
Eric Sandeen's avatar
Eric Sandeen committed
553
		spin_lock(&mp->m_sb_lock);
554 555
		if (!xfs_sb_version_hasattr2(&mp->m_sb)) {
			xfs_sb_version_addattr2(&mp->m_sb);
Eric Sandeen's avatar
Eric Sandeen committed
556
			spin_unlock(&mp->m_sb_lock);
557
			xfs_log_sb(tp);
558
		} else
Eric Sandeen's avatar
Eric Sandeen committed
559
			spin_unlock(&mp->m_sb_lock);
560 561 562 563 564 565 566
	}
}

/*
 * Create the initial contents of a shortform attribute list.
 */
void
Linus Torvalds's avatar
Linus Torvalds committed
567 568 569 570
xfs_attr_shortform_create(xfs_da_args_t *args)
{
	xfs_attr_sf_hdr_t *hdr;
	xfs_inode_t *dp;
571
	struct xfs_ifork *ifp;
Linus Torvalds's avatar
Linus Torvalds committed
572

573 574
	trace_xfs_attr_sf_create(args);

Linus Torvalds's avatar
Linus Torvalds committed
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
	dp = args->dp;
	ASSERT(dp != NULL);
	ifp = dp->i_afp;
	ASSERT(ifp != NULL);
	ASSERT(ifp->if_bytes == 0);
	if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) {
		ifp->if_flags &= ~XFS_IFEXTENTS;	/* just in case */
		dp->i_d.di_aformat = XFS_DINODE_FMT_LOCAL;
		ifp->if_flags |= XFS_IFINLINE;
	} else {
		ASSERT(ifp->if_flags & XFS_IFINLINE);
	}
	xfs_idata_realloc(dp, sizeof(*hdr), XFS_ATTR_FORK);
	hdr = (xfs_attr_sf_hdr_t *)ifp->if_u1.if_data;
	hdr->count = 0;
590
	hdr->totsize = cpu_to_be16(sizeof(*hdr));
Linus Torvalds's avatar
Linus Torvalds committed
591 592 593 594 595 596 597
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
}

/*
 * Add a name/value pair to the shortform attribute list.
 * Overflow from the inode has already been checked for.
 */
598 599
void
xfs_attr_shortform_add(xfs_da_args_t *args, int forkoff)
Linus Torvalds's avatar
Linus Torvalds committed
600 601 602 603
{
	xfs_attr_shortform_t *sf;
	xfs_attr_sf_entry_t *sfe;
	int i, offset, size;
604
	xfs_mount_t *mp;
Linus Torvalds's avatar
Linus Torvalds committed
605
	xfs_inode_t *dp;
606
	struct xfs_ifork *ifp;
Linus Torvalds's avatar
Linus Torvalds committed
607

608 609
	trace_xfs_attr_sf_add(args);

Linus Torvalds's avatar
Linus Torvalds committed
610
	dp = args->dp;
611 612 613
	mp = dp->i_mount;
	dp->i_d.di_forkoff = forkoff;

Linus Torvalds's avatar
Linus Torvalds committed
614 615 616 617
	ifp = dp->i_afp;
	ASSERT(ifp->if_flags & XFS_IFINLINE);
	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
	sfe = &sf->list[0];
618
	for (i = 0; i < sf->hdr.count; sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
619
#ifdef DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
620 621 622 623
		if (sfe->namelen != args->namelen)
			continue;
		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
			continue;
624
		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
Linus Torvalds's avatar
Linus Torvalds committed
625
			continue;
626 627
		ASSERT(0);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
628 629 630 631 632 633 634 635 636
	}

	offset = (char *)sfe - (char *)sf;
	size = XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
	xfs_idata_realloc(dp, size, XFS_ATTR_FORK);
	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
	sfe = (xfs_attr_sf_entry_t *)((char *)sf + offset);

	sfe->namelen = args->namelen;
637
	sfe->valuelen = args->valuelen;
638
	sfe->flags = XFS_ATTR_NSP_ARGS_TO_ONDISK(args->flags);
Linus Torvalds's avatar
Linus Torvalds committed
639 640
	memcpy(sfe->nameval, args->name, args->namelen);
	memcpy(&sfe->nameval[args->namelen], args->value, args->valuelen);
641
	sf->hdr.count++;
642
	be16_add_cpu(&sf->hdr.totsize, size);
Linus Torvalds's avatar
Linus Torvalds committed
643 644
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);

645
	xfs_sbversion_add_attr2(mp, args->trans);
Linus Torvalds's avatar
Linus Torvalds committed
646 647
}

648 649 650 651
/*
 * After the last attribute is removed revert to original inode format,
 * making all literal area available to the data fork once more.
 */
652 653
void
xfs_attr_fork_remove(
654 655 656 657 658 659 660 661 662 663 664 665 666
	struct xfs_inode	*ip,
	struct xfs_trans	*tp)
{
	xfs_idestroy_fork(ip, XFS_ATTR_FORK);
	ip->i_d.di_forkoff = 0;
	ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;

	ASSERT(ip->i_d.di_anextents == 0);
	ASSERT(ip->i_afp == NULL);

	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
}

Linus Torvalds's avatar
Linus Torvalds committed
667
/*
668
 * Remove an attribute from the shortform attribute list structure.
Linus Torvalds's avatar
Linus Torvalds committed
669 670 671 672 673 674 675
 */
int
xfs_attr_shortform_remove(xfs_da_args_t *args)
{
	xfs_attr_shortform_t *sf;
	xfs_attr_sf_entry_t *sfe;
	int base, size=0, end, totsize, i;
676
	xfs_mount_t *mp;
Linus Torvalds's avatar
Linus Torvalds committed
677 678
	xfs_inode_t *dp;

679 680
	trace_xfs_attr_sf_remove(args);

Linus Torvalds's avatar
Linus Torvalds committed
681
	dp = args->dp;
682
	mp = dp->i_mount;
Linus Torvalds's avatar
Linus Torvalds committed
683 684 685
	base = sizeof(xfs_attr_sf_hdr_t);
	sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
	sfe = &sf->list[0];
686
	end = sf->hdr.count;
687
	for (i = 0; i < end; sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
Linus Torvalds's avatar
Linus Torvalds committed
688 689 690 691 692 693
					base += size, i++) {
		size = XFS_ATTR_SF_ENTSIZE(sfe);
		if (sfe->namelen != args->namelen)
			continue;
		if (memcmp(sfe->nameval, args->name, args->namelen) != 0)
			continue;
694
		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
Linus Torvalds's avatar
Linus Torvalds committed
695 696 697
			continue;
		break;
	}
698
	if (i == end)
699
		return -ENOATTR;
Linus Torvalds's avatar
Linus Torvalds committed
700

701 702 703
	/*
	 * Fix up the attribute fork data, covering the hole
	 */
Linus Torvalds's avatar
Linus Torvalds committed
704
	end = base + size;
705
	totsize = be16_to_cpu(sf->hdr.totsize);
706 707
	if (end != totsize)
		memmove(&((char *)sf)[base], &((char *)sf)[end], totsize - end);
708
	sf->hdr.count--;
709
	be16_add_cpu(&sf->hdr.totsize, -size);
710 711 712 713 714

	/*
	 * Fix up the start offset of the attribute fork
	 */
	totsize -= size;
715
	if (totsize == sizeof(xfs_attr_sf_hdr_t) &&
716 717 718
	    (mp->m_flags & XFS_MOUNT_ATTR2) &&
	    (dp->i_d.di_format != XFS_DINODE_FMT_BTREE) &&
	    !(args->op_flags & XFS_DA_OP_ADDNAME)) {
719
		xfs_attr_fork_remove(dp, args->trans);
720 721 722 723
	} else {
		xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
		dp->i_d.di_forkoff = xfs_attr_shortform_bytesfit(dp, totsize);
		ASSERT(dp->i_d.di_forkoff);
724 725 726 727
		ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) ||
				(args->op_flags & XFS_DA_OP_ADDNAME) ||
				!(mp->m_flags & XFS_MOUNT_ATTR2) ||
				dp->i_d.di_format == XFS_DINODE_FMT_BTREE);
728 729 730 731 732
		xfs_trans_log_inode(args->trans, dp,
					XFS_ILOG_CORE | XFS_ILOG_ADATA);
	}

	xfs_sbversion_add_attr2(mp, args->trans);
Linus Torvalds's avatar
Linus Torvalds committed
733

Eric Sandeen's avatar
Eric Sandeen committed
734
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
735 736 737 738 739 740 741 742 743 744 745 746
}

/*
 * Look up a name in a shortform attribute list structure.
 */
/*ARGSUSED*/
int
xfs_attr_shortform_lookup(xfs_da_args_t *args)
{
	xfs_attr_shortform_t *sf;
	xfs_attr_sf_entry_t *sfe;
	int i;
747
	struct xfs_ifork *ifp;
Linus Torvalds's avatar
Linus Torvalds committed
748

749 750
	trace_xfs_attr_sf_lookup(args);

Linus Torvalds's avatar
Linus Torvalds committed
751 752 753 754
	ifp = args->dp->i_afp;
	ASSERT(ifp->if_flags & XFS_IFINLINE);
	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
	sfe = &sf->list[0];
755
	for (i = 0; i < sf->hdr.count;
Linus Torvalds's avatar
Linus Torvalds committed
756 757 758 759 760
				sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
		if (sfe->namelen != args->namelen)
			continue;
		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
			continue;
761
		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
Linus Torvalds's avatar
Linus Torvalds committed
762
			continue;
763
		return -EEXIST;
Linus Torvalds's avatar
Linus Torvalds committed
764
	}
765
	return -ENOATTR;
Linus Torvalds's avatar
Linus Torvalds committed
766 767 768
}

/*
769 770 771 772 773
 * Retreive the attribute value and length.
 *
 * If ATTR_KERNOVAL is specified, only the length needs to be returned.
 * Unlike a lookup, we only return an error if the attribute does not
 * exist or we can't retrieve the value.
Linus Torvalds's avatar
Linus Torvalds committed
774 775
 */
int
776 777
xfs_attr_shortform_getvalue(
	struct xfs_da_args	*args)
Linus Torvalds's avatar
Linus Torvalds committed
778
{
779 780 781
	struct xfs_attr_shortform *sf;
	struct xfs_attr_sf_entry *sfe;
	int			i;
Linus Torvalds's avatar
Linus Torvalds committed
782

783
	ASSERT(args->dp->i_afp->if_flags == XFS_IFINLINE);
Linus Torvalds's avatar
Linus Torvalds committed
784 785
	sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data;
	sfe = &sf->list[0];
786
	for (i = 0; i < sf->hdr.count;
Linus Torvalds's avatar
Linus Torvalds committed
787 788 789 790 791
				sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
		if (sfe->namelen != args->namelen)
			continue;
		if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
			continue;
792
		if (!xfs_attr_namesp_match(args->flags, sfe->flags))
Linus Torvalds's avatar
Linus Torvalds committed
793
			continue;
794 795
		return xfs_attr_copy_value(args, &sfe->nameval[args->namelen],
						sfe->valuelen);
Linus Torvalds's avatar
Linus Torvalds committed
796
	}
797
	return -ENOATTR;
Linus Torvalds's avatar
Linus Torvalds committed
798 799 800
}

/*
801 802
 * Convert from using the shortform to the leaf.  On success, return the
 * buffer so that we can keep it locked until we're totally done with it.
Linus Torvalds's avatar
Linus Torvalds committed
803 804
 */
int
805
xfs_attr_shortform_to_leaf(
806 807
	struct xfs_da_args		*args,
	struct xfs_buf			**leaf_bp)
Linus Torvalds's avatar
Linus Torvalds committed
808
{
809 810 811 812 813 814 815 816 817
	struct xfs_inode		*dp;
	struct xfs_attr_shortform	*sf;
	struct xfs_attr_sf_entry	*sfe;
	struct xfs_da_args		nargs;
	char				*tmpbuffer;
	int				error, i, size;
	xfs_dablk_t			blkno;
	struct xfs_buf			*bp;
	struct xfs_ifork		*ifp;
Linus Torvalds's avatar
Linus Torvalds committed
818

819 820
	trace_xfs_attr_sf_to_leaf(args);

Linus Torvalds's avatar
Linus Torvalds committed
821 822 823
	dp = args->dp;
	ifp = dp->i_afp;
	sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
824
	size = be16_to_cpu(sf->hdr.totsize);
825
	tmpbuffer = kmem_alloc(size, 0);
Linus Torvalds's avatar
Linus Torvalds committed
826 827 828 829 830
	ASSERT(tmpbuffer != NULL);
	memcpy(tmpbuffer, ifp->if_u1.if_data, size);
	sf = (xfs_attr_shortform_t *)tmpbuffer;

	xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
831
	xfs_bmap_local_to_extents_empty(args->trans, dp, XFS_ATTR_FORK);
832

Linus Torvalds's avatar
Linus Torvalds committed
833 834
	bp = NULL;
	error = xfs_da_grow_inode(args, &blkno);
835
	if (error)
Linus Torvalds's avatar
Linus Torvalds committed
836 837 838
		goto out;

	ASSERT(blkno == 0);
839
	error = xfs_attr3_leaf_create(args, blkno, &bp);
840
	if (error)
Linus Torvalds's avatar
Linus Torvalds committed
841 842 843 844
		goto out;

	memset((char *)&nargs, 0, sizeof(nargs));
	nargs.dp = dp;
845
	nargs.geo = args->geo;
Linus Torvalds's avatar
Linus Torvalds committed
846 847 848
	nargs.total = args->total;
	nargs.whichfork = XFS_ATTR_FORK;
	nargs.trans = args->trans;
849
	nargs.op_flags = XFS_DA_OP_OKNOENT;
Linus Torvalds's avatar
Linus Torvalds committed
850 851

	sfe = &sf->list[0];
852
	for (i = 0; i < sf->hdr.count; i++) {
853
		nargs.name = sfe->nameval;
Linus Torvalds's avatar
Linus Torvalds committed
854
		nargs.namelen = sfe->namelen;
855
		nargs.value = &sfe->nameval[nargs.namelen];
856
		nargs.valuelen = sfe->valuelen;
857
		nargs.hashval = xfs_da_hashname(sfe->nameval,
Linus Torvalds's avatar
Linus Torvalds committed
858
						sfe->namelen);
859
		nargs.flags = XFS_ATTR_NSP_ONDISK_TO_ARGS(sfe->flags);
860
		error = xfs_attr3_leaf_lookup_int(bp, &nargs); /* set a->index */
861
		ASSERT(error == -ENOATTR);
862
		error = xfs_attr3_leaf_add(bp, &nargs);
863
		ASSERT(error != -ENOSPC);
Linus Torvalds's avatar
Linus Torvalds committed
864 865 866 867 868
		if (error)
			goto out;
		sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
	}
	error = 0;
869
	*leaf_bp = bp;
Linus Torvalds's avatar
Linus Torvalds committed
870
out:
871
	kmem_free(tmpbuffer);
Eric Sandeen's avatar
Eric Sandeen committed
872
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
873 874 875 876 877 878 879
}

/*
 * Check a leaf attribute block to see if all the entries would fit into
 * a shortform attribute list.
 */
int
880
xfs_attr_shortform_allfit(
881 882
	struct xfs_buf		*bp,
	struct xfs_inode	*dp)
Linus Torvalds's avatar
Linus Torvalds committed
883
{
884 885
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr_leaf_entry *entry;
Linus Torvalds's avatar
Linus Torvalds committed
886
	xfs_attr_leaf_name_local_t *name_loc;
887 888 889
	struct xfs_attr3_icleaf_hdr leafhdr;
	int			bytes;
	int			i;
890
	struct xfs_mount	*mp = bp->b_mount;
Linus Torvalds's avatar
Linus Torvalds committed
891

892
	leaf = bp->b_addr;
893
	xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
894
	entry = xfs_attr3_leaf_entryp(leaf);
Linus Torvalds's avatar
Linus Torvalds committed
895 896

	bytes = sizeof(struct xfs_attr_sf_hdr);
897
	for (i = 0; i < leafhdr.count; entry++, i++) {
Linus Torvalds's avatar
Linus Torvalds committed
898 899 900
		if (entry->flags & XFS_ATTR_INCOMPLETE)
			continue;		/* don't copy partial entries */
		if (!(entry->flags & XFS_ATTR_LOCAL))
Eric Sandeen's avatar
Eric Sandeen committed
901
			return 0;
902
		name_loc = xfs_attr3_leaf_name_local(leaf, i);
Linus Torvalds's avatar
Linus Torvalds committed
903
		if (name_loc->namelen >= XFS_ATTR_SF_ENTSIZE_MAX)
Eric Sandeen's avatar
Eric Sandeen committed
904
			return 0;
905
		if (be16_to_cpu(name_loc->valuelen) >= XFS_ATTR_SF_ENTSIZE_MAX)
Eric Sandeen's avatar
Eric Sandeen committed
906
			return 0;
907
		bytes += sizeof(struct xfs_attr_sf_entry) - 1
Linus Torvalds's avatar
Linus Torvalds committed
908
				+ name_loc->namelen
909
				+ be16_to_cpu(name_loc->valuelen);
Linus Torvalds's avatar
Linus Torvalds committed
910
	}
911
	if ((dp->i_mount->m_flags & XFS_MOUNT_ATTR2) &&
912
	    (dp->i_d.di_format != XFS_DINODE_FMT_BTREE) &&
913
	    (bytes == sizeof(struct xfs_attr_sf_hdr)))
914 915
		return -1;
	return xfs_attr_shortform_bytesfit(dp, bytes);
Linus Torvalds's avatar
Linus Torvalds committed
916 917
}

918 919 920 921 922 923 924 925 926 927 928
/* Verify the consistency of an inline attribute fork. */
xfs_failaddr_t
xfs_attr_shortform_verify(
	struct xfs_inode		*ip)
{
	struct xfs_attr_shortform	*sfp;
	struct xfs_attr_sf_entry	*sfep;
	struct xfs_attr_sf_entry	*next_sfep;
	char				*endp;
	struct xfs_ifork		*ifp;
	int				i;
929
	int64_t				size;
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991

	ASSERT(ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL);
	ifp = XFS_IFORK_PTR(ip, XFS_ATTR_FORK);
	sfp = (struct xfs_attr_shortform *)ifp->if_u1.if_data;
	size = ifp->if_bytes;

	/*
	 * Give up if the attribute is way too short.
	 */
	if (size < sizeof(struct xfs_attr_sf_hdr))
		return __this_address;

	endp = (char *)sfp + size;

	/* Check all reported entries */
	sfep = &sfp->list[0];
	for (i = 0; i < sfp->hdr.count; i++) {
		/*
		 * struct xfs_attr_sf_entry has a variable length.
		 * Check the fixed-offset parts of the structure are
		 * within the data buffer.
		 */
		if (((char *)sfep + sizeof(*sfep)) >= endp)
			return __this_address;

		/* Don't allow names with known bad length. */
		if (sfep->namelen == 0)
			return __this_address;

		/*
		 * Check that the variable-length part of the structure is
		 * within the data buffer.  The next entry starts after the
		 * name component, so nextentry is an acceptable test.
		 */
		next_sfep = XFS_ATTR_SF_NEXTENTRY(sfep);
		if ((char *)next_sfep > endp)
			return __this_address;

		/*
		 * Check for unknown flags.  Short form doesn't support
		 * the incomplete or local bits, so we can use the namespace
		 * mask here.
		 */
		if (sfep->flags & ~XFS_ATTR_NSP_ONDISK_MASK)
			return __this_address;

		/*
		 * Check for invalid namespace combinations.  We only allow
		 * one namespace flag per xattr, so we can just count the
		 * bits (i.e. hweight) here.
		 */
		if (hweight8(sfep->flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
			return __this_address;

		sfep = next_sfep;
	}
	if ((void *)sfep != (void *)endp)
		return __this_address;

	return NULL;
}

Linus Torvalds's avatar
Linus Torvalds committed
992 993 994 995
/*
 * Convert a leaf attribute list to shortform attribute list
 */
int
996 997 998 999
xfs_attr3_leaf_to_shortform(
	struct xfs_buf		*bp,
	struct xfs_da_args	*args,
	int			forkoff)
Linus Torvalds's avatar
Linus Torvalds committed
1000
{
1001 1002 1003 1004 1005 1006 1007 1008 1009
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr3_icleaf_hdr ichdr;
	struct xfs_attr_leaf_entry *entry;
	struct xfs_attr_leaf_name_local *name_loc;
	struct xfs_da_args	nargs;
	struct xfs_inode	*dp = args->dp;
	char			*tmpbuffer;
	int			error;
	int			i;
Linus Torvalds's avatar
Linus Torvalds committed
1010

1011 1012
	trace_xfs_attr_leaf_to_sf(args);

1013
	tmpbuffer = kmem_alloc(args->geo->blksize, 0);
1014
	if (!tmpbuffer)
1015
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
1016

1017
	memcpy(tmpbuffer, bp->b_addr, args->geo->blksize);
1018

Linus Torvalds's avatar
Linus Torvalds committed
1019
	leaf = (xfs_attr_leafblock_t *)tmpbuffer;
1020
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
1021 1022 1023
	entry = xfs_attr3_leaf_entryp(leaf);

	/* XXX (dgc): buffer is about to be marked stale - why zero it? */
1024
	memset(bp->b_addr, 0, args->geo->blksize);
Linus Torvalds's avatar
Linus Torvalds committed
1025 1026 1027 1028 1029 1030 1031

	/*
	 * Clean out the prior contents of the attribute list.
	 */
	error = xfs_da_shrink_inode(args, 0, bp);
	if (error)
		goto out;
1032 1033

	if (forkoff == -1) {
1034
		ASSERT(dp->i_mount->m_flags & XFS_MOUNT_ATTR2);
1035
		ASSERT(dp->i_d.di_format != XFS_DINODE_FMT_BTREE);
1036
		xfs_attr_fork_remove(dp, args->trans);
Linus Torvalds's avatar
Linus Torvalds committed
1037
		goto out;
1038 1039 1040
	}

	xfs_attr_shortform_create(args);
Linus Torvalds's avatar
Linus Torvalds committed
1041 1042 1043 1044 1045

	/*
	 * Copy the attributes
	 */
	memset((char *)&nargs, 0, sizeof(nargs));
1046
	nargs.geo = args->geo;
Linus Torvalds's avatar
Linus Torvalds committed
1047 1048 1049 1050
	nargs.dp = dp;
	nargs.total = args->total;
	nargs.whichfork = XFS_ATTR_FORK;
	nargs.trans = args->trans;
1051
	nargs.op_flags = XFS_DA_OP_OKNOENT;
1052 1053

	for (i = 0; i < ichdr.count; entry++, i++) {
Linus Torvalds's avatar
Linus Torvalds committed
1054 1055 1056 1057 1058
		if (entry->flags & XFS_ATTR_INCOMPLETE)
			continue;	/* don't copy partial entries */
		if (!entry->nameidx)
			continue;
		ASSERT(entry->flags & XFS_ATTR_LOCAL);
1059
		name_loc = xfs_attr3_leaf_name_local(leaf, i);
1060
		nargs.name = name_loc->nameval;
Linus Torvalds's avatar
Linus Torvalds committed
1061
		nargs.namelen = name_loc->namelen;
1062
		nargs.value = &name_loc->nameval[nargs.namelen];
1063
		nargs.valuelen = be16_to_cpu(name_loc->valuelen);
1064
		nargs.hashval = be32_to_cpu(entry->hashval);
1065
		nargs.flags = XFS_ATTR_NSP_ONDISK_TO_ARGS(entry->flags);
1066
		xfs_attr_shortform_add(&nargs, forkoff);
Linus Torvalds's avatar
Linus Torvalds committed
1067 1068 1069 1070
	}
	error = 0;

out:
1071
	kmem_free(tmpbuffer);
1072
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
1073 1074 1075 1076 1077 1078
}

/*
 * Convert from using a single leaf to a root node and a leaf.
 */
int
1079 1080
xfs_attr3_leaf_to_node(
	struct xfs_da_args	*args)
Linus Torvalds's avatar
Linus Torvalds committed
1081
{
1082 1083 1084
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr3_icleaf_hdr icleafhdr;
	struct xfs_attr_leaf_entry *entries;
1085
	struct xfs_da_node_entry *btree;
1086 1087 1088 1089 1090 1091 1092 1093
	struct xfs_da3_icnode_hdr icnodehdr;
	struct xfs_da_intnode	*node;
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
	struct xfs_buf		*bp1 = NULL;
	struct xfs_buf		*bp2 = NULL;
	xfs_dablk_t		blkno;
	int			error;
Linus Torvalds's avatar
Linus Torvalds committed
1094

1095 1096
	trace_xfs_attr_leaf_to_node(args);

Linus Torvalds's avatar
Linus Torvalds committed
1097 1098 1099
	error = xfs_da_grow_inode(args, &blkno);
	if (error)
		goto out;
1100
	error = xfs_attr3_leaf_read(args->trans, dp, 0, -1, &bp1);
Linus Torvalds's avatar
Linus Torvalds committed
1101 1102
	if (error)
		goto out;
1103

1104
	error = xfs_da_get_buf(args->trans, dp, blkno, -1, &bp2, XFS_ATTR_FORK);
Linus Torvalds's avatar
Linus Torvalds committed
1105 1106
	if (error)
		goto out;
1107 1108

	/* copy leaf to new buffer, update identifiers */
1109
	xfs_trans_buf_set_type(args->trans, bp2, XFS_BLFT_ATTR_LEAF_BUF);
1110
	bp2->b_ops = bp1->b_ops;
1111
	memcpy(bp2->b_addr, bp1->b_addr, args->geo->blksize);
1112 1113 1114 1115
	if (xfs_sb_version_hascrc(&mp->m_sb)) {
		struct xfs_da3_blkinfo *hdr3 = bp2->b_addr;
		hdr3->blkno = cpu_to_be64(bp2->b_bn);
	}
1116
	xfs_trans_log_buf(args->trans, bp2, 0, args->geo->blksize - 1);
Linus Torvalds's avatar
Linus Torvalds committed
1117 1118 1119 1120

	/*
	 * Set up the new root node.
	 */
1121
	error = xfs_da3_node_create(args, 0, 1, &bp1, XFS_ATTR_FORK);
Linus Torvalds's avatar
Linus Torvalds committed
1122 1123
	if (error)
		goto out;
1124
	node = bp1->b_addr;
1125
	dp->d_ops->node_hdr_from_disk(&icnodehdr, node);
1126
	btree = dp->d_ops->node_tree_p(node);
1127

1128
	leaf = bp2->b_addr;
1129
	xfs_attr3_leaf_hdr_from_disk(args->geo, &icleafhdr, leaf);
1130 1131
	entries = xfs_attr3_leaf_entryp(leaf);

Linus Torvalds's avatar
Linus Torvalds committed
1132
	/* both on-disk, don't endian-flip twice */
1133
	btree[0].hashval = entries[icleafhdr.count - 1].hashval;
1134
	btree[0].before = cpu_to_be32(blkno);
1135
	icnodehdr.count = 1;
1136
	dp->d_ops->node_hdr_to_disk(node, &icnodehdr);
1137
	xfs_trans_log_buf(args->trans, bp1, 0, args->geo->blksize - 1);
Linus Torvalds's avatar
Linus Torvalds committed
1138 1139
	error = 0;
out:
1140
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
}

/*========================================================================
 * Routines used for growing the Btree.
 *========================================================================*/

/*
 * Create the initial contents of a leaf attribute list
 * or a leaf in a node attribute list.
 */
1151
STATIC int
1152 1153 1154 1155
xfs_attr3_leaf_create(
	struct xfs_da_args	*args,
	xfs_dablk_t		blkno,
	struct xfs_buf		**bpp)
Linus Torvalds's avatar
Linus Torvalds committed
1156
{
1157 1158 1159 1160 1161 1162
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr3_icleaf_hdr ichdr;
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
	struct xfs_buf		*bp;
	int			error;
Linus Torvalds's avatar
Linus Torvalds committed
1163

1164 1165
	trace_xfs_attr_leaf_create(args);

Linus Torvalds's avatar
Linus Torvalds committed
1166 1167 1168
	error = xfs_da_get_buf(args->trans, args->dp, blkno, -1, &bp,
					    XFS_ATTR_FORK);
	if (error)
1169 1170
		return error;
	bp->b_ops = &xfs_attr3_leaf_buf_ops;
1171
	xfs_trans_buf_set_type(args->trans, bp, XFS_BLFT_ATTR_LEAF_BUF);
1172
	leaf = bp->b_addr;
1173
	memset(leaf, 0, args->geo->blksize);
1174 1175

	memset(&ichdr, 0, sizeof(ichdr));
1176
	ichdr.firstused = args->geo->blksize;
1177 1178 1179 1180 1181 1182 1183 1184

	if (xfs_sb_version_hascrc(&mp->m_sb)) {
		struct xfs_da3_blkinfo *hdr3 = bp->b_addr;

		ichdr.magic = XFS_ATTR3_LEAF_MAGIC;

		hdr3->blkno = cpu_to_be64(bp->b_bn);
		hdr3->owner = cpu_to_be64(dp->i_ino);
1185
		uuid_copy(&hdr3->uuid, &mp->m_sb.sb_meta_uuid);
Linus Torvalds's avatar
Linus Torvalds committed
1186

1187 1188 1189 1190 1191 1192
		ichdr.freemap[0].base = sizeof(struct xfs_attr3_leaf_hdr);
	} else {
		ichdr.magic = XFS_ATTR_LEAF_MAGIC;
		ichdr.freemap[0].base = sizeof(struct xfs_attr_leaf_hdr);
	}
	ichdr.freemap[0].size = ichdr.firstused - ichdr.freemap[0].base;
Linus Torvalds's avatar
Linus Torvalds committed
1193

1194
	xfs_attr3_leaf_hdr_to_disk(args->geo, leaf, &ichdr);
1195
	xfs_trans_log_buf(args->trans, bp, 0, args->geo->blksize - 1);
Linus Torvalds's avatar
Linus Torvalds committed
1196 1197

	*bpp = bp;
1198
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1199 1200 1201 1202 1203 1204
}

/*
 * Split the leaf node, rebalance, then add the new entry.
 */
int
1205 1206 1207 1208
xfs_attr3_leaf_split(
	struct xfs_da_state	*state,
	struct xfs_da_state_blk	*oldblk,
	struct xfs_da_state_blk	*newblk)
Linus Torvalds's avatar
Linus Torvalds committed
1209 1210 1211 1212
{
	xfs_dablk_t blkno;
	int error;

1213 1214
	trace_xfs_attr_leaf_split(state->args);

Linus Torvalds's avatar
Linus Torvalds committed
1215 1216 1217 1218 1219 1220
	/*
	 * Allocate space for a new leaf node.
	 */
	ASSERT(oldblk->magic == XFS_ATTR_LEAF_MAGIC);
	error = xfs_da_grow_inode(state->args, &blkno);
	if (error)
Eric Sandeen's avatar
Eric Sandeen committed
1221
		return error;
1222
	error = xfs_attr3_leaf_create(state->args, blkno, &newblk->bp);
Linus Torvalds's avatar
Linus Torvalds committed
1223
	if (error)
Eric Sandeen's avatar
Eric Sandeen committed
1224
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
1225 1226 1227 1228 1229 1230 1231
	newblk->blkno = blkno;
	newblk->magic = XFS_ATTR_LEAF_MAGIC;

	/*
	 * Rebalance the entries across the two leaves.
	 * NOTE: rebalance() currently depends on the 2nd block being empty.
	 */
1232
	xfs_attr3_leaf_rebalance(state, oldblk, newblk);
1233
	error = xfs_da3_blk_link(state, oldblk, newblk);
Linus Torvalds's avatar
Linus Torvalds committed
1234
	if (error)
Eric Sandeen's avatar
Eric Sandeen committed
1235
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
1236 1237 1238 1239 1240 1241 1242 1243

	/*
	 * Save info on "old" attribute for "atomic rename" ops, leaf_add()
	 * modifies the index/blkno/rmtblk/rmtblkcnt fields to show the
	 * "new" attrs info.  Will need the "old" info to remove it later.
	 *
	 * Insert the "new" entry in the correct block.
	 */
1244 1245
	if (state->inleaf) {
		trace_xfs_attr_leaf_add_old(state->args);
1246
		error = xfs_attr3_leaf_add(oldblk->bp, state->args);
1247 1248
	} else {
		trace_xfs_attr_leaf_add_new(state->args);
1249
		error = xfs_attr3_leaf_add(newblk->bp, state->args);
1250
	}
Linus Torvalds's avatar
Linus Torvalds committed
1251 1252 1253 1254 1255 1256

	/*
	 * Update last hashval in each block since we added the name.
	 */
	oldblk->hashval = xfs_attr_leaf_lasthash(oldblk->bp, NULL);
	newblk->hashval = xfs_attr_leaf_lasthash(newblk->bp, NULL);
Eric Sandeen's avatar
Eric Sandeen committed
1257
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
1258 1259 1260 1261 1262 1263
}

/*
 * Add a name to the leaf attribute list structure.
 */
int
1264
xfs_attr3_leaf_add(
1265 1266
	struct xfs_buf		*bp,
	struct xfs_da_args	*args)
Linus Torvalds's avatar
Linus Torvalds committed
1267
{
1268 1269 1270 1271 1272 1273 1274
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr3_icleaf_hdr ichdr;
	int			tablesize;
	int			entsize;
	int			sum;
	int			tmp;
	int			i;
Linus Torvalds's avatar
Linus Torvalds committed
1275

1276 1277
	trace_xfs_attr_leaf_add(args);

1278
	leaf = bp->b_addr;
1279
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
1280
	ASSERT(args->index >= 0 && args->index <= ichdr.count);
1281
	entsize = xfs_attr_leaf_newentsize(args, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
1282 1283 1284 1285 1286

	/*
	 * Search through freemap for first-fit on new name length.
	 * (may need to figure in size of entry struct too)
	 */
1287 1288 1289 1290 1291
	tablesize = (ichdr.count + 1) * sizeof(xfs_attr_leaf_entry_t)
					+ xfs_attr3_leaf_hdr_size(leaf);
	for (sum = 0, i = XFS_ATTR_LEAF_MAPSIZE - 1; i >= 0; i--) {
		if (tablesize > ichdr.firstused) {
			sum += ichdr.freemap[i].size;
Linus Torvalds's avatar
Linus Torvalds committed
1292 1293
			continue;
		}
1294
		if (!ichdr.freemap[i].size)
Linus Torvalds's avatar
Linus Torvalds committed
1295 1296
			continue;	/* no space in this map */
		tmp = entsize;
1297
		if (ichdr.freemap[i].base < ichdr.firstused)
Linus Torvalds's avatar
Linus Torvalds committed
1298
			tmp += sizeof(xfs_attr_leaf_entry_t);
1299 1300 1301
		if (ichdr.freemap[i].size >= tmp) {
			tmp = xfs_attr3_leaf_add_work(bp, &ichdr, args, i);
			goto out_log_hdr;
Linus Torvalds's avatar
Linus Torvalds committed
1302
		}
1303
		sum += ichdr.freemap[i].size;
Linus Torvalds's avatar
Linus Torvalds committed
1304 1305 1306 1307 1308 1309 1310
	}

	/*
	 * If there are no holes in the address space of the block,
	 * and we don't have enough freespace, then compaction will do us
	 * no good and we should just give up.
	 */
1311
	if (!ichdr.holes && sum < entsize)
1312
		return -ENOSPC;
Linus Torvalds's avatar
Linus Torvalds committed
1313 1314 1315 1316 1317

	/*
	 * Compact the entries to coalesce free space.
	 * This may change the hdr->count via dropping INCOMPLETE entries.
	 */
1318
	xfs_attr3_leaf_compact(args, &ichdr, bp);
Linus Torvalds's avatar
Linus Torvalds committed
1319 1320 1321 1322 1323

	/*
	 * After compaction, the block is guaranteed to have only one
	 * free region, in freemap[0].  If it is not big enough, give up.
	 */
1324
	if (ichdr.freemap[0].size < (entsize + sizeof(xfs_attr_leaf_entry_t))) {
1325
		tmp = -ENOSPC;
1326 1327 1328 1329
		goto out_log_hdr;
	}

	tmp = xfs_attr3_leaf_add_work(bp, &ichdr, args, 0);
Linus Torvalds's avatar
Linus Torvalds committed
1330

1331
out_log_hdr:
1332
	xfs_attr3_leaf_hdr_to_disk(args->geo, leaf, &ichdr);
1333 1334 1335 1336
	xfs_trans_log_buf(args->trans, bp,
		XFS_DA_LOGRANGE(leaf, &leaf->hdr,
				xfs_attr3_leaf_hdr_size(leaf)));
	return tmp;
Linus Torvalds's avatar
Linus Torvalds committed
1337 1338 1339 1340 1341 1342
}

/*
 * Add a name to a leaf attribute list structure.
 */
STATIC int
1343 1344 1345 1346 1347
xfs_attr3_leaf_add_work(
	struct xfs_buf		*bp,
	struct xfs_attr3_icleaf_hdr *ichdr,
	struct xfs_da_args	*args,
	int			mapindex)
Linus Torvalds's avatar
Linus Torvalds committed
1348
{
1349 1350 1351 1352 1353 1354 1355
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr_leaf_entry *entry;
	struct xfs_attr_leaf_name_local *name_loc;
	struct xfs_attr_leaf_name_remote *name_rmt;
	struct xfs_mount	*mp;
	int			tmp;
	int			i;
Linus Torvalds's avatar
Linus Torvalds committed
1356

1357 1358
	trace_xfs_attr_leaf_add_work(args);

1359
	leaf = bp->b_addr;
1360 1361
	ASSERT(mapindex >= 0 && mapindex < XFS_ATTR_LEAF_MAPSIZE);
	ASSERT(args->index >= 0 && args->index <= ichdr->count);
Linus Torvalds's avatar
Linus Torvalds committed
1362 1363 1364 1365

	/*
	 * Force open some space in the entry array and fill it in.
	 */
1366 1367 1368
	entry = &xfs_attr3_leaf_entryp(leaf)[args->index];
	if (args->index < ichdr->count) {
		tmp  = ichdr->count - args->index;
Linus Torvalds's avatar
Linus Torvalds committed
1369
		tmp *= sizeof(xfs_attr_leaf_entry_t);
1370
		memmove(entry + 1, entry, tmp);
1371
		xfs_trans_log_buf(args->trans, bp,
Linus Torvalds's avatar
Linus Torvalds committed
1372 1373
		    XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(*entry)));
	}
1374
	ichdr->count++;
Linus Torvalds's avatar
Linus Torvalds committed
1375 1376 1377 1378 1379

	/*
	 * Allocate space for the new string (at the end of the run).
	 */
	mp = args->trans->t_mountp;
1380
	ASSERT(ichdr->freemap[mapindex].base < args->geo->blksize);
1381 1382
	ASSERT((ichdr->freemap[mapindex].base & 0x3) == 0);
	ASSERT(ichdr->freemap[mapindex].size >=
1383
		xfs_attr_leaf_newentsize(args, NULL));
1384
	ASSERT(ichdr->freemap[mapindex].size < args->geo->blksize);
1385 1386
	ASSERT((ichdr->freemap[mapindex].size & 0x3) == 0);

1387
	ichdr->freemap[mapindex].size -= xfs_attr_leaf_newentsize(args, &tmp);
1388 1389 1390

	entry->nameidx = cpu_to_be16(ichdr->freemap[mapindex].base +
				     ichdr->freemap[mapindex].size);
1391
	entry->hashval = cpu_to_be32(args->hashval);
Linus Torvalds's avatar
Linus Torvalds committed
1392
	entry->flags = tmp ? XFS_ATTR_LOCAL : 0;
1393
	entry->flags |= XFS_ATTR_NSP_ARGS_TO_ONDISK(args->flags);
1394
	if (args->op_flags & XFS_DA_OP_RENAME) {
Linus Torvalds's avatar
Linus Torvalds committed
1395 1396 1397 1398 1399 1400
		entry->flags |= XFS_ATTR_INCOMPLETE;
		if ((args->blkno2 == args->blkno) &&
		    (args->index2 <= args->index)) {
			args->index2++;
		}
	}
1401
	xfs_trans_log_buf(args->trans, bp,
Linus Torvalds's avatar
Linus Torvalds committed
1402
			  XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
1403 1404
	ASSERT((args->index == 0) ||
	       (be32_to_cpu(entry->hashval) >= be32_to_cpu((entry-1)->hashval)));
1405
	ASSERT((args->index == ichdr->count - 1) ||
1406
	       (be32_to_cpu(entry->hashval) <= be32_to_cpu((entry+1)->hashval)));
Linus Torvalds's avatar
Linus Torvalds committed
1407 1408 1409 1410 1411 1412 1413 1414 1415

	/*
	 * For "remote" attribute values, simply note that we need to
	 * allocate space for the "remote" value.  We can't actually
	 * allocate the extents in this transaction, and we can't decide
	 * which blocks they should be as we might allocate more blocks
	 * as part of this transaction (a split operation for example).
	 */
	if (entry->flags & XFS_ATTR_LOCAL) {
1416
		name_loc = xfs_attr3_leaf_name_local(leaf, args->index);
Linus Torvalds's avatar
Linus Torvalds committed
1417
		name_loc->namelen = args->namelen;
1418
		name_loc->valuelen = cpu_to_be16(args->valuelen);
Linus Torvalds's avatar
Linus Torvalds committed
1419 1420
		memcpy((char *)name_loc->nameval, args->name, args->namelen);
		memcpy((char *)&name_loc->nameval[args->namelen], args->value,
1421
				   be16_to_cpu(name_loc->valuelen));
Linus Torvalds's avatar
Linus Torvalds committed
1422
	} else {
1423
		name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index);
Linus Torvalds's avatar
Linus Torvalds committed
1424 1425 1426 1427 1428 1429 1430
		name_rmt->namelen = args->namelen;
		memcpy((char *)name_rmt->name, args->name, args->namelen);
		entry->flags |= XFS_ATTR_INCOMPLETE;
		/* just in case */
		name_rmt->valuelen = 0;
		name_rmt->valueblk = 0;
		args->rmtblkno = 1;
Dave Chinner's avatar
Dave Chinner committed
1431
		args->rmtblkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen);
1432
		args->rmtvaluelen = args->valuelen;
Linus Torvalds's avatar
Linus Torvalds committed
1433
	}
1434
	xfs_trans_log_buf(args->trans, bp,
1435
	     XFS_DA_LOGRANGE(leaf, xfs_attr3_leaf_name(leaf, args->index),
Linus Torvalds's avatar
Linus Torvalds committed
1436 1437 1438 1439 1440
				   xfs_attr_leaf_entsize(leaf, args->index)));

	/*
	 * Update the control info for this leaf node
	 */
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
	if (be16_to_cpu(entry->nameidx) < ichdr->firstused)
		ichdr->firstused = be16_to_cpu(entry->nameidx);

	ASSERT(ichdr->firstused >= ichdr->count * sizeof(xfs_attr_leaf_entry_t)
					+ xfs_attr3_leaf_hdr_size(leaf));
	tmp = (ichdr->count - 1) * sizeof(xfs_attr_leaf_entry_t)
					+ xfs_attr3_leaf_hdr_size(leaf);

	for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
		if (ichdr->freemap[i].base == tmp) {
			ichdr->freemap[i].base += sizeof(xfs_attr_leaf_entry_t);
			ichdr->freemap[i].size -= sizeof(xfs_attr_leaf_entry_t);
Linus Torvalds's avatar
Linus Torvalds committed
1453 1454
		}
	}
1455 1456
	ichdr->usedbytes += xfs_attr_leaf_entsize(leaf, args->index);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1457 1458 1459 1460 1461 1462
}

/*
 * Garbage collect a leaf attribute list block by copying it to a new buffer.
 */
STATIC void
1463
xfs_attr3_leaf_compact(
1464
	struct xfs_da_args	*args,
1465
	struct xfs_attr3_icleaf_hdr *ichdr_dst,
1466
	struct xfs_buf		*bp)
Linus Torvalds's avatar
Linus Torvalds committed
1467
{
1468 1469 1470
	struct xfs_attr_leafblock *leaf_src;
	struct xfs_attr_leafblock *leaf_dst;
	struct xfs_attr3_icleaf_hdr ichdr_src;
1471 1472 1473 1474
	struct xfs_trans	*trans = args->trans;
	char			*tmpbuffer;

	trace_xfs_attr_leaf_compact(args);
Linus Torvalds's avatar
Linus Torvalds committed
1475

1476
	tmpbuffer = kmem_alloc(args->geo->blksize, 0);
1477 1478
	memcpy(tmpbuffer, bp->b_addr, args->geo->blksize);
	memset(bp->b_addr, 0, args->geo->blksize);
1479 1480
	leaf_src = (xfs_attr_leafblock_t *)tmpbuffer;
	leaf_dst = bp->b_addr;
Linus Torvalds's avatar
Linus Torvalds committed
1481 1482

	/*
1483 1484 1485
	 * Copy the on-disk header back into the destination buffer to ensure
	 * all the information in the header that is not part of the incore
	 * header structure is preserved.
Linus Torvalds's avatar
Linus Torvalds committed
1486
	 */
1487 1488 1489 1490
	memcpy(bp->b_addr, tmpbuffer, xfs_attr3_leaf_hdr_size(leaf_src));

	/* Initialise the incore headers */
	ichdr_src = *ichdr_dst;	/* struct copy */
1491
	ichdr_dst->firstused = args->geo->blksize;
1492 1493 1494 1495 1496 1497 1498 1499
	ichdr_dst->usedbytes = 0;
	ichdr_dst->count = 0;
	ichdr_dst->holes = 0;
	ichdr_dst->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_src);
	ichdr_dst->freemap[0].size = ichdr_dst->firstused -
						ichdr_dst->freemap[0].base;

	/* write the header back to initialise the underlying buffer */
1500
	xfs_attr3_leaf_hdr_to_disk(args->geo, leaf_dst, ichdr_dst);
Linus Torvalds's avatar
Linus Torvalds committed
1501 1502 1503 1504 1505

	/*
	 * Copy all entry's in the same (sorted) order,
	 * but allocate name/value pairs packed and in sequence.
	 */
1506 1507
	xfs_attr3_leaf_moveents(args, leaf_src, &ichdr_src, 0,
				leaf_dst, ichdr_dst, 0, ichdr_src.count);
1508 1509 1510 1511
	/*
	 * this logs the entire buffer, but the caller must write the header
	 * back to the buffer when it is finished modifying it.
	 */
1512
	xfs_trans_log_buf(trans, bp, 0, args->geo->blksize - 1);
Linus Torvalds's avatar
Linus Torvalds committed
1513

1514
	kmem_free(tmpbuffer);
Linus Torvalds's avatar
Linus Torvalds committed
1515 1516
}

1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549
/*
 * Compare two leaf blocks "order".
 * Return 0 unless leaf2 should go before leaf1.
 */
static int
xfs_attr3_leaf_order(
	struct xfs_buf	*leaf1_bp,
	struct xfs_attr3_icleaf_hdr *leaf1hdr,
	struct xfs_buf	*leaf2_bp,
	struct xfs_attr3_icleaf_hdr *leaf2hdr)
{
	struct xfs_attr_leaf_entry *entries1;
	struct xfs_attr_leaf_entry *entries2;

	entries1 = xfs_attr3_leaf_entryp(leaf1_bp->b_addr);
	entries2 = xfs_attr3_leaf_entryp(leaf2_bp->b_addr);
	if (leaf1hdr->count > 0 && leaf2hdr->count > 0 &&
	    ((be32_to_cpu(entries2[0].hashval) <
	      be32_to_cpu(entries1[0].hashval)) ||
	     (be32_to_cpu(entries2[leaf2hdr->count - 1].hashval) <
	      be32_to_cpu(entries1[leaf1hdr->count - 1].hashval)))) {
		return 1;
	}
	return 0;
}

int
xfs_attr_leaf_order(
	struct xfs_buf	*leaf1_bp,
	struct xfs_buf	*leaf2_bp)
{
	struct xfs_attr3_icleaf_hdr ichdr1;
	struct xfs_attr3_icleaf_hdr ichdr2;
1550
	struct xfs_mount *mp = leaf1_bp->b_mount;
1551

1552 1553
	xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr1, leaf1_bp->b_addr);
	xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr2, leaf2_bp->b_addr);
1554 1555 1556
	return xfs_attr3_leaf_order(leaf1_bp, &ichdr1, leaf2_bp, &ichdr2);
}

Linus Torvalds's avatar
Linus Torvalds committed
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569
/*
 * Redistribute the attribute list entries between two leaf nodes,
 * taking into account the size of the new entry.
 *
 * NOTE: if new block is empty, then it will get the upper half of the
 * old block.  At present, all (one) callers pass in an empty second block.
 *
 * This code adjusts the args->index/blkno and args->index2/blkno2 fields
 * to match what it is doing in splitting the attribute leaf block.  Those
 * values are used in "atomic rename" operations on attributes.  Note that
 * the "new" and "old" values can end up in different blocks.
 */
STATIC void
1570 1571 1572 1573
xfs_attr3_leaf_rebalance(
	struct xfs_da_state	*state,
	struct xfs_da_state_blk	*blk1,
	struct xfs_da_state_blk	*blk2)
Linus Torvalds's avatar
Linus Torvalds committed
1574
{
1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586
	struct xfs_da_args	*args;
	struct xfs_attr_leafblock *leaf1;
	struct xfs_attr_leafblock *leaf2;
	struct xfs_attr3_icleaf_hdr ichdr1;
	struct xfs_attr3_icleaf_hdr ichdr2;
	struct xfs_attr_leaf_entry *entries1;
	struct xfs_attr_leaf_entry *entries2;
	int			count;
	int			totallen;
	int			max;
	int			space;
	int			swap;
Linus Torvalds's avatar
Linus Torvalds committed
1587 1588 1589 1590 1591 1592

	/*
	 * Set up environment.
	 */
	ASSERT(blk1->magic == XFS_ATTR_LEAF_MAGIC);
	ASSERT(blk2->magic == XFS_ATTR_LEAF_MAGIC);
1593 1594
	leaf1 = blk1->bp->b_addr;
	leaf2 = blk2->bp->b_addr;
1595 1596
	xfs_attr3_leaf_hdr_from_disk(state->args->geo, &ichdr1, leaf1);
	xfs_attr3_leaf_hdr_from_disk(state->args->geo, &ichdr2, leaf2);
1597
	ASSERT(ichdr2.count == 0);
Linus Torvalds's avatar
Linus Torvalds committed
1598 1599
	args = state->args;

1600 1601
	trace_xfs_attr_leaf_rebalance(args);

Linus Torvalds's avatar
Linus Torvalds committed
1602 1603 1604 1605 1606 1607 1608
	/*
	 * Check ordering of blocks, reverse if it makes things simpler.
	 *
	 * NOTE: Given that all (current) callers pass in an empty
	 * second block, this code should never set "swap".
	 */
	swap = 0;
1609
	if (xfs_attr3_leaf_order(blk1->bp, &ichdr1, blk2->bp, &ichdr2)) {
1610
		swap(blk1, blk2);
1611

1612 1613
		/* swap structures rather than reconverting them */
		swap(ichdr1, ichdr2);
1614

1615 1616
		leaf1 = blk1->bp->b_addr;
		leaf2 = blk2->bp->b_addr;
Linus Torvalds's avatar
Linus Torvalds committed
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627
		swap = 1;
	}

	/*
	 * Examine entries until we reduce the absolute difference in
	 * byte usage between the two blocks to a minimum.  Then get
	 * the direction to copy and the number of elements to move.
	 *
	 * "inleaf" is true if the new entry should be inserted into blk1.
	 * If "swap" is also true, then reverse the sense of "inleaf".
	 */
1628 1629 1630
	state->inleaf = xfs_attr3_leaf_figure_balance(state, blk1, &ichdr1,
						      blk2, &ichdr2,
						      &count, &totallen);
Linus Torvalds's avatar
Linus Torvalds committed
1631 1632 1633 1634 1635 1636
	if (swap)
		state->inleaf = !state->inleaf;

	/*
	 * Move any entries required from leaf to leaf:
	 */
1637
	if (count < ichdr1.count) {
Linus Torvalds's avatar
Linus Torvalds committed
1638 1639 1640 1641
		/*
		 * Figure the total bytes to be added to the destination leaf.
		 */
		/* number entries being moved */
1642 1643
		count = ichdr1.count - count;
		space  = ichdr1.usedbytes - totallen;
Linus Torvalds's avatar
Linus Torvalds committed
1644 1645 1646 1647 1648
		space += count * sizeof(xfs_attr_leaf_entry_t);

		/*
		 * leaf2 is the destination, compact it if it looks tight.
		 */
1649 1650
		max  = ichdr2.firstused - xfs_attr3_leaf_hdr_size(leaf1);
		max -= ichdr2.count * sizeof(xfs_attr_leaf_entry_t);
1651
		if (space > max)
1652
			xfs_attr3_leaf_compact(args, &ichdr2, blk2->bp);
Linus Torvalds's avatar
Linus Torvalds committed
1653 1654 1655 1656

		/*
		 * Move high entries from leaf1 to low end of leaf2.
		 */
1657 1658
		xfs_attr3_leaf_moveents(args, leaf1, &ichdr1,
				ichdr1.count - count, leaf2, &ichdr2, 0, count);
Linus Torvalds's avatar
Linus Torvalds committed
1659

1660
	} else if (count > ichdr1.count) {
Linus Torvalds's avatar
Linus Torvalds committed
1661 1662 1663 1664
		/*
		 * I assert that since all callers pass in an empty
		 * second buffer, this code should never execute.
		 */
1665
		ASSERT(0);
Linus Torvalds's avatar
Linus Torvalds committed
1666 1667 1668 1669 1670

		/*
		 * Figure the total bytes to be added to the destination leaf.
		 */
		/* number entries being moved */
1671 1672
		count -= ichdr1.count;
		space  = totallen - ichdr1.usedbytes;
Linus Torvalds's avatar
Linus Torvalds committed
1673 1674 1675 1676 1677
		space += count * sizeof(xfs_attr_leaf_entry_t);

		/*
		 * leaf1 is the destination, compact it if it looks tight.
		 */
1678 1679
		max  = ichdr1.firstused - xfs_attr3_leaf_hdr_size(leaf1);
		max -= ichdr1.count * sizeof(xfs_attr_leaf_entry_t);
1680
		if (space > max)
1681
			xfs_attr3_leaf_compact(args, &ichdr1, blk1->bp);
Linus Torvalds's avatar
Linus Torvalds committed
1682 1683 1684 1685

		/*
		 * Move low entries from leaf2 to high end of leaf1.
		 */
1686 1687
		xfs_attr3_leaf_moveents(args, leaf2, &ichdr2, 0, leaf1, &ichdr1,
					ichdr1.count, count);
Linus Torvalds's avatar
Linus Torvalds committed
1688 1689
	}

1690 1691
	xfs_attr3_leaf_hdr_to_disk(state->args->geo, leaf1, &ichdr1);
	xfs_attr3_leaf_hdr_to_disk(state->args->geo, leaf2, &ichdr2);
1692 1693
	xfs_trans_log_buf(args->trans, blk1->bp, 0, args->geo->blksize - 1);
	xfs_trans_log_buf(args->trans, blk2->bp, 0, args->geo->blksize - 1);
1694

Linus Torvalds's avatar
Linus Torvalds committed
1695 1696 1697
	/*
	 * Copy out last hashval in each block for B-tree code.
	 */
1698 1699 1700 1701
	entries1 = xfs_attr3_leaf_entryp(leaf1);
	entries2 = xfs_attr3_leaf_entryp(leaf2);
	blk1->hashval = be32_to_cpu(entries1[ichdr1.count - 1].hashval);
	blk2->hashval = be32_to_cpu(entries2[ichdr2.count - 1].hashval);
Linus Torvalds's avatar
Linus Torvalds committed
1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714

	/*
	 * Adjust the expected index for insertion.
	 * NOTE: this code depends on the (current) situation that the
	 * second block was originally empty.
	 *
	 * If the insertion point moved to the 2nd block, we must adjust
	 * the index.  We must also track the entry just following the
	 * new entry for use in an "atomic rename" operation, that entry
	 * is always the "old" entry and the "new" entry is what we are
	 * inserting.  The index/blkno fields refer to the "old" entry,
	 * while the index2/blkno2 fields refer to the "new" entry.
	 */
1715
	if (blk1->index > ichdr1.count) {
Linus Torvalds's avatar
Linus Torvalds committed
1716
		ASSERT(state->inleaf == 0);
1717
		blk2->index = blk1->index - ichdr1.count;
Linus Torvalds's avatar
Linus Torvalds committed
1718 1719
		args->index = args->index2 = blk2->index;
		args->blkno = args->blkno2 = blk2->blkno;
1720
	} else if (blk1->index == ichdr1.count) {
Linus Torvalds's avatar
Linus Torvalds committed
1721 1722 1723 1724 1725 1726
		if (state->inleaf) {
			args->index = blk1->index;
			args->blkno = blk1->blkno;
			args->index2 = 0;
			args->blkno2 = blk2->blkno;
		} else {
1727 1728 1729 1730 1731
			/*
			 * On a double leaf split, the original attr location
			 * is already stored in blkno2/index2, so don't
			 * overwrite it overwise we corrupt the tree.
			 */
1732
			blk2->index = blk1->index - ichdr1.count;
1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
			args->index = blk2->index;
			args->blkno = blk2->blkno;
			if (!state->extravalid) {
				/*
				 * set the new attr location to match the old
				 * one and let the higher level split code
				 * decide where in the leaf to place it.
				 */
				args->index2 = blk2->index;
				args->blkno2 = blk2->blkno;
			}
Linus Torvalds's avatar
Linus Torvalds committed
1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759
		}
	} else {
		ASSERT(state->inleaf == 1);
		args->index = args->index2 = blk1->index;
		args->blkno = args->blkno2 = blk1->blkno;
	}
}

/*
 * Examine entries until we reduce the absolute difference in
 * byte usage between the two blocks to a minimum.
 * GROT: Is this really necessary?  With other than a 512 byte blocksize,
 * GROT: there will always be enough room in either block for a new entry.
 * GROT: Do a double-split for this case?
 */
STATIC int
1760 1761 1762 1763 1764 1765 1766 1767
xfs_attr3_leaf_figure_balance(
	struct xfs_da_state		*state,
	struct xfs_da_state_blk		*blk1,
	struct xfs_attr3_icleaf_hdr	*ichdr1,
	struct xfs_da_state_blk		*blk2,
	struct xfs_attr3_icleaf_hdr	*ichdr2,
	int				*countarg,
	int				*usedbytesarg)
Linus Torvalds's avatar
Linus Torvalds committed
1768
{
1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779
	struct xfs_attr_leafblock	*leaf1 = blk1->bp->b_addr;
	struct xfs_attr_leafblock	*leaf2 = blk2->bp->b_addr;
	struct xfs_attr_leaf_entry	*entry;
	int				count;
	int				max;
	int				index;
	int				totallen = 0;
	int				half;
	int				lastdelta;
	int				foundit = 0;
	int				tmp;
Linus Torvalds's avatar
Linus Torvalds committed
1780 1781 1782 1783 1784

	/*
	 * Examine entries until we reduce the absolute difference in
	 * byte usage between the two blocks to a minimum.
	 */
1785 1786 1787
	max = ichdr1->count + ichdr2->count;
	half = (max + 1) * sizeof(*entry);
	half += ichdr1->usedbytes + ichdr2->usedbytes +
1788
			xfs_attr_leaf_newentsize(state->args, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
1789
	half /= 2;
1790
	lastdelta = state->args->geo->blksize;
1791
	entry = xfs_attr3_leaf_entryp(leaf1);
Linus Torvalds's avatar
Linus Torvalds committed
1792 1793 1794 1795 1796 1797 1798 1799
	for (count = index = 0; count < max; entry++, index++, count++) {

#define XFS_ATTR_ABS(A)	(((A) < 0) ? -(A) : (A))
		/*
		 * The new entry is in the first block, account for it.
		 */
		if (count == blk1->index) {
			tmp = totallen + sizeof(*entry) +
1800
				xfs_attr_leaf_newentsize(state->args, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810
			if (XFS_ATTR_ABS(half - tmp) > lastdelta)
				break;
			lastdelta = XFS_ATTR_ABS(half - tmp);
			totallen = tmp;
			foundit = 1;
		}

		/*
		 * Wrap around into the second block if necessary.
		 */
1811
		if (count == ichdr1->count) {
Linus Torvalds's avatar
Linus Torvalds committed
1812
			leaf1 = leaf2;
1813
			entry = xfs_attr3_leaf_entryp(leaf1);
Linus Torvalds's avatar
Linus Torvalds committed
1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835
			index = 0;
		}

		/*
		 * Figure out if next leaf entry would be too much.
		 */
		tmp = totallen + sizeof(*entry) + xfs_attr_leaf_entsize(leaf1,
									index);
		if (XFS_ATTR_ABS(half - tmp) > lastdelta)
			break;
		lastdelta = XFS_ATTR_ABS(half - tmp);
		totallen = tmp;
#undef XFS_ATTR_ABS
	}

	/*
	 * Calculate the number of usedbytes that will end up in lower block.
	 * If new entry not in lower block, fix up the count.
	 */
	totallen -= count * sizeof(*entry);
	if (foundit) {
		totallen -= sizeof(*entry) +
1836
				xfs_attr_leaf_newentsize(state->args, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
1837 1838 1839 1840
	}

	*countarg = count;
	*usedbytesarg = totallen;
1841
	return foundit;
Linus Torvalds's avatar
Linus Torvalds committed
1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859
}

/*========================================================================
 * Routines used for shrinking the Btree.
 *========================================================================*/

/*
 * Check a leaf block and its neighbors to see if the block should be
 * collapsed into one or the other neighbor.  Always keep the block
 * with the smaller block number.
 * If the current block is over 50% full, don't try to join it, return 0.
 * If the block is empty, fill in the state structure and return 2.
 * If it can be collapsed, fill in the state structure and return 1.
 * If nothing can be done, return 0.
 *
 * GROT: allow for INCOMPLETE entries in calculation.
 */
int
1860 1861 1862
xfs_attr3_leaf_toosmall(
	struct xfs_da_state	*state,
	int			*action)
Linus Torvalds's avatar
Linus Torvalds committed
1863
{
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873
	struct xfs_attr_leafblock *leaf;
	struct xfs_da_state_blk	*blk;
	struct xfs_attr3_icleaf_hdr ichdr;
	struct xfs_buf		*bp;
	xfs_dablk_t		blkno;
	int			bytes;
	int			forward;
	int			error;
	int			retval;
	int			i;
Linus Torvalds's avatar
Linus Torvalds committed
1874

1875 1876
	trace_xfs_attr_leaf_toosmall(state->args);

Linus Torvalds's avatar
Linus Torvalds committed
1877 1878 1879 1880 1881 1882
	/*
	 * Check for the degenerate case of the block being over 50% full.
	 * If so, it's not worth even looking to see if we might be able
	 * to coalesce with a sibling.
	 */
	blk = &state->path.blk[ state->path.active-1 ];
1883
	leaf = blk->bp->b_addr;
1884
	xfs_attr3_leaf_hdr_from_disk(state->args->geo, &ichdr, leaf);
1885 1886 1887
	bytes = xfs_attr3_leaf_hdr_size(leaf) +
		ichdr.count * sizeof(xfs_attr_leaf_entry_t) +
		ichdr.usedbytes;
1888
	if (bytes > (state->args->geo->blksize >> 1)) {
Linus Torvalds's avatar
Linus Torvalds committed
1889
		*action = 0;	/* blk over 50%, don't try to join */
Eric Sandeen's avatar
Eric Sandeen committed
1890
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1891 1892 1893 1894 1895
	}

	/*
	 * Check for the degenerate case of the block being empty.
	 * If the block is empty, we'll simply delete it, no need to
1896
	 * coalesce it with a sibling block.  We choose (arbitrarily)
Linus Torvalds's avatar
Linus Torvalds committed
1897 1898
	 * to merge with the forward block unless it is NULL.
	 */
1899
	if (ichdr.count == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
1900 1901 1902 1903
		/*
		 * Make altpath point to the block we want to keep and
		 * path point to the block we want to drop (this one).
		 */
1904
		forward = (ichdr.forw != 0);
Linus Torvalds's avatar
Linus Torvalds committed
1905
		memcpy(&state->altpath, &state->path, sizeof(state->path));
1906
		error = xfs_da3_path_shift(state, &state->altpath, forward,
Linus Torvalds's avatar
Linus Torvalds committed
1907 1908
						 0, &retval);
		if (error)
Eric Sandeen's avatar
Eric Sandeen committed
1909
			return error;
Linus Torvalds's avatar
Linus Torvalds committed
1910 1911 1912 1913 1914
		if (retval) {
			*action = 0;
		} else {
			*action = 2;
		}
1915
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1916 1917 1918 1919 1920 1921 1922 1923 1924 1925
	}

	/*
	 * Examine each sibling block to see if we can coalesce with
	 * at least 25% free space to spare.  We need to figure out
	 * whether to merge with the forward or the backward block.
	 * We prefer coalescing with the lower numbered sibling so as
	 * to shrink an attribute list over time.
	 */
	/* start with smaller blk num */
1926
	forward = ichdr.forw < ichdr.back;
Linus Torvalds's avatar
Linus Torvalds committed
1927
	for (i = 0; i < 2; forward = !forward, i++) {
1928
		struct xfs_attr3_icleaf_hdr ichdr2;
Linus Torvalds's avatar
Linus Torvalds committed
1929
		if (forward)
1930
			blkno = ichdr.forw;
Linus Torvalds's avatar
Linus Torvalds committed
1931
		else
1932
			blkno = ichdr.back;
Linus Torvalds's avatar
Linus Torvalds committed
1933 1934
		if (blkno == 0)
			continue;
1935
		error = xfs_attr3_leaf_read(state->args->trans, state->args->dp,
1936
					blkno, -1, &bp);
Linus Torvalds's avatar
Linus Torvalds committed
1937
		if (error)
Eric Sandeen's avatar
Eric Sandeen committed
1938
			return error;
Linus Torvalds's avatar
Linus Torvalds committed
1939

1940
		xfs_attr3_leaf_hdr_from_disk(state->args->geo, &ichdr2, bp->b_addr);
1941

1942 1943
		bytes = state->args->geo->blksize -
			(state->args->geo->blksize >> 2) -
1944 1945 1946 1947 1948
			ichdr.usedbytes - ichdr2.usedbytes -
			((ichdr.count + ichdr2.count) *
					sizeof(xfs_attr_leaf_entry_t)) -
			xfs_attr3_leaf_hdr_size(leaf);

1949
		xfs_trans_brelse(state->args->trans, bp);
Linus Torvalds's avatar
Linus Torvalds committed
1950 1951 1952 1953 1954
		if (bytes >= 0)
			break;	/* fits with at least 25% to spare */
	}
	if (i >= 2) {
		*action = 0;
Eric Sandeen's avatar
Eric Sandeen committed
1955
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1956 1957 1958 1959 1960 1961 1962 1963
	}

	/*
	 * Make altpath point to the block we want to keep (the lower
	 * numbered block) and path point to the block we want to drop.
	 */
	memcpy(&state->altpath, &state->path, sizeof(state->path));
	if (blkno < blk->blkno) {
1964
		error = xfs_da3_path_shift(state, &state->altpath, forward,
Linus Torvalds's avatar
Linus Torvalds committed
1965 1966
						 0, &retval);
	} else {
1967
		error = xfs_da3_path_shift(state, &state->path, forward,
Linus Torvalds's avatar
Linus Torvalds committed
1968 1969 1970
						 0, &retval);
	}
	if (error)
Eric Sandeen's avatar
Eric Sandeen committed
1971
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
1972 1973 1974 1975 1976
	if (retval) {
		*action = 0;
	} else {
		*action = 1;
	}
Eric Sandeen's avatar
Eric Sandeen committed
1977
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1978 1979 1980 1981 1982 1983 1984 1985 1986
}

/*
 * Remove a name from the leaf attribute list structure.
 *
 * Return 1 if leaf is less than 37% full, 0 if >= 37% full.
 * If two leaves are 37% full, when combined they will leave 25% free.
 */
int
1987 1988 1989
xfs_attr3_leaf_remove(
	struct xfs_buf		*bp,
	struct xfs_da_args	*args)
Linus Torvalds's avatar
Linus Torvalds committed
1990
{
1991 1992 1993 1994 1995 1996 1997 1998 1999 2000
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr3_icleaf_hdr ichdr;
	struct xfs_attr_leaf_entry *entry;
	int			before;
	int			after;
	int			smallest;
	int			entsize;
	int			tablesize;
	int			tmp;
	int			i;
Linus Torvalds's avatar
Linus Torvalds committed
2001

2002 2003
	trace_xfs_attr_leaf_remove(args);

2004
	leaf = bp->b_addr;
2005
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
2006

2007
	ASSERT(ichdr.count > 0 && ichdr.count < args->geo->blksize / 8);
2008 2009 2010 2011 2012 2013 2014
	ASSERT(args->index >= 0 && args->index < ichdr.count);
	ASSERT(ichdr.firstused >= ichdr.count * sizeof(*entry) +
					xfs_attr3_leaf_hdr_size(leaf));

	entry = &xfs_attr3_leaf_entryp(leaf)[args->index];

	ASSERT(be16_to_cpu(entry->nameidx) >= ichdr.firstused);
2015
	ASSERT(be16_to_cpu(entry->nameidx) < args->geo->blksize);
Linus Torvalds's avatar
Linus Torvalds committed
2016 2017 2018 2019 2020 2021 2022

	/*
	 * Scan through free region table:
	 *    check for adjacency of free'd entry with an existing one,
	 *    find smallest free region in case we need to replace it,
	 *    adjust any map that borders the entry table,
	 */
2023 2024 2025
	tablesize = ichdr.count * sizeof(xfs_attr_leaf_entry_t)
					+ xfs_attr3_leaf_hdr_size(leaf);
	tmp = ichdr.freemap[0].size;
Linus Torvalds's avatar
Linus Torvalds committed
2026 2027 2028
	before = after = -1;
	smallest = XFS_ATTR_LEAF_MAPSIZE - 1;
	entsize = xfs_attr_leaf_entsize(leaf, args->index);
2029
	for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
2030 2031
		ASSERT(ichdr.freemap[i].base < args->geo->blksize);
		ASSERT(ichdr.freemap[i].size < args->geo->blksize);
2032 2033 2034
		if (ichdr.freemap[i].base == tablesize) {
			ichdr.freemap[i].base -= sizeof(xfs_attr_leaf_entry_t);
			ichdr.freemap[i].size += sizeof(xfs_attr_leaf_entry_t);
Linus Torvalds's avatar
Linus Torvalds committed
2035 2036
		}

2037 2038
		if (ichdr.freemap[i].base + ichdr.freemap[i].size ==
				be16_to_cpu(entry->nameidx)) {
Linus Torvalds's avatar
Linus Torvalds committed
2039
			before = i;
2040 2041
		} else if (ichdr.freemap[i].base ==
				(be16_to_cpu(entry->nameidx) + entsize)) {
Linus Torvalds's avatar
Linus Torvalds committed
2042
			after = i;
2043 2044
		} else if (ichdr.freemap[i].size < tmp) {
			tmp = ichdr.freemap[i].size;
Linus Torvalds's avatar
Linus Torvalds committed
2045 2046 2047 2048 2049 2050 2051 2052 2053 2054
			smallest = i;
		}
	}

	/*
	 * Coalesce adjacent freemap regions,
	 * or replace the smallest region.
	 */
	if ((before >= 0) || (after >= 0)) {
		if ((before >= 0) && (after >= 0)) {
2055 2056 2057 2058
			ichdr.freemap[before].size += entsize;
			ichdr.freemap[before].size += ichdr.freemap[after].size;
			ichdr.freemap[after].base = 0;
			ichdr.freemap[after].size = 0;
Linus Torvalds's avatar
Linus Torvalds committed
2059
		} else if (before >= 0) {
2060
			ichdr.freemap[before].size += entsize;
Linus Torvalds's avatar
Linus Torvalds committed
2061
		} else {
2062 2063
			ichdr.freemap[after].base = be16_to_cpu(entry->nameidx);
			ichdr.freemap[after].size += entsize;
Linus Torvalds's avatar
Linus Torvalds committed
2064 2065 2066 2067 2068
		}
	} else {
		/*
		 * Replace smallest region (if it is smaller than free'd entry)
		 */
2069 2070 2071
		if (ichdr.freemap[smallest].size < entsize) {
			ichdr.freemap[smallest].base = be16_to_cpu(entry->nameidx);
			ichdr.freemap[smallest].size = entsize;
Linus Torvalds's avatar
Linus Torvalds committed
2072 2073 2074 2075 2076 2077
		}
	}

	/*
	 * Did we remove the first entry?
	 */
2078
	if (be16_to_cpu(entry->nameidx) == ichdr.firstused)
Linus Torvalds's avatar
Linus Torvalds committed
2079 2080 2081 2082 2083 2084 2085
		smallest = 1;
	else
		smallest = 0;

	/*
	 * Compress the remaining entries and zero out the removed stuff.
	 */
2086 2087
	memset(xfs_attr3_leaf_name(leaf, args->index), 0, entsize);
	ichdr.usedbytes -= entsize;
2088
	xfs_trans_log_buf(args->trans, bp,
2089
	     XFS_DA_LOGRANGE(leaf, xfs_attr3_leaf_name(leaf, args->index),
Linus Torvalds's avatar
Linus Torvalds committed
2090 2091
				   entsize));

2092 2093 2094
	tmp = (ichdr.count - args->index) * sizeof(xfs_attr_leaf_entry_t);
	memmove(entry, entry + 1, tmp);
	ichdr.count--;
2095
	xfs_trans_log_buf(args->trans, bp,
2096 2097 2098 2099
	    XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(xfs_attr_leaf_entry_t)));

	entry = &xfs_attr3_leaf_entryp(leaf)[ichdr.count];
	memset(entry, 0, sizeof(xfs_attr_leaf_entry_t));
Linus Torvalds's avatar
Linus Torvalds committed
2100 2101 2102 2103 2104 2105 2106 2107

	/*
	 * If we removed the first entry, re-find the first used byte
	 * in the name area.  Note that if the entry was the "firstused",
	 * then we don't have a "hole" in our block resulting from
	 * removing the name.
	 */
	if (smallest) {
2108
		tmp = args->geo->blksize;
2109 2110 2111
		entry = xfs_attr3_leaf_entryp(leaf);
		for (i = ichdr.count - 1; i >= 0; entry++, i--) {
			ASSERT(be16_to_cpu(entry->nameidx) >= ichdr.firstused);
2112
			ASSERT(be16_to_cpu(entry->nameidx) < args->geo->blksize);
2113 2114 2115

			if (be16_to_cpu(entry->nameidx) < tmp)
				tmp = be16_to_cpu(entry->nameidx);
Linus Torvalds's avatar
Linus Torvalds committed
2116
		}
2117
		ichdr.firstused = tmp;
2118
		ASSERT(ichdr.firstused != 0);
Linus Torvalds's avatar
Linus Torvalds committed
2119
	} else {
2120
		ichdr.holes = 1;	/* mark as needing compaction */
Linus Torvalds's avatar
Linus Torvalds committed
2121
	}
2122
	xfs_attr3_leaf_hdr_to_disk(args->geo, leaf, &ichdr);
2123
	xfs_trans_log_buf(args->trans, bp,
2124 2125
			  XFS_DA_LOGRANGE(leaf, &leaf->hdr,
					  xfs_attr3_leaf_hdr_size(leaf)));
Linus Torvalds's avatar
Linus Torvalds committed
2126 2127 2128 2129 2130

	/*
	 * Check if leaf is less than 50% full, caller may want to
	 * "join" the leaf with a sibling if so.
	 */
2131 2132 2133
	tmp = ichdr.usedbytes + xfs_attr3_leaf_hdr_size(leaf) +
	      ichdr.count * sizeof(xfs_attr_leaf_entry_t);

2134
	return tmp < args->geo->magicpct; /* leaf is < 37% full */
Linus Torvalds's avatar
Linus Torvalds committed
2135 2136 2137 2138 2139 2140
}

/*
 * Move all the attribute list entries from drop_leaf into save_leaf.
 */
void
2141 2142 2143 2144
xfs_attr3_leaf_unbalance(
	struct xfs_da_state	*state,
	struct xfs_da_state_blk	*drop_blk,
	struct xfs_da_state_blk	*save_blk)
Linus Torvalds's avatar
Linus Torvalds committed
2145
{
2146 2147 2148 2149 2150
	struct xfs_attr_leafblock *drop_leaf = drop_blk->bp->b_addr;
	struct xfs_attr_leafblock *save_leaf = save_blk->bp->b_addr;
	struct xfs_attr3_icleaf_hdr drophdr;
	struct xfs_attr3_icleaf_hdr savehdr;
	struct xfs_attr_leaf_entry *entry;
Linus Torvalds's avatar
Linus Torvalds committed
2151

2152 2153
	trace_xfs_attr_leaf_unbalance(state->args);

2154 2155
	drop_leaf = drop_blk->bp->b_addr;
	save_leaf = save_blk->bp->b_addr;
2156 2157
	xfs_attr3_leaf_hdr_from_disk(state->args->geo, &drophdr, drop_leaf);
	xfs_attr3_leaf_hdr_from_disk(state->args->geo, &savehdr, save_leaf);
2158
	entry = xfs_attr3_leaf_entryp(drop_leaf);
Linus Torvalds's avatar
Linus Torvalds committed
2159 2160 2161 2162

	/*
	 * Save last hashval from dying block for later Btree fixup.
	 */
2163
	drop_blk->hashval = be32_to_cpu(entry[drophdr.count - 1].hashval);
Linus Torvalds's avatar
Linus Torvalds committed
2164 2165 2166 2167 2168 2169

	/*
	 * Check if we need a temp buffer, or can we do it in place.
	 * Note that we don't check "leaf" for holes because we will
	 * always be dropping it, toosmall() decided that for us already.
	 */
2170
	if (savehdr.holes == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
2171 2172 2173 2174
		/*
		 * dest leaf has no holes, so we add there.  May need
		 * to make some room in the entry array.
		 */
2175 2176
		if (xfs_attr3_leaf_order(save_blk->bp, &savehdr,
					 drop_blk->bp, &drophdr)) {
2177 2178
			xfs_attr3_leaf_moveents(state->args,
						drop_leaf, &drophdr, 0,
2179
						save_leaf, &savehdr, 0,
2180
						drophdr.count);
Linus Torvalds's avatar
Linus Torvalds committed
2181
		} else {
2182 2183
			xfs_attr3_leaf_moveents(state->args,
						drop_leaf, &drophdr, 0,
2184
						save_leaf, &savehdr,
2185
						savehdr.count, drophdr.count);
Linus Torvalds's avatar
Linus Torvalds committed
2186 2187 2188 2189 2190 2191
		}
	} else {
		/*
		 * Destination has holes, so we make a temporary copy
		 * of the leaf and add them both to that.
		 */
2192 2193 2194
		struct xfs_attr_leafblock *tmp_leaf;
		struct xfs_attr3_icleaf_hdr tmphdr;

2195
		tmp_leaf = kmem_zalloc(state->args->geo->blksize, 0);
2196 2197 2198 2199 2200 2201 2202

		/*
		 * Copy the header into the temp leaf so that all the stuff
		 * not in the incore header is present and gets copied back in
		 * once we've moved all the entries.
		 */
		memcpy(tmp_leaf, save_leaf, xfs_attr3_leaf_hdr_size(save_leaf));
2203

2204
		memset(&tmphdr, 0, sizeof(tmphdr));
2205 2206 2207
		tmphdr.magic = savehdr.magic;
		tmphdr.forw = savehdr.forw;
		tmphdr.back = savehdr.back;
2208
		tmphdr.firstused = state->args->geo->blksize;
2209 2210

		/* write the header to the temp buffer to initialise it */
2211
		xfs_attr3_leaf_hdr_to_disk(state->args->geo, tmp_leaf, &tmphdr);
2212

2213 2214
		if (xfs_attr3_leaf_order(save_blk->bp, &savehdr,
					 drop_blk->bp, &drophdr)) {
2215 2216
			xfs_attr3_leaf_moveents(state->args,
						drop_leaf, &drophdr, 0,
2217
						tmp_leaf, &tmphdr, 0,
2218 2219 2220
						drophdr.count);
			xfs_attr3_leaf_moveents(state->args,
						save_leaf, &savehdr, 0,
2221
						tmp_leaf, &tmphdr, tmphdr.count,
2222
						savehdr.count);
Linus Torvalds's avatar
Linus Torvalds committed
2223
		} else {
2224 2225
			xfs_attr3_leaf_moveents(state->args,
						save_leaf, &savehdr, 0,
2226
						tmp_leaf, &tmphdr, 0,
2227 2228 2229
						savehdr.count);
			xfs_attr3_leaf_moveents(state->args,
						drop_leaf, &drophdr, 0,
2230
						tmp_leaf, &tmphdr, tmphdr.count,
2231
						drophdr.count);
Linus Torvalds's avatar
Linus Torvalds committed
2232
		}
2233
		memcpy(save_leaf, tmp_leaf, state->args->geo->blksize);
2234 2235
		savehdr = tmphdr; /* struct copy */
		kmem_free(tmp_leaf);
Linus Torvalds's avatar
Linus Torvalds committed
2236 2237
	}

2238
	xfs_attr3_leaf_hdr_to_disk(state->args->geo, save_leaf, &savehdr);
2239
	xfs_trans_log_buf(state->args->trans, save_blk->bp, 0,
2240
					   state->args->geo->blksize - 1);
Linus Torvalds's avatar
Linus Torvalds committed
2241 2242 2243 2244

	/*
	 * Copy out last hashval in each block for B-tree code.
	 */
2245 2246
	entry = xfs_attr3_leaf_entryp(save_leaf);
	save_blk->hashval = be32_to_cpu(entry[savehdr.count - 1].hashval);
Linus Torvalds's avatar
Linus Torvalds committed
2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266
}

/*========================================================================
 * Routines used for finding things in the Btree.
 *========================================================================*/

/*
 * Look up a name in a leaf attribute list structure.
 * This is the internal routine, it uses the caller's buffer.
 *
 * Note that duplicate keys are allowed, but only check within the
 * current leaf node.  The Btree code must check in adjacent leaf nodes.
 *
 * Return in args->index the index into the entry[] array of either
 * the found entry, or where the entry should have been (insert before
 * that entry).
 *
 * Don't change the args->value unless we find the attribute.
 */
int
2267 2268 2269
xfs_attr3_leaf_lookup_int(
	struct xfs_buf		*bp,
	struct xfs_da_args	*args)
Linus Torvalds's avatar
Linus Torvalds committed
2270
{
2271 2272 2273 2274 2275 2276 2277 2278 2279
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr3_icleaf_hdr ichdr;
	struct xfs_attr_leaf_entry *entry;
	struct xfs_attr_leaf_entry *entries;
	struct xfs_attr_leaf_name_local *name_loc;
	struct xfs_attr_leaf_name_remote *name_rmt;
	xfs_dahash_t		hashval;
	int			probe;
	int			span;
Linus Torvalds's avatar
Linus Torvalds committed
2280

2281 2282
	trace_xfs_attr_leaf_lookup(args);

2283
	leaf = bp->b_addr;
2284
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
2285
	entries = xfs_attr3_leaf_entryp(leaf);
2286 2287
	if (ichdr.count >= args->geo->blksize / 8)
		return -EFSCORRUPTED;
Linus Torvalds's avatar
Linus Torvalds committed
2288 2289 2290 2291 2292

	/*
	 * Binary search.  (note: small blocks will skip this loop)
	 */
	hashval = args->hashval;
2293 2294
	probe = span = ichdr.count / 2;
	for (entry = &entries[probe]; span > 4; entry = &entries[probe]) {
Linus Torvalds's avatar
Linus Torvalds committed
2295
		span /= 2;
2296
		if (be32_to_cpu(entry->hashval) < hashval)
Linus Torvalds's avatar
Linus Torvalds committed
2297
			probe += span;
2298
		else if (be32_to_cpu(entry->hashval) > hashval)
Linus Torvalds's avatar
Linus Torvalds committed
2299 2300 2301 2302
			probe -= span;
		else
			break;
	}
2303 2304 2305 2306
	if (!(probe >= 0 && (!ichdr.count || probe < ichdr.count)))
		return -EFSCORRUPTED;
	if (!(span <= 4 || be32_to_cpu(entry->hashval) == hashval))
		return -EFSCORRUPTED;
Linus Torvalds's avatar
Linus Torvalds committed
2307 2308 2309 2310 2311

	/*
	 * Since we may have duplicate hashval's, find the first matching
	 * hashval in the leaf.
	 */
2312
	while (probe > 0 && be32_to_cpu(entry->hashval) >= hashval) {
Linus Torvalds's avatar
Linus Torvalds committed
2313 2314 2315
		entry--;
		probe--;
	}
2316 2317
	while (probe < ichdr.count &&
	       be32_to_cpu(entry->hashval) < hashval) {
Linus Torvalds's avatar
Linus Torvalds committed
2318 2319 2320
		entry++;
		probe++;
	}
2321
	if (probe == ichdr.count || be32_to_cpu(entry->hashval) != hashval) {
Linus Torvalds's avatar
Linus Torvalds committed
2322
		args->index = probe;
2323
		return -ENOATTR;
Linus Torvalds's avatar
Linus Torvalds committed
2324 2325 2326 2327 2328
	}

	/*
	 * Duplicate keys may be present, so search all of them for a match.
	 */
2329
	for (; probe < ichdr.count && (be32_to_cpu(entry->hashval) == hashval);
Linus Torvalds's avatar
Linus Torvalds committed
2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342
			entry++, probe++) {
/*
 * GROT: Add code to remove incomplete entries.
 */
		/*
		 * If we are looking for INCOMPLETE entries, show only those.
		 * If we are looking for complete entries, show only those.
		 */
		if ((args->flags & XFS_ATTR_INCOMPLETE) !=
		    (entry->flags & XFS_ATTR_INCOMPLETE)) {
			continue;
		}
		if (entry->flags & XFS_ATTR_LOCAL) {
2343
			name_loc = xfs_attr3_leaf_name_local(leaf, probe);
Linus Torvalds's avatar
Linus Torvalds committed
2344 2345
			if (name_loc->namelen != args->namelen)
				continue;
2346 2347
			if (memcmp(args->name, name_loc->nameval,
							args->namelen) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2348
				continue;
2349
			if (!xfs_attr_namesp_match(args->flags, entry->flags))
Linus Torvalds's avatar
Linus Torvalds committed
2350 2351
				continue;
			args->index = probe;
2352
			return -EEXIST;
Linus Torvalds's avatar
Linus Torvalds committed
2353
		} else {
2354
			name_rmt = xfs_attr3_leaf_name_remote(leaf, probe);
Linus Torvalds's avatar
Linus Torvalds committed
2355 2356
			if (name_rmt->namelen != args->namelen)
				continue;
2357 2358
			if (memcmp(args->name, name_rmt->name,
							args->namelen) != 0)
Linus Torvalds's avatar
Linus Torvalds committed
2359
				continue;
2360
			if (!xfs_attr_namesp_match(args->flags, entry->flags))
Linus Torvalds's avatar
Linus Torvalds committed
2361 2362
				continue;
			args->index = probe;
2363
			args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen);
2364
			args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
Dave Chinner's avatar
Dave Chinner committed
2365 2366
			args->rmtblkcnt = xfs_attr3_rmt_blocks(
							args->dp->i_mount,
2367
							args->rmtvaluelen);
2368
			return -EEXIST;
Linus Torvalds's avatar
Linus Torvalds committed
2369 2370 2371
		}
	}
	args->index = probe;
2372
	return -ENOATTR;
Linus Torvalds's avatar
Linus Torvalds committed
2373 2374 2375 2376 2377
}

/*
 * Get the value associated with an attribute name from a leaf attribute
 * list structure.
2378 2379 2380 2381
 *
 * If ATTR_KERNOVAL is specified, only the length needs to be returned.
 * Unlike a lookup, we only return an error if the attribute does not
 * exist or we can't retrieve the value.
Linus Torvalds's avatar
Linus Torvalds committed
2382 2383
 */
int
2384 2385 2386
xfs_attr3_leaf_getvalue(
	struct xfs_buf		*bp,
	struct xfs_da_args	*args)
Linus Torvalds's avatar
Linus Torvalds committed
2387
{
2388 2389 2390 2391 2392
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr3_icleaf_hdr ichdr;
	struct xfs_attr_leaf_entry *entry;
	struct xfs_attr_leaf_name_local *name_loc;
	struct xfs_attr_leaf_name_remote *name_rmt;
Linus Torvalds's avatar
Linus Torvalds committed
2393

2394
	leaf = bp->b_addr;
2395
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
2396
	ASSERT(ichdr.count < args->geo->blksize / 8);
2397
	ASSERT(args->index < ichdr.count);
Linus Torvalds's avatar
Linus Torvalds committed
2398

2399
	entry = &xfs_attr3_leaf_entryp(leaf)[args->index];
Linus Torvalds's avatar
Linus Torvalds committed
2400
	if (entry->flags & XFS_ATTR_LOCAL) {
2401
		name_loc = xfs_attr3_leaf_name_local(leaf, args->index);
Linus Torvalds's avatar
Linus Torvalds committed
2402 2403
		ASSERT(name_loc->namelen == args->namelen);
		ASSERT(memcmp(args->name, name_loc->nameval, args->namelen) == 0);
2404 2405 2406
		return xfs_attr_copy_value(args,
					&name_loc->nameval[args->namelen],
					be16_to_cpu(name_loc->valuelen));
2407 2408 2409 2410 2411 2412 2413 2414 2415
	}

	name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index);
	ASSERT(name_rmt->namelen == args->namelen);
	ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0);
	args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen);
	args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
	args->rmtblkcnt = xfs_attr3_rmt_blocks(args->dp->i_mount,
					       args->rmtvaluelen);
2416
	return xfs_attr_copy_value(args, NULL, args->rmtvaluelen);
Linus Torvalds's avatar
Linus Torvalds committed
2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428
}

/*========================================================================
 * Utility routines.
 *========================================================================*/

/*
 * Move the indicated entries from one leaf to another.
 * NOTE: this routine modifies both source and destination leaves.
 */
/*ARGSUSED*/
STATIC void
2429
xfs_attr3_leaf_moveents(
2430
	struct xfs_da_args		*args,
2431 2432 2433 2434 2435 2436
	struct xfs_attr_leafblock	*leaf_s,
	struct xfs_attr3_icleaf_hdr	*ichdr_s,
	int				start_s,
	struct xfs_attr_leafblock	*leaf_d,
	struct xfs_attr3_icleaf_hdr	*ichdr_d,
	int				start_d,
2437
	int				count)
Linus Torvalds's avatar
Linus Torvalds committed
2438
{
2439 2440 2441 2442 2443
	struct xfs_attr_leaf_entry	*entry_s;
	struct xfs_attr_leaf_entry	*entry_d;
	int				desti;
	int				tmp;
	int				i;
Linus Torvalds's avatar
Linus Torvalds committed
2444 2445 2446 2447 2448 2449 2450 2451 2452 2453

	/*
	 * Check for nothing to do.
	 */
	if (count == 0)
		return;

	/*
	 * Set up environment.
	 */
2454 2455 2456
	ASSERT(ichdr_s->magic == XFS_ATTR_LEAF_MAGIC ||
	       ichdr_s->magic == XFS_ATTR3_LEAF_MAGIC);
	ASSERT(ichdr_s->magic == ichdr_d->magic);
2457
	ASSERT(ichdr_s->count > 0 && ichdr_s->count < args->geo->blksize / 8);
2458 2459
	ASSERT(ichdr_s->firstused >= (ichdr_s->count * sizeof(*entry_s))
					+ xfs_attr3_leaf_hdr_size(leaf_s));
2460
	ASSERT(ichdr_d->count < args->geo->blksize / 8);
2461 2462 2463 2464 2465 2466 2467
	ASSERT(ichdr_d->firstused >= (ichdr_d->count * sizeof(*entry_d))
					+ xfs_attr3_leaf_hdr_size(leaf_d));

	ASSERT(start_s < ichdr_s->count);
	ASSERT(start_d <= ichdr_d->count);
	ASSERT(count <= ichdr_s->count);

Linus Torvalds's avatar
Linus Torvalds committed
2468 2469 2470 2471

	/*
	 * Move the entries in the destination leaf up to make a hole?
	 */
2472 2473
	if (start_d < ichdr_d->count) {
		tmp  = ichdr_d->count - start_d;
Linus Torvalds's avatar
Linus Torvalds committed
2474
		tmp *= sizeof(xfs_attr_leaf_entry_t);
2475 2476 2477
		entry_s = &xfs_attr3_leaf_entryp(leaf_d)[start_d];
		entry_d = &xfs_attr3_leaf_entryp(leaf_d)[start_d + count];
		memmove(entry_d, entry_s, tmp);
Linus Torvalds's avatar
Linus Torvalds committed
2478 2479 2480 2481 2482 2483
	}

	/*
	 * Copy all entry's in the same (sorted) order,
	 * but allocate attribute info packed and in sequence.
	 */
2484 2485
	entry_s = &xfs_attr3_leaf_entryp(leaf_s)[start_s];
	entry_d = &xfs_attr3_leaf_entryp(leaf_d)[start_d];
Linus Torvalds's avatar
Linus Torvalds committed
2486 2487
	desti = start_d;
	for (i = 0; i < count; entry_s++, entry_d++, desti++, i++) {
2488
		ASSERT(be16_to_cpu(entry_s->nameidx) >= ichdr_s->firstused);
Linus Torvalds's avatar
Linus Torvalds committed
2489 2490 2491 2492 2493 2494 2495 2496
		tmp = xfs_attr_leaf_entsize(leaf_s, start_s + i);
#ifdef GROT
		/*
		 * Code to drop INCOMPLETE entries.  Difficult to use as we
		 * may also need to change the insertion index.  Code turned
		 * off for 6.2, should be revisited later.
		 */
		if (entry_s->flags & XFS_ATTR_INCOMPLETE) { /* skip partials? */
2497 2498 2499
			memset(xfs_attr3_leaf_name(leaf_s, start_s + i), 0, tmp);
			ichdr_s->usedbytes -= tmp;
			ichdr_s->count -= 1;
Linus Torvalds's avatar
Linus Torvalds committed
2500 2501 2502 2503 2504 2505
			entry_d--;	/* to compensate for ++ in loop hdr */
			desti--;
			if ((start_s + i) < offset)
				result++;	/* insertion index adjustment */
		} else {
#endif /* GROT */
2506
			ichdr_d->firstused -= tmp;
Linus Torvalds's avatar
Linus Torvalds committed
2507 2508
			/* both on-disk, don't endian flip twice */
			entry_d->hashval = entry_s->hashval;
2509
			entry_d->nameidx = cpu_to_be16(ichdr_d->firstused);
Linus Torvalds's avatar
Linus Torvalds committed
2510
			entry_d->flags = entry_s->flags;
2511
			ASSERT(be16_to_cpu(entry_d->nameidx) + tmp
2512
							<= args->geo->blksize);
2513 2514
			memmove(xfs_attr3_leaf_name(leaf_d, desti),
				xfs_attr3_leaf_name(leaf_s, start_s + i), tmp);
2515
			ASSERT(be16_to_cpu(entry_s->nameidx) + tmp
2516
							<= args->geo->blksize);
2517 2518 2519 2520 2521 2522 2523 2524
			memset(xfs_attr3_leaf_name(leaf_s, start_s + i), 0, tmp);
			ichdr_s->usedbytes -= tmp;
			ichdr_d->usedbytes += tmp;
			ichdr_s->count -= 1;
			ichdr_d->count += 1;
			tmp = ichdr_d->count * sizeof(xfs_attr_leaf_entry_t)
					+ xfs_attr3_leaf_hdr_size(leaf_d);
			ASSERT(ichdr_d->firstused >= tmp);
Linus Torvalds's avatar
Linus Torvalds committed
2525 2526 2527 2528 2529 2530 2531 2532
#ifdef GROT
		}
#endif /* GROT */
	}

	/*
	 * Zero out the entries we just copied.
	 */
2533
	if (start_s == ichdr_s->count) {
Linus Torvalds's avatar
Linus Torvalds committed
2534
		tmp = count * sizeof(xfs_attr_leaf_entry_t);
2535
		entry_s = &xfs_attr3_leaf_entryp(leaf_s)[start_s];
Linus Torvalds's avatar
Linus Torvalds committed
2536
		ASSERT(((char *)entry_s + tmp) <=
2537
		       ((char *)leaf_s + args->geo->blksize));
2538
		memset(entry_s, 0, tmp);
Linus Torvalds's avatar
Linus Torvalds committed
2539 2540 2541 2542 2543
	} else {
		/*
		 * Move the remaining entries down to fill the hole,
		 * then zero the entries at the top.
		 */
2544 2545 2546 2547
		tmp  = (ichdr_s->count - count) * sizeof(xfs_attr_leaf_entry_t);
		entry_s = &xfs_attr3_leaf_entryp(leaf_s)[start_s + count];
		entry_d = &xfs_attr3_leaf_entryp(leaf_s)[start_s];
		memmove(entry_d, entry_s, tmp);
Linus Torvalds's avatar
Linus Torvalds committed
2548 2549

		tmp = count * sizeof(xfs_attr_leaf_entry_t);
2550
		entry_s = &xfs_attr3_leaf_entryp(leaf_s)[ichdr_s->count];
Linus Torvalds's avatar
Linus Torvalds committed
2551
		ASSERT(((char *)entry_s + tmp) <=
2552
		       ((char *)leaf_s + args->geo->blksize));
2553
		memset(entry_s, 0, tmp);
Linus Torvalds's avatar
Linus Torvalds committed
2554 2555 2556 2557 2558
	}

	/*
	 * Fill in the freemap information
	 */
2559 2560 2561 2562 2563 2564 2565 2566
	ichdr_d->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_d);
	ichdr_d->freemap[0].base += ichdr_d->count * sizeof(xfs_attr_leaf_entry_t);
	ichdr_d->freemap[0].size = ichdr_d->firstused - ichdr_d->freemap[0].base;
	ichdr_d->freemap[1].base = 0;
	ichdr_d->freemap[2].base = 0;
	ichdr_d->freemap[1].size = 0;
	ichdr_d->freemap[2].size = 0;
	ichdr_s->holes = 1;	/* leaf may not be compact */
Linus Torvalds's avatar
Linus Torvalds committed
2567 2568 2569 2570 2571 2572
}

/*
 * Pick up the last hashvalue from a leaf block.
 */
xfs_dahash_t
2573 2574 2575
xfs_attr_leaf_lasthash(
	struct xfs_buf	*bp,
	int		*count)
Linus Torvalds's avatar
Linus Torvalds committed
2576
{
2577 2578
	struct xfs_attr3_icleaf_hdr ichdr;
	struct xfs_attr_leaf_entry *entries;
2579
	struct xfs_mount *mp = bp->b_mount;
Linus Torvalds's avatar
Linus Torvalds committed
2580

2581
	xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, bp->b_addr);
2582
	entries = xfs_attr3_leaf_entryp(bp->b_addr);
Linus Torvalds's avatar
Linus Torvalds committed
2583
	if (count)
2584 2585 2586 2587
		*count = ichdr.count;
	if (!ichdr.count)
		return 0;
	return be32_to_cpu(entries[ichdr.count - 1].hashval);
Linus Torvalds's avatar
Linus Torvalds committed
2588 2589 2590 2591 2592 2593
}

/*
 * Calculate the number of bytes used to store the indicated attribute
 * (whether local or remote only calculate bytes in this block).
 */
2594
STATIC int
Linus Torvalds's avatar
Linus Torvalds committed
2595 2596
xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index)
{
2597
	struct xfs_attr_leaf_entry *entries;
Linus Torvalds's avatar
Linus Torvalds committed
2598 2599 2600 2601
	xfs_attr_leaf_name_local_t *name_loc;
	xfs_attr_leaf_name_remote_t *name_rmt;
	int size;

2602 2603 2604
	entries = xfs_attr3_leaf_entryp(leaf);
	if (entries[index].flags & XFS_ATTR_LOCAL) {
		name_loc = xfs_attr3_leaf_name_local(leaf, index);
2605
		size = xfs_attr_leaf_entsize_local(name_loc->namelen,
2606
						   be16_to_cpu(name_loc->valuelen));
Linus Torvalds's avatar
Linus Torvalds committed
2607
	} else {
2608
		name_rmt = xfs_attr3_leaf_name_remote(leaf, index);
2609
		size = xfs_attr_leaf_entsize_remote(name_rmt->namelen);
Linus Torvalds's avatar
Linus Torvalds committed
2610
	}
2611
	return size;
Linus Torvalds's avatar
Linus Torvalds committed
2612 2613 2614 2615 2616 2617 2618 2619 2620
}

/*
 * Calculate the number of bytes that would be required to store the new
 * attribute (whether local or remote only calculate bytes in this block).
 * This routine decides as a side effect whether the attribute will be
 * a "local" or a "remote" attribute.
 */
int
2621 2622 2623
xfs_attr_leaf_newentsize(
	struct xfs_da_args	*args,
	int			*local)
Linus Torvalds's avatar
Linus Torvalds committed
2624
{
2625
	int			size;
Linus Torvalds's avatar
Linus Torvalds committed
2626

2627 2628 2629
	size = xfs_attr_leaf_entsize_local(args->namelen, args->valuelen);
	if (size < xfs_attr_leaf_entsize_local_max(args->geo->blksize)) {
		if (local)
Linus Torvalds's avatar
Linus Torvalds committed
2630
			*local = 1;
2631
		return size;
Linus Torvalds's avatar
Linus Torvalds committed
2632
	}
2633 2634 2635
	if (local)
		*local = 0;
	return xfs_attr_leaf_entsize_remote(args->namelen);
Linus Torvalds's avatar
Linus Torvalds committed
2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646
}


/*========================================================================
 * Manage the INCOMPLETE flag in a leaf entry
 *========================================================================*/

/*
 * Clear the INCOMPLETE flag on an entry in a leaf block.
 */
int
2647 2648
xfs_attr3_leaf_clearflag(
	struct xfs_da_args	*args)
Linus Torvalds's avatar
Linus Torvalds committed
2649
{
2650 2651 2652 2653 2654
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr_leaf_entry *entry;
	struct xfs_attr_leaf_name_remote *name_rmt;
	struct xfs_buf		*bp;
	int			error;
Linus Torvalds's avatar
Linus Torvalds committed
2655
#ifdef DEBUG
2656
	struct xfs_attr3_icleaf_hdr ichdr;
Linus Torvalds's avatar
Linus Torvalds committed
2657 2658 2659 2660 2661
	xfs_attr_leaf_name_local_t *name_loc;
	int namelen;
	char *name;
#endif /* DEBUG */

2662
	trace_xfs_attr_leaf_clearflag(args);
Linus Torvalds's avatar
Linus Torvalds committed
2663 2664 2665
	/*
	 * Set up the operation.
	 */
2666
	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, -1, &bp);
2667
	if (error)
Eric Sandeen's avatar
Eric Sandeen committed
2668
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
2669

2670
	leaf = bp->b_addr;
2671
	entry = &xfs_attr3_leaf_entryp(leaf)[args->index];
Linus Torvalds's avatar
Linus Torvalds committed
2672 2673 2674
	ASSERT(entry->flags & XFS_ATTR_INCOMPLETE);

#ifdef DEBUG
2675
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
2676 2677 2678
	ASSERT(args->index < ichdr.count);
	ASSERT(args->index >= 0);

Linus Torvalds's avatar
Linus Torvalds committed
2679
	if (entry->flags & XFS_ATTR_LOCAL) {
2680
		name_loc = xfs_attr3_leaf_name_local(leaf, args->index);
Linus Torvalds's avatar
Linus Torvalds committed
2681 2682 2683
		namelen = name_loc->namelen;
		name = (char *)name_loc->nameval;
	} else {
2684
		name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index);
Linus Torvalds's avatar
Linus Torvalds committed
2685 2686 2687
		namelen = name_rmt->namelen;
		name = (char *)name_rmt->name;
	}
2688
	ASSERT(be32_to_cpu(entry->hashval) == args->hashval);
Linus Torvalds's avatar
Linus Torvalds committed
2689 2690 2691 2692 2693
	ASSERT(namelen == args->namelen);
	ASSERT(memcmp(name, args->name, namelen) == 0);
#endif /* DEBUG */

	entry->flags &= ~XFS_ATTR_INCOMPLETE;
2694
	xfs_trans_log_buf(args->trans, bp,
Linus Torvalds's avatar
Linus Torvalds committed
2695 2696 2697 2698
			 XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));

	if (args->rmtblkno) {
		ASSERT((entry->flags & XFS_ATTR_LOCAL) == 0);
2699
		name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index);
2700
		name_rmt->valueblk = cpu_to_be32(args->rmtblkno);
2701
		name_rmt->valuelen = cpu_to_be32(args->rmtvaluelen);
2702
		xfs_trans_log_buf(args->trans, bp,
Linus Torvalds's avatar
Linus Torvalds committed
2703 2704 2705 2706 2707 2708
			 XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
	}

	/*
	 * Commit the flag value change and start the next trans in series.
	 */
2709
	return xfs_trans_roll_inode(&args->trans, args->dp);
Linus Torvalds's avatar
Linus Torvalds committed
2710 2711 2712 2713 2714 2715
}

/*
 * Set the INCOMPLETE flag on an entry in a leaf block.
 */
int
2716 2717
xfs_attr3_leaf_setflag(
	struct xfs_da_args	*args)
Linus Torvalds's avatar
Linus Torvalds committed
2718
{
2719 2720 2721 2722
	struct xfs_attr_leafblock *leaf;
	struct xfs_attr_leaf_entry *entry;
	struct xfs_attr_leaf_name_remote *name_rmt;
	struct xfs_buf		*bp;
Linus Torvalds's avatar
Linus Torvalds committed
2723
	int error;
2724 2725 2726
#ifdef DEBUG
	struct xfs_attr3_icleaf_hdr ichdr;
#endif
Linus Torvalds's avatar
Linus Torvalds committed
2727

2728 2729
	trace_xfs_attr_leaf_setflag(args);

Linus Torvalds's avatar
Linus Torvalds committed
2730 2731 2732
	/*
	 * Set up the operation.
	 */
2733
	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, -1, &bp);
2734
	if (error)
Eric Sandeen's avatar
Eric Sandeen committed
2735
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
2736

2737
	leaf = bp->b_addr;
2738
#ifdef DEBUG
2739
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf);
2740
	ASSERT(args->index < ichdr.count);
Linus Torvalds's avatar
Linus Torvalds committed
2741
	ASSERT(args->index >= 0);
2742 2743
#endif
	entry = &xfs_attr3_leaf_entryp(leaf)[args->index];
Linus Torvalds's avatar
Linus Torvalds committed
2744 2745 2746

	ASSERT((entry->flags & XFS_ATTR_INCOMPLETE) == 0);
	entry->flags |= XFS_ATTR_INCOMPLETE;
2747
	xfs_trans_log_buf(args->trans, bp,
Linus Torvalds's avatar
Linus Torvalds committed
2748 2749
			XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
	if ((entry->flags & XFS_ATTR_LOCAL) == 0) {
2750
		name_rmt = xfs_attr3_leaf_name_remote(leaf, args->index);
Linus Torvalds's avatar
Linus Torvalds committed
2751 2752
		name_rmt->valueblk = 0;
		name_rmt->valuelen = 0;
2753
		xfs_trans_log_buf(args->trans, bp,
Linus Torvalds's avatar
Linus Torvalds committed
2754 2755 2756 2757 2758 2759
			 XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
	}

	/*
	 * Commit the flag value change and start the next trans in series.
	 */
2760
	return xfs_trans_roll_inode(&args->trans, args->dp);
Linus Torvalds's avatar
Linus Torvalds committed
2761 2762 2763 2764 2765 2766 2767 2768 2769 2770
}

/*
 * In a single transaction, clear the INCOMPLETE flag on the leaf entry
 * given by args->blkno/index and set the INCOMPLETE flag on the leaf
 * entry given by args->blkno2/index2.
 *
 * Note that they could be in different blocks, or in the same block.
 */
int
2771 2772
xfs_attr3_leaf_flipflags(
	struct xfs_da_args	*args)
Linus Torvalds's avatar
Linus Torvalds committed
2773
{
2774 2775 2776 2777 2778 2779 2780
	struct xfs_attr_leafblock *leaf1;
	struct xfs_attr_leafblock *leaf2;
	struct xfs_attr_leaf_entry *entry1;
	struct xfs_attr_leaf_entry *entry2;
	struct xfs_attr_leaf_name_remote *name_rmt;
	struct xfs_buf		*bp1;
	struct xfs_buf		*bp2;
Linus Torvalds's avatar
Linus Torvalds committed
2781 2782
	int error;
#ifdef DEBUG
2783 2784
	struct xfs_attr3_icleaf_hdr ichdr1;
	struct xfs_attr3_icleaf_hdr ichdr2;
Linus Torvalds's avatar
Linus Torvalds committed
2785 2786 2787 2788 2789
	xfs_attr_leaf_name_local_t *name_loc;
	int namelen1, namelen2;
	char *name1, *name2;
#endif /* DEBUG */

2790 2791
	trace_xfs_attr_leaf_flipflags(args);

Linus Torvalds's avatar
Linus Torvalds committed
2792 2793 2794
	/*
	 * Read the block containing the "old" attr
	 */
2795
	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno, -1, &bp1);
2796 2797
	if (error)
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
2798 2799 2800 2801 2802

	/*
	 * Read the block containing the "new" attr, if it is different
	 */
	if (args->blkno2 != args->blkno) {
2803
		error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno2,
2804 2805 2806
					   -1, &bp2);
		if (error)
			return error;
Linus Torvalds's avatar
Linus Torvalds committed
2807 2808 2809 2810
	} else {
		bp2 = bp1;
	}

2811
	leaf1 = bp1->b_addr;
2812
	entry1 = &xfs_attr3_leaf_entryp(leaf1)[args->index];
Linus Torvalds's avatar
Linus Torvalds committed
2813

2814
	leaf2 = bp2->b_addr;
2815
	entry2 = &xfs_attr3_leaf_entryp(leaf2)[args->index2];
Linus Torvalds's avatar
Linus Torvalds committed
2816 2817

#ifdef DEBUG
2818
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr1, leaf1);
2819 2820 2821
	ASSERT(args->index < ichdr1.count);
	ASSERT(args->index >= 0);

2822
	xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr2, leaf2);
2823 2824 2825
	ASSERT(args->index2 < ichdr2.count);
	ASSERT(args->index2 >= 0);

Linus Torvalds's avatar
Linus Torvalds committed
2826
	if (entry1->flags & XFS_ATTR_LOCAL) {
2827
		name_loc = xfs_attr3_leaf_name_local(leaf1, args->index);
Linus Torvalds's avatar
Linus Torvalds committed
2828 2829 2830
		namelen1 = name_loc->namelen;
		name1 = (char *)name_loc->nameval;
	} else {
2831
		name_rmt = xfs_attr3_leaf_name_remote(leaf1, args->index);
Linus Torvalds's avatar
Linus Torvalds committed
2832 2833 2834 2835
		namelen1 = name_rmt->namelen;
		name1 = (char *)name_rmt->name;
	}
	if (entry2->flags & XFS_ATTR_LOCAL) {
2836
		name_loc = xfs_attr3_leaf_name_local(leaf2, args->index2);
Linus Torvalds's avatar
Linus Torvalds committed
2837 2838 2839
		namelen2 = name_loc->namelen;
		name2 = (char *)name_loc->nameval;
	} else {
2840
		name_rmt = xfs_attr3_leaf_name_remote(leaf2, args->index2);
Linus Torvalds's avatar
Linus Torvalds committed
2841 2842 2843
		namelen2 = name_rmt->namelen;
		name2 = (char *)name_rmt->name;
	}
2844
	ASSERT(be32_to_cpu(entry1->hashval) == be32_to_cpu(entry2->hashval));
Linus Torvalds's avatar
Linus Torvalds committed
2845 2846 2847 2848 2849 2850 2851 2852
	ASSERT(namelen1 == namelen2);
	ASSERT(memcmp(name1, name2, namelen1) == 0);
#endif /* DEBUG */

	ASSERT(entry1->flags & XFS_ATTR_INCOMPLETE);
	ASSERT((entry2->flags & XFS_ATTR_INCOMPLETE) == 0);

	entry1->flags &= ~XFS_ATTR_INCOMPLETE;
2853
	xfs_trans_log_buf(args->trans, bp1,
Linus Torvalds's avatar
Linus Torvalds committed
2854 2855 2856
			  XFS_DA_LOGRANGE(leaf1, entry1, sizeof(*entry1)));
	if (args->rmtblkno) {
		ASSERT((entry1->flags & XFS_ATTR_LOCAL) == 0);
2857
		name_rmt = xfs_attr3_leaf_name_remote(leaf1, args->index);
2858
		name_rmt->valueblk = cpu_to_be32(args->rmtblkno);
2859
		name_rmt->valuelen = cpu_to_be32(args->rmtvaluelen);
2860
		xfs_trans_log_buf(args->trans, bp1,
Linus Torvalds's avatar
Linus Torvalds committed
2861 2862 2863 2864
			 XFS_DA_LOGRANGE(leaf1, name_rmt, sizeof(*name_rmt)));
	}

	entry2->flags |= XFS_ATTR_INCOMPLETE;
2865
	xfs_trans_log_buf(args->trans, bp2,
Linus Torvalds's avatar
Linus Torvalds committed
2866 2867
			  XFS_DA_LOGRANGE(leaf2, entry2, sizeof(*entry2)));
	if ((entry2->flags & XFS_ATTR_LOCAL) == 0) {
2868
		name_rmt = xfs_attr3_leaf_name_remote(leaf2, args->index2);
Linus Torvalds's avatar
Linus Torvalds committed
2869 2870
		name_rmt->valueblk = 0;
		name_rmt->valuelen = 0;
2871
		xfs_trans_log_buf(args->trans, bp2,
Linus Torvalds's avatar
Linus Torvalds committed
2872 2873 2874 2875 2876 2877
			 XFS_DA_LOGRANGE(leaf2, name_rmt, sizeof(*name_rmt)));
	}

	/*
	 * Commit the flag value change and start the next trans in series.
	 */
2878
	error = xfs_trans_roll_inode(&args->trans, args->dp);
Linus Torvalds's avatar
Linus Torvalds committed
2879

2880
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
2881
}