xfs_dir2_sf.c 37.0 KB
Newer Older
D
Dave Chinner 已提交
1
// SPDX-License-Identifier: GPL-2.0
L
Linus Torvalds 已提交
2
/*
3 4
 * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
 * All Rights Reserved.
L
Linus Torvalds 已提交
5 6
 */
#include "xfs.h"
7
#include "xfs_fs.h"
8
#include "xfs_shared.h"
9
#include "xfs_format.h"
10 11
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
L
Linus Torvalds 已提交
12 13
#include "xfs_mount.h"
#include "xfs_inode.h"
14
#include "xfs_trans.h"
15
#include "xfs_dir2.h"
C
Christoph Hellwig 已提交
16
#include "xfs_dir2_priv.h"
C
Christoph Hellwig 已提交
17
#include "xfs_trace.h"
L
Linus Torvalds 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

/*
 * Prototypes for internal functions.
 */
static void xfs_dir2_sf_addname_easy(xfs_da_args_t *args,
				     xfs_dir2_sf_entry_t *sfep,
				     xfs_dir2_data_aoff_t offset,
				     int new_isize);
static void xfs_dir2_sf_addname_hard(xfs_da_args_t *args, int objchange,
				     int new_isize);
static int xfs_dir2_sf_addname_pick(xfs_da_args_t *args, int objchange,
				    xfs_dir2_sf_entry_t **sfepp,
				    xfs_dir2_data_aoff_t *offsetp);
#ifdef DEBUG
static void xfs_dir2_sf_check(xfs_da_args_t *args);
#else
#define	xfs_dir2_sf_check(args)
#endif /* DEBUG */
C
Christoph Hellwig 已提交
36

L
Linus Torvalds 已提交
37 38 39
static void xfs_dir2_sf_toino4(xfs_da_args_t *args);
static void xfs_dir2_sf_toino8(xfs_da_args_t *args);

40
int
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
xfs_dir2_sf_entsize(
	struct xfs_mount	*mp,
	struct xfs_dir2_sf_hdr	*hdr,
	int			len)
{
	int			count = len;

	count += sizeof(struct xfs_dir2_sf_entry);	/* namelen + offset */
	count += hdr->i8count ? XFS_INO64_SIZE : XFS_INO32_SIZE; /* ino # */

	if (xfs_sb_version_hasftype(&mp->m_sb))
		count += sizeof(uint8_t);
	return count;
}

struct xfs_dir2_sf_entry *
xfs_dir2_sf_nextentry(
	struct xfs_mount	*mp,
	struct xfs_dir2_sf_hdr	*hdr,
	struct xfs_dir2_sf_entry *sfep)
{
	return (void *)sfep + xfs_dir2_sf_entsize(mp, hdr, sfep->namelen);
}

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
/*
 * In short-form directory entries the inode numbers are stored at variable
 * offset behind the entry name. If the entry stores a filetype value, then it
 * sits between the name and the inode number.  The actual inode numbers can
 * come in two formats as well, either 4 bytes or 8 bytes wide.
 */
xfs_ino_t
xfs_dir2_sf_get_ino(
	struct xfs_mount		*mp,
	struct xfs_dir2_sf_hdr		*hdr,
	struct xfs_dir2_sf_entry	*sfep)
{
	uint8_t				*from = sfep->name + sfep->namelen;

	if (xfs_sb_version_hasftype(&mp->m_sb))
		from++;

	if (!hdr->i8count)
		return get_unaligned_be32(from);
	return get_unaligned_be64(from) & XFS_MAXINUMBER;
}

87
void
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
xfs_dir2_sf_put_ino(
	struct xfs_mount		*mp,
	struct xfs_dir2_sf_hdr		*hdr,
	struct xfs_dir2_sf_entry	*sfep,
	xfs_ino_t			ino)
{
	uint8_t				*to = sfep->name + sfep->namelen;

	ASSERT(ino <= XFS_MAXINUMBER);

	if (xfs_sb_version_hasftype(&mp->m_sb))
		to++;

	if (hdr->i8count)
		put_unaligned_be64(ino, to);
	else
		put_unaligned_be32(ino, to);
}

xfs_ino_t
xfs_dir2_sf_get_parent_ino(
	struct xfs_dir2_sf_hdr	*hdr)
{
	if (!hdr->i8count)
		return get_unaligned_be32(hdr->parent);
	return get_unaligned_be64(hdr->parent) & XFS_MAXINUMBER;
}

void
xfs_dir2_sf_put_parent_ino(
	struct xfs_dir2_sf_hdr		*hdr,
	xfs_ino_t			ino)
{
	ASSERT(ino <= XFS_MAXINUMBER);

	if (hdr->i8count)
		put_unaligned_be64(ino, hdr->parent);
	else
		put_unaligned_be32(ino, hdr->parent);
}

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
/*
 * The file type field is stored at the end of the name for filetype enabled
 * shortform directories, or not at all otherwise.
 */
uint8_t
xfs_dir2_sf_get_ftype(
	struct xfs_mount		*mp,
	struct xfs_dir2_sf_entry	*sfep)
{
	if (xfs_sb_version_hasftype(&mp->m_sb)) {
		uint8_t			ftype = sfep->name[sfep->namelen];

		if (ftype < XFS_DIR3_FT_MAX)
			return ftype;
	}

	return XFS_DIR3_FT_UNKNOWN;
}

148
void
149 150 151 152 153 154 155 156 157 158 159
xfs_dir2_sf_put_ftype(
	struct xfs_mount	*mp,
	struct xfs_dir2_sf_entry *sfep,
	uint8_t			ftype)
{
	ASSERT(ftype < XFS_DIR3_FT_MAX);

	if (xfs_sb_version_hasftype(&mp->m_sb))
		sfep->name[sfep->namelen] = ftype;
}

L
Linus Torvalds 已提交
160 161 162 163 164 165 166 167 168
/*
 * Given a block directory (dp/block), calculate its size as a shortform (sf)
 * directory and a header for the sf directory, if it will fit it the
 * space currently present in the inode.  If it won't fit, the output
 * size is too big (but not accurate).
 */
