xfs_dir2_sf.c 36.4 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2 3
 * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
 * All Rights Reserved.
L
Linus Torvalds 已提交
4
 *
5 6
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
L
Linus Torvalds 已提交
7 8
 * published by the Free Software Foundation.
 *
9 10 11 12
 * This program is distributed in the hope that it would be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
L
Linus Torvalds 已提交
13
 *
14 15 16
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
L
Linus Torvalds 已提交
17 18
 */
#include "xfs.h"
19
#include "xfs_fs.h"
L
Linus Torvalds 已提交
20 21 22 23
#include "xfs_types.h"
#include "xfs_log.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
24
#include "xfs_ag.h"
L
Linus Torvalds 已提交
25
#include "xfs_mount.h"
26
#include "xfs_da_btree.h"
L
Linus Torvalds 已提交
27 28 29
#include "xfs_bmap_btree.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
30
#include "xfs_inode_item.h"
L
Linus Torvalds 已提交
31
#include "xfs_error.h"
C
Christoph Hellwig 已提交
32
#include "xfs_dir2_format.h"
33
#include "xfs_dir2.h"
C
Christoph Hellwig 已提交
34
#include "xfs_dir2_priv.h"
C
Christoph Hellwig 已提交
35
#include "xfs_trace.h"
L
Linus Torvalds 已提交
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

/*
 * 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 */
#if XFS_BIG_INUMS
static void xfs_dir2_sf_toino4(xfs_da_args_t *args);
static void xfs_dir2_sf_toino8(xfs_da_args_t *args);
#endif /* XFS_BIG_INUMS */

59 60 61 62
/*
 * Inode numbers in short-form directories can come in two versions,
 * either 4 bytes or 8 bytes wide.  These helpers deal with the
 * two forms transparently by looking at the headers i8count field.
63 64
 *
 * For 64-bit inode number the most significant byte must be zero.
65 66 67
 */
static xfs_ino_t
xfs_dir2_sf_get_ino(
C
Christoph Hellwig 已提交
68
	struct xfs_dir2_sf_hdr	*hdr,
69 70
	xfs_dir2_inou_t		*from)
{
C
Christoph Hellwig 已提交
71
	if (hdr->i8count)
72
		return get_unaligned_be64(&from->i8.i) & 0x00ffffffffffffffULL;
73
	else
74
		return get_unaligned_be32(&from->i4.i);
75 76 77 78
}

static void
xfs_dir2_sf_put_ino(
C
Christoph Hellwig 已提交
79
	struct xfs_dir2_sf_hdr	*hdr,
80 81 82
	xfs_dir2_inou_t		*to,
	xfs_ino_t		ino)
{
83 84
	ASSERT((ino & 0xff00000000000000ULL) == 0);

C
Christoph Hellwig 已提交
85
	if (hdr->i8count)
86
		put_unaligned_be64(ino, &to->i8.i);
87
	else
88
		put_unaligned_be32(ino, &to->i4.i);
89 90 91 92
}

xfs_ino_t
xfs_dir2_sf_get_parent_ino(
C
Christoph Hellwig 已提交
93
	struct xfs_dir2_sf_hdr	*hdr)
94
{
C
Christoph Hellwig 已提交
95
	return xfs_dir2_sf_get_ino(hdr, &hdr->parent);
96 97
}

98
void
99
xfs_dir2_sf_put_parent_ino(
C
Christoph Hellwig 已提交
100
	struct xfs_dir2_sf_hdr	*hdr,
101 102
	xfs_ino_t		ino)
{
C
Christoph Hellwig 已提交
103
	xfs_dir2_sf_put_ino(hdr, &hdr->parent, ino);
104 105 106 107
}

/*
 * In short-form directory entries the inode numbers are stored at variable
108 109 110
 * offset behind the entry name. If the entry stores a filetype value, then it
 * sits between the name and the inode number. Hence the inode numbers may only
 * be accessed through the helpers below.
111 112
 */
static xfs_dir2_inou_t *
113 114
xfs_dir3_sfe_inop(
	struct xfs_mount	*mp,
115 116
	struct xfs_dir2_sf_entry *sfep)
{
117 118 119 120
	__uint8_t	*ptr = &sfep->name[sfep->namelen];
	if (xfs_sb_version_hasftype(&mp->m_sb))
		ptr++;
	return (xfs_dir2_inou_t *)ptr;
121 122 123
}

xfs_ino_t
124 125
xfs_dir3_sfe_get_ino(
	struct xfs_mount	*mp,
C
Christoph Hellwig 已提交
126
	struct xfs_dir2_sf_hdr	*hdr,
127 128
	struct xfs_dir2_sf_entry *sfep)
{
129
	return xfs_dir2_sf_get_ino(hdr, xfs_dir3_sfe_inop(mp, sfep));
130 131
}

132
void
133 134
xfs_dir3_sfe_put_ino(
	struct xfs_mount	*mp,
C
Christoph Hellwig 已提交
135
	struct xfs_dir2_sf_hdr	*hdr,
136 137 138
	struct xfs_dir2_sf_entry *sfep,
	xfs_ino_t		ino)
{
139
	xfs_dir2_sf_put_ino(hdr, xfs_dir3_sfe_inop(mp, sfep), ino);
140 141
}

L
Linus Torvalds 已提交
142 143 144 145 146 147 148 149 150
/*
 * 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 */
151
	xfs_dir2_data_hdr_t	*hdr,		/* block directory data */
L
Linus Torvalds 已提交
152 153 154 155 156 157 158 159 160 161 162 163 164
	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 */
165
	xfs_ino_t		parent = 0;	/* parent inode number */
L
Linus Torvalds 已提交
166
	int			size=0;		/* total computed size */