int						/* size for sf form */
xfs_dir2_block_sfsize(
	xfs_inode_t		*dp,		/* incore inode pointer */
169
	xfs_dir2_data_hdr_t	*hdr,		/* block directory data */
L
Linus Torvalds 已提交
170 171 172 173 174 175 176 177 178 179 180 181 182
	xfs_dir2_sf_hdr_t	*sfhp)		/* output: header for sf form */
{
	xfs_dir2_dataptr_t	addr;		/* data entry address */
	xfs_dir2_leaf_entry_t	*blp;		/* leaf area of the block */
	xfs_dir2_block_tail_t	*btp;		/* tail area of the block */
	int			count;		/* shortform entry count */
	xfs_dir2_data_entry_t	*dep;		/* data entry in the block */
	int			i;		/* block entry index */
	int			i8count;	/* count of big-inode entries */
	int			isdot;		/* entry is "." */
	int			isdotdot;	/* entry is ".." */
	xfs_mount_t		*mp;		/* mount structure pointer */
	int			namelen;	/* total name bytes */
183
	xfs_ino_t		parent = 0;	/* parent inode number */
L
Linus Torvalds 已提交
184
	int			size=0;		/* total computed size */
185
	int			has_ftype;
186
	struct xfs_da_geometry	*geo;
L
Linus Torvalds 已提交
187 188

	mp = dp->i_mount;
189
	geo = mp->m_dir_geo;
L
Linus Torvalds 已提交
190

191 192 193 194 195 196
	/*
	 * if there is a filetype field, add the extra byte to the namelen
	 * for each entry that we see.
	 */
	has_ftype = xfs_sb_version_hasftype(&mp->m_sb) ? 1 : 0;

L
Linus Torvalds 已提交
197
	count = i8count = namelen = 0;
198
	btp = xfs_dir2_block_tail_p(geo, hdr);
199
	blp = xfs_dir2_block_leaf_p(btp);
L
Linus Torvalds 已提交
200 201 202 203

	/*
	 * Iterate over the block's data entries by using the leaf pointers.
	 */
204
	for (i = 0; i < be32_to_cpu(btp->count); i++) {
205
		if ((addr = be32_to_cpu(blp[i].address)) == XFS_DIR2_NULL_DATAPTR)
L
Linus Torvalds 已提交
206 207 208 209
			continue;
		/*
		 * Calculate the pointer to the entry at hand.
		 */
210
		dep = (xfs_dir2_data_entry_t *)((char *)hdr +
211
				xfs_dir2_dataptr_to_off(geo, addr));
L
Linus Torvalds 已提交
212 213 214 215 216 217 218 219 220
		/*
		 * Detect . and .., so we can special-case them.
		 * . is not included in sf directories.
		 * .. is included by just the parent inode number.
		 */
		isdot = dep->namelen == 1 && dep->name[0] == '.';
		isdotdot =
			dep->namelen == 2 &&
			dep->name[0] == '.' && dep->name[1] == '.';
C
Christoph Hellwig 已提交
221

L
Linus Torvalds 已提交
222
		if (!isdot)
223
			i8count += be64_to_cpu(dep->inumber) > XFS_DIR2_MAX_SHORT_INUM;
C
Christoph Hellwig 已提交
224

225
		/* take into account the file type field */
L
Linus Torvalds 已提交
226 227
		if (!isdot && !isdotdot) {
			count++;
228
			namelen += dep->namelen + has_ftype;
L
Linus Torvalds 已提交
229
		} else if (isdotdot)
230
			parent = be64_to_cpu(dep->inumber);
L
Linus Torvalds 已提交
231 232 233
		/*
		 * Calculate the new size, see if we should give up yet.
		 */
C
Christoph Hellwig 已提交
234 235 236 237
		size = xfs_dir2_sf_hdr_size(i8count) +	/* header */
		       count * 3 * sizeof(u8) +		/* namelen + offset */
		       namelen +			/* name */
		       (i8count ?			/* inumber */
C
Christoph Hellwig 已提交
238 239
				count * XFS_INO64_SIZE :
				count * XFS_INO32_SIZE);
L
Linus Torvalds 已提交
240 241 242 243 244 245 246 247
		if (size > XFS_IFORK_DSIZE(dp))
			return size;		/* size value is a failure */
	}
	/*
	 * Create the output header, if it worked.
	 */
	sfhp->count = count;
	sfhp->i8count = i8count;
248
	xfs_dir2_sf_put_parent_ino(sfhp, parent);
L
Linus Torvalds 已提交
249 250 251 252 253 254 255 256 257
	return size;
}

/*
 * Convert a block format directory to shortform.
 * Caller has already checked that it will fit, and built us a header.
 */
int						/* error */
xfs_dir2_block_to_sf(
258
	struct xfs_da_args	*args,		/* operation arguments */
259
	struct xfs_buf		*bp,
L
Linus Torvalds 已提交
260
	int			size,		/* shortform directory size */
261
	struct xfs_dir2_sf_hdr	*sfhp)		/* shortform directory hdr */
L
Linus Torvalds 已提交
262
{
263 264
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
265 266
	int			error;		/* error return value */
	int			logflags;	/* inode logging flags */
267 268
	struct xfs_dir2_sf_entry *sfep;		/* shortform entry */
	struct xfs_dir2_sf_hdr	*sfp;		/* shortform directory header */
269
	unsigned int		offset = args->geo->data_entry_offset;
270
	unsigned int		end;
L
Linus Torvalds 已提交
271

C
Christoph Hellwig 已提交
272 273
	trace_xfs_dir2_block_to_sf(args);

L
Linus Torvalds 已提交
274
	/*
275 276 277
	 * Allocate a temporary destination buffer the size of the inode to
	 * format the data into.  Once we have formatted the data, we can free
	 * the block and copy the formatted data into the inode literal area.
L
Linus Torvalds 已提交
278
	 */
279
	sfp = kmem_alloc(mp->m_sb.sb_inodesize, 0);
280
	memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count));
281

L
Linus Torvalds 已提交
282
	/*
283 284
	 * Loop over the active and unused entries.  Stop when we reach the
	 * leaf/tail portion of the block.
L
Linus Torvalds 已提交
285
	 */
286
	end = xfs_dir3_data_end_offset(args->geo, bp->b_addr);
287
	sfep = xfs_dir2_sf_firstentry(sfp);
288 289 290 291
	while (offset < end) {
		struct xfs_dir2_data_unused	*dup = bp->b_addr + offset;
		struct xfs_dir2_data_entry	*dep = bp->b_addr + offset;

L
Linus Torvalds 已提交
292 293 294
		/*
		 * If it's unused, just skip over it.
		 */
295
		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
296
			offset += be16_to_cpu(dup->length);
L
Linus Torvalds 已提交
297 298
			continue;
		}
299

L
Linus Torvalds 已提交
300 301 302 303
		/*
		 * Skip .
		 */
		if (dep->namelen == 1 && dep->name[0] == '.')
304
			ASSERT(be64_to_cpu(dep->inumber) == dp->i_ino);
L
Linus Torvalds 已提交
305 306 307 308 309
		/*
		 * Skip .., but make sure the inode number is right.
		 */
		else if (dep->namelen == 2 &&
			 dep->name[0] == '.' && dep->name[1] == '.')
310
			ASSERT(be64_to_cpu(dep->inumber) ==
311
			       xfs_dir2_sf_get_parent_ino(sfp));
L
Linus Torvalds 已提交
312 313 314 315 316
		/*
		 * Normal entry, copy it into shortform.
		 */
		else {
			sfep->namelen = dep->namelen;
317
			xfs_dir2_sf_put_offset(sfep, offset);
L
Linus Torvalds 已提交
318
			memcpy(sfep->name, dep->name, dep->namelen);
319
			xfs_dir2_sf_put_ino(mp, sfp, sfep,
320
					      be64_to_cpu(dep->inumber));
321
			xfs_dir2_sf_put_ftype(mp, sfep,
322
					xfs_dir2_data_get_ftype(mp, dep));
323

324
			sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
L
Linus Torvalds 已提交
325
		}
326
		offset += xfs_dir2_data_entsize(mp, dep->namelen);
L
Linus Torvalds 已提交
327 328
	}
	ASSERT((char *)sfep - (char *)sfp == size);
329 330 331

	/* now we are done with the block, we can shrink the inode */
	logflags = XFS_ILOG_CORE;
332
	error = xfs_dir2_shrink_inode(args, args->geo->datablk, bp);
333
	if (error) {
D
Dave Chinner 已提交
334
		ASSERT(error != -ENOSPC);
335 336 337 338 339 340 341 342 343 344
		goto out;
	}

	/*
	 * The buffer is now unconditionally gone, whether
	 * xfs_dir2_shrink_inode worked or not.
	 *
	 * Convert the inode to local format and copy the data in.
	 */
	ASSERT(dp->i_df.if_bytes == 0);
345
	xfs_init_local_fork(dp, XFS_DATA_FORK, sfp, size);
346
	dp->i_df.if_format = XFS_DINODE_FMT_LOCAL;
347
	dp->i_disk_size = size;
348 349

	logflags |= XFS_ILOG_DDATA;
L
Linus Torvalds 已提交
350 351 352
	xfs_dir2_sf_check(args);
out:
	xfs_trans_log_inode(args->trans, dp, logflags);
353
	kmem_free(sfp);
L
Linus Torvalds 已提交
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
	return error;
}

/*
 * Add a name to a shortform directory.
 * There are two algorithms, "easy" and "hard" which we decide on
 * before changing anything.
 * Convert to block form if necessary, if the new entry won't fit.
 */
int						/* error */
xfs_dir2_sf_addname(
	xfs_da_args_t		*args)		/* operation arguments */
{
	xfs_inode_t		*dp;		/* incore directory inode */
	int			error;		/* error return value */
	int			incr_isize;	/* total change in size */
370
	int			new_isize;	/* size after adding name */
L
Linus Torvalds 已提交
371
	int			objchange;	/* changing to 8-byte inodes */
372
	xfs_dir2_data_aoff_t	offset = 0;	/* offset for new entry */
L
Linus Torvalds 已提交
373
	int			pick;		/* which algorithm to use */
C
Christoph Hellwig 已提交
374
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
375
	xfs_dir2_sf_entry_t	*sfep = NULL;	/* shortform entry */
L
Linus Torvalds 已提交
376

C
Christoph Hellwig 已提交
377 378
	trace_xfs_dir2_sf_addname(args);

D
Dave Chinner 已提交
379
	ASSERT(xfs_dir2_sf_lookup(args) == -ENOENT);
L
Linus Torvalds 已提交
380 381
	dp = args->dp;
	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
382 383
	ASSERT(dp->i_disk_size >= offsetof(struct xfs_dir2_sf_hdr, parent));
	ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
L
Linus Torvalds 已提交
384
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
385
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
386
	ASSERT(dp->i_disk_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
L
Linus Torvalds 已提交
387 388 389
	/*
	 * Compute entry (and change in) size.
	 */
390
	incr_isize = xfs_dir2_sf_entsize(dp->i_mount, sfp, args->namelen);
L
Linus Torvalds 已提交
391
	objchange = 0;
C
Christoph Hellwig 已提交
392

L
Linus Torvalds 已提交
393 394 395
	/*
	 * Do we have to change to 8 byte inodes?
	 */
C
Christoph Hellwig 已提交
396
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
L
Linus Torvalds 已提交
397
		/*
398
		 * Yes, adjust the inode size.  old count + (parent + new)
L
Linus Torvalds 已提交
399
		 */
C
Christoph Hellwig 已提交
400
		incr_isize += (sfp->count + 2) * XFS_INO64_DIFF;
L
Linus Torvalds 已提交
401 402
		objchange = 1;
	}
C
Christoph Hellwig 已提交
403

404
	new_isize = (int)dp->i_disk_size + incr_isize;
L
Linus Torvalds 已提交
405 406 407 408 409 410 411 412 413 414
	/*
	 * Won't fit as shortform any more (due to size),
	 * or the pick routine says it won't (due to offset values).
	 */
	if (new_isize > XFS_IFORK_DSIZE(dp) ||
	    (pick =
	     xfs_dir2_sf_addname_pick(args, objchange, &sfep, &offset)) == 0) {
		/*
		 * Just checking or no space reservation, it doesn't fit.
		 */
415
		if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0)
D
Dave Chinner 已提交
416
			return -ENOSPC;
L
Linus Torvalds 已提交
417 418 419 420 421 422 423 424 425 426 427
		/*
		 * Convert to block form then add the name.
		 */
		error = xfs_dir2_sf_to_block(args);
		if (error)
			return error;
		return xfs_dir2_block_addname(args);
	}
	/*
	 * Just checking, it fits.
	 */
428
	if (args->op_flags & XFS_DA_OP_JUSTCHECK)
L
Linus Torvalds 已提交
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
		return 0;
	/*
	 * Do it the easy way - just add it at the end.
	 */
	if (pick == 1)
		xfs_dir2_sf_addname_easy(args, sfep, offset, new_isize);
	/*
	 * Do it the hard way - look for a place to insert the new entry.
	 * Convert to 8 byte inode numbers first if necessary.
	 */
	else {
		ASSERT(pick == 2);
		if (objchange)
			xfs_dir2_sf_toino8(args);
		xfs_dir2_sf_addname_hard(args, objchange, new_isize);
	}
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
	return 0;
}

/*
 * Add the new entry the "easy" way.
 * This is copying the old directory and adding the new entry at the end.
 * Since it's sorted by "offset" we need room after the last offset
 * that's already there, and then room to convert to a block directory.
 * This is already checked by the pick routine.
 */
static void
xfs_dir2_sf_addname_easy(
	xfs_da_args_t		*args,		/* operation arguments */
	xfs_dir2_sf_entry_t	*sfep,		/* pointer to new entry */
	xfs_dir2_data_aoff_t	offset,		/* offset to use for new ent */
	int			new_isize)	/* new directory size */
{
463 464
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
465
	int			byteoff;	/* byte offset in sf dir */
C
Christoph Hellwig 已提交
466
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
467

C
Christoph Hellwig 已提交
468
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
469 470 471 472
	byteoff = (int)((char *)sfep - (char *)sfp);
	/*
	 * Grow the in-inode space.
	 */
473
	xfs_idata_realloc(dp, xfs_dir2_sf_entsize(mp, sfp, args->namelen),
474
			  XFS_DATA_FORK);
L
Linus Torvalds 已提交
475 476 477
	/*
	 * Need to set up again due to realloc of the inode data.
	 */
C
Christoph Hellwig 已提交
478
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
479 480 481 482 483
	sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff);
	/*
	 * Fill in the new entry.
	 */
	sfep->namelen = args->namelen;
484
	xfs_dir2_sf_put_offset(sfep, offset);
L
Linus Torvalds 已提交
485
	memcpy(sfep->name, args->name, sfep->namelen);
486
	xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber);
487
	xfs_dir2_sf_put_ftype(mp, sfep, args->filetype);
488

L
Linus Torvalds 已提交
489 490 491
	/*
	 * Update the header and inode.
	 */
C
Christoph Hellwig 已提交
492
	sfp->count++;
L
Linus Torvalds 已提交
493
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM)
C
Christoph Hellwig 已提交
494
		sfp->i8count++;
495
	dp->i_disk_size = new_isize;