167
	int			has_ftype;
L
Linus Torvalds 已提交
168 169 170

	mp = dp->i_mount;

171 172 173 174 175 176
	/*
	 * 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 已提交
177
	count = i8count = namelen = 0;
178
	btp = xfs_dir2_block_tail_p(mp, hdr);
179
	blp = xfs_dir2_block_leaf_p(btp);
L
Linus Torvalds 已提交
180 181 182 183

	/*
	 * Iterate over the block's data entries by using the leaf pointers.
	 */
184
	for (i = 0; i < be32_to_cpu(btp->count); i++) {
185
		if ((addr = be32_to_cpu(blp[i].address)) == XFS_DIR2_NULL_DATAPTR)
L
Linus Torvalds 已提交
186 187 188 189 190
			continue;
		/*
		 * Calculate the pointer to the entry at hand.
		 */
		dep = (xfs_dir2_data_entry_t *)
191
		      ((char *)hdr + xfs_dir2_dataptr_to_off(mp, addr));
L
Linus Torvalds 已提交
192 193 194 195 196 197 198 199 200 201 202
		/*
		 * 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] == '.';
#if XFS_BIG_INUMS
		if (!isdot)
203
			i8count += be64_to_cpu(dep->inumber) > XFS_DIR2_MAX_SHORT_INUM;
L
Linus Torvalds 已提交
204
#endif
205
		/* take into account the file type field */
L
Linus Torvalds 已提交
206 207
		if (!isdot && !isdotdot) {
			count++;
208
			namelen += dep->namelen + has_ftype;
L
Linus Torvalds 已提交
209
		} else if (isdotdot)
210
			parent = be64_to_cpu(dep->inumber);
L
Linus Torvalds 已提交
211 212 213
		/*
		 * Calculate the new size, see if we should give up yet.
		 */
214
		size = xfs_dir2_sf_hdr_size(i8count) +		/* header */
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222 223 224 225 226 227 228
		       count +					/* namelen */
		       count * (uint)sizeof(xfs_dir2_sf_off_t) + /* offset */
		       namelen +				/* name */
		       (i8count ?				/* inumber */
				(uint)sizeof(xfs_dir2_ino8_t) * count :
				(uint)sizeof(xfs_dir2_ino4_t) * count);
		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;
C
Christoph Hellwig 已提交
229
	xfs_dir2_sf_put_parent_ino(sfhp, parent);
L
Linus Torvalds 已提交
230 231 232 233 234 235 236 237 238 239
	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(
	xfs_da_args_t		*args,		/* operation arguments */
240
	struct xfs_buf		*bp,
L
Linus Torvalds 已提交
241 242 243
	int			size,		/* shortform directory size */
	xfs_dir2_sf_hdr_t	*sfhp)		/* shortform directory hdr */
{
244
	xfs_dir2_data_hdr_t	*hdr;		/* block header */
L
Linus Torvalds 已提交
245 246 247 248 249 250 251 252 253 254
	xfs_dir2_block_tail_t	*btp;		/* block tail pointer */
	xfs_dir2_data_entry_t	*dep;		/* data entry pointer */
	xfs_inode_t		*dp;		/* incore directory inode */
	xfs_dir2_data_unused_t	*dup;		/* unused data pointer */
	char			*endptr;	/* end of data entries */
	int			error;		/* error return value */
	int			logflags;	/* inode logging flags */
	xfs_mount_t		*mp;		/* filesystem mount point */
	char			*ptr;		/* current data pointer */
	xfs_dir2_sf_entry_t	*sfep;		/* shortform entry */
C
Christoph Hellwig 已提交
255
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform directory header */
L
Linus Torvalds 已提交
256

C
Christoph Hellwig 已提交
257 258
	trace_xfs_dir2_block_to_sf(args);

L
Linus Torvalds 已提交
259 260 261 262 263 264 265
	dp = args->dp;
	mp = dp->i_mount;

	/*
	 * Make a copy of the block data, so we can shrink the inode
	 * and add local data.
	 */
266
	hdr = kmem_alloc(mp->m_dirblksize, KM_SLEEP);
267
	memcpy(hdr, bp->b_addr, mp->m_dirblksize);
L
Linus Torvalds 已提交
268 269 270 271 272
	logflags = XFS_ILOG_CORE;
	if ((error = xfs_dir2_shrink_inode(args, mp->m_dirdatablk, bp))) {
		ASSERT(error != ENOSPC);
		goto out;
	}
273

L
Linus Torvalds 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
	/*
	 * The buffer is now unconditionally gone, whether
	 * xfs_dir2_shrink_inode worked or not.
	 *
	 * Convert the inode to local format.
	 */
	dp->i_df.if_flags &= ~XFS_IFEXTENTS;
	dp->i_df.if_flags |= XFS_IFINLINE;
	dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
	ASSERT(dp->i_df.if_bytes == 0);
	xfs_idata_realloc(dp, size, XFS_DATA_FORK);
	logflags |= XFS_ILOG_DDATA;
	/*
	 * Copy the header into the newly allocate local space.
	 */
C
Christoph Hellwig 已提交
289
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
290
	memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count));
L
Linus Torvalds 已提交
291 292 293 294
	dp->i_d.di_size = size;
	/*
	 * Set up to loop over the block's entries.
	 */
295
	btp = xfs_dir2_block_tail_p(mp, hdr);
296
	ptr = (char *)xfs_dir3_data_entry_p(hdr);
297 298
	endptr = (char *)xfs_dir2_block_leaf_p(btp);
	sfep = xfs_dir2_sf_firstentry(sfp);