L
Linus Torvalds 已提交
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
	xfs_dir2_sf_check(args);
}

/*
 * Add the new entry the "hard" way.
 * The caller has already converted to 8 byte inode numbers if necessary,
 * in which case we need to leave the i8count at 1.
 * Find a hole that the new entry will fit into, and copy
 * the first part of the entries, the new entry, and the last part of
 * the entries.
 */
/* ARGSUSED */
static void
xfs_dir2_sf_addname_hard(
	xfs_da_args_t		*args,		/* operation arguments */
	int			objchange,	/* changing inode number size */
	int			new_isize)	/* new directory size */
{
514 515
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
516 517 518 519 520 521
	int			add_datasize;	/* data size need for new ent */
	char			*buf;		/* buffer for old */
	int			eof;		/* reached end of old dir */
	int			nbytes;		/* temp for byte copies */
	xfs_dir2_data_aoff_t	new_offset;	/* next offset value */
	xfs_dir2_data_aoff_t	offset;		/* current offset value */
522
	int			old_isize;	/* previous size */
L
Linus Torvalds 已提交
523
	xfs_dir2_sf_entry_t	*oldsfep;	/* entry in original dir */
C
Christoph Hellwig 已提交
524
	xfs_dir2_sf_hdr_t	*oldsfp;	/* original shortform dir */
L
Linus Torvalds 已提交
525
	xfs_dir2_sf_entry_t	*sfep;		/* entry in new dir */
C
Christoph Hellwig 已提交
526
	xfs_dir2_sf_hdr_t	*sfp;		/* new shortform dir */
L
Linus Torvalds 已提交
527 528 529 530

	/*
	 * Copy the old directory to the stack buffer.
	 */
C
Christoph Hellwig 已提交
531
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
532
	old_isize = (int)dp->i_disk_size;
533
	buf = kmem_alloc(old_isize, 0);
C
Christoph Hellwig 已提交
534
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
L
Linus Torvalds 已提交
535 536 537 538 539 540
	memcpy(oldsfp, sfp, old_isize);
	/*
	 * Loop over the old directory finding the place we're going
	 * to insert the new entry.
	 * If it's going to end up at the end then oldsfep will point there.
	 */
541
	for (offset = args->geo->data_first_offset,
542
	      oldsfep = xfs_dir2_sf_firstentry(oldsfp),
543
	      add_datasize = xfs_dir2_data_entsize(mp, args->namelen),
L
Linus Torvalds 已提交
544 545
	      eof = (char *)oldsfep == &buf[old_isize];
	     !eof;
546
	     offset = new_offset + xfs_dir2_data_entsize(mp, oldsfep->namelen),
547
	      oldsfep = xfs_dir2_sf_nextentry(mp, oldsfp, oldsfep),
L
Linus Torvalds 已提交
548
	      eof = (char *)oldsfep == &buf[old_isize]) {
549
		new_offset = xfs_dir2_sf_get_offset(oldsfep);
L
Linus Torvalds 已提交
550 551 552 553 554 555 556 557 558 559 560 561 562
		if (offset + add_datasize <= new_offset)
			break;
	}
	/*
	 * Get rid of the old directory, then allocate space for
	 * the new one.  We do this so xfs_idata_realloc won't copy
	 * the data.
	 */
	xfs_idata_realloc(dp, -old_isize, XFS_DATA_FORK);
	xfs_idata_realloc(dp, new_isize, XFS_DATA_FORK);
	/*
	 * Reset the pointer since the buffer was reallocated.
	 */
C
Christoph Hellwig 已提交
563
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
564 565 566 567 568 569 570 571 572 573
	/*
	 * Copy the first part of the directory, including the header.
	 */
	nbytes = (int)((char *)oldsfep - (char *)oldsfp);
	memcpy(sfp, oldsfp, nbytes);
	sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + nbytes);
	/*
	 * Fill in the new entry, and update the header counts.
	 */
	sfep->namelen = args->namelen;
574
	xfs_dir2_sf_put_offset(sfep, offset);
L
Linus Torvalds 已提交
575
	memcpy(sfep->name, args->name, sfep->namelen);
576
	xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber);
577
	xfs_dir2_sf_put_ftype(mp, sfep, args->filetype);
C
Christoph Hellwig 已提交
578
	sfp->count++;
L
Linus Torvalds 已提交
579
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange)
C
Christoph Hellwig 已提交
580
		sfp->i8count++;
L
Linus Torvalds 已提交
581 582 583 584
	/*
	 * If there's more left to copy, do that.
	 */
	if (!eof) {
585
		sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
L
Linus Torvalds 已提交
586 587
		memcpy(sfep, oldsfep, old_isize - nbytes);
	}
588
	kmem_free(buf);
589
	dp->i_disk_size = new_isize;
L
Linus Torvalds 已提交
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
	xfs_dir2_sf_check(args);
}

/*
 * Decide if the new entry will fit at all.
 * If it will fit, pick between adding the new entry to the end (easy)
 * or somewhere else (hard).
 * Return 0 (won't fit), 1 (easy), 2 (hard).
 */
/*ARGSUSED*/
static int					/* pick result */
xfs_dir2_sf_addname_pick(
	xfs_da_args_t		*args,		/* operation arguments */
	int			objchange,	/* inode # size changes */
	xfs_dir2_sf_entry_t	**sfepp,	/* out(1): new entry ptr */
	xfs_dir2_data_aoff_t	*offsetp)	/* out(1): new offset */
{
607 608
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
609 610 611 612
	int			holefit;	/* found hole it will fit in */
	int			i;		/* entry number */
	xfs_dir2_data_aoff_t	offset;		/* data block offset */
	xfs_dir2_sf_entry_t	*sfep;		/* shortform entry */
C
Christoph Hellwig 已提交
613
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
614 615 616
	int			size;		/* entry's data size */
	int			used;		/* data bytes used */

C
Christoph Hellwig 已提交
617
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
618
	size = xfs_dir2_data_entsize(mp, args->namelen);
619
	offset = args->geo->data_first_offset;
620
	sfep = xfs_dir2_sf_firstentry(sfp);
L
Linus Torvalds 已提交
621 622 623 624 625 626
	holefit = 0;
	/*
	 * Loop over sf entries.
	 * Keep track of data offset and whether we've seen a place
	 * to insert the new entry.
	 */
C
Christoph Hellwig 已提交
627
	for (i = 0; i < sfp->count; i++) {
L
Linus Torvalds 已提交
628
		if (!holefit)
629 630
			holefit = offset + size <= xfs_dir2_sf_get_offset(sfep);
		offset = xfs_dir2_sf_get_offset(sfep) +
631
			 xfs_dir2_data_entsize(mp, sfep->namelen);
632
		sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
L
Linus Torvalds 已提交
633 634 635 636 637 638
	}
	/*
	 * Calculate data bytes used excluding the new entry, if this
	 * was a data block (block form directory).
	 */
	used = offset +
C
Christoph Hellwig 已提交
639
	       (sfp->count + 3) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
L
Linus Torvalds 已提交
640 641 642 643 644 645
	       (uint)sizeof(xfs_dir2_block_tail_t);
	/*
	 * If it won't fit in a block form then we can't insert it,
	 * we'll go back, convert to block, then try the insert and convert
	 * to leaf.
	 */
646
	if (used + (holefit ? 0 : size) > args->geo->blksize)
L
Linus Torvalds 已提交
647 648 649 650
		return 0;
	/*
	 * If changing the inode number size, do it the hard way.
	 */
C
Christoph Hellwig 已提交
651
	if (objchange)
L
Linus Torvalds 已提交
652 653 654 655
		return 2;
	/*
	 * If it won't fit at the end then do it the hard way (use the hole).
	 */
656
	if (used + size > args->geo->blksize)
L
Linus Torvalds 已提交
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
		return 2;
	/*
	 * Do it the easy way.
	 */
	*sfepp = sfep;
	*offsetp = offset;
	return 1;
}

#ifdef DEBUG
/*
 * Check consistency of shortform directory, assert if bad.
 */
static void
xfs_dir2_sf_check(
	xfs_da_args_t		*args)		/* operation arguments */
{
674 675
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
676 677 678 679 680
	int			i;		/* entry number */
	int			i8count;	/* number of big inode#s */
	xfs_ino_t		ino;		/* entry inode number */
	int			offset;		/* data offset */
	xfs_dir2_sf_entry_t	*sfep;		/* shortform dir entry */
C
Christoph Hellwig 已提交
681
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
682

C
Christoph Hellwig 已提交
683
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
684
	offset = args->geo->data_first_offset;
685
	ino = xfs_dir2_sf_get_parent_ino(sfp);
L
Linus Torvalds 已提交
686 687
	i8count = ino > XFS_DIR2_MAX_SHORT_INUM;

688
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
C
Christoph Hellwig 已提交
689
	     i < sfp->count;
690
	     i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) {
691
		ASSERT(xfs_dir2_sf_get_offset(sfep) >= offset);
692
		ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
L
Linus Torvalds 已提交
693 694
		i8count += ino > XFS_DIR2_MAX_SHORT_INUM;
		offset =
695
			xfs_dir2_sf_get_offset(sfep) +
696
			xfs_dir2_data_entsize(mp, sfep->namelen);
697
		ASSERT(xfs_dir2_sf_get_ftype(mp, sfep) < XFS_DIR3_FT_MAX);
L
Linus Torvalds 已提交
698
	}
C
Christoph Hellwig 已提交
699
	ASSERT(i8count == sfp->i8count);
700
	ASSERT((char *)sfep - (char *)sfp == dp->i_disk_size);
L
Linus Torvalds 已提交
701
	ASSERT(offset +
C
Christoph Hellwig 已提交
702
	       (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
703
	       (uint)sizeof(xfs_dir2_block_tail_t) <= args->geo->blksize);
L
Linus Torvalds 已提交
704 705 706
}
#endif	/* DEBUG */

707
/* Verify the consistency of an inline directory. */
708
xfs_failaddr_t
709
xfs_dir2_sf_verify(
710
	struct xfs_inode		*ip)