L
Linus Torvalds 已提交
299 300 301 302 303 304 305 306 307
	/*
	 * Loop over the active and unused entries.
	 * Stop when we reach the leaf/tail portion of the block.
	 */
	while (ptr < endptr) {
		/*
		 * If it's unused, just skip over it.
		 */
		dup = (xfs_dir2_data_unused_t *)ptr;
308 309
		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
			ptr += be16_to_cpu(dup->length);
L
Linus Torvalds 已提交
310 311 312 313 314 315 316
			continue;
		}
		dep = (xfs_dir2_data_entry_t *)ptr;
		/*
		 * Skip .
		 */
		if (dep->namelen == 1 && dep->name[0] == '.')
317
			ASSERT(be64_to_cpu(dep->inumber) == dp->i_ino);
L
Linus Torvalds 已提交
318 319 320 321 322
		/*
		 * Skip .., but make sure the inode number is right.
		 */
		else if (dep->namelen == 2 &&
			 dep->name[0] == '.' && dep->name[1] == '.')
323
			ASSERT(be64_to_cpu(dep->inumber) ==
324
			       xfs_dir2_sf_get_parent_ino(sfp));
L
Linus Torvalds 已提交
325 326 327 328 329
		/*
		 * Normal entry, copy it into shortform.
		 */
		else {
			sfep->namelen = dep->namelen;
330
			xfs_dir2_sf_put_offset(sfep,
L
Linus Torvalds 已提交
331
				(xfs_dir2_data_aoff_t)
332
				((char *)dep - (char *)hdr));
L
Linus Torvalds 已提交
333
			memcpy(sfep->name, dep->name, dep->namelen);
334
			xfs_dir3_sfe_put_ino(mp, sfp, sfep,
335
					     be64_to_cpu(dep->inumber));
336 337
			xfs_dir3_sfe_put_ftype(mp, sfp, sfep,
					xfs_dir3_dirent_get_ftype(mp, dep));
338

339
			sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep);
L
Linus Torvalds 已提交
340
		}
341
		ptr += xfs_dir3_data_entsize(mp, dep->namelen);
L
Linus Torvalds 已提交
342 343 344 345 346
	}
	ASSERT((char *)sfep - (char *)sfp == size);
	xfs_dir2_sf_check(args);
out:
	xfs_trans_log_inode(args->trans, dp, logflags);
347
	kmem_free(hdr);
L
Linus Torvalds 已提交
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
	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 */
{
	int			add_entsize;	/* size of the new entry */
	xfs_inode_t		*dp;		/* incore directory inode */
	int			error;		/* error return value */
	int			incr_isize;	/* total change in size */
	int			new_isize;	/* di_size after adding name */
	int			objchange;	/* changing to 8-byte inodes */
367
	xfs_dir2_data_aoff_t	offset = 0;	/* offset for new entry */
L
Linus Torvalds 已提交
368 369
	int			old_isize;	/* di_size before adding name */
	int			pick;		/* which algorithm to use */
C
Christoph Hellwig 已提交
370
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
371
	xfs_dir2_sf_entry_t	*sfep = NULL;	/* shortform entry */
L
Linus Torvalds 已提交
372

C
Christoph Hellwig 已提交
373 374
	trace_xfs_dir2_sf_addname(args);

L
Linus Torvalds 已提交
375 376 377 378 379 380 381 382 383 384 385 386
	ASSERT(xfs_dir2_sf_lookup(args) == ENOENT);
	dp = args->dp;
	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
	/*
	 * Make sure the shortform value has some of its header.
	 */
	if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
		ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
		return XFS_ERROR(EIO);
	}
	ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
387 388
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
L
Linus Torvalds 已提交
389 390 391
	/*
	 * Compute entry (and change in) size.
	 */
392
	add_entsize = xfs_dir3_sf_entsize(dp->i_mount, sfp, args->namelen);
L
Linus Torvalds 已提交
393 394 395 396 397 398
	incr_isize = add_entsize;
	objchange = 0;
#if XFS_BIG_INUMS
	/*
	 * Do we have to change to 8 byte inodes?
	 */
C
Christoph Hellwig 已提交
399
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
L
Linus Torvalds 已提交
400 401 402 403 404 405 406
		/*
		 * Yes, adjust the entry size and the total size.
		 */
		add_entsize +=
			(uint)sizeof(xfs_dir2_ino8_t) -
			(uint)sizeof(xfs_dir2_ino4_t);
		incr_isize +=
C
Christoph Hellwig 已提交
407
			(sfp->count + 2) *
L
Linus Torvalds 已提交
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
			((uint)sizeof(xfs_dir2_ino8_t) -
			 (uint)sizeof(xfs_dir2_ino4_t));
		objchange = 1;
	}
#endif
	old_isize = (int)dp->i_d.di_size;
	new_isize = old_isize + incr_isize;
	/*
	 * 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.
		 */
425
		if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0)
L
Linus Torvalds 已提交
426 427 428 429 430 431 432 433 434 435 436 437
			return XFS_ERROR(ENOSPC);
		/*
		 * 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.
	 */
438
	if (args->op_flags & XFS_DA_OP_JUSTCHECK)
L
Linus Torvalds 已提交
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
		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 XFS_BIG_INUMS
		if (objchange)
			xfs_dir2_sf_toino8(args);