711
{
712
	struct xfs_mount		*mp = ip->i_mount;
713
	struct xfs_ifork		*ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
714
	struct xfs_dir2_sf_hdr		*sfp;
715 716 717 718 719 720 721
	struct xfs_dir2_sf_entry	*sfep;
	struct xfs_dir2_sf_entry	*next_sfep;
	char				*endp;
	xfs_ino_t			ino;
	int				i;
	int				i8count;
	int				offset;
722
	int64_t				size;
723
	int				error;
724
	uint8_t				filetype;
725

726
	ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
727

728 729 730
	sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data;
	size = ifp->if_bytes;

731 732 733
	/*
	 * Give up if the directory is way too short.
	 */
734 735
	if (size <= offsetof(struct xfs_dir2_sf_hdr, parent) ||
	    size < xfs_dir2_sf_hdr_size(sfp->i8count))
736
		return __this_address;
737 738 739 740

	endp = (char *)sfp + size;

	/* Check .. entry */
741
	ino = xfs_dir2_sf_get_parent_ino(sfp);
742
	i8count = ino > XFS_DIR2_MAX_SHORT_INUM;
743 744
	error = xfs_dir_ino_validate(mp, ino);
	if (error)
745
		return __this_address;
746
	offset = mp->m_dir_geo->data_first_offset;
747 748 749 750 751 752 753 754 755

	/* Check all reported entries */
	sfep = xfs_dir2_sf_firstentry(sfp);
	for (i = 0; i < sfp->count; i++) {
		/*
		 * struct xfs_dir2_sf_entry has a variable length.
		 * Check the fixed-offset parts of the structure are
		 * within the data buffer.
		 */
756
		if (((char *)sfep + sizeof(*sfep)) >= endp)
757
			return __this_address;
758 759

		/* Don't allow names with known bad length. */
760
		if (sfep->namelen == 0)
761
			return __this_address;
762 763 764 765 766 767

		/*
		 * 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.
		 */
768
		next_sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
769
		if (endp < (char *)next_sfep)
770
			return __this_address;
771 772

		/* Check that the offsets always increase. */
773
		if (xfs_dir2_sf_get_offset(sfep) < offset)
774
			return __this_address;
775 776

		/* Check the inode number. */
777
		ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
778
		i8count += ino > XFS_DIR2_MAX_SHORT_INUM;
779 780
		error = xfs_dir_ino_validate(mp, ino);
		if (error)
781
			return __this_address;
782 783

		/* Check the file type. */
784
		filetype = xfs_dir2_sf_get_ftype(mp, sfep);
785
		if (filetype >= XFS_DIR3_FT_MAX)
786
			return __this_address;
787 788

		offset = xfs_dir2_sf_get_offset(sfep) +
789
				xfs_dir2_data_entsize(mp, sfep->namelen);
790 791 792

		sfep = next_sfep;
	}
793
	if (i8count != sfp->i8count)
794
		return __this_address;
795
	if ((void *)sfep != (void *)endp)
796
		return __this_address;
797 798

	/* Make sure this whole thing ought to be in local format. */
799 800
	if (offset + (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
	    (uint)sizeof(xfs_dir2_block_tail_t) > mp->m_dir_geo->blksize)
801
		return __this_address;
802

803
	return NULL;
804 805
}

L
Linus Torvalds 已提交
806 807 808 809 810 811 812 813 814 815
/*
 * Create a new (shortform) directory.
 */
int					/* error, always 0 */
xfs_dir2_sf_create(
	xfs_da_args_t	*args,		/* operation arguments */
	xfs_ino_t	pino)		/* parent inode number */
{
	xfs_inode_t	*dp;		/* incore directory inode */
	int		i8count;	/* parent inode is an 8-byte number */
C
Christoph Hellwig 已提交
816
	xfs_dir2_sf_hdr_t *sfp;		/* shortform structure */
L
Linus Torvalds 已提交
817 818
	int		size;		/* directory size */

C
Christoph Hellwig 已提交
819 820
	trace_xfs_dir2_sf_create(args);

L
Linus Torvalds 已提交
821 822 823
	dp = args->dp;

	ASSERT(dp != NULL);
824
	ASSERT(dp->i_disk_size == 0);
L
Linus Torvalds 已提交
825 826 827 828
	/*
	 * If it's currently a zero-length extent file,
	 * convert it to local format.
	 */
829
	if (dp->i_df.if_format == XFS_DINODE_FMT_EXTENTS) {
L
Linus Torvalds 已提交
830
		dp->i_df.if_flags &= ~XFS_IFEXTENTS;	/* just in case */
831
		dp->i_df.if_format = XFS_DINODE_FMT_LOCAL;
L
Linus Torvalds 已提交
832 833 834 835 836 837
		xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
		dp->i_df.if_flags |= XFS_IFINLINE;
	}
	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
	ASSERT(dp->i_df.if_bytes == 0);
	i8count = pino > XFS_DIR2_MAX_SHORT_INUM;
838
	size = xfs_dir2_sf_hdr_size(i8count);
L
Linus Torvalds 已提交
839 840 841 842 843 844 845
	/*
	 * Make a buffer for the data.
	 */
	xfs_idata_realloc(dp, size, XFS_DATA_FORK);
	/*
	 * Fill in the header,
	 */
C
Christoph Hellwig 已提交
846 847
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	sfp->i8count = i8count;
L
Linus Torvalds 已提交
848 849 850
	/*
	 * Now can put in the inode number, since i8count is set.
	 */
851
	xfs_dir2_sf_put_parent_ino(sfp, pino);
C
Christoph Hellwig 已提交
852
	sfp->count = 0;
853
	dp->i_disk_size = size;
L
Linus Torvalds 已提交
854 855 856 857 858 859 860 861 862 863 864 865 866
	xfs_dir2_sf_check(args);
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
	return 0;
}

/*
 * Lookup an entry in a shortform directory.
 * Returns EEXIST if found, ENOENT if not found.
 */
int						/* error */
xfs_dir2_sf_lookup(
	xfs_da_args_t		*args)		/* operation arguments */
{
867 868
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
869
	int			i;		/* entry index */
870
	int			error;
L
Linus Torvalds 已提交
871
	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
C
Christoph Hellwig 已提交
872
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
873
	enum xfs_dacmp		cmp;		/* comparison result */
874
	xfs_dir2_sf_entry_t	*ci_sfep;	/* case-insens. entry */
L
Linus Torvalds 已提交
875

C
Christoph Hellwig 已提交
876 877
	trace_xfs_dir2_sf_lookup(args);

L
Linus Torvalds 已提交
878 879 880
	xfs_dir2_sf_check(args);

	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
881 882
	ASSERT(dp->i_disk_size >= offsetof(struct xfs_dir2_sf_hdr, parent));
	ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
L
Linus Torvalds 已提交
883
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
884
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
885
	ASSERT(dp->i_disk_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
L
Linus Torvalds 已提交
886 887 888 889 890
	/*
	 * Special case for .
	 */
	if (args->namelen == 1 && args->name[0] == '.') {
		args->inumber = dp->i_ino;
891
		args->cmpresult = XFS_CMP_EXACT;
892
		args->filetype = XFS_DIR3_FT_DIR;
D
Dave Chinner 已提交
893
		return -EEXIST;
L
Linus Torvalds 已提交
894 895 896 897 898 899
	}
	/*
	 * Special case for ..
	 */
	if (args->namelen == 2 &&
	    args->name[0] == '.' && args->name[1] == '.') {
900
		args->inumber = xfs_dir2_sf_get_parent_ino(sfp);
901
		args->cmpresult = XFS_CMP_EXACT;
902
		args->filetype = XFS_DIR3_FT_DIR;
D
Dave Chinner 已提交
903
		return -EEXIST;
L
Linus Torvalds 已提交
904 905 906 907
	}
	/*
	 * Loop over all the entries trying to match ours.
	 */
908
	ci_sfep = NULL;
C
Christoph Hellwig 已提交
909
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
910
	     i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) {
911 912 913 914 915
		/*
		 * Compare name and if it's an exact match, return the inode
		 * number. If it's the first case-insensitive match, store the
		 * inode number and continue looking for an exact match.
		 */
916
		cmp = xfs_dir2_compname(args, sfep->name, sfep->namelen);
917 918
		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
			args->cmpresult = cmp;
919
			args->inumber = xfs_dir2_sf_get_ino(mp, sfp, sfep);
920
			args->filetype = xfs_dir2_sf_get_ftype(mp, sfep);
921
			if (cmp == XFS_CMP_EXACT)
D
Dave Chinner 已提交
922
				return -EEXIST;
923
			ci_sfep = sfep;
L
Linus Torvalds 已提交
924 925
		}
	}
926
	ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
927 928
	/*
	 * Here, we can only be doing a lookup (not a rename or replace).
D
Dave Chinner 已提交
929
	 * If a case-insensitive match was not found, return -ENOENT.
930
	 */
931
	if (!ci_sfep)
D
Dave Chinner 已提交
932
		return -ENOENT;
933 934
	/* otherwise process the CI match as required by the caller */
	error = xfs_dir_cilookup_result(args, ci_sfep->name, ci_sfep->namelen);
E
Eric Sandeen 已提交
935
	return error;
L
Linus Torvalds 已提交
936 937 938 939 940 941 942 943 944
}

/*
 * Remove an entry from a shortform directory.
 */
int						/* error */
xfs_dir2_sf_removename(
	xfs_da_args_t		*args)
{
945 946
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
947 948 949 950 951 952
	int			byteoff;	/* offset of removed entry */
	int			entsize;	/* this entry's size */
	int			i;		/* shortform entry index */
	int			newsize;	/* new inode size */
	int			oldsize;	/* old inode size */
	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
C
Christoph Hellwig 已提交
953
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
954

C
Christoph Hellwig 已提交
955 956
	trace_xfs_dir2_sf_removename(args);

L
Linus Torvalds 已提交
957
	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
958
	oldsize = (int)dp->i_disk_size;
959
	ASSERT(oldsize >= offsetof(struct xfs_dir2_sf_hdr, parent));
L
Linus Torvalds 已提交
960 961
	ASSERT(dp->i_df.if_bytes == oldsize);
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
962 963
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(oldsize >= xfs_dir2_sf_hdr_size(sfp->i8count));
L
Linus Torvalds 已提交
964 965 966 967
	/*
	 * Loop over the old directory entries.
	 * Find the one we're deleting.
	 */
C
Christoph Hellwig 已提交
968
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
969
	     i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) {
970 971
		if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
								XFS_CMP_EXACT) {
972
			ASSERT(xfs_dir2_sf_get_ino(mp, sfp, sfep) ==
973
			       args->inumber);
L
Linus Torvalds 已提交
974 975 976 977 978 979
			break;
		}
	}
	/*
	 * Didn't find it.
	 */
C
Christoph Hellwig 已提交
980
	if (i == sfp->count)
D
Dave Chinner 已提交
981
		return -ENOENT;
L
Linus Torvalds 已提交
982 983 984 985
	/*
	 * Calculate sizes.
	 */
	byteoff = (int)((char *)sfep - (char *)sfp);
986
	entsize = xfs_dir2_sf_entsize(mp, sfp, args->namelen);
L
Linus Torvalds 已提交
987 988 989 990 991 992 993 994 995 996
	newsize = oldsize - entsize;
	/*
	 * Copy the part if any after the removed entry, sliding it down.
	 */
	if (byteoff + entsize < oldsize)
		memmove((char *)sfp + byteoff, (char *)sfp + byteoff + entsize,
			oldsize - (byteoff + entsize));
	/*
	 * Fix up the header and file size.
	 */