#endif
		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 */
{
	int			byteoff;	/* byte offset in sf dir */
	xfs_inode_t		*dp;		/* incore directory inode */
C
Christoph Hellwig 已提交
477
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
478 479 480

	dp = args->dp;

C
Christoph Hellwig 已提交
481
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
482 483 484 485
	byteoff = (int)((char *)sfep - (char *)sfp);
	/*
	 * Grow the in-inode space.
	 */
486 487 488
	xfs_idata_realloc(dp,
			  xfs_dir3_sf_entsize(dp->i_mount, sfp, args->namelen),
			  XFS_DATA_FORK);
L
Linus Torvalds 已提交
489 490 491
	/*
	 * Need to set up again due to realloc of the inode data.
	 */
C
Christoph Hellwig 已提交
492
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
493 494 495 496 497
	sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff);
	/*
	 * Fill in the new entry.
	 */
	sfep->namelen = args->namelen;
498
	xfs_dir2_sf_put_offset(sfep, offset);
L
Linus Torvalds 已提交
499
	memcpy(sfep->name, args->name, sfep->namelen);
500
	xfs_dir3_sfe_put_ino(dp->i_mount, sfp, sfep, args->inumber);
501 502
	xfs_dir3_sfe_put_ftype(dp->i_mount, sfp, sfep, args->filetype);

L
Linus Torvalds 已提交
503 504 505
	/*
	 * Update the header and inode.
	 */
C
Christoph Hellwig 已提交
506
	sfp->count++;
L
Linus Torvalds 已提交
507 508
#if XFS_BIG_INUMS
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM)
C
Christoph Hellwig 已提交
509
		sfp->i8count++;
L
Linus Torvalds 已提交
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
#endif
	dp->i_d.di_size = new_isize;
	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 */
{
	int			add_datasize;	/* data size need for new ent */
	char			*buf;		/* buffer for old */
	xfs_inode_t		*dp;		/* incore directory inode */
	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 */
	int			old_isize;	/* previous di_size */
	xfs_dir2_sf_entry_t	*oldsfep;	/* entry in original dir */
C
Christoph Hellwig 已提交
539
	xfs_dir2_sf_hdr_t	*oldsfp;	/* original shortform dir */
L
Linus Torvalds 已提交
540
	xfs_dir2_sf_entry_t	*sfep;		/* entry in new dir */
C
Christoph Hellwig 已提交
541
	xfs_dir2_sf_hdr_t	*sfp;		/* new shortform dir */
542
	struct xfs_mount	*mp;
L
Linus Torvalds 已提交
543 544 545 546 547

	/*
	 * Copy the old directory to the stack buffer.
	 */
	dp = args->dp;
548
	mp = dp->i_mount;
L
Linus Torvalds 已提交
549

C
Christoph Hellwig 已提交
550
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
551 552
	old_isize = (int)dp->i_d.di_size;
	buf = kmem_alloc(old_isize, KM_SLEEP);
C
Christoph Hellwig 已提交
553
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
L
Linus Torvalds 已提交
554 555 556 557 558 559
	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.
	 */
560
	for (offset = xfs_dir3_data_first_offset(mp),
561
	      oldsfep = xfs_dir2_sf_firstentry(oldsfp),
562
	      add_datasize = xfs_dir3_data_entsize(mp, args->namelen),
L
Linus Torvalds 已提交
563 564
	      eof = (char *)oldsfep == &buf[old_isize];
	     !eof;
565 566
	     offset = new_offset + xfs_dir3_data_entsize(mp, oldsfep->namelen),
	      oldsfep = xfs_dir3_sf_nextentry(mp, oldsfp, oldsfep),
L
Linus Torvalds 已提交
567
	      eof = (char *)oldsfep == &buf[old_isize]) {
568
		new_offset = xfs_dir2_sf_get_offset(oldsfep);
L
Linus Torvalds 已提交
569 570 571 572 573 574 575 576 577 578 579 580 581
		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 已提交
582
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
583 584 585 586 587 588 589 590 591 592
	/*
	 * 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;
593
	xfs_dir2_sf_put_offset(sfep, offset);
L
Linus Torvalds 已提交
594
	memcpy(sfep->name, args->name, sfep->namelen);
595
	xfs_dir3_sfe_put_ino(mp, sfp, sfep, args->inumber);
596
	xfs_dir3_sfe_put_ftype(mp, sfp, sfep, args->filetype);
C
Christoph Hellwig 已提交
597
	sfp->count++;
L
Linus Torvalds 已提交
598 599
#if XFS_BIG_INUMS
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange)
C
Christoph Hellwig 已提交
600
		sfp->i8count++;
L
Linus Torvalds 已提交
601 602 603 604 605
#endif
	/*
	 * If there's more left to copy, do that.
	 */
	if (!eof) {
606
		sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep);
L
Linus Torvalds 已提交
607 608
		memcpy(sfep, oldsfep, old_isize - nbytes);
	}
609
	kmem_free(buf);
L
Linus Torvalds 已提交
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
	dp->i_d.di_size = new_isize;
	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 */
{
	xfs_inode_t		*dp;		/* incore directory inode */
	int			holefit;	/* found hole it will fit in */
	int			i;		/* entry number */
	xfs_mount_t		*mp;		/* filesystem mount point */
	xfs_dir2_data_aoff_t	offset;		/* data block offset */
	xfs_dir2_sf_entry_t	*sfep;		/* shortform entry */
C
Christoph Hellwig 已提交
634
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
635 636 637 638 639 640
	int			size;		/* entry's data size */
	int			used;		/* data bytes used */

	dp = args->dp;
	mp = dp->i_mount;

C
Christoph Hellwig 已提交
641
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
642
	size = xfs_dir3_data_entsize(mp, args->namelen);
643
	offset = xfs_dir3_data_first_offset(mp);
644
	sfep = xfs_dir2_sf_firstentry(sfp);
L
Linus Torvalds 已提交
645 646 647 648 649 650
	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 已提交
651
	for (i = 0; i < sfp->count; i++) {
L
Linus Torvalds 已提交
652
		if (!holefit)
653 654
			holefit = offset + size <= xfs_dir2_sf_get_offset(sfep);
		offset = xfs_dir2_sf_get_offset(sfep) +
655 656
			 xfs_dir3_data_entsize(mp, sfep->namelen);
		sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep);
L
Linus Torvalds 已提交
657 658 659 660 661 662
	}
	/*
	 * Calculate data bytes used excluding the new entry, if this
	 * was a data block (block form directory).
	 */
	used = offset +
C
Christoph Hellwig 已提交
663
	       (sfp->count + 3) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
L
Linus Torvalds 已提交
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
	       (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.
	 */
	if (used + (holefit ? 0 : size) > mp->m_dirblksize)
		return 0;
	/*
	 * If changing the inode number size, do it the hard way.
	 */
#if XFS_BIG_INUMS
	if (objchange) {
		return 2;
	}
#else
	ASSERT(objchange == 0);
#endif
	/*
	 * If it won't fit at the end then do it the hard way (use the hole).
	 */
	if (used + size > mp->m_dirblksize)
		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 */
{
	xfs_inode_t		*dp;		/* incore directory inode */
	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 已提交
709
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
710
	struct xfs_mount	*mp;
L
Linus Torvalds 已提交
711 712

	dp = args->dp;
713
	mp = dp->i_mount;
L
Linus Torvalds 已提交
714

C
Christoph Hellwig 已提交
715
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
716
	offset = xfs_dir3_data_first_offset(mp);
717
	ino = xfs_dir2_sf_get_parent_ino(sfp);
L
Linus Torvalds 已提交
718 719
	i8count = ino > XFS_DIR2_MAX_SHORT_INUM;

720
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
C
Christoph Hellwig 已提交
721
	     i < sfp->count;
722
	     i++, sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep)) {
723
		ASSERT(xfs_dir2_sf_get_offset(sfep) >= offset);
724
		ino = xfs_dir3_sfe_get_ino(mp, sfp, sfep);
L
Linus Torvalds 已提交
725 726
		i8count += ino > XFS_DIR2_MAX_SHORT_INUM;
		offset =
727
			xfs_dir2_sf_get_offset(sfep) +
728 729 730
			xfs_dir3_data_entsize(mp, sfep->namelen);
		ASSERT(xfs_dir3_sfe_get_ftype(mp, sfp, sfep) <
							XFS_DIR3_FT_MAX);
L
Linus Torvalds 已提交
731
	}
C
Christoph Hellwig 已提交
732
	ASSERT(i8count == sfp->i8count);
L
Linus Torvalds 已提交
733 734 735
	ASSERT(XFS_BIG_INUMS || i8count == 0);
	ASSERT((char *)sfep - (char *)sfp == dp->i_d.di_size);
	ASSERT(offset +
C
Christoph Hellwig 已提交
736
	       (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
737
	       (uint)sizeof(xfs_dir2_block_tail_t) <= mp->m_dirblksize);
L
Linus Torvalds 已提交
738 739 740 741 742 743 744 745 746 747 748 749 750
}
#endif	/* DEBUG */

/*
 * 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 已提交
751
	xfs_dir2_sf_hdr_t *sfp;		/* shortform structure */
L
Linus Torvalds 已提交
752 753
	int		size;		/* directory size */

C
Christoph Hellwig 已提交
754 755
	trace_xfs_dir2_sf_create(args);

L
Linus Torvalds 已提交
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
	dp = args->dp;

	ASSERT(dp != NULL);
	ASSERT(dp->i_d.di_size == 0);
	/*
	 * If it's currently a zero-length extent file,
	 * convert it to local format.
	 */
	if (dp->i_d.di_format == XFS_DINODE_FMT_EXTENTS) {
		dp->i_df.if_flags &= ~XFS_IFEXTENTS;	/* just in case */
		dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
		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;
773
	size = xfs_dir2_sf_hdr_size(i8count);
L
Linus Torvalds 已提交
774 775 776 777 778 779 780
	/*
	 * Make a buffer for the data.
	 */
	xfs_idata_realloc(dp, size, XFS_DATA_FORK);
	/*
	 * Fill in the header,
	 */
C
Christoph Hellwig 已提交
781 782
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	sfp->i8count = i8count;
L
Linus Torvalds 已提交
783 784 785
	/*
	 * Now can put in the inode number, since i8count is set.
	 */
786
	xfs_dir2_sf_put_parent_ino(sfp, pino);
C
Christoph Hellwig 已提交
787
	sfp->count = 0;
L
Linus Torvalds 已提交
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
	dp->i_d.di_size = size;
	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 */
{
	xfs_inode_t		*dp;		/* incore directory inode */
	int			i;		/* entry index */
804
	int			error;
L
Linus Torvalds 已提交
805
	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
C
Christoph Hellwig 已提交
806
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
807
	enum xfs_dacmp		cmp;		/* comparison result */
808
	xfs_dir2_sf_entry_t	*ci_sfep;	/* case-insens. entry */
L
Linus Torvalds 已提交
809

C
Christoph Hellwig 已提交
810 811
	trace_xfs_dir2_sf_lookup(args);

L
Linus Torvalds 已提交
812 813 814 815 816 817 818 819 820 821 822 823 824
	xfs_dir2_sf_check(args);
	dp = args->dp;

	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
	/*
	 * Bail out if the directory is way too short.
	 */
	if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
		ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
		return XFS_ERROR(EIO);
	}
	ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
825 826
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
L
Linus Torvalds 已提交
827 828 829 830 831
	/*
	 * Special case for .
	 */
	if (args->namelen == 1 && args->name[0] == '.') {
		args->inumber = dp->i_ino;
832
		args->cmpresult = XFS_CMP_EXACT;
833
		args->filetype = XFS_DIR3_FT_DIR;
L
Linus Torvalds 已提交
834 835 836 837 838 839 840
		return XFS_ERROR(EEXIST);
	}
	/*
	 * Special case for ..
	 */
	if (args->namelen == 2 &&
	    args->name[0] == '.' && args->name[1] == '.') {
841
		args->inumber = xfs_dir2_sf_get_parent_ino(sfp);
842
		args->cmpresult = XFS_CMP_EXACT;
843
		args->filetype = XFS_DIR3_FT_DIR;
L
Linus Torvalds 已提交
844 845 846 847 848
		return XFS_ERROR(EEXIST);
	}
	/*
	 * Loop over all the entries trying to match ours.
	 */
849
	ci_sfep = NULL;
C
Christoph Hellwig 已提交
850
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
851
	     i++, sfep = xfs_dir3_sf_nextentry(dp->i_mount, sfp, sfep)) {
852 853 854 855 856 857 858 859 860
		/*
		 * 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.
		 */
		cmp = dp->i_mount->m_dirnameops->compname(args, sfep->name,
								sfep->namelen);
		if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
			args->cmpresult = cmp;
861 862
			args->inumber = xfs_dir3_sfe_get_ino(dp->i_mount,
							     sfp, sfep);
863 864
			args->filetype = xfs_dir3_sfe_get_ftype(dp->i_mount,
								sfp, sfep);
865 866
			if (cmp == XFS_CMP_EXACT)
				return XFS_ERROR(EEXIST);
867
			ci_sfep = sfep;
L
Linus Torvalds 已提交
868 869
		}
	}