C
Christoph Hellwig 已提交
997
	sfp->count--;
998
	dp->i_disk_size = newsize;
L
Linus Torvalds 已提交
999 1000 1001 1002
	/*
	 * Reallocate, making it smaller.
	 */
	xfs_idata_realloc(dp, newsize - oldsize, XFS_DATA_FORK);
C
Christoph Hellwig 已提交
1003
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1004 1005 1006 1007
	/*
	 * Are we changing inode number size?
	 */
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) {
C
Christoph Hellwig 已提交
1008
		if (sfp->i8count == 1)
L
Linus Torvalds 已提交
1009 1010
			xfs_dir2_sf_toino4(args);
		else
C
Christoph Hellwig 已提交
1011
			sfp->i8count--;
L
Linus Torvalds 已提交
1012 1013 1014 1015 1016 1017
	}
	xfs_dir2_sf_check(args);
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
	return 0;
}

1018 1019 1020
/*
 * Check whether the sf dir replace operation need more blocks.
 */
1021
static bool
1022 1023 1024 1025 1026 1027 1028
xfs_dir2_sf_replace_needblock(
	struct xfs_inode	*dp,
	xfs_ino_t		inum)
{
	int			newsize;
	struct xfs_dir2_sf_hdr	*sfp;

1029
	if (dp->i_df.if_format != XFS_DINODE_FMT_LOCAL)
1030 1031 1032 1033 1034 1035 1036 1037 1038
		return false;

	sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
	newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF;

	return inum > XFS_DIR2_MAX_SHORT_INUM &&
	       sfp->i8count == 0 && newsize > XFS_IFORK_DSIZE(dp);
}

L
Linus Torvalds 已提交
1039 1040 1041 1042 1043 1044 1045
/*
 * Replace the inode number of an entry in a shortform directory.
 */
int						/* error */
xfs_dir2_sf_replace(
	xfs_da_args_t		*args)		/* operation arguments */
{
1046 1047
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
1048 1049 1050 1051
	int			i;		/* entry index */
	xfs_ino_t		ino=0;		/* entry old inode number */
	int			i8elevated;	/* sf_toino8 set i8count=1 */
	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
C
Christoph Hellwig 已提交
1052
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
1053

C
Christoph Hellwig 已提交
1054 1055
	trace_xfs_dir2_sf_replace(args);

L
Linus Torvalds 已提交
1056
	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
1057 1058
	ASSERT(dp->i_disk_size >= offsetof(struct xfs_dir2_sf_hdr, parent));
	ASSERT(dp->i_df.if_bytes == dp->i_disk_size);
L
Linus Torvalds 已提交
1059
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
1060
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
1061
	ASSERT(dp->i_disk_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
C
Christoph Hellwig 已提交
1062

L
Linus Torvalds 已提交
1063 1064 1065
	/*
	 * New inode number is large, and need to convert to 8-byte inodes.
	 */
C
Christoph Hellwig 已提交
1066
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
L
Linus Torvalds 已提交
1067 1068 1069 1070 1071
		int	error;			/* error return value */

		/*
		 * Won't fit as shortform, convert to block then do replace.
		 */
1072
		if (xfs_dir2_sf_replace_needblock(dp, args->inumber)) {
L
Linus Torvalds 已提交
1073
			error = xfs_dir2_sf_to_block(args);
1074
			if (error)
L
Linus Torvalds 已提交
1075 1076 1077 1078 1079 1080 1081 1082
				return error;
			return xfs_dir2_block_replace(args);
		}
		/*
		 * Still fits, convert to 8-byte now.
		 */
		xfs_dir2_sf_toino8(args);
		i8elevated = 1;
C
Christoph Hellwig 已提交
1083
		sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1084 1085
	} else
		i8elevated = 0;
C
Christoph Hellwig 已提交
1086

L
Linus Torvalds 已提交
1087 1088 1089 1090 1091 1092
	ASSERT(args->namelen != 1 || args->name[0] != '.');
	/*
	 * Replace ..'s entry.
	 */
	if (args->namelen == 2 &&
	    args->name[0] == '.' && args->name[1] == '.') {
1093
		ino = xfs_dir2_sf_get_parent_ino(sfp);
L
Linus Torvalds 已提交
1094
		ASSERT(args->inumber != ino);
1095
		xfs_dir2_sf_put_parent_ino(sfp, args->inumber);
L
Linus Torvalds 已提交
1096 1097 1098 1099 1100
	}
	/*
	 * Normal entry, look for the name.
	 */
	else {
1101
		for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
1102
		     i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) {
1103 1104
			if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
								XFS_CMP_EXACT) {
1105
				ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
L
Linus Torvalds 已提交
1106
				ASSERT(args->inumber != ino);
1107 1108
				xfs_dir2_sf_put_ino(mp, sfp, sfep,
						args->inumber);
1109
				xfs_dir2_sf_put_ftype(mp, sfep, args->filetype);
L
Linus Torvalds 已提交
1110 1111 1112 1113 1114 1115
				break;
			}
		}
		/*
		 * Didn't find it.
		 */
C
Christoph Hellwig 已提交
1116
		if (i == sfp->count) {
1117
			ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
L
Linus Torvalds 已提交
1118 1119
			if (i8elevated)
				xfs_dir2_sf_toino4(args);
D
Dave Chinner 已提交
1120
			return -ENOENT;
L
Linus Torvalds 已提交
1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
		}
	}
	/*
	 * See if the old number was large, the new number is small.
	 */
	if (ino > XFS_DIR2_MAX_SHORT_INUM &&
	    args->inumber <= XFS_DIR2_MAX_SHORT_INUM) {
		/*
		 * And the old count was one, so need to convert to small.
		 */
C
Christoph Hellwig 已提交
1131
		if (sfp->i8count == 1)
L
Linus Torvalds 已提交
1132 1133
			xfs_dir2_sf_toino4(args);
		else
C
Christoph Hellwig 已提交
1134
			sfp->i8count--;
L
Linus Torvalds 已提交
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
	}
	/*
	 * See if the old number was small, the new number is large.
	 */
	if (ino <= XFS_DIR2_MAX_SHORT_INUM &&
	    args->inumber > XFS_DIR2_MAX_SHORT_INUM) {
		/*
		 * add to the i8count unless we just converted to 8-byte
		 * inodes (which does an implied i8count = 1)
		 */
C
Christoph Hellwig 已提交
1145
		ASSERT(sfp->i8count != 0);
L
Linus Torvalds 已提交
1146
		if (!i8elevated)
C
Christoph Hellwig 已提交
1147
			sfp->i8count++;
L
Linus Torvalds 已提交
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
	}
	xfs_dir2_sf_check(args);
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA);
	return 0;
}

/*
 * Convert from 8-byte inode numbers to 4-byte inode numbers.
 * The last 8-byte inode number is gone, but the count is still 1.
 */