870
	ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
871 872
	/*
	 * Here, we can only be doing a lookup (not a rename or replace).
873
	 * If a case-insensitive match was not found, return ENOENT.
874
	 */
875 876 877 878 879
	if (!ci_sfep)
		return XFS_ERROR(ENOENT);
	/* otherwise process the CI match as required by the caller */
	error = xfs_dir_cilookup_result(args, ci_sfep->name, ci_sfep->namelen);
	return XFS_ERROR(error);
L
Linus Torvalds 已提交
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
}

/*
 * Remove an entry from a shortform directory.
 */
int						/* error */
xfs_dir2_sf_removename(
	xfs_da_args_t		*args)
{
	int			byteoff;	/* offset of removed entry */
	xfs_inode_t		*dp;		/* incore directory inode */
	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 已提交
896
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
897

C
Christoph Hellwig 已提交
898 899
	trace_xfs_dir2_sf_removename(args);

L
Linus Torvalds 已提交
900 901 902 903 904 905 906 907 908 909 910 911 912
	dp = args->dp;

	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
	oldsize = (int)dp->i_d.di_size;
	/*
	 * Bail out if the directory is way too short.
	 */
	if (oldsize < offsetof(xfs_dir2_sf_hdr_t, parent)) {
		ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
		return XFS_ERROR(EIO);
	}
	ASSERT(dp->i_df.if_bytes == oldsize);
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
913 914
	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 已提交
915 916 917 918
	/*
	 * Loop over the old directory entries.
	 * Find the one we're deleting.
	 */
C
Christoph Hellwig 已提交
919
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
920
	     i++, sfep = xfs_dir3_sf_nextentry(dp->i_mount, sfp, sfep)) {
921 922
		if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
								XFS_CMP_EXACT) {
923
			ASSERT(xfs_dir3_sfe_get_ino(dp->i_mount, sfp, sfep) ==
924
			       args->inumber);
L
Linus Torvalds 已提交
925 926 927 928 929 930
			break;
		}
	}
	/*
	 * Didn't find it.
	 */
C
Christoph Hellwig 已提交
931
	if (i == sfp->count)
L
Linus Torvalds 已提交
932 933 934 935 936
		return XFS_ERROR(ENOENT);
	/*
	 * Calculate sizes.
	 */
	byteoff = (int)((char *)sfep - (char *)sfp);
937
	entsize = xfs_dir3_sf_entsize(dp->i_mount, sfp, args->namelen);
L
Linus Torvalds 已提交
938 939 940 941 942 943 944 945 946 947
	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 已提交
948
	sfp->count--;
L
Linus Torvalds 已提交
949 950 951 952 953
	dp->i_d.di_size = newsize;
	/*
	 * Reallocate, making it smaller.
	 */
	xfs_idata_realloc(dp, newsize - oldsize, XFS_DATA_FORK);
C
Christoph Hellwig 已提交
954
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
955 956 957 958 959
#if XFS_BIG_INUMS
	/*
	 * Are we changing inode number size?
	 */
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) {
C
Christoph Hellwig 已提交
960
		if (sfp->i8count == 1)
L
Linus Torvalds 已提交
961 962
			xfs_dir2_sf_toino4(args);
		else
C
Christoph Hellwig 已提交
963
			sfp->i8count--;
L
Linus Torvalds 已提交
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
	}
#endif
	xfs_dir2_sf_check(args);
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
	return 0;
}

/*
 * Replace the inode number of an entry in a shortform directory.
 */
int						/* error */
xfs_dir2_sf_replace(
	xfs_da_args_t		*args)		/* operation arguments */
{
	xfs_inode_t		*dp;		/* incore directory inode */
	int			i;		/* entry index */
#if XFS_BIG_INUMS || defined(DEBUG)
	xfs_ino_t		ino=0;		/* entry old inode number */
#endif
#if XFS_BIG_INUMS
	int			i8elevated;	/* sf_toino8 set i8count=1 */
#endif
	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
C
Christoph Hellwig 已提交
987
	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
L
Linus Torvalds 已提交
988

C
Christoph Hellwig 已提交
989 990
	trace_xfs_dir2_sf_replace(args);

L
Linus Torvalds 已提交
991 992 993 994 995 996 997 998 999 1000 1001 1002
	dp = args->dp;

	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
	/*
	 * Bail out if the shortform directory is way too small.
	 */
	if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
		ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
		return XFS_ERROR(EIO);
	}
	ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
	ASSERT(dp->i_df.if_u1.if_data != NULL);