static void
xfs_dir2_sf_toino4(
	xfs_da_args_t		*args)		/* operation arguments */
{
1162 1163
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
1164 1165 1166 1167
	char			*buf;		/* old dir's buffer */
	int			i;		/* entry index */
	int			newsize;	/* new inode size */
	xfs_dir2_sf_entry_t	*oldsfep;	/* old sf entry */
C
Christoph Hellwig 已提交
1168
	xfs_dir2_sf_hdr_t	*oldsfp;	/* old sf directory */
L
Linus Torvalds 已提交
1169 1170
	int			oldsize;	/* old inode size */
	xfs_dir2_sf_entry_t	*sfep;		/* new sf entry */
C
Christoph Hellwig 已提交
1171
	xfs_dir2_sf_hdr_t	*sfp;		/* new sf directory */
L
Linus Torvalds 已提交
1172

C
Christoph Hellwig 已提交
1173 1174
	trace_xfs_dir2_sf_toino4(args);

L
Linus Torvalds 已提交
1175 1176 1177 1178 1179 1180
	/*
	 * Copy the old directory to the buffer.
	 * Then nuke it from the inode, and add the new buffer to the inode.
	 * Don't want xfs_idata_realloc copying the data here.
	 */
	oldsize = dp->i_df.if_bytes;
1181
	buf = kmem_alloc(oldsize, 0);
C
Christoph Hellwig 已提交
1182 1183
	oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(oldsfp->i8count == 1);
L
Linus Torvalds 已提交
1184 1185 1186 1187
	memcpy(buf, oldsfp, oldsize);
	/*
	 * Compute the new inode size.
	 */
C
Christoph Hellwig 已提交
1188
	newsize = oldsize - (oldsfp->count + 1) * XFS_INO64_DIFF;
L
Linus Torvalds 已提交
1189 1190 1191 1192 1193
	xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK);
	xfs_idata_realloc(dp, newsize, XFS_DATA_FORK);
	/*
	 * Reset our pointers, the data has moved.
	 */
C
Christoph Hellwig 已提交
1194 1195
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1196 1197 1198
	/*
	 * Fill in the new header.
	 */
C
Christoph Hellwig 已提交
1199 1200
	sfp->count = oldsfp->count;
	sfp->i8count = 0;
1201
	xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp));
L
Linus Torvalds 已提交
1202 1203 1204
	/*
	 * Copy the entries field by field.
	 */
1205 1206
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
		    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
C
Christoph Hellwig 已提交
1207
	     i < sfp->count;
1208 1209
	     i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep),
		  oldsfep = xfs_dir2_sf_nextentry(mp, oldsfp, oldsfep)) {
L
Linus Torvalds 已提交
1210
		sfep->namelen = oldsfep->namelen;
C
Christoph Hellwig 已提交
1211
		memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset));
L
Linus Torvalds 已提交
1212
		memcpy(sfep->name, oldsfep->name, sfep->namelen);
1213 1214
		xfs_dir2_sf_put_ino(mp, sfp, sfep,
				xfs_dir2_sf_get_ino(mp, oldsfp, oldsfep));
1215 1216
		xfs_dir2_sf_put_ftype(mp, sfep,
				xfs_dir2_sf_get_ftype(mp, oldsfep));
L
Linus Torvalds 已提交
1217 1218 1219 1220
	}
	/*
	 * Clean up the inode.
	 */
1221
	kmem_free(buf);
1222
	dp->i_disk_size = newsize;
L
Linus Torvalds 已提交
1223 1224 1225 1226
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}

/*
1227 1228 1229
 * Convert existing entries from 4-byte inode numbers to 8-byte inode numbers.
 * The new entry w/ an 8-byte inode number is not there yet; we leave with
 * i8count set to 1, but no corresponding 8-byte entry.
L
Linus Torvalds 已提交
1230 1231 1232 1233 1234
 */
static void
xfs_dir2_sf_toino8(
	xfs_da_args_t		*args)		/* operation arguments */
{
1235 1236
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
L
Linus Torvalds 已提交
1237 1238 1239 1240
	char			*buf;		/* old dir's buffer */
	int			i;		/* entry index */
	int			newsize;	/* new inode size */
	xfs_dir2_sf_entry_t	*oldsfep;	/* old sf entry */
C
Christoph Hellwig 已提交
1241
	xfs_dir2_sf_hdr_t	*oldsfp;	/* old sf directory */
L
Linus Torvalds 已提交
1242 1243
	int			oldsize;	/* old inode size */
	xfs_dir2_sf_entry_t	*sfep;		/* new sf entry */
C
Christoph Hellwig 已提交
1244
	xfs_dir2_sf_hdr_t	*sfp;		/* new sf directory */
L
Linus Torvalds 已提交
1245

C
Christoph Hellwig 已提交
1246 1247
	trace_xfs_dir2_sf_toino8(args);

L
Linus Torvalds 已提交
1248 1249 1250 1251 1252 1253
	/*
	 * Copy the old directory to the buffer.
	 * Then nuke it from the inode, and add the new buffer to the inode.
	 * Don't want xfs_idata_realloc copying the data here.
	 */
	oldsize = dp->i_df.if_bytes;
1254
	buf = kmem_alloc(oldsize, 0);
C
Christoph Hellwig 已提交
1255 1256
	oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(oldsfp->i8count == 0);
L
Linus Torvalds 已提交
1257 1258
	memcpy(buf, oldsfp, oldsize);
	/*
1259
	 * Compute the new inode size (nb: entry count + 1 for parent)
L
Linus Torvalds 已提交
1260
	 */
C
Christoph Hellwig 已提交
1261
	newsize = oldsize + (oldsfp->count + 1) * XFS_INO64_DIFF;
L
Linus Torvalds 已提交
1262 1263 1264 1265 1266
	xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK);
	xfs_idata_realloc(dp, newsize, XFS_DATA_FORK);
	/*
	 * Reset our pointers, the data has moved.
	 */
C
Christoph Hellwig 已提交
1267 1268
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1269 1270 1271
	/*
	 * Fill in the new header.
	 */
C
Christoph Hellwig 已提交
1272 1273
	sfp->count = oldsfp->count;
	sfp->i8count = 1;
1274
	xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp));
L
Linus Torvalds 已提交
1275 1276 1277
	/*
	 * Copy the entries field by field.
	 */
1278 1279
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
		    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
C
Christoph Hellwig 已提交
1280
	     i < sfp->count;
1281 1282
	     i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep),
		  oldsfep = xfs_dir2_sf_nextentry(mp, oldsfp, oldsfep)) {
L
Linus Torvalds 已提交
1283
		sfep->namelen = oldsfep->namelen;
C
Christoph Hellwig 已提交
1284
		memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset));
L
Linus Torvalds 已提交
1285
		memcpy(sfep->name, oldsfep->name, sfep->namelen);
1286 1287
		xfs_dir2_sf_put_ino(mp, sfp, sfep,
				xfs_dir2_sf_get_ino(mp, oldsfp, oldsfep));
1288 1289
		xfs_dir2_sf_put_ftype(mp, sfep,
				xfs_dir2_sf_get_ftype(mp, oldsfep));
L
Linus Torvalds 已提交
1290 1291 1292 1293
	}
	/*
	 * Clean up the inode.
	 */
1294
	kmem_free(buf);
1295
	dp->i_disk_size = newsize;
L
Linus Torvalds 已提交
1296 1297
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}