C
Christoph Hellwig 已提交
1003 1004
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
L
Linus Torvalds 已提交
1005 1006 1007 1008
#if XFS_BIG_INUMS
	/*
	 * New inode number is large, and need to convert to 8-byte inodes.
	 */
C
Christoph Hellwig 已提交
1009
	if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
L
Linus Torvalds 已提交
1010 1011 1012 1013 1014
		int	error;			/* error return value */
		int	newsize;		/* new inode size */

		newsize =
			dp->i_df.if_bytes +
C
Christoph Hellwig 已提交
1015
			(sfp->count + 1) *
L
Linus Torvalds 已提交
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
			((uint)sizeof(xfs_dir2_ino8_t) -
			 (uint)sizeof(xfs_dir2_ino4_t));
		/*
		 * Won't fit as shortform, convert to block then do replace.
		 */
		if (newsize > XFS_IFORK_DSIZE(dp)) {
			error = xfs_dir2_sf_to_block(args);
			if (error) {
				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 已提交
1033
		sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
	} else
		i8elevated = 0;
#endif
	ASSERT(args->namelen != 1 || args->name[0] != '.');
	/*
	 * Replace ..'s entry.
	 */
	if (args->namelen == 2 &&
	    args->name[0] == '.' && args->name[1] == '.') {
#if XFS_BIG_INUMS || defined(DEBUG)
1044
		ino = xfs_dir2_sf_get_parent_ino(sfp);
L
Linus Torvalds 已提交
1045 1046
		ASSERT(args->inumber != ino);
#endif
1047
		xfs_dir2_sf_put_parent_ino(sfp, args->inumber);
L
Linus Torvalds 已提交
1048 1049 1050 1051 1052
	}
	/*
	 * Normal entry, look for the name.
	 */
	else {
1053 1054
		for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count;
		     i++, sfep = xfs_dir3_sf_nextentry(dp->i_mount, sfp, sfep)) {
1055 1056
			if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
								XFS_CMP_EXACT) {
L
Linus Torvalds 已提交
1057
#if XFS_BIG_INUMS || defined(DEBUG)
1058 1059
				ino = xfs_dir3_sfe_get_ino(dp->i_mount,
							   sfp, sfep);
L
Linus Torvalds 已提交
1060 1061
				ASSERT(args->inumber != ino);
#endif
1062 1063
				xfs_dir3_sfe_put_ino(dp->i_mount, sfp, sfep,
						     args->inumber);
1064 1065
				xfs_dir3_sfe_put_ftype(dp->i_mount, sfp, sfep,
						       args->filetype);
L
Linus Torvalds 已提交
1066 1067 1068 1069 1070 1071
				break;
			}
		}
		/*
		 * Didn't find it.
		 */
C
Christoph Hellwig 已提交
1072
		if (i == sfp->count) {
1073
			ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
L
Linus Torvalds 已提交
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
#if XFS_BIG_INUMS
			if (i8elevated)
				xfs_dir2_sf_toino4(args);
#endif
			return XFS_ERROR(ENOENT);
		}
	}
#if XFS_BIG_INUMS
	/*
	 * 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 已提交
1090
		if (sfp->i8count == 1)
L
Linus Torvalds 已提交
1091 1092
			xfs_dir2_sf_toino4(args);
		else
C
Christoph Hellwig 已提交
1093
			sfp->i8count--;
L
Linus Torvalds 已提交
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
	}
	/*
	 * 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 已提交
1104
		ASSERT(sfp->i8count != 0);
L
Linus Torvalds 已提交
1105
		if (!i8elevated)
C
Christoph Hellwig 已提交
1106
			sfp->i8count++;
L
Linus Torvalds 已提交
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
	}
#endif
	xfs_dir2_sf_check(args);
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA);
	return 0;
}

#if XFS_BIG_INUMS
/*
 * 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 */
{
	char			*buf;		/* old dir's buffer */
	xfs_inode_t		*dp;		/* incore directory inode */
	int			i;		/* entry index */
	int			newsize;	/* new inode size */
	xfs_dir2_sf_entry_t	*oldsfep;	/* old sf entry */
C
Christoph Hellwig 已提交
1128
	xfs_dir2_sf_hdr_t	*oldsfp;	/* old sf directory */
L
Linus Torvalds 已提交
1129 1130
	int			oldsize;	/* old inode size */
	xfs_dir2_sf_entry_t	*sfep;		/* new sf entry */
C
Christoph Hellwig 已提交
1131
	xfs_dir2_sf_hdr_t	*sfp;		/* new sf directory */
1132
	struct xfs_mount	*mp;
L
Linus Torvalds 已提交
1133

C
Christoph Hellwig 已提交
1134 1135
	trace_xfs_dir2_sf_toino4(args);

L
Linus Torvalds 已提交
1136
	dp = args->dp;
1137
	mp = dp->i_mount;
L
Linus Torvalds 已提交
1138 1139 1140 1141 1142 1143 1144 1145

	/*
	 * 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;
	buf = kmem_alloc(oldsize, KM_SLEEP);
C
Christoph Hellwig 已提交
1146 1147
	oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(oldsfp->i8count == 1);
L
Linus Torvalds 已提交
1148 1149 1150 1151 1152 1153
	memcpy(buf, oldsfp, oldsize);
	/*
	 * Compute the new inode size.
	 */
	newsize =
		oldsize -
C
Christoph Hellwig 已提交
1154
		(oldsfp->count + 1) *
L
Linus Torvalds 已提交
1155 1156 1157 1158 1159 1160
		((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t));
	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 已提交
1161 1162
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1163 1164 1165
	/*
	 * Fill in the new header.
	 */
C
Christoph Hellwig 已提交
1166 1167
	sfp->count = oldsfp->count;
	sfp->i8count = 0;
1168
	xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp));
L
Linus Torvalds 已提交
1169 1170 1171
	/*
	 * Copy the entries field by field.
	 */
1172 1173
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
		    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
C
Christoph Hellwig 已提交
1174
	     i < sfp->count;
1175 1176
	     i++, sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep),
		  oldsfep = xfs_dir3_sf_nextentry(mp, oldsfp, oldsfep)) {
L
Linus Torvalds 已提交
1177 1178 1179
		sfep->namelen = oldsfep->namelen;
		sfep->offset = oldsfep->offset;
		memcpy(sfep->name, oldsfep->name, sfep->namelen);
1180 1181 1182 1183
		xfs_dir3_sfe_put_ino(mp, sfp, sfep,
			xfs_dir3_sfe_get_ino(mp, oldsfp, oldsfep));
		xfs_dir3_sfe_put_ftype(mp, sfp, sfep,
			xfs_dir3_sfe_get_ftype(mp, oldsfp, oldsfep));
L
Linus Torvalds 已提交
1184 1185 1186 1187
	}
	/*
	 * Clean up the inode.
	 */
1188
	kmem_free(buf);
L
Linus Torvalds 已提交
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206
	dp->i_d.di_size = newsize;
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}

/*
 * Convert from 4-byte inode numbers to 8-byte inode numbers.
 * The new 8-byte inode number is not there yet, we leave with the
 * count 1 but no corresponding entry.
 */
static void
xfs_dir2_sf_toino8(
	xfs_da_args_t		*args)		/* operation arguments */
{
	char			*buf;		/* old dir's buffer */
	xfs_inode_t		*dp;		/* incore directory inode */
	int			i;		/* entry index */
	int			newsize;	/* new inode size */
	xfs_dir2_sf_entry_t	*oldsfep;	/* old sf entry */
C
Christoph Hellwig 已提交
1207
	xfs_dir2_sf_hdr_t	*oldsfp;	/* old sf directory */
L
Linus Torvalds 已提交
1208 1209
	int			oldsize;	/* old inode size */
	xfs_dir2_sf_entry_t	*sfep;		/* new sf entry */
C
Christoph Hellwig 已提交
1210
	xfs_dir2_sf_hdr_t	*sfp;		/* new sf directory */
1211
	struct xfs_mount	*mp;
L
Linus Torvalds 已提交
1212

C
Christoph Hellwig 已提交
1213 1214
	trace_xfs_dir2_sf_toino8(args);

L
Linus Torvalds 已提交
1215
	dp = args->dp;
1216
	mp = dp->i_mount;
L
Linus Torvalds 已提交
1217 1218 1219 1220 1221 1222 1223 1224

	/*
	 * 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;
	buf = kmem_alloc(oldsize, KM_SLEEP);
C
Christoph Hellwig 已提交
1225 1226
	oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
	ASSERT(oldsfp->i8count == 0);
L
Linus Torvalds 已提交
1227 1228 1229 1230 1231 1232
	memcpy(buf, oldsfp, oldsize);
	/*
	 * Compute the new inode size.
	 */
	newsize =
		oldsize +
C
Christoph Hellwig 已提交
1233
		(oldsfp->count + 1) *
L
Linus Torvalds 已提交
1234 1235 1236 1237 1238 1239
		((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t));
	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 已提交
1240 1241
	oldsfp = (xfs_dir2_sf_hdr_t *)buf;
	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
L
Linus Torvalds 已提交
1242 1243 1244
	/*
	 * Fill in the new header.
	 */
C
Christoph Hellwig 已提交
1245 1246
	sfp->count = oldsfp->count;
	sfp->i8count = 1;
1247
	xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp));
L
Linus Torvalds 已提交
1248 1249 1250
	/*
	 * Copy the entries field by field.
	 */
1251 1252
	for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp),
		    oldsfep = xfs_dir2_sf_firstentry(oldsfp);
C
Christoph Hellwig 已提交
1253
	     i < sfp->count;
1254 1255
	     i++, sfep = xfs_dir3_sf_nextentry(mp, sfp, sfep),
		  oldsfep = xfs_dir3_sf_nextentry(mp, oldsfp, oldsfep)) {
L
Linus Torvalds 已提交
1256 1257 1258
		sfep->namelen = oldsfep->namelen;
		sfep->offset = oldsfep->offset;
		memcpy(sfep->name, oldsfep->name, sfep->namelen);
1259 1260 1261 1262
		xfs_dir3_sfe_put_ino(mp, sfp, sfep,
			xfs_dir3_sfe_get_ino(mp, oldsfp, oldsfep));
		xfs_dir3_sfe_put_ftype(mp, sfp, sfep,
			xfs_dir3_sfe_get_ftype(mp, oldsfp, oldsfep));
L
Linus Torvalds 已提交
1263 1264 1265 1266
	}
	/*
	 * Clean up the inode.
	 */
1267
	kmem_free(buf);
L
Linus Torvalds 已提交
1268 1269 1270 1271
	dp->i_d.di_size = newsize;
	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}
#endif	/* XFS_BIG_INUMS */